1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-27 07:43:50 +01:00

Adding EXPERIMENTAL_KEY_VIEW that provides a new keyboard binding view.

This commit is contained in:
lllucius
2013-10-01 06:00:13 +00:00
parent aad9d12c39
commit b0e65b57c2
10 changed files with 2791 additions and 68 deletions

View File

@@ -38,6 +38,539 @@ KeyConfigPrefs and MousePrefs use.
#include "FileDialog.h"
#if defined(EXPERIMENTAL_KEY_VIEW)
#include "../widgets/KeyView.h"
//
// KeyConfigPrefs
//
#define AssignDefaultsButtonID 17001
#define CurrentComboID 17002
#define SetButtonID 17003
#define ClearButtonID 17004
#define CommandsListID 17005
#define ExportButtonID 17006
#define ImportButtonID 17007
#define FilterID 17008
#define ViewByTreeID 17009
#define ViewByNameID 17010
#define ViewByKeyID 17011
BEGIN_EVENT_TABLE(KeyConfigPrefs, PrefsPanel)
EVT_BUTTON(AssignDefaultsButtonID, KeyConfigPrefs::OnDefaults)
EVT_BUTTON(SetButtonID, KeyConfigPrefs::OnSet)
EVT_BUTTON(ClearButtonID, KeyConfigPrefs::OnClear)
EVT_BUTTON(ExportButtonID, KeyConfigPrefs::OnExport)
EVT_BUTTON(ImportButtonID, KeyConfigPrefs::OnImport)
EVT_LISTBOX(CommandsListID, KeyConfigPrefs::OnSelected)
EVT_RADIOBUTTON(ViewByTreeID, KeyConfigPrefs::OnViewBy)
EVT_RADIOBUTTON(ViewByNameID, KeyConfigPrefs::OnViewBy)
EVT_RADIOBUTTON(ViewByKeyID, KeyConfigPrefs::OnViewBy)
END_EVENT_TABLE()
KeyConfigPrefs::KeyConfigPrefs(wxWindow * parent)
: PrefsPanel(parent, _("Keyboard")),
mView(NULL),
mFilter(NULL),
mKey(NULL)
{
Populate();
}
KeyConfigPrefs::~KeyConfigPrefs()
{
if (mKey)
{
mKey->Disconnect(wxEVT_KEY_DOWN,
wxKeyEventHandler(KeyConfigPrefs::OnHotkeyKeyDown),
NULL,
this);
mKey->Disconnect(wxEVT_CHAR,
wxKeyEventHandler(KeyConfigPrefs::OnHotkeyChar),
NULL,
this);
mKey->Disconnect(wxEVT_KILL_FOCUS,
wxFocusEventHandler(KeyConfigPrefs::OnHotkeyKillFocus),
NULL,
this);
}
if (mFilter)
{
mKey->Disconnect(wxEVT_KEY_DOWN,
wxKeyEventHandler(KeyConfigPrefs::OnFilterKeyDown),
NULL,
this);
mKey->Disconnect(wxEVT_CHAR,
wxKeyEventHandler(KeyConfigPrefs::OnFilterChar),
NULL,
this);
}
}
void KeyConfigPrefs::Populate()
{
ShuttleGui S(this, eIsCreatingFromPrefs);
AudacityProject *project = GetActiveProject();
if (!project) {
S.StartVerticalLay(true);
{
S.StartStatic(wxEmptyString, true);
{
S.AddTitle(_("Keyboard preferences currently unavailable."));
S.AddTitle(_("Open a new project to modify keyboard shortcuts."));
}
S.EndStatic();
}
S.EndVerticalLay();
return;
}
PopulateOrExchange(S);
mCommandSelected = -1;
mManager = project->GetCommandManager();
RefreshBindings();
}
/// Normally in classes derived from PrefsPanel this function
/// is used both to populate the panel and to exchange data with it.
/// With KeyConfigPrefs all the exchanges are handled specially,
/// so this is only used in populating the panel.
void KeyConfigPrefs::PopulateOrExchange(ShuttleGui & S)
{
S.SetBorder(2);
S.StartStatic(_("Key Bindings"), 1);
{
S.StartMultiColumn(3, wxEXPAND);
{
S.SetStretchyCol(1);
S.StartHorizontalLay(wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 0);
{
S.AddTitle(_("View by:"));
S.StartRadioButtonGroup(wxT("/Prefs/KeyConfig/ViewBy"), wxT("tree"));
{
S.Id(ViewByTreeID).TieRadioButton(_("Tree"), wxT("tree"));
S.Id(ViewByNameID).TieRadioButton(_("Name"), wxT("name"));
S.Id(ViewByKeyID).TieRadioButton(_("Key"), wxT("key"));
}
S.EndRadioButtonGroup();
}
S.EndHorizontalLay();
S.StartHorizontalLay(wxALIGN_CENTER|wxALIGN_CENTER_VERTICAL, 0);
{
// just a spacer
}
S.EndHorizontalLay();
S.StartHorizontalLay(wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 0);
{
S.AddTitle(_("Filter:"));
if (!mFilter) {
mFilter = new wxTextCtrl(this,
FilterID,
wxT(""),
wxDefaultPosition,
#if defined(__WXMAC__)
wxSize(300, -1),
#else
wxSize(210, -1),
#endif
wxTE_PROCESS_ENTER);
mFilter->SetName(_("Filter"));
mFilter->Connect(wxEVT_KEY_DOWN,
wxKeyEventHandler(KeyConfigPrefs::OnFilterKeyDown),
NULL,
this);
mFilter->Connect(wxEVT_CHAR,
wxKeyEventHandler(KeyConfigPrefs::OnFilterChar),
NULL,
this);
}
S.AddWindow(mFilter, wxALIGN_RIGHT);
}
S.EndHorizontalLay();
}
S.EndThreeColumn();
S.AddSpace(-1, 2);
S.StartHorizontalLay(wxEXPAND, 1);
{
if (!mView) {
mView = new KeyView(S.GetParent(), CommandsListID);
mView->SetName(_("Bindings"));
}
S.Prop(true);
S.AddWindow(mView, wxEXPAND);
}
S.EndHorizontalLay();
S.StartThreeColumn();
{
if (!mKey) {
mKey = new wxTextCtrl(this,
CurrentComboID,
wxT(""),
wxDefaultPosition,
#if defined(__WXMAC__)
wxSize(300, -1));
#else
wxSize(210, -1));
#endif
mKey->SetName(_("Hotkey"));
mKey->Connect(wxEVT_KEY_DOWN,
wxKeyEventHandler(KeyConfigPrefs::OnHotkeyKeyDown),
NULL,
this);
mKey->Connect(wxEVT_CHAR,
wxKeyEventHandler(KeyConfigPrefs::OnHotkeyChar),
NULL,
this);
mKey->Connect(wxEVT_KILL_FOCUS,
wxFocusEventHandler(KeyConfigPrefs::OnHotkeyKillFocus),
NULL,
this);
}
S.AddWindow(mKey);
/* i18n-hint: (verb)*/
S.Id(SetButtonID).AddButton(_("Set"));
S.Id(ClearButtonID).AddButton(_("Cl&ear"));
}
S.EndThreeColumn();
#if defined(__WXMAC__)
S.AddFixedText(_("Note: Pressing Cmd+Q will quit. All other keys are valid."));
#endif
S.StartThreeColumn();
{
S.Id(ImportButtonID).AddButton(_("&Import..."));
S.Id(ExportButtonID).AddButton(_("&Export..."));
S.Id(AssignDefaultsButtonID).AddButton(_("&Defaults"));
}
S.EndThreeColumn();
}
S.EndStatic();
}
void KeyConfigPrefs::RefreshBindings()
{
wxArrayString Labels;
wxArrayString Categories;
wxArrayString Prefixes;
mManager->GetAllCommandData(
mNames,
mKeys,
mDefaultKeys,
Labels,
Categories,
Prefixes,
true); // True to include effects (list items), false otherwise.
mView->RefreshBindings(mNames,
Categories,
Prefixes,
Labels,
mKeys);
mNewKeys = mKeys;
}
void KeyConfigPrefs::OnImport(wxCommandEvent & WXUNUSED(event))
{
wxString file = wxT("Audacity-keys.xml");
wxString path = gPrefs->Read(wxT("/DefaultOpenPath"),
::wxGetCwd());
file = FileSelector(_("Select an XML file containing Audacity keyboard shortcuts..."),
path,
file,
wxT(""),
_("XML files (*.xml)|*.xml|All files (*.*)|*.*"),
wxRESIZE_BORDER,
this);
if (!file) {
return;
}
path = wxPathOnly(file);
gPrefs->Write(wxT("/DefaultOpenPath"), path);
gPrefs->Flush();
XMLFileReader reader;
if (!reader.Parse(mManager, file)) {
wxMessageBox(reader.GetErrorStr(),
_("Error Importing Keyboard Shortcuts"),
wxOK | wxCENTRE, this);
}
RefreshBindings();
}
void KeyConfigPrefs::OnExport(wxCommandEvent & WXUNUSED(event))
{
wxString file = wxT("Audacity-keys.xml");
wxString path = gPrefs->Read(wxT("/DefaultExportPath"),
::wxGetCwd());
file = FileSelector(_("Export Keyboard Shortcuts As:"),
path,
file,
wxT("xml"),
_("XML files (*.xml)|*.xml|All files (*.*)|*.*"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
this);
if (!file) {
return;
}
path = wxPathOnly(file);
gPrefs->Write(wxT("/DefaultExportPath"), path);
gPrefs->Flush();
XMLFileWriter prefFile;
try
{
prefFile.Open(file, wxT("wb"));
mManager->WriteXML(prefFile);
prefFile.Close();
}
catch (XMLFileWriterException* pException)
{
wxMessageBox(_("Couldn't write to file: ") + file,
_("Error Exporting Keyboard Shortcuts"),
wxOK | wxCENTRE, this);
delete pException;
}
}
void KeyConfigPrefs::OnDefaults(wxCommandEvent & WXUNUSED(event))
{
mNewKeys = mDefaultKeys;
for (size_t i = 0; i < mNewKeys.GetCount(); i++) {
mManager->SetKeyFromIndex(i, mNewKeys[i]);
}
RefreshBindings();
}
void KeyConfigPrefs::OnHotkeyKeyDown(wxKeyEvent & e)
{
wxTextCtrl *t = (wxTextCtrl *)e.GetEventObject();
// Make sure we can navigate away from the hotkey textctrl.
// On Linux and OSX, it an get stuck, but it doesn't hurt
// to do it for Windows as well.
//
// Mac note: Don't waste time trying to figure out why the
// focus goes back to the prefs tree. Unless Voiceover is
// active, buttons on the Mac do not accept focus and all the
// controls between this one and the tree control are buttons.
if (e.GetKeyCode() == WXK_TAB) {
wxNavigationKeyEvent nevent;
nevent.SetWindowChange(e.ControlDown());
nevent.SetDirection(!e.ShiftDown());
nevent.SetEventObject(t);
nevent.SetCurrentFocus(t);
t->GetParent()->ProcessEvent(nevent);
return;
}
t->SetValue(KeyStringDisplay(KeyEventToKeyString(e)));
}
void KeyConfigPrefs::OnHotkeyChar(wxKeyEvent & WXUNUSED(e))
{
// event.Skip() not performed, so event will not be processed further.
}
void KeyConfigPrefs::OnHotkeyKillFocus(wxFocusEvent & e)
{
if (mKey->GetValue().IsEmpty() && mCommandSelected != wxNOT_FOUND) {
mKey->AppendText(mView->GetKey(mCommandSelected));
}
e.Skip();
}
void KeyConfigPrefs::OnFilterKeyDown(wxKeyEvent & e)
{
wxTextCtrl *t = (wxTextCtrl *)e.GetEventObject();
int keycode = e.GetKeyCode();
// Make sure we can navigate away from the hotkey textctrl.
// On Linux and OSX, it an get stuck, but it doesn't hurt
// to do it for Windows as well.
if (keycode == WXK_TAB) {
wxNavigationKeyEvent nevent;
nevent.SetWindowChange(e.ControlDown());
nevent.SetDirection(!e.ShiftDown());
nevent.SetEventObject(t);
nevent.SetCurrentFocus(t);
t->GetParent()->ProcessEvent(nevent);
return;
}
if (mViewType == ViewByKey) {
wxString key = KeyStringDisplay(KeyEventToKeyString(e));
t->SetValue(key);
if (key != wxEmptyString) {
mView->SetFilter(t->GetValue());
}
}
else
{
if (keycode == WXK_RETURN) {
mView->SetFilter(t->GetValue());
}
else {
e.Skip();
}
}
}
void KeyConfigPrefs::OnFilterChar(wxKeyEvent & e)
{
if (mViewType != ViewByKey)
{
e.Skip();
}
}
// Given a hotkey combination, returns the name (description) of the
// corresponding command, or the empty string if none is found.
wxString KeyConfigPrefs::NameFromKey(const wxString & key)
{
int i = mNewKeys.Index(key);
if (i == wxNOT_FOUND) {
return wxEmptyString;
}
return mNames[i];
}
// Sets the selected command to have this key
// This is not yet a committed change, which will happen on a save.
void KeyConfigPrefs::SetKeyForSelected(const wxString & key)
{
int index = mView->GetIndex(mCommandSelected);
mView->SetKey(mCommandSelected, key);
mManager->SetKeyFromIndex(index, key);
mNewKeys[index] = key;
}
void KeyConfigPrefs::OnSet(wxCommandEvent & WXUNUSED(event))
{
wxString newKey = mKey->GetValue();
wxString alreadyAssignedName = NameFromKey(newKey);
// Prevent same hotkey combination being used twice.
if (!alreadyAssignedName.IsEmpty()) {
wxMessageBox(
wxString::Format(
_("The keyboard shortcut '%s' is already assigned to:\n\n'%s'"),
newKey.c_str(),
alreadyAssignedName.c_str()),
_("Error"), wxICON_STOP | wxCENTRE, this);
return;
}
SetKeyForSelected(newKey);
}
void KeyConfigPrefs::OnClear(wxCommandEvent& WXUNUSED(event))
{
mKey->Clear();
SetKeyForSelected(wxEmptyString);
}
void KeyConfigPrefs::OnSelected(wxCommandEvent & e)
{
mCommandSelected = e.GetInt();
mKey->Clear();
mKey->AppendText(mView->GetKey(mCommandSelected));
}
void KeyConfigPrefs::OnViewBy(wxCommandEvent & e)
{
switch (e.GetId())
{
case ViewByTreeID:
mViewType = ViewByTree;
break;
case ViewByNameID:
mViewType = ViewByName;
break;
case ViewByKeyID:
mViewType = ViewByKey;
break;
}
mView->SetView(mViewType);
}
bool KeyConfigPrefs::Apply()
{
for (size_t i = 0; i < mNames.GetCount(); i++) {
wxString dkey = KeyStringNormalize(mDefaultKeys[i]);
wxString name = wxT("/NewKeys/") + mNames[i];
wxString key = KeyStringNormalize(mNewKeys[i]);
if (gPrefs->HasEntry(name)) {
if (key != KeyStringNormalize(gPrefs->Read(name, key))) {
gPrefs->Write(name, key);
}
if (key == dkey) {
gPrefs->DeleteEntry(name);
}
}
else {
if (key != dkey) {
gPrefs->Write(name, key);
}
}
}
return gPrefs->Flush();
}
void KeyConfigPrefs::Cancel()
{
// Restore original key values
for (size_t i = 0; i < mNames.GetCount(); i++) {
mManager->SetKeyFromIndex(i, mKeys[i]);
}
return;
}
#else
//
// KeyConfigPrefs
//
@@ -64,7 +597,6 @@ BEGIN_EVENT_TABLE(KeyConfigPrefs, PrefsPanel)
EVT_BUTTON(ExportButtonID, KeyConfigPrefs::OnExport)
EVT_BUTTON(ImportButtonID, KeyConfigPrefs::OnImport)
EVT_CHOICE(CategoryID, KeyConfigPrefs::OnCategory)
EVT_LIST_COL_CLICK(CommandsListID, KeyConfigPrefs::OnSort)
EVT_LIST_ITEM_SELECTED(CommandsListID, KeyConfigPrefs::OnItemSelected)
EVT_LIST_KEY_DOWN(CommandsListID, KeyConfigPrefs::OnKeyDown)
END_EVENT_TABLE()
@@ -184,54 +716,27 @@ void KeyConfigPrefs::PopulateOrExchange(ShuttleGui & S)
void KeyConfigPrefs::CreateList()
{
mList->InsertColumn(CommandColumn, _("Command"), wxLIST_FORMAT_LEFT);
mList->InsertColumn(KeyComboColumn, _("Combination"), wxLIST_FORMAT_LEFT);
mList->InsertColumn(KeyComboColumn, _("Key Combination"), wxLIST_FORMAT_LEFT);
RepopulateBindingsList();
mList->SetColumnWidth(CommandColumn, wxLIST_AUTOSIZE);
mList->SetColumnWidth(KeyComboColumn, wxLIST_AUTOSIZE);
}
int KeyConfigPrefs::SortItems(long item1, long item2)
{
if (mSortCol->Item(item1) < mSortCol->Item(item2)) {
return -1 * mSortDir;
}
if (mSortCol->Item(item1) > mSortCol->Item(item2)) {
return 1 * mSortDir;
}
return 0;
mList->SetColumnWidth(KeyComboColumn, 250);
}
static int wxCALLBACK SortCallback(long item1, long item2, long sortData)
{
return ((KeyConfigPrefs *)sortData)->SortItems(item1, item2);
}
wxArrayString *names = (wxArrayString *) sortData;
void KeyConfigPrefs::Sort(int column)
{
wxArrayString *data = NULL;
switch(column)
{
case CommandColumn:
data = &mLabels;
break;
case KeyComboColumn:
data = &mKeys;
break;
if (names->Item(item1) < names->Item(item2)) {
return -1;
}
mSortDir = -mSortDir;
if (mSortCol != data) {
mSortDir = 1;
if (names->Item(item1) > names->Item(item2)) {
return 1;
}
mSortCol = data;
mList->SortItems(SortCallback, (long) this);
return 0;
}
void KeyConfigPrefs::RepopulateBindingsList()
@@ -240,9 +745,8 @@ void KeyConfigPrefs::RepopulateBindingsList()
mList->DeleteAllItems(); // Delete contents, but not the column headers.
mNames.Clear();
mLabels.Clear();
mDefaultKeys.Clear();
wxArrayString Keys,Labels,Categories,Prefixes;
wxArrayString Keys,Labels,Categories;
mManager->GetAllCommandData(
mNames,
@@ -250,7 +754,6 @@ void KeyConfigPrefs::RepopulateBindingsList()
mDefaultKeys,
Labels,
Categories,
Prefixes,
// True to include effects (list items), false otherwise.
true
);
@@ -272,6 +775,10 @@ void KeyConfigPrefs::RepopulateBindingsList()
else
mNewKeys[i] = key; // Make sure mNewKeys is updated.
// if (cat != _("All") && ! Categories[i].StartsWith(cat)) {
if (cat != _("All") && ! (Categories[i]== cat)) {
continue;
}
wxString label;
// Labels for undo and redo change according to the last command
@@ -288,18 +795,7 @@ void KeyConfigPrefs::RepopulateBindingsList()
}
label = wxMenuItem::GetLabelFromText(label.BeforeFirst(wxT('\t')));
if (!Prefixes[i].IsEmpty()) {
label = wxMenuItem::GetLabelFromText(Prefixes[i]) + wxT(" - ") + label;
}
if (cat == _("All")) {
label = Categories[i] + wxT(" - ") + label;
}
mLabels.Add(label);
// if (cat != _("All") && ! Categories[i].StartsWith(cat)) {
if (cat != _("All") && ! (Categories[i] == cat)) {
continue;
}
mList->InsertItem(ndx, label);
mList->SetItem(ndx, KeyComboColumn, key);
mList->SetItemData(ndx, i);
@@ -309,9 +805,7 @@ void KeyConfigPrefs::RepopulateBindingsList()
ndx++;
}
mSortDir = 1;
mSortCol = NULL;
Sort(CommandColumn);
// mList->SortItems(SortCallback, (long) &mNames);
}
void KeyConfigPrefs::OnImport(wxCommandEvent & WXUNUSED(event))
@@ -561,11 +1055,6 @@ void KeyConfigPrefs::OnCategory(wxCommandEvent & WXUNUSED(event))
RepopulateBindingsList();
}
void KeyConfigPrefs::OnSort(wxListEvent & event)
{
Sort(event.GetColumn());
}
void KeyConfigPrefs::OnItemSelected(wxListEvent & e)
{
mCommandSelected = e.GetIndex();
@@ -622,3 +1111,5 @@ void KeyConfigPrefs::Cancel()
return;
}
#endif

