• Login
Community
  • Login

PythonScript Toggleable Script?

Scheduled Pinned Locked Moved Notepad++ & Plugin Development
34 Posts 3 Posters 3.9k 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.
  • J
    John Doe 1
    last edited by John Doe 1 Apr 22, 2022, 2:41 PM Apr 22, 2022, 2:41 PM

    Hello everyone, I was wondering if it is possible (or deeply time consuming) to make a toggleable PythonScript in Notepad++. Notepad++ has the useful feature of holding “Shift” + “Alt” to go into Column text select mode which is useful for making changes to data files such as CSVs. I would like to create a PythonScript that can be toggled on/off for activating column text select mode. Preferably as a button using the Customize Toolbar plugin. Thank you in advance for any assistance!

    A 1 Reply Last reply Apr 22, 2022, 3:17 PM Reply Quote 0
    • A
      Alan Kilborn @John Doe 1
      last edited by Apr 22, 2022, 3:17 PM

      @john-doe-1

      It’s probably possible, but not “easy”.
      Can you elaborate a bit more on the use-case?
      My guess is you want this:

      • user can move the caret around with plain (unmodified) arrow keys
      • if toolbar button commands non-column mode state, shift+arrow keys selects normal stream selection
      • if toolbar button commands column mode state, shift+arrow keys (note: no Alt) selects column mode selection
      J 2 Replies Last reply Apr 22, 2022, 3:34 PM Reply Quote 2
      • J
        John Doe 1 @Alan Kilborn
        last edited by Apr 22, 2022, 3:34 PM

        @alan-kilborn Yup pretty much, just want to have a toolbar button that the user can click to toggle between column text select and default text select modes. Just like how the Word wrap tool that Notepad++ comes with works, I just click it to toggle on word wrap and whenever I am ready to toggle it off I just click it once more.

        1 Reply Last reply Reply Quote 0
        • J John Doe 1 deleted this topic on Apr 22, 2022, 8:45 PM
        • J John Doe 1 restored this topic on Apr 22, 2022, 8:45 PM
        • J
          John Doe 1 @Alan Kilborn
          last edited by Apr 22, 2022, 8:46 PM

          @alan-kilborn I got it figured out now, should I delete this topic?

          A 1 Reply Last reply Apr 22, 2022, 8:50 PM Reply Quote 0
          • A
            Alan Kilborn @John Doe 1
            last edited by Apr 22, 2022, 8:50 PM

            @john-doe-1

            You typically don’t delete topics here.
            WHAT exactly do you have “figured out”?
            It is good to post what you have solved and how you’ve solved it, so others can benefit.

            J 1 Reply Last reply Apr 22, 2022, 8:52 PM Reply Quote 0
            • J
              John Doe 1 @Alan Kilborn
              last edited by Apr 22, 2022, 8:52 PM

              @alan-kilborn I figured out how to have the Script remember if it was toggled on or off after execution. I did this by writing to a text file to store data indicating what toggle the script was set to. This way the script can “remember” what it was set to by opening up that text file at the beginning of each execution.

              A 1 Reply Last reply Apr 22, 2022, 8:57 PM Reply Quote 0
              • A
                Alan Kilborn @John Doe 1
                last edited by Apr 22, 2022, 8:57 PM

                @john-doe-1

                Well…OK.
                But how does that solve the problem of how to have a mode where you don’t have to hold Shift+Alt while making a column block selection?

                J 1 Reply Last reply Apr 22, 2022, 9:00 PM Reply Quote 0
                • J
                  John Doe 1 @Alan Kilborn
                  last edited by Apr 22, 2022, 9:00 PM

                  @alan-kilborn I used the keyboard library which is 100% pure Python so I was able to just drop it in the PythonScript/lib folder to utilize it. From there I used the keyboard.press and keyboard.release functions to press and release Alt (turns out I just have to hold Alt to use column select not Alt and Shift).

                  I do have one minor concern with how my script is working though which is that if the user closes out Notepad++ without toggling my script to off, the Alt key remains to be held even when Notepad++ is closed for some reason, is there a way I can detect when the user closes Notepad++ to account for this?

                  A 1 Reply Last reply Apr 22, 2022, 11:25 PM Reply Quote 0
                  • A
                    Alan Kilborn @John Doe 1
                    last edited by Apr 22, 2022, 11:25 PM

                    @john-doe-1 said in PythonScript Toggleable Script?:

                    I used the keyboard library which is 100% pure Python so I was able to just drop it in the PythonScript/lib folder to utilize it. From there I used the keyboard.press and keyboard.release functions to press and release Alt.

                    This is, IMO, a bad way to do this.
                    Doesn’t this cut off the Alt key from functionally being part of any shortcuts that use it?
                    And doesn’t it neuter its functionality in dialog boxes (e.g. Find) where Alt can be used in combination with other keys to activate controls, e.g., Alt+c toggles the current setting for the Match case checkbox.
                    And lastly, Alt+f will activate the File menu, etc.
                    Sure…I suppose if you never use these things…

                    (turns out I just have to hold Alt to use column select not Alt and Shift)

                    I don’t know what this means.
                    Alt alone only works to column-select when used with the mouse, not the keyboard.

                    is there a way I can detect when the user closes Notepad++

                    A shutdown notification can be configured via NOTIFICATION.SHUTDOWN.

                    A 1 Reply Last reply Apr 23, 2022, 12:47 AM Reply Quote 0
                    • A
                      Alan Kilborn @Alan Kilborn
                      last edited by Apr 23, 2022, 12:47 AM

                      @alan-kilborn said in PythonScript Toggleable Script?:

                      Doesn’t this cut off the Alt key from functionally being part of any shortcuts that use it?
                      And doesn’t it neuter its functionality in dialog boxes (e.g. Find) where Alt can be used in combination with other keys to activate controls, e.g., Alt+c toggles the current setting for the Match case checkbox.
                      And lastly, Alt+f will activate the File menu, etc.

                      Actually, I think my brain was totally out to lunch with this reply.
                      I think it plays to the fact that I have no idea what the OP is trying to accomplish here.
                      I suppose it works logically if you enter this “special mode”, then all you do is make arrow and shift+arrow movements, but if you want to insert a character somewhere, would it be registered as Alt+t if you press t, with the OP’s method.
                      But, I really don’t know, at all. :-(

                      J 2 Replies Last reply Apr 25, 2022, 1:23 PM Reply Quote 0
                      • J
                        John Doe 1 @Alan Kilborn
                        last edited by John Doe 1 Apr 25, 2022, 1:23 PM Apr 25, 2022, 1:23 PM

                        @alan-kilborn Edit

                        1 Reply Last reply Reply Quote 0
                        • J
                          John Doe 1 @Alan Kilborn
                          last edited by Apr 25, 2022, 1:26 PM

                          @alan-kilborn @alan-kilborn Sorry input was being weird had to edit this reply. You make some good points, yes the holding of the “Alt” key does present some issues, most of them minor and not a problem for my uses. Except for inserting the “t” character and it being registed as “Alt” + “t” which would switch windows (depending on your configurations), that is a bit more of an issue. Do you have any ideas of an alternate way to achieve this? Thanks for your help so far!

                          P A 2 Replies Last reply Apr 25, 2022, 1:55 PM Reply Quote 0
                          • P
                            PeterJones @John Doe 1
                            last edited by Apr 25, 2022, 1:55 PM

                            @john-doe-1 ,

                            If your goal is to have the “alt” key behave as “sticky”, Windows OS has an accessibility feature (“ease of access - keyboard”) for that.

                            https://www.google.com/search?q=windows+10+sticky+alt+key =>
                            https://winaero.com/turn-on-or-off-sticky-keys-in-windows-10/ or https://www.howtogeek.com/739764/how-to-turn-off-sticky-keys-on-windows-10/ or …

                            No need to highjack the Alt key or any such thing.

                            J 1 Reply Last reply Apr 25, 2022, 2:04 PM Reply Quote 1
                            • J
                              John Doe 1 @PeterJones
                              last edited by John Doe 1 Apr 25, 2022, 2:04 PM Apr 25, 2022, 2:04 PM

                              @peterjones Thanks but I wanted to make it a toolbar icon attached to a PythonScript, I realize the same function can be achieved with Keyboard Shortcuts and Sticky Keys but having a toolbar icon is just my personal preference.

                              1 Reply Last reply Reply Quote 0
                              • A
                                Alan Kilborn @John Doe 1
                                last edited by Apr 25, 2022, 2:29 PM

                                @john-doe-1 said in PythonScript Toggleable Script?:

                                Do you have any ideas of an alternate way to achieve this?

                                Yes, here’s a demo script I call RectangularSelectModeToggle.py. Each time it is run (hint: you can tie the running of it to a toolbar button) it toggles the shift+arrows selection functionality between selecting normal (what’s called a stream selection) and a selecting a column block (called rectangular selection). The “Alt” key is not involved. The script is fairly “low level”, and not a short one.

                                # -*- coding: utf-8 -*-
                                from __future__ import print_function
                                
                                from Npp import *
                                import inspect
                                import os
                                import ctypes
                                from ctypes import wintypes
                                import platform
                                
                                #-------------------------------------------------------------------------------
                                
                                user32 = ctypes.WinDLL('user32')
                                
                                notepad.hwnd = user32.FindWindowW(u'Notepad++', None)
                                editor1.hwnd = user32.FindWindowExW(notepad.hwnd, None, u'Scintilla', None)
                                editor2.hwnd = user32.FindWindowExW(notepad.hwnd, editor1.hwnd, u'Scintilla', None)
                                
                                LRESULT = wintypes.LPARAM
                                
                                WndProcType = ctypes.WINFUNCTYPE(
                                    LRESULT,  # return type
                                    wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM  # arguments
                                    )
                                
                                running_32bit = platform.architecture()[0] == '32bit'
                                
                                SetWindowLong = user32.SetWindowLongW if running_32bit else user32.SetWindowLongPtrW
                                SetWindowLong.restype = WndProcType
                                SetWindowLong.argtypes = [wintypes.HWND, wintypes.INT, WndProcType]
                                
                                GWL_WNDPROC = -4
                                
                                WM_KEYDOWN    = 0x100
                                WM_KEYUP      = 0x101
                                WM_SYSKEYDOWN = 0x104
                                WM_SYSKEYUP   = 0x105
                                WM_KILLFOCUS  = 0x8
                                
                                VK_SHIFT         = 0x10
                                VK_CONTROL       = 0x11
                                VK_MENU = VK_ALT = 0x12
                                
                                VK_LEFT  = 0x25
                                VK_UP    = 0x26
                                VK_RIGHT = 0x27
                                VK_DOWN  = 0x28
                                
                                #-------------------------------------------------------------------------------
                                
                                class RSMT(object):
                                
                                    def __init__(self):
                                
                                        self.debug = True if 1 else False
                                
                                        self.this_script_name = inspect.getframeinfo(inspect.currentframe()).filename.split(os.sep)[-1].rsplit('.', 1)[0]
                                
                                        self.shift_pressed = self.ctrl_pressed = self.alt_pressed = False
                                
                                        self.altless_rectangular_select_mode_active = False
                                        # if this mode is active, user doesn't have to hold Alt while pressing Shift+arrows in order to make a rectangular selection
                                
                                        self.new_editor1_wnd_proc_hook_for_SetWindowLong = WndProcType(self.new_editor1_wnd_proc_hook)
                                        self.orig_editor1_wnd_proc = SetWindowLong(editor1.hwnd, GWL_WNDPROC, self.new_editor1_wnd_proc_hook_for_SetWindowLong)
                                
                                        self.new_editor2_wnd_proc_hook_for_SetWindowLong = WndProcType(self.new_editor2_wnd_proc_hook)
                                        self.orig_editor2_wnd_proc = SetWindowLong(editor2.hwnd, GWL_WNDPROC, self.new_editor2_wnd_proc_hook_for_SetWindowLong)
                                
                                    def common_editor_wnd_proc_hook(self, hwnd, msg, wParam, lParam):
                                
                                        retval = True  # default to allowing something other than this code to handle this message
                                
                                        if msg in [WM_KEYDOWN, WM_SYSKEYDOWN]:
                                
                                            if   wParam == VK_SHIFT:   self.shift_pressed = True
                                            elif wParam == VK_CONTROL: self.ctrl_pressed  = True
                                            elif wParam == VK_ALT:     self.alt_pressed   = True
                                
                                            elif wParam in [ VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN ]:
                                
                                                modifiers = ''
                                                modifiers += 'SHIFT+' if self.shift_pressed else ''
                                                modifiers += 'CTRL+'  if self.ctrl_pressed  else ''
                                                modifiers += 'ALT+'   if self.alt_pressed   else ''
                                                key = ''
                                                key += 'LEFT'  if wParam == VK_LEFT  else ''
                                                key += 'UP'    if wParam == VK_UP    else ''
                                                key += 'RIGHT' if wParam == VK_RIGHT else ''
                                                key += 'DOWN'  if wParam == VK_DOWN  else ''
                                                self.print(modifiers + key)
                                
                                                if self.altless_rectangular_select_mode_active:
                                
                                                    if self.shift_pressed and not self.ctrl_pressed and not self.alt_pressed:
                                
                                                        retval = False  # allow no further processing of this message; it will be handled here
                                
                                                        if wParam   == VK_LEFT:  editor.charLeftRectExtend()
                                                        elif wParam == VK_UP:    editor.lineUpRectExtend()
                                                        elif wParam == VK_RIGHT: editor.charRightRectExtend()
                                                        elif wParam == VK_DOWN:  editor.lineDownRectExtend()
                                
                                        elif msg in [WM_KEYUP, WM_SYSKEYUP]:
                                
                                            if wParam   == VK_SHIFT:   self.shift_pressed = False
                                            elif wParam == VK_CONTROL: self.ctrl_pressed  = False
                                            elif wParam == VK_ALT:     self.alt_pressed   = False
                                
                                        elif msg == WM_KILLFOCUS:
                                
                                            self.shift_pressed = self.ctrl_pressed = self.alt_pressed = False
                                
                                        return retval
                                
                                    def new_editor1_wnd_proc_hook(self, hwnd, msg, wParam, lParam):
                                        retval = self.common_editor_wnd_proc_hook(hwnd, msg, wParam, lParam)
                                        if retval: retval = self.orig_editor1_wnd_proc(hwnd, msg, wParam, lParam)
                                        return retval
                                
                                    def new_editor2_wnd_proc_hook(self, hwnd, msg, wParam, lParam):
                                        retval = self.common_editor_wnd_proc_hook(hwnd, msg, wParam, lParam)
                                        if retval: retval = self.orig_editor2_wnd_proc(hwnd, msg, wParam, lParam)
                                        return retval
                                
                                    def toggle(self):
                                        editor.setEmptySelection(editor.getCurrentPos())
                                        self.altless_rectangular_select_mode_active = not self.altless_rectangular_select_mode_active
                                        self.mb('Alt-less column select mode is now {}'.format('active' if self.altless_rectangular_select_mode_active else 'inactive'))
                                
                                    def print(self, *args):
                                        if self.debug:
                                            #console.show()
                                            print('RSMT:', *args)
                                
                                    def mb(self, msg, flags=0, title=''):  # a message-box function
                                        return notepad.messageBox(msg, title if title else self.this_script_name, flags)
                                
                                #-------------------------------------------------------------------------------
                                
                                if __name__ == '__main__':
                                    try:
                                        rsmt
                                    except NameError:
                                        rsmt = RSMT()
                                    rsmt.toggle()
                                
                                J 1 Reply Last reply Apr 25, 2022, 3:00 PM Reply Quote 1
                                • A Alan Kilborn referenced this topic on Apr 25, 2022, 2:32 PM
                                • J
                                  John Doe 1 @Alan Kilborn
                                  last edited by Apr 25, 2022, 3:00 PM

                                  @alan-kilborn Thanks for that! I still would like to know how to check if Notepad++ is closed or tabbed out of for future projects. Is there a function from Notepad, Editor or somewhere that can return whether or not Notepad++ is the active window? I would like to remedy this issue in my Script for satisfaction’s sake.

                                  A P 2 Replies Last reply Apr 25, 2022, 5:10 PM Reply Quote 0
                                  • A
                                    Alan Kilborn @John Doe 1
                                    last edited by Apr 25, 2022, 5:10 PM

                                    @john-doe-1 said in PythonScript Toggleable Script?:

                                    check if Notepad++ is closed

                                    You scan do this with NOTIFICATION.SHUTDOWN in a notepad.callback. To see how this works if you don’t know, type notepad.callback into the editor and then invoke PythonScript’s context-sensitive help on it.

                                    There’s also SCINTILLANOTIFICATION.FOCUSOUT which you could experiment with to see if it meets your needs. That one is an editor.callback.

                                    can return whether or not Notepad++ is the active window?

                                    I suppose you could try things with the windows API function GetForegroundWindow .

                                    J 1 Reply Last reply Apr 27, 2022, 6:30 PM Reply Quote 2
                                    • P
                                      PeterJones @John Doe 1
                                      last edited by Apr 27, 2022, 3:53 PM

                                      @john-doe-1 ,

                                      Your paradigm is BAD and causing you problems.

                                      Try this script out for size:

                                      # encoding=utf-8
                                      """in response to https://community.notepad-plus-plus.org/topic/22890/ and 22919
                                      
                                      alternate paradigm
                                      """
                                      from Npp import notepad, editor, SELECTIONMODE, STATUSBARSECTION
                                      
                                      try:
                                          columnSelectMode
                                          startPos
                                          endPos
                                      except NameError:
                                          #console.show()
                                          console.write('initialize toggle mode for the first time\n')
                                          columnSelectMode = False
                                          startPos = None
                                          endPos = None
                                      
                                      if not columnSelectMode:
                                          startPos = editor.getCurrentPos()
                                          endPos = startPos
                                          notepad.setStatusBar(STATUSBARSECTION.DOCTYPE, "In Rectangle/Column Selection Mode")
                                      
                                      else:
                                          endPos = editor.getCurrentPos()
                                          editor.setSel(startPos, endPos)
                                          editor.setSelectionMode( SELECTIONMODE.RECTANGLE )
                                      
                                          # this overrides the statusbar.. but the refresh UI will overwrite that with default
                                          notepad.setStatusBar(STATUSBARSECTION.DOCTYPE, "")
                                      
                                          # use the upcoming activateFile to refresh UI
                                          #   otherwise, it doesn't _look_ like column/rectangle select)
                                          notepad.activateFile(notepad.getCurrentFilename())
                                      
                                      columnSelectMode = not columnSelectMode
                                      
                                      #console.show()
                                      #console.write("toggle selection mode to {}: {} .. {}\n".format(columnSelectMode, startPos, endPos))
                                      
                                      J 1 Reply Last reply Apr 27, 2022, 4:31 PM Reply Quote 1
                                      • P PeterJones referenced this topic on Apr 27, 2022, 3:53 PM
                                      • J
                                        John Doe 1 @PeterJones
                                        last edited by Apr 27, 2022, 4:31 PM

                                        @peterjones the statement:

                                        editor.setSelectionMode( SELECTIONMODE.RECTANGLE )

                                        doesn’t seem to work, I tested it in a simple script with nothing else going on and this makes no changes to the selection mode, I’ve no idea why.

                                        P 2 Replies Last reply Apr 27, 2022, 5:39 PM Reply Quote 0
                                        • P
                                          PeterJones @John Doe 1
                                          last edited by PeterJones Apr 27, 2022, 5:49 PM Apr 27, 2022, 5:39 PM

                                          @john-doe-1 ,

                                          That’s what I thought at first. But when I triggered the refresh (the activate file line), it updated and showed that it really did change what was shown to be the rectangle – that’s why I included that line in the script, and included comments to say it was needed to get it to show it’s a rectangle.

                                          You can also tell that it was rectangle by just doing the editor.getSelectionMode() which returns a 1 (IIRC) for rectangle, or just do the copy then paste in the new location, and see it only grabbed the rectangle, not the full lines. Actually, I first saw it was working when I swapped to another tab and then back, and saw it was suddenly a rectangle instead of the stream it looked like.

                                          J A 2 Replies Last reply Apr 27, 2022, 5:50 PM Reply Quote 1
                                          9 out of 34
                                          • First post
                                            9/34
                                            Last post
                                          The Community of users of the Notepad++ text editor.
                                          Powered by NodeBB | Contributors