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