Community
    • Login

    Shortcut or menu path to "Rotate to right/left"

    Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
    18 Posts 5 Posters 3.4k 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.
    • PeterJonesP
      PeterJones
      last edited by

      Unfortunately, looking at the source code, it appears that the splitter-container hardcode-generates those commands, and I don’t think even the plugins communication system has any messages which talk directly with the SplitterContainer’s object. I couldn’t find any menu command ID’s which affect that either (though the move and clone to other view are there).

      Without a command ID or main menu position, you cannot record it in a macro. Without a published message ID, you’re not supposed to access those internal messages to manipulate it via a plugin.

      However, a clever PythonScript programmer could probably search through the child-windows of the main Notepad++ GUI window, until they found the SplitterContainer object’s HWND, and then SendMessage to that HWND to send a WM_COMMAND message to the SplitterContainer, with the lword as the left and right constants. Not that I would recommend someone do that 😉, since sending unpublished messages to internal hwnd objects is not guaranteed to stay consistently working from one version to the next (which is a risk you take by going beyond the published API).

      1 Reply Last reply Reply Quote 4
      • VTGroupGitHubV
        VTGroupGitHub
        last edited by

        Very much appreciated. It’s obviously not a major issue, but I wanted to make sure I wasn’t missing something obvious.

        1 Reply Last reply Reply Quote 1
        • EkopalypseE
          Ekopalypse
          last edited by Ekopalypse

          A python script might look like this

          import ctypes
          
          ROTATION_A_LEFT = 2000
          ROTATION_A_RIGHT = 2001
          WM_COMMAND = 0x111
          
          def isSingleView():
              npp_hwnd = ctypes.windll.user32.FindWindowW(u'Notepad++', None)
              splitter_hwnd = ctypes.windll.user32.FindWindowExW(npp_hwnd, None, u'splitterContainer', None)
              return (not bool(ctypes.windll.user32.IsWindowVisible(splitter_hwnd)), splitter_hwnd)
          
          def LOWORD(value): return value & 0xFFFF
          
          single_view, hwnd = isSingleView()
          if not single_view:
              ctypes.windll.user32.SendMessageW(hwnd, WM_COMMAND, LOWORD(ROTATION_A_LEFT), 0)
          

          @PeterJones - thx - explanation was perfect - I did not even had to check the source :-)

          deleeleeD 1 Reply Last reply Reply Quote 3
          • VTGroupGitHubV
            VTGroupGitHub
            last edited by

            Thank you! I’ve not done Python before, but with such clear code, I figured I’d give it a shot. :) Your code worked perfectly.

            Unfortunately, while I can run the script from the menu, when I try to assign it to “Customize Toolbar”, nothing happens when I press the new toolbar button. In case anyone sees something wrong with my last config line below, or knows something about a step I’m missing, I thought I’d post. All of the other commands below work as expected, it’s just that Rotate does nothing when the button is pressed.

            Edit,Line Operations,Sort Lines Lexicographically Ascending,sort.bmp
            Edit,Line Operations,Remove Consecutive Duplicate Lines,dedup.bmp
            Plugins,XML Tools,Pretty print (XML only),xml.bmp
            Plugins,JSON Viewer,Format JSON,json.bmp
            View,Move/Clone Current Document,Move to Other View,split.bmp
            Plugins,Python Script,Scripts,Rotate,rotate.bmp

            Thank you again.

            PeterJonesP 1 Reply Last reply Reply Quote 2
            • PeterJonesP
              PeterJones @VTGroupGitHub
              last edited by

              @VTGroupGitHub ,

              I don’t use Customize Toolbar. However, something does jump out at me: the first 5 rows of your config have 3 levels of menu (main menu, submenu, command) followed by the icon/bmp; the last has 4 levels of menu (main, submenu, submenu, command) before the icon/bmp. It may be that Customize Toolbar cannot go that deep (I don’t know).

              PythonScript plugin has Plugins > Python Script > Configuration…, which allows you to add a script into the Menu Items list on the left – this is a prerequisite to configuring a Settings > Shortcut Mapper keyboard shortcut to a script, and it might also be necessary for the script to work with Customize Toolbar as well – because if the Rotate script were in the left-hand list, it would show up in Plugins > Python Script > Rotate – which could be accessed using:

              Plugins,Python Script,Rotate,rotate.bmp
              

              … which then has the same number of levels as your other commands.

              Aside from that, there’s also the right-hand pane in the same Configuration… dialog, which allows you do add a PythonScript script directly into the toolbar (complete with icon) without needing the Customize Toolbar plugin at all.

              1 Reply Last reply Reply Quote 1
              • VTGroupGitHubV
                VTGroupGitHub
                last edited by

                I don’t know how to explain what happened, but I restarted N++ one more time, and now the button works as expected.
                Very odd, as I’d done that a couple of times already. Thanks to everyone for the assistance!

                And regarding the same number of levels comment, it appears that tool we’re using to talk decides that back-to-back commas should be displayed as a single comma. In all but the last config line above, there are 2 commas before the .bmp. The second comma just didn’t render in my post.

                1 Reply Last reply Reply Quote 2
                • PeterJonesP
                  PeterJones
                  last edited by

                  @VTGroupGitHub said in Shortcut or menu path to "Rotate to right/left":

                  tool we’re using to talk decides

                  Yes, it uses markdown format – though I’ve never seen it eat commas before; I guess it depends on what other markdown you’re using.

                  If you ever want text to come through literally, the best is to highlight the literal then press the </> toolbar button at the top:

                  Edit,Line Operations,Sort Lines Lexicographically Ascending,,sort.bmp
                  
                  1 Reply Last reply Reply Quote 1
                  • EkopalypseE
                    Ekopalypse
                    last edited by

                    What I think the problem was is that a newly created script hasn’t been assigned
                    an ID known to npp. This is normally done during plugin initialization where npp
                    asks for functions to register, hence the restart of npp solved it.

                    1 Reply Last reply Reply Quote 2
                    • deleeleeD
                      deleelee @Ekopalypse
                      last edited by

                      @Ekopalypse said in Shortcut or menu path to "Rotate to right/left":

                      A python script might look like this

                      import ctypes
                      
                      ROTATION_A_LEFT = 2000
                      ROTATION_A_RIGHT = 2001
                      WM_COMMAND = 0x111
                      
                      def isSingleView():
                          npp_hwnd = ctypes.windll.user32.FindWindowW(u'Notepad++', None)
                          splitter_hwnd = ctypes.windll.user32.FindWindowExW(npp_hwnd, None, u'splitterContainer', None)
                          return (not bool(ctypes.windll.user32.IsWindowVisible(splitter_hwnd)), splitter_hwnd)
                      
                      def LOWORD(value): return value & 0xFFFF
                      
                      single_view, hwnd = isSingleView()
                      if not single_view:
                          ctypes.windll.user32.SendMessageW(hwnd, WM_COMMAND, LOWORD(ROTATION_A_LEFT), 0)
                      

                      A few years old but it still works!!! I’ve been wanting a toolbar button for rotating the panes and this is great. Do you know if it would be possible to have the script toggle so that it rotates one way on the first click and back the other way on the next click? As it is, the first click rotates in the desired direction but the next click rotates once more in the same direction which doesn’t return the panes to the order they were in, so it requires three clicks to do that. Obviously, I could just have two buttons, one for right and one for left, but it would save toolbar space if they were combined into one button.

                      EkopalypseE 1 Reply Last reply Reply Quote 0
                      • EkopalypseE
                        Ekopalypse @deleelee
                        last edited by

                        @deleelee

                        Oh dear … When I see this old code, what was I thinking? :-D

                        let’s create a new property of the notepad class and store the state there.

                        import ctypes
                        
                        ROTATION_A_LEFT = 2000
                        ROTATION_A_RIGHT = 2001
                        WM_COMMAND = 0x111
                        
                        def isSingleView():
                            npp_hwnd = ctypes.windll.user32.FindWindowW(u'Notepad++', None)
                            splitter_hwnd = ctypes.windll.user32.FindWindowExW(npp_hwnd, None, u'splitterContainer', None)
                            return (not bool(ctypes.windll.user32.IsWindowVisible(splitter_hwnd)), splitter_hwnd)
                        
                        def LOWORD(value): return value & 0xFFFF
                        
                        if not hasattr(notepad, "rotateSplitView"):
                            notepad.rotateSplitView = False
                        
                        single_view, hwnd = isSingleView()
                        if not single_view:
                            if notepad.rotateSplitView:
                                ctypes.windll.user32.SendMessageW(hwnd, WM_COMMAND, LOWORD(ROTATION_A_LEFT), 0)
                            else:
                                ctypes.windll.user32.SendMessageW(hwnd, WM_COMMAND, LOWORD(ROTATION_A_RIGHT), 0)
                            notepad.rotateSplitView = not notepad.rotateSplitView
                        

                        But nowadays I guess I would do something like

                        from ctypes import wintypes, WinDLL
                        from Npp import notepad
                        
                        class ViewRotator():
                            def __init__(self):
                                self.LOWORD_LEFT = 2000
                                self.LOWORD_RIGHT = 2001
                                self.WM_COMMAND = 0x111
                                self.toggle_rotating_direction = False
                                self.npp_hwnd = WinDLL("user32").FindWindowW(u'Notepad++', None)
                                self.splitter_hwnd = WinDLL("user32").FindWindowExW(self.npp_hwnd, None, u'splitterContainer', None)
                                self.send = WinDLL("user32").SendMessageW
                                self.send.argtypes = [wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
                                self.send.resttype = wintypes.LPARAM  # LRESULT
                        
                            def isSingleView(self):
                                return not bool(WinDLL("user32").IsWindowVisible(self.splitter_hwnd))
                        
                            def rotate(self):
                                if not self.isSingleView():
                                    if self.toggle_rotating_direction:
                                        self.send(self.splitter_hwnd, self.WM_COMMAND, self.LOWORD_RIGHT, 0)
                                    else:
                                        self.send(self.splitter_hwnd, self.WM_COMMAND, self.LOWORD_LEFT, 0)
                                    self.toggle_rotating_direction = not self.toggle_rotating_direction
                        
                        
                        if not hasattr(notepad, "rotateSplitView"):
                            notepad.rotateSplitView = ViewRotator().rotate
                        
                        notepad.rotateSplitView()
                        
                        Alan KilbornA 1 Reply Last reply Reply Quote 1
                        • Alan KilbornA
                          Alan Kilborn @Ekopalypse
                          last edited by Alan Kilborn

                          @Ekopalypse

                          self.send.resttype = wintypes.LPARAM # LRESULT

                          I think you mean restype

                          def isSingleView(self):
                          …
                          if not self.isSingleView():

                          Any reason not to use notepad.isSingleView() instead of writing a custom function?


                          Oh dear … When I see this old code, what was I thinking? :-D

                          Yep, we all feel this way about our own old code. :-)
                          It’s because we’ve learned so much since the time of original writing.

                          EkopalypseE 1 Reply Last reply Reply Quote 3
                          • EkopalypseE
                            Ekopalypse @Alan Kilborn
                            last edited by

                            @Alan-Kilborn said in Shortcut or menu path to "Rotate to right/left":

                            I think you mean restype

                            Yes, I do, thanks for pointing that out.

                            Any reason not to use notepad.isSingleView() instead of writing a custom function?

                            No, I just hadn’t thought about whether there were any builtin methods.

                            Yep, we all feel this way about our own old code. :-)
                            It’s because we’ve learned so much since the time of original writing.

                            … and still learning … :-D that’s the beauty of this work, at least for me.

                            EkopalypseE 1 Reply Last reply Reply Quote 1
                            • EkopalypseE
                              Ekopalypse @Ekopalypse
                              last edited by Ekopalypse

                              @deleelee

                              UPDATED VERSION

                              from ctypes import wintypes, WinDLL
                              from Npp import notepad
                              
                              class ViewRotator():
                                  def __init__(self):
                                      self.LOWORD_LEFT = 2000
                                      self.LOWORD_RIGHT = 2001
                                      self.WM_COMMAND = 0x111
                                      self.toggle_rotating_direction = False
                                      self.npp_hwnd = WinDLL("user32").FindWindowW(u'Notepad++', None)
                                      self.splitter_hwnd = WinDLL("user32").FindWindowExW(self.npp_hwnd, None, u'splitterContainer', None)
                                      self.send = WinDLL("user32").SendMessageW
                                      self.send.argtypes = [wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
                                      self.send.restype = wintypes.LPARAM  # LRESULT
                              
                                  def rotate(self):
                                      if not notepad.isSingleView():
                                          if self.toggle_rotating_direction:
                                              self.send(self.splitter_hwnd, self.WM_COMMAND, self.LOWORD_RIGHT, 0)
                                          else:
                                              self.send(self.splitter_hwnd, self.WM_COMMAND, self.LOWORD_LEFT, 0)
                                          self.toggle_rotating_direction = not self.toggle_rotating_direction
                              
                              
                              if not hasattr(notepad, "rotateSplitView"):
                                  notepad.rotateSplitView = ViewRotator().rotate
                              
                              notepad.rotateSplitView()
                              
                              Alan KilbornA deleeleeD 2 Replies Last reply Reply Quote 4
                              • Alan KilbornA
                                Alan Kilborn @Ekopalypse
                                last edited by

                                @Ekopalypse

                                This is an interesting approach to remembering toggle_rotating_direction from run to run of the script. Side note: IMO the name of that variable could have been better.

                                If I were writing that script, I might have remembered only the variable in the notepad variable (i.e., notepad.toggle_rotating_direction = …) instead of the function.

                                Your way has the advantage that you’ve added a general purpose function (notepad.rotateSplitView()) that could also easily be called by other scripts.

                                EkopalypseE 1 Reply Last reply Reply Quote 1
                                • EkopalypseE
                                  Ekopalypse @Alan Kilborn
                                  last edited by

                                  @Alan-Kilborn said in Shortcut or menu path to "Rotate to right/left":

                                  IMO the name of that variable could have been better

                                  Sure, but I’m constantly struggling to name things.
                                  What would be your choice? change_rotating_direction?

                                  …

                                  I had the same thoughts and decided to use a function because it can easily be modified to use a parameter to get either a split landscape or portrait view.

                                  1 Reply Last reply Reply Quote 0
                                  • Alan KilbornA
                                    Alan Kilborn
                                    last edited by

                                    IMO the name of that variable could have been better

                                    The reason toggle_rotating_direction isn’t good IMO is because, every time the code runs a toggle happens, and not just when toggle_rotating_direction is true.

                                    what would be your choice?

                                    Hmm, for this one, since it is a boolean, I might go with next_rotate_is_to_the_right.

                                    With something like that, true/false makes sense.

                                    Of course, this is all up to individual taste.


                                    I’m constantly struggling to name things.

                                    Yes, it is hard sometimes. Often what I do is to name something zz or zzz and just barrel ahead coding with that. Later, like when I’ve completed the local block of code, a good name surfaces and a rename fixes things.

                                    I find that if I spend the time up front to come up with a great name, the code to be written changes the meaning somewhat and the great name is no longer great. So I end up taking the time to come up with 2 good names instead of just 1. Naming after the fact seems a viable technique, at least for me.

                                    Of course I don’t do this 100% of the time, just when a suitable name doesn’t immediately come to mind.

                                    1 Reply Last reply Reply Quote 1
                                    • deleeleeD
                                      deleelee @Ekopalypse
                                      last edited by

                                      @Ekopalypse said in Shortcut or menu path to "Rotate to right/left":

                                      @deleelee

                                      UPDATED VERSION

                                      from ctypes import wintypes, WinDLL
                                      from Npp import notepad
                                      
                                      class ViewRotator():
                                          def __init__(self):
                                              self.LOWORD_LEFT = 2000
                                              self.LOWORD_RIGHT = 2001
                                              self.WM_COMMAND = 0x111
                                              self.toggle_rotating_direction = False
                                              self.npp_hwnd = WinDLL("user32").FindWindowW(u'Notepad++', None)
                                              self.splitter_hwnd = WinDLL("user32").FindWindowExW(self.npp_hwnd, None, u'splitterContainer', None)
                                              self.send = WinDLL("user32").SendMessageW
                                              self.send.argtypes = [wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
                                              self.send.restype = wintypes.LPARAM  # LRESULT
                                      
                                          def rotate(self):
                                              if not notepad.isSingleView():
                                                  if self.toggle_rotating_direction:
                                                      self.send(self.splitter_hwnd, self.WM_COMMAND, self.LOWORD_RIGHT, 0)
                                                  else:
                                                      self.send(self.splitter_hwnd, self.WM_COMMAND, self.LOWORD_LEFT, 0)
                                                  self.toggle_rotating_direction = not self.toggle_rotating_direction
                                      
                                      
                                      if not hasattr(notepad, "rotateSplitView"):
                                          notepad.rotateSplitView = ViewRotator().rotate
                                      
                                      notepad.rotateSplitView()
                                      

                                      Wowsers!!! That is perfect. Thank you so much.

                                      1 Reply Last reply Reply Quote 1
                                      • First post
                                        Last post
                                      The Community of users of the Notepad++ text editor.
                                      Powered by NodeBB | Contributors