87#include <wx/evtloop.h>
93#include "../ActiveProject.h"
94#include "../Journal.h"
95#include "../JournalOutput.h"
96#include "../JournalRegistry.h"
100#include "../widgets/AudacityMessageBox.h"
101#include "../widgets/HelpSystem.h"
109#define MAX_MENU_LEN 20
110#define MAX_SUBMENU_LEN 15
112#define MAX_MENU_LEN 1000
113#define MAX_SUBMENU_LEN 1000
116#define COMMAND XO("Command")
182 :
name(name_), menubar(menubar_)
191 :
name(name_), menu(
std::make_unique< wxMenu >() )
202 return std::make_unique<CommandManager>();
222 bMakingOccultCommands( false )
240 static const auto list = [] {
242 const char *
const strings[] = {
295 std::vector<NormalizedKeyString> result(
298 std::sort( result.begin(), result.end() );
319 bool bFull =
gPrefs->ReadBool(
wxT(
"/GUI/Shortcuts/FullDefaults"),
false);
358 auto result = std::make_unique<wxMenuBar>();
372 if(
entry.name == sMenu)
373 return entry.menubar;
512 if(!tmpCurrentSubMenu)
517 return tmpCurrentSubMenu;
542 name, label_in, finder, callback, options );
561 auto &checker = options.
checker;
574 const wxString
key,
bool defaultValue ) ->
CheckFn
582 return MakeCheckFn( setting.GetPath(), setting.GetDefault() );
599 for (
size_t i = 0, cnt = nItems; i < cnt; i++) {
611 entry->flags = flags;
627 entry->enabled =
false;
628 entry->isGlobal =
true;
644 if((ID >= wxID_LOWEST) && (ID <= wxID_HIGHEST))
665 bool excludeFromMacros =
669 const wxString & accel = options.
accel;
674 const auto &longLabel = options.
longName;
676 const bool multi = !nameSuffix.
empty();
688 auto entry = std::make_unique<CommandListEntry>();
711 entry->parameter = parameter;
713#if defined(__WXMAC__)
716 if (
name ==
wxT(
"Preferences"))
717 entry->id = wxID_PREFERENCES;
719 entry->id = wxID_EXIT;
721 entry->id = wxID_ABOUT;
728 entry->longLabel = longLabel.empty() ?
label : longLabel;
730 entry->excludeFromMacros = excludeFromMacros;
733 entry->labelPrefix = labelPrefix;
736 entry->finder = finder;
737 entry->callback = callback;
738 entry->isEffect = bIsEffect;
739 entry->multi = multi;
740 entry->index = index;
741 entry->count = count;
743 entry->enabled =
true;
747 entry->isGlobal =
false;
762 const auto &path =
entry->name.GET();
785 wxLogDebug(
wxT(
"Command '%s' defined by '%s' and '%s'"),
789 entry->label.Debug());
790 wxFAIL_MSG(wxString::Format(
wxT(
"Command '%s' defined by '%s' and '%s'"),
795 entry->label.Debug()));
801 if (!
entry->key.empty()) {
813 if (
auto pEntry = iter->second) {
814 keyStr = pEntry->key;
816 pLabel = &pEntry->label;
855 if (!
entry->key.empty())
862 Accel = wxString(
"\t ") +
key;
863 if(
key.StartsWith(
"Left" ))
break;
864 if(
key.StartsWith(
"Right"))
break;
865 if(
key.StartsWith(
"Up" ))
break;
866 if(
key.StartsWith(
"Down"))
break;
867 if(
key.StartsWith(
"Return"))
break;
868 if(
key.StartsWith(
"Tab"))
break;
869 if(
key.StartsWith(
"Shift+Tab"))
break;
870 if(
key.StartsWith(
"0"))
break;
871 if(
key.StartsWith(
"1"))
break;
872 if(
key.StartsWith(
"2"))
break;
873 if(
key.StartsWith(
"3"))
break;
874 if(
key.StartsWith(
"4"))
break;
875 if(
key.StartsWith(
"5"))
break;
876 if(
key.StartsWith(
"6"))
break;
877 if(
key.StartsWith(
"7"))
break;
878 if(
key.StartsWith(
"8"))
break;
879 if(
key.StartsWith(
"9"))
break;
884 if(
key.StartsWith(
"NUMPAD_ENTER" ))
break;
885 if(
key.StartsWith(
"Backspace" ))
break;
886 if(
key.StartsWith(
"Delete" ))
break;
891 Accel = wxString(
"\t") +
entry->key.GET();
906 entry->enabled = enabled;
916 if (
entry->enabled != enabled) {
925 for(i=1; i<
entry->count; i++) {
932 wxMenuItem *item = multiEntry->
menu->FindItem(ID);
935 item->Enable(enabled);
938 wxLogDebug(
wxT(
"Warning: Menu entry with id %i in %s not found"),
939 ID,
entry->name.GET());
942 wxLogDebug(
wxT(
"Warning: Menu entry with id %i not in hash"), ID);
952 wxLogDebug(
wxT(
"Warning: Unknown command enabled: '%s'"),
953 (
const wxChar*)
name);
967 wxASSERT( (strictFlags & ~flags).
none() );
972 if(
entry->isOccult )
975 auto useFlags =
entry->useStrictFlags ? strictFlags : flags;
977 if (
entry->flags.any()) {
978 bool enable = ((useFlags &
entry->flags) ==
entry->flags);
989 wxLogDebug(
wxT(
"Warning: command doesn't exist: '%s'"),
993 return entry->enabled;
1015 entry->label = newLabel;
1041 bool rtl = (wxLayout_RightToLeft == wxTheApp->GetLayoutDirection());
1043 mark =
wxT(
"\u200f");
1045 static const wxString &separatorFormat =
wxT(
"%s / %s");
1047 for (
size_t ii = 0; ii < nCommands; ++ii) {
1048 const auto &pair = commands[ii];
1058 .
Format( mark, pair.Msgid().Stripped() );
1060 auto name = pair.Internal();
1063 if (!keyStr.empty()){
1064 auto keyString = keyStr.Display(
true);
1103 int type = evt.GetEventType();
1106 if (
entry->isGlobal && type == wxEVT_KEY_DOWN)
1112 entry->enabled =
false;
1117 wxWindow * pFocus = wxWindow::FindFocus();
1118 wxWindow * pParent = wxGetTopLevelParent( pFocus );
1119 bool validTarget = pParent == pWindow;
1122 if( pParent && pParent->GetParent() == pWindow ){
1124 validTarget = keystrokeHandlingWindow->HandleCommandKeystrokes();
1126 validTarget = validTarget && wxEventLoop::GetActive()->IsMain();
1129 if (!permit && !validTarget )
1136 wxKeyEvent temp = evt;
1140 if((type == wxEVT_KEY_DOWN) || (type == wxEVT_KEY_UP ))
1142 wxWindow * pWnd = wxWindow::FindFocus();
1152 switch( evt.GetKeyCode() ){
1165 case WXK_NUMPAD_ENTER:
1182 if (type == wxEVT_KEY_DOWN)
1184 if (
entry->skipKeydown)
1191 if (type == wxEVT_KEY_UP &&
entry->wantKeyup)
1209 bool handled =
false;
1210 if ( fields.size() == 2 ) {
1215 auto &command = fields[1];
1217 pManager->HandleTextualCommand( command, context, flags,
false );
1232 CommandFlag flags,
bool alwaysEnabled,
const wxEvent * evt,
1241 if (!alwaysEnabled &&
entry->flags.any()) {
1243 const auto NiceName =
entry->label.Stripped(
1248 NiceName, flags,
entry->flags );
1266 if (
auto &finder =
entry->finder) {
1267 auto &
handler = finder(project);
1271 (
entry->callback.nonMemberFn)(context);
1283 auto lastEffectDesc =
XO(
"Repeat %s").Format(
mNiceName);
1284 Modify(
wxT(
"RepeatLastAnalyzer"), lastEffectDesc);
1296 auto lastEffectDesc =
XO(
"Repeat %s").Format(
mNiceName);
1297 Modify(
wxT(
"RepeatLastTool"), lastEffectDesc);
1307 if (
auto &finder =
entry->finder) {
1312 (
entry->callback.nonMemberFn)(context);
1348 if( Str ==
entry->name ||
1352 Str ==
entry->labelPrefix.Translation() )
1363 if( Str ==
entry->name )
1380 auto &cat =
entry->labelTop;
1382 cats.push_back(cat);
1395 wxMenuBar *bar = p->GetMenuBar();
1396 size_t cnt = bar->GetMenuCount();
1397 for (
size_t i = 0; i < cnt; i++) {
1398 cats.push_back(bar->GetMenuLabelText(i));
1408 bool includeMultis)
const
1411 if (
entry->isEffect )
1415 else if( includeMultis )
1421 std::vector<bool> &vExcludeFromMacros,
1422 bool includeMultis)
const
1424 vExcludeFromMacros.clear();
1430 if (
entry->isEffect )
1433 names.push_back(
entry->longLabel), vExcludeFromMacros.push_back(
entry->excludeFromMacros);
1434 else if( includeMultis )
1435 names.push_back(
entry->longLabel), vExcludeFromMacros.push_back(
entry->excludeFromMacros);
1441 std::vector<NormalizedKeyString> &keys,
1442 std::vector<NormalizedKeyString> &default_keys,
1445#
if defined(EXPERIMENTAL_KEY_VIEW)
1455 if ( !
entry->multi || includeMultis )
1458 keys.push_back(
entry->key);
1459 default_keys.push_back(
entry->defaultKey);
1460 labels.push_back(
entry->label);
1461 categories.push_back(
entry->labelTop);
1462#if defined(EXPERIMENTAL_KEY_VIEW)
1463 prefixes.push_back(
entry->labelPrefix);
1483 return entry->longLabel;
1492 if (!
entry->labelPrefix.empty())
1497 return entry->label.Stripped();
1506 return entry->labelTop;
1526 return entry->defaultKey;
1531 if (tag ==
"audacitykeyboard") {
1535 if (tag ==
"command") {
1539 for (
auto pair : attrs)
1541 auto attr = pair.first;
1542 auto value = pair.second;
1544 if (value.IsStringView())
1546 const wxString strValue = value.ToWString();
1550 else if (attr ==
"key")
1586 xmlFile.StartTag(
wxT(
"audacitykeyboard"));
1587 xmlFile.WriteAttr(
wxT(
"audacityversion"), AUDACITY_VERSION_STRING);
1589 for(
const auto &
entry : mCommandList) {
1591 xmlFile.StartTag(
wxT(
"command"));
1592 xmlFile.WriteAttr(
wxT(
"name"),
entry->name);
1593 xmlFile.WriteAttr(
wxT(
"key"),
entry->key);
1594 xmlFile.EndTag(
wxT(
"command"));
1597 xmlFile.EndTag(
wxT(
"audacitykeyboard"));
1625 entry->flags = flags;
1629void CommandManager::CheckDups()
1632 for (
size_t j = 0; (int)j < cnt; j++) {
1640 for (
size_t i = 0; (int)i < cnt; i++) {
1647 msg.Printf(
wxT(
"key combo '%s' assigned to '%s' and '%s'"),
1681 if (!entry2->key.empty() && entry2->key == entry2->defaultKey) {
1682 if (entry2->key ==
entry->key) {
1683 auto name =
wxT(
"/NewKeys/") + entry2->name.GET();
1686 disabledShortcuts +=
1687 XO(
"\n* %s, because you have assigned the shortcut %s to %s")
1688 .Format(entry2->label.Strip(),
entry->key.GET(),
entry->label.Strip());
1697 " because their default shortcut is new or changed, and is the same shortcut"
1698 " that you have assigned to another command.")
1699 + disabledShortcuts;
1707#include "../KeyboardCapture.h"
1709static KeyboardCapture::PreFilter::Scope
scope1{
1716static KeyboardCapture::PostFilter::Scope
scope2{
1717[]( wxKeyEvent &
key ) {
1721 return manager.FilterKeyEvent(project.get(),
key);
AUDACITY_DLL_API std::weak_ptr< AudacityProject > GetActiveProject()
@ Internal
Indicates internal failure from Audacity.
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
constexpr CommandFlag AlwaysEnabledFlag
std::bitset< NCommandFlags > CommandFlag
constexpr CommandFlag NoFlagsSpecified
std::function< CommandHandlerObject &(AudacityProject &) > CommandHandlerFinder
static KeyboardCapture::PostFilter::Scope scope2
static const AudacityProject::AttachedObjects::RegisteredFactory key
static KeyboardCapture::PreFilter::Scope scope1
const TranslatableString name
std::vector< CommandID > CommandIDs
NormalizedKeyString KeyEventToKeyString(const wxKeyEvent &event)
ValueRestorer< T > valueRestorer(T &var)
inline functions provide convenient parameter type deduction
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
static ProjectFileIORegistry::AttributeWriterEntry entry
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
static const AttachedProjectObjects::RegisteredFactory manager
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
std::vector< Attribute > AttributesList
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
TemporarySelection temporarySelection
AudacityProject & project
CommandManager implements a system for organizing all user-callable commands.
void WriteXML(XMLWriter &xmlFile) const
void Enable(const wxString &name, bool enabled)
void HandleXMLEndTag(const std::string_view &tag) override
void AddItemList(const CommandID &name, const ComponentInterfaceSymbol items[], size_t nItems, CommandHandlerFinder finder, CommandFunctorPointer callback, CommandFlag flags, bool bIsEffect=false)
void AddGlobalCommand(const CommandID &name, const TranslatableString &label, CommandHandlerFinder finder, CommandFunctorPointer callback, const Options &options={})
void RegisterLastTool(const CommandContext &context)
wxMenu * CurrentSubMenu() const
CommandListEntry * NewIdentifier(const CommandID &name, const TranslatableString &label, wxMenu *menu, CommandHandlerFinder finder, CommandFunctorPointer callback, const CommandID &nameSuffix, int index, int count, const Options &options)
wxMenu * BeginMenu(const TranslatableString &tName)
wxString FormatLabelForMenu(const CommandID &id, const TranslatableString *pLabel) const
Format a string appropriate for insertion in a menu.
CommandNameHash mCommandNameHash
void BeginOccultCommands()
wxString FormatLabelWithDisabledAccel(const CommandListEntry *entry) const
void UpdateCheckmarks(AudacityProject &project)
bool HandleMenuID(AudacityProject &project, int id, CommandFlag flags, bool alwaysEnabled)
TranslatableStrings GetCategories(AudacityProject &)
std::unique_ptr< wxMenuBar > mTempMenuBar
TranslatableString mCurrentMenuName
TranslatableString DescribeCommandsAndShortcuts(const ComponentInterfaceSymbol commands[], size_t nCommands) const
bool FilterKeyEvent(AudacityProject *project, const wxKeyEvent &evt, bool permit=false)
wxMenu * CurrentMenu() const
void DoRepeatProcess(const CommandContext &context, int)
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
std::vector< NormalizedKeyString > mMaxListOnly
wxMenuBar * CurrentMenuBar() const
void Modify(const wxString &name, const TranslatableString &newLabel)
Changes the label text of a menu item.
void RegisterLastAnalyzer(const CommandContext &context)
static CommandManager & Get(AudacityProject &project)
std::unique_ptr< wxMenu > uCurrentMenu
void EndMenu()
This attaches a menu, if it's main, to the menubar.
wxMenu * BeginSubMenu(const TranslatableString &tName)
void RemoveDuplicateShortcuts()
void GetAllCommandLabels(TranslatableStrings &labels, std::vector< bool > &vExcludeFromMacros, bool includeMultis) const
void AddItem(AudacityProject &project, const CommandID &name, const TranslatableString &label_in, CommandHandlerFinder finder, CommandFunctorPointer callback, CommandFlag flags, const Options &options={})
TextualCommandResult HandleTextualCommand(const CommandID &Str, const CommandContext &context, CommandFlag flags, bool alwaysEnabled)
wxMenu * BeginMainMenu(const TranslatableString &tName)
TranslatableString GetLabelFromName(const CommandID &name)
bool bMakingOccultCommands
int NextIdentifier(int ID)
TranslatableString GetCategoryFromName(const CommandID &name)
void GetAllCommandData(CommandIDs &names, std::vector< NormalizedKeyString > &keys, std::vector< NormalizedKeyString > &default_keys, TranslatableStrings &labels, TranslatableStrings &categories, bool includeMultis)
static const std::vector< NormalizedKeyString > & ExcludedList()
CommandKeyHash mCommandKeyHash
std::function< bool(AudacityProject &) > CheckFn
void SetCommandFlags(const CommandID &name, CommandFlag flags)
bool GetEnabled(const CommandID &name)
void SetKeyFromName(const CommandID &name, const NormalizedKeyString &key)
NormalizedKeyString GetKeyFromName(const CommandID &name) const
void Check(const CommandID &name, bool checked)
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
wxMenuBar * GetMenuBar(const wxString &sMenu) const
CommandID GetNameFromNumericID(int id)
void GetAllCommandNames(CommandIDs &names, bool includeMultis) const
void EnableUsingFlags(CommandFlag flags, CommandFlag strictFlags)
NormalizedKeyString GetDefaultKeyFromName(const CommandID &name)
bool HandleCommandEntry(AudacityProject &project, const CommandListEntry *entry, CommandFlag flags, bool alwaysEnabled, const wxEvent *evt=nullptr, const CommandContext *pGivenContext=nullptr)
virtual ~CommandManager()
void SetKeyFromIndex(int i, const NormalizedKeyString &key)
int GetNumberOfKeysRead() const
std::unique_ptr< wxMenuBar > AddMenuBar(const wxString &sMenu)
TranslatableString GetPrefixedLabelFromName(const CommandID &name)
TranslatableString mNiceName
CommandNumericIDHash mCommandNumericIDHash
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
virtual bool HasEntry(const wxString &strName) const wxOVERRIDE
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
virtual void SetPath(const wxString &strPath) wxOVERRIDE
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Holds a msgid for the translation catalog; may also bind format arguments.
Identifier MSGID() const
MSGID is the English lookup key in the catalog, not necessarily for user's eyes if locale is some oth...
wxString Translation() const
TranslatableString & Format(Args &&...args) &
Capture variadic format arguments (by copy) when there is no plural.
wxString Debug() const
Format as an English string for debugging logs and developers' eyes, not for end users.
TranslatableString Stripped(unsigned options=MenuCodes) const
non-mutating, constructs another TranslatableString object
This class is an interface which should be implemented by classes which wish to be able to load and s...
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
void Output(const wxString &string)
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
constexpr auto JournalCode
Journal::RegisteredCommand sCommand
AuthorizationHandler handler
CommandListEntry is a structure used by CommandManager.
TranslatableString longLabel
std::function< bool(AudacityProject &) > CheckFn
CommandParameter parameter
CommandHandlerFinder finder
TranslatableString labelTop
NormalizedKeyString defaultKey
TranslatableString labelPrefix
CommandFunctorPointer callback
TranslatableString longName
static CheckFn MakeCheckFn(const wxString key, bool defaultValue)
Options && IsEffect(bool value=true) &&
CommandParameter parameter
virtual ~NonKeystrokeInterceptingWindow()
virtual bool HandleCommandKeystrokes()
virtual ~TopLevelKeystrokeHandlingWindow()