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/* i18n-hint: as in computer keyboard (not musical!) */
95: PrefsPanel(parent, winid, XO("Keyboard")),
96 mView(NULL),
97 mKey(NULL),
98 mFilter(NULL),
99 mFilterTimer(this, FilterTimerID),
100 mFilterPending(false)
101 , mProject{ pProject }
102{
103 Populate();
104 if (!name.empty()) {
105 auto index = mView->GetIndexByName(name);
106 mView->SelectNode(index);
107 }
108
109 // See bug #2315 for discussion. This should be reviewed
110 // and (possibly) removed after wx3.1.3.
111 Bind(wxEVT_SHOW, &KeyConfigPrefs::OnShow, this);
112}
113
115{
117}
118
120{
121 return XO("Preferences for KeyConfig");
122}
123
125{
126 return "Keyboard_Preferences";
127}
128
130{
132
133 if (!mProject) {
134 S.StartVerticalLay(true);
135 {
136 S.StartStatic( {}, true);
137 {
138 S.AddTitle(XO("Keyboard preferences currently unavailable."));
139 S.AddTitle(XO("Open a new project to modify keyboard shortcuts."));
140 }
141 S.EndStatic();
142 }
143 S.EndVerticalLay();
144
145 return;
146 }
147
149
150 mCommandSelected = wxNOT_FOUND;
151
153
154 // For speed, don't sort here. We're just creating.
155 // Instead sort when we do SetView later in this function.
156 RefreshBindings(false);
157
158 if (mViewByTree->GetValue()) {
160 }
161 else if (mViewByName->GetValue()) {
163 }
164 else if (mViewByKey->GetValue()) {
166 mFilterLabel->SetLabel(_("&Hotkey:"));
167 mFilter->SetName(wxStripMenuCodes(mFilterLabel->GetLabel()));
168 }
169
171}
172
178{
179 ChoiceSetting Setting{ L"/Prefs/KeyConfig/ViewBy",
180 {
181 { wxT("tree"), XXO("&Tree") },
182 { wxT("name"), XXO("&Name") },
183 { wxT("key"), XXO("&Key") },
184 },
185 0 // tree
186 };
187
188 S.SetBorder(2);
189
190 S.StartStatic(XO("Key Bindings"), 1);
191 {
192 S.StartHorizontalLay(wxEXPAND, 0);
193 {
194 S.Position(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL).AddTitle(XO("View by:"));
195
196 // Bug 2692: Place button group in panel so tabbing will work and,
197 // on the Mac, VoiceOver will announce as radio buttons.
198 S.StartPanel();
199 {
200 S.StartHorizontalLay();
201 {
202 S.StartRadioButtonGroup(Setting);
203 {
205 .Name(XO("View by tree"))
206 .TieRadioButton();
208 .Name(XO("View by name"))
209 .TieRadioButton();
211 .Name(XO("View by key"))
212 .TieRadioButton();
213#if !defined(__WXMAC__) && wxUSE_ACCESSIBILITY
214 // so that name can be set on a standard control
218#endif
219 }
220 S.EndRadioButtonGroup();
221 }
222 S.EndHorizontalLay();
223 }
224 S.EndPanel();
225
226 S.AddSpace(wxDefaultCoord, wxDefaultCoord, 1);
227
228 S.StartHorizontalLay(wxALIGN_CENTER_VERTICAL, 0);
229 {
230 mFilterLabel = S.Position(wxALIGN_CENTER_VERTICAL).AddVariableText(XO("Searc&h:"));
231
232 if (!mFilter) {
233 mFilter = safenew wxTextCtrl(S.GetParent(),
234 FilterID,
235 wxT(""),
236 wxDefaultPosition,
237#if defined(__WXMAC__)
238 wxSize(300, -1),
239#else
240 wxSize(210, -1),
241#endif
242 wxTE_PROCESS_ENTER);
243 mFilter->SetName(wxStripMenuCodes(mFilterLabel->GetLabel()));
244 }
245 S.Position(wxALIGN_NOT | wxALIGN_LEFT)
246 .ConnectRoot(wxEVT_KEY_DOWN,
248 .ConnectRoot(wxEVT_CHAR,
250 .AddWindow(mFilter);
251 }
252 S.EndHorizontalLay();
253 }
254 S.EndHorizontalLay();
255
256 S.AddSpace(wxDefaultCoord, 2);
257
258 S.StartHorizontalLay(wxEXPAND, 1);
259 {
260 if (!mView) {
261 mView = safenew KeyView(S.GetParent(), CommandsListID);
262 mView->SetName(_("Bindings"));
263 }
264 S.Prop(true)
265 .Position(wxEXPAND)
266 .AddWindow(mView);
267 }
268 S.EndHorizontalLay();
269
270 S.StartThreeColumn();
271 {
272 if (!mKey) {
273 mKey = safenew wxTextCtrl(S.GetParent(),
275 wxT(""),
276 wxDefaultPosition,
277#if defined(__WXMAC__)
278 wxSize(300, -1),
279#else
280 wxSize(210, -1),
281#endif
282 wxTE_PROCESS_ENTER);
283#if !defined(__WXMAC__) && wxUSE_ACCESSIBILITY
284 // so that name can be set on a standard control
285 mKey->SetAccessible(safenew WindowAccessible(mKey));
286#endif
287 mKey->SetName(_("Short cut"));
288 }
289 S
290 .ConnectRoot(wxEVT_KEY_DOWN,
292 .ConnectRoot(wxEVT_CHAR,
294 .ConnectRoot(wxEVT_KILL_FOCUS,
296 .ConnectRoot(wxEVT_CONTEXT_MENU,
298 .AddWindow(mKey);
299
300 /* i18n-hint: (verb)*/
301 mSet = S.Id(SetButtonID).AddButton(XXO("&Set"));
302 /* i18n-hint: (verb)*/
303 mClear = S.Id(ClearButtonID).AddButton(XXO("Cl&ear"));
304 }
305 S.EndThreeColumn();
306
307#if defined(__WXMAC__)
308 S.AddFixedText(XO("Note: Pressing Cmd+Q will quit. All other keys are valid."));
309#endif
310
311 S.StartThreeColumn();
312 {
313 S.Id(ImportButtonID).AddButton(XXO("&Import..."));
314 S.Id(ExportButtonID).AddButton(XXO("&Export..."));
315 S.Id(AssignDefaultsButtonID).AddButton(XXO("&Defaults"));
316 }
317 S.EndThreeColumn();
318 }
319 S.EndStatic();
320
321
322 // Need to layout so that the KeyView is properly sized before populating.
323 // Otherwise, the initial selection is not scrolled into view.
324 Layout();
325}
326
328{
329 TranslatableStrings Labels;
330 TranslatableStrings Categories;
331 TranslatableStrings Prefixes;
332
333 mNames.clear();
334 mKeys.clear();
335 mDefaultKeys.clear();
336 mStandardDefaultKeys.clear();
338 mNames,
339 mKeys,
341 Labels,
342 Categories,
343 Prefixes,
344 true); // True to include effects (list items), false otherwise.
345
348
350 Categories,
351 Prefixes,
352 Labels,
353 mKeys,
354 bSort);
355 //Not needed as NEW nodes are already shown expanded.
356 //mView->ExpandAll();
357
358 mNewKeys = mKeys;
359}
360
361// RefreshKeyInfo is used to update mKeys vector only
362// Introduced for efficiency purposes to avoid unnecessary usage of RefreshBinding
364{
365 mKeys.clear();
366
367 for (const auto & name : mNames)
368 mKeys.push_back(mManager->GetKeyFromName(name));
369}
370
371// Removes all shortcuts
372// Doesn't call RefreshBindings()
374{
375 const NormalizedKeyString noKey{ NO_SHORTCUT };
376 for (const auto & command : mNames)
377 mManager->SetKeyFromName(command, noKey);
378}
379
380// Checks if the given vector of keys contains illegal duplicates.
381// In case it does, stores the prefixed labels of operations
382// with illegal key duplicates in fMatching and sMatching.
383// Search for duplicates fully implemented here
384// to avoid possible problems with legal shortcut duplicates.
386 TranslatableString & fMatching, TranslatableString & sMatching) const
387{
388 using IndexesArray = std::vector<int>;
389 std::unordered_map<NormalizedKeyString, IndexesArray> seen;
390
391 for (size_t i{ 0 }; i < mKeys.size(); i++)
392 {
393 if (mKeys[i] == EMPTY_SHORTCUT || mKeys[i] == NO_SHORTCUT)
394 continue;
395
396 if (seen.count(mKeys[i]) == 0)
397 seen.insert({ mKeys[i], {(int)i} });
398 else
399 {
400 IndexesArray checkMe{ seen.at(mKeys[i]) };
401 for (int index : checkMe)
402 {
403 if (mDefaultKeys[i] == EMPTY_SHORTCUT ||
404 mDefaultKeys[i] != mDefaultKeys[index])
405 {
407 sMatching = mManager->GetPrefixedLabelFromName(mNames[index]);
408 return true;
409 }
410 else
411 seen.at(mKeys[i]).push_back(index);
412 }
413 }
414 }
415 return false;
416}
417
418
419// This function tries to add the given shortcuts(keys) "toAdd"
420// to the already existing shortcuts(keys). Shortcuts are added only if
421// 1. the shortcut for the operation isn't defined already
422// 2. the added shortcut doesn't create illegal shortcut duplicate
423// The names of operations for which the second condition was violated
424// are returned in a single error message
426 const std::vector<NormalizedKeyString> &toAdd)
427{
428 TranslatableString disabledShortcuts;
429
430 auto searchAddInKeys = [&](size_t index)
431 {
432 for (size_t k{ 0 }; k < toAdd.size(); k++)
433 if (k == index)
434 continue;
435 else if (toAdd[index] == mKeys[k] &&
437 mDefaultKeys[k] != mDefaultKeys[index]))
438 return (int)k;
439
440 return -1;
441 };
442
443 const NormalizedKeyString noKey{ EMPTY_SHORTCUT };
444
445 for (size_t i{ 0 }; i < toAdd.size(); i++)
446 {
447 if (mKeys[i] != NO_SHORTCUT)
448 continue;
449 else if (toAdd[i] == EMPTY_SHORTCUT)
450 mManager->SetKeyFromIndex(i, noKey);
451 else
452 {
453 int sRes{ searchAddInKeys(i) };
454
455 if (sRes == -1)
456 mManager->SetKeyFromIndex(i, toAdd[i]);
457 else
458 {
460
461 disabledShortcuts +=
462 XO(
463"\n * \"%s\" (because the shortcut \'%s\' is used by \"%s\")\n")
464 .Format(
466 name,
468
469 mManager->SetKeyFromIndex(i, noKey);
470 }
471 }
472 }
473
474 return disabledShortcuts;
475}
476
477// See bug #2315 for discussion. This should be reviewed
478// and (possibly) removed after wx3.1.3.
479void KeyConfigPrefs::OnShow(wxShowEvent & event)
480{
481 event.Skip();
482
483 // This is required to prevent a crash if Preferences
484 // were opened without a project.
485 if (event.IsShown() && mView != nullptr)
486 {
487 mView->Refresh();
488 }
489}
490
491void KeyConfigPrefs::OnImport(wxCommandEvent & WXUNUSED(event))
492{
493 wxString file = wxT("Audacity-keys.xml");
494
495 file = SelectFile(FileNames::Operation::Open,
496 XO("Select an XML file containing Audacity keyboard shortcuts..."),
497 wxEmptyString,
498 file,
499 wxT(""),
501 wxRESIZE_BORDER,
502 this);
503
504 if (!file) {
505 return;
506 }
507
508 // this RefreshKeyInfo is here to account for
509 // potential OnSet() function executions before importing
511
512 // saving pre-import settings
513 const std::vector<NormalizedKeyString> oldKeys{ mKeys };
514
515 // clearing all pre-import settings
516 ClearAllKeys();
517
518 // getting new settings
519 XMLFileReader reader;
520 if (!reader.Parse(mManager, file)) {
522 reader.GetErrorStr(),
523 XO("Error Importing Keyboard Shortcuts"),
524 wxOK | wxCENTRE,
525 this);
526 }
527
529
530 // checking new setting for duplicates
531 // if there are duplicates, throwing error and returning to pre-import state
532 TranslatableString fMatching;
533 TranslatableString sMatching;
534
535 if (ContainsIllegalDups(fMatching, sMatching))
536 {
537 // restore the old pre-import hotkeys stored in oldKeys
538 for (size_t k{ 0 }; k < mNames.size(); k++)
539 mManager->SetKeyFromName(mNames[k], oldKeys[k]);
540 mKeys = oldKeys;
541
542 // output an error message
544 XO(
545"The file with the shortcuts contains illegal shortcut duplicates for \"%s\" and \"%s\".\nNothing is imported.")
546 .Format( fMatching, sMatching ),
547 XO("Error Importing Keyboard Shortcuts"),
548 wxICON_ERROR | wxCENTRE, this);
549
550 // stop the function
551 return;
552 }
553
554 // adding possible old settings to the new settings and recording the conflicting ones
555 TranslatableString disabledShortcuts{ MergeWithExistingKeys(oldKeys) };
556
557 RefreshBindings(true);
558
559 TranslatableString message{
560 XO("Loaded %d keyboard shortcuts\n").Format(mManager->GetNumberOfKeysRead()) };
561
562 if (disabledShortcuts.Translation() != (""))
563 message += XO("\nThe following commands are not mentioned in the imported file, "
564 "but have their shortcuts removed because of the conflict with other new shortcuts:\n") +
565 disabledShortcuts;
566
567 AudacityMessageBox(message, XO("Loading Keyboard Shortcuts"), wxOK | wxCENTRE);
568}
569
570void KeyConfigPrefs::OnExport(wxCommandEvent & WXUNUSED(event))
571{
572 wxString file = wxT("Audacity-keys.xml");
573
574 file = SelectFile(FileNames::Operation::Export,
575 XO("Export Keyboard Shortcuts As:"),
576 wxEmptyString,
577 file,
578 wxT("xml"),
580 wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
581 this);
582
583 if (!file) {
584 return;
585 }
586
587 GuardedCall( [&] {
588 XMLFileWriter prefFile{ file, XO("Error Exporting Keyboard Shortcuts") };
589 mManager->WriteXML(prefFile);
590 prefFile.Commit();
591 } );
592}
593
594
595
596// There currently is only one clickable AButton
597// so we just do what it needs.
598void KeyConfigPrefs::OnDefaults(wxCommandEvent & WXUNUSED(event))
599{
600 wxMenu Menu;
601 Menu.Append( 1, _("Standard") );
602 Menu.Append( 2, _("Full") );
603 Menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &KeyConfigPrefs::OnImportDefaults, this );
605}
606
607void KeyConfigPrefs::FilterKeys( std::vector<NormalizedKeyString> & arr )
608{
609 const auto &MaxListOnly = CommandManager::ExcludedList();
610
611 // Remove items that are in MaxList.
612 for (size_t i = 0; i < arr.size(); i++) {
613 if( std::binary_search(MaxListOnly.begin(), MaxListOnly.end(), arr[i]) )
614 arr[i] = {};
615 }
616}
617
618void KeyConfigPrefs::OnImportDefaults(wxCommandEvent & event)
619{
620 gPrefs->DeleteEntry(wxT("/GUI/Shortcuts/FullDefaults"));
621 gPrefs->Flush();
622
624 if( event.GetId() == 1 )
626
627 for (size_t i = 0; i < mNewKeys.size(); i++) {
629 }
630
631 RefreshBindings(true);
632}
633
635{
636 wxTextCtrl *t = (wxTextCtrl *)e.GetEventObject();
637
638 // Make sure we can navigate away from the hotkey textctrl.
639 // On Linux and OSX, it can get stuck, but it doesn't hurt
640 // to do it for Windows as well.
641 //
642 // Mac note: Don't waste time trying to figure out why the
643 // focus goes back to the prefs tree. Unless Voiceover is
644 // active, buttons on the Mac do not accept focus and all the
645 // controls between this one and the tree control are buttons.
646 if (e.GetKeyCode() == WXK_TAB) {
647 t->Navigate(e.ShiftDown()
648 ? wxNavigationKeyEvent::IsBackward
649 : wxNavigationKeyEvent::IsForward);
650 return;
651 }
652
653 t->SetValue(KeyEventToKeyString(e).Display());
654}
655
656void KeyConfigPrefs::OnHotkeyChar(wxEvent & WXUNUSED(e))
657{
658 // event.Skip() not performed, so event will not be processed further.
659}
660
662{
663 if (mKey->GetValue().empty() && mCommandSelected != wxNOT_FOUND) {
664 mKey->AppendText(mView->GetKey(mCommandSelected).Display());
665 }
666
667 e.Skip();
668}
669
670void KeyConfigPrefs::OnHotkeyContext(wxEvent & WXUNUSED(e))
671{
672 // event.Skip() not performed, so event will not be processed further.
673}
674
675void KeyConfigPrefs::OnFilterTimer(wxTimerEvent & WXUNUSED(e))
676{
677 // The filter timer has expired, so set the filter
678 if (mFilterPending)
679 {
680 // Do not reset mFilterPending here...possible race
681 mView->SetFilter(mFilter->GetValue());
682 }
683}
684
686{
687 wxTextCtrl *t = (wxTextCtrl *)e.GetEventObject();
688 int keycode = e.GetKeyCode();
689
690 // Make sure we can navigate away from the hotkey textctrl.
691 // On Linux and OSX, it an get stuck, but it doesn't hurt
692 // to do it for Windows as well.
693 if (keycode == WXK_TAB) {
694 wxNavigationKeyEvent nevent;
695 nevent.SetWindowChange(e.ControlDown());
696 nevent.SetDirection(!e.ShiftDown());
697 nevent.SetEventObject(t);
698 nevent.SetCurrentFocus(t);
699 t->GetParent()->GetEventHandler()->ProcessEvent(nevent);
700
701 return;
702 }
703
704 if (mViewType == ViewByKey) {
705 wxString key = KeyEventToKeyString(e).Display();
706 t->SetValue(key);
707
708 if (!key.empty()) {
709 mView->SetFilter(t->GetValue());
710 }
711 }
712 else
713 {
714 if (keycode == WXK_RETURN) {
715 mFilterPending = false;
716 mView->SetFilter(t->GetValue());
717 }
718 else {
719 mFilterPending = true;
720 mFilterTimer.Start(500, wxTIMER_ONE_SHOT);
721
722 e.Skip();
723 }
724 }
725}
726
728{
729 if (mViewType != ViewByKey)
730 {
731 e.Skip();
732 }
733}
734
735// Given a hotkey combination, returns the name (description) of the
736// corresponding command, or the empty string if none is found.
738{
739 return mView->GetNameByKey(key);
740}
741
742// Sets the selected command to have this key
743// This is not yet a committed change, which will happen on a save.
745{
747
749 {
751 XO("You may not assign a key to this entry"),
752 XO("Error"),
753 wxICON_ERROR | wxCENTRE,
754 this);
755 return;
756 }
757
760 mNewKeys[ make_iterator_range( mNames ).index( name ) ] = key;
761}
762
763
764void KeyConfigPrefs::OnSet(wxCommandEvent & WXUNUSED(event))
765{
766 if (mCommandSelected == wxNOT_FOUND) {
768 XO("You must select a binding before assigning a shortcut"),
769 XO("Error"),
770 wxICON_WARNING | wxCENTRE,
771 this);
772 return;
773 }
774
775 CommandID newCommand{ mView->GetName(mCommandSelected) };
776 NormalizedKeyString enteredKey{ mKey->GetValue() };
777 NormalizedKeyString newComDefaultKey{
778 mManager->GetDefaultKeyFromName(newCommand) };
779 CommandIDs oldCommands;
780
781 // collecting commands competing for the same shortcut
782 for (size_t i{ 0 }; i < mNames.size(); i++)
783 {
784 if (mNewKeys[i] == enteredKey)
785 {
786 // ignore the Set button if the same shortcut is used
787 if (mNames[i] == newCommand)
788 return;
789
790 if (newComDefaultKey == EMPTY_SHORTCUT ||
791 mDefaultKeys[i] != newComDefaultKey)
792 {
793 oldCommands.push_back(mNames[i]);
794 }
795 }
796 }
797
798 // Prevent same hotkey combination being used twice.
799 if (!oldCommands.empty()) {
800 auto newlabel = Verbatim( wxT("'%s - %s'") )
801 .Format(
802 mManager->GetCategoryFromName(newCommand),
803 mManager->GetPrefixedLabelFromName(newCommand) );
804 auto oldlabel = Verbatim(wxT("'%s - %s'"))
805 .Format(
806 mManager->GetCategoryFromName(oldCommands[0]),
807 mManager->GetPrefixedLabelFromName(oldCommands[0]));
808
809 for (size_t i{ 1 }; i < oldCommands.size(); i++)
810 oldlabel += XO("\n\n\t and\n\n\t") +
811 Verbatim(wxT("'%s - %s'")).Format(
812 mManager->GetCategoryFromName(oldCommands[i]),
813 mManager->GetPrefixedLabelFromName(oldCommands[i]));
814
815 if (wxCANCEL == AudacityMessageBox(
816 XO(
817"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.")
818 .Format(
819 mKey->GetValue(),
820 oldlabel,
821 newlabel
822 ),
823 XO("Warning"),
824 wxOK | wxCANCEL | wxICON_STOP | wxCENTRE,
825 this))
826 {
827 return;
828 }
829
830 for (const auto & command : oldCommands)
831 {
832 mView->SetKeyByName(command, {});
833 mManager->SetKeyFromName(command, {});
834 mNewKeys[make_iterator_range(mNames).index(command)] = {};
835 }
836 }
837
838 SetKeyForSelected(enteredKey);
839}
840
841void KeyConfigPrefs::OnClear(wxCommandEvent& WXUNUSED(event))
842{
843 mKey->Clear();
844
845 if (mCommandSelected != wxNOT_FOUND) {
847 }
848}
849
850void KeyConfigPrefs::OnSelected(wxCommandEvent & WXUNUSED(e))
851{
853 mKey->Clear();
854
855 if (mCommandSelected != wxNOT_FOUND) {
856 bool canset = mView->CanSetKey(mCommandSelected);
857 if (canset) {
858 mKey->AppendText(mView->GetKey(mCommandSelected).Display());
859 }
860
861 mKey->Enable(canset);
862 mSet->Enable(canset);
863 mClear->Enable(canset);
864 }
865}
866
867void KeyConfigPrefs::OnViewBy(wxCommandEvent & e)
868{
869 switch (e.GetId())
870 {
871 case ViewByTreeID:
873 mFilterLabel->SetLabel(_("Searc&h:"));
874 break;
875
876 case ViewByNameID:
878 mFilterLabel->SetLabel(_("Searc&h:"));
879 break;
880
881 case ViewByKeyID:
883 mFilterLabel->SetLabel(_("&Hotkey:"));
884 break;
885 }
886
888 mFilter->SetName(wxStripMenuCodes(mFilterLabel->GetLabel()));
889}
890
892{
893 // On the Mac, preferences may be changed without any active
894 // projects. This means that the CommandManager isn't available
895 // either. So we can't attempt to save preferences, otherwise
896 // NULL ptr dereferences will happen in ShuttleGui because the
897 // radio buttons are never created. (See Populate() above.)
898 if ( !mProject ) {
899 return true;
900 }
901
904
905 bool bFull = gPrefs->ReadBool(wxT("/GUI/Shortcuts/FullDefaults"), false);
906 for (size_t i = 0; i < mNames.size(); i++) {
907 const auto &dkey = bFull ? mDefaultKeys[i] : mStandardDefaultKeys[i];
908 // using GET to interpret CommandID as a config path component
909 auto name = wxT("/NewKeys/") + mNames[i].GET();
910 const auto &key = mNewKeys[i];
911
912 if (gPrefs->HasEntry(name)) {
913 if (key != NormalizedKeyString{ gPrefs->ReadObject(name, key) } ) {
914 gPrefs->Write(name, key);
915 }
916 if (key == dkey) {
918 }
919 }
920 else {
921 if (key != dkey) {
922 gPrefs->Write(name, key);
923 }
924 }
925 }
926
927 return gPrefs->Flush();
928}
929
931{
932 // Restore original key values
933 for (size_t i = 0; i < mNames.size(); i++) {
935 }
936
937 return;
938}
939
942{
943 return [=](wxWindow *parent, wxWindowID winid, AudacityProject *pProject)
944 {
945 wxASSERT(parent); // to justify safenew
946 auto result = safenew KeyConfigPrefs{ parent, winid, pProject, name };
947 return result;
948 };
949}
950namespace{
953};
954}
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
FileConfig * gPrefs
Definition: Prefs.cpp:70
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,...
virtual bool DeleteEntry(const wxString &key, bool bDeleteGroupIfEmpty=true) wxOVERRIDE
Definition: FileConfig.cpp:209
virtual bool HasEntry(const wxString &strName) const wxOVERRIDE
Definition: FileConfig.cpp:138
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
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:173
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
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
std::unique_ptr< MenuItem > Menu(const Identifier &internalName, const TranslatableString &title, Args &&... args)
wxString Display(bool usesSpecialChars=false) const
Definition: Keyboard.cpp:56
Window placement information for wxWidgetsBasicUI can be constructed from a wxWindow pointer.