diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/InputHandler.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-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.cpp | 632 |
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 |