Community
    • Login

    Editor right-click, outside current text selection, lose this selection

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    35 Posts 6 Posters 10.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.
    • frying-panF
      frying-pan
      last edited by

      I wonder if explaining why it is very useful to me is needed.

      first example : (I copy a lot between different tabs and different applications)
      Some text is selected in a notepad++ tab, or any application window, that I need to copy repeatedly.
      I activate this tab or window with ctrl+tab/alt+tab, right-click anywhere and ‘copy’,
      go back to the other app and right-click anywhere and ‘paste’.
      So, I do not have to point every time the selection or the caret for copying/pasting.

      second example :
      I select some text in a notepad++ tab with the mouse, and immediatly right-click to copy it.
      If I have moved just a pixel farther than the selection, then I lose the selection and can not copy it.

      Added benefit : Since I can right-click anywhere, I do not have the context-menu appearing above the selection,
      so I can check my selection is good…

      indeed Microsoft Visual Studio (when editing Microsoft Word macro) is one of the infamous selection-looser
      Internet Explorer 11 also…

      ===

      Here is the improved code

      • code has been hardened against errors
      • the hook still can not be removed (*see below the code why),
        but now can be “de-activated”/“re-activated” by re-running the script
      • now hooks all scintilla windows (so incuding the notepad++ second view when splitted)
      • added : SHIFT + double left mouse click -> select text from click point until next space/space-like chars

      (sorry the two functions SHIFT+double-left-click and right-click can not be “de-activated” separately,
      I think a dialog prompt and two separate variables set by console.editor.SetProperty would do that)

      # PythonScript which hooks mouse clicks (WndProc HOOK on Scintilla windows)
      # * SHIFT + double left mouse click : select text from click point until next space/space-like chars : \s
      # * right mouse click : prevent right-click from moving the caret and losing current text selection
      # re-run the script to de-activate/re_activate the hook
      # Tested on NPP 64 bits (NOT tested on 32 Bits but could be compatible), /!\ this file uses TABS for indent
      
      from Npp import *
      
      import platform
      import ctypes
      from ctypes import wintypes
      import datetime
      from datetime import datetime
      
      s_script_name			= "Perso_ScintWndProc_Hook"
      s_hook_name				= "Scint_Mouse_Click_Hook"
      s_editorprop_regdone	= "SCINTWNDPROCHOOK_DONE"
      s_editorprop_active		= "SCINTWNDPROCHOOK_ACTIVE"
      s_true					= "TRUE"
      
      GWL_WNDPROC = -4				# used to set a new address for the windows procedure
      I_VK_SHIFT		= 0x10			# SHIFT virtual key code
      I_VK_CONTROL	= 0x11			# CONTROL virtual key code
      I_VK_ALT		= 0x12			# ALT virtual key code
      KSTATE_ISDOWN = 0x8000			# key pressed
      WM_LBUTTONDBLCLK	= 0x0203	# double left mouse click window message
      WM_RBUTTONDOWN		= 0x0204	# right mouse click window message
      
      LRESULT = wintypes.LPARAM
      WndProcType = ctypes.WINFUNCTYPE(LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
      
      # window message hook functions
      CallWindowProc = ctypes.windll.user32.CallWindowProcW
      CallWindowProc.restype = LRESULT
      CallWindowProc.argtypes = [WndProcType, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
      
      x86 = platform.architecture()[0] == "32bit"
      if x86:
      	SetWindowLong = ctypes.windll.user32.SetWindowLongW
      else:
      	SetWindowLong = ctypes.windll.user32.SetWindowLongPtrW
      SetWindowLong.restype = WndProcType
      SetWindowLong.argtypes = [wintypes.HWND, wintypes.INT, WndProcType]
      # end of window message hook functions
      
      EnumWindowsProc				= ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
      EnumWindows					= ctypes.windll.user32.EnumWindows
      EnumChildWindows			= ctypes.windll.user32.EnumChildWindows
      RealGetWindowClass			= ctypes.windll.user32.RealGetWindowClassW
      GetWindowThreadProcessId	= ctypes.windll.user32.GetWindowThreadProcessId
      GetCurrentProcessId			= ctypes.windll.kernel32.GetCurrentProcessId
      GetLastError				= ctypes.windll.kernel32.GetLastError
      SetLastError				= ctypes.windll.kernel32.SetLastError
      
      GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState # used to check if a modifier key is pressed
      
      class C_Scint_Mouse_Click_Hook(): # hook class
      	# class constructor
      	def __init__(self):
      		def Enum_Window_Hwnd(hwnd, lParam):
      			arr_enum_window_hwnd.append(hwnd)
      			return True
      
      		s_npp_class		= u"Notepad++"
      		s_scint_class	= u"Scintilla"
      
      		self.arr_scint_hwnd = []
      		self.npp_win_hwnd = 0
      
      		i_cur_pid = GetCurrentProcessId()
      
      		arr_enum_window_hwnd = []
      		EnumWindows(EnumWindowsProc(Enum_Window_Hwnd), 0)
      		# enum all class="Notepad++" NPP window handles
      		buff = ctypes.create_unicode_buffer(len(s_npp_class) + 1 + 1)
      		for win_hwnd in arr_enum_window_hwnd:
      			RealGetWindowClass(win_hwnd, buff, len(s_npp_class) + 1 + 1)
      			if buff.value == s_npp_class:
      				# check that found NPP window handle is owned by current process, stop at the first found
      				ci_win_pid = wintypes.DWORD(0)
      				GetWindowThreadProcessId(win_hwnd, ctypes.pointer(ci_win_pid))
      				if i_cur_pid == ci_win_pid.value:
      					self.npp_win_hwnd = win_hwnd
      					break
      		if self.npp_win_hwnd == 0:
      			return
      
      		del arr_enum_window_hwnd[:]
      		EnumChildWindows(self.npp_win_hwnd, EnumWindowsProc(Enum_Window_Hwnd), 0)
      		# enum all class="Scintilla" window handles, childs of NPP window
      		# get all of them in arr_scint_hwnd (at least all existing ones at that time)
      		buff = ctypes.create_unicode_buffer(len(s_scint_class) + 1 + 1)
      		for win_hwnd in arr_enum_window_hwnd:
      			RealGetWindowClass(win_hwnd, buff, len(s_scint_class) + 1 + 1)
      			if buff.value == s_scint_class:
      				self.arr_scint_hwnd.append(win_hwnd)
      
      	def RegHook(self):															# function to register the hook on all found Scintilla windows
      		s_npp_class		= u"Notepad++"
      		s_scint_class	= u"Scintilla"
      
      		if (self.npp_win_hwnd == 0 or len(self.arr_scint_hwnd) == 0):			# abort if no NPP handle or no Scintilla handle
      			return
      
      		self.NewWndProc = WndProcType(self.MyWndProc)							# get the address of our own WndProc
      
      		self.arr_scint_oldwndproc = []
      		s_list = "\t" + "Found " + str(len(self.arr_scint_hwnd)) + " " + s_scint_class + " Handle / WindowProc"
      		for i in range(0, len(self.arr_scint_hwnd)):
      			win_hwnd = self.arr_scint_hwnd[i]
      			SetLastError(0)
      			oldwndproc = SetWindowLong(win_hwnd, GWL_WNDPROC, self.NewWndProc)	# register and store oldwndproc addresses in arr_scint_oldwndproc
      			i_apierr = GetLastError()
      			if i_apierr == 0:
      				self.arr_scint_oldwndproc.append(oldwndproc)
      				s_list = s_list + "\n" + "\t\t" + hex(win_hwnd) + " / " + str(oldwndproc)
      			else:
      				del self.arr_scint_hwnd[i]
      				s_list = s_list + "\n" + "\t\t" + hex(win_hwnd) + " / " + "NO WindowProc, NOT hooked"
      		print "\t" + s_npp_class + " Handle = " + hex(self.npp_win_hwnd) + "\n" + s_list
      
      	def MyWndProc(self, hWnd, msg, wParam, lParam):							# our own WndProc function receives windows messages
      		oldwndproc = 0
      		for i in range(0, len(self.arr_scint_hwnd)):
      			if self.arr_scint_hwnd[i] == hWnd:								# target hWnd found at index i in arr_scint_hwnd
      				oldwndproc = self.arr_scint_oldwndproc[i]					# corresponding oldwndproc is picked in arr_scint_oldwndproc
      				break
      		if oldwndproc == 0:													# fatal error ! should not happen...
      			print "\t" + s_hook_name + " Fatal error ! oldwndproc = 0"
      			notepad.messageBox(s_hook_name + " Fatal error ! oldwndproc = 0", s_script_name)
      			return 0
      
      		if (msg != WM_LBUTTONDBLCLK and msg != WM_RBUTTONDOWN):				# if NOT mouse hooked messages : abort
      			return CallWindowProc(oldwndproc, hWnd, msg, wParam, lParam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
      		if console.editor.getProperty(s_editorprop_active) != s_true:		# if hook de-activated : abort
      			return CallWindowProc(oldwndproc, hWnd, msg, wParam, lParam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
      
      		b_shift_down	= ((GetAsyncKeyState(I_VK_SHIFT)	& KSTATE_ISDOWN) == KSTATE_ISDOWN)
      		b_ctrl_down		= ((GetAsyncKeyState(I_VK_CONTROL)	& KSTATE_ISDOWN) == KSTATE_ISDOWN)
      		b_alt_down		= ((GetAsyncKeyState(I_VK_ALT)		& KSTATE_ISDOWN) == KSTATE_ISDOWN)
      		if (b_shift_down and b_ctrl_down and b_alt_down):					# if SHIFT and CONTROL and ALT down : unregister
      			self.UnRegHook()
      			return CallWindowProc(oldwndproc, hWnd, msg, wParam, lParam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
      
      		if (msg == WM_LBUTTONDBLCLK and b_shift_down and not b_ctrl_down and not b_alt_down):
      			self.ExtendSelToSpaceLikeChars()								# extend selection from the clicked point to the space-like chars
      		elif (msg == WM_RBUTTONDOWN and not b_shift_down and not b_ctrl_down and not b_alt_down):
      			dummy = 0														# do nothing
      		else:
      			return CallWindowProc(oldwndproc, hWnd, msg, wParam, lParam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
      
      		#res = "DEBUG " + s_hook_name
      		#res = res + " Message = " + hex(msg) + " to hWnd = " + hex(hWnd)
      		#res = res + " Forwarded To oldwndproc = " + str(oldwndproc)
      		#print res + " At " + str(datetime.now())
      		return CallWindowProc(oldwndproc, hWnd, 0, 0, 0)					# nullify the mouse hooked messages
      
      	def ExtendSelToSpaceLikeChars(self):
      		# space-like chars are space/tab/carriage return/line feed/form feed... [\t\n\x0B\f\r\x20\x85\xA0\x{2028}\x{2029}]
      		editor.searchAnchor()
      		i_start = editor.searchPrev(FINDOPTION.REGEXP, "\s")
      		i_start = i_start + 1
      		if i_start < 0:
      			i_start = 0
      		i_end = editor.searchNext(FINDOPTION.REGEXP, "\s")
      		if i_end < 0:
      			i_end = editor.getTextLength()
      		editor.setSel(i_start, i_end)
      
      print "[" + s_script_name + " starts]"
      if console.editor.getProperty(s_editorprop_regdone) != s_true:
      	console.editor.setProperty(s_editorprop_regdone, s_true)
      	console.editor.setProperty(s_editorprop_active, s_true)
      	o_scint_mouse_click_hook = C_Scint_Mouse_Click_Hook()	# create an instance of hook class
      	o_scint_mouse_click_hook.RegHook()						# set up the mouse hook
      	print "\t" + s_hook_name + " registered and ACTIVATED (re-run script to toggle the hook)"
      else:
      	if console.editor.getProperty(s_editorprop_active) != s_true:
      		console.editor.setProperty(s_editorprop_active, s_true)
      		print "\t" + s_hook_name + " RE-ACTIVATED (re-run script to toggle the hook)"
      		notepad.messageBox(s_hook_name + " RE-ACTIVATED\n(re-run script to toggle the hook)", s_script_name)
      	else:
      		console.editor.setProperty(s_editorprop_active, "")
      		print "\t" + s_hook_name + " de-activated (re-run script to toggle the hook)"
      		notepad.messageBox(s_hook_name + " de-activated\n(re-run script to toggle the hook)", s_script_name)
      
      

      *Breaking the hook chain :
      If a hook is set by

      self.NewWndProc = WndProcType(self.MyWndProc)
      oldwndproc = SetWindowLong(win_hwnd, GWL_WNDPROC, self.NewWndProc)
      

      and then removed by

      SetWindowLong(win_hwnd, GWL_WNDPROC, oldwndproc)
      

      that breaks the hook chain if there is more than 1 hook.

      original WndProc funcA > hooked by MyWndProc funcB > hooked by MyOtherWndProc funcC
      if funcB hook is removed, than funcC will not receive any message after that, and will not be aware of it

      Some more sophisticated hook api could solve that problem but I don’t know them.
      I chose the easiest way, which is to “de-activate” my hook by just telling him to do nothing,
      except silently forwarding the window message to the the next WndProc (oldwndproc).
      This way my “de-activated” hook does nothing but does not break the hook chain.

      Alan KilbornA frying-panF 2 Replies Last reply Reply Quote 2
      • Alan KilbornA
        Alan Kilborn @frying-pan
        last edited by

        @frying-pan said in Editor right-click, outside current text selection, lose this selection:

        I wonder if explaining why it is very useful to me is needed.

        Not needed, but often I give people that at first seem to want odd things a “hard time” about it, and it draws them out into explaining/justifying their need.

        Future readers that come along then get the whole story, and maybe don’t dismiss something that could really be of use.

        I think you have done a good job of that, as well as solving your own problem with the script. Nicely done!

        frying-panF 1 Reply Last reply Reply Quote 1
        • frying-panF
          frying-pan @Alan Kilborn
          last edited by frying-pan

          @Alan-Kilborn

          Well, I would not have made Notepad++ my default text editor, if I couldn’t have solved the right-click question.

          It’s the first reason I bothered to install PythonScript and learn this syntax (while I’m only accustomed to Pascal/VB/VBS/AutoIt)

          And otherwise I use PSPad for its Project features, Notepad++ session and workspace are not as featured.
          PSPad is mostly missing code wrapping (if > endif, etc…)

          1 Reply Last reply Reply Quote 1
          • frying-panF
            frying-pan
            last edited by

            Oops, I forgot to delete 3 lines of old code (at line 141 of the script). But I can’t modify any more the previous post.

            So the code updated without the bug :

            # PythonScript which hooks mouse clicks (WndProc HOOK on Scintilla windows)
            # * SHIFT + double left mouse click : select text from click point until next space/space-like chars : \s
            # * right mouse click : prevent right-click from moving the caret and losing current text selection
            # re-run the script to de-activate/re-activate the hook
            # Tested on NPP 64 bits (NOT tested on 32 Bits but could be compatible), /!\ this file uses TABS for indent
            
            from Npp import *
            
            import platform
            import ctypes
            from ctypes import wintypes
            import datetime
            from datetime import datetime
            
            s_script_name			= "Perso_ScintWndProc_Hook"
            s_hook_name				= "Scint_Mouse_Click_Hook"
            s_editorprop_regdone	= "SCINTWNDPROCHOOK_DONE"
            s_editorprop_active		= "SCINTWNDPROCHOOK_ACTIVE"
            s_true					= "TRUE"
            
            GWL_WNDPROC = -4				# used to set a new address for the windows procedure
            I_VK_SHIFT		= 0x10			# SHIFT virtual key code
            I_VK_CONTROL	= 0x11			# CONTROL virtual key code
            I_VK_ALT		= 0x12			# ALT virtual key code
            KSTATE_ISDOWN = 0x8000			# key pressed
            WM_LBUTTONDBLCLK	= 0x0203	# double left mouse click window message
            WM_RBUTTONDOWN		= 0x0204	# right mouse click window message
            
            LRESULT = wintypes.LPARAM
            WndProcType = ctypes.WINFUNCTYPE(LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
            
            # window message hook functions
            CallWindowProc = ctypes.windll.user32.CallWindowProcW
            CallWindowProc.restype = LRESULT
            CallWindowProc.argtypes = [WndProcType, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
            
            x86 = platform.architecture()[0] == "32bit"
            if x86:
            	SetWindowLong = ctypes.windll.user32.SetWindowLongW
            else:
            	SetWindowLong = ctypes.windll.user32.SetWindowLongPtrW
            SetWindowLong.restype = WndProcType
            SetWindowLong.argtypes = [wintypes.HWND, wintypes.INT, WndProcType]
            # end of window message hook functions
            
            EnumWindowsProc				= ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
            EnumWindows					= ctypes.windll.user32.EnumWindows
            EnumChildWindows			= ctypes.windll.user32.EnumChildWindows
            RealGetWindowClass			= ctypes.windll.user32.RealGetWindowClassW
            GetWindowThreadProcessId	= ctypes.windll.user32.GetWindowThreadProcessId
            GetCurrentProcessId			= ctypes.windll.kernel32.GetCurrentProcessId
            GetLastError				= ctypes.windll.kernel32.GetLastError
            SetLastError				= ctypes.windll.kernel32.SetLastError
            
            GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState # used to check if a modifier key is pressed
            
            class C_Scint_Mouse_Click_Hook(): # hook class
            	# class constructor
            	def __init__(self):
            		def Enum_Window_Hwnd(hwnd, lParam):
            			arr_enum_window_hwnd.append(hwnd)
            			return True
            
            		s_npp_class		= u"Notepad++"
            		s_scint_class	= u"Scintilla"
            
            		self.arr_scint_hwnd = []
            		self.npp_win_hwnd = 0
            
            		i_cur_pid = GetCurrentProcessId()
            
            		arr_enum_window_hwnd = []
            		EnumWindows(EnumWindowsProc(Enum_Window_Hwnd), 0)
            		# enum all class="Notepad++" NPP window handles
            		buff = ctypes.create_unicode_buffer(len(s_npp_class) + 1 + 1)
            		for win_hwnd in arr_enum_window_hwnd:
            			RealGetWindowClass(win_hwnd, buff, len(s_npp_class) + 1 + 1)
            			if buff.value == s_npp_class:
            				# check that found NPP window handle is owned by current process, stop at the first found
            				ci_win_pid = wintypes.DWORD(0)
            				GetWindowThreadProcessId(win_hwnd, ctypes.pointer(ci_win_pid))
            				if i_cur_pid == ci_win_pid.value:
            					self.npp_win_hwnd = win_hwnd
            					break
            		if self.npp_win_hwnd == 0:
            			return
            
            		del arr_enum_window_hwnd[:]
            		EnumChildWindows(self.npp_win_hwnd, EnumWindowsProc(Enum_Window_Hwnd), 0)
            		# enum all class="Scintilla" window handles, childs of NPP window
            		# get all of them in arr_scint_hwnd (at least all existing ones at that time)
            		buff = ctypes.create_unicode_buffer(len(s_scint_class) + 1 + 1)
            		for win_hwnd in arr_enum_window_hwnd:
            			RealGetWindowClass(win_hwnd, buff, len(s_scint_class) + 1 + 1)
            			if buff.value == s_scint_class:
            				self.arr_scint_hwnd.append(win_hwnd)
            
            	def RegHook(self):															# function to register the hook on all found Scintilla windows
            		s_npp_class		= u"Notepad++"
            		s_scint_class	= u"Scintilla"
            
            		if (self.npp_win_hwnd == 0 or len(self.arr_scint_hwnd) == 0):			# abort if no NPP handle or no Scintilla handle
            			return
            
            		self.NewWndProc = WndProcType(self.MyWndProc)							# get the address of our own WndProc
            
            		self.arr_scint_oldwndproc = []
            		s_list = "\t" + "Found " + str(len(self.arr_scint_hwnd)) + " " + s_scint_class + " Handle / WindowProc"
            		for i in range(0, len(self.arr_scint_hwnd)):
            			win_hwnd = self.arr_scint_hwnd[i]
            			SetLastError(0)
            			oldwndproc = SetWindowLong(win_hwnd, GWL_WNDPROC, self.NewWndProc)	# register and store oldwndproc addresses in arr_scint_oldwndproc
            			i_apierr = GetLastError()
            			if i_apierr == 0:
            				self.arr_scint_oldwndproc.append(oldwndproc)
            				s_list = s_list + "\n" + "\t\t" + hex(win_hwnd) + " / " + str(oldwndproc)
            			else:
            				del self.arr_scint_hwnd[i]
            				s_list = s_list + "\n" + "\t\t" + hex(win_hwnd) + " / " + "NO WindowProc, NOT hooked"
            		print "\t" + s_npp_class + " Handle = " + hex(self.npp_win_hwnd) + "\n" + s_list
            
            	def MyWndProc(self, hWnd, msg, wParam, lParam):							# our own WndProc function receives windows messages
            		oldwndproc = 0
            		for i in range(0, len(self.arr_scint_hwnd)):
            			if self.arr_scint_hwnd[i] == hWnd:								# target hWnd found at index i in arr_scint_hwnd
            				oldwndproc = self.arr_scint_oldwndproc[i]					# corresponding oldwndproc is picked in arr_scint_oldwndproc
            				break
            		if oldwndproc == 0:													# fatal error ! should not happen...
            			print "\t" + s_hook_name + " Fatal error ! oldwndproc = 0"
            			notepad.messageBox(s_hook_name + " Fatal error ! oldwndproc = 0", s_script_name)
            			return 0
            
            		if (msg != WM_LBUTTONDBLCLK and msg != WM_RBUTTONDOWN):				# if NOT mouse hooked messages : abort
            			return CallWindowProc(oldwndproc, hWnd, msg, wParam, lParam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
            		if console.editor.getProperty(s_editorprop_active) != s_true:		# if hook de-activated : abort
            			return CallWindowProc(oldwndproc, hWnd, msg, wParam, lParam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
            
            		b_shift_down	= ((GetAsyncKeyState(I_VK_SHIFT)	& KSTATE_ISDOWN) == KSTATE_ISDOWN)
            		b_ctrl_down		= ((GetAsyncKeyState(I_VK_CONTROL)	& KSTATE_ISDOWN) == KSTATE_ISDOWN)
            		b_alt_down		= ((GetAsyncKeyState(I_VK_ALT)		& KSTATE_ISDOWN) == KSTATE_ISDOWN)
            
            		if (msg == WM_LBUTTONDBLCLK and b_shift_down and not(b_ctrl_down) and not(b_alt_down)):
            			self.ExtendSelToSpaceLikeChars()								# extend selection from the clicked point to the space-like chars
            		elif (msg == WM_RBUTTONDOWN and not(b_shift_down) and not(b_ctrl_down) and not(b_alt_down)):
            			dummy = 0														# do nothing
            		else:
            			return CallWindowProc(oldwndproc, hWnd, msg, wParam, lParam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
            
            		#res = "DEBUG " + s_hook_name
            		#res = res + " Message = " + hex(msg) + " to hWnd = " + hex(hWnd)
            		#res = res + " Forwarded To oldwndproc = " + str(oldwndproc)
            		#print res + " At " + str(datetime.now())
            		return CallWindowProc(oldwndproc, hWnd, 0, 0, 0)					# nullify the mouse hooked messages
            
            	def ExtendSelToSpaceLikeChars(self):
            		# space-like chars are space/tab/carriage return/line feed/form feed... [\t\n\x0B\f\r\x20\x85\xA0\x{2028}\x{2029}]
            		editor.searchAnchor()
            		i_start = editor.searchPrev(FINDOPTION.REGEXP, "\s")
            		i_start = i_start + 1
            		if i_start < 0:
            			i_start = 0
            		i_end = editor.searchNext(FINDOPTION.REGEXP, "\s")
            		if i_end < 0:
            			i_end = editor.getTextLength()
            		editor.setSel(i_start, i_end)
            
            print "[" + s_script_name + " starts]"
            if console.editor.getProperty(s_editorprop_regdone) != s_true:
            	console.editor.setProperty(s_editorprop_regdone, s_true)
            	console.editor.setProperty(s_editorprop_active, s_true)
            	o_scint_mouse_click_hook = C_Scint_Mouse_Click_Hook()	# create an instance of hook class
            	o_scint_mouse_click_hook.RegHook()						# set up the mouse hook
            	print "\t" + s_hook_name + " registered and ACTIVATED (re-run script to toggle the hook)"
            else:
            	if console.editor.getProperty(s_editorprop_active) != s_true:
            		console.editor.setProperty(s_editorprop_active, s_true)
            		print "\t" + s_hook_name + " RE-ACTIVATED (re-run script to toggle the hook)"
            		notepad.messageBox(s_hook_name + " RE-ACTIVATED\n(re-run script to toggle the hook)", s_script_name)
            	else:
            		console.editor.setProperty(s_editorprop_active, "")
            		print "\t" + s_hook_name + " de-activated (re-run script to toggle the hook)"
            		notepad.messageBox(s_hook_name + " de-activated\n(re-run script to toggle the hook)", s_script_name)
            
            
            1 Reply Last reply Reply Quote 0
            • frying-panF
              frying-pan @frying-pan
              last edited by frying-pan

              This post is deleted!
              1 Reply Last reply Reply Quote 0
              • frying-panF
                frying-pan
                last edited by

                Code update, probably the last for this script, since I have all the keyboard + mouse shortcuts I need.

                • corrected one bug :
                  (in case of a Scintilla window hook failure, the list of hooked WndProcs was incorrectly shortened and enumerated)
                • cleaned the code
                • added more keyboard + mouse shortcuts :
                  • SHIFT + double-left-click : select from clicked point the whole variable name : alphanumeric with _ and . (dot)
                  • CTRL + SHIFT + double-left-click : select from clicked point the whole bracket content : () [] {}, from left in case of mismatch
                  • ALT + SHIFT + double-left-click : select from clicked point the whole quote content : “” ‘’, from left in case of mismatch
                  • ALT + right-click : select from clicked point until space/space-like characters are met : space/tab/cr/lf/formfeed/vtab etc…
                  • right-click : prevent right-click from moving the caret and losing current text selection
                • added options to disable each shortcut separately (these options values must be edited in the script file)

                note : my code rendering would be better if I could set up the real tabs to appear as 4 chars wide on this forum
                (I prefer to have 4 chars wide tabs in Notepad++ but this forum render them as 8 chars wide)

                note: since the code is too long to be loaded in one post, it will posted in two chunks.

                Code chunk 1/2:

                # PythonScript which hooks mouse clicks (WndProc HOOK on Scintilla windows)
                # Tested on Notepad++ 64 bits (NOT tested on 32 Bits but could be compatible) /!\ this file uses TABS for indent /!\
                	# SHIFT + double-left-click			: select from clicked point the whole variable name : alphanumeric with _ and . (dot)
                	# CTRL + SHIFT + double-left-click	: select from clicked point the whole bracket content : () [] {}, from left in case of mismatch
                	# ALT + SHIFT + double-left-click	: select from clicked point the whole quote content : "" '', from left in case of mismatch
                	# ALT + right-click					: select from clicked point until space/space-like characters are met : space/tab/cr/lf/formfeed/vtab etc...
                	# right-click						: prevent right-click from moving the caret and losing current text selection
                # re-run the script to de-activate/re-activate the whole hook (re-running the script will also apply new *options values*)
                # change the *options values* (just below) to choose which mouse hooks will be active
                
                # *options values* : set value to INTEGER 1 to have the feature enabled (any OTHER INTEGER value will DISABLE it)
                i_feature_shift_double_left_click			= 1
                i_feature_control_shift_double_left_click	= 1
                i_feature_alt_shift_double_left_click		= 1
                i_feature_alt_right_click					= 1
                i_feature_right_click						= 1
                i_message_box_warnings						= 1	# if disabled, no message box will pop on registering hook error,
                												# or when toggling the hook (console messages will still occur)
                # end of *options values*
                
                from Npp import *
                
                import platform
                import ctypes
                from ctypes import wintypes
                import datetime
                from datetime import datetime
                
                s_script_name	= "Perso_ScintWndProc_Hook"
                s_hook_name		= "Scint_Mouse_Click_Hook"
                
                s_editorprop_regdone		= "SCINTWNDPROC_HOOK_DONE"
                s_editorprop_active			= "SCINTWNDPROC_HOOK_ACTIVE"
                s_editorprop_sdlc_active	= "SCINTWNDPROC_SDLC_ACTIVE"
                s_editorprop_csdlc_active	= "SCINTWNDPROC_CSDLC_ACTIVE"
                s_editorprop_asdlc_active	= "SCINTWNDPROC_ASDLC_ACTIVE"
                s_editorprop_arc_active		= "SCINTWNDPROC_ARC_ACTIVE"
                s_editorprop_rc_active		= "SCINTWNDPROC_RC_ACTIVE"
                s_true						= "TRUE"
                
                s_reruntogglehook	= "Re-run the script to toggle the whole hook"
                s_willapplynewopt	= "This will also apply new *options values* saved in the script file"
                s_feature_sdlc_off	= "* Feature SHIFT + double-left-click is DISABLED by script option"
                s_feature_csdlc_off	= "* Feature CTRL + SHIFT + double-left-click is DISABLED by script option"
                s_feature_asdlc_off	= "* Feature ALT + SHIFT + double-left-click is DISABLED by script option"
                s_feature_arc_off	= "* Feature ALT + right-click is DISABLED by script option"
                s_feature_rc_off	= "* Feature right-click is DISABLED by script option"
                
                GWL_WNDPROC = -4				# used to set a new address for the windows procedure
                I_VK_SHIFT		= 0x10			# SHIFT virtual key code
                I_VK_CONTROL	= 0x11			# CONTROL virtual key code
                I_VK_ALT		= 0x12			# ALT virtual key code
                KSTATE_ISDOWN = 0x8000			# key pressed
                WM_LBUTTONDBLCLK	= 0x0203	# mouse double-left-click window message
                WM_RBUTTONDOWN		= 0x0204	# mouse right-click down window message
                WM_RBUTTONUP		= 0x0205	# mouse right-click up window message
                
                LRESULT = wintypes.LPARAM
                WndProcType = ctypes.WINFUNCTYPE(LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
                
                # window message hook functions
                CallWindowProc = ctypes.windll.user32.CallWindowProcW
                CallWindowProc.restype = LRESULT
                CallWindowProc.argtypes = [WndProcType, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM]
                
                x86 = platform.architecture()[0] == "32bit"
                if x86:
                	SetWindowLong = ctypes.windll.user32.SetWindowLongW
                else:
                	SetWindowLong = ctypes.windll.user32.SetWindowLongPtrW
                SetWindowLong.restype = WndProcType
                SetWindowLong.argtypes = [wintypes.HWND, wintypes.INT, WndProcType]
                # end of window message hook functions
                
                EnumWindowsProc				= ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
                EnumWindows					= ctypes.windll.user32.EnumWindows
                EnumChildWindows			= ctypes.windll.user32.EnumChildWindows
                GetAsyncKeyState			= ctypes.windll.user32.GetAsyncKeyState
                RealGetWindowClass			= ctypes.windll.user32.RealGetWindowClassW
                GetWindowThreadProcessId	= ctypes.windll.user32.GetWindowThreadProcessId
                GetCurrentProcessId			= ctypes.windll.kernel32.GetCurrentProcessId
                SetLastError				= ctypes.windll.kernel32.SetLastError
                GetLastError				= ctypes.windll.kernel32.GetLastError
                
                class C_Scint_Mouse_Click_Hook():
                	# class constructor
                	def __init__(self):
                		self.arr_scint_hwnd = []
                		self.arr_scint_oldwndproc = []
                
                	# function to register the hook on all found Scintilla windows
                	def RegHook(self):
                		def Enum_Window_Hwnd(hwnd, lparam):
                			arr_enum_window_hwnd.append(hwnd)
                			return True
                
                		s_npp_class		= u"Notepad++"
                		s_scint_class	= u"Scintilla"
                
                		i_cur_pid = GetCurrentProcessId()
                		npp_win_hwnd = None
                
                		arr_enum_window_hwnd = []
                		EnumWindows(EnumWindowsProc(Enum_Window_Hwnd), 0)
                		# get all class="Notepad++" top_level NPP windows handle
                		buff = ctypes.create_unicode_buffer(len(s_npp_class) + 1 + 1)
                		for win_hwnd in arr_enum_window_hwnd:
                			RealGetWindowClass(win_hwnd, buff, len(s_npp_class) + 1 + 1)
                			if buff.value == s_npp_class:
                				# check if each NPP window handle is owned by current process, stop at the first found
                				ci_win_pid = wintypes.DWORD(0)
                				GetWindowThreadProcessId(win_hwnd, ctypes.pointer(ci_win_pid))
                				if i_cur_pid == ci_win_pid.value:
                					npp_win_hwnd = win_hwnd
                					break
                		if npp_win_hwnd is None:
                			print "\t" + s_npp_class + " window NOT found ! NO hook !"
                			return False
                		print "\t" + s_npp_class + " Handle = " + hex(npp_win_hwnd)
                
                		del arr_enum_window_hwnd[:]
                		EnumChildWindows(npp_win_hwnd, EnumWindowsProc(Enum_Window_Hwnd), 0)
                		# get all class="Scintilla" windows handle, childs of NPP window, in arr_scint_hwnd
                		# (at least all existing Scintilla windows at that time, should include primary and secondary views)
                		buff = ctypes.create_unicode_buffer(len(s_scint_class) + 1 + 1)
                		for win_hwnd in arr_enum_window_hwnd:
                			RealGetWindowClass(win_hwnd, buff, len(s_scint_class) + 1 + 1)
                			if buff.value == s_scint_class:
                				self.arr_scint_hwnd.append(win_hwnd)
                
                		# get the address of our own WndProc
                		self.newWndProc = WndProcType(self.MyWndProc)
                
                		# register the hook for each window present in arr_scint_hwnd
                		s_list = "\t" + "Found " + str(len(self.arr_scint_hwnd)) + " " + s_scint_class + " Handle / WindowProc"
                		i_index = 0
                		while i_index < len(self.arr_scint_hwnd):
                			win_hwnd = self.arr_scint_hwnd[i_index]
                			# register hook and store oldwndproc addresses in arr_scint_oldwndproc
                			SetLastError(0)
                			oldwndproc = SetWindowLong(win_hwnd, GWL_WNDPROC, self.newWndProc)
                			i_apierr = GetLastError()
                			if i_apierr == 0:
                				self.arr_scint_oldwndproc.append(oldwndproc)
                				s_list = s_list + "\n" + "\t\t" + hex(win_hwnd) + " / " + str(oldwndproc)
                				i_index = i_index + 1
                			else:
                				del self.arr_scint_hwnd[i_index]
                				s_list = s_list + "\n" + "\t\t" + hex(win_hwnd) + " / " + "NO WindowProc ! NOT hooked !"
                		print s_list
                		if len(self.arr_scint_hwnd) == 0:
                			print "\t" + s_scint_class + " window(s) or WindowProc(s) NOT found ! NO hook !"
                			return False
                		return True
                
                	def MyWndProc(self, hwnd, msg, wparam, lparam):							# our own WndProc function receives windows messages
                		oldwndproc = None
                		for i in range(0, len(self.arr_scint_hwnd)):
                			if self.arr_scint_hwnd[i] == hwnd:								# target hwnd found at index i in arr_scint_hwnd
                				oldwndproc = self.arr_scint_oldwndproc[i]					# corresponding oldwndproc is picked in arr_scint_oldwndproc
                				break
                		if oldwndproc is None:												# fatal error ! should not happen...
                			print "\t" + s_hook_name + " Fatal error ! Hooked WndProc NOT found !"
                			notepad.messageBox(s_hook_name + " Fatal error ! Hooked WndProc NOT found !", s_script_name, MESSAGEBOXFLAGS.ICONEXCLAMATION)
                			return 0
                
                		if (msg != WM_LBUTTONDBLCLK and msg != WM_RBUTTONDOWN and msg != WM_RBUTTONUP): # if NOT in mouse hooked messages : abort
                			return CallWindowProc(oldwndproc, hwnd, msg, wparam, lparam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
                		if console.editor.getProperty(s_editorprop_active) != s_true:		# if hook de-activated : abort
                			return CallWindowProc(oldwndproc, hwnd, msg, wparam, lparam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
                
                		b_shift_down	= ((GetAsyncKeyState(I_VK_SHIFT)	& KSTATE_ISDOWN) == KSTATE_ISDOWN)
                		b_ctrl_down		= ((GetAsyncKeyState(I_VK_CONTROL)	& KSTATE_ISDOWN) == KSTATE_ISDOWN)
                		b_alt_down		= ((GetAsyncKeyState(I_VK_ALT)		& KSTATE_ISDOWN) == KSTATE_ISDOWN)
                
                		if (console.editor.getProperty(s_editorprop_sdlc_active) == "1" and \
                				msg == WM_LBUTTONDBLCLK and b_shift_down and not(b_ctrl_down) and not(b_alt_down)):
                			self.ExtendSel_AlphaNumUnderscoreDot()							# select from clicked point the whole variable name
                		if (console.editor.getProperty(s_editorprop_csdlc_active) == "1" and \
                				msg == WM_LBUTTONDBLCLK and b_shift_down and b_ctrl_down and not(b_alt_down)):
                			self.ExtendSel_BracketContent()									# select from clicked point the whole bracket content
                		if (console.editor.getProperty(s_editorprop_asdlc_active) == "1" and \
                				msg == WM_LBUTTONDBLCLK and b_shift_down and not(b_ctrl_down) and b_alt_down):
                			self.ExtendSel_Quotes()											# select from clicked point the whole quote content
                		elif (console.editor.getProperty(s_editorprop_arc_active) == "1" and \
                				msg == WM_RBUTTONUP and not(b_shift_down) and not(b_ctrl_down) and b_alt_down):
                			self.ExtendSel_SpaceSpaceLike()
                		elif (console.editor.getProperty(s_editorprop_rc_active) == "1" and \
                				msg == WM_RBUTTONDOWN and not(b_shift_down) and not(b_ctrl_down) and not(b_alt_down)):
                			dummy = 0														# do nothing
                		else:
                			return CallWindowProc(oldwndproc, hwnd, msg, wparam, lparam)	# -> IMPORTANT pass other msg to NPP, otherwise will block NPP
                
                		#res = "DEBUG " + s_hook_name
                		#res = res + " Message = " + hex(msg) + " to hwnd = " + hex(hwnd)
                		#res = res + " Forwarded To oldwndproc = " + str(oldwndproc)
                		#print res + " At " + str(datetime.now())
                		return CallWindowProc(oldwndproc, hwnd, 0, 0, 0)					# nullify the mouse hooked messages
                
                
                1 Reply Last reply Reply Quote 1
                • frying-panF
                  frying-pan
                  last edited by

                  Code chunk 2/2:

                  	def FindBorderBiDir(self, i_start, s_pattern):
                  		i_text_len = editor.getTextLength()
                  		t_search = editor.findText(FINDOPTION.REGEXP, i_start, 0, s_pattern)
                  		if t_search is None:
                  			i_left = -1
                  		else:
                  			i_left = t_search[0]
                  		t_search = editor.findText(FINDOPTION.REGEXP, i_start, i_text_len, s_pattern)
                  		if t_search is None:
                  			i_right = i_text_len
                  		else:
                  			i_right = t_search[0]
                  		return (i_left, i_right)
                  
                  	def ExtendSel_SpaceSpaceLike(self):
                  		s_pattern = "\s"
                  
                  		i_caret_pos = editor.getCurrentPos()
                  		i_start	= editor.getSelectionStart()
                  		i_end	= editor.getSelectionEnd()
                  
                  		t_border = self.FindBorderBiDir(i_caret_pos, s_pattern)
                  		i_left	= t_border[0] + 1
                  		i_right	= t_border[1]
                  		if i_left < i_right:
                  			editor.setSel(i_left, i_right)
                  		else:
                  			editor.setSel(i_start, i_end)
                  
                  	def ExtendSel_AlphaNumUnderscoreDot(self):
                  		s_pattern = "[^\w_.]"
                  
                  		i_caret_pos = editor.getCurrentPos()
                  		i_start	= editor.getSelectionStart()
                  		i_end	= editor.getSelectionEnd()
                  
                  		t_border = self.FindBorderBiDir(i_caret_pos, s_pattern)
                  		i_left	= t_border[0] + 1
                  		i_right	= t_border[1]
                  		if i_left < i_right:
                  			editor.setSel(i_left, i_right)
                  		else:
                  			editor.setSel(i_start, i_end)
                  
                  	def ExtendSel_Quotes(self):
                  		i_text_len = editor.getTextLength()
                  		i_caret_pos = editor.getCurrentPos()
                  		i_start	= editor.getSelectionStart()
                  		i_end	= editor.getSelectionEnd()
                  
                  		t_search_prev_single = editor.findText(0, i_caret_pos, 0, "'")
                  		t_search_next_single = editor.findText(0, i_caret_pos, i_text_len, "'")
                  		t_search_prev_double = editor.findText(0, i_caret_pos, 0, chr(34))
                  		t_search_next_double = editor.findText(0, i_caret_pos, i_text_len, chr(34))
                  
                  		i_left	= None
                  		i_right	= None
                  		if ((t_search_prev_single is None) or (t_search_next_single is None)):
                  			if (not(t_search_prev_double is None) and not(t_search_next_double is None)):
                  				i_left	= t_search_prev_double[0] + 1
                  				i_right	= t_search_next_double[0]
                  		elif ((t_search_prev_double is None) or (t_search_next_double is None)):
                  			if (not(t_search_prev_single is None) and not(t_search_next_single is None)):
                  				i_left	= t_search_prev_single[0] + 1
                  				i_right	= t_search_next_single[0]
                  		elif t_search_prev_single[0] < t_search_prev_double[0]:
                  				i_left	= t_search_prev_double[0] + 1
                  				i_right	= t_search_next_double[0]
                  		elif t_search_prev_double[0] < t_search_prev_single[0]:
                  				i_left	= t_search_prev_single[0] + 1
                  				i_right	= t_search_next_single[0]
                  		if (not(i_left is None) and not(i_right is None)):
                  			editor.setSel(i_left, i_right)
                  		else:
                  			editor.setSel(i_start, i_end)
                  
                  	def ExtendSel_BracketContent(self):
                  		def SearchOrphan(i_start, i_end, s_pattern_brackets):
                  			if i_start == i_end:
                  				return None
                  
                  			if i_start < i_end: b_forward = True
                  			else: b_forward = False
                  
                  			i_par = 0; i_sqr = 0; i_cur = 0
                  			i_pos = i_start
                  			while True:
                  				t_search = editor.findText(FINDOPTION.REGEXP, i_pos, i_end, s_pattern_brackets)
                  				if t_search is None:
                  					return None
                  
                  				i_pos = t_search[0]
                  				s_char = editor.getTextRange(i_pos, i_pos + 1)
                  				if		s_char == "(": i_par += 1
                  				elif	s_char == ")": i_par -= 1
                  				elif	s_char == "[": i_sqr += 1
                  				elif	s_char == "]": i_sqr -= 1
                  				elif	s_char == "{": i_cur += 1
                  				elif	s_char == "}": i_cur -= 1
                  
                  				if b_forward:
                  					if (i_par < 0 or i_sqr < 0 or i_cur < 0): break
                  				else:
                  					if (i_par > 0 or i_sqr > 0 or i_cur > 0): break
                  				if b_forward: i_pos = i_pos + 1
                  			return (i_pos, s_char)
                  
                  		i_text_len = editor.getTextLength()
                  		i_caret_pos = editor.getCurrentPos()
                  		i_start	= editor.getSelectionStart()
                  		i_end	= editor.getSelectionEnd()
                  
                  		s_pattern_par = "\(\)"
                  		s_pattern_sqr = "\[\]"
                  		s_pattern_cur = "\{\}"
                  		s_pattern_brackets = "[" + s_pattern_par + s_pattern_sqr + s_pattern_cur + "]"
                  
                  		i_left	= None
                  		i_right	= None
                  		while True:
                  			t_orphan_back = SearchOrphan(i_caret_pos, 0, s_pattern_brackets)
                  			if t_orphan_back is None: break
                  
                  			s_char = t_orphan_back[1]
                  			if		s_char == "(": s_pattern = "[" + s_pattern_par + "]"
                  			elif	s_char == "[": s_pattern = "[" + s_pattern_sqr + "]"
                  			elif	s_char == "{": s_pattern = "[" + s_pattern_cur + "]"
                  			t_orphan_forward = SearchOrphan(i_caret_pos, i_text_len, s_pattern)
                  			if not(t_orphan_forward is None):
                  				i_left	= t_orphan_back[0] + 1
                  				i_right	= t_orphan_forward[0]
                  				break
                  
                  			if		s_char == "(": s_pattern_brackets = s_pattern_brackets.replace(s_pattern_par, "")
                  			elif	s_char == "[": s_pattern_brackets = s_pattern_brackets.replace(s_pattern_sqr, "")
                  			elif	s_char == "{": s_pattern_brackets = s_pattern_brackets.replace(s_pattern_cur, "")
                  			if s_pattern_brackets == "[]": break
                  		if (not(i_left is None) and not(i_right is None)):
                  			editor.setSel(i_left, i_right)
                  		else:
                  			editor.setSel(i_start, i_end)
                  	# end of class
                  
                  s_opt_info = ""
                  if i_feature_shift_double_left_click			!= 1: s_opt_info = s_opt_info + "\t" + s_feature_sdlc_off + "\n"
                  if i_feature_control_shift_double_left_click	!= 1: s_opt_info = s_opt_info + "\t" + s_feature_csdlc_off + "\n"
                  if i_feature_alt_shift_double_left_click		!= 1: s_opt_info = s_opt_info + "\t" + s_feature_asdlc_off + "\n"
                  if i_feature_alt_right_click					!= 1: s_opt_info = s_opt_info + "\t" + s_feature_arc_off + "\n"
                  if i_feature_right_click						!= 1: s_opt_info = s_opt_info + "\t" + s_feature_rc_off + "\n"
                  if not(s_opt_info == ""): s_opt_info = s_opt_info[:-1]
                  console.editor.setProperty(s_editorprop_sdlc_active,	str(i_feature_shift_double_left_click))
                  console.editor.setProperty(s_editorprop_csdlc_active,	str(i_feature_control_shift_double_left_click))
                  console.editor.setProperty(s_editorprop_asdlc_active,	str(i_feature_alt_shift_double_left_click))
                  console.editor.setProperty(s_editorprop_arc_active,		str(i_feature_alt_right_click))
                  console.editor.setProperty(s_editorprop_rc_active,		str(i_feature_right_click))
                  
                  print "[" + s_script_name + " starts]"
                  if console.editor.getProperty(s_editorprop_regdone) != s_true:
                  	console.editor.setProperty(s_editorprop_active, s_true)
                  	o_scint_mouse_click_hook = C_Scint_Mouse_Click_Hook()	# create an instance of the hook class
                  	b_reg = o_scint_mouse_click_hook.RegHook()				# register the hook on all found Scintilla windows
                  	if b_reg:
                  		console.editor.setProperty(s_editorprop_regdone, s_true)
                  		print "\t" + s_hook_name + " registered and activated (" + s_reruntogglehook + ")"
                  		if not(s_opt_info == ""): print s_opt_info
                  	else:
                  		if not(s_opt_info == ""): print s_opt_info
                  		print "\t" + s_hook_name + " registering failed"
                  		if i_message_box_warnings == 1:
                  			notepad.messageBox(s_hook_name + " registering failed", s_script_name, MESSAGEBOXFLAGS.ICONSTOP)
                  else:
                  	if console.editor.getProperty(s_editorprop_active) != s_true:
                  		console.editor.setProperty(s_editorprop_active, s_true)
                  		print "\t" + s_hook_name + " re-activated (" + s_reruntogglehook + ")"
                  		if not(s_opt_info == ""): print s_opt_info
                  		if i_message_box_warnings == 1:
                  			notepad.messageBox( \
                  				s_hook_name + " re-activated\n\n" + s_reruntogglehook + "\n" + s_willapplynewopt + "\n" + s_opt_info, \
                  				s_script_name, MESSAGEBOXFLAGS.ICONINFORMATION)
                  	else:
                  		console.editor.setProperty(s_editorprop_active, "")
                  		print "\t" + s_hook_name + " DE-ACTIVATED (" + s_reruntogglehook + ")"
                  		if not(s_opt_info == ""): print s_opt_info
                  		if i_message_box_warnings == 1:
                  			notepad.messageBox( \
                  				s_hook_name + " DE-ACTIVATED\n\n" + s_reruntogglehook + "\n" + s_willapplynewopt + "\n" + s_opt_info, \
                  				s_script_name, MESSAGEBOXFLAGS.ICONEXCLAMATION)
                  
                  
                  1 Reply Last reply Reply Quote 1
                  • frying-panF
                    frying-pan
                    last edited by

                    Hey ! the forum changed the line 313 of my uploaded code

                    line 313 was uploaded as :
                    (I must replace \ by BKL to have it displayed)
                    s_pattern_sqr = “BKL[BKL]”
                    but is displayed without the \ as :
                    s_pattern_sqr = “[]”

                    which breaks the ‘CTRL + SHIFT + double-left-click’ feature : select brackets content : () [] {}

                    having compared the whole uploaded code with my original code there should no other error
                    (I used a simple file compare utility to do that : ExamDiff)

                    So there is a bug in the text parser of the forum :
                    the preview is ok but the final post has lost the backslashes

                    Michael VincentM Alan KilbornA 2 Replies Last reply Reply Quote 1
                    • Michael VincentM
                      Michael Vincent @frying-pan
                      last edited by

                      @frying-pan
                      Wow, that’s quite a script. Maybe post to GitHub or a Gist and then link from here to that post?

                      Cheers.

                      frying-panF 1 Reply Last reply Reply Quote 0
                      • Alan KilbornA
                        Alan Kilborn @frying-pan
                        last edited by

                        This post is deleted!
                        1 Reply Last reply Reply Quote 0
                        • frying-panF
                          frying-pan @Michael Vincent
                          last edited by

                          @Michael-Vincent

                          Uploaded on GitHub the last posted script in this thread, as version v1 (without the backslash error)

                          [https://github.com/frying-pan/NotepadPP-PythonScript-Mouse-Gesture/blob/master/Perso_ScintWndProc_Hook.v1.py](link url)

                          and made a version v2.0 : with some code cleaning and more object_oriented style

                          [https://github.com/frying-pan/NotepadPP-PythonScript-Mouse-Gesture/blob/master/Perso_ScintWndProc_Hook.v2.0.py](link url)

                          frying-panF 1 Reply Last reply Reply Quote 2
                          • frying-panF
                            frying-pan @frying-pan
                            last edited by

                            @frying-pan
                            Because of file name changes, above links are not valid any more,
                            see below for new links to each repository.

                            1 Reply Last reply Reply Quote 0
                            • frying-panF
                              frying-pan
                              last edited by

                              An update of :
                              Perso_ScintWndProc_Hook_v1_0.py to Perso_ScintWndProc_Hook_v1_1.py and
                              Perso_ScintWndProc_Hook_v2_0.py to Perso_ScintWndProc_Hook_v2.1.py for the same small bug

                              Update to Perso_ScintWndProc_Hook_v3_0.py with new features :

                              • option to select brackets/quotes with content
                              • option to auto-copy selection to clipboard and/or console
                              • expand selection from a previous selection
                                -> now requires the two libraries : Perso__Lib_Edit.py, Perso__Lib_Window.py

                              all files provided in the GitHub repository :
                              https://github.com/frying-pan/NotepadPP-PythonScript-Mouse-Gesture

                              ===

                              And another small python script to tweak the CR and LF graphic :
                              these changes can be applied at each Notepad++ start by running the script from startup.py

                              Screenshot.jpg

                              Perso_CrLfDisplay_Callback_v1_0.py

                              file provided in the GitHub repository :
                              https://github.com/frying-pan/NotepadPP-PythonScript-CrLf-Graphic-Tweak

                              1 Reply Last reply Reply Quote 3
                              • frying-panF
                                frying-pan
                                last edited by

                                I take advantage of this thread to advertise about two other python scripts that I wrote:

                                +++

                                Bracket Indicator : highlight the bracket () [] {} where is the selection/caret
                                based on BracketHighlighter.py idea, with easier customization for highlight style,
                                a different algo for highlighting (using the ‘re’ regexp object, possibly more efficient),
                                following the brackets matching rules of Scintilla

                                RestartNPP : to restart Notepad++ remembering the current session (opened file),
                                can possibly made into a button, and be run with a keyboard shortcut via ‘Shortcut Mapper’
                                can be handy when developping a script/plugin

                                +++

                                Perso_ScintWndProc_Hook was renamed to FP_MouseSelectGest_Hook, and updated
                                Perso_CrLfDisplay_Callback was renamed to FP_CrLfDisplay_Callback, and updated

                                FP_MouseSelectGest_Hook, FP_BracketIndicator_Callback and FP_CrLfDisplay_Callback
                                can be run from startup.py on each NPP start (with pythonscript option : initialisation=ATSTARTUP)

                                All four scripts have options at the top of the main script file that can be changed by the user
                                Some of these scripts use libraries which are provided in the same Github folder as the main script

                                Here is the root of the Github repositories :
                                https://github.com/frying-pan?tab=repositories

                                and the four repositories for the four scripts :
                                https://github.com/frying-pan/NotepadPP-PythonScript-Mouse_Select_Gesture
                                https://github.com/frying-pan/NotepadPP-PythonScript-Bracket_Indicator
                                https://github.com/frying-pan/NotepadPP-PythonScript-Restart_NotepadPP
                                https://github.com/frying-pan/NotepadPP-PythonScript-CrLf_Display

                                Alan KilbornA 1 Reply Last reply Reply Quote 1
                                • Alan KilbornA
                                  Alan Kilborn @frying-pan
                                  last edited by

                                  This thread got rather out of control.

                                  Anyway, I’m back to say that I did change my mind and I like the very first basic functionality this thread introduced: The ability to not lose your selection if you right click somewhere not on that selection.

                                  However, I noticed that it isn’t compatible with one of the new features of Notepad++: The ability go right-click the bookmark margin and get a right-click popup menu of bookmarking functions.

                                  I like the idea of both features; I wonder if there is some way to get both functionalities. Hmmm…

                                  frying-panF 1 Reply Last reply Reply Quote 0
                                  • frying-panF
                                    frying-pan @Alan Kilborn
                                    last edited by

                                    @Alan-Kilborn

                                    “This thread got rather out of control.”

                                    I could create a separate thread to describe and post the links for the 4 scripts I have made available on Github

                                    “However, I noticed that it isn’t compatible with one of the new features of Notepad++: The ability go right-click the bookmark margin and get a right-click popup menu of bookmarking functions.”

                                    I have NPP 7.8.2 and the right-click on the bookmark margin has been introduced later, so I can not test this for the moment
                                    (I guess the right-click context-menu on the bookmark margin does not appear at all…)

                                    I could discriminate between right-clicks on the margin or the text by pixel position,
                                    I will need the widths in pixels of all left margins :
                                    the function “editor.getMarginWidthN(margin_index) → int”, gives that for each margin_index,
                                    but I don’t know how many of margin_indexes must be checked (probably 5, from 0 to SC_MAX_MARGIN (4)),
                                    the function SCI_GETMARGINS (that gives the number of margins) seems to be missing from the NPP Scintilla commands

                                    Possible workarounds :
                                    My script does not intercept shift+right-click, so it could be tried on the bookmark margin
                                    (in case this context-menu is not sensitive to the shift key, which is often the case for context-menus)

                                    Also control+right-click and alt+right-click can be disabled in my script
                                    (there is a middle-click alternative for these features)
                                    to see if they trigger the bookmark margin context-menu, which seems less likely…

                                    Alan KilbornA 1 Reply Last reply Reply Quote 1
                                    • Alan KilbornA
                                      Alan Kilborn @frying-pan
                                      last edited by

                                      @frying-pan

                                      I noticed that the feature that this thread started with is being implemented natively in Notepad++. See HERE.

                                      frying-panF 1 Reply Last reply Reply Quote 1
                                      • frying-panF
                                        frying-pan @Alan Kilborn
                                        last edited by

                                        @Alan-Kilborn

                                        ok i see
                                        [https://github.com/notepad-plus-plus/notepad-plus-plus/pull/8564](link url)
                                        ‘Keep selection when right-click outside of selection’ has been added, or will be added to the next NPP version (as an option).

                                        So I will remove this specific feature from my
                                        NotepadPP-PythonScript-Mouse_Select_Gesture script
                                        once I have updated my NPP and tested the native option.

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