74#define MAX_MENU_LEN 20
75#define MAX_SUBMENU_LEN 15
77#define MAX_MENU_LEN 1000
78#define MAX_SUBMENU_LEN 1000
87 std::function<
void()> doSeparator
92 [
this](
auto &item,
auto &){ DoEndGroup(item); },
135 bool bSelectAllIfNone;
136 gPrefs->
Read(
wxT(
"/GUI/SelectAllOnNone"), &bSelectAllIfNone,
false);
152 static const auto list = [] {
154 const char *
const strings[] = {
207 std::vector<NormalizedKeyString> result(
210 std::sort( result.begin(), result.end() );
254 if (
const auto pMenu =
dynamic_cast<const MenuItem*
>( pItem )) {
255 const auto &
title = pMenu->GetTitle();
256 mMenuNames.emplace_back(
title);
259 else if (
const auto pConditionalGroup =
262 const auto flag = (*pConditionalGroup)();
264 bMakingOccultCommands =
true;
265 BeginOccultCommands();
268 mFlags.push_back(
flag);
278 if (
const auto pCommand =
dynamic_cast<const CommandItem*
>(pItem)) {
279 auto &options = pCommand->options;
281 pCommand->name, pCommand->label_in,
282 pCommand->finder, pCommand->callback,
283 pCommand->flags, options);
286 if (
const auto pCommandList =
dynamic_cast<const CommandGroupItem*
>(pItem)) {
287 AddItemList(pCommandList->name,
288 pCommandList->items.data(), pCommandList->items.size(),
289 pCommandList->finder, pCommandList->callback,
290 pCommandList->flags, pCommandList->isEffect);
305 if (
const auto pMenu =
dynamic_cast<const MenuItem*
>(pItem)) {
307 mMenuNames.pop_back();
310 if (
const auto pConditionalGroup =
313 const bool flag = mFlags.back();
316 bMakingOccultCommands =
false;
329 -> std::unique_ptr<CommandListEntry>
331 return std::make_unique<CommandListEntry>();
373 mbSeparatorAllowed =
true;
374 VisitEntry(*
entry, &options);
391 for (
size_t i = 0, cnt = nItems; i < cnt; i++) {
402 entry->flags = flags;
403 mbSeparatorAllowed =
true;
404 VisitEntry(*
entry,
nullptr);
415 NewIdentifier(
name, label_in,
finder, callback,
418 entry->enabled =
false;
419 entry->isGlobal =
true;
421 VisitEntry(*
entry, &options);
426 mbSeparatorAllowed =
false;
434 if((ID >= wxID_LOWEST) && (ID <= wxID_HIGHEST))
457 bool excludeFromMacros =
458 (options.allowInMacros == 0) ||
459 ((options.allowInMacros == -1) &&
label.
MSGID().
GET().Contains(
"..."));
461 const wxString & accel = options.accel;
462 bool bIsEffect = options.bIsEffect;
463 CommandID parameter = options.parameter ==
"" ? nameIn : options.parameter;
466 const auto &longLabel = options.longName;
468 const bool multi = !nameSuffix.
empty();
477 auto entry = AllocateEntry(options);
481 if (MenuNames().
size() > 1)
483 labelPrefix = MenuNames().back().
Stripped();
501 entry->id = mCurrentID;
502 entry->parameter = parameter;
504#if defined(__WXMAC__)
507 if (
name ==
wxT(
"Preferences"))
508 entry->id = wxID_PREFERENCES;
510 entry->id = wxID_EXIT;
512 entry->id = wxID_ABOUT;
519 entry->longLabel = longLabel.empty() ?
label : longLabel;
521 entry->excludeFromMacros = excludeFromMacros;
524 entry->labelPrefix = labelPrefix;
525 entry->labelTop = MenuNames()[0].Stripped();
527 entry->callback = callback;
528 entry->isEffect = bIsEffect;
529 entry->multi = multi;
530 entry->index = index;
531 entry->count = count;
533 entry->enabled =
true;
534 entry->skipKeydown = options.skipKeyDown;
535 entry->wantKeyup = options.wantKeyUp ||
entry->skipKeydown;
536 entry->allowDup = options.allowDup;
537 entry->isGlobal =
false;
538 entry->isOccult = bMakingOccultCommands;
539 entry->checkmarkFn = options.checker;
545 if( std::binary_search( mMaxListOnly.begin(), mMaxListOnly.end(),
552 const auto &path =
entry->name.GET();
559 cm.mCommandList.push_back(std::move(
entry));
568 prev = cm.mCommandNameHash[
entry->name];
575 wxLogDebug(
wxT(
"Command '%s' defined by '%s' and '%s'"),
579 entry->label.Debug());
580 wxFAIL_MSG(wxString::Format(
wxT(
"Command '%s' defined by '%s' and '%s'"),
585 entry->label.Debug()));
591 if (!
entry->key.empty()) {
603 if (
auto pEntry = iter->second) {
604 keyStr = pEntry->key;
606 pLabel = &pEntry->label;
636 entry.Enable(enabled);
638 for (
int i = 1, ID =
entry.id;
646 iter->second->EnableMultiItem(enabled);
648 wxLogDebug(
wxT(
"Warning: Menu entry with id %i not in hash"), ID);
667 Enable(*iter->second, enabled);
669 wxLogDebug(
wxT(
"Warning: Unknown command enabled: '%s'"),
670 (
const wxChar*)
name);
680 wxASSERT( (strictFlags & ~flags).
none() );
685 if(
entry->isOccult )
688 auto useFlags =
entry->useStrictFlags ? strictFlags : flags;
690 if (
entry->flags.any()) {
691 bool enable = ((useFlags &
entry->flags) ==
entry->flags);
701 return iter->second->GetEnabled();
704 wxLogDebug(
wxT(
"Warning: command doesn't exist: '%s'"),
724 iter->second->Check(checked);
736 iter->second->Modify(newLabel);
750 iter->second->key =
key;
770 mark =
wxT(
"\u200f");
772 static const wxString &separatorFormat =
wxT(
"%s / %s");
774 for (
size_t ii = 0; ii < nCommands; ++ii) {
775 const auto &pair = commands[ii];
785 .
Format( mark, pair.Msgid().Stripped() );
787 auto name = pair.Internal();
790 if (!keyStr.empty()){
791 auto keyString = keyStr.Display(
true);
820 CommandFlag flags,
bool alwaysEnabled,
const wxEvent * evt,
829 if (!alwaysEnabled &&
entry->flags.any()) {
831 const auto NiceName =
entry->label.Stripped(
862 (
entry.callback.nonMemberFn)(context);
872 auto lastEffectDesc =
XO(
"Repeat %s").Format(
mNiceName);
873 Modify(
wxT(
"RepeatLastAnalyzer"), lastEffectDesc);
884 auto lastEffectDesc =
XO(
"Repeat %s").Format(
mNiceName);
885 Modify(
wxT(
"RepeatLastTool"), lastEffectDesc);
897 const auto entry = iter->second;
904 (
entry->callback.nonMemberFn)(context);
921 const auto entry = iter->second;
946 if( Str ==
entry->name ||
950 Str ==
entry->labelPrefix.Translation() )
953 entry.get(), flags, alwaysEnabled,
961 if( Str ==
entry->name )
964 entry.get(), flags, alwaysEnabled,
978 auto &cat =
entry->labelTop;
993 wxMenuBar *bar = p->GetMenuBar();
994 size_t cnt = bar->GetMenuCount();
995 for (
size_t i = 0; i < cnt; i++) {
996 cats.push_back(bar->GetMenuLabelText(i));
1006 bool includeMultis)
const
1009 if (
entry->isEffect )
1013 else if( includeMultis )
1019 std::vector<bool> &vExcludeFromMacros,
1020 bool includeMultis)
const
1022 vExcludeFromMacros.clear();
1028 if (
entry->isEffect )
1031 names.push_back(
entry->longLabel), vExcludeFromMacros.push_back(
entry->excludeFromMacros);
1032 else if( includeMultis )
1033 names.push_back(
entry->longLabel), vExcludeFromMacros.push_back(
entry->excludeFromMacros);
1039 std::vector<NormalizedKeyString> &keys,
1040 std::vector<NormalizedKeyString> &default_keys,
1051 if ( !
entry->multi || includeMultis )
1054 keys.push_back(
entry->key);
1055 default_keys.push_back(
entry->defaultKey);
1056 labels.push_back(
entry->label);
1057 categories.push_back(
entry->labelTop);
1058 prefixes.push_back(
entry->labelPrefix);
1067 return iter->second->name;
1075 return iter->second->longLabel;
1085 const auto entry = iter->second;
1086 if (!
entry->labelPrefix.empty())
1091 return entry->label.Stripped();
1101 return iter->second->labelTop;
1109 return iter->second->key;
1118 return iter->second->defaultKey;
1124 if (tag ==
"audacitykeyboard") {
1128 if (tag ==
"command") {
1132 for (
auto pair : attrs)
1134 auto attr = pair.first;
1135 auto value = pair.second;
1137 if (value.IsStringView())
1139 const wxString strValue = value.ToWString();
1143 else if (attr ==
"key")
1151 iter->second->key =
key;
1181 xmlFile.StartTag(
wxT(
"audacitykeyboard"));
1182 xmlFile.WriteAttr(
wxT(
"audacityversion"), AUDACITY_VERSION_STRING);
1186 xmlFile.StartTag(
wxT(
"command"));
1187 xmlFile.WriteAttr(
wxT(
"name"),
entry->name);
1188 xmlFile.WriteAttr(
wxT(
"key"),
entry->key);
1189 xmlFile.EndTag(
wxT(
"command"));
1192 xmlFile.EndTag(
wxT(
"audacitykeyboard"));
1208 iter->second->flags = flags;
1212void CommandManager::CheckDups()
1215 for (
size_t j = 0; (int)j < cnt; j++) {
1223 for (
size_t i = 0; (int)i < cnt; i++) {
1230 msg.Printf(
wxT(
"key combo '%s' assigned to '%s' and '%s'"),
1264 if (!entry2->key.empty() && entry2->key == entry2->defaultKey) {
1265 if (entry2->key ==
entry->key) {
1266 auto name =
wxT(
"/NewKeys/") + entry2->name.GET();
1269 disabledShortcuts +=
1270 XO(
"\n* %s, because you have assigned the shortcut %s to %s")
1271 .Format(entry2->label.Strip(),
entry->key.GET(),
entry->label.Strip());
1278 return disabledShortcuts;
1296 if ( options[ii].quickTest ) {
1297 quickFlags[ii] =
true;
1306 flags = (lastFlags & ~quickFlags) | flags;
1309 for (
const auto &predicate
1311 if ( !options[ii].quickTest && predicate(
mProject ) )
1345 auto iter = enablers.begin(),
end = enablers.end();
1346 while ((flags & flagsRqd) != flagsRqd && iter !=
end) {
1347 const auto &enabler = *iter;
1348 auto actual = enabler.actualFlags();
1349 auto MissingFlags = (~flags & flagsRqd);
1352 (flags & actual) == actual
1355 (MissingFlags & enabler.possibleFlags()).any()
1358 enabler.tryEnable(
project, flagsRqd );
1363 return (flags & flagsRqd) == flagsRqd;
1371 auto reason =
XO(
"There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred.");
1373 auto untranslatedTitle =
XO(
"Disallowed");
1376 bool enableDefaultMessage =
true;
1377 bool defaultMessage =
true;
1380 if ( options.message ) {
1381 reason = options.message( Name );
1382 defaultMessage =
false;
1383 if ( !options.title.empty() )
1384 untranslatedTitle = options.title;
1385 helpPage = options.helpPage;
1389 enableDefaultMessage =
1390 enableDefaultMessage && options.enableDefaultMessage;
1396 auto missingFlags = flagsRequired & ~flagsGot;
1399 unsigned priority = 0;
1400 for (
const auto &options : alloptions )
1401 priority = std::max( priority, options.priority );
1406 while( priority-- ) {
1408 for (
const auto &options : alloptions ) {
1410 priority == options.priority
1414 doOption( options ) )
1427 !enableDefaultMessage
1443 int cur = undoManager.GetCurrentState();
1445 if (undoManager.UndoAvailable()) {
1446 undoManager.GetShortDescription(cur, &
desc);
1454 if (undoManager.RedoAvailable()) {
1455 undoManager.GetShortDescription(cur+1, &
desc);
1467 switch (message.
type) {
1494 auto flags2 = flags;
1503 auto actual = enabler.actualFlags();
1505 enabler.applicable(
project ) && (flags & actual) == actual
1507 flags2 |= enabler.possibleFlags();
@ Internal
Indicates internal failure from Audacity.
Toolkit-neutral facade for basic user interface services.
constexpr CommandFlag AlwaysEnabledFlag
std::bitset< NCommandFlags > CommandFlag
constexpr CommandFlag NoFlagsSpecified
std::function< CommandHandlerObject &(AudacityProject &) > CommandHandlerFinder
static const AudacityProject::AttachedObjects::RegisteredFactory key
XXO("&Cut/Copy/Paste Toolbar")
std::vector< CommandID > CommandIDs
static ProjectFileIORegistry::AttributeWriterEntry entry
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
audacity::BasicSettings * gPrefs
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...
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
TranslatableStrings GetCategories()
int mLastToolRegisteredId
bool GetEnabled(const CommandID &name) const
void Enable(const wxString &name, bool enabled)
void HandleXMLEndTag(const std::string_view &tag) override
void RegisterLastTool(const CommandContext &context)
CommandID GetNameFromNumericID(int id) const
wxString FormatLabelForMenu(const CommandID &id, const TranslatableString *pLabel) const
Format a string appropriate for insertion in a menu.
bool HandleCommandEntry(const CommandListEntry *entry, CommandFlag flags, bool alwaysEnabled, const wxEvent *evt=nullptr, const CommandContext *pGivenContext=nullptr)
CommandNameHash mCommandNameHash
void TellUserWhyDisallowed(const TranslatableString &Name, CommandFlag flagsGot, CommandFlag flagsRequired)
TranslatableString GetCategoryFromName(const CommandID &name) const
TranslatableString DescribeCommandsAndShortcuts(const ComponentInterfaceSymbol commands[], size_t nCommands) const
int mLastAnalyzerRegisteredId
AudacityProject & mProject
void DoRepeatProcess(const CommandContext &context, int)
NormalizedKeyString GetDefaultKeyFromName(const CommandID &name) const
bool TryToMakeActionAllowed(CommandFlag &flags, CommandFlag flagsRqd)
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
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)
CommandManager(AudacityProject &project)
static const TranslatableString COMMAND
int mLastToolRegistration
void OnUndoRedo(struct UndoRedoMessage)
TranslatableString GetLabelFromName(const CommandID &name) const
void GetAllCommandLabels(TranslatableStrings &labels, std::vector< bool > &vExcludeFromMacros, bool includeMultis) const
void ModifyUndoMenuItems()
TextualCommandResult HandleTextualCommand(const CommandID &Str, const CommandContext &context, CommandFlag flags, bool alwaysEnabled)
TranslatableString ReportDuplicateShortcuts()
void UpdatePrefs() override
static int NextIdentifier(int ID)
static const std::vector< NormalizedKeyString > & ExcludedList()
CommandKeyHash mCommandKeyHash
void GetAllCommandData(CommandIDs &names, std::vector< NormalizedKeyString > &keys, std::vector< NormalizedKeyString > &default_keys, TranslatableStrings &labels, TranslatableStrings &categories, TranslatableStrings &prefixes, bool includeMultis)
void SetCommandFlags(const CommandID &name, CommandFlag flags)
void SetKeyFromName(const CommandID &name, const NormalizedKeyString &key)
NormalizedKeyString GetKeyFromName(const CommandID &name) const
TranslatableString GetPrefixedLabelFromName(const CommandID &name) const
AudacityProject & GetProject()
void UpdateMenus(bool checkActive=true)
~CommandManager() override
void Check(const CommandID &name, bool checked)
bool ReportIfActionNotAllowed(const TranslatableString &Name, CommandFlag &flags, CommandFlag flagsRqd)
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
void GetAllCommandNames(CommandIDs &names, bool includeMultis) const
bool HandleMenuID(int id, CommandFlag flags, bool alwaysEnabled)
virtual bool ReallyDoQuickCheck()
Default implementation returns true.
void EnableUsingFlags(CommandFlag flags, CommandFlag strictFlags)
void SetKeyFromIndex(int i, const NormalizedKeyString &key)
int mLastAnalyzerRegistration
int GetNumberOfKeysRead() const
CommandFlag GetUpdateFlags(bool quick=false) const
virtual void ExecuteCommand(const CommandContext &context, const wxEvent *evt, const CommandListEntry &entry)
const Observer::Subscription mUndoSubscription
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.
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
CallbackReturn Publish(const MenuUpdateMessage &message)
Send a message to connected callbacks.
static ProjectHistory & Get(AudacityProject &project)
static const Predicates & RegisteredPredicates()
static const std::vector< CommandFlagOptions > & Options()
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
Maintain a non-persistent list of states of the project, to support undo and redo commands.
static UndoManager & Get(AudacityProject &project)
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...
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
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
bool IsUsingRtlLayout()
Whether using a right-to-left language layout.
const TranslatableString desc
AuthorizationHandler handler
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
virtual ~CommandListEntry()
virtual void UpdateCheckmark(AudacityProject &project)
Default implementation does nothing.
virtual void Enable(bool enabled)
Default implementation simply assigns this->enabled
virtual bool GetEnabled() const
Default implementation simply returns this->enabled
virtual void Check(bool checked)
Default implementation does nothing.
virtual void Modify(const TranslatableString &newLabel)
Default implementation simply assigns this->label
wxString FormatLabelForMenu() const
virtual void EnableMultiItem(bool enabled)
Default implementation simply assigns this->enabled
CommandListEntry * NewIdentifier(const CommandID &name, const TranslatableString &label, CommandHandlerFinder finder, CommandFunctorPointer callback, const CommandID &nameSuffix, int index, int count, const MenuRegistry::Options &options)
std::vector< NormalizedKeyString > mMaxListOnly
void DoVisit(const Registry::SingleItem &item)
virtual std::unique_ptr< CommandListEntry > AllocateEntry(const MenuRegistry::Options &options)
virtual void EndOccultCommands()
std::function< void(const Registry::SingleItem &, const Registry::Path &)> LeafVisitor
void AddGlobalCommand(const CommandID &name, const TranslatableString &label, CommandHandlerFinder finder, CommandFunctorPointer callback, const MenuRegistry::Options &options={})
void AddItem(const CommandID &name, const TranslatableString &label_in, CommandHandlerFinder finder, CommandFunctorPointer callback, CommandFlag flags, const MenuRegistry::Options &options={})
void DoBeginGroup(const MenuRegistry::GroupItem< MenuRegistry::Traits > &item)
virtual void VisitEntry(CommandListEntry &entry, const MenuRegistry::Options *options)
virtual void BeginOccultCommands()
Populator(AudacityProject &project, LeafVisitor leafVisitor, std::function< void()> doSeparator)
void AddItemList(const CommandID &name, const ComponentInterfaceSymbol items[], size_t nItems, CommandHandlerFinder finder, CommandFunctorPointer callback, CommandFlag flags, bool bIsEffect=false)
virtual void BeginMenu(const TranslatableString &tName)
void DoEndGroup(const MenuRegistry::GroupItem< MenuRegistry::Traits > &item)
Has variadic and range constructors that check types.
Common abstract base class for items that are not groups.
Type of message published by UndoManager.
enum UndoRedoMessage::Type type