aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp')
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp632
1 files changed, 632 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp
new file mode 100644
index 00000000..9a0d2d54
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp
@@ -0,0 +1,632 @@
+/*
+ * InputHandler.cpp
+ * ----------------
+ * Purpose: Implementation of keyboard input handling, keymap loading, ...
+ * 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 "CommandSet.h"
+#include "InputHandler.h"
+#include "resource.h"
+#include "Mainfrm.h"
+#include "../soundlib/MIDIEvents.h"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+#define TRANSITIONBIT 0x8000
+#define REPEATBIT 0x4000
+
+CInputHandler::CInputHandler(CWnd *mainframe)
+{
+ m_pMainFrm = mainframe;
+
+ //Init CommandSet and Load defaults
+ m_activeCommandSet = std::make_unique<CCommandSet>();
+ m_lastCommands.fill(kcNull);
+
+ mpt::PathString sDefaultPath = theApp.GetConfigPath() + P_("Keybindings.mkb");
+
+ const bool bNoExistingKbdFileSetting = TrackerSettings::Instance().m_szKbdFile.empty();
+
+ // 1. Try to load keybindings from the path saved in the settings.
+ // 2. If the setting doesn't exist or the loading fails, try to load from default location.
+ // 3. If neither one of these worked, load default keybindings from resources.
+ // 4. If there were no keybinding setting already, create a keybinding file to default location
+ // and set its path to settings.
+
+ if (bNoExistingKbdFileSetting || !(m_activeCommandSet->LoadFile(TrackerSettings::Instance().m_szKbdFile)))
+ {
+ if (bNoExistingKbdFileSetting)
+ TrackerSettings::Instance().m_szKbdFile = sDefaultPath;
+ bool bSuccess = false;
+ if (sDefaultPath.IsFile())
+ bSuccess = m_activeCommandSet->LoadFile(sDefaultPath);
+ if (!bSuccess)
+ {
+ // Load keybindings from resources.
+ MPT_LOG_GLOBAL(LogDebug, "InputHandler", U_("Loading keybindings from resources\n"));
+ bSuccess = m_activeCommandSet->LoadDefaultKeymap();
+ if (bSuccess && bNoExistingKbdFileSetting)
+ {
+ m_activeCommandSet->SaveFile(TrackerSettings::Instance().m_szKbdFile);
+ }
+ }
+ if (!bSuccess)
+ ErrorBox(IDS_UNABLE_TO_LOAD_KEYBINDINGS);
+ }
+ // We will only overwrite the default Keybindings.mkb file from now on.
+ TrackerSettings::Instance().m_szKbdFile = sDefaultPath;
+
+ //Get Keymap
+ m_activeCommandSet->GenKeyMap(m_keyMap);
+ SetupSpecialKeyInterception(); // Feature: use Windows keys as modifier keys, intercept special keys
+}
+
+
+CommandID CInputHandler::SendCommands(CWnd *wnd, const KeyMapRange &cmd)
+{
+ CommandID executeCommand = kcNull;
+ if(wnd != nullptr)
+ {
+ // Some commands (e.g. open/close/document switching) may invalidate the key map and thus its iterators.
+ // To avoid this problem, copy over the elements we are interested in before sending commands.
+ std::vector<KeyMap::value_type> commands;
+ commands.reserve(std::distance(cmd.first, cmd.second));
+ for(auto i = cmd.first; i != cmd.second; i++)
+ {
+ commands.push_back(*i);
+ }
+ for(const auto &i : commands)
+ {
+ m_lastCommands[m_lastCommandPos] = i.second;
+ m_lastCommandPos = (m_lastCommandPos + 1) % m_lastCommands.size();
+ if(wnd->SendMessage(WM_MOD_KEYCOMMAND, i.second, i.first.AsLPARAM()) != kcNull)
+ {
+ // Command was handled, no need to let the OS handle the key
+ executeCommand = i.second;
+ }
+ }
+ }
+ return executeCommand;
+}
+
+
+CommandID CInputHandler::GeneralKeyEvent(InputTargetContext context, int code, WPARAM wParam, LPARAM lParam)
+{
+ KeyMapRange cmd = { m_keyMap.end(), m_keyMap.end() };
+ KeyEventType keyEventType;
+
+ if(code == HC_ACTION)
+ {
+ //Get the KeyEventType (key up, key down, key repeat)
+ DWORD scancode = static_cast<LONG>(lParam) >> 16;
+ if((scancode & 0xC000) == 0xC000)
+ {
+ keyEventType = kKeyEventUp;
+ } else if((scancode & 0xC000) == 0x0000)
+ {
+ keyEventType = kKeyEventDown;
+ } else
+ {
+ keyEventType = kKeyEventRepeat;
+ }
+
+ // Catch modifier change (ctrl, alt, shift) - Only check on keyDown or keyUp.
+ // NB: we want to catch modifiers even when the input handler is locked
+ if(keyEventType == kKeyEventUp || keyEventType == kKeyEventDown)
+ {
+ scancode = (static_cast<LONG>(lParam) >> 16) & 0x1FF;
+ CatchModifierChange(wParam, keyEventType, scancode);
+ }
+
+ if(!InterceptSpecialKeys(static_cast<UINT>(wParam), static_cast<LONG>(lParam), true) && !IsBypassed())
+ {
+ // only execute command when the input handler is not locked
+ // and the input is not a consequence of special key interception.
+ cmd = m_keyMap.equal_range(KeyCombination(context, m_modifierMask, static_cast<UINT>(wParam), keyEventType));
+ }
+ }
+ if(code == HC_MIDI)
+ {
+ cmd = m_keyMap.equal_range(KeyCombination(context, ModMidi, static_cast<UINT>(wParam), static_cast<KeyEventType>(lParam)));
+ }
+
+ return SendCommands(m_pMainFrm, cmd);
+}
+
+
+CommandID CInputHandler::KeyEvent(InputTargetContext context, UINT &nChar, UINT &/*nRepCnt*/, UINT &nFlags, KeyEventType keyEventType, CWnd *pSourceWnd)
+{
+ if(InterceptSpecialKeys(nChar, nFlags, false))
+ return kcDummyShortcut;
+ KeyMapRange cmd = m_keyMap.equal_range(KeyCombination(context, m_modifierMask, nChar, keyEventType));
+
+ if(pSourceWnd == nullptr)
+ pSourceWnd = m_pMainFrm; // By default, send command message to main frame.
+ return SendCommands(pSourceWnd, cmd);
+}
+
+
+// Feature: use Windows keys as modifier keys, intercept special keys
+bool CInputHandler::InterceptSpecialKeys(UINT nChar, UINT nFlags, bool generateMsg)
+{
+ KeyEventType keyEventType = GetKeyEventType(HIWORD(nFlags));
+ enum { VK_NonExistentKey = VK_F24+1 };
+
+ if(nChar == VK_NonExistentKey)
+ {
+ return true;
+ } else if(m_bInterceptWindowsKeys && (nChar == VK_LWIN || nChar == VK_RWIN))
+ {
+ if(keyEventType == kKeyEventDown)
+ {
+ INPUT inp[2];
+ inp[0].type = inp[1].type = INPUT_KEYBOARD;
+ inp[0].ki.time = inp[1].ki.time = 0;
+ inp[0].ki.dwExtraInfo = inp[1].ki.dwExtraInfo = 0;
+ inp[0].ki.wVk = inp[1].ki.wVk = VK_NonExistentKey;
+ inp[0].ki.wScan = inp[1].ki.wScan = 0;
+ inp[0].ki.dwFlags = 0;
+ inp[1].ki.dwFlags = KEYEVENTF_KEYUP;
+ SendInput(2, inp, sizeof(INPUT));
+ }
+ }
+
+ if((nChar == VK_NUMLOCK && m_bInterceptNumLock)
+ || (nChar == VK_CAPITAL && m_bInterceptCapsLock)
+ || (nChar == VK_SCROLL && m_bInterceptScrollLock))
+ {
+ if(GetMessageExtraInfo() == 0xC0FFEE)
+ {
+ SetMessageExtraInfo(0);
+ return true;
+ } else if(keyEventType == kKeyEventDown && generateMsg)
+ {
+ // Prevent keys from lighting up by simulating a second press.
+ INPUT inp[2];
+ inp[0].type = inp[1].type = INPUT_KEYBOARD;
+ inp[0].ki.time = inp[1].ki.time = 0;
+ inp[0].ki.dwExtraInfo = inp[1].ki.dwExtraInfo = 0xC0FFEE;
+ inp[0].ki.wVk = inp[1].ki.wVk = static_cast<WORD>(nChar);
+ inp[0].ki.wScan = inp[1].ki.wScan = 0;
+ inp[0].ki.dwFlags = KEYEVENTF_KEYUP;
+ inp[1].ki.dwFlags = 0;
+ SendInput(2, inp, sizeof(INPUT));
+ }
+ }
+ return false;
+};
+
+
+void CInputHandler::SetupSpecialKeyInterception()
+{
+ m_bInterceptWindowsKeys = m_bInterceptNumLock = m_bInterceptCapsLock = m_bInterceptScrollLock = false;
+ for(const auto &i : m_keyMap)
+ {
+ ASSERT(i.second != kcNull);
+ if(i.first.Modifier() == ModWin)
+ m_bInterceptWindowsKeys = true;
+ if(i.first.KeyCode() == VK_NUMLOCK)
+ m_bInterceptNumLock = true;
+ if(i.first.KeyCode() == VK_CAPITAL)
+ m_bInterceptCapsLock = true;
+ if(i.first.KeyCode() == VK_SCROLL)
+ m_bInterceptScrollLock = true;
+ }
+};
+
+
+//Deal with Modifier keypresses. Private surouting used above.
+bool CInputHandler::CatchModifierChange(WPARAM wParam, KeyEventType keyEventType, int scancode)
+{
+ FlagSet<Modifiers> modifierMask = ModNone;
+ // Scancode for right modifier keys should have bit 8 set, but Right Shift is actually 0x36.
+ const bool isRight = ((scancode & 0x100) || scancode == 0x36) && TrackerSettings::Instance().MiscDistinguishModifiers;
+ switch(wParam)
+ {
+ case VK_CONTROL:
+ modifierMask.set(isRight ? ModRCtrl : ModCtrl);
+ break;
+ case VK_SHIFT:
+ modifierMask.set(isRight ? ModRShift : ModShift);
+ break;
+ case VK_MENU:
+ modifierMask.set(isRight ? ModRAlt : ModAlt);
+ break;
+ case VK_LWIN: case VK_RWIN: // Feature: use Windows keys as modifier keys
+ modifierMask.set(ModWin);
+ break;
+ }
+
+ if (modifierMask) // This keypress just changed the modifier mask
+ {
+ if (keyEventType == kKeyEventDown)
+ {
+ m_modifierMask.set(modifierMask);
+ // Right Alt is registered as Ctrl+Alt.
+ // Left Ctrl + Right Alt seems like a pretty difficult to use key combination anyway, so just ignore Ctrl.
+ if(scancode == 0x138)
+ m_modifierMask.reset(ModCtrl);
+#ifdef _DEBUG
+ LogModifiers();
+#endif
+ } else if (keyEventType == kKeyEventUp)
+ m_modifierMask.reset(modifierMask);
+
+ return true;
+ }
+
+ return false;
+}
+
+
+// Translate MIDI messages to shortcut commands
+CommandID CInputHandler::HandleMIDIMessage(InputTargetContext context, uint32 message)
+{
+ auto byte1 = MIDIEvents::GetDataByte1FromEvent(message), byte2 = MIDIEvents::GetDataByte2FromEvent(message);
+ switch(MIDIEvents::GetTypeFromEvent(message))
+ {
+ case MIDIEvents::evControllerChange:
+ if(byte2 != 0)
+ {
+ // Only capture MIDI CCs for now. Some controllers constantly send some MIDI CCs with value 0
+ // (e.g. the Roland D-50 sends CC123 whenenver all notes have been released), so we will ignore those.
+ return GeneralKeyEvent(context, HC_MIDI, byte1, kKeyEventDown);
+ }
+ break;
+
+ case MIDIEvents::evNoteOff:
+ byte2 = 0;
+ [[fallthrough]];
+ case MIDIEvents::evNoteOn:
+ if(byte2 != 0)
+ {
+ return GeneralKeyEvent(context, HC_MIDI, byte1 | 0x80, kKeyEventDown);
+ } else
+ {
+ // If the key-down triggered a note, we still want that note to be stopped. So we always pretend that no key was assigned to this event
+ GeneralKeyEvent(context, HC_MIDI, byte1 | 0x80, kKeyEventUp);
+ }
+ break;
+ }
+
+ return kcNull;
+}
+
+
+int CInputHandler::GetKeyListSize(CommandID cmd) const
+{
+ return m_activeCommandSet->GetKeyListSize(cmd);
+}
+
+
+//----------------------- Misc
+
+
+void CInputHandler::LogModifiers()
+{
+ MPT_LOG_GLOBAL(LogDebug, "InputHandler", U_("----------------------------------\n"));
+ MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModCtrl] ? U_("Ctrl On") : U_("Ctrl --"));
+ MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModShift] ? U_("\tShft On") : U_("\tShft --"));
+ MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModAlt] ? U_("\tAlt On") : U_("\tAlt --"));
+ MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModWin] ? U_("\tWin On\n") : U_("\tWin --\n"));
+}
+
+
+KeyEventType CInputHandler::GetKeyEventType(UINT nFlags)
+{
+ if (nFlags & TRANSITIONBIT)
+ {
+ // Key released
+ return kKeyEventUp;
+ } else if (nFlags & REPEATBIT)
+ {
+ // Key repeated
+ return kKeyEventRepeat;
+ } else
+ {
+ // New key down
+ return kKeyEventDown;
+ }
+}
+
+
+bool CInputHandler::SelectionPressed() const
+{
+ int nSelectionKeys = m_activeCommandSet->GetKeyListSize(kcSelect);
+ KeyCombination key;
+
+ for (int k=0; k<nSelectionKeys; k++)
+ {
+ key = m_activeCommandSet->GetKey(kcSelect, k);
+ if (m_modifierMask & key.Modifier())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool CInputHandler::ShiftPressed() const
+{
+ return m_modifierMask[ModShift | ModRShift];
+}
+
+
+bool CInputHandler::CtrlPressed() const
+{
+ return m_modifierMask[ModCtrl | ModRCtrl];
+}
+
+
+bool CInputHandler::AltPressed() const
+{
+ return m_modifierMask[ModAlt | ModRAlt];
+}
+
+
+void CInputHandler::Bypass(bool b)
+{
+ if(b)
+ m_bypassCount++;
+ else
+ m_bypassCount--;
+ ASSERT(m_bypassCount >= 0);
+}
+
+
+bool CInputHandler::IsBypassed() const
+{
+ return m_bypassCount > 0;
+}
+
+
+FlagSet<Modifiers> CInputHandler::GetModifierMask() const
+{
+ return m_modifierMask;
+}
+
+
+void CInputHandler::SetModifierMask(FlagSet<Modifiers> mask)
+{
+ m_modifierMask = mask;
+}
+
+
+CString CInputHandler::GetKeyTextFromCommand(CommandID c, const TCHAR *prependText) const
+{
+ CString s;
+ if(prependText != nullptr)
+ {
+ s = prependText;
+ s.AppendChar(_T('\t'));
+ }
+ s += m_activeCommandSet->GetKeyTextFromCommand(c, 0);
+ return s;
+}
+
+
+CString CInputHandler::GetMenuText(UINT id) const
+{
+ static constexpr std::tuple<UINT, CommandID, const TCHAR *> MenuItems[] =
+ {
+ { ID_FILE_NEW, kcFileNew, _T("&New") },
+ { ID_FILE_OPEN, kcFileOpen, _T("&Open...") },
+ { ID_FILE_OPENTEMPLATE, kcNull, _T("Open &Template") },
+ { ID_FILE_CLOSE, kcFileClose, _T("&Close") },
+ { ID_FILE_CLOSEALL, kcFileCloseAll, _T("C&lose All") },
+ { ID_FILE_APPENDMODULE, kcFileAppend, _T("Appen&d Module...") },
+ { ID_FILE_SAVE, kcFileSave, _T("&Save") },
+ { ID_FILE_SAVE_AS, kcFileSaveAs, _T("Save &As...") },
+ { ID_FILE_SAVE_COPY, kcFileSaveCopy, _T("Save Cop&y...") },
+ { ID_FILE_SAVEASTEMPLATE, kcFileSaveTemplate, _T("Sa&ve as Template") },
+ { ID_FILE_SAVEASWAVE, kcFileSaveAsWave, _T("Stream Export (&WAV, FLAC, MP3, etc.)...") },
+ { ID_FILE_SAVEMIDI, kcFileSaveMidi, _T("Export as M&IDI...") },
+ { ID_FILE_SAVEOPL, kcFileSaveOPL, _T("Export O&PL Register Dump...") },
+ { ID_FILE_SAVECOMPAT, kcFileExportCompat, _T("Compatibility &Export...") },
+ { ID_IMPORT_MIDILIB, kcFileImportMidiLib, _T("Import &MIDI Library...") },
+ { ID_ADD_SOUNDBANK, kcFileAddSoundBank, _T("Add Sound &Bank...") },
+
+ { ID_PLAYER_PLAY, kcPlayPauseSong, _T("Pause / &Resume") },
+ { ID_PLAYER_PLAYFROMSTART, kcPlaySongFromStart, _T("&Play from Start") },
+ { ID_PLAYER_STOP, kcStopSong, _T("&Stop") },
+ { ID_PLAYER_PAUSE, kcPauseSong, _T("P&ause") },
+ { ID_MIDI_RECORD, kcMidiRecord, _T("&MIDI Record") },
+ { ID_ESTIMATESONGLENGTH, kcEstimateSongLength, _T("&Estimate Song Length") },
+ { ID_APPROX_BPM, kcApproxRealBPM, _T("Approximate Real &BPM") },
+
+ { ID_EDIT_UNDO, kcEditUndo, _T("&Undo") },
+ { ID_EDIT_REDO, kcEditRedo, _T("&Redo") },
+ { ID_EDIT_CUT, kcEditCut, _T("Cu&t") },
+ { ID_EDIT_COPY, kcEditCopy, _T("&Copy") },
+ { ID_EDIT_PASTE, kcEditPaste, _T("&Paste") },
+ { ID_EDIT_SELECT_ALL, kcEditSelectAll, _T("Select &All") },
+ { ID_EDIT_CLEANUP, kcNull, _T("C&leanup") },
+ { ID_EDIT_FIND, kcEditFind, _T("&Find / Replace") },
+ { ID_EDIT_FINDNEXT, kcEditFindNext, _T("Find &Next") },
+ { ID_EDIT_GOTO_MENU, kcPatternGoto, _T("&Goto") },
+ { ID_EDIT_SPLITKEYBOARDSETTINGS, kcShowSplitKeyboardSettings, _T("Split &Keyboard Settings") },
+ // "Paste Special" sub menu
+ { ID_EDIT_PASTE_SPECIAL, kcEditMixPaste, _T("&Mix Paste") },
+ { ID_EDIT_MIXPASTE_ITSTYLE, kcEditMixPasteITStyle, _T("M&ix Paste (IT Style)") },
+ { ID_EDIT_PASTEFLOOD, kcEditPasteFlood, _T("Paste Fl&ood") },
+ { ID_EDIT_PUSHFORWARDPASTE, kcEditPushForwardPaste, _T("&Push Forward Paste (Insert)") },
+
+ { ID_VIEW_GLOBALS, kcViewGeneral, _T("&General") },
+ { ID_VIEW_SAMPLES, kcViewSamples, _T("&Samples") },
+ { ID_VIEW_PATTERNS, kcViewPattern, _T("&Patterns") },
+ { ID_VIEW_INSTRUMENTS, kcViewInstruments, _T("&Instruments") },
+ { ID_VIEW_COMMENTS, kcViewComments, _T("&Comments") },
+ { ID_VIEW_OPTIONS, kcViewOptions, _T("S&etup") },
+ { ID_VIEW_TOOLBAR, kcViewMain, _T("&Main") },
+ { IDD_TREEVIEW, kcViewTree, _T("&Tree") },
+ { ID_PLUGIN_SETUP, kcViewAddPlugin, _T("Pl&ugin Manager") },
+ { ID_CHANNEL_MANAGER, kcViewChannelManager, _T("Ch&annel Manager") },
+ { ID_CLIPBOARD_MANAGER, kcToggleClipboardManager, _T("C&lipboard Manager") },
+ { ID_VIEW_SONGPROPERTIES, kcViewSongProperties, _T("Song P&roperties") },
+ { ID_PATTERN_MIDIMACRO, kcShowMacroConfig, _T("&Zxx Macro Configuration") },
+ { ID_VIEW_MIDIMAPPING, kcViewMIDImapping, _T("&MIDI Mapping") },
+ { ID_VIEW_EDITHISTORY, kcViewEditHistory, _T("Edit &History") },
+ // Help submenu
+ { ID_HELPSHOW, kcHelp, _T("&Help") },
+ { ID_EXAMPLE_MODULES, kcNull, _T("&Example Modules") },
+ };
+
+ for(const auto & [cmdID, command, text] : MenuItems)
+ {
+ if(id == cmdID)
+ {
+ if(command != kcNull)
+ return GetKeyTextFromCommand(command, text);
+ else
+ return text;
+ }
+ }
+ MPT_ASSERT_NOTREACHED();
+ return _T("Unknown Item");
+}
+
+
+void CInputHandler::UpdateMainMenu()
+{
+ CMenu *pMenu = (CMainFrame::GetMainFrame())->GetMenu();
+ if (!pMenu) return;
+
+ pMenu->GetSubMenu(0)->ModifyMenu(0, MF_BYPOSITION | MF_STRING, 0, GetMenuText(ID_FILE_NEW));
+ static constexpr int MenuItems[] =
+ {
+ ID_FILE_OPEN,
+ ID_FILE_APPENDMODULE,
+ ID_FILE_CLOSE,
+ ID_FILE_SAVE,
+ ID_FILE_SAVE_AS,
+ ID_FILE_SAVEASWAVE,
+ ID_FILE_SAVEMIDI,
+ ID_FILE_SAVECOMPAT,
+ ID_IMPORT_MIDILIB,
+ ID_ADD_SOUNDBANK,
+
+ ID_PLAYER_PLAY,
+ ID_PLAYER_PLAYFROMSTART,
+ ID_PLAYER_STOP,
+ ID_PLAYER_PAUSE,
+ ID_MIDI_RECORD,
+ ID_ESTIMATESONGLENGTH,
+ ID_APPROX_BPM,
+
+ ID_EDIT_UNDO,
+ ID_EDIT_REDO,
+ ID_EDIT_CUT,
+ ID_EDIT_COPY,
+ ID_EDIT_PASTE,
+ ID_EDIT_PASTE_SPECIAL,
+ ID_EDIT_MIXPASTE_ITSTYLE,
+ ID_EDIT_PASTEFLOOD,
+ ID_EDIT_PUSHFORWARDPASTE,
+ ID_EDIT_SELECT_ALL,
+ ID_EDIT_FIND,
+ ID_EDIT_FINDNEXT,
+ ID_EDIT_GOTO_MENU,
+ ID_EDIT_SPLITKEYBOARDSETTINGS,
+
+ ID_VIEW_GLOBALS,
+ ID_VIEW_SAMPLES,
+ ID_VIEW_PATTERNS,
+ ID_VIEW_INSTRUMENTS,
+ ID_VIEW_COMMENTS,
+ ID_VIEW_TOOLBAR,
+ IDD_TREEVIEW,
+ ID_VIEW_OPTIONS,
+ ID_PLUGIN_SETUP,
+ ID_CHANNEL_MANAGER,
+ ID_CLIPBOARD_MANAGER,
+ ID_VIEW_SONGPROPERTIES,
+ ID_VIEW_SONGPROPERTIES,
+ ID_PATTERN_MIDIMACRO,
+ ID_VIEW_EDITHISTORY,
+ ID_HELPSHOW,
+ };
+ for(const auto id : MenuItems)
+ {
+ pMenu->ModifyMenu(id, MF_BYCOMMAND | MF_STRING, id, GetMenuText(id));
+ }
+}
+
+
+void CInputHandler::SetNewCommandSet(const CCommandSet *newSet)
+{
+ m_activeCommandSet->Copy(newSet);
+ m_activeCommandSet->GenKeyMap(m_keyMap);
+ SetupSpecialKeyInterception(); // Feature: use Windows keys as modifier keys, intercept special keys
+ UpdateMainMenu();
+}
+
+
+bool CInputHandler::SetEffectLetters(const CModSpecifications &modSpecs)
+{
+ MPT_LOG_GLOBAL(LogDebug, "InputHandler", U_("Changing command set."));
+ bool retval = m_activeCommandSet->QuickChange_SetEffects(modSpecs);
+ if(retval) m_activeCommandSet->GenKeyMap(m_keyMap);
+ return retval;
+}
+
+
+bool CInputHandler::IsKeyPressHandledByTextBox(DWORD key, HWND hWnd) const
+{
+ if(hWnd == nullptr)
+ return false;
+
+ TCHAR activeWindowClassName[6];
+ GetClassName(hWnd, activeWindowClassName, mpt::saturate_cast<int>(std::size(activeWindowClassName)));
+ const bool textboxHasFocus = _tcsicmp(activeWindowClassName, _T("Edit")) == 0;
+ if(!textboxHasFocus)
+ return false;
+
+ //Alpha-numerics (only shift or no modifier):
+ if(!GetModifierMask().test_any_except(ModShift)
+ && ((key>='A'&&key<='Z') || (key>='0'&&key<='9') ||
+ key==VK_DIVIDE || key==VK_MULTIPLY || key==VK_SPACE || key==VK_RETURN ||
+ key==VK_CAPITAL || (key>=VK_OEM_1 && key<=VK_OEM_3) || (key>=VK_OEM_4 && key<=VK_OEM_8)))
+ return true;
+
+ //navigation (any modifier):
+ if(key == VK_LEFT || key == VK_RIGHT || key == VK_UP || key == VK_DOWN ||
+ key == VK_HOME || key == VK_END || key == VK_DELETE || key == VK_INSERT || key == VK_BACK)
+ return true;
+
+ //Copy paste etc..
+ if(GetModifierMask() == ModCtrl &&
+ (key == 'Y' || key == 'Z' || key == 'X' || key == 'C' || key == 'V' || key == 'A'))
+ return true;
+
+ return false;
+}
+
+
+BypassInputHandler::BypassInputHandler()
+{
+ if(CMainFrame::GetInputHandler())
+ {
+ bypassed = true;
+ CMainFrame::GetInputHandler()->Bypass(true);
+ }
+}
+
+
+BypassInputHandler::~BypassInputHandler()
+{
+ if(bypassed)
+ {
+ CMainFrame::GetInputHandler()->Bypass(false);
+ bypassed = false;
+ }
+}
+
+OPENMPT_NAMESPACE_END