817 lines
23 KiB
C++
817 lines
23 KiB
C++
/*
|
|
* KeyConfigDlg.cpp
|
|
* ----------------
|
|
* Purpose: Implementation of OpenMPT's keyboard configuration dialog.
|
|
* Notes : (currently none)
|
|
* Authors: OpenMPT Devs
|
|
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
|
*/
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "KeyConfigDlg.h"
|
|
#include "FileDialog.h"
|
|
#include "../soundlib/mod_specifications.h"
|
|
#include "../soundlib/MIDIEvents.h"
|
|
|
|
|
|
OPENMPT_NAMESPACE_BEGIN
|
|
|
|
|
|
//***************************************************************************************//
|
|
// CCustEdit: customised CEdit control to catch keypresses.
|
|
// (does what CHotKeyCtrl does,but better)
|
|
//***************************************************************************************//
|
|
|
|
BEGIN_MESSAGE_MAP(CCustEdit, CEdit)
|
|
ON_WM_SETFOCUS()
|
|
ON_WM_KILLFOCUS()
|
|
ON_MESSAGE(WM_MOD_MIDIMSG, &CCustEdit::OnMidiMsg)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
LRESULT CCustEdit::OnMidiMsg(WPARAM dwMidiDataParam, LPARAM)
|
|
{
|
|
if(!m_isFocussed)
|
|
return 1;
|
|
|
|
uint32 midiData = static_cast<uint32>(dwMidiDataParam);
|
|
const auto byte1 = MIDIEvents::GetDataByte1FromEvent(midiData), byte2 = MIDIEvents::GetDataByte2FromEvent(midiData);
|
|
switch(MIDIEvents::GetTypeFromEvent(midiData))
|
|
{
|
|
case MIDIEvents::evControllerChange:
|
|
if(byte2 != 0)
|
|
{
|
|
SetKey(ModMidi, byte1);
|
|
if(!m_isDummy)
|
|
m_pOptKeyDlg->OnSetKeyChoice();
|
|
}
|
|
break;
|
|
|
|
case MIDIEvents::evNoteOn:
|
|
case MIDIEvents::evNoteOff:
|
|
SetKey(ModMidi, byte1 | 0x80);
|
|
if(!m_isDummy)
|
|
m_pOptKeyDlg->OnSetKeyChoice();
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
BOOL CCustEdit::PreTranslateMessage(MSG *pMsg)
|
|
{
|
|
if(pMsg)
|
|
{
|
|
if(pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN)
|
|
{
|
|
SetKey(CMainFrame::GetInputHandler()->GetModifierMask(), static_cast<UINT>(pMsg->wParam));
|
|
return -1; // Keypress handled, don't pass on message.
|
|
} else if(pMsg->message == WM_KEYUP || pMsg->message == WM_SYSKEYUP)
|
|
{
|
|
//if a key has been released but custom edit box is empty, we have probably just
|
|
//navigated into the box with TAB or SHIFT-TAB. No need to set keychoice.
|
|
if(code != 0 && !m_isDummy)
|
|
m_pOptKeyDlg->OnSetKeyChoice();
|
|
}
|
|
}
|
|
return CEdit::PreTranslateMessage(pMsg);
|
|
}
|
|
|
|
|
|
void CCustEdit::SetKey(FlagSet<Modifiers> inMod, UINT inCode)
|
|
{
|
|
mod = inMod;
|
|
code = inCode;
|
|
//Setup display
|
|
SetWindowText(KeyCombination::GetKeyText(mod, code));
|
|
}
|
|
|
|
|
|
void CCustEdit::OnSetFocus(CWnd *pOldWnd)
|
|
{
|
|
CEdit::OnSetFocus(pOldWnd);
|
|
// Lock the input handler
|
|
CMainFrame::GetInputHandler()->Bypass(true);
|
|
// Accept MIDI input
|
|
CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWnd);
|
|
|
|
m_isFocussed = true;
|
|
}
|
|
|
|
|
|
void CCustEdit::OnKillFocus(CWnd *pNewWnd)
|
|
{
|
|
CEdit::OnKillFocus(pNewWnd);
|
|
//unlock the input handler
|
|
CMainFrame::GetInputHandler()->Bypass(false);
|
|
m_isFocussed = false;
|
|
}
|
|
|
|
|
|
//***************************************************************************************//
|
|
// COptionsKeyboard:
|
|
//
|
|
//***************************************************************************************//
|
|
|
|
// Initialisation
|
|
|
|
BEGIN_MESSAGE_MAP(COptionsKeyboard, CPropertyPage)
|
|
ON_LBN_SELCHANGE(IDC_CHOICECOMBO, &COptionsKeyboard::OnKeyChoiceSelect)
|
|
ON_LBN_SELCHANGE(IDC_COMMAND_LIST, &COptionsKeyboard::OnCommandKeySelChanged)
|
|
ON_LBN_SELCHANGE(IDC_KEYCATEGORY, &COptionsKeyboard::OnCategorySelChanged)
|
|
ON_EN_UPDATE(IDC_CHORDDETECTWAITTIME, &COptionsKeyboard::OnChordWaitTimeChanged) //rewbs.autochord
|
|
ON_COMMAND(IDC_DELETE, &COptionsKeyboard::OnDeleteKeyChoice)
|
|
ON_COMMAND(IDC_RESTORE, &COptionsKeyboard::OnRestoreKeyChoice)
|
|
ON_COMMAND(IDC_LOAD, &COptionsKeyboard::OnLoad)
|
|
ON_COMMAND(IDC_SAVE, &COptionsKeyboard::OnSave)
|
|
ON_COMMAND(IDC_CHECKKEYDOWN, &COptionsKeyboard::OnCheck)
|
|
ON_COMMAND(IDC_CHECKKEYHOLD, &COptionsKeyboard::OnCheck)
|
|
ON_COMMAND(IDC_CHECKKEYUP, &COptionsKeyboard::OnCheck)
|
|
ON_COMMAND(IDC_NOTESREPEAT, &COptionsKeyboard::OnNotesRepeat)
|
|
ON_COMMAND(IDC_NONOTESREPEAT, &COptionsKeyboard::OnNoNotesRepeat)
|
|
ON_COMMAND(IDC_CLEARLOG, &COptionsKeyboard::OnClearLog)
|
|
ON_COMMAND(IDC_RESTORE_KEYMAP, &COptionsKeyboard::OnRestoreDefaultKeymap)
|
|
ON_EN_CHANGE(IDC_FIND, &COptionsKeyboard::OnSearchTermChanged)
|
|
ON_EN_CHANGE(IDC_FINDHOTKEY, &COptionsKeyboard::OnFindHotKey)
|
|
ON_EN_SETFOCUS(IDC_FINDHOTKEY, &COptionsKeyboard::OnClearHotKey)
|
|
ON_WM_DESTROY()
|
|
END_MESSAGE_MAP()
|
|
|
|
void COptionsKeyboard::DoDataExchange(CDataExchange *pDX)
|
|
{
|
|
CPropertyPage::DoDataExchange(pDX);
|
|
DDX_Control(pDX, IDC_KEYCATEGORY, m_cmbCategory);
|
|
DDX_Control(pDX, IDC_COMMAND_LIST, m_lbnCommandKeys);
|
|
DDX_Control(pDX, IDC_CHOICECOMBO, m_cmbKeyChoice);
|
|
DDX_Control(pDX, IDC_CHORDDETECTWAITTIME, m_eChordWaitTime);//rewbs.autochord
|
|
DDX_Control(pDX, IDC_KEYREPORT, m_eReport);
|
|
DDX_Control(pDX, IDC_CUSTHOTKEY, m_eCustHotKey);
|
|
DDX_Control(pDX, IDC_FINDHOTKEY, m_eFindHotKey);
|
|
DDX_Control(pDX, IDC_CHECKKEYDOWN, m_bKeyDown);
|
|
DDX_Control(pDX, IDC_CHECKKEYHOLD, m_bKeyHold);
|
|
DDX_Control(pDX, IDC_CHECKKEYUP, m_bKeyUp);
|
|
DDX_Control(pDX, IDC_FIND, m_eFind);
|
|
}
|
|
|
|
|
|
BOOL COptionsKeyboard::OnSetActive()
|
|
{
|
|
CMainFrame::m_nLastOptionsPage = OPTIONS_PAGE_KEYBOARD;
|
|
return CPropertyPage::OnSetActive();
|
|
}
|
|
|
|
|
|
|
|
BOOL COptionsKeyboard::OnInitDialog()
|
|
{
|
|
CPropertyPage::OnInitDialog();
|
|
m_fullPathName = TrackerSettings::Instance().m_szKbdFile;
|
|
|
|
m_localCmdSet = std::make_unique<CCommandSet>();
|
|
m_localCmdSet->Copy(CMainFrame::GetInputHandler()->m_activeCommandSet.get());
|
|
|
|
//Fill category combo and automatically selects first category
|
|
DefineCommandCategories();
|
|
for(size_t c = 0; c < commandCategories.size(); c++)
|
|
{
|
|
if(commandCategories[c].name && !commandCategories[c].commands.empty())
|
|
m_cmbCategory.SetItemData(m_cmbCategory.AddString(commandCategories[c].name), c);
|
|
}
|
|
m_cmbCategory.SetCurSel(0);
|
|
UpdateDialog();
|
|
|
|
m_eCustHotKey.SetParent(m_hWnd, IDC_CUSTHOTKEY, this);
|
|
m_eFindHotKey.SetParent(m_hWnd, IDC_FINDHOTKEY, this);
|
|
m_eReport.FmtLines(TRUE);
|
|
m_eReport.SetWindowText(_T(""));
|
|
|
|
m_eChordWaitTime.SetWindowText(mpt::cfmt::val(TrackerSettings::Instance().gnAutoChordWaitTime));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CommandCategory::AddCommands(CommandID first, CommandID last, bool addSeparatorAtEnd)
|
|
{
|
|
int count = last - first + 1, val = first;
|
|
commands.insert(commands.end(), count, kcNull);
|
|
std::generate(commands.end() - count, commands.end(), [&val] { return static_cast<CommandID>(val++); });
|
|
if(addSeparatorAtEnd)
|
|
separators.push_back(last);
|
|
}
|
|
|
|
|
|
// Filter commands: We only need user to see a select set off commands
|
|
// for each category
|
|
void COptionsKeyboard::DefineCommandCategories()
|
|
{
|
|
{
|
|
CommandCategory newCat(_T("Global keys"), kCtxAllContexts);
|
|
|
|
newCat.AddCommands(kcStartFile, kcEndFile, true);
|
|
newCat.AddCommands(kcStartPlayCommands, kcEndPlayCommands, true);
|
|
newCat.AddCommands(kcStartEditCommands, kcEndEditCommands, true);
|
|
newCat.AddCommands(kcStartView, kcEndView, true);
|
|
newCat.AddCommands(kcStartMisc, kcEndMisc, true);
|
|
newCat.commands.push_back(kcDummyShortcut);
|
|
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
commandCategories.emplace_back(_T(" General [Top]"), kCtxCtrlGeneral);
|
|
commandCategories.emplace_back(_T(" General [Bottom]"), kCtxViewGeneral);
|
|
commandCategories.emplace_back(_T(" Pattern Editor [Top]"), kCtxCtrlPatterns);
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - Order List"), kCtxCtrlOrderlist);
|
|
|
|
newCat.AddCommands(kcStartOrderlistCommands, kcEndOrderlistCommands);
|
|
newCat.separators.push_back(kcEndOrderlistNavigation);
|
|
newCat.separators.push_back(kcEndOrderlistEdit);
|
|
newCat.separators.push_back(kcEndOrderlistNum);
|
|
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - Quick Channel Settings"), kCtxChannelSettings);
|
|
newCat.AddCommands(kcStartChnSettingsCommands, kcEndChnSettingsCommands);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - General"), kCtxViewPatterns);
|
|
|
|
newCat.AddCommands(kcStartPlainNavigate, kcEndPlainNavigate, true);
|
|
newCat.AddCommands(kcStartJumpSnap, kcEndJumpSnap, true);
|
|
newCat.AddCommands(kcStartHomeEnd, kcEndHomeEnd, true);
|
|
newCat.AddCommands(kcPrevPattern, kcNextSequence, true);
|
|
newCat.AddCommands(kcStartSelect, kcEndSelect, true);
|
|
newCat.AddCommands(kcStartPatternClipboard, kcEndPatternClipboard, true);
|
|
newCat.AddCommands(kcClearRow, kcInsertWholeRowGlobal, true);
|
|
newCat.AddCommands(kcStartChannelKeys, kcEndChannelKeys, true);
|
|
newCat.AddCommands(kcBeginTranspose, kcEndTranspose, true);
|
|
newCat.AddCommands(kcPatternAmplify, kcPatternShrinkSelection, true);
|
|
newCat.AddCommands(kcStartPatternEditMisc, kcEndPatternEditMisc, true);
|
|
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - Note Column"), kCtxViewPatternsNote);
|
|
|
|
newCat.AddCommands(kcVPStartNotes, kcVPEndNotes, true);
|
|
newCat.AddCommands(kcSetOctave0, kcSetOctave9, true);
|
|
newCat.AddCommands(kcStartNoteMisc, kcEndNoteMisc);
|
|
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - Instrument Column"), kCtxViewPatternsIns);
|
|
newCat.AddCommands(kcSetIns0, kcSetIns9);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - Volume Column"), kCtxViewPatternsVol);
|
|
newCat.AddCommands(kcSetVolumeStart, kcSetVolumeEnd);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - Effect Column"), kCtxViewPatternsFX);
|
|
newCat.AddCommands(kcSetFXStart, kcSetFXEnd);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Pattern Editor - Effect Parameter Column"), kCtxViewPatternsFXparam);
|
|
newCat.AddCommands(kcSetFXParam0, kcSetFXParamF);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Sample [Top]"), kCtxCtrlSamples);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Sample Editor"), kCtxViewSamples);
|
|
|
|
newCat.AddCommands(kcStartSampleEditing, kcEndSampleEditing, true);
|
|
newCat.AddCommands(kcStartSampleMisc, kcEndSampleMisc, true);
|
|
newCat.AddCommands(kcStartSampleCues, kcEndSampleCueGroup);
|
|
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Instrument Editor"), kCtxCtrlInstruments);
|
|
newCat.AddCommands(kcStartInstrumentCtrlMisc, kcEndInstrumentCtrlMisc);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Envelope Editor"), kCtxViewInstruments);
|
|
newCat.AddCommands(kcStartInstrumentMisc, kcEndInstrumentMisc);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
commandCategories.emplace_back(_T(" Comments [Top]"), kCtxCtrlComments);
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Comments [Bottom]"), kCtxViewComments);
|
|
newCat.AddCommands(kcStartCommentsCommands, kcEndCommentsCommands);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
|
|
{
|
|
CommandCategory newCat(_T(" Plugin Editor"), kCtxVSTGUI);
|
|
newCat.AddCommands(kcStartVSTGUICommands, kcEndVSTGUICommands);
|
|
commandCategories.push_back(newCat);
|
|
}
|
|
}
|
|
|
|
|
|
// Pure GUI methods
|
|
|
|
void COptionsKeyboard::UpdateDialog()
|
|
{
|
|
OnCategorySelChanged(); // Fills command list and automatically selects first command.
|
|
OnCommandKeySelChanged(); // Fills command key choice list for that command and automatically selects first choice.
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnKeyboardChanged()
|
|
{
|
|
OnSettingsChanged();
|
|
UpdateDialog();
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnCategorySelChanged()
|
|
{
|
|
int cat = static_cast<int>(m_cmbCategory.GetItemData(m_cmbCategory.GetCurSel()));
|
|
|
|
if(cat >= 0 && cat != m_curCategory)
|
|
{
|
|
// Changed category
|
|
UpdateShortcutList(cat);
|
|
}
|
|
}
|
|
|
|
|
|
// Force last active category to be selected in dropdown menu.
|
|
void COptionsKeyboard::UpdateCategory()
|
|
{
|
|
for(int i = 0; i < m_cmbCategory.GetCount(); i++)
|
|
{
|
|
if((int)m_cmbCategory.GetItemData(i) == m_curCategory)
|
|
{
|
|
m_cmbCategory.SetCurSel(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void COptionsKeyboard::OnSearchTermChanged()
|
|
{
|
|
CString findString;
|
|
m_eFind.GetWindowText(findString);
|
|
|
|
if(findString.IsEmpty())
|
|
{
|
|
UpdateCategory();
|
|
}
|
|
UpdateShortcutList(findString.IsEmpty() ? m_curCategory : -1);
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnFindHotKey()
|
|
{
|
|
if(m_eFindHotKey.code == 0)
|
|
{
|
|
UpdateCategory();
|
|
}
|
|
UpdateShortcutList(m_eFindHotKey.code == 0 ? m_curCategory : -1);
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnClearHotKey()
|
|
{
|
|
// Focus key search: Clear input
|
|
m_eFindHotKey.SetKey(ModNone, 0);
|
|
}
|
|
|
|
|
|
// Fills command list and automatically selects first command.
|
|
void COptionsKeyboard::UpdateShortcutList(int category)
|
|
{
|
|
CString findString;
|
|
m_eFind.GetWindowText(findString);
|
|
findString.MakeLower();
|
|
|
|
const bool searchByName = !findString.IsEmpty(), searchByKey = (m_eFindHotKey.code != 0);
|
|
const bool doSearch = (searchByName || searchByKey);
|
|
|
|
int firstCat = category, lastCat = category;
|
|
if(category == -1)
|
|
{
|
|
// We will search in all categories
|
|
firstCat = 0;
|
|
lastCat = static_cast<int>(commandCategories.size()) - 1;
|
|
}
|
|
|
|
CommandID curCommand = static_cast<CommandID>(m_lbnCommandKeys.GetItemData(m_lbnCommandKeys.GetCurSel()));
|
|
m_lbnCommandKeys.ResetContent();
|
|
|
|
for(int cat = firstCat; cat <= lastCat; cat++)
|
|
{
|
|
// When searching, we also add the category names to the list.
|
|
bool addCategoryName = (firstCat != lastCat);
|
|
|
|
for(size_t cmd = 0; cmd < commandCategories[cat].commands.size(); cmd++)
|
|
{
|
|
CommandID com = (CommandID)commandCategories[cat].commands[cmd];
|
|
|
|
CString cmdText = m_localCmdSet->GetCommandText(com);
|
|
bool addKey = true;
|
|
|
|
if(searchByKey)
|
|
{
|
|
addKey = false;
|
|
int numChoices = m_localCmdSet->GetKeyListSize(com);
|
|
for(int choice = 0; choice < numChoices; choice++)
|
|
{
|
|
const KeyCombination &kc = m_localCmdSet->GetKey(com, choice);
|
|
if(kc.KeyCode() == m_eFindHotKey.code && kc.Modifier() == m_eFindHotKey.mod)
|
|
{
|
|
addKey = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(searchByName && addKey)
|
|
{
|
|
addKey = (cmdText.MakeLower().Find(findString) >= 0);
|
|
}
|
|
|
|
if(addKey)
|
|
{
|
|
m_curCategory = cat;
|
|
|
|
if(!m_localCmdSet->isHidden(com))
|
|
{
|
|
if(doSearch && addCategoryName)
|
|
{
|
|
const CString catName = _T("------ ") + commandCategories[cat].name.Trim() + _T(" ------");
|
|
m_lbnCommandKeys.SetItemData(m_lbnCommandKeys.AddString(catName), DWORD_PTR(-1));
|
|
addCategoryName = false;
|
|
}
|
|
|
|
int item = m_lbnCommandKeys.AddString(m_localCmdSet->GetCommandText(com));
|
|
m_lbnCommandKeys.SetItemData(item, com);
|
|
|
|
if(curCommand == com)
|
|
{
|
|
// Keep selection on previously selected string
|
|
m_lbnCommandKeys.SetCurSel(item);
|
|
}
|
|
}
|
|
|
|
if(commandCategories[cat].SeparatorAt(com))
|
|
m_lbnCommandKeys.SetItemData(m_lbnCommandKeys.AddString(_T("------------------------------------------------------")), DWORD_PTR(-1));
|
|
}
|
|
}
|
|
}
|
|
|
|
if(m_lbnCommandKeys.GetCurSel() == -1)
|
|
{
|
|
m_lbnCommandKeys.SetCurSel(0);
|
|
}
|
|
OnCommandKeySelChanged();
|
|
}
|
|
|
|
|
|
// Fills key choice list and automatically selects first key choice
|
|
void COptionsKeyboard::OnCommandKeySelChanged()
|
|
{
|
|
const CommandID cmd = static_cast<CommandID>(m_lbnCommandKeys.GetItemData(m_lbnCommandKeys.GetCurSel()));
|
|
CString str;
|
|
|
|
//Separator
|
|
if(cmd == kcNull)
|
|
{
|
|
m_cmbKeyChoice.SetWindowText(_T(""));
|
|
m_cmbKeyChoice.EnableWindow(FALSE);
|
|
m_eCustHotKey.SetWindowText(_T(""));
|
|
m_eCustHotKey.EnableWindow(FALSE);
|
|
m_bKeyDown.SetCheck(0);
|
|
m_bKeyDown.EnableWindow(FALSE);
|
|
m_bKeyHold.SetCheck(0);
|
|
m_bKeyHold.EnableWindow(FALSE);
|
|
m_bKeyUp.SetCheck(0);
|
|
m_bKeyUp.EnableWindow(FALSE);
|
|
m_curCommand = kcNull;
|
|
}
|
|
|
|
//Fill "choice" list
|
|
else if((cmd >= 0) && (cmd != m_curCommand) || m_forceUpdate) // Have we changed command?
|
|
{
|
|
m_forceUpdate = false;
|
|
|
|
m_cmbKeyChoice.EnableWindow(TRUE);
|
|
m_eCustHotKey.EnableWindow(TRUE);
|
|
m_bKeyDown.EnableWindow(TRUE);
|
|
m_bKeyHold.EnableWindow(TRUE);
|
|
m_bKeyUp.EnableWindow(TRUE);
|
|
|
|
m_curCommand = cmd;
|
|
m_curCategory = GetCategoryFromCommandID(cmd);
|
|
|
|
m_cmbKeyChoice.ResetContent();
|
|
int numChoices = m_localCmdSet->GetKeyListSize(cmd);
|
|
if((cmd < kcNumCommands) && (numChoices > 0))
|
|
{
|
|
for(int i = 0; i < numChoices; i++)
|
|
{
|
|
CString s = MPT_CFORMAT("Choice {} (of {})")(i + 1, numChoices);
|
|
m_cmbKeyChoice.SetItemData(m_cmbKeyChoice.AddString(s), i);
|
|
}
|
|
}
|
|
m_cmbKeyChoice.SetItemData(m_cmbKeyChoice.AddString(_T("<new>")), numChoices);
|
|
m_cmbKeyChoice.SetCurSel(0);
|
|
m_curKeyChoice = -1;
|
|
OnKeyChoiceSelect();
|
|
}
|
|
}
|
|
|
|
//Fills or clears key choice info
|
|
void COptionsKeyboard::OnKeyChoiceSelect()
|
|
{
|
|
int choice = static_cast<int>(m_cmbKeyChoice.GetItemData(m_cmbKeyChoice.GetCurSel()));
|
|
CommandID cmd = m_curCommand;
|
|
|
|
//If nothing there, clear
|
|
if(cmd == kcNull || choice >= m_localCmdSet->GetKeyListSize(cmd) || choice < 0)
|
|
{
|
|
m_curKeyChoice = choice;
|
|
m_forceUpdate = true;
|
|
m_eCustHotKey.SetKey(ModNone, 0);
|
|
m_bKeyDown.SetCheck(0);
|
|
m_bKeyHold.SetCheck(0);
|
|
m_bKeyUp.SetCheck(0);
|
|
return;
|
|
}
|
|
|
|
//else, if changed, Fill
|
|
if(choice != m_curKeyChoice || m_forceUpdate)
|
|
{
|
|
m_curKeyChoice = choice;
|
|
m_forceUpdate = false;
|
|
KeyCombination kc = m_localCmdSet->GetKey(cmd, choice);
|
|
m_eCustHotKey.SetKey(kc.Modifier(), kc.KeyCode());
|
|
|
|
m_bKeyDown.SetCheck((kc.EventType() & kKeyEventDown) ? BST_CHECKED : BST_UNCHECKED);
|
|
m_bKeyHold.SetCheck((kc.EventType() & kKeyEventRepeat) ? BST_CHECKED : BST_UNCHECKED);
|
|
m_bKeyUp.SetCheck((kc.EventType() & kKeyEventUp) ? BST_CHECKED : BST_UNCHECKED);
|
|
}
|
|
}
|
|
|
|
void COptionsKeyboard::OnChordWaitTimeChanged()
|
|
{
|
|
CString s;
|
|
UINT val;
|
|
m_eChordWaitTime.GetWindowText(s);
|
|
val = _tstoi(s);
|
|
if(val > 5000)
|
|
{
|
|
val = 5000;
|
|
m_eChordWaitTime.SetWindowText(_T("5000"));
|
|
}
|
|
OnSettingsChanged();
|
|
}
|
|
|
|
// Change handling
|
|
|
|
void COptionsKeyboard::OnRestoreKeyChoice()
|
|
{
|
|
KeyCombination kc;
|
|
CommandID cmd = m_curCommand;
|
|
|
|
CInputHandler *ih = CMainFrame::GetInputHandler();
|
|
|
|
// Do nothing if there's nothing to restore
|
|
if(cmd == kcNull || m_curKeyChoice < 0 || m_curKeyChoice >= ih->GetKeyListSize(cmd))
|
|
{
|
|
::MessageBeep(MB_ICONWARNING);
|
|
return;
|
|
}
|
|
|
|
// Restore current key combination choice for currently selected command.
|
|
kc = ih->m_activeCommandSet->GetKey(cmd, m_curKeyChoice);
|
|
m_localCmdSet->Remove(m_curKeyChoice, cmd);
|
|
m_localCmdSet->Add(kc, cmd, true, m_curKeyChoice);
|
|
|
|
ForceUpdateGUI();
|
|
return;
|
|
}
|
|
|
|
void COptionsKeyboard::OnDeleteKeyChoice()
|
|
{
|
|
CommandID cmd = m_curCommand;
|
|
|
|
// Do nothing if there's no key defined for this slot.
|
|
if(m_curCommand == kcNull || m_curKeyChoice < 0 || m_curKeyChoice >= m_localCmdSet->GetKeyListSize(cmd))
|
|
{
|
|
::MessageBeep(MB_ICONWARNING);
|
|
return;
|
|
}
|
|
|
|
// Delete current key combination choice for currently selected command.
|
|
m_localCmdSet->Remove(m_curKeyChoice, cmd);
|
|
|
|
ForceUpdateGUI();
|
|
return;
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnSetKeyChoice()
|
|
{
|
|
CommandID cmd = m_curCommand;
|
|
if(cmd == kcNull)
|
|
{
|
|
Reporting::Warning("Invalid slot.", "Invalid key data", this);
|
|
return;
|
|
}
|
|
|
|
FlagSet<KeyEventType> event = kKeyEventNone;
|
|
if(m_bKeyDown.GetCheck() != BST_UNCHECKED)
|
|
event |= kKeyEventDown;
|
|
if(m_bKeyHold.GetCheck() != BST_UNCHECKED)
|
|
event |= kKeyEventRepeat;
|
|
if(m_bKeyUp.GetCheck() != BST_UNCHECKED)
|
|
event |= kKeyEventUp;
|
|
|
|
KeyCombination kc((commandCategories[m_curCategory]).id, m_eCustHotKey.mod, m_eCustHotKey.code, event);
|
|
//detect invalid input
|
|
if(!kc.KeyCode())
|
|
{
|
|
Reporting::Warning("You need to say to which key you'd like to map this command to.", "Invalid key data", this);
|
|
return;
|
|
}
|
|
if(!kc.EventType())
|
|
{
|
|
::MessageBeep(MB_ICONWARNING);
|
|
kc.EventType(kKeyEventDown);
|
|
}
|
|
|
|
bool add = true;
|
|
std::pair<CommandID, KeyCombination> conflictCmd;
|
|
if((conflictCmd = m_localCmdSet->IsConflicting(kc, cmd)).first != kcNull
|
|
&& conflictCmd.first != cmd
|
|
&& !m_localCmdSet->IsCrossContextConflict(kc, conflictCmd.second))
|
|
{
|
|
ConfirmAnswer delOld = Reporting::Confirm(_T("New shortcut (") + kc.GetKeyText() + _T(") has the same key combination as ") + m_localCmdSet->GetCommandText(conflictCmd.first) + _T(" in ") + conflictCmd.second.GetContextText() + _T(".\nDo you want to delete the other shortcut, only keeping the new one?"), _T("Shortcut Conflict"), true, false, this);
|
|
if(delOld == cnfYes)
|
|
{
|
|
m_localCmdSet->Remove(conflictCmd.second, conflictCmd.first);
|
|
} else if(delOld == cnfCancel)
|
|
{
|
|
// Cancel altogther; restore original choice
|
|
add = false;
|
|
if(m_curKeyChoice >= 0 && m_curKeyChoice < m_localCmdSet->GetKeyListSize(cmd))
|
|
{
|
|
KeyCombination origKc = m_localCmdSet->GetKey(cmd, m_curKeyChoice);
|
|
m_eCustHotKey.SetKey(origKc.Modifier(), origKc.KeyCode());
|
|
} else
|
|
{
|
|
m_eCustHotKey.SetWindowText(_T(""));
|
|
}
|
|
}
|
|
}
|
|
|
|
if(add)
|
|
{
|
|
CString report, reportHistory;
|
|
//process valid input
|
|
m_localCmdSet->Remove(m_curKeyChoice, cmd);
|
|
report = m_localCmdSet->Add(kc, cmd, true, m_curKeyChoice);
|
|
|
|
//Update log
|
|
m_eReport.GetWindowText(reportHistory);
|
|
m_eReport.SetWindowText(report + reportHistory);
|
|
ForceUpdateGUI();
|
|
}
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnOK()
|
|
{
|
|
CMainFrame::GetInputHandler()->SetNewCommandSet(m_localCmdSet.get());
|
|
|
|
CString cs;
|
|
m_eChordWaitTime.GetWindowText(cs);
|
|
TrackerSettings::Instance().gnAutoChordWaitTime = _tstoi(cs);
|
|
|
|
CPropertyPage::OnOK();
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnDestroy()
|
|
{
|
|
CPropertyPage::OnDestroy();
|
|
m_localCmdSet = nullptr;
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnLoad()
|
|
{
|
|
auto dlg = OpenFileDialog()
|
|
.DefaultExtension("mkb")
|
|
.DefaultFilename(m_fullPathName)
|
|
.ExtensionFilter("OpenMPT Key Bindings (*.mkb)|*.mkb||")
|
|
.AddPlace(theApp.GetInstallPkgPath() + P_("extraKeymaps"))
|
|
.WorkingDirectory(TrackerSettings::Instance().m_szKbdFile);
|
|
if(!dlg.Show(this))
|
|
return;
|
|
|
|
m_fullPathName = dlg.GetFirstFile();
|
|
m_localCmdSet->LoadFile(m_fullPathName);
|
|
ForceUpdateGUI();
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnSave()
|
|
{
|
|
auto dlg = SaveFileDialog()
|
|
.DefaultExtension("mkb")
|
|
.DefaultFilename(m_fullPathName)
|
|
.ExtensionFilter("OpenMPT Key Bindings (*.mkb)|*.mkb||")
|
|
.WorkingDirectory(TrackerSettings::Instance().m_szKbdFile);
|
|
if(!dlg.Show(this))
|
|
return;
|
|
|
|
m_fullPathName = dlg.GetFirstFile();
|
|
m_localCmdSet->SaveFile(m_fullPathName);
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnNotesRepeat()
|
|
{
|
|
m_localCmdSet->QuickChange_NotesRepeat(true);
|
|
ForceUpdateGUI();
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnNoNotesRepeat()
|
|
{
|
|
m_localCmdSet->QuickChange_NotesRepeat(false);
|
|
ForceUpdateGUI();
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::ForceUpdateGUI()
|
|
{
|
|
m_forceUpdate = true; // m_nCurKeyChoice and m_nCurHotKey haven't changed, yet we still want to update.
|
|
int ntmpChoice = m_curKeyChoice; // next call will overwrite m_nCurKeyChoice
|
|
OnCommandKeySelChanged(); // update keychoice list
|
|
m_cmbKeyChoice.SetCurSel(ntmpChoice); // select fresh keychoice (thus restoring m_nCurKeyChoice)
|
|
OnKeyChoiceSelect(); // update key data
|
|
OnSettingsChanged(); // Enable "apply" button
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnClearLog()
|
|
{
|
|
m_eReport.SetWindowText(_T(""));
|
|
ForceUpdateGUI();
|
|
}
|
|
|
|
|
|
void COptionsKeyboard::OnRestoreDefaultKeymap()
|
|
{
|
|
if(Reporting::Confirm("Discard all custom changes and restore default key configuration?", false, true, this) == cnfYes)
|
|
{
|
|
m_localCmdSet->LoadDefaultKeymap();
|
|
ForceUpdateGUI();
|
|
}
|
|
}
|
|
|
|
|
|
int COptionsKeyboard::GetCategoryFromCommandID(CommandID command) const
|
|
{
|
|
for(size_t cat = 0; cat < commandCategories.size(); cat++)
|
|
{
|
|
const auto &cmds = commandCategories[cat].commands;
|
|
if(mpt::contains(cmds, command))
|
|
return static_cast<int>(cat);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
OPENMPT_NAMESPACE_END
|