Can't rebind mouse buttons/scroll wheel
-
@Valour549 said in Can't rebind mouse buttons/scroll wheel:
Is there any particular reason why we can’t rebind modifier + mouse buttons/scroll wheel as hotkeys?
Maybe because the buttons/wheel on a mouse aren’t considers “keys” per se - they are forms of input from a completely different piece of hardware.
Admittedly, programs like Autohotkey are able to manage input from both the keyboard and mouse, so in theory, it should be possible for NP++ to do the same. Further to @Alan-Kilborn 's point about filing a feature request: there doesn’t seem to be one for this particular use of the mouse yet.
-
I just double-checked, and VK_LBUTTON, VK_RBUTTON, and VK_MBUTTON are sent as virtual keycodes (as are the X1 and X2 mouse buttons, so if you’ve got a mouse with extra buttons, they might be mappable as well), just the same as the other keyboard keys, so it should be doable. Scroll wheel events are processed differently (not with virtual keycodes, but with the WM_MOUSEWHEEL message), so I don’t think that Shortcut Mapper could process a scrollwheel up/down without (as far as I can tell) a huge redesign (since the Shortcut Mapper is currently limited to processing virtual keys).
I recently expanded which keys were available to Notepad++, so I know where in the code to look, so I’ll run some experiments of trying to add the five mouse clicks (though not the scrollwheel).
Since @mathlete2 was kind enough to check to see that no request currently exists, if you were to create an official feature request, then once I get my experiments working, I could attach a PR to that feature request. If you do make a request, make sure it links back to this discussion (and, if you can search the history of the forum to see if there have been other requests for allowing mouse buttons in the Shortcut Mapper; if so, link them); and once you’ve made the feature request, paste a link to that Issue back here in this discussion.
Unfortunately, even if I am able to implement it, I cannot guarantee that the developer would accept my PR, so there is an element of risk, that the effort might be rejected.
-
update: it didn’t end up being as easy as I’d hoped; on other real keys, it was just a matter of adding them to the available list for the shortcut mapper; when I tried the same with the mouse clicks (even unused ones, like the middle mouse button), nothing happened. So it’s going to be more involved than I hoped, so it will take more time, and I’m less hopeful that I have the skills to implement it. I won’t give up yet, but I thought I would manage expectations.
-
@PeterJones said:
So it’s going to be more involved than I hoped, so it will take more time…
Possibly the scripted version (supporting the mouse wheel) will beat you to publication. :-)
-
Here’s a scripted version of what the OP wants. It’s in two scripts, one is dependent on the other. First, the master script,
MouseWheelHookDemo.py
(the one that gets run):# -*- coding: utf-8 -*- ######################################### # # MouseWheelHookDemo (MWHD) # ######################################### # note: # This script was developed and tested under Python3 64-bit on unicode (non-ANSI) encoded data. # It may work as-is using Python2 and/or ANSI-encoded data and/or 32-bits, but that would be incidental. # references: # https://community.notepad-plus-plus.org/topic/26661 # for newbie info on PythonScripts, see https://community.notepad-plus-plus.org/topic/23039/faq-desk-how-to-install-and-run-a-script-in-pythonscript # to execute, use this in (e.g.) user startup.py: # from MouseWheelHookDemo import MOUSE_WHEEL_HOOK_DEMO # another execution option would be to copy and then paste that line into the PythonScript console >>> box # note: if running via startup.py, need to make sure that "Initialisation" for "Python Script Configuration" is set to "ATSTARTUP" and not "LAZY". from Npp import * from ctypes import WinDLL from MsgHooker import MH as MsgHook WM_MOUSEWHEEL = 0x020A WM_SYSCOMMAND = 0x0112 SC_KEYMENU = 0xF100 VK_MENU = 0x12 def HIWORD(value): return (value >> 16) & 0xFFFF def LOWORD(value): return value & 0xFFFF user32 = WinDLL('user32') def alt_held(): return (user32.GetAsyncKeyState(VK_MENU) & 0x8000) != 0 class MWHD(object): def __init__(self): # these must be assigned to "self" variables so that they aren't garbage collected! self.mh1 = MsgHook([ editor1.hwnd, editor2.hwnd ], self.editor_hook_func, [ WM_MOUSEWHEEL ]) self.mh2 = MsgHook([ notepad.hwnd ], self.notepad_hook_func, [ WM_SYSCOMMAND ]) def editor_hook_func(self, hwnd, msg, wParam, lParam): wheel_delta = HIWORD(wParam) if wheel_delta > 0x7FFF: wheel_delta -= 0x1_0000 keys_down = LOWORD(wParam) alt_down = alt_held() if alt_down and keys_down == 0: notepad.menuCommand(MENUCOMMAND.SEARCH_GOTONEXTFOUND if wheel_delta < 0 else MENUCOMMAND.SEARCH_GOTOPREVFOUND) return False return True # https://stackoverflow.com/questions/352270/how-to-cancel-the-system-key-down-state-in-windows def notepad_hook_func(self, hwnd, msg, wParam, lParam): if (wParam & 0xFFF0) == SC_KEYMENU and lParam == 0: return False return True MOUSE_WHEEL_HOOK_DEMO = MWHD()
Next, the dependent script,
MsgHooker.py
:# -*- coding: utf-8 -*- import platform from ctypes import (WinDLL, WINFUNCTYPE) from ctypes.wintypes import (HWND, INT, LPARAM, UINT, WPARAM) user32 = WinDLL('user32') GWL_WNDPROC = -4 # used to set a new address for the window procedure LRESULT = LPARAM WndProcType = WINFUNCTYPE( LRESULT, # return type HWND, UINT, WPARAM, LPARAM # function arguments ) running_32bit = platform.architecture()[0] == '32bit' SetWindowLong = user32.SetWindowLongW if running_32bit else user32.SetWindowLongPtrW SetWindowLong.restype = WndProcType SetWindowLong.argtypes = [ HWND, INT, WndProcType ] class MH(object): def __init__(self, hwnd_to_hook_list, hook_function, # supplied hook_function must have args: hwnd, msg, wparam, lparam # and must return True/False (False means the function handled the msg) msgs_to_hook_list=None, # None means ALL msgs ): self.users_hook_fn = hook_function self.msg_list = msgs_to_hook_list if msgs_to_hook_list is not None else [] self.new_wnd_proc_hook_for_SetWindowLong = WndProcType(self._new_wnd_proc_hook) # the result of this call must be a self.xxx variable! self.orig_wnd_proc_by_hwnd_dict = {} for h in hwnd_to_hook_list: self.orig_wnd_proc_by_hwnd_dict[h] = SetWindowLong(h, GWL_WNDPROC, self.new_wnd_proc_hook_for_SetWindowLong) def _new_wnd_proc_hook(self, hwnd, msg, wParam, lParam): retval = True # assume that this message will go unhandled (by us) need_to_call_orig_proc = True if len(self.msg_list) == 0 or msg in self.msg_list: retval = self.users_hook_fn(hwnd, msg, wParam, lParam) if not retval: need_to_call_orig_proc = False if need_to_call_orig_proc: retval = self.orig_wnd_proc_by_hwnd_dict[hwnd](hwnd, msg, wParam, lParam) return retval
-
A Alan Kilborn referenced this topic on
-
Script compatibility note for PythonScript2:
Find
0x1_0000
in the code and replace with0x10000
. -
Thank you all for the replies.
I have created a suggestion on github here.
@Alan-Kilborn How do I use the scripts you created? Kind of a noob here so a step by step would be much appreciated 🙏
-
@Valour549 said in Can't rebind mouse buttons/scroll wheel:
How do I use the scripts you created? Kind of a noob here
As it says near the top of @Alan-Kilborn’s first script,
for newbie info on PythonScripts, see https://community.notepad-plus-plus.org/topic/23039/faq-desk-how-to-install-and-run-a-script-in-pythonscript
-
@Alan-Kilborn Thank you very much for the script, it’s working great.
@PeterJones Thank you for the FAQ guide. I found a typo I think. The highlight parts are meant to say step #4 right?
-
@Valour549 said in Can't rebind mouse buttons/scroll wheel:
I found a typo I think. The highlight parts are meant to say step #4 right?
Not a typo, but a “forgot to update”. Originally, the “install PythonScript” was “step #0”, which meant that the shortcuts were at step #3. At some point, that changed, and I forgot to update the cross-reference.
Fixed.
-
I must say that I like the functionality provided by the script myself, and I’ve started to use it. One thing I don’t particularly like, however, is not anything to do with the script, but rather how the NextResult (F4) and PreviousResult (Shift+F4) functionality works. After the last match is encountered, another F4 “wraps around” to the first match. It’s similar for Shift+F4. I find this annoying for reasons I’ve specified HERE. If other users feel the same way, and can publicly state so, maybe we can get an option to control this behavior.