Audacity 3.2.0
CommandManager.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 CommandManager.cpp
6
7 Brian Gunlogson
8 Dominic Mazzoni
9
10*******************************************************************//****************************************************************//****************************************************************//****************************************************************//******************************************************************/
57#include "CommandManager.h"
58#include "CommandContext.h"
59
60#include <wx/log.h>
61
62#include "BasicUI.h"
63#include "Project.h"
64#include "ProjectHistory.h"
65#include "UndoManager.h"
66
67#include <cassert>
68
69// On wxGTK, there may be many many many plugins, but the menus don't automatically
70// allow for scrolling, so we build sub-menus. If the menu gets longer than
71// MAX_MENU_LEN, we put things in submenus that have MAX_SUBMENU_LEN items in them.
72//
73#ifdef __WXGTK__
74#define MAX_MENU_LEN 20
75#define MAX_SUBMENU_LEN 15
76#else
77#define MAX_MENU_LEN 1000
78#define MAX_SUBMENU_LEN 1000
79#endif
80
82
84
86 LeafVisitor leafVisitor,
87 std::function<void()> doSeparator
88) : Visitor{
89 std::tuple {
90 [this](auto &item, auto &){ DoBeginGroup(item); },
91 move(leafVisitor),
92 [this](auto &item, auto &){ DoEndGroup(item); },
93 },
94 move(doSeparator)
95 }
96 , mProject{ project }
97{
98 // The list of defaults to exclude depends on
99 // preference wxT("/GUI/Shortcuts/FullDefaults"), which may have changed.
100 SetMaxList();
101}
102
104
108};
109
111{
112 return project.AttachedObjects::Get<CommandManager>(key);
113}
114
116{
117 return Get(const_cast<AudacityProject &>(project));
118}
119
124 : mProject{ project }
127{
128 mLastProcessId = 0;
129
130 UpdatePrefs();
131}
132
134{
135 bool bSelectAllIfNone;
136 gPrefs->Read(wxT("/GUI/SelectAllOnNone"), &bSelectAllIfNone, false);
137 // 0 is grey out, 1 is Autoselect, 2 is Give warnings.
138 // Audacity autoselects or warns.
139 mWhatIfNoSelection = bSelectAllIfNone ? 1 : 2;
140}
141
146{
147 PurgeData();
148}
149
150const std::vector<NormalizedKeyString> &CommandManager::ExcludedList()
151{
152 static const auto list = [] {
153 // These short cuts are for the max list only....
154 const char *const strings[] = {
155 // "Ctrl+I",
156 "Ctrl+Alt+I",
157 //"Ctrl+J",
158 "Ctrl+Alt+J",
159 "Ctrl+Alt+V",
160 "Alt+X",
161 "Alt+K",
162 "Shift+Alt+X",
163 "Shift+Alt+K",
164 "Alt+L",
165 "Shift+Alt+C",
166 "Alt+I",
167 "Alt+J",
168 "Shift+Alt+J",
169 "Ctrl+Shift+A",
170 //"Q",
171 //"Shift+J",
172 //"Shift+K",
173 //"Shift+Home",
174 //"Shift+End",
175 "Ctrl+[",
176 "Ctrl+]",
177 "1",
178 "Shift+F5",
179 "Shift+F6",
180 "Shift+F7",
181 "Shift+F8",
182 "Ctrl+Shift+F5",
183 "Ctrl+Shift+F7",
184 "Ctrl+Shift+N",
185 "Ctrl+Shift+M",
186 "Ctrl+Home",
187 "Ctrl+End",
188 "Shift+C",
189 "Alt+Shift+Up",
190 "Alt+Shift+Down",
191 "Shift+P",
192 "Alt+Shift+Left",
193 "Alt+Shift+Right",
194 "Ctrl+Shift+T",
195 //"Command+M",
196 //"Option+Command+M",
197 "Shift+H",
198 "Shift+O",
199 "Shift+I",
200 "Shift+N",
201 "D",
202 "A",
203 "Alt+Shift+F6",
204 "Alt+F6",
205 };
206
207 std::vector<NormalizedKeyString> result(
208 std::begin(strings), std::end(strings)
209 );
210 std::sort( result.begin(), result.end() );
211 return result;
212 }();
213 return list;
214}
215
216// CommandManager needs to know which defaults are standard and which are in the
217// full (max) list.
219{
220 // This list is a DUPLICATE of the list in
221 // KeyConfigPrefs::OnImportDefaults(wxCommandEvent & event)
222
223 // TODO: At a later date get rid of the maxList entirely and
224 // instead use flags in the menu entries to indicate whether the default
225 // shortcut is standard or full.
226
227 mMaxListOnly.clear();
228
229 // if the full list, don't exclude any.
230 bool bFull = gPrefs->ReadBool(wxT("/GUI/Shortcuts/FullDefaults"),false);
231 if( bFull )
232 return;
233
235}
236
237
239{
240 // mCommandList contains unique pointers to CommandListEntrys
241 mCommandList.clear();
242
243 // Then clear the three hashes of dangling pointers
244 mCommandNameHash.clear();
245 mCommandKeyHash.clear();
246 mCommandNumericIDHash.clear();
247}
248
251{
252 using namespace MenuRegistry;
253 auto pItem = &item;
254 if (const auto pMenu = dynamic_cast<const MenuItem*>( pItem )) {
255 const auto &title = pMenu->GetTitle();
256 mMenuNames.emplace_back(title);
257 BeginMenu(title);
258 }
259 else if (const auto pConditionalGroup =
260 dynamic_cast<const ConditionalGroupItem*>( pItem )
261 ) {
262 const auto flag = (*pConditionalGroup)();
263 if (!flag) {
264 bMakingOccultCommands = true;
265 BeginOccultCommands();
266 }
267 // to avoid repeated call of condition predicate in EndGroup():
268 mFlags.push_back(flag);
269 }
270 else
271 assert(IsSection(item));
272}
273
275{
276 using namespace MenuRegistry;
277 auto pItem = &item;
278 if (const auto pCommand = dynamic_cast<const CommandItem*>(pItem)) {
279 auto &options = pCommand->options;
280 AddItem(
281 pCommand->name, pCommand->label_in,
282 pCommand->finder, pCommand->callback,
283 pCommand->flags, options);
284 }
285 else
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);
291 }
292 else
293 wxASSERT( false );
294}
295
297{
298}
299
302{
303 using namespace MenuRegistry;
304 auto pItem = &item;
305 if (const auto pMenu = dynamic_cast<const MenuItem*>(pItem)) {
306 EndMenu();
307 mMenuNames.pop_back();
308 }
309 else
310 if (const auto pConditionalGroup =
311 dynamic_cast<const ConditionalGroupItem*>(pItem)
312 ) {
313 const bool flag = mFlags.back();
314 if (!flag) {
315 EndOccultCommands();
316 bMakingOccultCommands = false;
317 }
318 mFlags.pop_back();
319 }
320 else
321 assert(IsSection(item));
322}
323
325{
326}
327
329 -> std::unique_ptr<CommandListEntry>
330{
331 return std::make_unique<CommandListEntry>();
332}
333
335 const MenuRegistry::Options *)
336{
337}
338
340{
341 for (const auto &entry : mCommandList)
342 entry->UpdateCheckmark(mProject);
343}
344
346{
347}
348
350 const TranslatableString &label_in,
352 CommandFunctorPointer callback,
353 CommandFlag flags,
354 const MenuRegistry::Options &options)
355{
356 if (options.global) {
357 //wxASSERT( flags == AlwaysEnabledFlag );
358 AddGlobalCommand(
359 name, label_in, finder, callback, options );
360 return;
361 }
362
363 wxASSERT( flags != NoFlagsSpecified );
364
366 NewIdentifier(name,
367 label_in,
368 finder, callback,
369 {}, 0, 0,
370 options);
371 entry->useStrictFlags = options.useStrictFlags;
373 mbSeparatorAllowed = true;
374 VisitEntry(*entry, &options);
375}
376
384 const ComponentInterfaceSymbol items[],
385 size_t nItems,
387 CommandFunctorPointer callback,
388 CommandFlag flags,
389 bool bIsEffect)
390{
391 for (size_t i = 0, cnt = nItems; i < cnt; i++) {
393 NewIdentifier(name,
394 items[i].Msgid(),
395 finder,
396 callback,
397 items[i].Internal(),
398 i,
399 cnt,
401 .IsEffect(bIsEffect));
402 entry->flags = flags;
403 mbSeparatorAllowed = true;
404 VisitEntry(*entry, nullptr);
405 }
406}
407
409 const TranslatableString &label_in,
411 CommandFunctorPointer callback,
412 const MenuRegistry::Options &options)
413{
415 NewIdentifier(name, label_in, finder, callback,
416 {}, 0, 0, options);
417
418 entry->enabled = false;
419 entry->isGlobal = true;
420 entry->flags = AlwaysEnabledFlag;
421 VisitEntry(*entry, &options);
422}
423
425{
426 mbSeparatorAllowed = false; // boolean to prevent too many separators.
427}
428
430{
431 ID++;
432
433 //Skip the reserved identifiers used by wxWidgets
434 if((ID >= wxID_LOWEST) && (ID <= wxID_HIGHEST))
435 ID = wxID_HIGHEST+1;
436
437 return ID;
438}
439
448 CommandFunctorPointer callback,
449 const CommandID &nameSuffix,
450 int index,
451 int count,
452 const MenuRegistry::Options &options)
454{
455 auto &cm = Get(mProject);
456
457 bool excludeFromMacros =
458 (options.allowInMacros == 0) ||
459 ((options.allowInMacros == -1) && label.MSGID().GET().Contains("..."));
460
461 const wxString & accel = options.accel;
462 bool bIsEffect = options.bIsEffect;
463 CommandID parameter = options.parameter == "" ? nameIn : options.parameter;
464
465 // if empty, new identifier's long label will be same as label, below:
466 const auto &longLabel = options.longName;
467
468 const bool multi = !nameSuffix.empty();
469 auto name = nameIn;
470
471 // If we have the identifier already, reuse it.
472 CommandListEntry *prev = cm.mCommandNameHash[name];
473 if (prev && prev->label == label && !multi)
474 return prev;
475
476 {
477 auto entry = AllocateEntry(options);
478 assert(entry);
479
480 TranslatableString labelPrefix;
481 if (MenuNames().size() > 1)
482 // submenus only, not main
483 labelPrefix = MenuNames().back().Stripped();
484
485 // For key bindings for commands with a list, such as align,
486 // the name in prefs is the category name plus the effect name.
487 // This feature is not used for built-in effects.
488 if (multi)
489 name = CommandID{ { name, nameSuffix }, wxT('_') };
490
491 // wxMac 2.5 and higher will do special things with the
492 // Preferences, Exit (Quit), and About menu items,
493 // if we give them the right IDs.
494 // Otherwise we just pick increasing ID numbers for each NEW
495 // command. Note that the name string we are comparing
496 // ("About", "Preferences") is the internal command name
497 // (untranslated), not the label that actually appears in the
498 // menu (which might be translated).
499
500 mCurrentID = NextIdentifier(mCurrentID);
501 entry->id = mCurrentID;
502 entry->parameter = parameter;
503
504#if defined(__WXMAC__)
505 // See bug #2642 for some history as to why these items
506 // on Mac have their IDs set explicitly and not others.
507 if (name == wxT("Preferences"))
508 entry->id = wxID_PREFERENCES;
509 else if (name == wxT("Exit"))
510 entry->id = wxID_EXIT;
511 else if (name == wxT("About"))
512 entry->id = wxID_ABOUT;
513#endif
514
515 entry->name = name;
516 entry->label = label;
517
518 // long label is the same as label unless options specified otherwise:
519 entry->longLabel = longLabel.empty() ? label : longLabel;
520
521 entry->excludeFromMacros = excludeFromMacros;
522 entry->key = NormalizedKeyString{ accel.BeforeFirst(wxT('\t')) };
523 entry->defaultKey = entry->key;
524 entry->labelPrefix = labelPrefix;
525 entry->labelTop = MenuNames()[0].Stripped();
526 entry->finder = finder;
527 entry->callback = callback;
528 entry->isEffect = bIsEffect;
529 entry->multi = multi;
530 entry->index = index;
531 entry->count = count;
532 entry->flags = AlwaysEnabledFlag;
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;
540
541 // Exclude accelerators that are in the MaxList.
542 // Note that the default is unaffected, intentionally so.
543 // There are effectively two levels of default, the full (max) list
544 // and the normal reduced list.
545 if( std::binary_search( mMaxListOnly.begin(), mMaxListOnly.end(),
546 entry->key ) )
547 {
548 entry->key = {};
549 }
550 auto newKeysGroup = gPrefs->BeginGroup("/NewKeys");
551 // using GET to interpret CommandID as a config path component
552 const auto &path = entry->name.GET();
553 if (gPrefs->HasEntry(path)) {
554 // Key from preferences overrides the default key given
555 entry->key =
556 NormalizedKeyString{ gPrefs->Read(path, entry->key) };
557 }
558
559 cm.mCommandList.push_back(std::move(entry));
560 // Don't use the variable entry eny more!
561 }
562
563 // New variable
564 CommandListEntry *entry = &*cm.mCommandList.back();
565 cm.mCommandNumericIDHash[entry->id] = entry;
566
567#if defined(_DEBUG)
568 prev = cm.mCommandNameHash[entry->name];
569 if (prev) {
570 // Under Linux it looks as if we may ask for a newID for the same command
571 // more than once. So it's only an error if two different commands
572 // have the exact same name.
573 if( prev->label != entry->label )
574 {
575 wxLogDebug(wxT("Command '%s' defined by '%s' and '%s'"),
576 // using GET in a log message for devs' eyes only
577 entry->name.GET(),
578 prev->label.Debug(),
579 entry->label.Debug());
580 wxFAIL_MSG(wxString::Format(wxT("Command '%s' defined by '%s' and '%s'"),
581 // using GET in an assertion violation message for devs'
582 // eyes only
583 entry->name.GET(),
584 prev->label.Debug(),
585 entry->label.Debug()));
586 }
587 }
588#endif
589 cm.mCommandNameHash[entry->name] = entry;
590
591 if (!entry->key.empty()) {
592 cm.mCommandKeyHash[entry->key] = entry;
593 }
594
595 return entry;
596}
597
599 const CommandID &id, const TranslatableString *pLabel) const
600{
601 NormalizedKeyString keyStr;
602 if (auto iter = mCommandNameHash.find(id); iter != mCommandNameHash.end()) {
603 if (auto pEntry = iter->second) {
604 keyStr = pEntry->key;
605 if (!pLabel)
606 pLabel = &pEntry->label;
607 }
608 }
609 if (pLabel)
610 return CommandListEntry::FormatLabelForMenu(*pLabel, keyStr);
611 return {};
612}
613
615 const TranslatableString &translatableLabel,
616 const NormalizedKeyString &keyStr)
617{
618 auto label = translatableLabel.Translation();
619 auto key = keyStr.GET();
620 if (!key.empty())
621 {
622 // using GET to compose menu item name for wxWidgets
623 label += wxT("\t") + key;
624 }
625
626 return label;
627}
628
635{
636 entry.Enable(enabled);
637 if (entry.multi) {
638 for (int i = 1, ID = entry.id;
639 i < entry.count;
640 ++i, ID = NextIdentifier(ID)
641 ) {
642 // This menu item is not necessarily in the same menu, because
643 // multi-items can be spread across multiple sub menus
644 if (auto iter = mCommandNumericIDHash.find(ID);
645 iter != mCommandNumericIDHash.end())
646 iter->second->EnableMultiItem(enabled);
647 else
648 wxLogDebug(wxT("Warning: Menu entry with id %i not in hash"), ID);
649 }
650 }
651}
652
654{
655 enabled = b;
656}
657
659{
660 enabled = b;
661}
662
663void CommandManager::Enable(const wxString &name, bool enabled)
664{
665 if (auto iter = mCommandNameHash.find(name);
666 iter != mCommandNameHash.end())
667 Enable(*iter->second, enabled);
668 else
669 wxLogDebug(wxT("Warning: Unknown command enabled: '%s'"),
670 (const wxChar*)name);
671}
672
674 CommandFlag flags, CommandFlag strictFlags)
675{
676 // strictFlags are a subset of flags. strictFlags represent the real
677 // conditions now, but flags are the conditions that could be made true.
678 // Some commands use strict flags only, refusing the chance to fix
679 // conditions
680 wxASSERT( (strictFlags & ~flags).none() );
681
682 for(const auto &entry : mCommandList) {
683 if (entry->multi && entry->index != 0)
684 continue;
685 if( entry->isOccult )
686 continue;
687
688 auto useFlags = entry->useStrictFlags ? strictFlags : flags;
689
690 if (entry->flags.any()) {
691 bool enable = ((useFlags & entry->flags) == entry->flags);
692 Enable(*entry, enable);
693 }
694 }
695}
696
698{
699 if (auto iter = mCommandNameHash.find(name);
700 iter != mCommandNameHash.end())
701 return iter->second->GetEnabled();
702 else {
703 // using GET in a log message for devs' eyes only
704 wxLogDebug(wxT("Warning: command doesn't exist: '%s'"),
705 name.GET());
706 return false;
707 }
708}
709
711{
712 return enabled;
713}
714
716{
717 return mXMLKeysRead;
718}
719
720void CommandManager::Check(const CommandID &name, bool checked)
721{
722 if (auto iter = mCommandNameHash.find(name);
723 iter != mCommandNameHash.end())
724 iter->second->Check(checked);
725}
726
728{
729}
730
732void CommandManager::Modify(const wxString &name, const TranslatableString &newLabel)
733{
734 if (auto iter = mCommandNameHash.find(name);
735 iter != mCommandNameHash.end())
736 iter->second->Modify(newLabel);
737}
738
740{
741 label = newLabel;
742}
743
744
747{
748 if (auto iter = mCommandNameHash.find(name);
749 iter != mCommandNameHash.end())
750 iter->second->key = key;
751}
752
754{
755 if (!(0 <= i && i < NCommands())) {
756 assert(false);
757 return;
758 }
759 const auto &entry = mCommandList[i];
760 entry->key = key;
761}
762
764 const ComponentInterfaceSymbol commands[], size_t nCommands) const
765{
766 wxString mark;
767 // This depends on the language setting and may change in-session after
768 // change of preferences:
770 mark = wxT("\u200f");
771
772 static const wxString &separatorFormat = wxT("%s / %s");
773 TranslatableString result;
774 for (size_t ii = 0; ii < nCommands; ++ii) {
775 const auto &pair = commands[ii];
776 // If RTL, then the control character forces right-to-left sequencing of
777 // "/" -separated command names, and puts any "(...)" shortcuts to the
778 // left, consistently with accelerators in menus (assuming matching
779 // operating system preferences for language), even if the command name
780 // was missing from the translation file and defaulted to the English.
781
782 // Note: not putting this and other short format strings in the
783 // translation catalogs
784 auto piece = Verbatim( wxT("%s%s") )
785 .Format( mark, pair.Msgid().Stripped() );
786
787 auto name = pair.Internal();
788 if (!name.empty()) {
789 auto keyStr = GetKeyFromName(name);
790 if (!keyStr.empty()){
791 auto keyString = keyStr.Display(true);
792 auto format = wxT("%s %s(%s)");
793#ifdef __WXMAC__
794 // The unicode controls push and pop left-to-right embedding.
795 // This keeps the directionally weak characters, such as uparrow
796 // for Shift, left of the key name,
797 // consistently with how menu accelerators appear, even when the
798 // system language is RTL.
799 format = wxT("%s %s(\u202a%s\u202c)");
800#endif
801 // The mark makes correctly placed parentheses for RTL, even
802 // in the case that the piece is untranslated.
803 piece = Verbatim( format ).Format( piece, mark, keyString );
804 }
805 }
806
807 if (result.empty())
808 result = piece;
809 else
810 result = Verbatim( separatorFormat ).Format( result, piece );
811 }
812 return result;
813}
814
820 CommandFlag flags, bool alwaysEnabled, const wxEvent * evt,
821 const CommandContext *pGivenContext)
822{
823 if (!entry )
824 return false;
825
826 if (flags != AlwaysEnabledFlag && !entry->enabled)
827 return false;
828
829 if (!alwaysEnabled && entry->flags.any()) {
830
831 const auto NiceName = entry->label.Stripped(
833 // NB: The call may have the side effect of changing flags.
834 bool allowed = ReportIfActionNotAllowed(NiceName, flags, entry->flags);
835 // If the function was disallowed, it STILL should count as having been
836 // handled (by doing nothing or by telling the user of the problem).
837 // Otherwise we may get other handlers having a go at obeying the command.
838 if (!allowed)
839 return true;
840 mNiceName = NiceName;
841 }
842 else {
843 mNiceName = {};
844 }
845
846 CommandContext context{ mProject, evt, entry->index, entry->parameter };
847 if (pGivenContext)
848 context.temporarySelection = pGivenContext->temporarySelection;
849 ExecuteCommand(context, evt, *entry);
850 return true;
851}
852
854 const wxEvent *evt, const CommandListEntry &entry)
855{
856 // Discriminate the union entry->callback by entry->finder
857 if (auto &finder = entry.finder) {
858 auto &handler = finder(mProject);
859 (handler.*(entry.callback.memberFn))(context);
860 }
861 else
862 (entry.callback.nonMemberFn)(context);
863 mLastProcessId = 0;
864}
865
866// Called by Contrast and Plot Spectrum Plug-ins to mark them as Last Analzers.
867// Note that Repeat data has previously been collected
869 if (mLastProcessId != 0) {
872 auto lastEffectDesc = XO("Repeat %s").Format(mNiceName);
873 Modify(wxT("RepeatLastAnalyzer"), lastEffectDesc);
874 }
875 return;
876}
877
878// Called by Selected Tools to mark them as Last Tools.
879// Note that Repeat data has previously been collected
881 if (mLastProcessId != 0) {
884 auto lastEffectDesc = XO("Repeat %s").Format(mNiceName);
885 Modify(wxT("RepeatLastTool"), lastEffectDesc);
886 }
887 return;
888}
889
890// Used to invoke Repeat Last Analyzer Process for built-in, non-nyquist plug-ins.
892{
893 mLastProcessId = 0; //Don't Process this as repeat
894 if (auto iter = mCommandNumericIDHash.find(id);
895 iter != mCommandNumericIDHash.end()
896 ) {
897 const auto entry = iter->second;
898 // Discriminate the union entry->callback by entry->finder
899 if (auto &finder = entry->finder) {
900 auto &handler = finder(context.project);
901 (handler.*(entry->callback.memberFn))(context);
902 }
903 else
904 (entry->callback.nonMemberFn)(context);
905 }
906}
907
908
915 int id, CommandFlag flags, bool alwaysEnabled)
916{
918 if (auto iter = mCommandNumericIDHash.find(id);
919 iter != mCommandNumericIDHash.end()
920 ) {
921 const auto entry = iter->second;
922 if (GlobalMenuHook::Call(entry->name))
923 return true;
924
925 return HandleCommandEntry(entry, flags, alwaysEnabled);
926 }
927 return false;
928}
929
935 const CommandContext & context, CommandFlag flags, bool alwaysEnabled)
936{
937 assert(&context.project == &GetProject());
938 if( Str.empty() )
939 return CommandFailure;
940 // Linear search for now...
941 for (const auto &entry : mCommandList)
942 {
943 if (!entry->multi)
944 {
945 // Testing against labelPrefix too allows us to call Nyquist functions by name.
946 if( Str == entry->name ||
947 // PRL: uh oh, mixing internal string (Str) with user-visible
948 // (labelPrefix, which was initialized from a user-visible
949 // sub-menu name)
950 Str == entry->labelPrefix.Translation() )
951 {
952 return HandleCommandEntry(
953 entry.get(), flags, alwaysEnabled,
954 nullptr, &context)
956 }
957 }
958 else
959 {
960 // Handle multis too...
961 if( Str == entry->name )
962 {
963 return HandleCommandEntry(
964 entry.get(), flags, alwaysEnabled,
965 nullptr, &context)
967 }
968 }
969 }
970 return CommandNotFound;
971}
972
974{
976
977 for (const auto &entry : mCommandList) {
978 auto &cat = entry->labelTop;
979 if ( ! make_iterator_range( cats ).contains(cat) ) {
980 cats.push_back(cat);
981 }
982 }
983#if 0
984 mCommandList.size(); i++) {
985 if (includeMultis || !mCommandList[i]->multi)
986 names.push_back(mCommandList[i]->name);
987 }
988
989 if (p == NULL) {
990 return;
991 }
992
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));
997 }
998
999 cats.push_back(COMMAND);
1000#endif
1001
1002 return cats;
1003}
1004
1006 bool includeMultis) const
1007{
1008 for(const auto &entry : mCommandList) {
1009 if ( entry->isEffect )
1010 continue;
1011 if (!entry->multi)
1012 names.push_back(entry->name);
1013 else if( includeMultis )
1014 names.push_back(entry->name );// + wxT(":")/*+ mCommandList[i]->label*/);
1015 }
1016}
1017
1019 std::vector<bool> &vExcludeFromMacros,
1020 bool includeMultis) const
1021{
1022 vExcludeFromMacros.clear();
1023 for(const auto &entry : mCommandList) {
1024 // This is fetching commands from the menus, for use as batch commands.
1025 // Until we have properly merged EffectManager and CommandManager
1026 // we explicitly exclude effects, as they are already handled by the
1027 // effects Manager.
1028 if ( entry->isEffect )
1029 continue;
1030 if (!entry->multi)
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);
1034 }
1035}
1036
1039 std::vector<NormalizedKeyString> &keys,
1040 std::vector<NormalizedKeyString> &default_keys,
1041 TranslatableStrings &labels,
1042 TranslatableStrings &categories,
1043#if defined(EXPERIMENTAL_KEY_VIEW)
1044 TranslatableStrings &prefixes,
1045#endif
1046 bool includeMultis)
1047{
1048 for(const auto &entry : mCommandList) {
1049 // GetAllCommandData is used by KeyConfigPrefs.
1050 // It does need the effects.
1051 //if ( entry->isEffect )
1052 // continue;
1053 if ( !entry->multi || includeMultis )
1054 {
1055 names.push_back(entry->name);
1056 keys.push_back(entry->key);
1057 default_keys.push_back(entry->defaultKey);
1058 labels.push_back(entry->label);
1059 categories.push_back(entry->labelTop);
1060#if defined(EXPERIMENTAL_KEY_VIEW)
1061 prefixes.push_back(entry->labelPrefix);
1062#endif
1063 }
1064 }
1065}
1066
1068{
1069 if (auto iter = mCommandNumericIDHash.find(id);
1070 iter != mCommandNumericIDHash.end())
1071 return iter->second->name;
1072 return {};
1073}
1074
1076{
1077 if (auto iter = mCommandNameHash.find(name);
1078 iter != mCommandNameHash.end())
1079 return iter->second->longLabel;
1080 return {};
1081}
1082
1085{
1086 if (auto iter = mCommandNameHash.find(name);
1087 iter != mCommandNameHash.end()
1088 ) {
1089 const auto entry = iter->second;
1090 if (!entry->labelPrefix.empty())
1091 return Verbatim( wxT("%s - %s") )
1092 .Format(entry->labelPrefix, entry->label)
1093 .Stripped();
1094 else
1095 return entry->label.Stripped();
1096 }
1097 return {};
1098}
1099
1102{
1103 if (auto iter = mCommandNameHash.find(name);
1104 iter != mCommandNameHash.end())
1105 return iter->second->labelTop;
1106 return {};
1107}
1108
1110{
1111 if (auto iter = mCommandNameHash.find(name);
1112 iter != mCommandNameHash.end())
1113 return iter->second->key;
1114 return {};
1115}
1116
1118const
1119{
1120 if (auto iter = mCommandNameHash.find(name);
1121 iter != mCommandNameHash.end())
1122 return iter->second->defaultKey;
1123 return {};
1124}
1125
1126bool CommandManager::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
1127{
1128 if (tag == "audacitykeyboard") {
1129 mXMLKeysRead = 0;
1130 }
1131
1132 if (tag == "command") {
1133 wxString name;
1135
1136 for (auto pair : attrs)
1137 {
1138 auto attr = pair.first;
1139 auto value = pair.second;
1140
1141 if (value.IsStringView())
1142 {
1143 const wxString strValue = value.ToWString();
1144
1145 if (attr == "name")
1146 name = strValue;
1147 else if (attr == "key")
1148 key = NormalizedKeyString{ strValue };
1149 }
1150 }
1151
1152 if (auto iter = mCommandNameHash.find(name);
1153 iter != mCommandNameHash.end()
1154 ) {
1155 iter->second->key = key;
1156 ++mXMLKeysRead;
1157 }
1158 }
1159
1160 return true;
1161}
1162
1163// This message is displayed now in KeyConfigPrefs::OnImport()
1164void CommandManager::HandleXMLEndTag(const std::string_view& tag)
1165{
1166 /*
1167 if (tag == "audacitykeyboard") {
1168 AudacityMessageBox(
1169 XO("Loaded %d keyboard shortcuts\n")
1170 .Format( mXMLKeysRead ),
1171 XO("Loading Keyboard Shortcuts"),
1172 wxOK | wxCENTRE);
1173 }
1174 */
1175}
1176
1177XMLTagHandler *CommandManager::HandleXMLChild(const std::string_view& WXUNUSED(tag))
1178{
1179 return this;
1180}
1181
1183// may throw
1184{
1185 xmlFile.StartTag(wxT("audacitykeyboard"));
1186 xmlFile.WriteAttr(wxT("audacityversion"), AUDACITY_VERSION_STRING);
1187
1188 for(const auto &entry : mCommandList) {
1189
1190 xmlFile.StartTag(wxT("command"));
1191 xmlFile.WriteAttr(wxT("name"), entry->name);
1192 xmlFile.WriteAttr(wxT("key"), entry->key);
1193 xmlFile.EndTag(wxT("command"));
1194 }
1195
1196 xmlFile.EndTag(wxT("audacitykeyboard"));
1197}
1198
1200{
1201}
1202
1204{
1205}
1206
1208 CommandFlag flags)
1209{
1210 if (auto iter = mCommandNameHash.find(name);
1211 iter != mCommandNameHash.end())
1212 iter->second->flags = flags;
1213}
1214
1215#if defined(_DEBUG)
1216void CommandManager::CheckDups()
1217{
1218 int cnt = mCommandList.size();
1219 for (size_t j = 0; (int)j < cnt; j++) {
1220 if (mCommandList[j]->key.empty()) {
1221 continue;
1222 }
1223
1224 if (mCommandList[j]->allowDup)
1225 continue;
1226
1227 for (size_t i = 0; (int)i < cnt; i++) {
1228 if (i == j) {
1229 continue;
1230 }
1231
1232 if (mCommandList[i]->key == mCommandList[j]->key) {
1233 wxString msg;
1234 msg.Printf(wxT("key combo '%s' assigned to '%s' and '%s'"),
1235 // using GET to form debug message
1236 mCommandList[i]->key.GET(),
1237 mCommandList[i]->label.Debug(),
1238 mCommandList[j]->label.Debug());
1239 wxASSERT_MSG(mCommandList[i]->key != mCommandList[j]->key, msg);
1240 }
1241 }
1242 }
1243}
1244
1245#endif
1246
1247// If a default shortcut of a command is introduced or changed, then this
1248// shortcut may be the same shortcut a user has previously assigned to another
1249// command. This function removes such duplicates by removing the shortcut
1250// from the command whose default has changed.
1251// Note that two commands may have the same shortcut if their default shortcuts
1252// are the same. However, in this function default shortcuts are checked against
1253// user assigned shortcuts. Two such commands with the same shortcut
1254// must both be in either the first or the second group, so there is no need
1255// to test for this case.
1256// Note that if a user is using the full set of default shortcuts, and one
1257// of these is changed, then if /GUI/Shortcuts/FullDefaults is not set in audacity.cfg,
1258// because the defaults appear as user assigned shortcuts in audacity.cfg,
1259// the previous default overrides the changed default, and no duplicate can
1260// be introduced.
1262{
1263 TranslatableString disabledShortcuts;
1264
1265 for (auto& entry : mCommandList) {
1266 if (!entry->key.empty() && entry->key != entry->defaultKey) { // user assigned
1267 for (auto& entry2 : mCommandList) {
1268 if (!entry2->key.empty() && entry2->key == entry2->defaultKey) { // default
1269 if (entry2->key == entry->key) {
1270 auto name = wxT("/NewKeys/") + entry2->name.GET();
1272
1273 disabledShortcuts +=
1274 XO("\n* %s, because you have assigned the shortcut %s to %s")
1275 .Format(entry2->label.Strip(), entry->key.GET(), entry->label.Strip());
1276 }
1277 }
1278 }
1279 }
1280 }
1281
1282 return disabledShortcuts;
1283}
1284
1286{
1287 // This method determines all of the flags that determine whether
1288 // certain menu items and commands should be enabled or disabled,
1289 // and returns them in a bitfield. Note that if none of the flags
1290 // have changed, it's not necessary to even check for updates.
1291
1292 // static variable, used to remember flags for next time.
1293 static CommandFlag lastFlags;
1294
1295 CommandFlag flags, quickFlags;
1296
1297 const auto &options = ReservedCommandFlag::Options();
1298 size_t ii = 0;
1299 for ( const auto &predicate : ReservedCommandFlag::RegisteredPredicates() ) {
1300 if ( options[ii].quickTest ) {
1301 quickFlags[ii] = true;
1302 if( predicate( mProject ) )
1303 flags[ii] = true;
1304 }
1305 ++ii;
1306 }
1307
1308 if (quick)
1309 // quick 'short-circuit' return.
1310 flags = (lastFlags & ~quickFlags) | flags;
1311 else {
1312 ii = 0;
1313 for ( const auto &predicate
1315 if ( !options[ii].quickTest && predicate( mProject ) )
1316 flags[ii] = true;
1317 ++ii;
1318 }
1319 }
1320
1321 lastFlags = flags;
1322 return flags;
1323}
1324
1326 const TranslatableString & Name, CommandFlag & flags, CommandFlag flagsRqd )
1327{
1328 auto &project = mProject;
1329 bool bAllowed = TryToMakeActionAllowed( flags, flagsRqd );
1330 if( bAllowed )
1331 return true;
1332 TellUserWhyDisallowed( Name, flags & flagsRqd, flagsRqd);
1333 return false;
1334}
1335
1340 CommandFlag & flags, CommandFlag flagsRqd )
1341{
1342 auto &project = mProject;
1343
1344 if( flags.none() )
1345 flags = GetUpdateFlags();
1346
1347 // Visit the table of recovery actions
1348 auto &enablers = RegisteredMenuItemEnabler::Enablers();
1349 auto iter = enablers.begin(), end = enablers.end();
1350 while ((flags & flagsRqd) != flagsRqd && iter != end) {
1351 const auto &enabler = *iter;
1352 auto actual = enabler.actualFlags();
1353 auto MissingFlags = (~flags & flagsRqd);
1354 if (
1355 // Do we have the right precondition?
1356 (flags & actual) == actual
1357 &&
1358 // Can we get the condition we need?
1359 (MissingFlags & enabler.possibleFlags()).any()
1360 ) {
1361 // Then try the function
1362 enabler.tryEnable( project, flagsRqd );
1363 flags = GetUpdateFlags();
1364 }
1365 ++iter;
1366 }
1367 return (flags & flagsRqd) == flagsRqd;
1368}
1369
1371 const TranslatableString & Name, CommandFlag flagsGot, CommandFlag flagsRequired )
1372{
1373 // The default string for 'reason' is a catch all. I hope it won't ever be seen
1374 // and that we will get something more specific.
1375 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.");
1376 // The default title string is 'Disallowed'.
1377 auto untranslatedTitle = XO("Disallowed");
1378 wxString helpPage;
1379
1380 bool enableDefaultMessage = true;
1381 bool defaultMessage = true;
1382
1383 auto doOption = [&](const CommandFlagOptions &options) {
1384 if ( options.message ) {
1385 reason = options.message( Name );
1386 defaultMessage = false;
1387 if ( !options.title.empty() )
1388 untranslatedTitle = options.title;
1389 helpPage = options.helpPage;
1390 return true;
1391 }
1392 else {
1393 enableDefaultMessage =
1394 enableDefaultMessage && options.enableDefaultMessage;
1395 return false;
1396 }
1397 };
1398
1399 const auto &alloptions = ReservedCommandFlag::Options();
1400 auto missingFlags = flagsRequired & ~flagsGot;
1401
1402 // Find greatest priority
1403 unsigned priority = 0;
1404 for ( const auto &options : alloptions )
1405 priority = std::max( priority, options.priority );
1406
1407 // Visit all unsatisfied conditions' options, by descending priority,
1408 // stopping when we find a message
1409 ++priority;
1410 while( priority-- ) {
1411 size_t ii = 0;
1412 for ( const auto &options : alloptions ) {
1413 if (
1414 priority == options.priority
1415 &&
1416 missingFlags[ii]
1417 &&
1418 doOption( options ) )
1419 goto done;
1420
1421 ++ii;
1422 }
1423 }
1424 done:
1425
1426 if (
1427 // didn't find a message
1428 defaultMessage
1429 &&
1430 // did find a condition that suppresses the default message
1431 !enableDefaultMessage
1432 )
1433 return;
1434
1435 // Does not have the warning icon...
1437 untranslatedTitle,
1438 reason,
1439 helpPage);
1440}
1441
1443{
1444 auto &project = mProject;
1446 auto &undoManager = UndoManager::Get( project );
1447 int cur = undoManager.GetCurrentState();
1448
1449 if (undoManager.UndoAvailable()) {
1450 undoManager.GetShortDescription(cur, &desc);
1451 Modify(wxT("Undo"), XXO("&Undo %s").Format(desc));
1452 Enable(wxT("Undo"), ProjectHistory::Get(project).UndoAvailable());
1453 }
1454 else {
1455 Modify(wxT("Undo"), XXO("&Undo"));
1456 }
1457
1458 if (undoManager.RedoAvailable()) {
1459 undoManager.GetShortDescription(cur+1, &desc);
1460 Modify(wxT("Redo"), XXO("&Redo %s").Format( desc ));
1461 Enable(wxT("Redo"), ProjectHistory::Get(project).RedoAvailable());
1462 }
1463 else {
1464 Modify(wxT("Redo"), XXO("&Redo"));
1465 Enable(wxT("Redo"), false);
1466 }
1467}
1468
1470{
1471 switch (message.type) {
1476 break;
1477 default:
1478 return;
1479 }
1481 UpdateMenus();
1482}
1483
1484// checkActive is a temporary hack that should be removed as soon as we
1485// get multiple effect preview working
1486void CommandManager::UpdateMenus(bool checkActive)
1487{
1488 auto &project = mProject;
1489
1490 bool quick = checkActive && ReallyDoQuickCheck();
1491 auto flags = GetUpdateFlags(quick);
1492 // Return from this function if nothing's changed since
1493 // the last time we were here.
1494 if (flags == mLastFlags)
1495 return;
1496 mLastFlags = flags;
1497
1498 auto flags2 = flags;
1499
1500 // We can enable some extra items if we have select-all-on-none.
1501 //EXPLAIN-ME: Why is this here rather than in GetUpdateFlags()?
1502 //ANSWER: Because flags2 is used in the menu enable/disable.
1503 //The effect still needs flags to determine whether it will need
1504 //to actually do the 'select all' to make the command valid.
1505
1506 for ( const auto &enabler : RegisteredMenuItemEnabler::Enablers() ) {
1507 auto actual = enabler.actualFlags();
1508 if (
1509 enabler.applicable( project ) && (flags & actual) == actual
1510 )
1511 flags2 |= enabler.possibleFlags();
1512 }
1513
1514 // With select-all-on-none, some items that we don't want enabled may have
1515 // been enabled, since we changed the flags. Here we manually disable them.
1516 // 0 is grey out, 1 is Autoselect, 2 is Give warnings.
1518 flags2, // the "lax" flags
1519 (mWhatIfNoSelection == 0 ? flags2 : flags) // the "strict" flags
1520 );
1521
1522 Publish({});
1523}
1524
1526{
1527 return true;
1528}
wxT("CloseDown"))
@ Internal
Indicates internal failure from Audacity.
Toolkit-neutral facade for basic user interface services.
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:34
std::bitset< NCommandFlags > CommandFlag
Definition: CommandFlag.h:30
constexpr CommandFlag NoFlagsSpecified
Definition: CommandFlag.h:35
std::function< CommandHandlerObject &(AudacityProject &) > CommandHandlerFinder
static const AudacityProject::AttachedObjects::RegisteredFactory key
const TranslatableString name
Definition: Distortion.cpp:76
@ none
Definition: Dither.h:20
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
std::vector< CommandID > CommandIDs
Definition: Identifier.h:233
static ProjectFileIORegistry::AttributeWriterEntry entry
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: IteratorX.h:210
static const auto title
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
TranslatableString label
Definition: TagsEditor.cpp:165
static TranslatableStrings names
Definition: TagsEditor.cpp:153
const auto project
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
int id
static std::once_flag flag
std::vector< Attribute > AttributesList
Definition: XMLTagHandler.h:40
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:274
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()
CommandList mCommandList
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
void OnUndoRedo(struct UndoRedoMessage)
TranslatableString GetLabelFromName(const CommandID &name) const
void GetAllCommandLabels(TranslatableStrings &labels, std::vector< bool > &vExcludeFromMacros, bool includeMultis) const
TextualCommandResult HandleTextualCommand(const CommandID &Str, const CommandContext &context, CommandFlag flags, bool alwaysEnabled)
TranslatableString ReportDuplicateShortcuts()
void UpdatePrefs() override
static int NextIdentifier(int ID)
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
void SetCommandFlags(const CommandID &name, CommandFlag flags)
size_t NCommands() const
void SetKeyFromName(const CommandID &name, const NormalizedKeyString &key)
CommandFlag mLastFlags
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,...
Abstract base class used in importing a file.
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
bool empty() const
Definition: Identifier.h:61
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Definition: Identifier.h:66
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
CallbackReturn Publish(const MenuUpdateMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
static ProjectHistory & Get(AudacityProject &project)
static const Predicates & RegisteredPredicates()
Definition: CommandFlag.cpp:27
static const std::vector< CommandFlagOptions > & Options()
Definition: CommandFlag.cpp:32
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.
Definition: UndoManager.h:155
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:42
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
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.
Definition: BasicUI.h:262
bool IsUsingRtlLayout()
Whether using a right-to-left language layout.
Definition: BasicUI.h:389
static bool IsSection(const GroupItem< RegistryTraits > &item)
Definition: MenuRegistry.h:197
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
const TranslatableString desc
Definition: ExportPCM.cpp:51
auto finder(TrackId id, int &distance)
STL namespace.
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
TranslatableString label
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)
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)
Options && IsEffect(bool value=true) &&
Definition: MenuRegistry.h:50
static const MenuItemEnablers & Enablers()
Definition: CommandFlag.cpp:56
Has variadic and range constructors that check types.
Definition: Registry.h:292
Common abstract base class for items that are not groups.
Definition: Registry.h:224
Type of message published by UndoManager.
Definition: UndoManager.h:55
enum UndoRedoMessage::Type type