• Login
Community
  • Login

Notepad++ official documentation is online now

Scheduled Pinned Locked Moved Announcements
17 Posts 6 Posters 6.3k 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.
  • A
    Alan Kilborn @Ekopalypse
    last edited by Sep 19, 2019, 8:23 PM

    @Ekopalypse said in Notepad++ official documentation is online now:

    Do you mean adding/removing on the fly aka at runtime?

    I suppose I mean somehow achieving with P.S. a way to customize this (tab bar rclick context) menu, much like the regular (editor window rclick) context menu can be customized. The method would be very different, but the results would be similar.

    Lately I learned that my knowledge about python isn’t that good

    I’m having a hard time believing that.

    E 1 Reply Last reply Sep 19, 2019, 9:47 PM Reply Quote 0
    • E
      Ekopalypse
      last edited by Sep 19, 2019, 8:35 PM

      for testing my notepad wrapper methods I’ve modified the test from the pythonscript plugin

              from threading import Thread
              import queue
              item_count = -1
              def start_monitor(_q):
                  MN_GETHMENU = 0x1E1
                  i = 10
                  tabbar_context_menu_hwnd = 0
                  while i:
                      tabbar_context_menu_hwnd = user32.FindWindowExW(None, None, '#32768', None)
                      log(tabbar_context_menu_hwnd)
                      if tabbar_context_menu_hwnd:
                          break
                      else:
                          time.sleep(.1)
                          i -= 1
      
                  if tabbar_context_menu_hwnd:
                      menu_handle = user32.SendMessageW(tabbar_context_menu_hwnd, MN_GETHMENU, 0, 0)
                      global item_count
                      item_count = user32.GetMenuItemCount(menu_handle)
                      _q.put(item_count)
                      user32.SendMessageW(tabbar_context_menu_hwnd, WM_CLOSE, 0, 0)
      
              q = queue.Queue()
              t = Thread(target=start_monitor, args=(q,))
              t.start()
              self.assertIsNone(notepad.triggerTabbarContextMenu(0,0))
              t.join()
              item_count = q.get()
              self.assertNotEqual(item_count, -1, msg="Can't find popup menu")
              self.assertEqual(item_count, 28, msg=f'Expected 28 menu items but received:{item_count}')
      

      so yes, it is possible to use it as an starting point, I guess.
      Let me think about it.

      I’m having a hard time believing that.

      Then you better don’t check the official python mailing list :-(

      1 Reply Last reply Reply Quote 0
      • E
        Ekopalypse
        last edited by Sep 19, 2019, 8:43 PM

        Just a word of warning - don’t run the current code unmodified as it might
        hang npp.

        1 Reply Last reply Reply Quote 0
        • E
          Ekopalypse @Alan Kilborn
          last edited by Sep 19, 2019, 9:47 PM

          @Alan-Kilborn

          basically you run this once to register your menu items
          and the hook. Then every time you right click the tabbar menu you
          should see your newly created menu items.
          Not sure whether this is the most optimized version but I guess
          a good starting point.

          The critical parts is the notepad.allocateCmdID and passing this value
          to the menuitem wID field.

          MENU_MAPPER holds this id and the function which should get executed.

          from threading import Thread
          import queue
          import time
          
          import ctypes
          from ctypes import wintypes  
          import platform
          
          user32 = ctypes.WinDLL('user32', use_last_error=True)
          MENU_MAPPER = {}
          
          LRESULT = wintypes.LPARAM
          ULONG_PTR = wintypes.LPVOID
          
          WndProcType = ctypes.WINFUNCTYPE(LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
          
          CallWindowProc = user32.CallWindowProcW
          CallWindowProc.restype = LRESULT
          CallWindowProc.argtypes = [WndProcType, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
          
          x86 = platform.architecture()[0] == '32bit'
          SetWindowLong = user32.SetWindowLongW if x86 else user32.SetWindowLongPtrW
          SetWindowLong.restype = WndProcType
          SetWindowLong.argtypes = [wintypes.HWND, wintypes.INT, WndProcType]
          
          CONFIGURATION = None
          
          MFS_ENABLED = 0x0
          MFS_DISABLED = 0x3
          MIIM_STATE = 1
          MIIM_ID = 2
          MIIM_SUBMENU = 4
          MIIM_STRING = 64
          MFT_STRING = 0
          MFS_ENABLED = 0
          
          class MENUITEMINFO(ctypes.Structure):
              ''' Implements the winapi MENUITEMINFO structure'''
              _fields_ = [('cbSize', wintypes.UINT),
                          ('fMask',  wintypes.UINT),
                          ('fType',  wintypes.UINT),
                          ('fState',  wintypes.UINT),
                          ('wID',  wintypes.UINT),
                          ('hSubMenu',  wintypes.HMENU),
                          ('hbmpChecked',  wintypes.HBITMAP),
                          ('hbmpUnchecked', wintypes.HBITMAP),
                          ('dwItemData',  ULONG_PTR),
                          ('dwTypeData',  wintypes.LPWSTR),
                          ('cch',  wintypes.UINT),
                          ('hbmpItem',  wintypes.HBITMAP),]
          
          
          class Hook():
          
              def __init__(self):
                  self.nppHandle = user32.FindWindowW(u'Notepad++', None)
          
              def register(self):
                  self.newWndProc = WndProcType(self.MyWndProc)
                  self.prevWndProc = SetWindowLong(self.nppHandle, -4, self.newWndProc)
          
              def MyWndProc(self, hWnd, msg, wParam, lParam):
                  if msg == 273 and wParam in MENU_MAPPER:
                      MENU_MAPPER[wParam]()
          
                  # hand control back to npp
                  return CallWindowProc(self.prevWndProc, hWnd, msg, wParam, lParam)
          
          def modify_context_menu():
              i = 10
              tabbar_context_menu_hwnd = 0
              while i:
                  tabbar_context_menu_hwnd = user32.FindWindowExW(None, None, u'#32768', None)
                  if tabbar_context_menu_hwnd:
                      menu_handle = user32.SendMessageW(tabbar_context_menu_hwnd, MN_GETHMENU, 0, 0)
          
                      mii = MENUITEMINFO()
                      mii.cbSize = ctypes.sizeof(mii)
                      mii.fMask = MIIM_ID | MIIM_STRING
                      mii.fType = MFT_STRING
                      mii.fState = MFS_ENABLED
          
                      startid = notepad.allocateCmdID(2)
          
                      mii.wID = startid
                      mii.dwTypeData = "TEST1"
                      user32.InsertMenuItemW(menu_handle, 0, True, ctypes.byref(mii))
                      MENU_MAPPER[startid] = lambda: console.write('TEST1 clicked\n')
          
                      mii.wID = startid+1
                      mii.dwTypeData = "TEST2"
                      user32.InsertMenuItemW(menu_handle, 0, True, ctypes.byref(mii))
                      MENU_MAPPER[startid+1] = lambda: console.write('TEST2 clicked\n')
          
                      break
          
                  else:
                      time.sleep(.1)
                      i -= 1
          
          MN_GETHMENU = 0x1E1
          t = Thread(target=modify_context_menu)
          t.start()
          notepad.triggerTabbarContextMenu(0,6)
          t.join()
          hook = Hook()
          hook.register()
          print('done')
          
          A 1 Reply Last reply Sep 20, 2019, 6:10 PM Reply Quote 1
          • A
            Alan Kilborn @Ekopalypse
            last edited by Sep 20, 2019, 6:10 PM

            @Ekopalypse

            So, it does work to achieve the goal, but 2 immediate things were noticed.

            1. import queue fails because there is no module queue in the Python that ships with Pythonscript. However, this isn’t a real problem, because the code doesn’t even use queue. Easy enough to remove the line. Done.

            2. A bigger problem; when exiting Notepad++ after this “new feature” has been added, I get this, every time:

            8e787af3-3493-427e-8d7a-ac34248faafa-image.png

            E 1 Reply Last reply Sep 20, 2019, 6:35 PM Reply Quote 2
            • E
              Ekopalypse @Alan Kilborn
              last edited by Ekopalypse Sep 20, 2019, 6:37 PM Sep 20, 2019, 6:35 PM

              @Alan-Kilborn

              Do you already have another HOOK registered?
              Which Npp version and which PS version are you currently running?

              Right, in python3 it is called queue and in python2 it is called Queue.
              Removed the code but forgot to remove the import.

              This part is critical if it is already used but maybe slightly different defined.

              LRESULT = wintypes.LPARAM
              ULONG_PTR = wintypes.LPVOID
              
              WndProcType = ctypes.WINFUNCTYPE(LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
              
              CallWindowProc = user32.CallWindowProcW
              CallWindowProc.restype = LRESULT
              CallWindowProc.argtypes = [WndProcType, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
              
              x86 = platform.architecture()[0] == '32bit'
              SetWindowLong = user32.SetWindowLongW if x86 else user32.SetWindowLongPtrW
              SetWindowLong.restype = WndProcType
              SetWindowLong.argtypes = [wintypes.HWND, wintypes.INT, WndProcType]
              
              A 1 Reply Last reply Sep 20, 2019, 8:06 PM Reply Quote 1
              • A
                Alan Kilborn @Ekopalypse
                last edited by Sep 20, 2019, 8:06 PM

                @Ekopalypse said in Notepad++ official documentation is online now:

                Do you already have another HOOK registered?

                Very likely. Is this a problem?

                Which Npp version and which PS version are you currently running?

                7.7.1 32-bit PS1.3.0.0

                PS1.4.0.0 has a trivial amount of difference from 1.3.0.0 so that’s why.

                This part is critical if it is already used but maybe slightly different defined.

                This I don’t understand.

                Are you glad I got you involved in this? :)

                E 2 Replies Last reply Sep 20, 2019, 8:18 PM Reply Quote 1
                • E
                  Ekopalypse @Alan Kilborn
                  last edited by Sep 20, 2019, 8:18 PM

                  @Alan-Kilborn

                  Are you glad I got you involved in this? :)

                  Yes of course :-D, and actually I do something similar with python3script while handling the
                  menu items (in PS when you create a script it appears in the script menu - I do the same) so it is important for me as well.

                  If you hook the message queue you need to ensure that data passed and provided do not get mangled in between
                  because at then end npp needs to handle the messages as well.

                  I actually never tested whether this codes work with 32bit and because of the following

                  LRESULT = wintypes.LPARAM
                  CallWindowProc.restype = LRESULT
                  
                  
                  x86 = platform.architecture()[0] == '32bit'
                  SetWindowLong = user32.SetWindowLongW if x86 else user32.SetWindowLongPtrW
                  

                  it might be that I’m doing the wrong thing.
                  Let me check with a npp 32bit version and msdn.

                  1 Reply Last reply Reply Quote 0
                  • E
                    Ekopalypse @Alan Kilborn
                    last edited by Sep 20, 2019, 8:36 PM

                    @Alan-Kilborn

                    seems my code is correct and works on npp and PS 32bit version.
                    According to MSDN SetWindowLongPtrW is for x64 and SetWindowLongW for 32bit.
                    WindowProc callback function returns LRESULT which is defined as typedef LONG_PTR LRESULT;
                    the same as LPARAM (wich is also typedef LONG_PTR LPARAM;)
                    LONG_PTR is defined to be int64 on 64bit and long on 32bit

                    #if defined(_WIN64)
                     typedef __int64 LONG_PTR; 
                    #else
                     typedef long LONG_PTR;
                    #endif
                    

                    So I don’t see a problem with my code what means that one of your other hook(s),
                    defines something slightly different and corrupts the message handling, I guess.

                    1 Reply Last reply Reply Quote 1
                    • A
                      Alan Kilborn
                      last edited by Sep 23, 2019, 1:32 PM

                      @Ekopalypse said in Notepad++ official documentation is online now:

                      one of your other hook(s),
                      defines something slightly different and corrupts the message handling

                      Okay, I will go examine those others and use your way of doing it as the definitive way it should be done.

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