Audacity 3.2.0
KeyConfigPrefs.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 KeyConfigPrefs.cpp
6
7 Brian Gunlogson
8 Dominic Mazzoni
9 James Crook
10
11*******************************************************************//*********************************************************************/
21
22
23
24#include "KeyConfigPrefs.h"
25
26#include <wx/setup.h> // for wxUSE_* macros
27#include <wx/defs.h>
28#include <wx/ffile.h>
29#include <wx/menu.h>
30#include <wx/button.h>
31#include <wx/radiobut.h>
32#include <wx/stattext.h>
33#include <wx/statbox.h>
34#include <wx/textctrl.h>
35
36#include "ActiveProject.h"
37#include "Prefs.h"
38#include "Project.h"
39#include "../ProjectWindows.h"
40#include "../commands/CommandManager.h"
41#include "XMLFileReader.h"
42
43#include "SelectFile.h"
44#include "ShuttleGui.h"
45
46#include "FileNames.h"
47
48#include "../widgets/BasicMenu.h"
49#include "../widgets/KeyView.h"
50#include "AudacityMessageBox.h"
52
53#if wxUSE_ACCESSIBILITY
54#include "WindowAccessible.h"
55#endif
56
57//
58// KeyConfigPrefs
59//
60#define AssignDefaultsButtonID 17001
61#define CurrentComboID 17002
62#define SetButtonID 17003
63#define ClearButtonID 17004
64#define CommandsListID 17005
65#define ExportButtonID 17006
66#define ImportButtonID 17007
67#define FilterID 17008
68#define ViewByTreeID 17009
69#define ViewByNameID 17010
70#define ViewByKeyID 17011
71#define FilterTimerID 17012
72
73// EMPTY_SHORTCUT means "user chose to have no shortcut"
74#define EMPTY_SHORTCUT ("")
75// NO_SHORTCUT means "user made no choice"
76#define NO_SHORTCUT (wxString)((wxChar)7)
77
78BEGIN_EVENT_TABLE(KeyConfigPrefs, PrefsPanel)
90
92 wxWindow * parent, wxWindowID winid, AudacityProject *pProject,
93 const CommandID &name)
94: PrefsPanel(parent, winid, XO("Shortcuts")),
95 mView(NULL),
96 mKey(NULL),
97 mFilter(NULL),
98 mFilterTimer(this, FilterTimerID),
99 mFilterPending(false)
100 , mProject{ pProject }
101{
102 Populate();
103 if (!name.empty()) {
104 auto index = mView->GetIndexByName(name);
105 mView->SelectNode(index);
106 }
107
108 // See bug #2315 for discussion. This should be reviewed
109 // and (possibly) removed after wx3.1.3.
110 Bind(wxEVT_SHOW, &KeyConfigPrefs::OnShow, this);
111}
112
114{
116}
117
119{
120 return XO("Preferences for KeyConfig");
121}
122
124{
125 return "Keyboard_Preferences";
126}
127
129{
131
132 if (!mProject) {
133 S.StartVerticalLay(true);
134 {
135 S.StartStatic( {}, true);
136 {
137 S.AddTitle(XO("Keyboard preferences currently unavailable."));
138 S.AddTitle(XO("Open a new project to modify keyboard shortcuts."));
139 }
140 S.EndStatic();
141 }
142 S.EndVerticalLay();
143
144 return;
145 }
146
148
149 mCommandSelected = wxNOT_FOUND;
150
152
153 // For speed, don't sort here. We're just creating.
154 // Instead sort when we do SetView later in this function.
155 RefreshBindings(false);
156
157 if (mViewByTree->GetValue()) {
159 }
160 else if (mViewByName->GetValue()) {
162 }
163 else if (mViewByKey->GetValue()) {
165 mFilterLabel->SetLabel(_("&Hotkey:"));
166 mFilter->SetName(wxStripMenuCodes(mFilterLabel->GetLabel()));
167 }
168
170}
171
177{
178 ChoiceSetting Setting{ L"/Prefs/KeyConfig/ViewBy",
179 {
180 { wxT("tree"), XXO("&Tree") },
181 { wxT("name"), XXO("&Name") },
182 { wxT("key"), XXO("&Key") },
183 },
184 0 // tree
185 };
186
187 S.SetBorder(2);
188
189 S.StartStatic(XO("Key Bindings"), 1);
190 {
191 S.StartHorizontalLay(wxEXPAND, 0);
192 {
193 S.Position(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL).AddTitle(XO("View by:"));
194
195 // Bug 2692: Place button group in panel so tabbing will work and,
196 // on the Mac, VoiceOver will announce as radio buttons.
197 S.StartPanel();
198 {
199 S.StartHorizontalLay();
200 {
201 S.StartRadioButtonGroup(Setting);
202 {
204 .Name(XO("View by tree"))
205 .TieRadioButton();
207 .Name(XO("View by name"))
208 .TieRadioButton();
210 .Name(XO("View by key"))
211 .TieRadioButton();
212#if !defined(__WXMAC__) && wxUSE_ACCESSIBILITY
213 // so that name can be set on a standard control
217#endif
218 }
219 S.EndRadioButtonGroup();
220 }
221 S.EndHorizontalLay();
222 }
223 S.EndPanel();
224
225 S.AddSpace(wxDefaultCoord, wxDefaultCoord, 1);
226
227 S.StartHorizontalLay(wxALIGN_CENTER_VERTICAL, 0);
228 {
229 mFilterLabel = S.Position(wxALIGN_CENTER_VERTICAL).AddVariableText(XO("Searc&h:"));
230
231 if (!mFilter) {
232 mFilter = safenew wxTextCtrl(S.GetParent(),
233 FilterID,
234 wxT(""),
235 wxDefaultPosition,
236#if defined(__WXMAC__)
237 wxSize(300, -1),
238#else
239 wxSize(210, -1),
240#endif
241 wxTE_PROCESS_ENTER);
242 mFilter->SetName(wxStripMenuCodes(mFilterLabel->GetLabel()));
243 }
244 S.Position(wxALIGN_NOT | wxALIGN_LEFT)
245 .ConnectRoot(wxEVT_KEY_DOWN,
247 .ConnectRoot(wxEVT_CHAR,
249 .AddWindow(mFilter);
250 }
251 S.EndHorizontalLay();
252 }
253 S.EndHorizontalLay();
254
255 S.AddSpace(wxDefaultCoord, 2);
256
257 S.StartHorizontalLay(wxEXPAND, 1);
258 {
259 if (!mView) {
260 mView = safenew KeyView(S.GetParent(), CommandsListID);
261 mView->SetName(_("Bindings"));
262 }
263 S.Prop(true)
264 .Position(wxEXPAND)
265 .AddWindow(mView);
266 }
267 S.EndHorizontalLay();
268
269 S.StartThreeColumn();
270 {
271 if (!mKey) {
272 mKey = safenew wxTextCtrl(S.GetParent(),
274 wxT(""),
275 wxDefaultPosition,
276#if defined(__WXMAC__)
277 wxSize(300, -1),
278#else
279 wxSize(210, -1),
280#endif
281 wxTE_PROCESS_ENTER);
282#if !defined(__WXMAC__) && wxUSE_ACCESSIBILITY
283 // so that name can be set on a standard control
284 mKey->SetAccessible(safenew WindowAccessible(mKey));
285#endif
286 mKey->SetName(_("Short cut"));
287 }
288 S
289 .ConnectRoot(wxEVT_KEY_DOWN,
291 .ConnectRoot(wxEVT_CHAR,
293 .ConnectRoot(wxEVT_KILL_FOCUS,
295 .ConnectRoot(wxEVT_CONTEXT_MENU,
297 .AddWindow(mKey);
298
299 /* i18n-hint: (verb)*/
300 mSet = S.Id(SetButtonID).AddButton(XXO("&Set"));
301 /* i18n-hint: (verb)*/
302 mClear = S.Id(ClearButtonID).AddButton(XXO("Cl&ear"));
303 }
304 S.EndThreeColumn();
305
306#if defined(__WXMAC__)
307 S.AddFixedText(XO("Note: Pressing Cmd+Q will quit. All other keys are valid."));
308#endif
309
310 S.StartThreeColumn();
311 {
312 S.Id(ImportButtonID).AddButton(XXO("&Import..."));
313 S.Id(ExportButtonID).AddButton(XXO("&Export..."));
314 S.Id(AssignDefaultsButtonID).AddButton(XXO("&Defaults"));
315 }
316 S.EndThreeColumn();
317 }
318 S.EndStatic();
319
320
321 // Need to layout so that the KeyView is properly sized before populating.
322 // Otherwise, the initial selection is not scrolled into view.
323 Layout();
324}
325
327{
328 TranslatableStrings Labels;
329 TranslatableStrings Categories;
330 TranslatableStrings Prefixes;
331
332 mNames.clear();
333 mKeys.clear();
334 mDefaultKeys.clear();
335 mStandardDefaultKeys.clear();
337 mNames,
338 mKeys,
340 Labels,
341 Categories,
342 Prefixes,
343 true); // True to include effects (list items), false otherwise.
344
347
349 Categories,
350 Prefixes,
351 Labels,
352 mKeys,
353 bSort);
354 //Not needed as NEW nodes are already shown expanded.
355 //mView->ExpandAll();
356
357 mNewKeys = mKeys;
358}
359
360// RefreshKeyInfo is used to update mKeys vector only
361// Introduced for efficiency purposes to avoid unnecessary usage of RefreshBinding
363{
364 mKeys.clear();
365
366 for (const auto & name : mNames)
367 mKeys.push_back(mManager->GetKeyFromName(name));
368}
369
370// Removes all shortcuts
371// Doesn't call RefreshBindings()
373{
374 const NormalizedKeyString noKey{ NO_SHORTCUT };
375 for (const auto & command : mNames)
376 mManager->SetKeyFromName(command, noKey);
377}
378
379// Checks if the given vector of keys contains illegal duplicates.
380// In case it does, stores the prefixed labels of operations
381// with illegal key duplicates in fMatching and sMatching.
382// Search for duplicates fully implemented here
383// to avoid possible problems with legal shortcut duplicates.
385 TranslatableString & fMatching, TranslatableString & sMatching) const
386{
387 using IndexesArray = std::vector<int>;
388 std::unordered_map<NormalizedKeyString, IndexesArray> seen;
389
390 for (size_t i{ 0 }; i < mKeys.size(); i++)
391 {
392 if (mKeys[i] == EMPTY_SHORTCUT || mKeys[i] == NO_SHORTCUT)
393 continue;
394
395 if (seen.count(mKeys[i]) == 0)
396 seen.insert({ mKeys[i], {(int)i} });
397 else
398 {
399 IndexesArray checkMe{ seen.at(mKeys[i]) };
400 for (int index : checkMe)
401 {
402 if (mDefaultKeys[i] == EMPTY_SHORTCUT ||
403 mDefaultKeys[i] != mDefaultKeys[index])
404 {
406 sMatching = mManager->GetPrefixedLabelFromName(mNames[index]);
407 return true;
408 }
409 else
410 seen.at(mKeys[i]).push_back(index);
411 }
412 }
413 }
414 return false;
415}
416
417
418// This function tries to add the given shortcuts(keys) "toAdd"
419// to the already existing shortcuts(keys). Shortcuts are added only if
420// 1. the shortcut for the operation isn't defined already
421// 2. the added shortcut doesn't create illegal shortcut duplicate
422// The names of operations for which the second condition was violated
423// are returned in a single error message
425 const std::vector<NormalizedKeyString> &toAdd)
426{
427 TranslatableString disabledShortcuts;
428
429 auto searchAddInKeys = [&](size_t index)
430 {
431 for (size_t k{ 0 }; k < toAdd.size(); k++)
432 if (k == index)
433 continue;
434 else if (toAdd[index] == mKeys[k] &&
436 mDefaultKeys[k] != mDefaultKeys[index]))
437 return (int)k;
438
439 return -1;
440 };
441
442 const NormalizedKeyString noKey{ EMPTY_SHORTCUT };
443
444 for (size_t i{ 0 }; i < toAdd.size(); i++)
445 {
446 if (mKeys[i] != NO_SHORTCUT)
447 continue;
448 else if (toAdd[i] == EMPTY_SHORTCUT)
449 mManager->SetKeyFromIndex(i, noKey);
450 else
451 {
452 int sRes{ searchAddInKeys(i) };
453
454 if (sRes == -1)
455 mManager->SetKeyFromIndex(i, toAdd[i]);
456 else
457 {
459
460 disabledShortcuts +=
461 XO(
462"\n * \"%s\" (because the shortcut \'%s\' is used by \"%s\")\n")
463 .Format(
465 name,
467
468 mManager->SetKeyFromIndex(i, noKey);
469 }
470 }
471 }
472
473 return disabledShortcuts;
474}
475
476// See bug #2315 for discussion. This should be reviewed
477// and (possibly) removed after wx3.1.3.
478void KeyConfigPrefs::OnShow(wxShowEvent & event)
479{
480 event.Skip();
481
482 // This is required to prevent a crash if Preferences
483 // were opened without a project.
484 if (event.IsShown() && mView != nullptr)
485 {
486 mView->Refresh();
487 }
488}
489
490void KeyConfigPrefs::OnImport(wxCommandEvent & WXUNUSED(event))
491{
492 wxString file = wxT("Audacity-keys.xml");
493
494 file = SelectFile(FileNames::Operation::Open,
495 XO("Select an XML file containing Audacity keyboard shortcuts..."),
496 wxEmptyString,
497 file,
498 wxT(""),
500 wxRESIZE_BORDER,
501 this);
502
503 if (!file) {
504 return;
505 }
506
507 // this RefreshKeyInfo is here to account for
508 // potential OnSet() function executions before importing
510
511 // saving pre-import settings
512 const std::vector<NormalizedKeyString> oldKeys{ mKeys };
513
514 // clearing all pre-import settings
515 ClearAllKeys();
516
517 // getting new settings
518 XMLFileReader reader;
519 if (!reader.Parse(mManager, file)) {
521 reader.GetErrorStr(),
522 XO("Error Importing Keyboard Shortcuts"),
523 wxOK | wxCENTRE,
524 this);
525 }
526
528
529 // checking new setting for duplicates
530 // if there are duplicates, throwing error and returning to pre-import state
531 TranslatableString fMatching;
532 TranslatableString sMatching;
533
534 if (ContainsIllegalDups(fMatching, sMatching))
535 {
536 // restore the old pre-import hotkeys stored in oldKeys
537 for (size_t k{ 0 }; k < mNames.size(); k++)
538 mManager->SetKeyFromName(mNames[k], oldKeys[k]);
539 mKeys = oldKeys;
540
541 // output an error message
543 XO(
544"The file with the shortcuts contains illegal shortcut duplicates for \"%s\" and \"%s\".\nNothing is imported.")
545 .Format( fMatching, sMatching ),
546 XO("Error Importing Keyboard Shortcuts"),
547 wxICON_ERROR | wxCENTRE, this);
548
549 // stop the function
550 return;
551 }
552
553 // adding possible old settings to the new settings and recording the conflicting ones
554 TranslatableString disabledShortcuts{ MergeWithExistingKeys(oldKeys) };
555
556 RefreshBindings(true);
557
558 TranslatableString message{
559 XO("Loaded %d keyboard shortcuts\n").Format(mManager->GetNumberOfKeysRead()) };
560
561 if (disabledShortcuts.Translation() != (""))
562 message += XO("\nThe following commands are not mentioned in the imported file, "
563 "but have their shortcuts removed because of the conflict with other new shortcuts:\n") +
564 disabledShortcuts;
565
566 AudacityMessageBox(message, XO("Loading Keyboard Shortcuts"), wxOK | wxCENTRE);
567}
568
569void KeyConfigPrefs::OnExport(wxCommandEvent & WXUNUSED(event))
570{
571 wxString file = wxT("Audacity-keys.xml");
572
573 file = SelectFile(FileNames::Operation::Export,
574 XO("Export Keyboard Shortcuts As:"),
575 wxEmptyString,
576 file,
577 wxT("xml"),
579 wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
580 this);
581
582 if (!file) {
583 return;
584 }
585
586 GuardedCall( [&] {
587 XMLFileWriter prefFile{ file, XO("Error Exporting Keyboard Shortcuts") };
588 mManager->WriteXML(prefFile);
589 prefFile.Commit();
590 } );
591}
592
593
594
595// There currently is only one clickable AButton
596// so we just do what it needs.
597void KeyConfigPrefs::OnDefaults(wxCommandEvent & WXUNUSED(event))
598{
599 wxMenu Menu;
600 Menu.Append( 1, _("Standard") );
601 Menu.Append( 2, _("Full") );
602 Menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &KeyConfigPrefs::OnImportDefaults, this );
604}
605
606void KeyConfigPrefs::FilterKeys( std::vector<NormalizedKeyString> & arr )
607{
608 const auto &MaxListOnly = CommandManager::ExcludedList();
609
610 // Remove items that are in MaxList.
611 for (size_t i = 0; i < arr.size(); i++) {
612 if( std::binary_search(MaxListOnly.begin(), MaxListOnly.end(), arr[i]) )
613 arr[i] = {};
614 }
615}
616
617void KeyConfigPrefs::OnImportDefaults(wxCommandEvent & event)
618{
619 gPrefs->DeleteEntry(wxT("/GUI/Shortcuts/FullDefaults"));
620 gPrefs->Flush();
621
623 if( event.GetId() == 1 )
625
626 for (size_t i = 0; i < mNewKeys.size(); i++) {
628 }
629
630 RefreshBindings(true);
631}
632
634{
635 wxTextCtrl *t = (wxTextCtrl *)e.GetEventObject();
636
637 // Make sure we can navigate away from the hotkey textctrl.
638 // On Linux and OSX, it can get stuck, but it doesn't hurt
639 // to do it for Windows as well.
640 //
641 // Mac note: Don't waste time trying to figure out why the
642 // focus goes back to the prefs tree. Unless Voiceover is
643 // active, buttons on the Mac do not accept focus and all the
644 // controls between this one and the tree control are buttons.
645 if (e.GetKeyCode() == WXK_TAB) {
646 t->Navigate(e.ShiftDown()
647 ? wxNavigationKeyEvent::IsBackward
648 : wxNavigationKeyEvent::IsForward);
649 return;
650 }
651
652 t->SetValue(KeyEventToKeyString(e).Display());
653}
654
655void KeyConfigPrefs::OnHotkeyChar(wxEvent & WXUNUSED(e))
656{
657 // event.Skip() not performed, so event will not be processed further.
658}
659
661{
662 if (mKey->GetValue().empty() && mCommandSelected != wxNOT_FOUND) {
663 mKey->AppendText(mView->GetKey(mCommandSelected).Display());
664 }
665
666 e.Skip();
667}
668
669void KeyConfigPrefs::OnHotkeyContext(wxEvent & WXUNUSED(e))
670{
671 // event.Skip() not performed, so event will not be processed further.
672}
673
674void KeyConfigPrefs::OnFilterTimer(wxTimerEvent & WXUNUSED(e))
675{
676 // The filter timer has expired, so set the filter
677 if (mFilterPending)
678 {
679 // Do not reset mFilterPending here...possible race
680 mView->SetFilter(mFilter->GetValue());
681 }
682}
683
685{
686 wxTextCtrl *t = (wxTextCtrl *)e.GetEventObject();
687 int keycode = e.GetKeyCode();
688
689 // Make sure we can navigate away from the hotkey textctrl.
690 // On Linux and OSX, it an get stuck, but it doesn't hurt
691 // to do it for Windows as well.
692 if (keycode == WXK_TAB) {
693 wxNavigationKeyEvent nevent;
694 nevent.SetWindowChange(e.ControlDown());
695 nevent.SetDirection(!e.ShiftDown());
696 nevent.SetEventObject(t);
697 nevent.SetCurrentFocus(t);
698 t->GetParent()->GetEventHandler()->ProcessEvent(nevent);
699
700 return;
701 }
702
703 if (mViewType == ViewByKey) {
704 wxString key = KeyEventToKeyString(e).Display();
705 t->SetValue(key);
706
707 if (!key.empty()) {
708 mView->SetFilter(t->GetValue());
709 }
710 }
711 else
712 {
713 if (keycode == WXK_RETURN) {
714 mFilterPending = false;
715 mView->SetFilter(t->GetValue());
716 }
717 else {
718 mFilterPending = true;
719 mFilterTimer.Start(500, wxTIMER_ONE_SHOT);
720
721 e.Skip();
722 }
723 }
724}
725
727{
728 if (mViewType != ViewByKey)
729 {
730 e.Skip();
731 }
732}
733
734// Given a hotkey combination, returns the name (description) of the
735// corresponding command, or the empty string if none is found.
737{
738 return mView->GetNameByKey(key);
739}
740
741// Sets the selected command to have this key
742// This is not yet a committed change, which will happen on a save.
744{
746
748 {
750 XO("You may not assign a key to this entry"),
751 XO("Error"),
752 wxICON_ERROR | wxCENTRE,
753 this);
754 return;
755 }
756
759 mNewKeys[ make_iterator_range( mNames ).index( name ) ] = key;
760}
761
762
763void KeyConfigPrefs::OnSet(wxCommandEvent & WXUNUSED(event))
764{
765 if (mCommandSelected == wxNOT_FOUND) {
767 XO("You must select a binding before assigning a shortcut"),
768 XO("Error"),
769 wxICON_WARNING | wxCENTRE,
770 this);
771 return;
772 }
773
774 CommandID newCommand{ mView->GetName(mCommandSelected) };
775 NormalizedKeyString enteredKey{ mKey->GetValue() };
776 NormalizedKeyString newComDefaultKey{
777 mManager->GetDefaultKeyFromName(newCommand) };
778 CommandIDs oldCommands;
779
780 // collecting commands competing for the same shortcut
781 for (size_t i{ 0 }; i < mNames.size(); i++)
782 {
783 if (mNewKeys[i] == enteredKey)
784 {
785 // ignore the Set button if the same shortcut is used
786 if (mNames[i] == newCommand)
787 return;
788
789 if (newComDefaultKey == EMPTY_SHORTCUT ||
790 mDefaultKeys[i] != newComDefaultKey)
791 {
792 oldCommands.push_back(mNames[i]);
793 }
794 }
795 }
796
797 // Prevent same hotkey combination being used twice.
798 if (!oldCommands.empty()) {
799 auto newlabel = Verbatim( wxT("'%s - %s'") )
800 .Format(
801 mManager->GetCategoryFromName(newCommand),
802 mManager->GetPrefixedLabelFromName(newCommand) );
803 auto oldlabel = Verbatim(wxT("'%s - %s'"))
804 .Format(
805 mManager->GetCategoryFromName(oldCommands[0]),
806 mManager->GetPrefixedLabelFromName(oldCommands[0]));
807
808 for (size_t i{ 1 }; i < oldCommands.size(); i++)
809 oldlabel += XO("\n\n\t and\n\n\t") +
810 Verbatim(wxT("'%s - %s'")).Format(
811 mManager->GetCategoryFromName(oldCommands[i]),
812 mManager->GetPrefixedLabelFromName(oldCommands[i]));
813
814 if (wxCANCEL == AudacityMessageBox(
815 XO(
816"The keyboard shortcut '%s' is already assigned to:\n\n\t%s\n\n\nClick OK to assign the shortcut to\n\n\t%s\n\ninstead. Otherwise, click Cancel.")
817 .Format(
818 mKey->GetValue(),
819 oldlabel,
820 newlabel
821 ),
822 XO("Warning"),
823 wxOK | wxCANCEL | wxICON_STOP | wxCENTRE,
824 this))
825 {
826 return;
827 }
828
829 for (const auto & command : oldCommands)
830 {
831 mView->SetKeyByName(command, {});
832 mManager->SetKeyFromName(command, {});
833 mNewKeys[make_iterator_range(mNames).index(command)] = {};
834 }
835 }
836
837 SetKeyForSelected(enteredKey);
838}
839
840void KeyConfigPrefs::OnClear(wxCommandEvent& WXUNUSED(event))
841{
842 mKey->Clear();
843
844 if (mCommandSelected != wxNOT_FOUND) {
846 }
847}
848
849void KeyConfigPrefs::OnSelected(wxCommandEvent & WXUNUSED(e))
850{
852 mKey->Clear();
853
854 if (mCommandSelected != wxNOT_FOUND) {
855 bool canset = mView->CanSetKey(mCommandSelected);
856 if (canset) {
857 mKey->AppendText(mView->GetKey(mCommandSelected).Display());
858 }
859
860 mKey->Enable(canset);
861 mSet->Enable(canset);
862 mClear->Enable(canset);
863 }
864}
865
866void KeyConfigPrefs::OnViewBy(wxCommandEvent & e)
867{
868 switch (e.GetId())
869 {
870 case ViewByTreeID:
872 mFilterLabel->SetLabel(_("Searc&h:"));
873 break;
874
875 case ViewByNameID:
877 mFilterLabel->SetLabel(_("Searc&h:"));
878 break;
879
880 case ViewByKeyID:
882 mFilterLabel->SetLabel(_("&Hotkey:"));
883 break;
884 }
885
887 mFilter->SetName(wxStripMenuCodes(mFilterLabel->GetLabel()));
888}
889
891{
892 // On the Mac, preferences may be changed without any active
893 // projects. This means that the CommandManager isn't available
894 // either. So we can't attempt to save preferences, otherwise
895 // NULL ptr dereferences will happen in ShuttleGui because the
896 // radio buttons are never created. (See Populate() above.)
897 if ( !mProject ) {
898 return true;
899 }
900
903
904 bool bFull = gPrefs->ReadBool(wxT("/GUI/Shortcuts/FullDefaults"), false);
905 for (size_t i = 0; i < mNames.size(); i++) {
906 const auto &dkey = bFull ? mDefaultKeys[i] : mStandardDefaultKeys[i];
907 // using GET to interpret CommandID as a config path component
908 auto name = wxT("/NewKeys/") + mNames[i].GET();
909 const auto &key = mNewKeys[i];
910
911 if (gPrefs->HasEntry(name)) {
913 gPrefs->Write(name, key);
914 }
915 if (key == dkey) {
917 }
918 }
919 else {
920 if (key != dkey) {
921 gPrefs->Write(name, key);
922 }
923 }
924 }
925
926 return gPrefs->Flush();
927}
928
930{
931 // Restore original key values
932 for (size_t i = 0; i < mNames.size(); i++) {
934 }
935
936 return;
937}
938
941{
942 return [=](wxWindow *parent, wxWindowID winid, AudacityProject *pProject)
943 {
944 wxASSERT(parent); // to justify safenew
945 auto result = safenew KeyConfigPrefs{ parent, winid, pProject, name };
946 return result;
947 };
948}
949namespace{
952};
953}
Handle changing of active project and keep global project pointer.
wxT("CloseDown"))
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), F3 delayedHandler=DefaultDelayedHandlerAction) noexcept(noexcept(handler(std::declval< AudacityException * >())) &&noexcept(handler(nullptr)) &&noexcept(std::function< void(AudacityException *)>{std::move(delayedHandler)}))
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
END_EVENT_TABLE()
static const AudacityProject::AttachedObjects::RegisteredFactory key
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
std::vector< CommandID > CommandIDs
Definition: Identifier.h:233
#define _(s)
Definition: Internat.h:73
PrefsPanel::Factory KeyConfigPrefsFactory(const CommandID &name)
#define CurrentComboID
#define EMPTY_SHORTCUT
#define NO_SHORTCUT
#define ClearButtonID
#define ImportButtonID
#define CommandsListID
#define FilterTimerID
#define ExportButtonID
#define ViewByTreeID
#define SetButtonID
#define ViewByNameID
#define AssignDefaultsButtonID
#define ViewByKeyID
#define FilterID
#define KEY_CONFIG_PREFS_PLUGIN_SYMBOL
@ ViewByTree
Definition: KeyView.h:62
@ ViewByKey
Definition: KeyView.h:64
@ ViewByName
Definition: KeyView.h:63
NormalizedKeyString KeyEventToKeyString(const wxKeyEvent &event)
Definition: Keyboard.cpp:83
#define safenew
Definition: MemoryX.h:10
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:448
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:46
@ eIsSavingToPrefs
Definition: ShuttleGui.h:47
#define S(N)
Definition: ToChars.cpp:64
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
void Popup(const BasicUI::WindowPlacement &window, const Point &pos={})
Display the menu at pos, invoke at most one action, then hide it.
Definition: BasicMenu.cpp:209
void WriteXML(XMLWriter &xmlFile) const
static CommandManager & Get(AudacityProject &project)
TranslatableString GetCategoryFromName(const CommandID &name)
void GetAllCommandData(CommandIDs &names, std::vector< NormalizedKeyString > &keys, std::vector< NormalizedKeyString > &default_keys, TranslatableStrings &labels, TranslatableStrings &categories, bool includeMultis)
static const std::vector< NormalizedKeyString > & ExcludedList()
void SetKeyFromName(const CommandID &name, const NormalizedKeyString &key)
NormalizedKeyString GetKeyFromName(const CommandID &name) const
NormalizedKeyString GetDefaultKeyFromName(const CommandID &name)
void SetKeyFromIndex(int i, const NormalizedKeyString &key)
int GetNumberOfKeysRead() const
TranslatableString GetPrefixedLabelFromName(const CommandID &name)
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
FILES_API const FileType XMLFiles
Definition: FileNames.h:73
FILES_API const FileType AllFiles
Definition: FileNames.h:70
Abstract base class used in importing a file.
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
A PrefsPanel for keybindings.
void OnFilterKeyDown(wxKeyEvent &e)
void OnImport(wxCommandEvent &e)
void OnHotkeyChar(wxEvent &e)
void OnImportDefaults(wxCommandEvent &e)
TranslatableString MergeWithExistingKeys(const std::vector< NormalizedKeyString > &toAdd)
void OnHotkeyKillFocus(wxEvent &e)
wxStaticText * mFilterLabel
TranslatableString GetDescription() const override
wxTextCtrl * mKey
CommandManager * mManager
void PopulateOrExchange(ShuttleGui &S) override
std::vector< NormalizedKeyString > mDefaultKeys
AudacityProject * mProject
ComponentInterfaceSymbol GetSymbol() const override
void FilterKeys(std::vector< NormalizedKeyString > &arr)
void OnSet(wxCommandEvent &e)
bool ContainsIllegalDups(TranslatableString &fMatching, TranslatableString &sMatching) const
void OnHotkeyContext(wxEvent &e)
void OnFilterChar(wxEvent &e)
ManualPageID HelpPageName() override
If not empty string, the Help button is added below the panel.
wxButton * mClear
void OnFilterTimer(wxTimerEvent &e)
CommandID NameFromKey(const NormalizedKeyString &key)
KeyView * mView
bool Commit() override
std::vector< NormalizedKeyString > mStandardDefaultKeys
std::vector< NormalizedKeyString > mNewKeys
std::vector< NormalizedKeyString > mKeys
void Cancel() override
wxButton * mSet
void OnClear(wxCommandEvent &e)
wxRadioButton * mViewByKey
wxTextCtrl * mFilter
wxRadioButton * mViewByTree
void OnExport(wxCommandEvent &e)
void OnSelected(wxCommandEvent &e)
CommandIDs mNames
ViewByType mViewType
wxRadioButton * mViewByName
void OnViewBy(wxCommandEvent &e)
wxTimer mFilterTimer
void OnDefaults(wxCommandEvent &e)
void RefreshBindings(bool bSort)
void OnShow(wxShowEvent &e)
void OnHotkeyKeyDown(wxKeyEvent &e)
void SetKeyForSelected(const NormalizedKeyString &key)
Provides multiple views of keyboard shortcuts.
Definition: KeyView.h:73
bool CanSetKey(int index) const
Definition: KeyView.cpp:338
int GetSelected() const
Definition: KeyView.cpp:185
bool SetKey(int index, const NormalizedKeyString &key)
Definition: KeyView.cpp:355
void SetView(ViewByType type)
Definition: KeyView.cpp:414
void SetFilter(const wxString &filter)
Definition: KeyView.cpp:472
NormalizedKeyString GetKey(int index) const
Definition: KeyView.cpp:322
void RefreshBindings(const CommandIDs &names, const TranslatableStrings &categories, const TranslatableStrings &prefixes, const TranslatableStrings &labels, const std::vector< NormalizedKeyString > &keys, bool bSort)
Definition: KeyView.cpp:636
bool SetKeyByName(const CommandID &name, const NormalizedKeyString &key)
Definition: KeyView.cpp:396
wxString GetName() const override
Definition: KeyView.cpp:194
CommandID GetNameByKey(const NormalizedKeyString &key) const
Definition: KeyView.cpp:282
Base class for a panel in the PrefsDialog. Classes derived from this class include BatchPrefs,...
Definition: PrefsPanel.h:51
std::function< PrefsPanel *(wxWindow *parent, wxWindowID winid, AudacityProject *) > Factory
Definition: PrefsPanel.h:82
Definition: Prefs.h:178
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:630
Holds a msgid for the translation catalog; may also bind format arguments.
TranslatableString & Format(Args &&...args) &
Capture variadic format arguments (by copy) when there is no plural.
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
Reads a file and passes the results through an XMLTagHandler.
Definition: XMLFileReader.h:19
const TranslatableString & GetErrorStr() const
bool Parse(XMLTagHandler *baseHandler, const FilePath &fname)
Wrapper to output XML data to files.
Definition: XMLWriter.h:84
T ReadObject(const wxString &key, const T &defaultValue) const
virtual bool Flush() noexcept=0
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
bool DeleteEntry(const wxString &key)
Deletes specified entry if exists.
constexpr auto Menu
Items will appear in a main toolbar menu or in a sub-menu.
wxString Display(bool usesSpecialChars=false) const
Definition: Keyboard.cpp:56
Window placement information for wxWidgetsBasicUI can be constructed from a wxWindow pointer.