Audacity 3.2.0
MenuCreator.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 MenuCreator.cpp
6
7 Dominic Mazzoni
8 Brian Gunlogson
9 et al.
10
11 Paul Licameli split from Menus.cpp
12
13*******************************************************************//****************************************************************//*******************************************************************/
24#include "MenuCreator.h"
25
26#include "ActiveProject.h"
27#include "CommandContext.h"
29#include "KeyboardCapture.h"
30#include "Journal.h"
31#include "JournalOutput.h"
32#include "JournalRegistry.h"
33#include "Registry.h"
34#include "ProjectWindows.h"
35#include "AudacityMessageBox.h"
36
37#include <wx/evtloop.h>
38#include <wx/frame.h>
39#include <wx/log.h>
40#include <wx/menu.h>
41#include <wx/windowptr.h>
42
43namespace {
45{
46 MenuBarListEntry(const wxString &name, wxMenuBar *menubar);
48
49 wxString name;
50 wxWeakRef<wxMenuBar> menubar; // This structure does not assume memory ownership!
51};
52
54{
58
60 std::unique_ptr<wxMenu> menu;
61};
62
63MenuBarListEntry::MenuBarListEntry(const wxString &name, wxMenuBar *menubar)
64 : name{ name }, menubar{ menubar }
65{
66}
67
69{
70}
71
73 : menu{ std::make_unique<wxMenu>() }
74{
75}
76
78{
79}
80}
81
83
86{
87}
88
90
92{
93 return static_cast<MenuCreator&>(CommandManager::Get(project));
94}
95
97{
98 return static_cast<const MenuCreator&>(CommandManager::Get(project));
99}
100
104
105namespace {
106
107using namespace MenuRegistry;
108
111 : CommandManager::Populator { proj,
112 // leaf visit
113 [this](const auto &item, const auto&) {
114 const auto pCurrentMenu = CurrentMenu();
115 if (!pCurrentMenu) {
116 // There may have been a mistake in the placement hint that
117 // registered this single item. It's not within any menu.
118 assert(false);
119 }
120 else TypeSwitch::VDispatch<void, LeafTypes>(item,
121 [&](const SpecialItem &special) {
122 if (auto pSpecial =
123 dynamic_cast<const MenuCreator::SpecialItem*>(&special))
124 pSpecial->fn(mProject, *pCurrentMenu);
125 },
126 [this](auto &item){ DoVisit(item); }
127 );
128 },
129
130 [this]{
131 if (mbSeparatorAllowed)
132 CurrentMenu()->AppendSeparator();
133 Populator::DoSeparator();
134 }
135 }
136 {
137 auto menubar = AddMenuBar(wxT("appmenu"));
138 assert(menubar);
139
140 MenuRegistry::Visit(*this, mProject);
141
142 GetProjectFrame(mProject).SetMenuBar(menubar.release());
143 }
144
146
149 void UpdateCheckmark(AudacityProject &project) final;
150 void Modify(const TranslatableString &newLabel) final;
151 bool GetEnabled() const final;
152 void Check(bool checked) final;
153 void Enable(bool enabled) final;
154 void EnableMultiItem(bool enabled) final;
155
156 wxMenu *menu{};
157 };
158
159private:
160 std::unique_ptr<CommandManager::CommandListEntry>
161 AllocateEntry(const MenuRegistry::Options &options) final;
162 void VisitEntry(CommandManager::CommandListEntry &,
163 const MenuRegistry::Options *pOptions) final;
164 void BeginMenu(const TranslatableString & tName) final;
165 void EndMenu() final;
166 void BeginMainMenu(const TranslatableString & tName);
167 void EndMainMenu();
168 void BeginSubMenu(const TranslatableString & tName);
169 void EndSubMenu();
170 void BeginOccultCommands() final;
171 void EndOccultCommands() final;
172
173 std::unique_ptr<wxMenuBar> AddMenuBar(const wxString & sMenu);
174 wxMenuBar * CurrentMenuBar() const;
175 wxMenuBar * GetMenuBar(const wxString & sMenu) const;
176 wxMenu * CurrentSubMenu() const;
177 wxMenu * CurrentMenu() const;
178
179 std::vector<MenuBarListEntry> mMenuBarList;
180 std::vector<SubMenuListEntry> mSubMenuList;
181 std::unique_ptr<wxMenuBar> mTempMenuBar;
182 std::unique_ptr<wxMenu> uCurrentMenu;
183 wxMenu *mCurrentMenu {};
184};
185
186MenuItemVisitor::CommandListEntryEx::~CommandListEntryEx() = default;
187
188void
189MenuItemVisitor::CommandListEntryEx::UpdateCheckmark(AudacityProject &project)
190{
191 if (menu && checkmarkFn && !isOccult) {
192 menu->Check(id, checkmarkFn(project));
193 }
194}
195
196void
197MenuItemVisitor::CommandListEntryEx::Modify(const TranslatableString &newLabel)
198{
199 if (menu) {
200 label = newLabel;
201 menu->SetLabel(id, FormatLabelForMenu());
202 }
203}
204
205bool MenuItemVisitor::CommandListEntryEx::GetEnabled() const
206{
207 if (!menu)
208 return false;
209 return enabled;
210}
211
212void MenuItemVisitor::CommandListEntryEx::Check(bool checked)
213{
214 if (!menu || isOccult)
215 return;
216 menu->Check(id, checked);
217}
218
219void MenuItemVisitor::CommandListEntryEx::Enable(bool b)
220{
221 if (!menu) {
222 enabled = b;
223 return;
224 }
225
226 // LL: Refresh from real state as we can get out of sync on the
227 // Mac due to its reluctance to enable menus when in a modal
228 // state.
229 enabled = menu->IsEnabled(id);
230
231 // Only enabled if needed
232 if (enabled != b) {
233 menu->Enable(id, b);
234 enabled = menu->IsEnabled(id);
235 }
236}
237
238void MenuItemVisitor::CommandListEntryEx::EnableMultiItem(bool b)
239{
240 if (menu) {
241 const auto item = menu->FindItem(id);
242 if (item) {
243 item->Enable(b);
244 return;
245 }
246 }
247 // using GET in a log message for devs' eyes only
248 wxLogDebug(wxT("Warning: Menu entry with id %i in %s not found"),
249 id, name.GET());
250}
251
252auto MenuItemVisitor::AllocateEntry(const MenuRegistry::Options &options)
253 -> std::unique_ptr<CommandManager::CommandListEntry>
254{
255 auto result = std::make_unique<CommandListEntryEx>();
256 if (!options.global)
257 result->menu = CurrentMenu();
258 return result;
259}
260
261// A label that may have its accelerator disabled.
262// The problem is that as soon as we show accelerators in the menu, the menu might
263// catch them in normal wxWidgets processing, rather than passing the key presses on
264// to the controls that had the focus. We would like all the menu accelerators to be
265// disabled, in fact.
266static wxString
268{
269 auto label = entry.label.Translation();
270#if 1
271 wxString Accel;
272 do{
273 if (!entry.key.empty())
274 {
275 // Dummy accelerator that looks Ok in menus but is non functional.
276 // Note the space before the key.
277#ifdef __WXMSW__
278 // using GET to compose menu item name for wxWidgets
279 auto key = entry.key.GET();
280 Accel = wxString("\t ") + key;
281 if( key.StartsWith("Left" )) break;
282 if( key.StartsWith("Right")) break;
283 if( key.StartsWith("Up" )) break;
284 if( key.StartsWith("Down")) break;
285 if( key.StartsWith("Return")) break;
286 if( key.StartsWith("Tab")) break;
287 if( key.StartsWith("Shift+Tab")) break;
288 if( key.StartsWith("0")) break;
289 if( key.StartsWith("1")) break;
290 if( key.StartsWith("2")) break;
291 if( key.StartsWith("3")) break;
292 if( key.StartsWith("4")) break;
293 if( key.StartsWith("5")) break;
294 if( key.StartsWith("6")) break;
295 if( key.StartsWith("7")) break;
296 if( key.StartsWith("8")) break;
297 if( key.StartsWith("9")) break;
298 // Uncomment the below so as not to add the illegal accelerators.
299 // Accel = "";
300 //if( entry.key.StartsWith("Space" )) break;
301 // These ones appear to be illegal already and mess up accelerator processing.
302 if( key.StartsWith("NUMPAD_ENTER" )) break;
303 if( key.StartsWith("Backspace" )) break;
304 if( key.StartsWith("Delete" )) break;
305
306 // https://github.com/audacity/audacity/issues/4457
307 // This code was proposed by David Bailes to fix
308 // the decimal separator input in wxTextCtrls that
309 // are children of the main window.
310 if( key.StartsWith(",") ) break;
311 if( key.StartsWith(".") ) break;
312
313 // https://github.com/audacity/audacity/issues/5868
314 // On German and Norwegian keyboards, [ and ] are
315 // AltGr 8 and AltGr 9. On Windows typing 8 or 9 match
316 // [ or ] repectively when they are accelerators in menus.
317 if ( key.StartsWith("[") ) break;
318 if ( key.StartsWith("]") ) break;
319
320#endif
321 //wxLogDebug("Added Accel:[%s][%s]", entry.label, entry.key );
322 // Normal accelerator.
323 // using GET to compose menu item name for wxWidgets
324 Accel = wxString("\t") + entry.key.GET();
325 }
326 } while (false );
327 label += Accel;
328#endif
329 return label;
330}
331
332void MenuItemVisitor::VisitEntry(CommandManager::CommandListEntry &entry,
333 const MenuRegistry::Options *pOptions)
334{
335 if (!pOptions)
336 // command list item
337 CurrentMenu()->Append(entry.id, entry.FormatLabelForMenu());
338 else if (pOptions->global)
339 ;
340 else {
341 auto ID = entry.id;
343 auto &checker = pOptions->checker;
344 if (checker) {
345 CurrentMenu()->AppendCheckItem(ID, label);
346 CurrentMenu()->Check(ID, checker(mProject));
347 }
348 else
349 CurrentMenu()->Append(ID, label);
350 }
351}
352
353MenuItemVisitor::~MenuItemVisitor() = default;
354
355void MenuItemVisitor::BeginMenu(const TranslatableString & tName)
356{
357 if (mCurrentMenu)
358 return BeginSubMenu(tName);
359 else
360 return BeginMainMenu(tName);
361}
362
364// and in all cases ends the menu
365void MenuItemVisitor::EndMenu()
366{
367 if (mSubMenuList.empty())
368 EndMainMenu();
369 else
370 EndSubMenu();
371}
372
373void MenuItemVisitor::BeginMainMenu(const TranslatableString & tName)
374{
375 uCurrentMenu = std::make_unique<wxMenu>();
376 mCurrentMenu = uCurrentMenu.get();
377}
378
380void MenuItemVisitor::EndMainMenu()
381{
382 // Add the menu to the menubar after all menu items have been
383 // added to the menu to allow OSX to rearrange special menu
384 // items like Preferences, About, and Quit.
385 assert(uCurrentMenu);
386 CurrentMenuBar()->Append(
387 uCurrentMenu.release(), MenuNames()[0].Translation());
388 mCurrentMenu = nullptr;
389}
390
393void MenuItemVisitor::BeginSubMenu(const TranslatableString & tName)
394{
395 mSubMenuList.emplace_back();
396 mbSeparatorAllowed = false;
397}
398
402void MenuItemVisitor::EndSubMenu()
403{
404 //Save the submenu's information
405 SubMenuListEntry tmpSubMenu{ std::move( mSubMenuList.back() ) };
406
407 //Pop off the NEW submenu so CurrentMenu returns the parent of the submenu
408 mSubMenuList.pop_back();
409
410 //Add the submenu to the current menu
411 auto name = MenuNames().back().Translation();
412 CurrentMenu()->Append(0, name, tmpSubMenu.menu.release(),
413 name /* help string */ );
414 mbSeparatorAllowed = true;
415}
416
417void MenuItemVisitor::BeginOccultCommands()
418{
419 // To do: perhaps allow occult item switching at lower levels of the
420 // menu tree.
421 assert(!CurrentMenu());
422
423 // Make a temporary menu bar collecting items added after.
424 // This bar will be discarded but other side effects on the command
425 // manager persist.
426 mTempMenuBar = AddMenuBar(wxT("ext-menu"));
427}
428
429void MenuItemVisitor::EndOccultCommands()
430{
431 auto iter = mMenuBarList.end();
432 if (iter != mMenuBarList.begin())
433 mMenuBarList.erase(--iter);
434 else
435 assert(false);
436 mTempMenuBar.reset();
437}
438
439}
440
442{
443 {
444 MenuItemVisitor visitor{ mProject };
445 }
446
448
449#if defined(_DEBUG)
450// c->CheckDups();
451#endif
452}
453
454// Get hackcess to a protected method
455class wxFrameEx : public wxFrame
456{
457public:
458 using wxFrame::DetachMenuBar;
459};
460
462{
463 auto &project = mProject;
464 // On OSX, we can't rebuild the menus while a modal dialog is being shown
465 // since the enabled state for menus like Quit and Preference gets out of
466 // sync with wxWidgets idea of what it should be.
467#if defined(__WXMAC__) && defined(_DEBUG)
468 {
469 wxDialog *dlg =
470 wxDynamicCast(wxGetTopLevelParent(wxWindow::FindFocus()), wxDialog);
471 assert((!dlg || !dlg->IsModal()));
472 }
473#endif
474
475 // Delete the menus, since we will soon recreate them.
476 // Rather oddly, the menus don't vanish as a result of doing this.
477 {
478 auto &window = static_cast<wxFrameEx&>( GetProjectFrame( project ) );
479 wxWindowPtr<wxMenuBar> menuBar{ window.GetMenuBar() };
480 window.DetachMenuBar();
481 // menuBar gets deleted here
482 }
483
484 PurgeData();
486}
487
488constexpr auto JournalCode = wxT("CM"); // for CommandManager
489
491 const wxEvent *evt, const CommandListEntry &entry)
492{
493 Journal::Output({ JournalCode, entry.name.GET() });
494 return CommandManager::ExecuteCommand(context, evt, entry);
495}
496
497// a temporary hack that should be removed as soon as we
498// get multiple effect preview working
500{
501 return !GetProjectFrame(mProject).IsActive();
502}
503
507
509{
510 for( auto p : AllProjects{} ) {
511 Get(*p).RebuildMenuBar();
512#if defined(__WXGTK__)
513 // Workaround for:
514 //
515 // http://bugzilla.audacityteam.org/show_bug.cgi?id=458
516 //
517 // This workaround should be removed when Audacity updates to wxWidgets 3.x which has a fix.
518 auto &window = GetProjectFrame( *p );
519 wxRect r = window.GetRect();
520 window.SetSize(wxSize(1,1));
521 window.SetSize(r.GetSize());
522#endif
523 }
524}
525
527{
528 const auto disabledShortcuts = ReportDuplicateShortcuts();
529 if (!disabledShortcuts.Translation().empty()) {
530 TranslatableString message = XO("The following commands have had their shortcuts removed,"
531 " because their default shortcut is new or changed, and is the same shortcut"
532 " that you have assigned to another command.")
533 + disabledShortcuts;
534 AudacityMessageBox(message, XO("Shortcuts have been removed"), wxOK | wxCENTRE);
535
536 gPrefs->Flush();
538 }
539}
540
541static CommandManager::Factory::SubstituteInShared<MenuCreator> scope;
542
543namespace {
544
545// Register a callback for the journal
547[]( const wxArrayStringEx &fields )
548{
549 // Expect JournalCode and the command name.
550 // To do, perhaps, is to include some parameters.
551 bool handled = false;
552 if ( fields.size() == 2 ) {
553 if (auto project = GetActiveProject().lock()) {
554 auto pManager = &CommandManager::Get( *project );
555 auto flags = CommandManager::Get( *project ).GetUpdateFlags();
556 const CommandContext context( *project );
557 auto &command = fields[1];
558 handled =
559 pManager->HandleTextualCommand( command, context, flags, false );
560 }
561 }
562 return handled;
563}
564};
565
566}
567
569 AudacityProject &project, const wxKeyEvent & evt, bool permit)
570{
571 auto &cm = Get(project);
572
573 auto pWindow = FindProjectFrame(&project);
574 CommandListEntry *entry = cm.mCommandKeyHash[KeyEventToKeyString(evt)];
575 if (entry == NULL)
576 {
577 return false;
578 }
579
580 int type = evt.GetEventType();
581
582 // Global commands aren't tied to any specific project
583 if (entry->isGlobal && type == wxEVT_KEY_DOWN)
584 {
585 // Global commands are always disabled so they do not interfere with the
586 // rest of the command handling. But, to use the common handler, we
587 // enable them temporarily and then disable them again after handling.
588 // LL: Why do they need to be disabled???
589 entry->enabled = false;
590 auto cleanup = valueRestorer( entry->enabled, true );
591 return cm.HandleCommandEntry(entry, NoFlagsSpecified, false, &evt);
592 }
593
594 wxWindow * pFocus = wxWindow::FindFocus();
595 wxWindow * pParent = wxGetTopLevelParent( pFocus );
596 bool validTarget = pParent == pWindow;
597 // Bug 1557. MixerBoard should count as 'destined for project'
598 // MixerBoard IS a TopLevelWindow, and its parent is the project.
599 if( pParent && pParent->GetParent() == pWindow ){
600 if(auto keystrokeHandlingWindow = dynamic_cast< TopLevelKeystrokeHandlingWindow* >( pParent ))
601 validTarget = keystrokeHandlingWindow->HandleCommandKeystrokes();
602 }
603 validTarget = validTarget && wxEventLoop::GetActive()->IsMain();
604
605 // Any other keypresses must be destined for this project window
606 if (!permit && !validTarget )
607 {
608 return false;
609 }
610
611 auto flags = cm.GetUpdateFlags();
612
613 wxKeyEvent temp = evt;
614
615 // Possibly let wxWidgets do its normal key handling IF it is one of
616 // the standard navigation keys.
617 if((type == wxEVT_KEY_DOWN) || (type == wxEVT_KEY_UP ))
618 {
619 wxWindow * pWnd = wxWindow::FindFocus();
620 bool bIntercept =
621 pWnd && !dynamic_cast< NonKeystrokeInterceptingWindow * >( pWnd );
622
623 //wxLogDebug("Focus: %p TrackPanel: %p", pWnd, pTrackPanel );
624 // We allow the keystrokes below to be handled by wxWidgets controls IF we are
625 // in some sub window rather than in the TrackPanel itself.
626 // Otherwise they will go to our command handler and if it handles them
627 // they will NOT be available to wxWidgets.
628 if( bIntercept ){
629 switch( evt.GetKeyCode() ){
630 case WXK_LEFT:
631 case WXK_RIGHT:
632 case WXK_UP:
633 case WXK_DOWN:
634 // Don't trap WXK_SPACE (Bug 1727 - SPACE not starting/stopping playback
635 // when cursor is in a time control)
636 // case WXK_SPACE:
637 case WXK_TAB:
638 case WXK_BACK:
639 case WXK_HOME:
640 case WXK_END:
641 case WXK_RETURN:
642 case WXK_NUMPAD_ENTER:
643 case WXK_DELETE:
644 case '0':
645 case '1':
646 case '2':
647 case '3':
648 case '4':
649 case '5':
650 case '6':
651 case '7':
652 case '8':
653 case '9':
654 return false;
655 case ',':
656 case '.':
657 if (!evt.HasAnyModifiers())
658 return false;
659 }
660 }
661 }
662
663 if (type == wxEVT_KEY_DOWN)
664 {
665 if (entry->skipKeydown)
666 {
667 return true;
668 }
669 return cm.HandleCommandEntry(entry, flags, false, &temp);
670 }
671
672 if (type == wxEVT_KEY_UP && entry->wantKeyup)
673 {
674 return cm.HandleCommandEntry(entry, flags, false, &temp);
675 }
676
677 return false;
678}
679
681[]( wxKeyEvent & ) {
682 // We must have a project since we will be working with the
683 // CommandManager, which is tied to individual projects.
684 auto project = GetActiveProject().lock();
685 return project && GetProjectFrame( *project ).IsEnabled();
686} };
688[]( wxKeyEvent &key ) {
689 // Capture handler window didn't want it, so ask the CommandManager.
690 if (auto project = GetActiveProject().lock()) {
692 }
693 else
694 return false;
695} };
696
697
699{
700 wxString newStr;
701
702 long key = event.GetKeyCode();
703
704 if (event.ControlDown())
705 newStr += wxT("Ctrl+");
706
707 if (event.AltDown())
708 newStr += wxT("Alt+");
709
710 if (event.ShiftDown())
711 newStr += wxT("Shift+");
712
713#if defined(__WXMAC__)
714 if (event.RawControlDown())
715 newStr += wxT("RawCtrl+");
716#endif
717
718 if (event.RawControlDown() && key >= 1 && key <= 26)
719 newStr += (wxChar)(64 + key);
720 else if (key >= 33 && key <= 255 && key != 127)
721 newStr += (wxChar)key;
722 else
723 {
724 switch(key)
725 {
726 case WXK_BACK:
727 newStr += wxT("Backspace");
728 break;
729 case WXK_DELETE:
730 newStr += wxT("Delete");
731 break;
732 case WXK_SPACE:
733 newStr += wxT("Space");
734 break;
735 case WXK_TAB:
736 newStr += wxT("Tab");
737 break;
738 case WXK_RETURN:
739 newStr += wxT("Return");
740 break;
741 case WXK_PAGEUP:
742 newStr += wxT("PageUp");
743 break;
744 case WXK_PAGEDOWN:
745 newStr += wxT("PageDown");
746 break;
747 case WXK_END:
748 newStr += wxT("End");
749 break;
750 case WXK_HOME:
751 newStr += wxT("Home");
752 break;
753 case WXK_LEFT:
754 newStr += wxT("Left");
755 break;
756 case WXK_UP:
757 newStr += wxT("Up");
758 break;
759 case WXK_RIGHT:
760 newStr += wxT("Right");
761 break;
762 case WXK_DOWN:
763 newStr += wxT("Down");
764 break;
765 case WXK_ESCAPE:
766 newStr += wxT("Escape");
767 break;
768 case WXK_INSERT:
769 newStr += wxT("Insert");
770 break;
771 case WXK_NUMPAD0:
772 newStr += wxT("NUMPAD0");
773 break;
774 case WXK_NUMPAD1:
775 newStr += wxT("NUMPAD1");
776 break;
777 case WXK_NUMPAD2:
778 newStr += wxT("NUMPAD2");
779 break;
780 case WXK_NUMPAD3:
781 newStr += wxT("NUMPAD3");
782 break;
783 case WXK_NUMPAD4:
784 newStr += wxT("NUMPAD4");
785 break;
786 case WXK_NUMPAD5:
787 newStr += wxT("NUMPAD5");
788 break;
789 case WXK_NUMPAD6:
790 newStr += wxT("NUMPAD6");
791 break;
792 case WXK_NUMPAD7:
793 newStr += wxT("NUMPAD7");
794 break;
795 case WXK_NUMPAD8:
796 newStr += wxT("NUMPAD8");
797 break;
798 case WXK_NUMPAD9:
799 newStr += wxT("NUMPAD9");
800 break;
801 case WXK_MULTIPLY:
802 newStr += wxT("*");
803 break;
804 case WXK_ADD:
805 newStr += wxT("+");
806 break;
807 case WXK_SUBTRACT:
808 newStr += wxT("-");
809 break;
810 case WXK_DECIMAL:
811 newStr += wxT(".");
812 break;
813 case WXK_DIVIDE:
814 newStr += wxT("/");
815 break;
816 case WXK_F1:
817 newStr += wxT("F1");
818 break;
819 case WXK_F2:
820 newStr += wxT("F2");
821 break;
822 case WXK_F3:
823 newStr += wxT("F3");
824 break;
825 case WXK_F4:
826 newStr += wxT("F4");
827 break;
828 case WXK_F5:
829 newStr += wxT("F5");
830 break;
831 case WXK_F6:
832 newStr += wxT("F6");
833 break;
834 case WXK_F7:
835 newStr += wxT("F7");
836 break;
837 case WXK_F8:
838 newStr += wxT("F8");
839 break;
840 case WXK_F9:
841 newStr += wxT("F9");
842 break;
843 case WXK_F10:
844 newStr += wxT("F10");
845 break;
846 case WXK_F11:
847 newStr += wxT("F11");
848 break;
849 case WXK_F12:
850 newStr += wxT("F12");
851 break;
852 case WXK_F13:
853 newStr += wxT("F13");
854 break;
855 case WXK_F14:
856 newStr += wxT("F14");
857 break;
858 case WXK_F15:
859 newStr += wxT("F15");
860 break;
861 case WXK_F16:
862 newStr += wxT("F16");
863 break;
864 case WXK_F17:
865 newStr += wxT("F17");
866 break;
867 case WXK_F18:
868 newStr += wxT("F18");
869 break;
870 case WXK_F19:
871 newStr += wxT("F19");
872 break;
873 case WXK_F20:
874 newStr += wxT("F20");
875 break;
876 case WXK_F21:
877 newStr += wxT("F21");
878 break;
879 case WXK_F22:
880 newStr += wxT("F22");
881 break;
882 case WXK_F23:
883 newStr += wxT("F23");
884 break;
885 case WXK_F24:
886 newStr += wxT("F24");
887 break;
888 case WXK_NUMPAD_ENTER:
889 newStr += wxT("NUMPAD_ENTER");
890 break;
891 case WXK_NUMPAD_F1:
892 newStr += wxT("NUMPAD_F1");
893 break;
894 case WXK_NUMPAD_F2:
895 newStr += wxT("NUMPAD_F2");
896 break;
897 case WXK_NUMPAD_F3:
898 newStr += wxT("NUMPAD_F3");
899 break;
900 case WXK_NUMPAD_F4:
901 newStr += wxT("NUMPAD_F4");
902 break;
903 case WXK_NUMPAD_HOME:
904 newStr += wxT("NUMPAD_HOME");
905 break;
906 case WXK_NUMPAD_LEFT:
907 newStr += wxT("NUMPAD_LEFT");
908 break;
909 case WXK_NUMPAD_UP:
910 newStr += wxT("NUMPAD_UP");
911 break;
912 case WXK_NUMPAD_RIGHT:
913 newStr += wxT("NUMPAD_RIGHT");
914 break;
915 case WXK_NUMPAD_DOWN:
916 newStr += wxT("NUMPAD_DOWN");
917 break;
918 case WXK_NUMPAD_PAGEUP:
919 newStr += wxT("NUMPAD_PAGEUP");
920 break;
921 case WXK_NUMPAD_PAGEDOWN:
922 newStr += wxT("NUMPAD_PAGEDOWN");
923 break;
924 case WXK_NUMPAD_END:
925 newStr += wxT("NUMPAD_END");
926 break;
927 case WXK_NUMPAD_BEGIN:
928 newStr += wxT("NUMPAD_HOME");
929 break;
930 case WXK_NUMPAD_INSERT:
931 newStr += wxT("NUMPAD_INSERT");
932 break;
933 case WXK_NUMPAD_DELETE:
934 newStr += wxT("NUMPAD_DELETE");
935 break;
936 case WXK_NUMPAD_EQUAL:
937 newStr += wxT("NUMPAD_EQUAL");
938 break;
939 case WXK_NUMPAD_MULTIPLY:
940 newStr += wxT("NUMPAD_MULTIPLY");
941 break;
942 case WXK_NUMPAD_ADD:
943 newStr += wxT("NUMPAD_ADD");
944 break;
945 case WXK_NUMPAD_SUBTRACT:
946 newStr += wxT("NUMPAD_SUBTRACT");
947 break;
948 case WXK_NUMPAD_DECIMAL:
949 newStr += wxT("NUMPAD_DECIMAL");
950 break;
951 case WXK_NUMPAD_DIVIDE:
952 newStr += wxT("NUMPAD_DIVIDE");
953 break;
954 default:
955 return {}; // Don't do anything if we don't recognize the key
956 }
957 }
958
959 return NormalizedKeyString{ newStr };
960}
961
967std::unique_ptr<wxMenuBar> MenuItemVisitor::AddMenuBar(const wxString & sMenu)
968{
969 wxMenuBar *menuBar = GetMenuBar(sMenu);
970 if (menuBar) {
971 wxASSERT(false);
972 return {};
973 }
974
975 auto result = std::make_unique<wxMenuBar>();
976 mMenuBarList.emplace_back(sMenu, result.get());
977
978 return result;
979}
980
984wxMenuBar *MenuItemVisitor::GetMenuBar(const wxString & sMenu) const
985{
986 for (const auto &entry : mMenuBarList)
987 {
988 if(entry.name == sMenu)
989 return entry.menubar;
990 }
991
992 return NULL;
993}
994
998wxMenuBar *MenuItemVisitor::CurrentMenuBar() const
999{
1000 if(mMenuBarList.empty())
1001 return NULL;
1002
1003 return mMenuBarList.back().menubar;
1004}
1005
1008wxMenu *MenuItemVisitor::CurrentSubMenu() const
1009{
1010 if(mSubMenuList.empty())
1011 return NULL;
1012
1013 return mSubMenuList.back().menu.get();
1014}
1015
1020wxMenu * MenuItemVisitor::CurrentMenu() const
1021{
1022 if(!mCurrentMenu)
1023 return NULL;
1024
1025 wxMenu * tmpCurrentSubMenu = CurrentSubMenu();
1026
1027 if(!tmpCurrentSubMenu)
1028 {
1029 return mCurrentMenu;
1030 }
1031
1032 return tmpCurrentSubMenu;
1033}
AUDACITY_DLL_API std::weak_ptr< AudacityProject > GetActiveProject()
Handle changing of active project and keep global project pointer.
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:34
constexpr CommandFlag NoFlagsSpecified
Definition: CommandFlag.h:35
XO("Cut/Copy/Paste")
static ProjectFileIORegistry::AttributeWriterEntry entry
The output stream of the journal system.
Journal system's error status, command dictionary, initializers.
ValueRestorer< T > valueRestorer(T &var)
inline functions provide convenient parameter type deduction
Definition: MemoryX.h:253
static KeyboardCapture::PostFilter::Scope scope2
constexpr auto JournalCode
static CommandManager::Factory::SubstituteInShared< MenuCreator > scope
NormalizedKeyString KeyEventToKeyString(const wxKeyEvent &event)
static KeyboardCapture::PreFilter::Scope scope1
static const AudacityProject::AttachedObjects::RegisteredFactory key
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
wxString name
Definition: TagsEditor.cpp:166
TranslatableString label
Definition: TagsEditor.cpp:165
const auto project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
CommandManager implements a system for organizing all user-callable commands.
AudacityProject & mProject
static CommandManager & Get(AudacityProject &project)
TranslatableString ReportDuplicateShortcuts()
CommandFlag mLastFlags
CommandFlag GetUpdateFlags(bool quick=false) const
virtual void ExecuteCommand(const CommandContext &context, const wxEvent *evt, const CommandListEntry &entry)
typename GlobalVariable< PreFilter, const std::function< bool(wxKeyEvent &) >, nullptr, Options... >::Scope Scope
MenuCreator is responsible for creating the main menu bar.
Definition: MenuCreator.h:19
void CreateMenusAndCommands()
static void RebuildAllMenuBars()
MenuCreator(AudacityProject &project)
Definition: MenuCreator.cpp:84
~MenuCreator() override
void RemoveDuplicateShortcuts()
void RebuildMenuBar()
static bool FilterKeyEvent(AudacityProject &project, const wxKeyEvent &evt, bool permit=false)
bool ReallyDoQuickCheck() override
Default implementation returns true.
void ExecuteCommand(const CommandContext &context, const wxEvent *evt, const CommandListEntry &entry) override
static MenuCreator & Get(AudacityProject &project)
Definition: MenuCreator.cpp:91
Holds a msgid for the translation catalog; may also bind format arguments.
virtual bool Flush() noexcept=0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:383
void Output(const wxString &string)
MENUS_API void Visit(Visitor< Traits > &visitor, AudacityProject &project)
static wxString FormatLabelWithDisabledAccel(const CommandManager::CommandListEntry &entry)
Journal::RegisteredCommand sCommand
STL namespace.
Definition: MenuCreator.cpp:45
wxWeakRef< wxMenuBar > menubar
Definition: MenuCreator.cpp:50
wxString name
Definition: MenuCreator.cpp:49
~MenuBarListEntry()
Definition: MenuCreator.cpp:68
~CommandListEntryEx() final
Definition: MenuCreator.cpp:54
~SubMenuListEntry()
Definition: MenuCreator.cpp:77
SubMenuListEntry(SubMenuListEntry &&)=default
SubMenuListEntry()
Definition: MenuCreator.cpp:72
std::unique_ptr< wxMenu > menu
Definition: MenuCreator.cpp:60
TranslatableString name
Definition: MenuCreator.cpp:59