• Login
Community
  • Login

Can't rebind mouse buttons/scroll wheel

Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
13 Posts 4 Posters 1.2k Views
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M
    mathlete2 @Valour549
    last edited by Feb 28, 2025, 4:40 PM

    @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.

    P 1 Reply Last reply Feb 28, 2025, 11:58 PM Reply Quote 0
    • P
      PeterJones @mathlete2
      last edited by Feb 28, 2025, 11:58 PM

      @Valour549 ,

      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.

      P 1 Reply Last reply Mar 1, 2025, 7:35 PM Reply Quote 1
      • P
        PeterJones @PeterJones
        last edited by Mar 1, 2025, 7:35 PM

        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.

        A 1 Reply Last reply Mar 1, 2025, 7:57 PM Reply Quote 1
        • A
          Alan Kilborn @PeterJones
          last edited by Mar 1, 2025, 7:57 PM

          @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. :-)

          1 Reply Last reply Reply Quote 1
          • A
            Alan Kilborn
            last edited by Mar 2, 2025, 12:35 AM

            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
            
            1 Reply Last reply Reply Quote 1
            • A Alan Kilborn referenced this topic on Mar 2, 2025, 12:35 AM
            • A
              Alan Kilborn
              last edited by Mar 2, 2025, 10:44 AM

              Script compatibility note for PythonScript2:

              Find 0x1_0000 in the code and replace with 0x10000.

              1 Reply Last reply Reply Quote 1
              • V
                Valour549
                last edited by Mar 4, 2025, 6:35 PM

                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 🙏

                P 1 Reply Last reply Mar 4, 2025, 6:49 PM Reply Quote 0
                • P
                  PeterJones @Valour549
                  last edited by Mar 4, 2025, 6:49 PM

                  @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

                  1 Reply Last reply Reply Quote 0
                  • V
                    Valour549
                    last edited by Mar 4, 2025, 10:01 PM

                    @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?

                    671fa878-d756-4d56-8e6b-67bf53015d46-image.png

                    P 1 Reply Last reply Mar 4, 2025, 10:08 PM Reply Quote 1
                    • P
                      PeterJones @Valour549
                      last edited by Mar 4, 2025, 10:08 PM

                      @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.

                      1 Reply Last reply Reply Quote 2
                      • A
                        Alan Kilborn
                        last edited by Mar 5, 2025, 4:08 PM

                        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.

                        1 Reply Last reply Reply Quote 0
                        • A Alan Kilborn referenced this topic on Mar 29, 2025, 12:09 AM
                        12 out of 13
                        • First post
                          12/13
                          Last post
                        The Community of users of the Notepad++ text editor.
                        Powered by NodeBB | Contributors