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 TranslatableStrings &prefixes,
1044 bool includeMultis)
1045{
1046 for(const auto &entry : mCommandList) {
1047 // GetAllCommandData is used by KeyConfigPrefs.
1048 // It does need the effects.
1049 //if ( entry->isEffect )
1050 // continue;
1051 if ( !entry->multi || includeMultis )
1052 {
1053 names.push_back(entry->name);
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);
1059 }
1060 }
1061}
1062
1064{
1065 if (auto iter = mCommandNumericIDHash.find(id);
1066 iter != mCommandNumericIDHash.end())
1067 return iter->second->name;
1068 return {};
1069}
1070
1072{
1073 if (auto iter = mCommandNameHash.find(name);
1074 iter != mCommandNameHash.end())
1075 return iter->second->longLabel;
1076 return {};
1077}
1078
1081{
1082 if (auto iter = mCommandNameHash.find(name);
1083 iter != mCommandNameHash.end()
1084 ) {
1085 const auto entry = iter->second;
1086 if (!entry->labelPrefix.empty())
1087 return Verbatim( wxT("%s - %s") )
1088 .Format(entry->labelPrefix, entry->label)
1089 .Stripped();
1090 else
1091 return entry->label.Stripped();
1092 }
1093 return {};
1094}
1095
1098{
1099 if (auto iter = mCommandNameHash.find(name);
1100 iter != mCommandNameHash.end())
1101 return iter->second->labelTop;
1102 return {};
1103}
1104
1106{
1107 if (auto iter = mCommandNameHash.find(name);
1108 iter != mCommandNameHash.end())
1109 return iter->second->key;
1110 return {};
1111}
1112
1114const
1115{
1116 if (auto iter = mCommandNameHash.find(name);
1117 iter != mCommandNameHash.end())
1118 return iter->second->defaultKey;
1119 return {};
1120}
1121
1122bool CommandManager::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
1123{
1124 if (tag == "audacitykeyboard") {
1125 mXMLKeysRead = 0;
1126 }
1127
1128 if (tag == "command") {
1129 wxString name;
1131
1132 for (auto pair : attrs)
1133 {
1134 auto attr = pair.first;
1135 auto value = pair.second;
1136
1137 if (value.IsStringView())
1138 {
1139 const wxString strValue = value.ToWString();
1140
1141 if (attr == "name")
1142 name = strValue;
1143 else if (attr == "key")
1144 key = NormalizedKeyString{ strValue };
1145 }
1146 }
1147
1148 if (auto iter = mCommandNameHash.find(name);
1149 iter != mCommandNameHash.end()
1150 ) {
1151 iter->second->key = key;
1152 ++mXMLKeysRead;
1153 }
1154 }
1155
1156 return true;
1157}
1158
1159// This message is displayed now in KeyConfigPrefs::OnImport()
1160void CommandManager::HandleXMLEndTag(const std::string_view& tag)
1161{
1162 /*
1163 if (tag == "audacitykeyboard") {
1164 AudacityMessageBox(
1165 XO("Loaded %d keyboard shortcuts\n")
1166 .Format( mXMLKeysRead ),
1167 XO("Loading Keyboard Shortcuts"),
1168 wxOK | wxCENTRE);
1169 }
1170 */
1171}
1172
1173XMLTagHandler *CommandManager::HandleXMLChild(const std::string_view& WXUNUSED(tag))
1174{
1175 return this;
1176}
1177
1179// may throw
1180{
1181 xmlFile.StartTag(wxT("audacitykeyboard"));
1182 xmlFile.WriteAttr(wxT("audacityversion"), AUDACITY_VERSION_STRING);
1183
1184 for(const auto &entry : mCommandList) {
1185
1186 xmlFile.StartTag(wxT("command"));
1187 xmlFile.WriteAttr(wxT("name"), entry->name);
1188 xmlFile.WriteAttr(wxT("key"), entry->key);
1189 xmlFile.EndTag(wxT("command"));
1190 }
1191
1192 xmlFile.EndTag(wxT("audacitykeyboard"));
1193}
1194
1196{
1197}
1198
1200{
1201}
1202
1204 CommandFlag flags)
1205{
1206 if (auto iter = mCommandNameHash.find(name);
1207 iter != mCommandNameHash.end())
1208 iter->second->flags = flags;
1209}
1210
1211#if defined(_DEBUG)
1212void CommandManager::CheckDups()
1213{
1214 int cnt = mCommandList.size();
1215 for (size_t j = 0; (int)j < cnt; j++) {
1216 if (mCommandList[j]->key.empty()) {
1217 continue;
1218 }
1219
1220 if (mCommandList[j]->allowDup)
1221 continue;
1222
1223 for (size_t i = 0; (int)i < cnt; i++) {
1224 if (i == j) {
1225 continue;
1226 }
1227
1228 if (mCommandList[i]->key == mCommandList[j]->key) {
1229 wxString msg;
1230 msg.Printf(wxT("key combo '%s' assigned to '%s' and '%s'"),
1231 // using GET to form debug message
1232 mCommandList[i]->key.GET(),
1233 mCommandList[i]->label.Debug(),
1234 mCommandList[j]->label.Debug());
1235 wxASSERT_MSG(mCommandList[i]->key != mCommandList[j]->key, msg);
1236 }
1237 }
1238 }
1239}
1240
1241#endif
1242
1243// If a default shortcut of a command is introduced or changed, then this
1244// shortcut may be the same shortcut a user has previously assigned to another
1245// command. This function removes such duplicates by removing the shortcut
1246// from the command whose default has changed.
1247// Note that two commands may have the same shortcut if their default shortcuts
1248// are the same. However, in this function default shortcuts are checked against
1249// user assigned shortcuts. Two such commands with the same shortcut
1250// must both be in either the first or the second group, so there is no need
1251// to test for this case.
1252// Note that if a user is using the full set of default shortcuts, and one
1253// of these is changed, then if /GUI/Shortcuts/FullDefaults is not set in audacity.cfg,
1254// because the defaults appear as user assigned shortcuts in audacity.cfg,
1255// the previous default overrides the changed default, and no duplicate can
1256// be introduced.
1258{
1259 TranslatableString disabledShortcuts;
1260
1261 for (auto& entry : mCommandList) {
1262 if (!entry->key.empty() && entry->key != entry->defaultKey) { // user assigned
1263 for (auto& entry2 : mCommandList) {
1264 if (!entry2->key.empty() && entry2->key == entry2->defaultKey) { // default
1265 if (entry2->key == entry->key) {
1266 auto name = wxT("/NewKeys/") + entry2->name.GET();
1268
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());
1272 }
1273 }
1274 }
1275 }
1276 }
1277
1278 return disabledShortcuts;
1279}
1280
1282{
1283 // This method determines all of the flags that determine whether
1284 // certain menu items and commands should be enabled or disabled,
1285 // and returns them in a bitfield. Note that if none of the flags
1286 // have changed, it's not necessary to even check for updates.
1287
1288 // static variable, used to remember flags for next time.
1289 static CommandFlag lastFlags;
1290
1291 CommandFlag flags, quickFlags;
1292
1293 const auto &options = ReservedCommandFlag::Options();
1294 size_t ii = 0;
1295 for ( const auto &predicate : ReservedCommandFlag::RegisteredPredicates() ) {
1296 if ( options[ii].quickTest ) {
1297 quickFlags[ii] = true;
1298 if( predicate( mProject ) )
1299 flags[ii] = true;
1300 }
1301 ++ii;
1302 }
1303
1304 if (quick)
1305 // quick 'short-circuit' return.
1306 flags = (lastFlags & ~quickFlags) | flags;
1307 else {
1308 ii = 0;
1309 for ( const auto &predicate
1311 if ( !options[ii].quickTest && predicate( mProject ) )
1312 flags[ii] = true;
1313 ++ii;
1314 }
1315 }
1316
1317 lastFlags = flags;
1318 return flags;
1319}
1320
1322 const TranslatableString & Name, CommandFlag & flags, CommandFlag flagsRqd )
1323{
1324 auto &project = mProject;
1325 bool bAllowed = TryToMakeActionAllowed( flags, flagsRqd );
1326 if( bAllowed )
1327 return true;
1328 TellUserWhyDisallowed( Name, flags & flagsRqd, flagsRqd);
1329 return false;
1330}
1331
1336 CommandFlag & flags, CommandFlag flagsRqd )
1337{
1338 auto &project = mProject;
1339
1340 if( flags.none() )
1341 flags = GetUpdateFlags();
1342
1343 // Visit the table of recovery actions
1344 auto &enablers = RegisteredMenuItemEnabler::Enablers();
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);
1350 if (
1351 // Do we have the right precondition?
1352 (flags & actual) == actual
1353 &&
1354 // Can we get the condition we need?
1355 (MissingFlags & enabler.possibleFlags()).any()
1356 ) {
1357 // Then try the function
1358 enabler.tryEnable( project, flagsRqd );
1359 flags = GetUpdateFlags();
1360 }
1361 ++iter;
1362 }
1363 return (flags & flagsRqd) == flagsRqd;
1364}
1365
1367 const TranslatableString & Name, CommandFlag flagsGot, CommandFlag flagsRequired )
1368{
1369 // The default string for 'reason' is a catch all. I hope it won't ever be seen
1370 // and that we will get something more specific.
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.");
1372 // The default title string is 'Disallowed'.
1373 auto untranslatedTitle = XO("Disallowed");
1374 wxString helpPage;
1375
1376 bool enableDefaultMessage = true;
1377 bool defaultMessage = true;
1378
1379 auto doOption = [&](const CommandFlagOptions &options) {
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;
1386 return true;
1387 }
1388 else {
1389 enableDefaultMessage =
1390 enableDefaultMessage && options.enableDefaultMessage;
1391 return false;
1392 }
1393 };
1394
1395 const auto &alloptions = ReservedCommandFlag::Options();
1396 auto missingFlags = flagsRequired & ~flagsGot;
1397
1398 // Find greatest priority
1399 unsigned priority = 0;
1400 for ( const auto &options : alloptions )
1401 priority = std::max( priority, options.priority );
1402
1403 // Visit all unsatisfied conditions' options, by descending priority,
1404 // stopping when we find a message
1405 ++priority;
1406 while( priority-- ) {
1407 size_t ii = 0;
1408 for ( const auto &options : alloptions ) {
1409 if (
1410 priority == options.priority
1411 &&
1412 missingFlags[ii]
1413 &&
1414 doOption( options ) )
1415 goto done;
1416
1417 ++ii;
1418 }
1419 }
1420 done:
1421
1422 if (
1423 // didn't find a message
1424 defaultMessage
1425 &&
1426 // did find a condition that suppresses the default message
1427 !enableDefaultMessage
1428 )
1429 return;
1430
1431 // Does not have the warning icon...
1433 untranslatedTitle,
1434 reason,
1435 helpPage);
1436}
1437
1439{
1440 auto &project = mProject;
1442 auto &undoManager = UndoManager::Get( project );
1443 int cur = undoManager.GetCurrentState();
1444
1445 if (undoManager.UndoAvailable()) {
1446 undoManager.GetShortDescription(cur, &desc);
1447 Modify(wxT("Undo"), XXO("&Undo %s").Format(desc));
1448 Enable(wxT("Undo"), ProjectHistory::Get(project).UndoAvailable());
1449 }
1450 else {
1451 Modify(wxT("Undo"), XXO("&Undo"));
1452 }
1453
1454 if (undoManager.RedoAvailable()) {
1455 undoManager.GetShortDescription(cur+1, &desc);
1456 Modify(wxT("Redo"), XXO("&Redo %s").Format( desc ));
1457 Enable(wxT("Redo"), ProjectHistory::Get(project).RedoAvailable());
1458 }
1459 else {
1460 Modify(wxT("Redo"), XXO("&Redo"));
1461 Enable(wxT("Redo"), false);
1462 }
1463}
1464
1466{
1467 switch (message.type) {
1472 break;
1473 default:
1474 return;
1475 }
1477 UpdateMenus();
1478}
1479
1480// checkActive is a temporary hack that should be removed as soon as we
1481// get multiple effect preview working
1482void CommandManager::UpdateMenus(bool checkActive)
1483{
1484 auto &project = mProject;
1485
1486 bool quick = checkActive && ReallyDoQuickCheck();
1487 auto flags = GetUpdateFlags(quick);
1488 // Return from this function if nothing's changed since
1489 // the last time we were here.
1490 if (flags == mLastFlags)
1491 return;
1492 mLastFlags = flags;
1493
1494 auto flags2 = flags;
1495
1496 // We can enable some extra items if we have select-all-on-none.
1497 //EXPLAIN-ME: Why is this here rather than in GetUpdateFlags()?
1498 //ANSWER: Because flags2 is used in the menu enable/disable.
1499 //The effect still needs flags to determine whether it will need
1500 //to actually do the 'select all' to make the command valid.
1501
1502 for ( const auto &enabler : RegisteredMenuItemEnabler::Enablers() ) {
1503 auto actual = enabler.actualFlags();
1504 if (
1505 enabler.applicable( project ) && (flags & actual) == actual
1506 )
1507 flags2 |= enabler.possibleFlags();
1508 }
1509
1510 // With select-all-on-none, some items that we don't want enabled may have
1511 // been enabled, since we changed the flags. Here we manually disable them.
1512 // 0 is grey out, 1 is Autoselect, 2 is Give warnings.
1514 flags2, // the "lax" flags
1515 (mWhatIfNoSelection == 0 ? flags2 : flags) // the "strict" flags
1516 );
1517
1518 Publish({});
1519}
1520
1522{
1523 return true;
1524}
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:275
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)
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)
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:264
bool IsUsingRtlLayout()
Whether using a right-to-left language layout.
Definition: BasicUI.h:391
static bool IsSection(const GroupItem< RegistryTraits > &item)
Definition: MenuRegistry.h:197
const TranslatableString desc
Definition: ExportPCM.cpp:51
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101
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