View File

@@ -12,6 +12,70 @@
#ifndef __AUDACITY_KEY_CONFIG_PREFS__
#define __AUDACITY_KEY_CONFIG_PREFS__
#if defined(EXPERIMENTAL_KEY_VIEW)
#include <wx/defs.h>
#include <wx/imaglist.h>
#include <wx/listctrl.h>
#include <wx/srchctrl.h>
#include <wx/textctrl.h>
#include <wx/string.h>
#include "../ShuttleGui.h"
#include "../commands/CommandManager.h"
#include "../widgets/KeyView.h"
#include "PrefsPanel.h"
class KeyConfigPrefs:public PrefsPanel
{
public:
KeyConfigPrefs(wxWindow * parent);
~KeyConfigPrefs();
virtual bool Apply();
virtual void Cancel();
private:
void Populate();
void PopulateOrExchange(ShuttleGui & S);
void RefreshBindings();
wxString NameFromKey(const wxString & key);
void SetKeyForSelected(const wxString & key);
void OnViewBy(wxCommandEvent & e);
void OnDefaults(wxCommandEvent & e);
void OnImport(wxCommandEvent & e);
void OnExport(wxCommandEvent & e);
void OnSet(wxCommandEvent & e);
void OnClear(wxCommandEvent & e);
void OnSelected(wxCommandEvent & e);
void OnHotkeyKeyDown(wxKeyEvent & e);
void OnHotkeyChar(wxKeyEvent & e);
void OnHotkeyKillFocus(wxFocusEvent & e);
void OnFilterKeyDown(wxKeyEvent & e);
void OnFilterChar(wxKeyEvent & e);
KeyView *mView;
wxTextCtrl *mFilter;
wxTextCtrl *mKey;
ViewByType mViewType;
CommandManager *mManager;
int mCommandSelected;
wxArrayString mNames;
wxArrayString mDefaultKeys;
wxArrayString mKeys;
wxArrayString mNewKeys; // Used for work in progress.
DECLARE_EVENT_TABLE();
};
#else
#include <wx/defs.h>
#include <wx/listctrl.h>
#include <wx/textctrl.h>
@@ -30,16 +94,13 @@ class KeyConfigPrefs:public PrefsPanel
virtual bool Apply();
virtual void Cancel();
int SortItems(long item1, long item2);
private:
private:
void Populate();
void PopulateOrExchange(ShuttleGui & S);
void CreateList();
void RepopulateBindingsList();
wxString NameFromKey( const wxString & key );
void SetKeyForSelected( const wxString & key );
void Sort(int column);
void OnDefaults(wxCommandEvent & e);
void OnImport(wxCommandEvent & e);
@@ -47,7 +108,6 @@ private:
void OnSet(wxCommandEvent & e);
void OnClear(wxCommandEvent & e);
void OnCategory(wxCommandEvent & e);
void OnSort(wxListEvent & e);
void OnItemSelected(wxListEvent & e);
void OnKeyDown(wxListEvent & e);
@@ -63,14 +123,12 @@ private:
wxArrayString mCats;
wxArrayString mNames;
wxArrayString mLabels;
wxArrayString mDefaultKeys;
wxArrayString mKeys;
wxArrayString mNewKeys; // Used for work in progress.
wxArrayString *mSortCol;
int mSortDir;
DECLARE_EVENT_TABLE();
};
#endif#endif
#endif