class C_Scint_Mouse_Click_Hook(): # class constructor def __init__(self, s_script_name, s_hook_name, i_feature_on, s_editorprop_hook_on, dic_features): import platform import ctypes from ctypes import wintypes import time self.time = time from Perso__Lib_Window import C_Get_NPPScintilla_Wins from Perso__Lib_Edit import C_Extend_Sel_From_Caret self.o_get_nppscintilla_wins = C_Get_NPPScintilla_Wins() self.o_extend_sel_from_caret = C_Extend_Sel_From_Caret() self.HookDone = False self.script_name = s_script_name self.hook_name = s_hook_name self.feature_on = i_feature_on self.editorprop_hook_on = s_editorprop_hook_on self.editorprop_sdlc_on = dic_features["SDLC"] self.editorprop_csdlc_on = dic_features["CSDLC"] self.editorprop_asdlc_on = dic_features["ASDLC"] self.editorprop_arc_on = dic_features["ARC"] self.editorprop_rc_on = dic_features["RC"] self.editorprop_gbr_on = dic_features["GBR"] self.editorprop_gqt_on = dic_features["GQT"] self.editorprop_clpcopy_on = dic_features["CLPCOPY"] self.editorprop_concopy_on = dic_features["CONCOPY"] self.last_down_time = time.time() self.lastsel_start = 0 self.lastsel_end = 0 self.i_num_editor = 2 # number of NPP views/editors window s_plat_arch_x86 = "32bit" self.GWL_WNDPROC = -4 # used to set a new address for a window procedure self.WM_NONE = 0x0000 # null window message self.WM_LBUTTONDOWN = 0x0201 # mouse left-click-down window message self.WM_LBUTTONDBLCLK = 0x0203 # mouse double-left-click window message self.WM_RBUTTONDOWN = 0x0204 # mouse right-click down window message self.WM_RBUTTONUP = 0x0205 # mouse right-click up window message self.I_VK_SHIFT = 0x10 # SHIFT virtual key code self.I_VK_CONTROL = 0x11 # CONTROL virtual key code self.I_VK_ALT = 0x12 # ALT virtual key code self.KSTATE_ISDOWN = 0x8000 # key pressed LRESULT = wintypes.LPARAM self.WndProcType = ctypes.WINFUNCTYPE(LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM) # window message hook functions self.CallWindowProc = ctypes.windll.user32.CallWindowProcW self.CallWindowProc.restype = LRESULT self.CallWindowProc.argtypes = [ self.WndProcType, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM ] b_x86 = (platform.architecture()[0].lower() == s_plat_arch_x86.lower()) if b_x86: self.SetWindowLong = ctypes.windll.user32.SetWindowLongW else: self.SetWindowLong = ctypes.windll.user32.SetWindowLongPtrW self.SetWindowLong.restype = self.WndProcType self.SetWindowLong.argtypes = [ wintypes.HWND, wintypes.INT, self.WndProcType ] # end of window message hook functions self.GetDoubleClickTime = ctypes.windll.user32.GetDoubleClickTime self.GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState self.SetLastError = ctypes.windll.kernel32.SetLastError self.GetLastError = ctypes.windll.kernel32.GetLastError # function to register the hook on Scintilla editors window def RegHook(self): # our own WndProc function that receives hooked windows messages def HOOK_MyWndProc(hwnd, msg, wparam, lparam): # window messages may be processed slower than actual double-left-click f_s_dblclick_processing_bonus = float(0.1) oldwndproc = None for i in range(0, len(self.lst_scint_hwnd)): # target hwnd found at index i in lst_scint_hwnd if self.lst_scint_hwnd[i] == hwnd: # corresponding oldwndproc is picked in lst_scint_oldwndproc oldwndproc = self.lst_scint_oldwndproc[i] break if oldwndproc is None: # FATAL ERROR ! you are doomed... print "\t" + self.hook_name + " Fatal error ! Hooked WndProc NOT found !" notepad.messageBox(self.hook_name + " Fatal error ! Hooked WndProc NOT found !", \ self.script_name, MESSAGEBOXFLAGS.ICONSTOP) return 0 if (msg != self.WM_LBUTTONDOWN and msg != self.WM_LBUTTONDBLCLK and \ msg != self.WM_RBUTTONDOWN and msg != self.WM_RBUTTONUP): # if NOT in mouse hooked messages : abort return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass other msg, otherwise will block NPP if console.editor.getProperty(self.editorprop_hook_on) != str( self.feature_on): # if hook de-activated : abort return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass other msg, otherwise will block NPP if hwnd == self.lst_scint_hwnd[0]: curedit = editor1 else: curedit = editor2 if msg == self.WM_LBUTTONDOWN: f_s_dblclick = (float(self.GetDoubleClickTime()) / float(1000)) + f_s_dblclick_processing_bonus cur_time = self.time.time() # save current selection before the first left-click down of a double-left-click loose this selection if ((cur_time - self.last_down_time) > f_s_dblclick): self.lastsel_start = curedit.getSelectionStart() self.lastsel_end = curedit.getSelectionEnd() # if second left-click down is out of the last selection, collapse this last selection to caret position elif (curedit.getCurrentPos() < self.lastsel_start or curedit.getCurrentPos() > self.lastsel_end): self.lastsel_start = curedit.getCurrentPos() self.lastsel_end = curedit.getCurrentPos() self.last_down_time = cur_time return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass other msg, otherwise will block NPP b_shift_down = ((self.GetAsyncKeyState(self.I_VK_SHIFT) & self.KSTATE_ISDOWN) == self.KSTATE_ISDOWN) b_ctrl_down = ((self.GetAsyncKeyState(self.I_VK_CONTROL) & self.KSTATE_ISDOWN) == self.KSTATE_ISDOWN) b_alt_down = ((self.GetAsyncKeyState(self.I_VK_ALT) & self.KSTATE_ISDOWN) == self.KSTATE_ISDOWN) b_clp_copy = (console.editor.getProperty( self.editorprop_clpcopy_on) == str(self.feature_on)) b_con_copy = (console.editor.getProperty( self.editorprop_concopy_on) == str(self.feature_on)) # select from clicked point the whole variable name if (msg == self.WM_LBUTTONDBLCLK and b_shift_down and not(b_ctrl_down) and not(b_alt_down) and \ (console.editor.getProperty(self.editorprop_sdlc_on) == str(self.feature_on))): self.o_extend_sel_from_caret.ExtendSel_AlphaNumUnderscoreDot( \ curedit, self.lastsel_start, self.lastsel_end, b_clp_copy, b_con_copy) # select from clicked point the whole bracket [content] elif (msg == self.WM_LBUTTONDBLCLK and b_shift_down and b_ctrl_down and not(b_alt_down) and \ (console.editor.getProperty(self.editorprop_csdlc_on) == str(self.feature_on))): b_get_brackets = (console.editor.getProperty( self.editorprop_gbr_on) == str(self.feature_on)) self.o_extend_sel_from_caret.ExtendSel_To_Brackets( \ curedit, b_get_brackets, self.lastsel_start, self.lastsel_end, b_clp_copy, b_con_copy) # select from clicked point the whole quote [content] elif (msg == self.WM_LBUTTONDBLCLK and b_shift_down and not(b_ctrl_down) and b_alt_down and \ (console.editor.getProperty(self.editorprop_asdlc_on) == str(self.feature_on))): b_get_quotes = (console.editor.getProperty( self.editorprop_gqt_on) == str(self.feature_on)) self.o_extend_sel_from_caret.ExtendSel_To_Quotes( \ curedit, b_get_quotes, self.lastsel_start, self.lastsel_end, b_clp_copy, b_con_copy) # select from clicked point until space/space-like chars elif (msg == self.WM_RBUTTONUP and not(b_shift_down) and not(b_ctrl_down) and b_alt_down and \ (console.editor.getProperty(self.editorprop_arc_on) == str(self.feature_on))): self.o_extend_sel_from_caret.ExtendSel_To_SpacesSpacesLike( \ curedit, curedit.getSelectionStart(), curedit.getSelectionEnd(), b_clp_copy, b_con_copy) # do nothing, keep the selection elif (msg == self.WM_RBUTTONDOWN and not(b_shift_down) and not(b_ctrl_down) and not(b_alt_down) and \ (console.editor.getProperty(self.editorprop_rc_on) == str(self.feature_on))): pass else: return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass other msg, otherwise will block NPP #s_debug = "DEBUG : " + s_hook_name + " got Msg = " + hex(msg) + ", to Hwnd = " + hex(hwnd) #s_debug = s_debug + ", oldWndProc = " + str(oldwndproc) #s_debug = s_debug + " (Edit1_Hwnd = " + hex(self.lst_scint_hwnd[0]) + ", Edit2_Hwnd = " + hex(self.lst_scint_hwnd[1]) + ")" #s_debug = s_debug + " At " + str(self.time.time()) #print s_debug return self.CallWindowProc(oldwndproc, hwnd, self.WM_NONE, 0, 0) # NULLIFY the mouse hooked messages # end of hook if self.HookDone: return False self.lst_scint_hwnd = [] self.lst_scint_oldwndproc = [] npp_win_hwnd, self.lst_scint_hwnd, s_npp_class, s_scint_class = self.o_get_nppscintilla_wins.GetWinsInfos( self.i_num_editor) if npp_win_hwnd is None: print "\t" + s_npp_class + " main window NOT found ! NO hook !" return False if len(self.lst_scint_hwnd) != self.i_num_editor: print "\t" + s_scint_class + " " + self.i_num_editor + " editors window NOT found ! NO hook !" return False print "\t" + "Found " + str(len( self.lst_scint_hwnd)) + " " + s_scint_class + " editors window" # get the address of our own WndProc self.newWndProc = self.WndProcType(HOOK_MyWndProc) # register the hook for each window present in lst_scint_hwnd, number must be i_num_editor s_hookreport = "" i_index = 0 while i_index < len(self.lst_scint_hwnd): win_hwnd = self.lst_scint_hwnd[i_index] # register hooks and store oldwndproc addresses in lst_scint_oldwndproc self.SetLastError(0) oldwndproc = self.SetWindowLong(win_hwnd, self.GWL_WNDPROC, self.newWndProc) i_apierr = self.GetLastError() if i_apierr == 0: self.lst_scint_oldwndproc.append(oldwndproc) #s_hookreport = s_hookreport + "\t\t" + "DEBUG : " + hex(win_hwnd) + " / " + str(oldwndproc) + " WindowProc OK" + "\n" i_index = i_index + 1 else: del self.lst_scint_hwnd[i_index] s_hookreport = s_hookreport + "\t\t" + hex( win_hwnd) + " / " + "NO WindowProc ! NOT hooked !" + "\n" if not (s_hookreport == ""): s_hookreport = s_hookreport[:-1] print s_hookreport if len(self.lst_scint_hwnd) != self.i_num_editor: for i in range(0, len(self.lst_scint_hwnd)): # un-register hooks that were successfull since the whole hook is canceled self.SetWindowLong(self.lst_scint_hwnd[i], self.GWL_WNDPROC, self.lst_scint_oldwndproc[i]) print "\t" + s_scint_class + " " + str( self.i_num_editor ) + " editors WindowProc NOT found ! NO hook !" return False self.HookDone = True return True
def __init__(self, s_script_name, s_hook_name, i_feature_on, s_editorprop_hook_on, dic_features): import platform import ctypes from ctypes import wintypes import time self.time = time from Perso__Lib_Window import C_Get_NPPScintilla_Wins from Perso__Lib_Edit import C_Extend_Sel_From_Caret self.o_get_nppscintilla_wins = C_Get_NPPScintilla_Wins() self.o_extend_sel_from_caret = C_Extend_Sel_From_Caret() self.HookDone = False self.script_name = s_script_name self.hook_name = s_hook_name self.feature_on = i_feature_on self.editorprop_hook_on = s_editorprop_hook_on self.editorprop_sdlc_on = dic_features["SDLC"] self.editorprop_csdlc_on = dic_features["CSDLC"] self.editorprop_asdlc_on = dic_features["ASDLC"] self.editorprop_arc_on = dic_features["ARC"] self.editorprop_rc_on = dic_features["RC"] self.editorprop_gbr_on = dic_features["GBR"] self.editorprop_gqt_on = dic_features["GQT"] self.editorprop_clpcopy_on = dic_features["CLPCOPY"] self.editorprop_concopy_on = dic_features["CONCOPY"] self.last_down_time = time.time() self.lastsel_start = 0 self.lastsel_end = 0 self.i_num_editor = 2 # number of NPP views/editors window s_plat_arch_x86 = "32bit" self.GWL_WNDPROC = -4 # used to set a new address for a window procedure self.WM_NONE = 0x0000 # null window message self.WM_LBUTTONDOWN = 0x0201 # mouse left-click-down window message self.WM_LBUTTONDBLCLK = 0x0203 # mouse double-left-click window message self.WM_RBUTTONDOWN = 0x0204 # mouse right-click down window message self.WM_RBUTTONUP = 0x0205 # mouse right-click up window message self.I_VK_SHIFT = 0x10 # SHIFT virtual key code self.I_VK_CONTROL = 0x11 # CONTROL virtual key code self.I_VK_ALT = 0x12 # ALT virtual key code self.KSTATE_ISDOWN = 0x8000 # key pressed LRESULT = wintypes.LPARAM self.WndProcType = ctypes.WINFUNCTYPE(LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM) # window message hook functions self.CallWindowProc = ctypes.windll.user32.CallWindowProcW self.CallWindowProc.restype = LRESULT self.CallWindowProc.argtypes = [ self.WndProcType, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM ] b_x86 = (platform.architecture()[0].lower() == s_plat_arch_x86.lower()) if b_x86: self.SetWindowLong = ctypes.windll.user32.SetWindowLongW else: self.SetWindowLong = ctypes.windll.user32.SetWindowLongPtrW self.SetWindowLong.restype = self.WndProcType self.SetWindowLong.argtypes = [ wintypes.HWND, wintypes.INT, self.WndProcType ] # end of window message hook functions self.GetDoubleClickTime = ctypes.windll.user32.GetDoubleClickTime self.GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState self.SetLastError = ctypes.windll.kernel32.SetLastError self.GetLastError = ctypes.windll.kernel32.GetLastError
def RegHook(self): # our own WndProc function that receives hooked windows messages def HOOK_MyWndProc(hwnd, msg, wparam, lparam): f_s_dblclick_processing_bonus = float(0.1) oldwndproc = None for i in range(0, len(self.lst_scint_hwnd)): # target hwnd found at index i in lst_scint_hwnd if self.lst_scint_hwnd[i] == hwnd: # corresponding oldwndproc is picked in lst_scint_oldwndproc oldwndproc = self.lst_scint_oldwndproc[i] break if oldwndproc is None: # FATAL ERROR ! you are doomed... print "\t" + self.hook_name + " Fatal error ! Hooked WndProc NOT found !" notepad.messageBox(self.hook_name + " Fatal error ! Hooked WndProc NOT found !", \ self.script_name, MESSAGEBOXFLAGS.ICONEXCLAMATION) return 0 if (msg != WM_LBUTTONDOWN and \ 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, otherwise will block NPP if console.editor.getProperty(self.editorprop_hook_on) != str( self.feature_on): # if hook de-activated : abort return CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT pass other msg, otherwise will block NPP # save selection before the first left-click down of a double-left-click loose it if msg == WM_LBUTTONDOWN: f_s_dblclick = (float(GetDoubleClickTime()) / float(1000)) + f_s_dblclick_processing_bonus cur_time = time.time() if (cur_time - self.last_down_time) > f_s_dblclick: self.lastsel_start = editor.getSelectionStart() self.lastsel_end = editor.getSelectionEnd() else: if (editor.getCurrentPos() < self.lastsel_start or editor.getCurrentPos() > self.lastsel_end): self.lastsel_start = editor.getCurrentPos() self.lastsel_end = editor.getCurrentPos() self.last_down_time = cur_time return CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT pass other msg, 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) b_clp_copy = (console.editor.getProperty( self.editorprop_clpcopy_on) == str(self.feature_on)) b_con_copy = (console.editor.getProperty( self.editorprop_concopy_on) == str(self.feature_on)) # select from clicked point the whole variable name if (msg == WM_LBUTTONDBLCLK and b_shift_down and not(b_ctrl_down) and not(b_alt_down) and \ (console.editor.getProperty(self.editorprop_sdlc_on) == str(self.feature_on))): self.o_extend_sel_from_caret.ExtendSel_AlphaNumUnderscoreDot( \ self.lastsel_start, self.lastsel_end, b_clp_copy, b_con_copy) # select from clicked point the whole bracket [content] elif (msg == WM_LBUTTONDBLCLK and b_shift_down and b_ctrl_down and not(b_alt_down) and \ (console.editor.getProperty(self.editorprop_csdlc_on) == str(self.feature_on))): b_get_brackets = (console.editor.getProperty( self.editorprop_gbr_on) == str(self.feature_on)) self.o_extend_sel_from_caret.ExtendSel_To_Brackets( \ b_get_brackets, self.lastsel_start, self.lastsel_end, b_clp_copy, b_con_copy) # select from clicked point the whole quote [content] elif (msg == WM_LBUTTONDBLCLK and b_shift_down and not(b_ctrl_down) and b_alt_down and \ (console.editor.getProperty(self.editorprop_asdlc_on) == str(self.feature_on))): b_get_quotes = (console.editor.getProperty( self.editorprop_gqt_on) == str(self.feature_on)) self.o_extend_sel_from_caret.ExtendSel_To_Quotes( \ b_get_quotes, self.lastsel_start, self.lastsel_end, b_clp_copy, b_con_copy) # select from clicked point until space/space-like chars elif (msg == WM_RBUTTONUP and not(b_shift_down) and not(b_ctrl_down) and b_alt_down and \ (console.editor.getProperty(self.editorprop_arc_on) == str(self.feature_on))): self.o_extend_sel_from_caret.ExtendSel_To_SpacesSpacesLike( \ editor.getSelectionStart(), editor.getSelectionEnd(), b_clp_copy, b_con_copy) # do nothing, keep the selection elif (msg == WM_RBUTTONDOWN and not(b_shift_down) and not(b_ctrl_down) and not(b_alt_down) and \ (console.editor.getProperty(self.editorprop_rc_on) == str(self.feature_on))): dummy = 0 else: return CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT pass other msg, otherwise will block NPP #s_debug = "DEBUG " + s_hook_name + " Message = " + hex(msg) + " to hwnd = " + hex(hwnd) #s_debug = s_debug + " Forwarded To oldwndproc = " + str(oldwndproc) + " At " + str(datetime.now()) #print s_debug return CallWindowProc(oldwndproc, hwnd, WM_NONE, 0, 0) # NULLIFY the mouse hooked messages if self.HookDone: return False self.lst_scint_hwnd = [] self.lst_scint_oldwndproc = [] o_get_nppscintilla_wins = C_Get_NPPScintilla_Wins() npp_win_hwnd, self.lst_scint_hwnd, s_npp_class, s_scint_class = o_get_nppscintilla_wins.GetWinsInfos( ) 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) print "\t" + "Found " + str(len( self.lst_scint_hwnd)) + " " + s_scint_class + " Handle(s)" # get the address of our own WndProc self.newWndProc = WndProcType(HOOK_MyWndProc) # register the hook for each window present in lst_scint_hwnd s_errlist = "" i_index = 0 while i_index < len(self.lst_scint_hwnd): win_hwnd = self.lst_scint_hwnd[i_index] # register hook and store oldwndproc addresses in lst_scint_oldwndproc SetLastError(0) oldwndproc = SetWindowLong(win_hwnd, GWL_WNDPROC, self.newWndProc) i_apierr = GetLastError() if i_apierr == 0: self.lst_scint_oldwndproc.append(oldwndproc) i_index = i_index + 1 else: del self.lst_scint_hwnd[i_index] s_errlist = s_errlist + "\t\t" + hex( win_hwnd) + " / " + "NO WindowProc ! NOT hooked !" + "\n" if not (s_errlist == ""): s_errlist = s_errlist[:-1] print s_errlist if len(self.lst_scint_hwnd) == 0: print "\t" + s_scint_class + " window(s) or WindowProc(s) NOT found or NO hook !" return False self.o_extend_sel_from_caret = C_Extend_Sel_From_Caret() self.HookDone = True return True