87#include <wx/evtloop.h>
93#include "../ActiveProject.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() );
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;
892 if(
key.StartsWith(
",") )
break;
893 if(
key.StartsWith(
".") )
break;
899 Accel = wxString(
"\t") +
entry->key.GET();
914 entry->enabled = enabled;
924 if (
entry->enabled != enabled) {
933 for(i=1; i<
entry->count; i++) {
940 wxMenuItem *item = multiEntry->
menu->FindItem(ID);
943 item->Enable(enabled);
946 wxLogDebug(
wxT(
"Warning: Menu entry with id %i in %s not found"),
947 ID,
entry->name.GET());
950 wxLogDebug(
wxT(
"Warning: Menu entry with id %i not in hash"), ID);
960 wxLogDebug(
wxT(
"Warning: Unknown command enabled: '%s'"),
961 (
const wxChar*)
name);
975 wxASSERT( (strictFlags & ~flags).
none() );
980 if(
entry->isOccult )
983 auto useFlags =
entry->useStrictFlags ? strictFlags : flags;
985 if (
entry->flags.any()) {
986 bool enable = ((useFlags &
entry->flags) ==
entry->flags);
997 wxLogDebug(
wxT(
"Warning: command doesn't exist: '%s'"),
1001 return entry->enabled;
1023 entry->label = newLabel;
1049 bool rtl = (wxLayout_RightToLeft == wxTheApp->GetLayoutDirection());
1051 mark =
wxT(
"\u200f");
1053 static const wxString &separatorFormat =
wxT(
"%s / %s");
1055 for (
size_t ii = 0; ii < nCommands; ++ii) {
1056 const auto &pair = commands[ii];
1066 .
Format( mark, pair.Msgid().Stripped() );
1068 auto name = pair.Internal();
1071 if (!keyStr.empty()){
1072 auto keyString = keyStr.Display(
true);
1111 int type = evt.GetEventType();
1114 if (
entry->isGlobal && type == wxEVT_KEY_DOWN)
1120 entry->enabled =
false;
1126 wxWindow * pParent = wxGetTopLevelParent( pFocus );
1127 bool validTarget = pParent == pWindow;
1130 if( pParent && pParent->GetParent() == pWindow ){
1132 validTarget = keystrokeHandlingWindow->HandleCommandKeystrokes();
1134 validTarget = validTarget && wxEventLoop::GetActive()->IsMain();
1137 if (!permit && !validTarget )
1144 wxKeyEvent temp = evt;
1148 if((type == wxEVT_KEY_DOWN) || (type == wxEVT_KEY_UP ))
1160 switch( evt.GetKeyCode() ){
1173 case WXK_NUMPAD_ENTER:
1188 if (!evt.HasAnyModifiers())
1194 if (type == wxEVT_KEY_DOWN)
1196 if (
entry->skipKeydown)
1203 if (type == wxEVT_KEY_UP &&
entry->wantKeyup)
1221 bool handled =
false;
1222 if ( fields.size() == 2 ) {
1227 auto &command = fields[1];
1229 pManager->HandleTextualCommand( command, context, flags,
false );
1244 CommandFlag flags,
bool alwaysEnabled,
const wxEvent * evt,
1253 if (!alwaysEnabled &&
entry->flags.any()) {
1255 const auto NiceName =
entry->label.Stripped(
1260 NiceName, flags,
entry->flags );
1278 if (
auto &finder =
entry->finder) {
1283 (
entry->callback.nonMemberFn)(context);
1295 auto lastEffectDesc =
XO(
"Repeat %s").Format(
mNiceName);
1296 Modify(
wxT(
"RepeatLastAnalyzer"), lastEffectDesc);
1308 auto lastEffectDesc =
XO(
"Repeat %s").Format(
mNiceName);
1309 Modify(
wxT(
"RepeatLastTool"), lastEffectDesc);
1319 if (
auto &finder =
entry->finder) {
1324 (
entry->callback.nonMemberFn)(context);
1360 if( Str ==
entry->name ||
1364 Str ==
entry->labelPrefix.Translation() )
1375 if( Str ==
entry->name )
1392 auto &cat =
entry->labelTop;
1394 cats.push_back(cat);
1407 wxMenuBar *bar = p->GetMenuBar();
1408 size_t cnt = bar->GetMenuCount();
1409 for (
size_t i = 0; i < cnt; i++) {
1410 cats.push_back(bar->GetMenuLabelText(i));
1420 bool includeMultis)
const
1423 if (
entry->isEffect )
1427 else if( includeMultis )
1433 std::vector<bool> &vExcludeFromMacros,
1434 bool includeMultis)
const
1436 vExcludeFromMacros.clear();
1442 if (
entry->isEffect )
1445 names.push_back(
entry->longLabel), vExcludeFromMacros.push_back(
entry->excludeFromMacros);
1446 else if( includeMultis )
1447 names.push_back(
entry->longLabel), vExcludeFromMacros.push_back(
entry->excludeFromMacros);
1453 std::vector<NormalizedKeyString> &keys,
1454 std::vector<NormalizedKeyString> &default_keys,
1457#
if defined(EXPERIMENTAL_KEY_VIEW)
1467 if ( !
entry->multi || includeMultis )
1470 keys.push_back(
entry->key);
1471 default_keys.push_back(
entry->defaultKey);
1472 labels.push_back(
entry->label);
1473 categories.push_back(
entry->labelTop);
1474#if defined(EXPERIMENTAL_KEY_VIEW)
1475 prefixes.push_back(
entry->labelPrefix);
1495 return entry->longLabel;
1504 if (!
entry->labelPrefix.empty())
1509 return entry->label.Stripped();
1518 return entry->labelTop;
1538 return entry->defaultKey;
1543 if (tag ==
"audacitykeyboard") {
1547 if (tag ==
"command") {
1551 for (
auto pair : attrs)
1553 auto attr = pair.first;
1554 auto value = pair.second;
1556 if (value.IsStringView())
1558 const wxString strValue = value.ToWString();
1562 else if (attr ==
"key")
1598 xmlFile.StartTag(
wxT(
"audacitykeyboard"));
1599 xmlFile.WriteAttr(
wxT(
"audacityversion"), AUDACITY_VERSION_STRING);
1601 for(
const auto &
entry : mCommandList) {
1603 xmlFile.StartTag(
wxT(
"command"));
1604 xmlFile.WriteAttr(
wxT(
"name"),
entry->name);
1605 xmlFile.WriteAttr(
wxT(
"key"),
entry->key);
1606 xmlFile.EndTag(
wxT(
"command"));
1609 xmlFile.EndTag(
wxT(
"audacitykeyboard"));
1637 entry->flags = flags;
1641void CommandManager::CheckDups()
1644 for (
size_t j = 0; (int)j < cnt; j++) {
1652 for (
size_t i = 0; (int)i < cnt; i++) {
1659 msg.Printf(
wxT(
"key combo '%s' assigned to '%s' and '%s'"),
1693 if (!entry2->key.empty() && entry2->key == entry2->defaultKey) {
1694 if (entry2->key ==
entry->key) {
1695 auto name =
wxT(
"/NewKeys/") + entry2->name.GET();
1698 disabledShortcuts +=
1699 XO(
"\n* %s, because you have assigned the shortcut %s to %s")
1700 .Format(entry2->label.Strip(),
entry->key.GET(),
entry->label.Strip());
1709 " because their default shortcut is new or changed, and is the same shortcut"
1710 " that you have assigned to another command.")
1711 + disabledShortcuts;
1719#include "../KeyboardCapture.h"
1729[]( wxKeyEvent &
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
The output stream of the journal system.
Journal system's error status, command dictionary, initializers.
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)
audacity::BasicSettings * gPrefs
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,...
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
typename GlobalVariable< PreFilter, const std::function< bool(wxKeyEvent &) >, nullptr, Options... >::Scope Scope
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...
virtual bool Flush() noexcept=0
GroupScope BeginGroup(const wxString &prefix)
Appends a prefix to the current group or sets a new absolute path. Group that was set as current befo...
virtual bool HasEntry(const wxString &key) const =0
Checks whether specified key exists within the current group.
virtual bool Write(const wxString &key, bool value)=0
bool ReadBool(const wxString &key, bool defaultValue) const
virtual bool Read(const wxString &key, bool *value) const =0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
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()