def __init__(self, s_script_name, s_hook_name, i_option_on, s_editorprop_hook_on, dic_editorprop): import platform import ctypes from ctypes import wintypes import time, re self.ctypes = ctypes self.time = time self.re = re s_plat_arch_x86 = "32bit" self.I_VK_SHIFT = 0x10 # virtual key code SHIFT self.I_VK_CONTROL = 0x11 # virtual key code CONTROL self.I_VK_ALT = 0x12 # virtual key code ALT self.KSTATE_ISDOWN = 0x8000 # key is pressed 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 # WM_LBUTTONUP = 0x0202 # mouse left-click up self.WM_LBUTTONDBLCLK = 0x0203 # mouse left-click doubled (second down) self.WM_RBUTTONDOWN = 0x0204 # mouse right-click down self.WM_RBUTTONUP = 0x0205 # mouse right-click up # WM_RBUTTONDBLCLK = 0x0206 # mouse right-click doubled (second down) # WM_MBUTTONDOWN = 0x0207 # mouse middle-click down self.WM_MBUTTONUP = 0x0208 # mouse middle-click up # WM_MBUTTONDBLCLK = 0x0209 # mouse middle-click doubled (second down) 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 # custom object import from FP__Lib_Edit import C_Extend_Sel_From_Sel from FP__Lib_Window import C_Get_NPPScintilla_Wins self.o_extend_sel_from_sel = C_Extend_Sel_From_Sel() self.o_get_nppscintilla_wins = C_Get_NPPScintilla_Wins() # class const self.i_num_editor = 2 # required number of NPP views/editors window # script params self.script_name = s_script_name self.hook_name = s_hook_name self.option_on = i_option_on self.editorprop_hook_on = s_editorprop_hook_on self.editorprop_csdlc_on = dic_editorprop["CSDLC"] self.editorprop_csmc_on = dic_editorprop["CSMC"] self.editorprop_asdlc_on = dic_editorprop["ASDLC"] self.editorprop_asmc_on = dic_editorprop["ASMC"] self.editorprop_crc_on = dic_editorprop["CRC"] self.editorprop_cmc_on = dic_editorprop["CMC"] self.editorprop_arc_on = dic_editorprop["ARC"] self.editorprop_amc_on = dic_editorprop["AMC"] self.editorprop_mc_move_on = dic_editorprop["MC_MOVE"] self.editorprop_rc_keep_on = dic_editorprop["RC_KEEP"] self.editorprop_angle_on = dic_editorprop["ANGLE"] self.editorprop_sl_angle_on = dic_editorprop["SL_ANGLE"] self.editorprop_get_br_on = dic_editorprop["GET_BR"] self.editorprop_get_qt_on = dic_editorprop["GET_QT"] self.editorprop_clpcopy_on = dic_editorprop["CLPCOPY"] self.editorprop_concopy_on = dic_editorprop["CONCOPY"] self.editorprop_wordchars = dic_editorprop["WORDCHR"] # instance state datas self.hook_done = False self.fatalerror = False self.last_down_time = time.time() self.lastsel_start = 0 self.lastsel_end = 0
def __init__(self, s_script_name, s_hook_name, i_option_on, s_editorprop_hook_on, dic_editorprop): import platform import ctypes from ctypes import wintypes import time self.time = time 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 from FP__Lib_Window import C_Get_NPPScintilla_Wins from FP__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() # local const self.i_num_editor = 2 # required number of NPP views/editors window # script params self.script_name = s_script_name self.hook_name = s_hook_name self.option_on = i_option_on self.editorprop_hook_on = s_editorprop_hook_on self.editorprop_sdlc_on = dic_editorprop["SDLC"] self.editorprop_csdlc_on = dic_editorprop["CSDLC"] self.editorprop_asdlc_on = dic_editorprop["ASDLC"] self.editorprop_arc_on = dic_editorprop["ARC"] self.editorprop_rc_on = dic_editorprop["RC"] self.editorprop_angle_on = dic_editorprop["ANGLE"] self.editorprop_gbr_on = dic_editorprop["GBR"] self.editorprop_gqt_on = dic_editorprop["GQT"] self.editorprop_clpcopy_on = dic_editorprop["CLPCOPY"] self.editorprop_concopy_on = dic_editorprop["CONCOPY"] self.editorprop_wordchr = dic_editorprop["WORDCHR"] # instance state datas self.hook_done = False self.last_down_time = time.time() self.lastsel_start = 0 self.lastsel_end = 0
class C_Scint_Mouse_Click_Hook(): # class constructor def __init__(self, s_script_name, s_hook_name, i_option_on, s_editorprop_hook_on, dic_editorprop): import platform import ctypes from ctypes import wintypes import time, re self.ctypes = ctypes self.time = time self.re = re s_plat_arch_x86 = "32bit" self.I_VK_SHIFT = 0x10 # virtual key code SHIFT self.I_VK_CONTROL = 0x11 # virtual key code CONTROL self.I_VK_ALT = 0x12 # virtual key code ALT self.KSTATE_ISDOWN = 0x8000 # key is pressed 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 # WM_LBUTTONUP = 0x0202 # mouse left-click up self.WM_LBUTTONDBLCLK = 0x0203 # mouse left-click doubled (second down) self.WM_RBUTTONDOWN = 0x0204 # mouse right-click down self.WM_RBUTTONUP = 0x0205 # mouse right-click up # WM_RBUTTONDBLCLK = 0x0206 # mouse right-click doubled (second down) # WM_MBUTTONDOWN = 0x0207 # mouse middle-click down self.WM_MBUTTONUP = 0x0208 # mouse middle-click up # WM_MBUTTONDBLCLK = 0x0209 # mouse middle-click doubled (second down) 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 # custom object import from FP__Lib_Edit import C_Extend_Sel_From_Sel from FP__Lib_Window import C_Get_NPPScintilla_Wins self.o_extend_sel_from_sel = C_Extend_Sel_From_Sel() self.o_get_nppscintilla_wins = C_Get_NPPScintilla_Wins() # class const self.i_num_editor = 2 # required number of NPP views/editors window # script params self.script_name = s_script_name self.hook_name = s_hook_name self.option_on = i_option_on self.editorprop_hook_on = s_editorprop_hook_on self.editorprop_csdlc_on = dic_editorprop["CSDLC"] self.editorprop_csmc_on = dic_editorprop["CSMC"] self.editorprop_asdlc_on = dic_editorprop["ASDLC"] self.editorprop_asmc_on = dic_editorprop["ASMC"] self.editorprop_crc_on = dic_editorprop["CRC"] self.editorprop_cmc_on = dic_editorprop["CMC"] self.editorprop_arc_on = dic_editorprop["ARC"] self.editorprop_amc_on = dic_editorprop["AMC"] self.editorprop_mc_move_on = dic_editorprop["MC_MOVE"] self.editorprop_rc_keep_on = dic_editorprop["RC_KEEP"] self.editorprop_angle_on = dic_editorprop["ANGLE"] self.editorprop_sl_angle_on = dic_editorprop["SL_ANGLE"] self.editorprop_get_br_on = dic_editorprop["GET_BR"] self.editorprop_get_qt_on = dic_editorprop["GET_QT"] self.editorprop_clpcopy_on = dic_editorprop["CLPCOPY"] self.editorprop_concopy_on = dic_editorprop["CONCOPY"] self.editorprop_wordchars = dic_editorprop["WORDCHR"] # instance state datas self.hook_done = False self.fatalerror = False self.last_down_time = time.time() self.lastsel_start = 0 self.lastsel_end = 0 # 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): def _Set_Middle_Click_Sel(lparam, b_move): i_sel_start = curedit.getSelectionStart() i_sel_end = curedit.getSelectionEnd() if b_move: x = self.ctypes.c_int16( lparam & 0x000000000000FFFF).value # same for 32 and 64 bits y = self.ctypes.c_int16((lparam & 0x00000000FFFF0000) >> 16 ).value # same for 32 and 64 bits i_pos = curedit.positionFromPoint( x, y ) # lparam and positionFromPoint are client coordinates if (i_pos < i_sel_start or i_pos > i_sel_end): i_sel_start = i_pos i_sel_end = i_pos curedit.setSelectionStart = i_pos curedit.setSelectionEnd = i_pos return (i_sel_start, i_sel_end) # 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: if not (self.fatalerror): self.fatalerror = True console.writeError( self.hook_name + " Fatal error ! Hooked WndProc NOT found !") console.show() notepad.messageBox(self.hook_name + " Fatal error ! Hooked WndProc NOT found !", \ self.script_name, MESSAGEBOXFLAGS.ICONSTOP) return 0 # -> FATAL ERROR ! you are f...ed... if (msg != self.WM_LBUTTONDOWN and msg != self.WM_LBUTTONDBLCLK and \ msg != self.WM_MBUTTONUP and \ msg != self.WM_RBUTTONDOWN and msg != self.WM_RBUTTONUP): # if NOT a hooked mouse click : abort return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass msg, otherwise will block NPP if console.editor.getProperty(self.editorprop_hook_on) != str( self.option_on): # if hook de-activated : abort return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass msg, otherwise will block NPP # identify which editor will receive the click before it has been activated if hwnd == self.lst_scint_hwnd[0]: curedit = editor1 elif hwnd == self.lst_scint_hwnd[1]: curedit = editor2 else: # if NOT editor1 or editor2 : abort return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass msg, otherwise will block NPP # double-click timing hack 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 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) if (b_shift_down or b_ctrl_down or b_alt_down): b_clp_copy = (console.editor.getProperty( self.editorprop_clpcopy_on) == str(self.option_on)) b_con_copy = (console.editor.getProperty( self.editorprop_concopy_on) == str(self.option_on)) # select the bracket content (uses double-click hack) if (b_shift_down and b_ctrl_down and not(b_alt_down) and ( \ (msg == self.WM_LBUTTONDBLCLK and console.editor.getProperty(self.editorprop_csdlc_on) == str(self.option_on)) or \ (msg == self.WM_MBUTTONUP and console.editor.getProperty(self.editorprop_csmc_on) == str(self.option_on)))): if msg == self.WM_MBUTTONUP: b_move = (console.editor.getProperty( self.editorprop_mc_move_on) == str(self.option_on)) i_sel_start, i_sel_end = _Set_Middle_Click_Sel( lparam, b_move) else: i_sel_start = self.lastsel_start i_sel_end = self.lastsel_end b_angle = (console.editor.getProperty( self.editorprop_angle_on) == str(self.option_on)) b_sl_angle = (console.editor.getProperty( self.editorprop_sl_angle_on) == str(self.option_on)) b_get_brackets = (console.editor.getProperty( self.editorprop_get_br_on) == str(self.option_on)) self.o_extend_sel_from_sel.ExtendSel_To_Brackets( \ curedit, i_sel_start, i_sel_end, b_angle, b_sl_angle, b_get_brackets, b_clp_copy, b_con_copy) # select the quote content (uses double-click hack) elif (b_shift_down and not(b_ctrl_down) and b_alt_down and ( \ (msg == self.WM_LBUTTONDBLCLK and console.editor.getProperty(self.editorprop_asdlc_on) == str(self.option_on)) or \ (msg == self.WM_MBUTTONUP and console.editor.getProperty(self.editorprop_asmc_on) == str(self.option_on)))): if msg == self.WM_MBUTTONUP: b_move = (console.editor.getProperty( self.editorprop_mc_move_on) == str(self.option_on)) i_sel_start, i_sel_end = _Set_Middle_Click_Sel( lparam, b_move) else: i_sel_start = self.lastsel_start i_sel_end = self.lastsel_end b_get_quotes = (console.editor.getProperty( self.editorprop_get_qt_on) == str(self.option_on)) self.o_extend_sel_from_sel.ExtendSel_To_Quotes( \ curedit, b_get_quotes, i_sel_start, i_sel_end, b_clp_copy, b_con_copy) # select the whole word elif (not(b_shift_down) and b_ctrl_down and not(b_alt_down) and ( \ (msg == self.WM_RBUTTONUP and console.editor.getProperty(self.editorprop_crc_on) == str(self.option_on)) or \ (msg == self.WM_MBUTTONUP and console.editor.getProperty(self.editorprop_cmc_on) == str(self.option_on)))): if msg == self.WM_MBUTTONUP: b_move = (console.editor.getProperty( self.editorprop_mc_move_on) == str(self.option_on)) i_sel_start, i_sel_end = _Set_Middle_Click_Sel( lparam, b_move) else: i_sel_start = curedit.getSelectionStart() i_sel_end = curedit.getSelectionEnd() s_escaped_wordchars = self.re.escape( console.editor.getProperty(self.editorprop_wordchars)) self.o_extend_sel_from_sel.ExtendSel_WordWSpecialChars( \ curedit, i_sel_start, i_sel_end, s_escaped_wordchars, b_clp_copy, b_con_copy) # select until space/space-like chars elif (not(b_shift_down) and not(b_ctrl_down) and b_alt_down and ( \ (msg == self.WM_RBUTTONUP and console.editor.getProperty(self.editorprop_arc_on) == str(self.option_on)) or \ (msg == self.WM_MBUTTONUP and console.editor.getProperty(self.editorprop_amc_on) == str(self.option_on)))): if msg == self.WM_MBUTTONUP: b_move = (console.editor.getProperty( self.editorprop_mc_move_on) == str(self.option_on)) i_sel_start, i_sel_end = _Set_Middle_Click_Sel( lparam, b_move) else: i_sel_start = curedit.getSelectionStart() i_sel_end = curedit.getSelectionEnd() self.o_extend_sel_from_sel.ExtendSel_To_SpacesSpacesLike( \ curedit, i_sel_start, i_sel_end, b_clp_copy, b_con_copy) # keep the selection/caret elif (not(b_shift_down) and not(b_ctrl_down) and not(b_alt_down) and \ msg == self.WM_RBUTTONDOWN and console.editor.getProperty(self.editorprop_rc_keep_on) == str(self.option_on)): pass # normal WindowProc processing else: return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass 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.hook_done: return False self.lst_scint_hwnd = [] self.lst_scint_oldwndproc = [] # get Scintilla windows in lst_scint_hwnd, number must be >= i_num_editor, truncate to i_num_editor npp_win_hwnd, self.lst_scint_hwnd, s_npp_class, s_scint_class = \ self.o_get_nppscintilla_wins.GetNPPAndEditorsInfos() 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" + "Required " + str(self.i_num_editor) + " " + s_scint_class + " editors window : " + \ str(len(self.lst_scint_hwnd)) + " found ! NO hook !" return False del self.lst_scint_hwnd[self.i_num_editor:] # get the address of our own WndProc self.newWndProc = self.WndProcType(_HOOK_MyWndProc) # register the hook for each Scintilla 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 each hook 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" + "-> DEBUG : " + hex(win_hwnd) + " : WindowProc OK" + " : " + str(oldwndproc) + "\n" i_index = i_index + 1 else: del self.lst_scint_hwnd[i_index] s_hookreport = s_hookreport + "\t" + "-> " + hex( win_hwnd) + " : " + "NO WindowProc ! NOT hooked !" + "\n" if s_hookreport != "": s_hookreport = s_hookreport[:-1] print s_hookreport if len(self.lst_scint_hwnd) != self.i_num_editor: # un-register hooks that were successfull since the whole hook is canceled for i in range(0, len(self.lst_scint_hwnd)): self.SetWindowLong(self.lst_scint_hwnd[i], self.GWL_WNDPROC, self.lst_scint_oldwndproc[i]) print "\t" + "Missing editors WindowProc ! NO hook !" return False self.hook_done = True return True
class C_Scint_Mouse_Click_Hook(): # class constructor def __init__(self, s_script_name, s_hook_name, i_option_on, s_editorprop_hook_on, dic_editorprop): import platform import ctypes from ctypes import wintypes import time self.time = time 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 from FP__Lib_Window import C_Get_NPPScintilla_Wins from FP__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() # local const self.i_num_editor = 2 # required number of NPP views/editors window # script params self.script_name = s_script_name self.hook_name = s_hook_name self.option_on = i_option_on self.editorprop_hook_on = s_editorprop_hook_on self.editorprop_sdlc_on = dic_editorprop["SDLC"] self.editorprop_csdlc_on = dic_editorprop["CSDLC"] self.editorprop_asdlc_on = dic_editorprop["ASDLC"] self.editorprop_arc_on = dic_editorprop["ARC"] self.editorprop_rc_on = dic_editorprop["RC"] self.editorprop_angle_on = dic_editorprop["ANGLE"] self.editorprop_gbr_on = dic_editorprop["GBR"] self.editorprop_gqt_on = dic_editorprop["GQT"] self.editorprop_clpcopy_on = dic_editorprop["CLPCOPY"] self.editorprop_concopy_on = dic_editorprop["CONCOPY"] self.editorprop_wordchr = dic_editorprop["WORDCHR"] # instance state datas self.hook_done = False self.last_down_time = time.time() self.lastsel_start = 0 self.lastsel_end = 0 # 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: 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 # -> FATAL ERROR ! you are f...ed... 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 msg, otherwise will block NPP if console.editor.getProperty(self.editorprop_hook_on) != str( self.option_on): # if hook de-activated : abort return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass msg, otherwise will block NPP # identify which editor will receive the click before it has been activated if hwnd == self.lst_scint_hwnd[0]: curedit = editor1 elif hwnd == self.lst_scint_hwnd[1]: curedit = editor2 else: # if NOT editor1 or editor2 : abort return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass msg, otherwise will block NPP 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 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.option_on)) b_con_copy = (console.editor.getProperty( self.editorprop_concopy_on) == str(self.option_on)) # select from clicked point the whole word 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.option_on))): s_specchars = console.editor.getProperty( self.editorprop_wordchr) self.o_extend_sel_from_caret.ExtendSel_WordWSpecialChars( \ curedit, self.lastsel_start, self.lastsel_end, s_specchars, 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.option_on))): b_angle = (console.editor.getProperty( self.editorprop_angle_on) == str(self.option_on)) b_get_brackets = (console.editor.getProperty( self.editorprop_gbr_on) == str(self.option_on)) self.o_extend_sel_from_caret.ExtendSel_To_Brackets( \ curedit, self.lastsel_start, self.lastsel_end, b_angle, b_get_brackets, 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.option_on))): b_get_quotes = (console.editor.getProperty( self.editorprop_gqt_on) == str(self.option_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.option_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.option_on))): pass else: return self.CallWindowProc( oldwndproc, hwnd, msg, wparam, lparam ) # -> IMPORTANT : pass 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.hook_done: 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" + "Required " + self.i_num_editor + " " + s_scint_class + " 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 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" + "Required " + self.i_num_editor + " " + s_scint_class + " editors WindowProc NOT found ! NO hook !" return False self.hook_done = True return True