InputHandler.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. /*
  2. * InputHandler.cpp
  3. * ----------------
  4. * Purpose: Implementation of keyboard input handling, keymap loading, ...
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "CommandSet.h"
  11. #include "InputHandler.h"
  12. #include "resource.h"
  13. #include "Mainfrm.h"
  14. #include "../soundlib/MIDIEvents.h"
  15. OPENMPT_NAMESPACE_BEGIN
  16. #define TRANSITIONBIT 0x8000
  17. #define REPEATBIT 0x4000
  18. CInputHandler::CInputHandler(CWnd *mainframe)
  19. {
  20. m_pMainFrm = mainframe;
  21. //Init CommandSet and Load defaults
  22. m_activeCommandSet = std::make_unique<CCommandSet>();
  23. m_lastCommands.fill(kcNull);
  24. mpt::PathString sDefaultPath = theApp.GetConfigPath() + P_("Keybindings.mkb");
  25. const bool bNoExistingKbdFileSetting = TrackerSettings::Instance().m_szKbdFile.empty();
  26. // 1. Try to load keybindings from the path saved in the settings.
  27. // 2. If the setting doesn't exist or the loading fails, try to load from default location.
  28. // 3. If neither one of these worked, load default keybindings from resources.
  29. // 4. If there were no keybinding setting already, create a keybinding file to default location
  30. // and set its path to settings.
  31. if (bNoExistingKbdFileSetting || !(m_activeCommandSet->LoadFile(TrackerSettings::Instance().m_szKbdFile)))
  32. {
  33. if (bNoExistingKbdFileSetting)
  34. TrackerSettings::Instance().m_szKbdFile = sDefaultPath;
  35. bool bSuccess = false;
  36. if (sDefaultPath.IsFile())
  37. bSuccess = m_activeCommandSet->LoadFile(sDefaultPath);
  38. if (!bSuccess)
  39. {
  40. // Load keybindings from resources.
  41. MPT_LOG_GLOBAL(LogDebug, "InputHandler", U_("Loading keybindings from resources\n"));
  42. bSuccess = m_activeCommandSet->LoadDefaultKeymap();
  43. if (bSuccess && bNoExistingKbdFileSetting)
  44. {
  45. m_activeCommandSet->SaveFile(TrackerSettings::Instance().m_szKbdFile);
  46. }
  47. }
  48. if (!bSuccess)
  49. ErrorBox(IDS_UNABLE_TO_LOAD_KEYBINDINGS);
  50. }
  51. // We will only overwrite the default Keybindings.mkb file from now on.
  52. TrackerSettings::Instance().m_szKbdFile = sDefaultPath;
  53. //Get Keymap
  54. m_activeCommandSet->GenKeyMap(m_keyMap);
  55. SetupSpecialKeyInterception(); // Feature: use Windows keys as modifier keys, intercept special keys
  56. }
  57. CommandID CInputHandler::SendCommands(CWnd *wnd, const KeyMapRange &cmd)
  58. {
  59. CommandID executeCommand = kcNull;
  60. if(wnd != nullptr)
  61. {
  62. // Some commands (e.g. open/close/document switching) may invalidate the key map and thus its iterators.
  63. // To avoid this problem, copy over the elements we are interested in before sending commands.
  64. std::vector<KeyMap::value_type> commands;
  65. commands.reserve(std::distance(cmd.first, cmd.second));
  66. for(auto i = cmd.first; i != cmd.second; i++)
  67. {
  68. commands.push_back(*i);
  69. }
  70. for(const auto &i : commands)
  71. {
  72. m_lastCommands[m_lastCommandPos] = i.second;
  73. m_lastCommandPos = (m_lastCommandPos + 1) % m_lastCommands.size();
  74. if(wnd->SendMessage(WM_MOD_KEYCOMMAND, i.second, i.first.AsLPARAM()) != kcNull)
  75. {
  76. // Command was handled, no need to let the OS handle the key
  77. executeCommand = i.second;
  78. }
  79. }
  80. }
  81. return executeCommand;
  82. }
  83. CommandID CInputHandler::GeneralKeyEvent(InputTargetContext context, int code, WPARAM wParam, LPARAM lParam)
  84. {
  85. KeyMapRange cmd = { m_keyMap.end(), m_keyMap.end() };
  86. KeyEventType keyEventType;
  87. if(code == HC_ACTION)
  88. {
  89. //Get the KeyEventType (key up, key down, key repeat)
  90. DWORD scancode = static_cast<LONG>(lParam) >> 16;
  91. if((scancode & 0xC000) == 0xC000)
  92. {
  93. keyEventType = kKeyEventUp;
  94. } else if((scancode & 0xC000) == 0x0000)
  95. {
  96. keyEventType = kKeyEventDown;
  97. } else
  98. {
  99. keyEventType = kKeyEventRepeat;
  100. }
  101. // Catch modifier change (ctrl, alt, shift) - Only check on keyDown or keyUp.
  102. // NB: we want to catch modifiers even when the input handler is locked
  103. if(keyEventType == kKeyEventUp || keyEventType == kKeyEventDown)
  104. {
  105. scancode = (static_cast<LONG>(lParam) >> 16) & 0x1FF;
  106. CatchModifierChange(wParam, keyEventType, scancode);
  107. }
  108. if(!InterceptSpecialKeys(static_cast<UINT>(wParam), static_cast<LONG>(lParam), true) && !IsBypassed())
  109. {
  110. // only execute command when the input handler is not locked
  111. // and the input is not a consequence of special key interception.
  112. cmd = m_keyMap.equal_range(KeyCombination(context, m_modifierMask, static_cast<UINT>(wParam), keyEventType));
  113. }
  114. }
  115. if(code == HC_MIDI)
  116. {
  117. cmd = m_keyMap.equal_range(KeyCombination(context, ModMidi, static_cast<UINT>(wParam), static_cast<KeyEventType>(lParam)));
  118. }
  119. return SendCommands(m_pMainFrm, cmd);
  120. }
  121. CommandID CInputHandler::KeyEvent(InputTargetContext context, UINT &nChar, UINT &/*nRepCnt*/, UINT &nFlags, KeyEventType keyEventType, CWnd *pSourceWnd)
  122. {
  123. if(InterceptSpecialKeys(nChar, nFlags, false))
  124. return kcDummyShortcut;
  125. KeyMapRange cmd = m_keyMap.equal_range(KeyCombination(context, m_modifierMask, nChar, keyEventType));
  126. if(pSourceWnd == nullptr)
  127. pSourceWnd = m_pMainFrm; // By default, send command message to main frame.
  128. return SendCommands(pSourceWnd, cmd);
  129. }
  130. // Feature: use Windows keys as modifier keys, intercept special keys
  131. bool CInputHandler::InterceptSpecialKeys(UINT nChar, UINT nFlags, bool generateMsg)
  132. {
  133. KeyEventType keyEventType = GetKeyEventType(HIWORD(nFlags));
  134. enum { VK_NonExistentKey = VK_F24+1 };
  135. if(nChar == VK_NonExistentKey)
  136. {
  137. return true;
  138. } else if(m_bInterceptWindowsKeys && (nChar == VK_LWIN || nChar == VK_RWIN))
  139. {
  140. if(keyEventType == kKeyEventDown)
  141. {
  142. INPUT inp[2];
  143. inp[0].type = inp[1].type = INPUT_KEYBOARD;
  144. inp[0].ki.time = inp[1].ki.time = 0;
  145. inp[0].ki.dwExtraInfo = inp[1].ki.dwExtraInfo = 0;
  146. inp[0].ki.wVk = inp[1].ki.wVk = VK_NonExistentKey;
  147. inp[0].ki.wScan = inp[1].ki.wScan = 0;
  148. inp[0].ki.dwFlags = 0;
  149. inp[1].ki.dwFlags = KEYEVENTF_KEYUP;
  150. SendInput(2, inp, sizeof(INPUT));
  151. }
  152. }
  153. if((nChar == VK_NUMLOCK && m_bInterceptNumLock)
  154. || (nChar == VK_CAPITAL && m_bInterceptCapsLock)
  155. || (nChar == VK_SCROLL && m_bInterceptScrollLock))
  156. {
  157. if(GetMessageExtraInfo() == 0xC0FFEE)
  158. {
  159. SetMessageExtraInfo(0);
  160. return true;
  161. } else if(keyEventType == kKeyEventDown && generateMsg)
  162. {
  163. // Prevent keys from lighting up by simulating a second press.
  164. INPUT inp[2];
  165. inp[0].type = inp[1].type = INPUT_KEYBOARD;
  166. inp[0].ki.time = inp[1].ki.time = 0;
  167. inp[0].ki.dwExtraInfo = inp[1].ki.dwExtraInfo = 0xC0FFEE;
  168. inp[0].ki.wVk = inp[1].ki.wVk = static_cast<WORD>(nChar);
  169. inp[0].ki.wScan = inp[1].ki.wScan = 0;
  170. inp[0].ki.dwFlags = KEYEVENTF_KEYUP;
  171. inp[1].ki.dwFlags = 0;
  172. SendInput(2, inp, sizeof(INPUT));
  173. }
  174. }
  175. return false;
  176. };
  177. void CInputHandler::SetupSpecialKeyInterception()
  178. {
  179. m_bInterceptWindowsKeys = m_bInterceptNumLock = m_bInterceptCapsLock = m_bInterceptScrollLock = false;
  180. for(const auto &i : m_keyMap)
  181. {
  182. ASSERT(i.second != kcNull);
  183. if(i.first.Modifier() == ModWin)
  184. m_bInterceptWindowsKeys = true;
  185. if(i.first.KeyCode() == VK_NUMLOCK)
  186. m_bInterceptNumLock = true;
  187. if(i.first.KeyCode() == VK_CAPITAL)
  188. m_bInterceptCapsLock = true;
  189. if(i.first.KeyCode() == VK_SCROLL)
  190. m_bInterceptScrollLock = true;
  191. }
  192. };
  193. //Deal with Modifier keypresses. Private surouting used above.
  194. bool CInputHandler::CatchModifierChange(WPARAM wParam, KeyEventType keyEventType, int scancode)
  195. {
  196. FlagSet<Modifiers> modifierMask = ModNone;
  197. // Scancode for right modifier keys should have bit 8 set, but Right Shift is actually 0x36.
  198. const bool isRight = ((scancode & 0x100) || scancode == 0x36) && TrackerSettings::Instance().MiscDistinguishModifiers;
  199. switch(wParam)
  200. {
  201. case VK_CONTROL:
  202. modifierMask.set(isRight ? ModRCtrl : ModCtrl);
  203. break;
  204. case VK_SHIFT:
  205. modifierMask.set(isRight ? ModRShift : ModShift);
  206. break;
  207. case VK_MENU:
  208. modifierMask.set(isRight ? ModRAlt : ModAlt);
  209. break;
  210. case VK_LWIN: case VK_RWIN: // Feature: use Windows keys as modifier keys
  211. modifierMask.set(ModWin);
  212. break;
  213. }
  214. if (modifierMask) // This keypress just changed the modifier mask
  215. {
  216. if (keyEventType == kKeyEventDown)
  217. {
  218. m_modifierMask.set(modifierMask);
  219. // Right Alt is registered as Ctrl+Alt.
  220. // Left Ctrl + Right Alt seems like a pretty difficult to use key combination anyway, so just ignore Ctrl.
  221. if(scancode == 0x138)
  222. m_modifierMask.reset(ModCtrl);
  223. #ifdef _DEBUG
  224. LogModifiers();
  225. #endif
  226. } else if (keyEventType == kKeyEventUp)
  227. m_modifierMask.reset(modifierMask);
  228. return true;
  229. }
  230. return false;
  231. }
  232. // Translate MIDI messages to shortcut commands
  233. CommandID CInputHandler::HandleMIDIMessage(InputTargetContext context, uint32 message)
  234. {
  235. auto byte1 = MIDIEvents::GetDataByte1FromEvent(message), byte2 = MIDIEvents::GetDataByte2FromEvent(message);
  236. switch(MIDIEvents::GetTypeFromEvent(message))
  237. {
  238. case MIDIEvents::evControllerChange:
  239. if(byte2 != 0)
  240. {
  241. // Only capture MIDI CCs for now. Some controllers constantly send some MIDI CCs with value 0
  242. // (e.g. the Roland D-50 sends CC123 whenenver all notes have been released), so we will ignore those.
  243. return GeneralKeyEvent(context, HC_MIDI, byte1, kKeyEventDown);
  244. }
  245. break;
  246. case MIDIEvents::evNoteOff:
  247. byte2 = 0;
  248. [[fallthrough]];
  249. case MIDIEvents::evNoteOn:
  250. if(byte2 != 0)
  251. {
  252. return GeneralKeyEvent(context, HC_MIDI, byte1 | 0x80, kKeyEventDown);
  253. } else
  254. {
  255. // 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
  256. GeneralKeyEvent(context, HC_MIDI, byte1 | 0x80, kKeyEventUp);
  257. }
  258. break;
  259. }
  260. return kcNull;
  261. }
  262. int CInputHandler::GetKeyListSize(CommandID cmd) const
  263. {
  264. return m_activeCommandSet->GetKeyListSize(cmd);
  265. }
  266. //----------------------- Misc
  267. void CInputHandler::LogModifiers()
  268. {
  269. MPT_LOG_GLOBAL(LogDebug, "InputHandler", U_("----------------------------------\n"));
  270. MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModCtrl] ? U_("Ctrl On") : U_("Ctrl --"));
  271. MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModShift] ? U_("\tShft On") : U_("\tShft --"));
  272. MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModAlt] ? U_("\tAlt On") : U_("\tAlt --"));
  273. MPT_LOG_GLOBAL(LogDebug, "InputHandler", m_modifierMask[ModWin] ? U_("\tWin On\n") : U_("\tWin --\n"));
  274. }
  275. KeyEventType CInputHandler::GetKeyEventType(UINT nFlags)
  276. {
  277. if (nFlags & TRANSITIONBIT)
  278. {
  279. // Key released
  280. return kKeyEventUp;
  281. } else if (nFlags & REPEATBIT)
  282. {
  283. // Key repeated
  284. return kKeyEventRepeat;
  285. } else
  286. {
  287. // New key down
  288. return kKeyEventDown;
  289. }
  290. }
  291. bool CInputHandler::SelectionPressed() const
  292. {
  293. int nSelectionKeys = m_activeCommandSet->GetKeyListSize(kcSelect);
  294. KeyCombination key;
  295. for (int k=0; k<nSelectionKeys; k++)
  296. {
  297. key = m_activeCommandSet->GetKey(kcSelect, k);
  298. if (m_modifierMask & key.Modifier())
  299. {
  300. return true;
  301. }
  302. }
  303. return false;
  304. }
  305. bool CInputHandler::ShiftPressed() const
  306. {
  307. return m_modifierMask[ModShift | ModRShift];
  308. }
  309. bool CInputHandler::CtrlPressed() const
  310. {
  311. return m_modifierMask[ModCtrl | ModRCtrl];
  312. }
  313. bool CInputHandler::AltPressed() const
  314. {
  315. return m_modifierMask[ModAlt | ModRAlt];
  316. }
  317. void CInputHandler::Bypass(bool b)
  318. {
  319. if(b)
  320. m_bypassCount++;
  321. else
  322. m_bypassCount--;
  323. ASSERT(m_bypassCount >= 0);
  324. }
  325. bool CInputHandler::IsBypassed() const
  326. {
  327. return m_bypassCount > 0;
  328. }
  329. FlagSet<Modifiers> CInputHandler::GetModifierMask() const
  330. {
  331. return m_modifierMask;
  332. }
  333. void CInputHandler::SetModifierMask(FlagSet<Modifiers> mask)
  334. {
  335. m_modifierMask = mask;
  336. }
  337. CString CInputHandler::GetKeyTextFromCommand(CommandID c, const TCHAR *prependText) const
  338. {
  339. CString s;
  340. if(prependText != nullptr)
  341. {
  342. s = prependText;
  343. s.AppendChar(_T('\t'));
  344. }
  345. s += m_activeCommandSet->GetKeyTextFromCommand(c, 0);
  346. return s;
  347. }
  348. CString CInputHandler::GetMenuText(UINT id) const
  349. {
  350. static constexpr std::tuple<UINT, CommandID, const TCHAR *> MenuItems[] =
  351. {
  352. { ID_FILE_NEW, kcFileNew, _T("&New") },
  353. { ID_FILE_OPEN, kcFileOpen, _T("&Open...") },
  354. { ID_FILE_OPENTEMPLATE, kcNull, _T("Open &Template") },
  355. { ID_FILE_CLOSE, kcFileClose, _T("&Close") },
  356. { ID_FILE_CLOSEALL, kcFileCloseAll, _T("C&lose All") },
  357. { ID_FILE_APPENDMODULE, kcFileAppend, _T("Appen&d Module...") },
  358. { ID_FILE_SAVE, kcFileSave, _T("&Save") },
  359. { ID_FILE_SAVE_AS, kcFileSaveAs, _T("Save &As...") },
  360. { ID_FILE_SAVE_COPY, kcFileSaveCopy, _T("Save Cop&y...") },
  361. { ID_FILE_SAVEASTEMPLATE, kcFileSaveTemplate, _T("Sa&ve as Template") },
  362. { ID_FILE_SAVEASWAVE, kcFileSaveAsWave, _T("Stream Export (&WAV, FLAC, MP3, etc.)...") },
  363. { ID_FILE_SAVEMIDI, kcFileSaveMidi, _T("Export as M&IDI...") },
  364. { ID_FILE_SAVEOPL, kcFileSaveOPL, _T("Export O&PL Register Dump...") },
  365. { ID_FILE_SAVECOMPAT, kcFileExportCompat, _T("Compatibility &Export...") },
  366. { ID_IMPORT_MIDILIB, kcFileImportMidiLib, _T("Import &MIDI Library...") },
  367. { ID_ADD_SOUNDBANK, kcFileAddSoundBank, _T("Add Sound &Bank...") },
  368. { ID_PLAYER_PLAY, kcPlayPauseSong, _T("Pause / &Resume") },
  369. { ID_PLAYER_PLAYFROMSTART, kcPlaySongFromStart, _T("&Play from Start") },
  370. { ID_PLAYER_STOP, kcStopSong, _T("&Stop") },
  371. { ID_PLAYER_PAUSE, kcPauseSong, _T("P&ause") },
  372. { ID_MIDI_RECORD, kcMidiRecord, _T("&MIDI Record") },
  373. { ID_ESTIMATESONGLENGTH, kcEstimateSongLength, _T("&Estimate Song Length") },
  374. { ID_APPROX_BPM, kcApproxRealBPM, _T("Approximate Real &BPM") },
  375. { ID_EDIT_UNDO, kcEditUndo, _T("&Undo") },
  376. { ID_EDIT_REDO, kcEditRedo, _T("&Redo") },
  377. { ID_EDIT_CUT, kcEditCut, _T("Cu&t") },
  378. { ID_EDIT_COPY, kcEditCopy, _T("&Copy") },
  379. { ID_EDIT_PASTE, kcEditPaste, _T("&Paste") },
  380. { ID_EDIT_SELECT_ALL, kcEditSelectAll, _T("Select &All") },
  381. { ID_EDIT_CLEANUP, kcNull, _T("C&leanup") },
  382. { ID_EDIT_FIND, kcEditFind, _T("&Find / Replace") },
  383. { ID_EDIT_FINDNEXT, kcEditFindNext, _T("Find &Next") },
  384. { ID_EDIT_GOTO_MENU, kcPatternGoto, _T("&Goto") },
  385. { ID_EDIT_SPLITKEYBOARDSETTINGS, kcShowSplitKeyboardSettings, _T("Split &Keyboard Settings") },
  386. // "Paste Special" sub menu
  387. { ID_EDIT_PASTE_SPECIAL, kcEditMixPaste, _T("&Mix Paste") },
  388. { ID_EDIT_MIXPASTE_ITSTYLE, kcEditMixPasteITStyle, _T("M&ix Paste (IT Style)") },
  389. { ID_EDIT_PASTEFLOOD, kcEditPasteFlood, _T("Paste Fl&ood") },
  390. { ID_EDIT_PUSHFORWARDPASTE, kcEditPushForwardPaste, _T("&Push Forward Paste (Insert)") },
  391. { ID_VIEW_GLOBALS, kcViewGeneral, _T("&General") },
  392. { ID_VIEW_SAMPLES, kcViewSamples, _T("&Samples") },
  393. { ID_VIEW_PATTERNS, kcViewPattern, _T("&Patterns") },
  394. { ID_VIEW_INSTRUMENTS, kcViewInstruments, _T("&Instruments") },
  395. { ID_VIEW_COMMENTS, kcViewComments, _T("&Comments") },
  396. { ID_VIEW_OPTIONS, kcViewOptions, _T("S&etup") },
  397. { ID_VIEW_TOOLBAR, kcViewMain, _T("&Main") },
  398. { IDD_TREEVIEW, kcViewTree, _T("&Tree") },
  399. { ID_PLUGIN_SETUP, kcViewAddPlugin, _T("Pl&ugin Manager") },
  400. { ID_CHANNEL_MANAGER, kcViewChannelManager, _T("Ch&annel Manager") },
  401. { ID_CLIPBOARD_MANAGER, kcToggleClipboardManager, _T("C&lipboard Manager") },
  402. { ID_VIEW_SONGPROPERTIES, kcViewSongProperties, _T("Song P&roperties") },
  403. { ID_PATTERN_MIDIMACRO, kcShowMacroConfig, _T("&Zxx Macro Configuration") },
  404. { ID_VIEW_MIDIMAPPING, kcViewMIDImapping, _T("&MIDI Mapping") },
  405. { ID_VIEW_EDITHISTORY, kcViewEditHistory, _T("Edit &History") },
  406. // Help submenu
  407. { ID_HELPSHOW, kcHelp, _T("&Help") },
  408. { ID_EXAMPLE_MODULES, kcNull, _T("&Example Modules") },
  409. };
  410. for(const auto & [cmdID, command, text] : MenuItems)
  411. {
  412. if(id == cmdID)
  413. {
  414. if(command != kcNull)
  415. return GetKeyTextFromCommand(command, text);
  416. else
  417. return text;
  418. }
  419. }
  420. MPT_ASSERT_NOTREACHED();
  421. return _T("Unknown Item");
  422. }
  423. void CInputHandler::UpdateMainMenu()
  424. {
  425. CMenu *pMenu = (CMainFrame::GetMainFrame())->GetMenu();
  426. if (!pMenu) return;
  427. pMenu->GetSubMenu(0)->ModifyMenu(0, MF_BYPOSITION | MF_STRING, 0, GetMenuText(ID_FILE_NEW));
  428. static constexpr int MenuItems[] =
  429. {
  430. ID_FILE_OPEN,
  431. ID_FILE_APPENDMODULE,
  432. ID_FILE_CLOSE,
  433. ID_FILE_SAVE,
  434. ID_FILE_SAVE_AS,
  435. ID_FILE_SAVEASWAVE,
  436. ID_FILE_SAVEMIDI,
  437. ID_FILE_SAVECOMPAT,
  438. ID_IMPORT_MIDILIB,
  439. ID_ADD_SOUNDBANK,
  440. ID_PLAYER_PLAY,
  441. ID_PLAYER_PLAYFROMSTART,
  442. ID_PLAYER_STOP,
  443. ID_PLAYER_PAUSE,
  444. ID_MIDI_RECORD,
  445. ID_ESTIMATESONGLENGTH,
  446. ID_APPROX_BPM,
  447. ID_EDIT_UNDO,
  448. ID_EDIT_REDO,
  449. ID_EDIT_CUT,
  450. ID_EDIT_COPY,
  451. ID_EDIT_PASTE,
  452. ID_EDIT_PASTE_SPECIAL,
  453. ID_EDIT_MIXPASTE_ITSTYLE,
  454. ID_EDIT_PASTEFLOOD,
  455. ID_EDIT_PUSHFORWARDPASTE,
  456. ID_EDIT_SELECT_ALL,
  457. ID_EDIT_FIND,
  458. ID_EDIT_FINDNEXT,
  459. ID_EDIT_GOTO_MENU,
  460. ID_EDIT_SPLITKEYBOARDSETTINGS,
  461. ID_VIEW_GLOBALS,
  462. ID_VIEW_SAMPLES,
  463. ID_VIEW_PATTERNS,
  464. ID_VIEW_INSTRUMENTS,
  465. ID_VIEW_COMMENTS,
  466. ID_VIEW_TOOLBAR,
  467. IDD_TREEVIEW,
  468. ID_VIEW_OPTIONS,
  469. ID_PLUGIN_SETUP,
  470. ID_CHANNEL_MANAGER,
  471. ID_CLIPBOARD_MANAGER,
  472. ID_VIEW_SONGPROPERTIES,
  473. ID_VIEW_SONGPROPERTIES,
  474. ID_PATTERN_MIDIMACRO,
  475. ID_VIEW_EDITHISTORY,
  476. ID_HELPSHOW,
  477. };
  478. for(const auto id : MenuItems)
  479. {
  480. pMenu->ModifyMenu(id, MF_BYCOMMAND | MF_STRING, id, GetMenuText(id));
  481. }
  482. }
  483. void CInputHandler::SetNewCommandSet(const CCommandSet *newSet)
  484. {
  485. m_activeCommandSet->Copy(newSet);
  486. m_activeCommandSet->GenKeyMap(m_keyMap);
  487. SetupSpecialKeyInterception(); // Feature: use Windows keys as modifier keys, intercept special keys
  488. UpdateMainMenu();
  489. }
  490. bool CInputHandler::SetEffectLetters(const CModSpecifications &modSpecs)
  491. {
  492. MPT_LOG_GLOBAL(LogDebug, "InputHandler", U_("Changing command set."));
  493. bool retval = m_activeCommandSet->QuickChange_SetEffects(modSpecs);
  494. if(retval) m_activeCommandSet->GenKeyMap(m_keyMap);
  495. return retval;
  496. }
  497. bool CInputHandler::IsKeyPressHandledByTextBox(DWORD key, HWND hWnd) const
  498. {
  499. if(hWnd == nullptr)
  500. return false;
  501. TCHAR activeWindowClassName[6];
  502. GetClassName(hWnd, activeWindowClassName, mpt::saturate_cast<int>(std::size(activeWindowClassName)));
  503. const bool textboxHasFocus = _tcsicmp(activeWindowClassName, _T("Edit")) == 0;
  504. if(!textboxHasFocus)
  505. return false;
  506. //Alpha-numerics (only shift or no modifier):
  507. if(!GetModifierMask().test_any_except(ModShift)
  508. && ((key>='A'&&key<='Z') || (key>='0'&&key<='9') ||
  509. key==VK_DIVIDE || key==VK_MULTIPLY || key==VK_SPACE || key==VK_RETURN ||
  510. key==VK_CAPITAL || (key>=VK_OEM_1 && key<=VK_OEM_3) || (key>=VK_OEM_4 && key<=VK_OEM_8)))
  511. return true;
  512. //navigation (any modifier):
  513. if(key == VK_LEFT || key == VK_RIGHT || key == VK_UP || key == VK_DOWN ||
  514. key == VK_HOME || key == VK_END || key == VK_DELETE || key == VK_INSERT || key == VK_BACK)
  515. return true;
  516. //Copy paste etc..
  517. if(GetModifierMask() == ModCtrl &&
  518. (key == 'Y' || key == 'Z' || key == 'X' || key == 'C' || key == 'V' || key == 'A'))
  519. return true;
  520. return false;
  521. }
  522. BypassInputHandler::BypassInputHandler()
  523. {
  524. if(CMainFrame::GetInputHandler())
  525. {
  526. bypassed = true;
  527. CMainFrame::GetInputHandler()->Bypass(true);
  528. }
  529. }
  530. BypassInputHandler::~BypassInputHandler()
  531. {
  532. if(bypassed)
  533. {
  534. CMainFrame::GetInputHandler()->Bypass(false);
  535. bypassed = false;
  536. }
  537. }
  538. OPENMPT_NAMESPACE_END