From 1891432cec50563e6b7f3e3f58264de7d11374cc Mon Sep 17 00:00:00 2001 From: lededev <30518126+lededev@users.noreply.github.com> Date: Thu, 19 Dec 2024 03:15:05 +0800 Subject: [PATCH] One-Hand mice merge without horizontal wheel --- Src/MainFrm.cpp | 9 +- Src/Merge.rc | 4 +- Src/MouseHook.cpp | 300 ++++++++++++-------- Src/MouseHook.h | 16 +- Translations/WinMerge/ChineseSimplified.po | 22 +- Translations/WinMerge/ChineseTraditional.po | 22 +- 6 files changed, 226 insertions(+), 147 deletions(-) diff --git a/Src/MainFrm.cpp b/Src/MainFrm.cpp index 656aa52d5cb..c137437ded4 100644 --- a/Src/MainFrm.cpp +++ b/Src/MainFrm.cpp @@ -2808,17 +2808,18 @@ BOOL CMainFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult) return FALSE; strTipText = strFullText.substr(newline1st + 1).c_str(); } + constexpr size_t tipBufLen = 512; if (pNMHDR->code == TTN_NEEDTEXTA) { - m_upszLongTextA.reset(new CHAR[256]); + m_upszLongTextA.reset(new CHAR[tipBufLen]); pTTTA->lpszText = m_upszLongTextA.get(); - _wcstombsz(pTTTA->lpszText, strTipText, 256); + _wcstombsz(pTTTA->lpszText, strTipText, tipBufLen); } else { - m_upszLongTextW.reset(new WCHAR[256]); + m_upszLongTextW.reset(new WCHAR[tipBufLen]); pTTTW->lpszText = m_upszLongTextW.get(); - lstrcpyn(pTTTW->lpszText, strTipText, 256); + lstrcpyn(pTTTW->lpszText, strTipText, tipBufLen); } *pResult = 0; diff --git a/Src/Merge.rc b/Src/Merge.rc index d31125f4ec0..a037e57e313 100644 --- a/Src/Merge.rc +++ b/Src/Merge.rc @@ -4574,8 +4574,8 @@ STRINGTABLE BEGIN ID_MICE_PREVDIFF "\nPrevious Difference (Alt+Up)\n(Right Button+Wheel Up)\n(Alt+Wheel Up)" ID_MICE_NEXTDIFF "\nNext Difference (Alt+Down)\n(Right Button+Wheel Down)\n(Alt+Wheel Down)" - ID_MICE_L2R "\nCopy to Right (Alt+Right)\n(Right Button+Wheel Right)\n(Alt+Wheel Right)\n(Alt+Shift+Wheel Down)" - ID_MICE_R2L "\nCopy to Left (Alt+Left)\n(Right Button+Wheel Left)\n(Alt+Wheel Left)\n(Alt+Shift+Wheel Up)" + ID_MICE_L2R "\nCopy to Right (Alt+Right)\n(Right Button+Wheel Right)\n(Alt+Wheel Right)\n(Right Button+Shift+Wheel Down)\n(Alt+Shift+Wheel Down)\n(Hold Right Button+Middle Button Click+Wheel Down)" + ID_MICE_R2L "\nCopy to Left (Alt+Left)\n(Right Button+Wheel Left)\n(Alt+Wheel Left)\n(Right Button+Shift+Wheel Up)\n(Alt+Shift+Wheel Up)\n(Hold Right Button+Middle Button Click+Wheel Up)" ID_MICE_L2RNEXT "\nCopy to Right and Advance (Ctrl+Alt+Right)\n(Ctrl+Alt+Wheel Right)\n(Ctrl+Alt+Shift+Wheel Down)" ID_MICE_R2LNEXT "\nCopy to Left and Advance (Ctrl+Alt+Left)\n(Ctrl+Alt+Wheel Left)\n(Ctrl+Alt+Shift+Wheel Up)" END diff --git a/Src/MouseHook.cpp b/Src/MouseHook.cpp index 12c4651eafb..59c0919094a 100644 --- a/Src/MouseHook.cpp +++ b/Src/MouseHook.cpp @@ -6,165 +6,222 @@ # define WM_MOUSEHWHEEL 0x20e #endif +// Define state matrix +const std::function CMouseHook::stateMatrix[3][6] = { + // State::Idle + { + [](LPARAM) { Transition(State::RightButtonDown); return false; }, // WM_RBUTTONDOWN + [](LPARAM) { return false; }, // WM_RBUTTONUP + [](LPARAM) { return false; }, // WM_LBUTTONDOWN + [](LPARAM) { return false; }, // WM_MBUTTONUP + [](LPARAM lParam) { return MouseWheelAction(lParam); }, // WM_MOUSEWHEEL + [](LPARAM lParam) { return MouseHWheelAction(lParam); } // WM_MOUSEHWHEEL + }, + // State::RightButtonDown + { + [](LPARAM) { return false; }, // WM_RBUTTONDOWN + [](LPARAM) { Transition(State::Idle); EndRightWheelScrolling(); return false; }, // WM_RBUTTONUP + [](LPARAM) { return false; }, // WM_LBUTTONDOWN + [](LPARAM) { Transition(State::HorizontalScrollSimulated); return false; }, // WM_MBUTTONUP + [](LPARAM lParam) { return RightButtonDown_MouseWheel(lParam); }, // WM_MOUSEWHEEL + [](LPARAM lParam) { return RightButtonDown_MouseHWheel(lParam); } // WM_MOUSEHWHEEL + }, + // State::HorizontalScrollSimulated + { + [](LPARAM) { return false; }, // WM_RBUTTONDOWN + [](LPARAM) { Transition(State::Idle); EndRightWheelScrolling(); return false; }, // WM_RBUTTONUP + [](LPARAM) { return false; }, // WM_LBUTTONDOWN + [](LPARAM) { Transition(State::RightButtonDown); return false; }, // WM_MBUTTONUP + [](LPARAM lParam) { // WM_MOUSEWHEEL + const auto b = RightButtonDown_MouseWheel(lParam); + Transition(State::RightButtonDown); + return b; + }, + [](LPARAM lParam) { // WM_MOUSEHWHEEL + const auto b = RightButtonDown_MouseHWheel(lParam); + Transition(State::RightButtonDown); + return b; + } + } +}; + +void CMouseHook::Transition(State nextState) { + m_currentState = nextState; +} + LRESULT CALLBACK CMouseHook::MouseProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode < 0) return CallNextHookEx(m_hMouseHook, nCode, wParam, lParam); - if (wParam == WM_LBUTTONDOWN) - { - EndRightWheelScrolling(); - } - else if (wParam == WM_RBUTTONDOWN) + const static std::unordered_map actionIdx = { - m_bRButtonDown = true; - } - else if (wParam == WM_RBUTTONUP) + {WM_RBUTTONDOWN, 0} + ,{WM_RBUTTONUP, 1} + ,{WM_LBUTTONDOWN, 2} + ,{WM_MBUTTONUP, 3} + ,{WM_MOUSEWHEEL, 4} + ,{WM_MOUSEHWHEEL, 5} + }; + auto it = actionIdx.find(static_cast(wParam)); + if (it != actionIdx.end()) { - m_bRButtonDown = false; - EndRightWheelScrolling(); + auto bRet = stateMatrix[static_cast(m_currentState)][it->second](lParam); + if (bRet) + return 1; } - else if (wParam == WM_MOUSEWHEEL) - { - MOUSEHOOKSTRUCTEX* pMouseStruct = (MOUSEHOOKSTRUCTEX*)lParam; - short zDelta = HIWORD(pMouseStruct->mouseData); - if (GetAsyncKeyState(VK_MENU) & 0x8000) + return CallNextHookEx(m_hMouseHook, nCode, wParam, lParam); +} + +bool CMouseHook::MouseWheelAction(LPARAM lParam) +{ + MOUSEHOOKSTRUCTEX* pMouseStruct = (MOUSEHOOKSTRUCTEX*)lParam; + short zDelta = HIWORD(pMouseStruct->mouseData); + + if (GetAsyncKeyState(VK_MENU) & 0x8000) + { + HWND hwndTarget = GetForegroundWindow(); + // When hold Alt key, use nFlags to check MK_CONTROL MK_SHIFT holding got problem, Use GetAsyncKeyState() instead. + const auto bShiftDown = GetAsyncKeyState(VK_SHIFT) & 0x8000; + const auto bControlDown = GetAsyncKeyState(VK_CONTROL) & 0x8000; + // zDelta > 0 scrool up, < 0 scrool down + if (zDelta > 0) { - HWND hwndTarget = GetForegroundWindow(); - // When hold Alt key, use nFlags to check MK_CONTROL MK_SHIFT holding got problem, Use GetAsyncKeyState() instead. - const auto bShiftDown = GetAsyncKeyState(VK_SHIFT) & 0x8000; - const auto bControlDown = GetAsyncKeyState(VK_CONTROL) & 0x8000; - // zDelta > 0 scrool up, < 0 scrool down - if (zDelta > 0) + // Check Shift key hold for mice without HWheel function + if (bShiftDown && bControlDown) { - // Check Shift key hold for mice without HWheel function - if (bShiftDown && bControlDown) - { - // Alt+Ctrl+Shift+ScrollUp as Alt+Ctrl+Left - PostMessage(hwndTarget, WM_COMMAND, ID_R2LNEXT, 0); - return 1; - } - else if (bShiftDown) - { - // Alt+Shift+ScrollUp as Alt+Left - PostMessage(hwndTarget, WM_COMMAND, ID_R2L, 0); - return 1; - } - else if (!m_bRButtonDown) - { - // Alt+ScrollUp as Alt+Up - PostMessage(hwndTarget, WM_COMMAND, ID_PREVDIFF, 0); - return 1; - } + // Alt+Ctrl+Shift+ScrollUp as Alt+Ctrl+Left + PostMessage(hwndTarget, WM_COMMAND, ID_R2LNEXT, 0); + return true; } - else if (zDelta < 0) + else if (bShiftDown) { - // Check Shift key hold for mice without HWheel function - if (bShiftDown && bControlDown) - { - // Alt+Ctrl+Shift+ScrollDown as Alt+Ctrl+Right - PostMessage(hwndTarget, WM_COMMAND, ID_L2RNEXT, 0); - return 1; - } - else if (bShiftDown) - { - // Alt+Shift+ScrollDown as Alt+Right - PostMessage(hwndTarget, WM_COMMAND, ID_L2R, 0); - return 1; - } - else if (!m_bRButtonDown) - { - // Alt+ScrollDown as Alt+Down - PostMessage(hwndTarget, WM_COMMAND, ID_NEXTDIFF, 0); - return 1; - } + // Alt+Shift+ScrollUp as Alt+Left + PostMessage(hwndTarget, WM_COMMAND, ID_R2L, 0); + return true; + } + else if (m_currentState == State::Idle) + { + // Alt+ScrollUp as Alt+Up + PostMessage(hwndTarget, WM_COMMAND, ID_PREVDIFF, 0); + return true; } } - - // Hold mice right button for One-handed operation - if (m_bRButtonDown) + else if (zDelta < 0) { - HWND hwndTarget = GetForegroundWindow(); - if (zDelta > 0) + // Check Shift key hold for mice without HWheel function + if (bShiftDown && bControlDown) { - // RButton+ScrollUp as Alt+Up - StartRightWheelScrolling(); - PostMessage(hwndTarget, WM_COMMAND, ID_PREVDIFF, 0); - return 1; + // Alt+Ctrl+Shift+ScrollDown as Alt+Ctrl+Right + PostMessage(hwndTarget, WM_COMMAND, ID_L2RNEXT, 0); + return true; } - else if (zDelta < 0) + else if (bShiftDown) { - // RButton+ScrollDown as Alt+Down - StartRightWheelScrolling(); + // Alt+Shift+ScrollDown as Alt+Right + PostMessage(hwndTarget, WM_COMMAND, ID_L2R, 0); + return true; + } + else if (m_currentState == State::Idle) + { + // Alt+ScrollDown as Alt+Down PostMessage(hwndTarget, WM_COMMAND, ID_NEXTDIFF, 0); - return 1; + return true; } } } - else if (wParam == WM_MOUSEHWHEEL) - { - MOUSEHOOKSTRUCTEX* pMouseStruct = (MOUSEHOOKSTRUCTEX*)lParam; - short zDelta = HIWORD(pMouseStruct->mouseData); + return false; +} - if (GetAsyncKeyState(VK_MENU) & 0x8000) +bool CMouseHook::MouseHWheelAction(LPARAM lParam) +{ + MOUSEHOOKSTRUCTEX* pMouseStruct = (MOUSEHOOKSTRUCTEX*)lParam; + short zDelta = HIWORD(pMouseStruct->mouseData); + + if (GetAsyncKeyState(VK_MENU) & 0x8000) + { + HWND hwndTarget = GetForegroundWindow(); + const auto bControlDown = GetAsyncKeyState(VK_CONTROL) & 0x8000; + // zDelta > 0 scrool right, < 0 scrool left + if (zDelta > 0) { - HWND hwndTarget = GetForegroundWindow(); - const auto bControlDown = GetAsyncKeyState(VK_CONTROL) & 0x8000; - // zDelta > 0 scrool right, < 0 scrool left - if (zDelta > 0) + if (bControlDown) { - if (bControlDown) - { - // Alt+Ctrl+HScrollRight as Alt+Ctrl+Right - PostMessage(hwndTarget, WM_COMMAND, ID_L2RNEXT, 0); - return 1; - } - else if (!m_bRButtonDown) - { - // Alt+HScrollRight as Alt+Right - PostMessage(hwndTarget, WM_COMMAND, ID_L2R, 0); - return 1; - } + // Alt+Ctrl+HScrollRight as Alt+Ctrl+Right + PostMessage(hwndTarget, WM_COMMAND, ID_L2RNEXT, 0); + return true; } - else if (zDelta < 0) + else if (m_currentState == State::Idle) { - if (bControlDown) - { - // Alt+Ctrl+HScrollLeft as Alt+Ctrl+Left - PostMessage(hwndTarget, WM_COMMAND, ID_R2LNEXT, 0); - return 1; - } - else if (!m_bRButtonDown) - { - // Alt+HScrollLeft as Alt+Left - PostMessage(hwndTarget, WM_COMMAND, ID_R2L, 0); - return 1; - } + // Alt+HScrollRight as Alt+Right + PostMessage(hwndTarget, WM_COMMAND, ID_L2R, 0); + return true; } } - - // Hold mice right button for One-handed operation - if (m_bRButtonDown) + else if (zDelta < 0) { - HWND hwndTarget = GetForegroundWindow(); - if (zDelta > 0) + if (bControlDown) { - // RButton+ScrollRight as Alt+Right - StartRightWheelScrolling(); - PostMessage(hwndTarget, WM_COMMAND, ID_L2R, 0); - return 1; + // Alt+Ctrl+HScrollLeft as Alt+Ctrl+Left + PostMessage(hwndTarget, WM_COMMAND, ID_R2LNEXT, 0); + return true; } - else if (zDelta < 0) + else if (m_currentState == State::Idle) { - // RButton+ScrollLeft as Alt+Left - StartRightWheelScrolling(); + // Alt+HScrollLeft as Alt+Left PostMessage(hwndTarget, WM_COMMAND, ID_R2L, 0); - return 1; + return true; } } } - return CallNextHookEx(m_hMouseHook, nCode, wParam, lParam); + return false; +} + +bool CMouseHook::RightButtonDown_MouseWheel(LPARAM lParam) +{ + MOUSEHOOKSTRUCTEX* pMouseStruct = (MOUSEHOOKSTRUCTEX*)lParam; + short zDelta = HIWORD(pMouseStruct->mouseData); + HWND hwndTarget = GetForegroundWindow(); + if (zDelta > 0) + { + // RButton(hold)+ScrollUp as Alt+Up, RButton(hold) MButtonClk WheelScrollUp as Alt+Left + StartRightWheelScrolling(); + PostMessage(hwndTarget, WM_COMMAND, (State::HorizontalScrollSimulated == m_currentState) ? + ID_R2L : ID_PREVDIFF, 0); + return true; + } + else if (zDelta < 0) + { + // RButton(hold)+ScrollDown as Alt+Down, RButton(hold) MButtonClk WheelScrollUp as Alt+Right + StartRightWheelScrolling(); + PostMessage(hwndTarget, WM_COMMAND, (State::HorizontalScrollSimulated == m_currentState) ? + ID_L2R : ID_NEXTDIFF, 0); + return true; + } + return false; +} + +bool CMouseHook::RightButtonDown_MouseHWheel(LPARAM lParam) +{ + MOUSEHOOKSTRUCTEX* pMouseStruct = (MOUSEHOOKSTRUCTEX*)lParam; + short zDelta = HIWORD(pMouseStruct->mouseData); + HWND hwndTarget = GetForegroundWindow(); + if (zDelta > 0) + { + // RButton+ScrollRight as Alt+Right + StartRightWheelScrolling(); + PostMessage(hwndTarget, WM_COMMAND, ID_L2R, 0); + return true; + } + else if (zDelta < 0) + { + // RButton+ScrollLeft as Alt+Left + StartRightWheelScrolling(); + PostMessage(hwndTarget, WM_COMMAND, ID_R2L, 0); + return true; + } + return false; } void CMouseHook::SetMouseHook() @@ -180,4 +237,3 @@ void CMouseHook::UnhookMouseHook() m_hMouseHook = nullptr; } } - diff --git a/Src/MouseHook.h b/Src/MouseHook.h index 92b747aafcb..46f66e88b0c 100644 --- a/Src/MouseHook.h +++ b/Src/MouseHook.h @@ -10,6 +10,20 @@ class CMouseHook inline static void EndRightWheelScrolling() { if (!m_bIgnoreRBUp) return; m_endTimeRightWheelScrolling = std::chrono::system_clock::now(); m_bIgnoreRBUp = false; } inline static HHOOK m_hMouseHook; inline static bool m_bIgnoreRBUp; - inline static bool m_bRButtonDown; inline static std::chrono::system_clock::time_point m_endTimeRightWheelScrolling; + + enum class State { + Idle, + RightButtonDown, + HorizontalScrollSimulated + }; + inline static State m_currentState; + static void Transition(State nextState); + // State matrix definition + static const std::function stateMatrix[3][6]; + + static bool MouseWheelAction(LPARAM lParam); + static bool MouseHWheelAction(LPARAM lParam); + static bool RightButtonDown_MouseWheel(LPARAM lParam); + static bool RightButtonDown_MouseHWheel(LPARAM lParam); }; diff --git a/Translations/WinMerge/ChineseSimplified.po b/Translations/WinMerge/ChineseSimplified.po index 98c5eb1f382..c746d0b9ab1 100644 --- a/Translations/WinMerge/ChineseSimplified.po +++ b/Translations/WinMerge/ChineseSimplified.po @@ -4391,31 +4391,25 @@ msgstr "" "(鼠标右键+滚轮下滚)\n" "(Alt+滚轮下滚)" -msgid "" -"\n" -"Copy to Right (Alt+Right)\n" -"(Right Button+Wheel Right)\n" -"(Alt+Wheel Right)\n" -"(Alt+Shift+Wheel Down)" +msgid "\nCopy to Right (Alt+Right)\n(Right Button+Wheel Right)\n(Alt+Wheel Right)\n(Right Button+Shift+Wheel Down)\n(Alt+Shift+Wheel Down)\n(Hold Right Button+Middle Button Click+Wheel Down)" msgstr "" "\n" "复制到右侧 (Alt+Right)\n" "(鼠标右键+滚轮右滚)\n" "(Alt+滚轮右滚)\n" -"(Alt+Shift+滚轮下滚)" +"(鼠标右键+Shift+滚轮下滚)\n" +"(Alt+Shift+滚轮下滚)\n" +"(按住鼠标右键+点击鼠标中键+滚轮下滚)" -msgid "" -"\n" -"Copy to Left (Alt+Left)\n" -"(Right Button+Wheel Left)\n" -"(Alt+Wheel Left)\n" -"(Alt+Shift+Wheel Up)" +msgid "\nCopy to Left (Alt+Left)\n(Right Button+Wheel Left)\n(Alt+Wheel Left)\n(Right Button+Shift+Wheel Up)\n(Alt+Shift+Wheel Up)\n(Hold Right Button+Middle Button Click+Wheel Up)" msgstr "" "\n" "复制到左侧 (Alt+Left)\n" "(鼠标右键+滚轮左滚)\n" "(Alt+滚轮左滚)\n" -"(Alt+Shift+滚轮上滚)" +"(鼠标右键+Shift+滚轮上滚)\n" +"(Alt+Shift+滚轮上滚)\n" +"(按住鼠标右键+点击鼠标中键+滚轮上滚)" msgid "" "\n" diff --git a/Translations/WinMerge/ChineseTraditional.po b/Translations/WinMerge/ChineseTraditional.po index b8f120af65d..95266857136 100644 --- a/Translations/WinMerge/ChineseTraditional.po +++ b/Translations/WinMerge/ChineseTraditional.po @@ -5024,11 +5024,25 @@ msgstr "\n上一個差異 (Alt+Up)\n(滑鼠右鍵+滾輪上滾)\n(Alt+滾輪上 msgid "\nNext Difference (Alt+Down)\n(Right Button+Wheel Down)\n(Alt+Wheel Down)" msgstr "\n下一個差異 (Alt+Down)\n(滑鼠右鍵+滾輪下滾)\n(Alt+滾輪下滾)" -msgid "\nCopy to Right (Alt+Right)\n(Right Button+Wheel Right)\n(Alt+Wheel Right)\n(Alt+Shift+Wheel Down)" -msgstr "\n複製到右側 (Alt+Right)\n(滑鼠右鍵+滾輪右滾)\n(Alt+滾輪右滾)\n(Alt+Shift+滾輪下滾)" +msgid "\nCopy to Right (Alt+Right)\n(Right Button+Wheel Right)\n(Alt+Wheel Right)\n(Right Button+Shift+Wheel Down)\n(Alt+Shift+Wheel Down)\n(Hold Right Button+Middle Button Click+Wheel Down)" +msgstr "" +"\n" +"複製到右側 (Alt+Right)\n" +"(滑鼠右鍵+滾輪右滾)\n" +"(Alt+滾輪右滾)\n" +"(滑鼠右鍵+Shift+滾輪下滾)\n" +"(Alt+Shift+滾輪下滾)\n" +"(按住滑鼠右鍵+點擊滑鼠中鍵+滾輪下滾)" -msgid "\nCopy to Left (Alt+Left)\n(Right Button+Wheel Left)\n(Alt+Wheel Left)\n(Alt+Shift+Wheel Up)" -msgstr "\n複製到左側 (Alt+Left)\n(滑鼠右鍵+滾輪左滾)\n(Alt+滾輪左滾)\n(Alt+Shift+滾輪上滾)" +msgid "\nCopy to Left (Alt+Left)\n(Right Button+Wheel Left)\n(Alt+Wheel Left)\n(Right Button+Shift+Wheel Up)\n(Alt+Shift+Wheel Up)\n(Hold Right Button+Middle Button Click+Wheel Up)" +msgstr "" +"\n" +"複製到左側 (Alt+Left)\n" +"(滑鼠右鍵+滾輪左滾)\n" +"(Alt+滾輪左滾)\n" +"(滑鼠右鍵+Shift+滾輪上滾)\n" +"(Alt+Shift+滾輪上滾)\n" +"(按住滑鼠右鍵+點擊滑鼠中鍵+滾輪上滾)" msgid "\nCopy to Right and Advance (Ctrl+Alt+Right)\n(Ctrl+Alt+Wheel Right)\n(Ctrl+Alt+Shift+Wheel Down)" msgstr "\n複製到右側後到下一個 (Ctrl+Alt+Right)\n(Ctrl+Alt+滾輪右滾)\n(Ctrl+Alt+Shift+滾輪下滾)"