Update LedKeyboardShortcuts.ahk

Update LedKeyboardShortcuts.ahk

Uses a gui for the notifications

#NoEnv
#SingleInstance force ; only lets one run
SetBatchLines, -1

#Persistent ; prevents the script from exiting automatically

; Register a function to be called on exit:
OnExit("ExitFunc")

; make the notification box
notificationEnable := true ; set this to false to disable the notification popups
notificationTime := 1 ; seconds the notifications stay

n := new Notification("Starting led shortcuts!", 200,14, ,notificationTime)

; make the socket
socket := new McLightingServer("ws://192.168.1.33:81") ; replace with the ip address of the mclighting controller

incrementAmount := 10 ; how finely you adjust the color, speed, and brightness per keypress
maxColor := 255
maxMode := 56 ; the maximum mode id number

red := 127
green := 127
blue := 127

brightness := 127
speed := 127
mode := 1

^#Numpad4:: ; switch modes down
	mode := mode - 1
	if(mode < 0)
		mode := maxMode
	cmd = /%mode% ; / is the set effect mode command
	setNotification("Mode: " + mode)
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#Numpad6:: ; switch modes up
	mode := mode + 1
	if(mode > maxMode)
		mode := 0
	setNotification("Mode: " + mode)
	cmd = /%mode%
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#Numpad5:: ; turn off
	setNotification("Leds are off")
	sendCmd("=off") ; = is the set control command
	return

^#Numpad0:: ; turn on to static color
	setNotification("Leds are on")
	cmd := "*"+toHexColor(red,green,blue) ; * is the set all command
	sendCmd(cmd) ; = is the set control command
	return

^#NumpadSub:: ; decreases brightness
	brightness := brightness - incrementAmount
	if(brightness < 0)
		brightness := 0
	cmd = `%%brightness% ;  % is the set brightness mode command. ` is the escape character
	setNotification("Brightness: " + brightness)
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#NumpadAdd:: ; increases brightness
	brightness := brightness + incrementAmount
	if(brightness > maxColor)
		brightness := maxColor
	cmd = `%%brightness% ; % is the set brightness mode command
	setNotification("Brightness: " + brightness)
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#NumpadDiv:: ; decreases speed
	speed := speed - incrementAmount
	if(speed < 0)
		speed := 0
	cmd = ?%speed% ;  ? is the set brightness mode command
	setNotification("Speed: "+ speed)
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#NumpadMult:: ; increases speed
	speed := speed + incrementAmount
	if(speed > maxColor)
		speed := maxColor
	cmd = ?%speed% ;  ? is the set brightness mode command
	setNotification("Speed: "+ speed)
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#Numpad7:: ; increase red amount
	red := red + incrementAmount
	if(red > maxColor)
		red := maxColor

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	setNotification("Red: " + red)
	sendCmd(cmd)

	return

^#Numpad1:: ; decrease red amount
	red := red - incrementAmount
	if(red < 0)
		red := 0

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	setNotification("Red: " + red)
	sendCmd(cmd)

	return

^#Numpad8:: ; increase green amount
	green := green + incrementAmount
	if(green > maxColor)
		green := maxColor

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	setNotification("Green: " + green)
	sendCmd(cmd)

	return

^#Numpad2:: ; decrease green amount
	green := green - incrementAmount
	if(green < 0)
		green := 0

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	setNotification("Green: " + green)
	sendCmd(cmd)

	return

^#Numpad9:: ; increase blue amount
	blue := blue + incrementAmount
	if(blue > maxColor)
		blue := maxColor

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	setNotification("Blue: " + blue)
	sendCmd(cmd)

	return

^#Numpad3:: ; decrease blue amount
	blue := blue - incrementAmount
	if(blue < 0)
		blue := 0

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	setNotification("Blue: " + blue)
	sendCmd(cmd)

	return

sendCmd(cmd)
{
	global socket
	; MsgBox connected: %connected%
	if( socket.connected )
	{
		socket.Send(cmd)
		; MsgBox %cmd%
	}
	else
		setNotification("Not connected to server!")

	return
}

toHexColor(r, g, b)
{
	SetFormat, IntegerFast, hex ; To print values as hexadecimal
	colorNum := r*65536 + g*256 + b
	color = 000000%colorNum% ; convert to string and add preceeding zeros
	color := StrReplace(color, "0x") ; replace the 0x from the middle
	color := SubStr( color, -5 ) ; get last 6 characters

	SetFormat, IntegerFast, dec ; sets the print mode back to decimal
	return ""+color ;
}

setNotification(message)
{
	global notificationEnable
	global n ; the notification class
	if(notificationEnable)
	{
		n.updateMessage(message)
		n.nshow()
	}

	return
}

ExitFunc(ExitReason, ExitCode)
{
	; MsgBox Exiting
	global n ; notification class
	n.ndestroy() ; deletes the notification
	return 0 ; must return zero to exit
}

class McLightingServer extends WebSocket
{
	OnOpen(Event)
	{
		setNotification("Connection established!")
		; InputBox, Data, WebSocket, Enter some text to send through the websocket.
		; this.Send(Data)

		; global connected
		this.connected := 1
		; MsgBox connection %connected%
	}

	OnMessage(Event)
	{
		; MsgBox, % "Received Data: " Event.data
		; this.Close()
	}

	OnClose(Event)
	{
		setNotification("Websocket Closed!")
		; MsgBox closed!
		this.connected := 0
		this.Disconnect()
		ExitApp
	}

	OnError(Event)
	{
		setNotification("Websocket Error")
		; MsgBox error!
		this.connected := 0
		this.Close()
	}

	__Delete()
	{
		; setNotification(" Exiting Led controller ") ; sticks after program closes
		ExitApp
	}
}

class WebSocket
{
	__New(WS_URL)
	{
		this.connected := false ; flag for connection status
		static wb

		; Create an IE instance
		Gui, +hWndhOld
		Gui, New, +hWndhWnd
		this.hWnd := hWnd
		Gui, Add, ActiveX, vWB, Shell.Explorer
		Gui, %hOld%: Default

		; Write an appropriate document
		WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible'"
		. "content='IE=edge'><body></body>")
		while (WB.ReadyState < 4)
			sleep, 50
		this.document := WB.document

		; Add our handlers to the JavaScript namespace
		this.document.parentWindow.ahk_savews := this._SaveWS.Bind(this)
		this.document.parentWindow.ahk_event := this._Event.Bind(this)
		this.document.parentWindow.ahk_ws_url := WS_URL

		; Add some JavaScript to the page to open a socket
		Script := this.document.createElement("script")
		Script.text := "ws = new WebSocket(ahk_ws_url);`n"
		. "ws.onopen = function(event){ ahk_event('Open', event); };`n"
		. "ws.onclose = function(event){ ahk_event('Close', event); };`n"
		. "ws.onerror = function(event){ ahk_event('Error', event); };`n"
		. "ws.onmessage = function(event){ ahk_event('Message', event); };"
		this.document.body.appendChild(Script)
	}

	; Called by the JS in response to WS events
	_Event(EventName, Event)
	{
		this["On" EventName](Event)
	}

	; Sends data through the WebSocket
	Send(Data)
	{
		; MsgBox %Data%
		this.document.parentWindow.ws.send(Data)
	}

	; Closes the WebSocket connection
	Close(Code:=1000, Reason:="")
	{
		this.document.parentWindow.ws.close(Code, Reason)
	}

	; Closes and deletes the WebSocket, removing
	; references so the class can be garbage collected
	Disconnect()
	{
		if this.hWnd
		{
			this.Close()
			Gui, % this.hWnd ": Destroy"
			this.hWnd := False
		}
	}
}

class Notification
{
	__New(message, pnW=700, pnH=300, position="b r", time=10000)
	{
		this.showTime := time ; time that the notification shows up for

		global pn_msg
		this.pn_msg := message ; message to display

		Gui, Notify: +AlwaysOnTop +ToolWindow -SysMenu -Caption +LastFound
		this.PN_hwnd := WinExist()

		WinSet, ExStyle, +0x20
		WinSet, Transparent, 0 ; makes the box transparent so that it can fade in and out. 255 -> opaque; 0 -> transparent
		Gui, Notify: Color, 0x111111 ; background color
		Gui, Notify: Font, cWhite s10 w500, Terminal ; message color, size, weight, and font
		Gui, Notify: Add, Text, % " x" 20 " y" 12 " vpn_msg", % this.pn_msg ; add message
		RealW := pnW + 50
		RealH := pnH + 20
		Gui, Notify: Show, W%RealW% H%RealH% NoActivate
		this.WinMove(this.PN_hwnd, position)
		global windowID := ("ahk_id "this.PN_hwnd) ; window id
		this.nshow()
	}

	ndestroy() {
		this.winfade(windowID,0,50) ; fades the box out
		Gui, Notify: Destroy
		return
	}

	nhide(){ ; called by the timer
		showTimer:
		SetTimer, showTimer, Off ; turn off the timer
		global windowID
		winfade(windowID,0,50) ; fades the box out
		return
	}

	nshow(){
		w:= ("ahk_id "+this.PN_hwnd)

		WinGet,s,Transparent,%w% ; makes the notification visible
		s:=(s="")?255:s ;prevent trans unset bug
		WinSet,Transparent,210,%w%

		Closetick := this.showTime*1000
		SetTimer, showTimer, % Closetick ; reset the hide timer
		return
	}

	updateMessage(message) {
		this.pn_msg := message
		GuiControl, Notify: Text, pn_msg, % this.pn_msg
		return
	}

	WinMove(hwnd,position) {
	   SysGet, Mon, MonitorWorkArea
	   WinGetPos,ix,iy,w,h, ahk_id %hwnd%
	   x := InStr(position,"l") ? MonLeft : InStr(position,"hc") ?  (MonRight-w)/2 : InStr(position,"r") ? MonRight - w : ix
	   y := InStr(position,"t") ? MonTop : InStr(position,"vc") ?  (MonBottom-h)/2 : InStr(position,"b") ? MonBottom - h : iy
	   WinMove, ahk_id %hwnd%,,x,y
	   return
	}
}

; winfade must be global for the timer to use it
winfade(w:="",t:=128,i:=1,d:=10) {
	w:=(w="")?("ahk_id " WinActive("A")):w
	t:=(t>255)?255:(t<0)?0:t
	WinGet,s,Transparent,%w%
	s:=(s="")?255:s ;prevent trans unset bug
	WinSet,Transparent,%s%,%w%
	i:=(s<t)?abs(i):-1*abs(i)
	while(k:=(i<0)?(s>t):(s<t)&&WinExist(w)) {
		WinGet,s,Transparent,%w%
		s+=i
		WinSet,Transparent,%s%,%w%
		sleep %d%
	}
	return
}

#NoEnv
#SingleInstance force ; only lets one run
SetBatchLines, -1

socket := new Example("ws://192.168.1.33:81") ; !!! Update this !!!
connected := 0 ;  state variable for server connection

incrementAmount := 10 ; how finely you adjust the color, speed, and brightness per keypress
maxColor := 255
maxMode := 56 ; the maximum mode id number

red := 127
green := 127
blue := 127

brightness := 127
speed := 127
mode := 1

^#Numpad4:: ; switch modes down
	mode := mode - 1
	if(mode < 0)
		mode := maxMode
	cmd = /%mode% ; / is the set effect mode command
	TrayTip, , Mode: %mode%
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#Numpad6:: ; switch modes up
	mode := mode + 1
	if(mode > maxMode)
		mode := 0
	TrayTip, , Mode: %mode%
	cmd = /%mode%
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#Numpad5:: ; turn off
	TrayTip, , Leds are off
	sendCmd("=off") ; = is the set control command
	return

^#Numpad0:: ; turn on to static color
	TrayTip, , Leds are on
	cmd := "*"+toHexColor(red,green,blue) ; * is the set all command
	sendCmd(cmd) ; = is the set control command
	return

^#NumpadSub:: ; decreases brightness
	brightness := brightness - incrementAmount
	if(brightness < 0)
		brightness := 0
	cmd = `%%brightness% ;  % is the set brightness mode command. ` is the escape character
	TrayTip, , Brightness: %brightness%
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#NumpadAdd:: ; increases brightness
	brightness := brightness + incrementAmount
	if(brightness > maxColor)
		brightness := maxColor
	cmd = `%%brightness% ; % is the set brightness mode command
	TrayTip, , Brightness: %brightness%
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#NumpadDiv:: ; decreases speed
	speed := speed - incrementAmount
	if(speed < 0)
		speed := 0
	cmd = ?%speed% ;  ? is the set brightness mode command
	TrayTip, , Speed: %speed%
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#NumpadMult:: ; increases speed
	speed := speed + incrementAmount
	if(speed > maxColor)
		speed := maxColor
	cmd = ?%speed% ;  ? is the set brightness mode command
	TrayTip, , Speed: %speed%
	sendCmd(cmd)
	; MsgBox %cmd%
	return

^#Numpad7:: ; increase red amount
	red := red + incrementAmount
	if(red > maxColor)
		red := maxColor

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	TrayTip, , Red: %red%
	sendCmd(cmd)

	return

^#Numpad1:: ; decrease red amount
	red := red - incrementAmount
	if(red < 0)
		red := 0

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	TrayTip, , Red: %red%
	sendCmd(cmd)

	return

^#Numpad8:: ; increase green amount
	green := green + incrementAmount
	if(green > maxColor)
		green := maxColor

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	TrayTip, , Green: %green%
	sendCmd(cmd)

	return

^#Numpad2:: ; decrease green amount
	green := green - incrementAmount
	if(green < 0)
		green := 0

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	TrayTip, , Green: %green%
	sendCmd(cmd)

	return

^#Numpad9:: ; increase blue amount
	blue := blue + incrementAmount
	if(blue > maxColor)
		blue := maxColor

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	TrayTip, , Blue: %blue%
	sendCmd(cmd)

	return

^#Numpad3:: ; decrease blue amount
	blue := blue - incrementAmount
	if(blue < 0)
		blue := 0

	cmd := "#"+toHexColor(red,green,blue) ; # is the set main color command
	; MsgBox %cmd%
	TrayTip, , Blue: %blue%
	sendCmd(cmd)

	return

sendCmd(cmd)
{
	global connected
	global socket
	; MsgBox %connected%
	if( connected )
	{
		socket.Send(cmd)
		; MsgBox %cmd%
	}
	else
		TrayTip, , Not connected to server!
}

toHexColor(r, g, b)
{
	SetFormat, IntegerFast, hex ; To print values as hexadecimal
	colorNum := r*65536 + g*256 + b
	color = 000000%colorNum% ; convert to string and add preceeding zeros
	color := StrReplace(color, "0x") ; replace the 0x from the middle
	color := SubStr( color, -5 ) ; get last 6 characters

	SetFormat, IntegerFast, dec ; sets the print mode back to decimal
	return ""+color ;
}

class Example extends WebSocket
{
	OnOpen(Event)
	{
		TrayTip, ,Connection established to led controller!
		; InputBox, Data, WebSocket, Enter some text to send through the websocket.
		; this.Send(Data)
		global connected := 1
	}

	OnMessage(Event)
	{
		; MsgBox, % "Received Data: " Event.data
		; this.Close()
	}

	OnClose(Event)
	{
		TrayTip, , Websocket Closed
		global connected := 0
		this.Disconnect()
		ExitApp
	}

	OnError(Event)
	{
		TrayTip, , Websocket Error
		global connected := 0
		this.Close()
	}

	__Delete()
	{
		TrayTip, , Exiting
		ExitApp
	}
}

class WebSocket
{
	__New(WS_URL)
	{
		static wb

		; Create an IE instance
		Gui, +hWndhOld
		Gui, New, +hWndhWnd
		this.hWnd := hWnd
		Gui, Add, ActiveX, vWB, Shell.Explorer
		Gui, %hOld%: Default

		; Write an appropriate document
		WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible'"
		. "content='IE=edge'><body></body>")
		while (WB.ReadyState < 4)
			sleep, 50
		this.document := WB.document

		; Add our handlers to the JavaScript namespace
		this.document.parentWindow.ahk_savews := this._SaveWS.Bind(this)
		this.document.parentWindow.ahk_event := this._Event.Bind(this)
		this.document.parentWindow.ahk_ws_url := WS_URL

		; Add some JavaScript to the page to open a socket
		Script := this.document.createElement("script")
		Script.text := "ws = new WebSocket(ahk_ws_url);`n"
		. "ws.onopen = function(event){ ahk_event('Open', event); };`n"
		. "ws.onclose = function(event){ ahk_event('Close', event); };`n"
		. "ws.onerror = function(event){ ahk_event('Error', event); };`n"
		. "ws.onmessage = function(event){ ahk_event('Message', event); };"
		this.document.body.appendChild(Script)
	}

	; Called by the JS in response to WS events
	_Event(EventName, Event)
	{
		this["On" EventName](Event)
	}

	; Sends data through the WebSocket
	Send(Data)
	{
		; MsgBox %Data%
		this.document.parentWindow.ws.send(Data)
	}

	; Closes the WebSocket connection
	Close(Code:=1000, Reason:="")
	{
		this.document.parentWindow.ws.close(Code, Reason)
	}

	; Closes and deletes the WebSocket, removing
	; references so the class can be garbage collected
	Disconnect()
	{
		if this.hWnd
		{
			this.Close()
			Gui, % this.hWnd ": Destroy"
			this.hWnd := False
		}
	}
}


支持映射小键盘

支持映射小键盘

global __vimLastAction

; 初始化class_Vim
vim_Init:
    #UseHook
    SetKeyDelay, -1
return

; 用于热键映射
vim_Key:
    vim.Key()
return

; 用于热键超时
vim_TimeOut:
    vim.IsTimeOut()
return

; 用于Count计数
Internal:
    __v.SetPlugin("Internal", "Array", "0.1", "内置插件")
    __v.SetAction("<1>", "数字 1")
    __v.SetAction("<2>", "数字 2")
    __v.SetAction("<3>", "数字 3")
    __v.SetAction("<4>", "数字 4")
    __v.SetAction("<5>", "数字 5")
    __v.SetAction("<6>", "数字 6")
    __v.SetAction("<7>", "数字 7")
    __v.SetAction("<8>", "数字 8")
    __v.SetAction("<9>", "数字 9")
    __v.SetAction("<0>", "数字 0")
    __v.SetAction("<>", "空操作")
    __v.SetAction("<Pass>", "空操作")
    __v.SetAction("<Default>", "恢复默认")
    __v := ""
return

<1>:
<2>:
<3>:
<4>:
<5>:
<6>:
<7>:
<8>:
<9>:
<0>:
<>:
<Pass>:
<Default>:
return

Class_vim()
{
    global __v
    GoSub, vim_Init
    __v := v := new __vim
    __v.LoadPlugin("Internal")
    return v
}

GetLastAction()
{
    return __vimLastAction
}

ShowInfo()
{
    Global vim
    obj := vim.GetMore(true)
    winObj := vim.GetWin(vim.LastFoundWin)
    if winObj.Count
        np .= winObj.Count
    Loop, % obj.MaxIndex()
    {
        act := vim.GetAction(obj[A_Index]["Action"])
        key := RegExReplace(obj[A_Index]["key"], "<S-(.*)>", "$1")

        ; Type = 1 : Function
        if (act.Type = 1)
        {
            ActionDescList := act.Comment
            np .= key "`t" %ActionDescList%[Key] "`n"
        }
        else
        {
            np .= key "`t" act.Comment "`n"
        }

        if (A_Index = 1)
            np .= "=====================`n"
    }
    MouseGetPos, posx, posy, A
    posx += 40
    posy += 40
    Tooltip, %np%, %posx%, %posy%
}

HideInfo()
{
    ; 当屏幕有非快捷键补全帮助信息时，不清理
    Global showToolTipStatus
    if (!showToolTipStatus)
    {
        Tooltip
    }
}

; 定义数据结构
; Class __vim {{{1
Class __vim
{
    __new()
    {
        this.PluginList := []
        this.WinList    := []
        this.WinInfo    := []
        this.ActionList := []
        this.ActionFromPlugin := []
        this.ExcludeWinList := []
        this.winGlobal  := new __win
        this.ErrorCode  := 0
        tihs.ActionFromPluginName := ""
        this.LastFoundWin := ""
    }

    ; LoadPlugin(PluginName) {{{2
    ; 用于注册插件
    LoadPlugin(PluginName)
    {
        if this.PluginList[PluginName]
            return this.PluginList[PluginName]
        p := new __Plugin(PluginName)
        this.PluginList[PluginName] := p
        back := this.ActionFromPluginName
        this.ActionFromPluginName := PluginName
        p.CheckSub()
        if p.Error
        {
            MsgBox load plugin "%PluginName%" error
            this.ActionFromPluginName := back
        }
    }

    ; SetPlugin(PluginName, Author = "", Ver = "", Comment = "") {{{2
    ; 设置Plugin信息
    SetPlugin(PluginName, Author = "", Ver = "", Comment = "")
    {
        p := this.PluginList[PluginName]
        p.Author := Author
        p.Ver := Ver
        p.Comment := Comment
    }

    ; GetPlugin(PluginName) {{{2
    ; 获取Plugin信息
    GetPlugin(PluginName)
    {
        return this.PluginList[PluginName]
    }

    ; Comment(Action, Desc, Complex = 1) {{{2
    ; 兼容老版本的vimcore.ahk
    Comment(Action, Desc = "", Complex = 1)
    {
        act := this.SetAction(Action, Desc)
        if not complex ; 不允许多次运行
            act.SetMaxTimes(1)
        return act
    }

    ; SetAction(Name, Comment) {{{2
    ; 用于为Action(动作)进行注释
    SetAction(Name, Comment = "")
    {
        if this.ActionList[Name]
            ra := This.ActionList[Name]
        else
            ra := This.ActionList[Name] := new __Action(Name, Comment)
        This.ActionFromPlugin[Name] := this.ActionFromPluginName
        return ra
    }

    ; GetAction(Name) {{{2
    ; 获取Action的信息
    GetAction(Name)
    {
        return this.ActionList[Name]
    }

    ; SetWin(winName, class, filepath, title) {{{2
    ; 添加win成员
    SetWin(winName, class, filepath = "", title = "")
    {
        if this.WinList[winName]
            rw := this.WinList[winName]
        else
            rw := this.WinList[winName] := new __win(class, filepath, title)

        if (class != "")
        {
            this.WinInfo["class`t"class]       := winName
        }

        if (filepath != "")
        {
            this.WinInfo["filepath`t"filepath] := winName
        }

        if (title != "")
        {
            this.WinInfo["title`t"title]       := winName
        }

        return rw
    }

    ; GetWin(winName) {{{2
    GetWin(winName = "")
    {
        if strlen(winName)
            return this.WinList[winName]
        else
            return this.winGlobal
    }

    ; CheckWin() {{{2
    CheckWin()
    {
        /*
        WinGetTitle, t, A
        if Strlen(winName := this.WinInfo["title`t"t])
            return winName
        */

        WinGet, f, ProcessName, A
        if Strlen(winName := this.WinInfo["filepath`t"f])
            return winName
        WinGetClass, c, A
        if Strlen(winName := this.WinInfo["class`t"c])
            return winName
    }

    ; mode(mode, win = "") {{{2
    ; 兼容老版本的vimcore.ahk
    mode(mode, win = "")
    {
        if not IsObject(this.GetWin(win))
            this.SetWin(win, win)
        return this.SetMode(mode, win)
    }

    ; SetMode(modeName, winName) {{{2
    SetMode(modeName, winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.ChangeMode(modeName)
    }

    ; SetModeFunction(func, modeName, winName = "") {{{2
    SetModeFunction(func, modeName, winName = "")
    {
        winObj := This.GetWin(winName)
        modeObj := winObj.modeList[modeName]
        modeObj.modeFunction := func
    }

    ; GetMode(winName) {{{2
    ; 获取指定win中当前的模式
    GetMode(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.modeList[winObj.ExistMode()]
    }

    ; GetInputState {{{2
    ; 0：英文 1：中文
    GetInputState(WinTitle = "A")
    {
        ControlGet, hwnd, HWND, , , %WinTitle%
        if (A_Cursor = "IBeam")
            return 1
        if (WinActive(WinTitle))
        {
            ptrSize := !A_PtrSize ? 4 : A_PtrSize
            VarSetCapacity(stGTI, cbSize := 4 + 4 + (PtrSize * 6) + 16, 0)
            NumPut(cbSize, stGTI, 0, "UInt")   ;   DWORD   cbSize;
            hwnd := DllCall("GetGUIThreadInfo", Uint, 0, Uint, &stGTI)
                             ? NumGet(stGTI, 8 + PtrSize, "UInt") : hwnd
        }
        return DllCall("SendMessage"
            , UInt, DllCall("imm32\ImmGetDefaultIMEWnd", Uint, hwnd)
            , UInt, 0x0283  ;Message : WM_IME_CONTROL
            , Int, 0x0005  ;wParam  : IMC_GETOPENSTATUS
            , Int, 0)      ;lParam  : 0
    }

    ; BeforeActionDo(Function, winName = "") {{{2
    BeforeActionDo(Function, winName = "")
    {
        winObj  := this.GetWin(winName)
        winObj.BeforeActionDoFunc := Function
    }

    ; AfterActionDo(Function, winName = "") {{{2
    AfterActionDo(Function, winName = "")
    {
        winObj := this.GetWin(winName)
        winObj.AfterActionDoFunc := Function
    }

    ; SetMaxCount(Int, winName) {{{2
    SetMaxCount(Int, winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.MaxCount := int
    }

    ; GetMaxCount(winName) {{{2
    GetMaxCount(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.MaxCount
    }

    ; SetCount(int, winName) {{{2
    SetCount(int, winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.Count := int
    }

    ; GetCount(winName) {{{2
    GetCount(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.Count
    }

    ; SetTimeOut(Int, winName) {{{2
    SetTimeOut(Int, winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.TimeOut := int
    }

    ; GetTimeOut(winName) {{{2
    GetTimeOut(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.TimeOut
    }

    ; Map(keyName, Action, winName) {{{2
    ; 例 Map("<C-A>jjj", <a>, "TC")
    ; Map("<nowait>jk", <b>, "TC")
    Map(keyName, Action, winName = "")
    {
        if not this.GetAction(Action)
        {
            if Islabel(action)
                this.SetAction(Action, Action)
            else
            {
                MsgBox % "map " keyname " to "  action  " error"
                this.ErrorCode := "MAP_ACTION_ERROR"
                return
            }
        }

        winObj := This.GetWin(winName)
        modeObj := this.GetMode(winName)
        Class := winObj.class
        filepath := winObj.filepath
        if winName
        {
            if strlen(filepath)
                Hotkey, IfWinActive, ahk_exe %filepath%
            else
                Hotkey, IfWinActive, ahk_class %class%
        }
        else
            Hotkey, IfWinActive
        keyName := RegExReplace(keyName, "i)<nowait>", "", nowait)
        keyName := RegExReplace(keyName, "i)<super>", "", super)
        newkeyName := keyName
        keyIndex := 0
        Loop
        {
            if not strlen(newkeyName)
                break
            else
            {
                saveMoreKey .= thisKey
                modeobj.SetMoreKey(saveMoreKey)
            }

            if RegExMatch(newkeyName, "P)^<.+?>", m)
            {
                thisKey := SubStr(newkeyName, 1, m)
                newkeyName := SubStr(newkeyName, m+1)
            }
            else
            {
                thisKey := SubStr(newkeyName, 1, 1)
                newKeyName := SubStr(newkeyName, 2)
            }

            if RegExMatch(thisKey, "^([A-Z])$", m)
                thisKey := "<S-" m ">"
            SaveKeyName .= thisKey
            key := this.Convert2AHK(thisKey)
            this.Debug.Add("Map: " thiskey " to: " Action)

            if (Action <> "<Default>")
            {
                Hotkey, %Key%, vim_Key, On, UseErrorLevel
            }
            else
            {
                Hotkey, %Key%, Off, UseErrorLevel
            }

            if ErrorLevel
            {
                Msgbox % KeyName "`n" key "`n映射错误"
                this.ErrorCode := "MAP_KEY_ERROR"
                return
            }
            else
            {
                winObj.SuperKeyList[key] := super
                winObj.KeyList[key] := true
                keyIndex++
            }
        }

        modeObj.SetKeyMap(SavekeyName, Action)
        modeObj.SetNoWait(SavekeyName, nowait)
        return false
    }

    ; ExcludeWin(winName = "", Bold = true) {{{2
    ExcludeWin(winName = "", Bold = true)
    {
        this.ExcludeWinList[winName] := Bold
    }

    ; Toggle(winName) {{{2
    Toggle(winName)
    {
        winObj := this.GetWin(winName)
        winObj.Status := !winObj.Status
        this.Control(winObj.Status, winName)
        return winObj.Status
    }

    ; Control(bold, winName) {{{2
    Control(bold, winName = "", all=false)
    {
        local class

        winObj := this.GetWin(winName)
        class := winObj.Class
        filepath := winObj.filepath
        if Strlen(filepath)
            Hotkey, IfWinActive, ahk_exe %filepath%
        else if Strlen(class)
            Hotkey, IfWinActive, ahk_class %class%
        else
            Hotkey, IfWinActive

        this.Debug.Add("===== Control End  =====")
        for i , k in winObj.KeyList
        {
            if winObj.SuperKeyList[i] And (not all)
                Continue
            this.Debug.Add("class: " class "`tkey: " i "`tcontrol: " bold)
            if bold
                Hotkey, %i%, vim_Key, On
            else
                Hotkey, %i%, vim_Key, off
            winObj.KeyList[i] := bold
        }
        this.Debug.Add("===== Control Start =====")
    }

    ; Copy(winName1, winName2) {{{2
    ; 复制winName1到winName2, winName2按class/filepath/title定义
    Copy(winName1, winName2, class, filepath = "", title = "")
    {
        this.debug.Add("Copy>> "winName1 "`t"  winName2 "`t" class)
        win1 := This.GetWin(winName1)
        win2 := this.SetWin(winName2, class, filepath, title)
        win2.class := class
        win2.filepath := filepath
        win2.title := title
        win2.KeyList := win1.KeyList
        win2.SuperKeyList := win1.SuperKeyList
        win2.modeList  := win1.modeList
        win2.mode      := win1.mode
        win2.LastKey   := win1.LastKey
        win2.KeyTemp   := win1.KeyTemp
        win2.MaxCount  := win1.MaxCount
        win2.Count     := win1.Count
        win2.TimeOut   := win1.TimeOut
        win2.Info      := win1.Info
        win2.BeforeActionDoFunc := win1.BeforeActionDoFunc
        win2.AfterActionDoFunc  := win1.AfterActionDoFunc
        win2.ShowInfoFunc := win1.ShowInfoFunc
        win2.HideInfoFunc := win1.HideInfoFunc
        this.Control(Bold := true, winName2, all := true)
        return win2
    }

    ; CopyMode(win, mode, to) {{{2
    ; 在当前的Win中复制一个模式
    CopyMode(winName, fromMode, toMode)
    {
        winObj := This.GetWin(winName)
        winObj.mode := modeName
        winObj.KeyTemp := ""
        winObj.Count := 0
        from := winObj.modeList[fromMode]
        from.name  := toMode
        winObj.modeList[toMode] := from
        return from
    }

    ; Delete(winName) {{{2
    Delete(winName = "")
    {
        this.Control(false, winName, all := true)
        this.WinList[winName] := ""
    }

    ; GetMore() {{{2
    GetMore(obj = false)
    {
        rt_obj := []
        winObj  := this.GetWin(this.LastFoundWin)
        modeObj := this.GetMode(this.LastFoundWin)
        if Strlen(winObj.KeyTemp)
        {
            r := winObj.KeyTemp "`n"
            rt_obj[1] := {"key":winObj.KeyTemp, "Action":modeObj.GetKeyMap(winObj.KeyTemp)}
            m := "i)^" this.ToMatch(winObj.KeyTemp) ".+"
            for i , k in modeObj.keymapList
            {
                if RegExMatch(i, m)
                {
                    r .= i "`t" modeObj.GetKeyMap(i) "`n"
                    rt_obj[rt_obj.MaxIndex()+1] := {"key":i, "Action":modeObj.GetKeyMap(i)}
                }
            }

            if obj
                return rt_obj
            else
                return r
        }
        else
            if winObj.count
                return winObj.count
    }

    ; Clear() {{{2
    Clear(winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.KeyTemp := ""
        winObj.Count := 0
    }

    ; Key() {{{2
    Key()
    {
        ; 获取winName
        winName := this.CheckWin()
        ; 获取当前的热键
        k := this.CheckCapsLock(this.Convert2VIM(A_ThisHotkey))
        ; 如果winName在排除窗口中，直接发送热键
        if this.ExcludeWinList[winName]
        {
            Send, % this.Convert2AHK(k, ToSend := true)
            return
        }
        ; 如果当前热键在当前winName无效，判断全局热键中是否有效？
        winObj := this.GetWin(winName)
        if Not winObj.KeyList[A_thisHotkey]
        {
            winObj := this.GetWin()
            if Not winObj.KeyList[A_thisHotkey]
            {
                ; 无效热键，按普通键输出
                Send, % this.Convert2AHK(k, ToSend := true)
                return
            }
            else
                winName := ""
        }

        this.LastFoundWin := winName
        ; 执行在判断热键前的函数, 如果函数返回true，按普通键输出
        if IsFunc(f := winObj.BeforeActionDoFunc)
        {
            if %f%()
            {
                Send, % this.Convert2AHK(k, ToSend := true)
                return
            }
        }

        ; 获取当前模式对应的对象
        modeObj := this.GetMode(winName)
        ; 把当前热键添加到热键缓存中, 并设置最后热键为k
        winObj.KeyTemp .= winObj.LastKey := k
        this.Debug.Add(" win: " winname "`tHotkey: " k)

        /*
            if winObj.Count
                this.Debug.Add(" [" winName "`t热键:" winObj.Count winObj.KeyTemp)
            else
                this.Debug.Add(" [" winName "]`t热键:" winObj.KeyTemp)
        */

        ; 热键缓存是否有对应的Action?
        ; 判断是否有更多热键, 如果当前具有<nowait>设置，则无视更多热键
        if modeObj.GetMoreKey(winObj.KeyTemp) And (Not modeObj.GetNoWait(winObj.KeyTemp))
        {
            ; 启用TimeOut
            if strlen(modeObj.GetKeymap(winObj.KeyTemp))
                if tick := winObj.TimeOut
                    Settimer, Vim_TimeOut, %tick%

            winObj.ShowMore()
            ; 执行在判断热键后的函数, 如果函数返回true，按普通键输出
            if IsFunc(f := winObj.AfterActionDoFunc)
            {
                if %f%()
                    Send, % this.Convert2AHK(k, ToSend := true)
            }

            return
        }

        ; 如果没有更多，热键缓存是否有对应的Action?
        if strlen(actionName := modeObj.GetKeymap(winObj.KeyTemp))
        {
            actObj := this.GetAction(actionName)
            if (actObj.Type = 0) And RegExMatch(actionName , "^<(\d)>$", m)
            {
                ; 数字则进行累加
                winObj.Count := winObj.Count * 10 + m1
                if winObj.MaxCount And winObj.Count > winObj.MaxCount
                    winObj.Count := winObj.MaxCount

                winObj.KeyTemp := ""
                winObj.ShowMore()
                if IsFunc(f := winObj.AfterActionDoFunc)
                    %f%()
                return
            }
            else
            {
                ; 非数字则直接运行
                ;this.Debug.Add("act: " actionName "`tLK: " winObj.KeyTemp)
                __vimLastAction := {"LastKey":k, "winName":winName, "ActionName":ActionName, "KeyTemp":winObj.KeyTemp, "Count":winObj.Count}
                Settimer, Vim_TimeOut, Off
                actObj.Do(winObj.Count)
                winObj.Count := 0
            }
        }
        else
        {
            Settimer, Vim_TimeOut, Off
            ; 如果没有，按普通键输出
            if strlen(actionName := modeObj.GetKeymap(winObj.LastKey))
            {
                actObj := this.GetAction(actionName)
                __vimLastAction := {"LastKey":k, "WinName":winName, "ActionName":ActionName, "KeyTemp":winObj.KeyTemp, "Count":winObj.Count}
                actObj.Do(winObj.Count)
                winObj.Count := 0
            }
            else
            {
                Send, % this.Convert2AHK(k, ToSend := true)
                winObj.Count := 0
            }
        }

        winObj.KeyTemp := ""
        winObj.HideMore()
        ; 执行在判断热键后的函数, 如果函数返回true，按普通键输出
        if IsFunc(f := winObj.AfterActionDoFunc)
        {
            if %f%()
                Send, % this.Convert2AHK(k, ToSend := true)
            ; %f%() ;直接运行
        }
        return
    }

    ; IsTimeOut() {{{2
    IsTimeOut()
    {
        winObj  := this.GetWin(this.LastFoundWin)
        modeObj := this.GetMode(this.LastFoundWin)
        act := this.GetAction(modeObj.GetKeyMap(winObj.KeyTemp))
        if act
        {
            winObj.HideMore()
            __vimLastAction := {"LastKey":k, "WinName":winName, "ActionName":ActionName, "KeyTemp":winObj.KeyTemp, "Count":winObj.Count}
            act.Do(winObj.Count)
            winObj.Count := 0
            winObj.KeyTemp := ""
            ; 执行在判断热键后的函数, 如果函数返回true，按普通键输出
            if IsFunc(f := winObj.AfterActionDoFunc)
            {
                if %f%()
                Send, % this.Convert2AHK(k, ToSend := true)
            }
            Settimer, Vim_TimeOut, Off
        }
    }

    ; Debug(Bold) {{{2
    Debug(Bold, history = false)
    {
        if Bold
        {
            this.Debug := new __vimDebug(history)
            this.Debug.var(this)
        }
        else
        {
            GUI, vimDebug:Destroy
            this.Debug := ""
        }
    }

    ; Convert2VIM(key) {{{2
    ; 将AHK热键名转换为类VIM的热键名
    ; 例 Convert2VIM("f1")
    Convert2VIM(key)
    {
        if RegExMatch(key, "^[A-Z]$")
            return "<S-" this.Upper(key) ">"
		if RegExMatch(key, "i)^(F[0-9]{1,2}|Numpad.*)$")
            return "<" key ">"
        if RegExMatch(key, "i)^(AppsKey|Tab|Enter|Space|Home|End|CapsLock|ScrollLock|Up|Down|Left|Right|PgUp|PgDn|Pause)$")
            return "<" key ">"
        if RegExMatch(key, "i)^(BS|BackSpace)$")
            return "<BS>"
        if RegExMatch(key, "i)^(Esc|Escape)$")
            return "<Esc>"
        if RegExMatch(key, "i)^(Ins|Insert)$")
            return "<Insert>"
        if RegExMatch(key, "i)^(Del|Delete)$")
            return "<Delete>"
        if RegExMatch(key, "i)^PrintScreen$")
            return "<PrtSc>"
        if RegExMatch(key, "i)^shift\s&\s(.*)", m) or RegExMatch(key, "^\+(.*)", m)
            return "<S-" this.Upper(m1) ">"
        if RegExMatch(key, "i)^lshift\s&\s(.*)", m) or RegExMatch(key, "^<\+(.*)", m)
            return "<LS-" this.Upper(m1) ">"
        if RegExMatch(key, "i)^rshift\s&\s(.*)", m) or RegExMatch(key, "^>\+(.*)", m)
            return "<RS-" this.Upper(m1) ">"
        if RegExMatch(key, "i)^Ctrl\s&\s(.*)", m) or RegExMatch(key, "^\^(.*)", m)
            return "<c-" m1 ">"
        if RegExMatch(key, "i)^lctrl\s&\s(.*)", m) or RegExMatch(key, "^<\^(.*)", m)
            return "<lc-" m1 ">"
        if RegExMatch(key, "i)^rctrl\s&\s(.*)", m) or RegExMatch(key, "^>\^(.*)", m)
            return "<rc-" m1 ">"
        if RegExMatch(key, "i)^alt\s&\s(.*)", m) or RegExMatch(key, "^\!(.*)", m)
            return "<a-" m1 ">"
        if RegExMatch(key, "i)^lalt\s&\s(.*)", m) or RegExMatch(key, "^<\!(.*)", m)
            return "<la-" m1 ">"
        if RegExMatch(key, "i)^ralt\s&\s(.*)", m) or RegExMatch(key, "^>\!(.*)", m)
            return "<ra-" m1 ">"
        if RegExMatch(key, "i)^lwin\s&\s(.*)", m) or RegExMatch(key, "^#(.*)", m)
            return "<w-" m1 ">"
        if RegExMatch(key, "i)^space\s&\s(.*)", m)
            return "<sp-" m1 ">"
        if RegExMatch(key, "i)^alt$")
            return "<Alt>"
        if RegExMatch(key, "i)^ctrl$")
            return "<Ctrl>"
        if RegExMatch(key, "i)^shift$")
            return "<Shift>"
        if RegExMatch(key, "i)^lwin$")
            return "<Win>"
        return key
    }

    Upper(k)
    {
        StringUpper, k, k
        return k
    }

    ; Convert2AHK(key, ToSend=false) {{{2
    ; 将类VIM的热键名转换为AHK热键名
    ; 例 Convert2AHK("<F1>")
    Convert2AHK(key, ToSend=false)
    {
        this.CheckCapsLock(key)
        if RegExMatch(key, "^<.*>$")
        {
            key := SubStr(key, 2, strlen(key)-2)
			if RegExMatch(key, "i)^(F[0-9]{1,2}|Numpad.*)$")
                return ToSend ? "{" key "}" : key
            if RegExMatch(key, "i)^(AppsKey|Tab|Enter|Space|Home|End|CapsLock|ScrollLock|Up|Down|Left|Right|PgUp|PgDn|BS|ESC|Insert|Delete|Pause)$")
                return ToSend ? "{" key "}" : key
            if RegExMatch(key, "i)^PrtSc$")
                return ToSend ? "{PrintScreen}" : "PrintScreen"
            if RegExMatch(key, "i)^alt$")
                return ToSend ? "{!}" : "alt"
            if RegExMatch(key, "i)^ctrl$")
                return ToSend ? "{^}" : "ctrl"
            if RegExMatch(key, "i)^shift$")
                return ToSend ? "{+}" : "shift"
            if RegExMatch(key, "i)^win$")
                return ToSend ? "{#}" : "lwin"
            if RegExMatch(key, "<LT>")
                return "<"
            if RegExMatch(key, "<RT>")
                return ">"
            if RegExMatch(key, "i)^S\-(.*)", m)
                return ToSend ? "+" this.CheckToSend(m1) : "+" m1
            if RegExMatch(key, "i)^LS\-(.*)", m)
                return ToSend ? "+" this.CheckToSend(m1) : "<+" m1
            if RegExMatch(key, "i)^RS-(.*)", m)
                return ToSend ? "+" this.CheckToSend(m1) : ">+" m1
            if RegExMatch(key, "i)^C\-(.*)", m)
                return ToSend ? "^" this.CheckToSend(m1) : "^" m1
            if RegExMatch(key, "i)^LC\-(.*)", m)
                return ToSend ? "^" this.CheckToSend(m1) : "<^" m1
            if RegExMatch(key, "i)^RC\-(.*)", m)
                return ToSend ? "^" this.CheckToSend(m1) : ">^" m1
            if RegExMatch(key, "i)^A\-(.*)", m)
                return ToSend ? "!" this.CheckToSend(m1) : "!" m1
            if RegExMatch(key, "i)^LA\-(.*)", m)
                ; 暂时先不区分左右键
                return ToSend ? "!" this.CheckToSend(m1): "<!" m1
                ;return ToSend ? "{lalt down}" this.CheckToSend(m1) "{lalt up}": "<!" m1
            if RegExMatch(key, "i)^RA\-(.*)", m)
                return ToSend ? "!" this.CheckToSend(m1) : ">!" m1
            if RegExMatch(key, "i)^w\-(.*)", m)
                return ToSend ? "#" this.CheckToSend(m1) : "#" m1
            if RegExMatch(key, "i)^SP\-(.*)", m)
                return ToSend ? "{space}" this.CheckToSend(m1) : "space & " m1
        }
        else
            return key
    }

    CheckToSend(key)
    {
        if RegExMatch(key, "i)^(F[0-9]{1,2}|Numpad.*)$")
            return "{" key "}"
        if RegExMatch(key, "i)^(AppsKey|Tab|Enter|Space|Home|End|CapsLock|ScrollLock|Up|Down|Left|Right|PgUp|PgDn|BS|ESC|Insert|Delete|Pause)$")
            return "{" key "}"
        if RegExMatch(key, "i)^PrtSc$")
            return "{PrintScreen}"
        if RegExMatch(key, "i)^alt$")
            return "{!}"
        if RegExMatch(key, "i)^ctrl$")
            return "{^}"
        if RegExMatch(key, "i)^shift$")
            return "{+}"
        if RegExMatch(key, "i)^win$")
            return "{#}"
        if RegExMatch(key, "<LT>")
            return "<"
        if RegExMatch(key, "<RT>")
            return ">"
        return Key
    }

    ; CheckCapsLock(key) {{{2
    ; 检测CapsLock是否按下，返回对应的值
    ; key 的值为类VIM键，如 CheckCapsLock("<S-A>")
    CheckCapsLock(key)
    {
        if GetKeyState("CapsLock", "T")
        {
            if RegExMatch(key, "^[a-z]$")
                return "<S-" key ">"
            if RegExMatch(key, "i)^<S\-([a-zA-Z])>", m)
            {
                StringLower, key, m1
                return key
            }
        }
        return key
    }

    ToMatch(v)
    {
        v := RegExReplace(v , "\+|\?|\.|\*|\{|\}|\(|\)|\||\^|\$|\[|\]|\\", "\$0")
        return RegExReplace(v , "\s", "\s")
    }
}

; Class __win {{{1
Class __win
{
    __new(class = "", filepath = "", title = "")
    {
        this.class     := class
        this.filepath  := filepath
        this.title     := title
        this.KeyList   := []
        this.SuperKeyList   := []
        this.modeList  := []
        this.Status    := true
        this.mode      := ""
        this.LastKey   := ""
        this.KeyTemp   := ""
        this.MaxCount  := 99
        this.Count     := 0
        this.TimeOut   := 0
        this.Info := default_enable_show_info
        this.BeforeActionDoFunc := ""
        this.AfterActionDoFunc   := ""
        this.ShowInfoFunc := "ShowInfo"
        this.HideInfoFunc := "HideInfo"
    }

    ; ChangeMode(modeName) {{{2
    ; 检查模式是否存在，如存在则返回模式对象，如果不存在则新建并返回模式对象
    ChangeMode(modeName)
    {
        this.mode := modeName
        this.KeyTemp := ""
        this.Count := 0
        if not this.modeList[modeName]
            this.modeList[modeName] := new __Mode(modeName)

        modeObj := this.modeList[modeName]
        if IsFunc(func := modeObj.modeFunction)
            %func%()
        return modeObj
    }

    ; ExistMode() {{{2
    ; 获取当前模式名
    ExistMode()
    {
        return this.mode
    }

    ; SetInfo() {{{2
    SetInfo(bold)
    {
        this.info := bold
    }

    ; SetShowInfo() {{{2
    SetShowInfo(func)
    {
        this.ShowInfoFunc := func
    }

    ; SetHideInfo() {{{2
    SetHideInfo(func)
    {
        this.HideInfoFunc := func
    }

    ; ShowMore() {{{2
    ShowMore()
    {
        if IsFunc(f := this.ShowInfoFunc) And this.Info
            %f%()
    }

    ; HideMore() {{{2
    HideMore()
    {
        if IsFunc(f:=this.HideInfoFunc) And this.Info
            %f%()
    }
}

; Class __Mode {{{1
Class __Mode
{
    __new(modeName)
    {
        this.name  := modeName
        this.keymapList := []
        this.keymoreList := []
        this.nowaitList := []
        this.modeFunction := ""
    }

    ; SetKeyMap(key, action) {{{2
    SetKeyMap(key, action)
    {
        this.keymapList[key] := action
    }

    ; GetKeyMap(key) {{{2
    GetKeyMap(key)
    {
        return this.keymapList[key]
    }

    ; DelKeyMap(key) {{{2
    DelKeyMap(key)
    {
        this.keymapList[key] := ""
    }

    ; SetNoWait(key, bold) {{{2
    ; 无视TimeOut执行热键
    SetNoWait(key, bold)
    {
        this.nowaitList[key] := bold
    }

    ; GetNoWait(key) {{{2
    GetNoWait(key)
    {
        return this.nowaitList[key]
    }

    ; SetMoreKey(key) {{{2
    SetMoreKey(key)
    {
        this.keymoreList[key] := true
    }

    ; GetMoreKey(key) {{{2
    GetMoreKey(key)
    {
        return this.keymoreList[key]
    }
}

; Class __Action {{{1
Class __Action
{
    ; Action 有几种类型
    ; 0 代表执行Action对应的Label (默认)
    ; 1 代表执行Function的值对应的函数
    ; 2 代表运行CmdLine对应的值
    ; 3 代表发送HotString对应的文本

    __new(Name, Comment)
    {
        this.Name := Name
        this.Comment := Comment
        this.MaxTimes := 0
        this.Type := 0
        this.Function := ""
        this.CmdLine := ""
        this.HotString := ""
    }

    ; SetFunction(Function) {{{2
    ; 设置Action执行的函数名
    SetFunction(Function)
    {
        this.Function := Function
        this.Type := 1
    }

    ; SetCmdLine(CmdLine) {{{2
    ; 设置Action运行的字符串
    SetCmdLine(CmdLine)
    {
        this.CmdLine := CmdLine
        this.Type := 2
    }

    ; SetHotString(HotString) {{{2
    ; 设置Action发送的文本
    SetHotString(HotString)
    {
        this.HotString := HotString
        this.Type := 3
    }

    ; SetMaxTimes(Times) {{{2
    ; 设置最大运行次数
    SetMaxTimes(Times)
    {
        this.MaxTimes := Times
    }

    ; Do(Times=1) {{{2
    ; 执行Action
    Do(Times=0)
    {
        Times := !Times ? 1 : Times
        if this.MaxTimes And (Times > this.MaxTimes)
            Times := this.MaxTimes
        Loop, %Times%
        {
            if this.Type = 0
            {
                if IsLabel(label := this.Name)
                    GoSub, %label%
            }
            else if this.Type = 1
            {
                if IsFunc(f := this.Function)
                    %f%()
            }
            else if this.Type = 2
            {
                c := this.CmdLine
                Run, %cmd%
            }
            else if This.Type = 3
            {
                t := this.HotString
                Send, %t%
            }
        }
    }
}

; Class __Plugin {{{1
Class __Plugin
{
    __new(PluginName)
    {
        this.PluginName := PluginName
        this.Author := ""
        this.Ver := ""
        this.Comment := ""
    }

    ; CheckSub() {{{2
    CheckSub()
    {
        if IsLabel(p := this.PluginName)
        {
            GoSub, %p%
            this.Error := false
        }
        else
            this.Error := true
    }
}

; Class __vimDebug {{{1
Class __vimDebug
{
    __new(key)
    {
        this.mode := key
        if key
        {
            GUI, vimDebug:Destroy
            GUI, vimDebug:+hwnd_vimdebug -Caption +ToolWindow +Border
            GUI, vimDebug:color, 454545, 454545
            GUI, vimDebug:font, s16 cFFFFFF
            GUI, vimDebug:Add, Edit, x-2 y-2 w400 h60 readonly
            GUI, vimDebug:Show, w378 h56 y600
            WinSet, AlwaysOnTop, On, ahk_id %_vimdebug%
        }
        else
        {
            GUI, vimDebug:font, s12
            GUI, vimDebug:Destroy
            GUI, vimDebug:Add, Edit, x10 y10 w400 h300 readonly
            GUI, vimDebug:Add, Edit, x10 y320 w400 h26 readonly
            GUI, vimDebug:Show, w420 h356
        }
    }

    var(obj)
    {
        this.vim := obj
    }

    Set(v)
    {
        winName := this.vim.CheckWin()
        winObj := this.vim.GetWin(winName)
        if winObj.Count
            k := " 热键缓存:" winObj.Count winObj.KeyTemp
        else
             k := " 热键缓存:" winObj.KeyTemp
        GUI, vimDebug:Default
        GUIControl, , Edit1, %v%
        GUIControl, , Edit2, %k%
    }

    Get()
    {
        GUI, vimDebug:Default
        GUIControlGet, v, , Edit1
        return v
    }

    ; Add(v) {{{2
    Add(v)
    {
        b := this.Get()
        if this.mode
            this.Set(b)
        else
            this.Set(v "`n" b)
    }

    ; Clear() {{{2
    Clear()
    {
        this.Set("")
    }
}

global __vimLastAction

; 初始化class_Vim
vim_Init:
    #UseHook
    SetKeyDelay, -1
return

; 用于热键映射
vim_Key:
    vim.Key()
return

; 用于热键超时
vim_TimeOut:
    vim.IsTimeOut()
return

; 用于Count计数
Internal:
    __v.SetPlugin("Internal", "Array", "0.1", "内置插件")
    __v.SetAction("<1>", "数字 1")
    __v.SetAction("<2>", "数字 2")
    __v.SetAction("<3>", "数字 3")
    __v.SetAction("<4>", "数字 4")
    __v.SetAction("<5>", "数字 5")
    __v.SetAction("<6>", "数字 6")
    __v.SetAction("<7>", "数字 7")
    __v.SetAction("<8>", "数字 8")
    __v.SetAction("<9>", "数字 9")
    __v.SetAction("<0>", "数字 0")
    __v.SetAction("<>", "空操作")
    __v.SetAction("<Pass>", "空操作")
    __v.SetAction("<Default>", "恢复默认")
    __v := ""
return

<1>:
<2>:
<3>:
<4>:
<5>:
<6>:
<7>:
<8>:
<9>:
<0>:
<>:
<Pass>:
<Default>:
return

Class_vim()
{
    global __v
    GoSub, vim_Init
    __v := v := new __vim
    __v.LoadPlugin("Internal")
    return v
}

GetLastAction()
{
    return __vimLastAction
}

ShowInfo()
{
    Global vim
    obj := vim.GetMore(true)
    winObj := vim.GetWin(vim.LastFoundWin)
    if winObj.Count
        np .= winObj.Count
    Loop, % obj.MaxIndex()
    {
        act := vim.GetAction(obj[A_Index]["Action"])
        key := RegExReplace(obj[A_Index]["key"], "<S-(.*)>", "$1")

        ; Type = 1 : Function
        if (act.Type = 1)
        {
            ActionDescList := act.Comment
            np .= key "`t" %ActionDescList%[Key] "`n"
        }
        else
        {
            np .= key "`t" act.Comment "`n"
        }

        if (A_Index = 1)
            np .= "=====================`n"
    }
    MouseGetPos, posx, posy, A
    posx += 40
    posy += 40
    Tooltip, %np%, %posx%, %posy%
}

HideInfo()
{
    ; 当屏幕有非快捷键补全帮助信息时，不清理
    Global showToolTipStatus
    if (!showToolTipStatus)
    {
        Tooltip
    }
}

; 定义数据结构
; Class __vim {{{1
Class __vim
{
    __new()
    {
        this.PluginList := []
        this.WinList    := []
        this.WinInfo    := []
        this.ActionList := []
        this.ActionFromPlugin := []
        this.ExcludeWinList := []
        this.winGlobal  := new __win
        this.ErrorCode  := 0
        tihs.ActionFromPluginName := ""
        this.LastFoundWin := ""
    }

    ; LoadPlugin(PluginName) {{{2
    ; 用于注册插件
    LoadPlugin(PluginName)
    {
        if this.PluginList[PluginName]
            return this.PluginList[PluginName]
        p := new __Plugin(PluginName)
        this.PluginList[PluginName] := p
        back := this.ActionFromPluginName
        this.ActionFromPluginName := PluginName
        p.CheckSub()
        if p.Error
        {
            MsgBox load plugin "%PluginName%" error
            this.ActionFromPluginName := back
        }
    }

    ; SetPlugin(PluginName, Author = "", Ver = "", Comment = "") {{{2
    ; 设置Plugin信息
    SetPlugin(PluginName, Author = "", Ver = "", Comment = "")
    {
        p := this.PluginList[PluginName]
        p.Author := Author
        p.Ver := Ver
        p.Comment := Comment
    }

    ; GetPlugin(PluginName) {{{2
    ; 获取Plugin信息
    GetPlugin(PluginName)
    {
        return this.PluginList[PluginName]
    }

    ; Comment(Action, Desc, Complex = 1) {{{2
    ; 兼容老版本的vimcore.ahk
    Comment(Action, Desc = "", Complex = 1)
    {
        act := this.SetAction(Action, Desc)
        if not complex ; 不允许多次运行
            act.SetMaxTimes(1)
        return act
    }

    ; SetAction(Name, Comment) {{{2
    ; 用于为Action(动作)进行注释
    SetAction(Name, Comment = "")
    {
        if this.ActionList[Name]
            ra := This.ActionList[Name]
        else
            ra := This.ActionList[Name] := new __Action(Name, Comment)
        This.ActionFromPlugin[Name] := this.ActionFromPluginName
        return ra
    }

    ; GetAction(Name) {{{2
    ; 获取Action的信息
    GetAction(Name)
    {
        return this.ActionList[Name]
    }

    ; SetWin(winName, class, filepath, title) {{{2
    ; 添加win成员
    SetWin(winName, class, filepath = "", title = "")
    {
        if this.WinList[winName]
            rw := this.WinList[winName]
        else
            rw := this.WinList[winName] := new __win(class, filepath, title)

        if (class != "")
        {
            this.WinInfo["class`t"class]       := winName
        }

        if (filepath != "")
        {
            this.WinInfo["filepath`t"filepath] := winName
        }

        if (title != "")
        {
            this.WinInfo["title`t"title]       := winName
        }

        return rw
    }

    ; GetWin(winName) {{{2
    GetWin(winName = "")
    {
        if strlen(winName)
            return this.WinList[winName]
        else
            return this.winGlobal
    }

    ; CheckWin() {{{2
    CheckWin()
    {
        /*
        WinGetTitle, t, A
        if Strlen(winName := this.WinInfo["title`t"t])
            return winName
        */

        WinGet, f, ProcessName, A
        if Strlen(winName := this.WinInfo["filepath`t"f])
            return winName
        WinGetClass, c, A
        if Strlen(winName := this.WinInfo["class`t"c])
            return winName
    }

    ; mode(mode, win = "") {{{2
    ; 兼容老版本的vimcore.ahk
    mode(mode, win = "")
    {
        if not IsObject(this.GetWin(win))
            this.SetWin(win, win)
        return this.SetMode(mode, win)
    }

    ; SetMode(modeName, winName) {{{2
    SetMode(modeName, winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.ChangeMode(modeName)
    }

    ; SetModeFunction(func, modeName, winName = "") {{{2
    SetModeFunction(func, modeName, winName = "")
    {
        winObj := This.GetWin(winName)
        modeObj := winObj.modeList[modeName]
        modeObj.modeFunction := func
    }

    ; GetMode(winName) {{{2
    ; 获取指定win中当前的模式
    GetMode(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.modeList[winObj.ExistMode()]
    }

    ; GetInputState {{{2
    ; 0：英文 1：中文
    GetInputState(WinTitle = "A")
    {
        ControlGet, hwnd, HWND, , , %WinTitle%
        if (A_Cursor = "IBeam")
            return 1
        if (WinActive(WinTitle))
        {
            ptrSize := !A_PtrSize ? 4 : A_PtrSize
            VarSetCapacity(stGTI, cbSize := 4 + 4 + (PtrSize * 6) + 16, 0)
            NumPut(cbSize, stGTI, 0, "UInt")   ;   DWORD   cbSize;
            hwnd := DllCall("GetGUIThreadInfo", Uint, 0, Uint, &stGTI)
                             ? NumGet(stGTI, 8 + PtrSize, "UInt") : hwnd
        }
        return DllCall("SendMessage"
            , UInt, DllCall("imm32\ImmGetDefaultIMEWnd", Uint, hwnd)
            , UInt, 0x0283  ;Message : WM_IME_CONTROL
            , Int, 0x0005  ;wParam  : IMC_GETOPENSTATUS
            , Int, 0)      ;lParam  : 0
    }

    ; BeforeActionDo(Function, winName = "") {{{2
    BeforeActionDo(Function, winName = "")
    {
        winObj  := this.GetWin(winName)
        winObj.BeforeActionDoFunc := Function
    }

    ; AfterActionDo(Function, winName = "") {{{2
    AfterActionDo(Function, winName = "")
    {
        winObj := this.GetWin(winName)
        winObj.AfterActionDoFunc := Function
    }

    ; SetMaxCount(Int, winName) {{{2
    SetMaxCount(Int, winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.MaxCount := int
    }

    ; GetMaxCount(winName) {{{2
    GetMaxCount(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.MaxCount
    }

    ; SetCount(int, winName) {{{2
    SetCount(int, winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.Count := int
    }

    ; GetCount(winName) {{{2
    GetCount(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.Count
    }

    ; SetTimeOut(Int, winName) {{{2
    SetTimeOut(Int, winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.TimeOut := int
    }

    ; GetTimeOut(winName) {{{2
    GetTimeOut(winName = "")
    {
        winObj := This.GetWin(winName)
        return winObj.TimeOut
    }

    ; Map(keyName, Action, winName) {{{2
    ; 例 Map("<C-A>jjj", <a>, "TC")
    ; Map("<nowait>jk", <b>, "TC")
    Map(keyName, Action, winName = "")
    {
        if not this.GetAction(Action)
        {
            if Islabel(action)
                this.SetAction(Action, Action)
            else
            {
                MsgBox % "map " keyname " to "  action  " error"
                this.ErrorCode := "MAP_ACTION_ERROR"
                return
            }
        }

        winObj := This.GetWin(winName)
        modeObj := this.GetMode(winName)
        Class := winObj.class
        filepath := winObj.filepath
        if winName
        {
            if strlen(filepath)
                Hotkey, IfWinActive, ahk_exe %filepath%
            else
                Hotkey, IfWinActive, ahk_class %class%
        }
        else
            Hotkey, IfWinActive
        keyName := RegExReplace(keyName, "i)<nowait>", "", nowait)
        keyName := RegExReplace(keyName, "i)<super>", "", super)
        newkeyName := keyName
        keyIndex := 0
        Loop
        {
            if not strlen(newkeyName)
                break
            else
            {
                saveMoreKey .= thisKey
                modeobj.SetMoreKey(saveMoreKey)
            }

            if RegExMatch(newkeyName, "P)^<.+?>", m)
            {
                thisKey := SubStr(newkeyName, 1, m)
                newkeyName := SubStr(newkeyName, m+1)
            }
            else
            {
                thisKey := SubStr(newkeyName, 1, 1)
                newKeyName := SubStr(newkeyName, 2)
            }

            if RegExMatch(thisKey, "^([A-Z])$", m)
                thisKey := "<S-" m ">"
            SaveKeyName .= thisKey
            key := this.Convert2AHK(thisKey)
            this.Debug.Add("Map: " thiskey " to: " Action)

            if (Action <> "<Default>")
            {
                Hotkey, %Key%, vim_Key, On, UseErrorLevel
            }
            else
            {
                Hotkey, %Key%, Off, UseErrorLevel
            }

            if ErrorLevel
            {
                Msgbox % KeyName "`n" key "`n映射错误"
                this.ErrorCode := "MAP_KEY_ERROR"
                return
            }
            else
            {
                winObj.SuperKeyList[key] := super
                winObj.KeyList[key] := true
                keyIndex++
            }
        }

        modeObj.SetKeyMap(SavekeyName, Action)
        modeObj.SetNoWait(SavekeyName, nowait)
        return false
    }

    ; ExcludeWin(winName = "", Bold = true) {{{2
    ExcludeWin(winName = "", Bold = true)
    {
        this.ExcludeWinList[winName] := Bold
    }

    ; Toggle(winName) {{{2
    Toggle(winName)
    {
        winObj := this.GetWin(winName)
        winObj.Status := !winObj.Status
        this.Control(winObj.Status, winName)
        return winObj.Status
    }

    ; Control(bold, winName) {{{2
    Control(bold, winName = "", all=false)
    {
        local class

        winObj := this.GetWin(winName)
        class := winObj.Class
        filepath := winObj.filepath
        if Strlen(filepath)
            Hotkey, IfWinActive, ahk_exe %filepath%
        else if Strlen(class)
            Hotkey, IfWinActive, ahk_class %class%
        else
            Hotkey, IfWinActive

        this.Debug.Add("===== Control End  =====")
        for i , k in winObj.KeyList
        {
            if winObj.SuperKeyList[i] And (not all)
                Continue
            this.Debug.Add("class: " class "`tkey: " i "`tcontrol: " bold)
            if bold
                Hotkey, %i%, vim_Key, On
            else
                Hotkey, %i%, vim_Key, off
            winObj.KeyList[i] := bold
        }
        this.Debug.Add("===== Control Start =====")
    }

    ; Copy(winName1, winName2) {{{2
    ; 复制winName1到winName2, winName2按class/filepath/title定义
    Copy(winName1, winName2, class, filepath = "", title = "")
    {
        this.debug.Add("Copy>> "winName1 "`t"  winName2 "`t" class)
        win1 := This.GetWin(winName1)
        win2 := this.SetWin(winName2, class, filepath, title)
        win2.class := class
        win2.filepath := filepath
        win2.title := title
        win2.KeyList := win1.KeyList
        win2.SuperKeyList := win1.SuperKeyList
        win2.modeList  := win1.modeList
        win2.mode      := win1.mode
        win2.LastKey   := win1.LastKey
        win2.KeyTemp   := win1.KeyTemp
        win2.MaxCount  := win1.MaxCount
        win2.Count     := win1.Count
        win2.TimeOut   := win1.TimeOut
        win2.Info      := win1.Info
        win2.BeforeActionDoFunc := win1.BeforeActionDoFunc
        win2.AfterActionDoFunc  := win1.AfterActionDoFunc
        win2.ShowInfoFunc := win1.ShowInfoFunc
        win2.HideInfoFunc := win1.HideInfoFunc
        this.Control(Bold := true, winName2, all := true)
        return win2
    }

    ; CopyMode(win, mode, to) {{{2
    ; 在当前的Win中复制一个模式
    CopyMode(winName, fromMode, toMode)
    {
        winObj := This.GetWin(winName)
        winObj.mode := modeName
        winObj.KeyTemp := ""
        winObj.Count := 0
        from := winObj.modeList[fromMode]
        from.name  := toMode
        winObj.modeList[toMode] := from
        return from
    }

    ; Delete(winName) {{{2
    Delete(winName = "")
    {
        this.Control(false, winName, all := true)
        this.WinList[winName] := ""
    }

    ; GetMore() {{{2
    GetMore(obj = false)
    {
        rt_obj := []
        winObj  := this.GetWin(this.LastFoundWin)
        modeObj := this.GetMode(this.LastFoundWin)
        if Strlen(winObj.KeyTemp)
        {
            r := winObj.KeyTemp "`n"
            rt_obj[1] := {"key":winObj.KeyTemp, "Action":modeObj.GetKeyMap(winObj.KeyTemp)}
            m := "i)^" this.ToMatch(winObj.KeyTemp) ".+"
            for i , k in modeObj.keymapList
            {
                if RegExMatch(i, m)
                {
                    r .= i "`t" modeObj.GetKeyMap(i) "`n"
                    rt_obj[rt_obj.MaxIndex()+1] := {"key":i, "Action":modeObj.GetKeyMap(i)}
                }
            }

            if obj
                return rt_obj
            else
                return r
        }
        else
            if winObj.count
                return winObj.count
    }

    ; Clear() {{{2
    Clear(winName = "")
    {
        winObj := This.GetWin(winName)
        winObj.KeyTemp := ""
        winObj.Count := 0
    }

    ; Key() {{{2
    Key()
    {
        ; 获取winName
        winName := this.CheckWin()
        ; 获取当前的热键
        k := this.CheckCapsLock(this.Convert2VIM(A_ThisHotkey))
        ; 如果winName在排除窗口中，直接发送热键
        if this.ExcludeWinList[winName]
        {
            Send, % this.Convert2AHK(k, ToSend := true)
            return
        }
        ; 如果当前热键在当前winName无效，判断全局热键中是否有效？
        winObj := this.GetWin(winName)
        if Not winObj.KeyList[A_thisHotkey]
        {
            winObj := this.GetWin()
            if Not winObj.KeyList[A_thisHotkey]
            {
                ; 无效热键，按普通键输出
                Send, % this.Convert2AHK(k, ToSend := true)
                return
            }
            else
                winName := ""
        }

        this.LastFoundWin := winName
        ; 执行在判断热键前的函数, 如果函数返回true，按普通键输出
        if IsFunc(f := winObj.BeforeActionDoFunc)
        {
            if %f%()
            {
                Send, % this.Convert2AHK(k, ToSend := true)
                return
            }
        }

        ; 获取当前模式对应的对象
        modeObj := this.GetMode(winName)
        ; 把当前热键添加到热键缓存中, 并设置最后热键为k
        winObj.KeyTemp .= winObj.LastKey := k
        this.Debug.Add(" win: " winname "`tHotkey: " k)

        /*
            if winObj.Count
                this.Debug.Add(" [" winName "`t热键:" winObj.Count winObj.KeyTemp)
            else
                this.Debug.Add(" [" winName "]`t热键:" winObj.KeyTemp)
        */

        ; 热键缓存是否有对应的Action?
        ; 判断是否有更多热键, 如果当前具有<nowait>设置，则无视更多热键
        if modeObj.GetMoreKey(winObj.KeyTemp) And (Not modeObj.GetNoWait(winObj.KeyTemp))
        {
            ; 启用TimeOut
            if strlen(modeObj.GetKeymap(winObj.KeyTemp))
                if tick := winObj.TimeOut
                    Settimer, Vim_TimeOut, %tick%

            winObj.ShowMore()
            ; 执行在判断热键后的函数, 如果函数返回true，按普通键输出
            if IsFunc(f := winObj.AfterActionDoFunc)
            {
                if %f%()
                    Send, % this.Convert2AHK(k, ToSend := true)
            }

            return
        }

        ; 如果没有更多，热键缓存是否有对应的Action?
        if strlen(actionName := modeObj.GetKeymap(winObj.KeyTemp))
        {
            actObj := this.GetAction(actionName)
            if (actObj.Type = 0) And RegExMatch(actionName , "^<(\d)>$", m)
            {
                ; 数字则进行累加
                winObj.Count := winObj.Count * 10 + m1
                if winObj.MaxCount And winObj.Count > winObj.MaxCount
                    winObj.Count := winObj.MaxCount

                winObj.KeyTemp := ""
                winObj.ShowMore()
                if IsFunc(f := winObj.AfterActionDoFunc)
                    %f%()
                return
            }
            else
            {
                ; 非数字则直接运行
                ;this.Debug.Add("act: " actionName "`tLK: " winObj.KeyTemp)
                __vimLastAction := {"LastKey":k, "winName":winName, "ActionName":ActionName, "KeyTemp":winObj.KeyTemp, "Count":winObj.Count}
                Settimer, Vim_TimeOut, Off
                actObj.Do(winObj.Count)
                winObj.Count := 0
            }
        }
        else
        {
            Settimer, Vim_TimeOut, Off
            ; 如果没有，按普通键输出
            if strlen(actionName := modeObj.GetKeymap(winObj.LastKey))
            {
                actObj := this.GetAction(actionName)
                __vimLastAction := {"LastKey":k, "WinName":winName, "ActionName":ActionName, "KeyTemp":winObj.KeyTemp, "Count":winObj.Count}
                actObj.Do(winObj.Count)
                winObj.Count := 0
            }
            else
            {
                Send, % this.Convert2AHK(k, ToSend := true)
                winObj.Count := 0
            }
        }

        winObj.KeyTemp := ""
        winObj.HideMore()
        ; 执行在判断热键后的函数, 如果函数返回true，按普通键输出
        if IsFunc(f := winObj.AfterActionDoFunc)
        {
            if %f%()
                Send, % this.Convert2AHK(k, ToSend := true)
            ; %f%() ;直接运行
        }
        return
    }

    ; IsTimeOut() {{{2
    IsTimeOut()
    {
        winObj  := this.GetWin(this.LastFoundWin)
        modeObj := this.GetMode(this.LastFoundWin)
        act := this.GetAction(modeObj.GetKeyMap(winObj.KeyTemp))
        if act
        {
            winObj.HideMore()
            __vimLastAction := {"LastKey":k, "WinName":winName, "ActionName":ActionName, "KeyTemp":winObj.KeyTemp, "Count":winObj.Count}
            act.Do(winObj.Count)
            winObj.Count := 0
            winObj.KeyTemp := ""
            ; 执行在判断热键后的函数, 如果函数返回true，按普通键输出
            if IsFunc(f := winObj.AfterActionDoFunc)
            {
                if %f%()
                Send, % this.Convert2AHK(k, ToSend := true)
            }
            Settimer, Vim_TimeOut, Off
        }
    }

    ; Debug(Bold) {{{2
    Debug(Bold, history = false)
    {
        if Bold
        {
            this.Debug := new __vimDebug(history)
            this.Debug.var(this)
        }
        else
        {
            GUI, vimDebug:Destroy
            this.Debug := ""
        }
    }

    ; Convert2VIM(key) {{{2
    ; 将AHK热键名转换为类VIM的热键名
    ; 例 Convert2VIM("f1")
    Convert2VIM(key)
    {
        if RegExMatch(key, "^[A-Z]$")
            return "<S-" this.Upper(key) ">"
        if RegExMatch(key, "i)^((F1)|(F2)|(F3)|(F4)|(F5)|(F6)|(F7)|(F8)|(F9)|(F10)|(F11)|(F12))$")
            return "<" key ">"
        if RegExMatch(key, "i)^((AppsKey)|(Tab)|(Enter)|(Space)|(Home)|(End)|(CapsLock)|(ScrollLock)|(Up)|(Down)|(Left)|(Right)|(PgUp)|(PgDn)|(Pause))$")
            return "<" key ">"
        if RegExMatch(key, "i)^((BS)|(BackSpace))$")
            return "<BS>"
        if RegExMatch(key, "i)^((Esc)|(Escape))$")
            return "<Esc>"
        if RegExMatch(key, "i)^((Ins)|(Insert))$")
            return "<Insert>"
        if RegExMatch(key, "i)^((Del)|(Delete))$")
            return "<Delete>"
        if RegExMatch(key, "i)^PrintScreen$")
            return "<PrtSc>"
        if RegExMatch(key, "i)^shift\s&\s(.*)", m) or RegExMatch(key, "^\+(.*)", m)
            return "<S-" this.Upper(m1) ">"
        if RegExMatch(key, "i)^lshift\s&\s(.*)", m) or RegExMatch(key, "^<\+(.*)", m)
            return "<LS-" this.Upper(m1) ">"
        if RegExMatch(key, "i)^rshift\s&\s(.*)", m) or RegExMatch(key, "^>\+(.*)", m)
            return "<RS-" this.Upper(m1) ">"
        if RegExMatch(key, "i)^Ctrl\s&\s(.*)", m) or RegExMatch(key, "^\^(.*)", m)
            return "<c-" m1 ">"
        if RegExMatch(key, "i)^lctrl\s&\s(.*)", m) or RegExMatch(key, "^<\^(.*)", m)
            return "<lc-" m1 ">"
        if RegExMatch(key, "i)^rctrl\s&\s(.*)", m) or RegExMatch(key, "^>\^(.*)", m)
            return "<rc-" m1 ">"
        if RegExMatch(key, "i)^alt\s&\s(.*)", m) or RegExMatch(key, "^\!(.*)", m)
            return "<a-" m1 ">"
        if RegExMatch(key, "i)^lalt\s&\s(.*)", m) or RegExMatch(key, "^<\!(.*)", m)
            return "<la-" m1 ">"
        if RegExMatch(key, "i)^ralt\s&\s(.*)", m) or RegExMatch(key, "^>\!(.*)", m)
            return "<ra-" m1 ">"
        if RegExMatch(key, "i)^lwin\s&\s(.*)", m) or RegExMatch(key, "^#(.*)", m)
            return "<w-" m1 ">"
        if RegExMatch(key, "i)^space\s&\s(.*)", m)
            return "<sp-" m1 ">"
        if RegExMatch(key, "i)^alt$")
            return "<Alt>"
        if RegExMatch(key, "i)^ctrl$")
            return "<Ctrl>"
        if RegExMatch(key, "i)^shift$")
            return "<Shift>"
        if RegExMatch(key, "i)^lwin$")
            return "<Win>"
        return key
    }

    Upper(k)
    {
        StringUpper, k, k
        return k
    }

    ; Convert2AHK(key, ToSend=false) {{{2
    ; 将类VIM的热键名转换为AHK热键名
    ; 例 Convert2AHK("<F1>")
    Convert2AHK(key, ToSend=false)
    {
        this.CheckCapsLock(key)
        if RegExMatch(key, "^<.*>$")
        {
            key := SubStr(key, 2, strlen(key)-2)
            if RegExMatch(key, "i)^((F1)|(F2)|(F3)|(F4)|(F5)|(F6)|(F7)|(F8)|(F9)|(F10)|(F11)|(F12))$")
                return ToSend ? "{" key "}" : key
            if RegExMatch(key, "i)^((AppsKey)|(Tab)|(Enter)|(Space)|(Home)|(End)|(CapsLock)|(ScrollLock)|(Up)|(Down)|(Left)|(Right)|(PgUp)|(PgDn)|(BS)|(ESC)|(Insert)|(Delete)|(Pause))$")
                return ToSend ? "{" key "}" : key
            if RegExMatch(key, "i)^PrtSc$")
                return ToSend ? "{PrintScreen}" : "PrintScreen"
            if RegExMatch(key, "i)^alt$")
                return ToSend ? "{!}" : "alt"
            if RegExMatch(key, "i)^ctrl$")
                return ToSend ? "{^}" : "ctrl"
            if RegExMatch(key, "i)^shift$")
                return ToSend ? "{+}" : "shift"
            if RegExMatch(key, "i)^win$")
                return ToSend ? "{#}" : "lwin"
            if RegExMatch(key, "<LT>")
                return "<"
            if RegExMatch(key, "<RT>")
                return ">"
            if RegExMatch(key, "i)^S\-(.*)", m)
                return ToSend ? "+" this.CheckToSend(m1) : "+" m1
            if RegExMatch(key, "i)^LS\-(.*)", m)
                return ToSend ? "+" this.CheckToSend(m1) : "<+" m1
            if RegExMatch(key, "i)^RS-(.*)", m)
                return ToSend ? "+" this.CheckToSend(m1) : ">+" m1
            if RegExMatch(key, "i)^C\-(.*)", m)
                return ToSend ? "^" this.CheckToSend(m1) : "^" m1
            if RegExMatch(key, "i)^LC\-(.*)", m)
                return ToSend ? "^" this.CheckToSend(m1) : "<^" m1
            if RegExMatch(key, "i)^RC\-(.*)", m)
                return ToSend ? "^" this.CheckToSend(m1) : ">^" m1
            if RegExMatch(key, "i)^A\-(.*)", m)
                return ToSend ? "!" this.CheckToSend(m1) : "!" m1
            if RegExMatch(key, "i)^LA\-(.*)", m)
                ; 暂时先不区分左右键
                return ToSend ? "!" this.CheckToSend(m1): "<!" m1
                ;return ToSend ? "{lalt down}" this.CheckToSend(m1) "{lalt up}": "<!" m1
            if RegExMatch(key, "i)^RA\-(.*)", m)
                return ToSend ? "!" this.CheckToSend(m1) : ">!" m1
            if RegExMatch(key, "i)^w\-(.*)", m)
                return ToSend ? "#" this.CheckToSend(m1) : "#" m1
            if RegExMatch(key, "i)^SP\-(.*)", m)
                return ToSend ? "{space}" this.CheckToSend(m1) : "space & " m1
        }
        else
            return key
    }

    CheckToSend(key)
    {
        if RegExMatch(key, "i)^((F1)|(F2)|(F3)|(F4)|(F5)|(F6)|(F7)|(F8)|(F9)|(F10)|(F11)|(F12))$")
            return "{" key "}"
        if RegExMatch(key, "i)^((AppsKey)|(Tab)|(Enter)|(Space)|(Home)|(End)|(CapsLock)|(ScrollLock)|(Up)|(Down)|(Left)|(Right)|(PgUp)|(PgDn)|(BS)|(ESC)|(Insert)|(Delete)|(Pause))$")
            return "{" key "}"
        if RegExMatch(key, "i)^PrtSc$")
            return "{PrintScreen}"
        if RegExMatch(key, "i)^alt$")
            return "{!}"
        if RegExMatch(key, "i)^ctrl$")
            return "{^}"
        if RegExMatch(key, "i)^shift$")
            return "{+}"
        if RegExMatch(key, "i)^win$")
            return "{#}"
        if RegExMatch(key, "<LT>")
            return "<"
        if RegExMatch(key, "<RT>")
            return ">"
        return Key
    }

    ; CheckCapsLock(key) {{{2
    ; 检测CapsLock是否按下，返回对应的值
    ; key 的值为类VIM键，如 CheckCapsLock("<S-A>")
    CheckCapsLock(key)
    {
        if GetKeyState("CapsLock", "T")
        {
            if RegExMatch(key, "^[a-z]$")
                return "<S-" key ">"
            if RegExMatch(key, "i)^<S\-([a-zA-Z])>", m)
            {
                StringLower, key, m1
                return key
            }
        }
        return key
    }

    ToMatch(v)
    {
        v := RegExReplace(v , "\+|\?|\.|\*|\{|\}|\(|\)|\||\^|\$|\[|\]|\\", "\$0")
        return RegExReplace(v , "\s", "\s")
    }
}

; Class __win {{{1
Class __win
{
    __new(class = "", filepath = "", title = "")
    {
        this.class     := class
        this.filepath  := filepath
        this.title     := title
        this.KeyList   := []
        this.SuperKeyList   := []
        this.modeList  := []
        this.Status    := true
        this.mode      := ""
        this.LastKey   := ""
        this.KeyTemp   := ""
        this.MaxCount  := 99
        this.Count     := 0
        this.TimeOut   := 0
        this.Info := default_enable_show_info
        this.BeforeActionDoFunc := ""
        this.AfterActionDoFunc   := ""
        this.ShowInfoFunc := "ShowInfo"
        this.HideInfoFunc := "HideInfo"
    }

    ; ChangeMode(modeName) {{{2
    ; 检查模式是否存在，如存在则返回模式对象，如果不存在则新建并返回模式对象
    ChangeMode(modeName)
    {
        this.mode := modeName
        this.KeyTemp := ""
        this.Count := 0
        if not this.modeList[modeName]
            this.modeList[modeName] := new __Mode(modeName)

        modeObj := this.modeList[modeName]
        if IsFunc(func := modeObj.modeFunction)
            %func%()
        return modeObj
    }

    ; ExistMode() {{{2
    ; 获取当前模式名
    ExistMode()
    {
        return this.mode
    }

    ; SetInfo() {{{2
    SetInfo(bold)
    {
        this.info := bold
    }

    ; SetShowInfo() {{{2
    SetShowInfo(func)
    {
        this.ShowInfoFunc := func
    }

    ; SetHideInfo() {{{2
    SetHideInfo(func)
    {
        this.HideInfoFunc := func
    }

    ; ShowMore() {{{2
    ShowMore()
    {
        if IsFunc(f := this.ShowInfoFunc) And this.Info
            %f%()
    }

    ; HideMore() {{{2
    HideMore()
    {
        if IsFunc(f:=this.HideInfoFunc) And this.Info
            %f%()
    }
}

; Class __Mode {{{1
Class __Mode
{
    __new(modeName)
    {
        this.name  := modeName
        this.keymapList := []
        this.keymoreList := []
        this.nowaitList := []
        this.modeFunction := ""
    }

    ; SetKeyMap(key, action) {{{2
    SetKeyMap(key, action)
    {
        this.keymapList[key] := action
    }

    ; GetKeyMap(key) {{{2
    GetKeyMap(key)
    {
        return this.keymapList[key]
    }

    ; DelKeyMap(key) {{{2
    DelKeyMap(key)
    {
        this.keymapList[key] := ""
    }

    ; SetNoWait(key, bold) {{{2
    ; 无视TimeOut执行热键
    SetNoWait(key, bold)
    {
        this.nowaitList[key] := bold
    }

    ; GetNoWait(key) {{{2
    GetNoWait(key)
    {
        return this.nowaitList[key]
    }

    ; SetMoreKey(key) {{{2
    SetMoreKey(key)
    {
        this.keymoreList[key] := true
    }

    ; GetMoreKey(key) {{{2
    GetMoreKey(key)
    {
        return this.keymoreList[key]
    }
}

; Class __Action {{{1
Class __Action
{
    ; Action 有几种类型
    ; 0 代表执行Action对应的Label (默认)
    ; 1 代表执行Function的值对应的函数
    ; 2 代表运行CmdLine对应的值
    ; 3 代表发送HotString对应的文本

    __new(Name, Comment)
    {
        this.Name := Name
        this.Comment := Comment
        this.MaxTimes := 0
        this.Type := 0
        this.Function := ""
        this.CmdLine := ""
        this.HotString := ""
    }

    ; SetFunction(Function) {{{2
    ; 设置Action执行的函数名
    SetFunction(Function)
    {
        this.Function := Function
        this.Type := 1
    }

    ; SetCmdLine(CmdLine) {{{2
    ; 设置Action运行的字符串
    SetCmdLine(CmdLine)
    {
        this.CmdLine := CmdLine
        this.Type := 2
    }

    ; SetHotString(HotString) {{{2
    ; 设置Action发送的文本
    SetHotString(HotString)
    {
        this.HotString := HotString
        this.Type := 3
    }

    ; SetMaxTimes(Times) {{{2
    ; 设置最大运行次数
    SetMaxTimes(Times)
    {
        this.MaxTimes := Times
    }

    ; Do(Times=1) {{{2
    ; 执行Action
    Do(Times=0)
    {
        Times := !Times ? 1 : Times
        if this.MaxTimes And (Times > this.MaxTimes)
            Times := this.MaxTimes
        Loop, %Times%
        {
            if this.Type = 0
            {
                if IsLabel(label := this.Name)
                    GoSub, %label%
            }
            else if this.Type = 1
            {
                if IsFunc(f := this.Function)
                    %f%()
            }
            else if this.Type = 2
            {
                c := this.CmdLine
                Run, %cmd%
            }
            else if This.Type = 3
            {
                t := this.HotString
                Send, %t%
            }
        }
    }
}

; Class __Plugin {{{1
Class __Plugin
{
    __new(PluginName)
    {
        this.PluginName := PluginName
        this.Author := ""
        this.Ver := ""
        this.Comment := ""
    }

    ; CheckSub() {{{2
    CheckSub()
    {
        if IsLabel(p := this.PluginName)
        {
            GoSub, %p%
            this.Error := false
        }
        else
            this.Error := true
    }
}

; Class __vimDebug {{{1
Class __vimDebug
{
    __new(key)
    {
        this.mode := key
        if key
        {
            GUI, vimDebug:Destroy
            GUI, vimDebug:+hwnd_vimdebug -Caption +ToolWindow +Border
            GUI, vimDebug:color, 454545, 454545
            GUI, vimDebug:font, s16 cFFFFFF
            GUI, vimDebug:Add, Edit, x-2 y-2 w400 h60 readonly
            GUI, vimDebug:Show, w378 h56 y600
            WinSet, AlwaysOnTop, On, ahk_id %_vimdebug%
        }
        else
        {
            GUI, vimDebug:font, s12
            GUI, vimDebug:Destroy
            GUI, vimDebug:Add, Edit, x10 y10 w400 h300 readonly
            GUI, vimDebug:Add, Edit, x10 y320 w400 h26 readonly
            GUI, vimDebug:Show, w420 h356
        }
    }

    var(obj)
    {
        this.vim := obj
    }

    Set(v)
    {
        winName := this.vim.CheckWin()
        winObj := this.vim.GetWin(winName)
        if winObj.Count
            k := " 热键缓存:" winObj.Count winObj.KeyTemp
        else
             k := " 热键缓存:" winObj.KeyTemp
        GUI, vimDebug:Default
        GUIControl, , Edit1, %v%
        GUIControl, , Edit2, %k%
    }

    Get()
    {
        GUI, vimDebug:Default
        GUIControlGet, v, , Edit1
        return v
    }

    ; Add(v) {{{2
    Add(v)
    {
        b := this.Get()
        if this.mode
            this.Set(b)
        else
            this.Set(v "`n" b)
    }

    ; Clear() {{{2
    Clear()
    {
        this.Set("")
    }
}


Add some comments; no code changes

Add some comments; no code changes

; JKLmouse for AutoHotkey by Michael Geary
; http://www.jklmouse.com/ - documentation
; http://unlicense.org/ - public domain statement
; Version 0.1: 11/3/2011

#SingleInstance force

; Use either the embedded icon in a compiled .exe,
; or a separate .ico file when running the .ahk script.
iconPath := RegExReplace( A_ScriptFullPath, "i).ahk$", ".ico" )

; Set up the tray icon and menu
Menu, Tray, Icon, %iconPath%
Menu, Tray, Tip, JKLmouse
Menu, Tray, Add
Menu, Tray, Add, About JKLmouse, MenuAbout

; Display tray balloon
; TODO: more useful content?
; TODO: display this only during an install
TrayTip JKLmouse, The Automatic Keyboard Mouse

; Remove forced delay between triggered mouse events.
; TODO: experiment with this when accelerated
SetMouseDelay, -1

; Hotkey repeat count for acceleration
repeat := 0

; Each of these modifier keys enables the same set of JKLmouse hotkeys
SetKeys( "CapsLock" )
SetKeys( "~LButton" )
SetKeys( "~RButton" )
SetKeys( "~MButton" )
SetKeys( "~XButton1" )
SetKeys( "~XButton2" )

return

; Set up all the hotkeys for a specific modifier key
SetKeys( mod ) {

	; Left hand
	SetKey( mod, "W", "MouseLeftUp" )
	SetKey( mod, "E", "MouseUp" )
	SetKey( mod, "R", "MouseRightUp" )
	SetKey( mod, "S", "MouseLeft" )
	SetKey( mod, "D", "MouseDown" )
	SetKey( mod, "F", "MouseRight" )
	SetKey( mod, "X", "MouseLeftDown" )
	SetKey( mod, "C", "MouseDown" )
	SetKey( mod, "V", "MouseRightDown" )

	; Right hand
	SetKey( mod, "U", "MouseLeftUp" )
	SetKey( mod, "I", "MouseUp" )
	SetKey( mod, "O", "MouseRightUp" )
	SetKey( mod, "J", "MouseLeft" )
	SetKey( mod, "K", "MouseDown" )
	SetKey( mod, "L", "MouseRight" )
	SetKey( mod, "M", "MouseLeftDown" )
	SetKey( mod, "`,","MouseDown" )
	SetKey( mod, "`.","MouseRightDown" )

	; Numeric
	SetKey( mod, "7", "MouseLeftUp" )
	SetKey( mod, "8", "MouseUp" )
	SetKey( mod, "9", "MouseRightUp" )
	SetKey( mod, "4", "MouseLeft" )
	SetKey( mod, "5", "MouseDown" )
	SetKey( mod, "6", "MouseRight" )
	SetKey( mod, "1", "MouseLeftDown" )
	SetKey( mod, "2", "MouseDown" )
	SetKey( mod, "3", "MouseRightDown" )

	; Arrows
	SetKey( mod, "Up", "MouseUp" )
	SetKey( mod, "Down", "MouseDown" )
	SetKey( mod, "Left", "MouseLeft" )
	SetKey( mod, "Right", "MouseRight" )
}

; Set up one hotkey by combining the modifier and key names.
; Take the specified action on key down,
; and always stop acceleration on key up.
SetKey( mod, key, action ) {
	keydown = %mod% & %key%
	Hotkey %keydown%, %action%
	Hotkey %keydown% UP, StopAcceleration
}

; Move the mouse the specifed x and y distances, both scaled by the
; mouse acceleration factor calculated from the key repeat count.
Move( x, y ) {
	global repeat
	++repeat
	factor := Floor( ( repeat + 1 ) / 2 )
	x := x * factor
	y := y * factor
	MouseMove, x, y, 0, R
}

; Handle the keydown for each of the mouse movement directions
; TODO: Maybe a way to simplify this?
MouseUp:
	Move( 0, -1 )
	return
MouseDown:
	Move( 0, 1 )
	return
MouseLeft:
	Move( -1, 0 )
	return
MouseRight:
	Move( 1, 0 )
	return
MouseLeftUp:
	Move( -1, -1 )
	return
MouseRightUp:
	Move( 1, -1 )
	return
MouseLeftDown:
	Move( -1, 1 )
	return
MouseRightDown:
	Move( 1, 1 )
	return

; Handle the keyup for all hotkeys
StopAcceleration:
	repeat = 0
	return

; Handle the About menu item
MenuAbout:
	Run, http://www.jklmouse.com/
	return

; JKLmouse for AutoHotkey by Michael Geary
; http://www.jklmouse.com/ - documentation
; http://unlicense.org/ - public domain statement
; Version 0.1: 11/3/2011

#SingleInstance force

iconPath := RegExReplace( A_ScriptFullPath, "i).ahk$", ".ico" )
Menu, Tray, Icon, %iconPath%
Menu, Tray, Tip, JKLmouse
Menu, Tray, Add
Menu, Tray, Add, About JKLmouse, MenuAbout

TrayTip JKLmouse, The Automatic Keyboard Mouse

SetMouseDelay, -1

repeat := 0

SetKeys( "CapsLock" )
SetKeys( "~LButton" )
SetKeys( "~RButton" )
SetKeys( "~MButton" )
SetKeys( "~XButton1" )
SetKeys( "~XButton2" )

return

SetKeys( mod ) {

	; Left hand
	SetKey( mod, "W", "MouseLeftUp" )
	SetKey( mod, "E", "MouseUp" )
	SetKey( mod, "R", "MouseRightUp" )
	SetKey( mod, "S", "MouseLeft" )
	SetKey( mod, "D", "MouseDown" )
	SetKey( mod, "F", "MouseRight" )
	SetKey( mod, "X", "MouseLeftDown" )
	SetKey( mod, "C", "MouseDown" )
	SetKey( mod, "V", "MouseRightDown" )

	; Right hand
	SetKey( mod, "U", "MouseLeftUp" )
	SetKey( mod, "I", "MouseUp" )
	SetKey( mod, "O", "MouseRightUp" )
	SetKey( mod, "J", "MouseLeft" )
	SetKey( mod, "K", "MouseDown" )
	SetKey( mod, "L", "MouseRight" )
	SetKey( mod, "M", "MouseLeftDown" )
	SetKey( mod, "`,","MouseDown" )
	SetKey( mod, "`.","MouseRightDown" )

	; Numeric
	SetKey( mod, "7", "MouseLeftUp" )
	SetKey( mod, "8", "MouseUp" )
	SetKey( mod, "9", "MouseRightUp" )
	SetKey( mod, "4", "MouseLeft" )
	SetKey( mod, "5", "MouseDown" )
	SetKey( mod, "6", "MouseRight" )
	SetKey( mod, "1", "MouseLeftDown" )
	SetKey( mod, "2", "MouseDown" )
	SetKey( mod, "3", "MouseRightDown" )

	; Arrows
	SetKey( mod, "Up", "MouseUp" )
	SetKey( mod, "Down", "MouseDown" )
	SetKey( mod, "Left", "MouseLeft" )
	SetKey( mod, "Right", "MouseRight" )
}

SetKey( mod, key, action ) {
	keydown = %mod% & %key%
	Hotkey %keydown%, %action%
	Hotkey %keydown% UP, StopAcceleration
}

Move( x, y ) {
	global repeat
	++repeat
	factor := Floor( ( repeat + 1 ) / 2 )
	x := x * factor
	y := y * factor
	MouseMove, x, y, 0, R
}

MouseUp:
	Move( 0, -1 )
	return
MouseDown:
	Move( 0, 1 )
	return
MouseLeft:
	Move( -1, 0 )
	return
MouseRight:
	Move( 1, 0 )
	return
MouseLeftUp:
	Move( -1, -1 )
	return
MouseRightUp:
	Move( 1, -1 )
	return
MouseLeftDown:
	Move( -1, 1 )
	return
MouseRightDown:
	Move( 1, 1 )
	return

StopAcceleration:
	repeat = 0
	return

MenuAbout:
	Run, http://www.jklmouse.com/
	return


Update EMB_Core_BlockComment.ahk

Update EMB_Core_BlockComment.ahk

Removing dependency on "\n"

EMB_Core_BlockComment(mbsource){
  pair := []
  Matches := []
  i := 1

  ;pattern ignores everything inside quotes (all defined strings)
  pattern = "(?:.|\s)*?"|(\/\*(?:.|\s)*?\*\/)

  ;Match in the mbsource where matches pattern
  i := regexmatch(mbsource, "iO)" . pattern, oMatch, i)

  ;Loop over all i
  while i <> 0 {
    If oMatch.Value(1) {
	  ;msgbox, % oMatch.Value(0) "-->" oMatch.Value(1) "-->" oMatch.Count

      ;Add those that match 2nd criteria (real comments) to match object
      pair := [oMatch.Pos(0), oMatch.Len(0)]

      ;Add pair array to collection of pairs - "Matches" represents all the matches that are not part of existing strings.
      Matches.push(pair)
    }

    ;Next i
    i := oMatch.Pos(0) + oMatch.Len(0)

    ;Get next match from mbsource
    i := regexmatch(mbsource, "iO)" pattern, oMatch, i)
  }

  ;Match11 := Matches[1][1]
  ;Match12 := Matches[1][2]
  ;msgbox, Match11:%Match11% ¦ Match12: %Match12%

  ;if !EMB_Compact_Transpile then
  ;  for each match in matches
  ;    extract and remove match from code
  ;    replace /* with '
  ;    replace `r`n with `r`n'
  ;    replace */ with `r`n
  ;    re-insert new code where match was
  ;  next match
  ;Else
  ;  for each match in matches
  ;    remove match from code
  ;  next match
  ;End If

  ;msgbox, transpile or remove?
  ;msgbox, % !EMB_Compact_Transpile
  if (!EMB_Compact_Transpile) {
    ;Loop over Matches
	loop, % Matches.Length()
    {
	  LoopVar := Matches.Length() - A_Index + 1
      subSrc := p_EMB_Core_BC_removeFromString(mbsource,Matches[LoopVar][1],Matches[LoopVar][2])
		subSrc := RegExReplace(subSrc,"\/\*\s*","'")
		subSrc := StrReplace(subSrc,"`r`n","`r`n'")
		subSrc := RegExReplace(subSrc,"\s*\*\/","`r`n")
      p_EMB_Core_BC_insertIntoString(mbsource,subSrc,matches[LoopVar][1])
    }
  } else {
    p_EMB_Core_BC_removeFromString(mbsource,matches[A_Index][0],matches[A_Index][1])
  }

return mbsource
}

p_EMB_Core_BC_removeFromString(ByRef src,index,length){
  ;Supply function with an index and length of a string to extract
  ;The function will extract and return the string and remove it from the original src string.
  out_String := SubStr(src, index, length)
  src := SubStr(src,1,index - 1) . SubStr(src,index + length + 1)
  return out_String
}

p_EMB_Core_BC_insertIntoString(ByRef src,in_String,index){
  ;inserts a string "in_String" into src before the index supplied.
  ;Mid(s,1,Index-1) & "---" & Mid(s,Index)
  src := SubStr(src,1,index-1) . in_String . SubStr(src,index)
  return
}

EMB_Core_BlockComment(mbsource){
  pair := []
  Matches := []
  i := 1

  ;pattern ignores everything inside quotes (all defined strings)
  pattern = "(?:.|\s)*?"|(\/\*(?:.|\s)*?\*\/)

  ;Match in the mbsource where matches pattern
  i := regexmatch(mbsource, "iO)" . pattern, oMatch, i)

  ;Loop over all i
  while i <> 0 {
    'If substring exists then a match was found/extracted
    If oMatch.Value(1) {
      ;Add those that match 2nd criteria (real comments) to match object
      pair := [oMatch.Pos(0), oMatch.Len(0)]

      ;Add pair array to collection of pairs - "Matches" represents all the matches that are not part of existing strings.
      Matches.push(pair)
    }

    ;Get position to search from.
    i := oMatch.Pos(0) + oMatch.Len(0)

    ;Get next match from mbsource
    i := regexmatch(mbsource, "iO)" pattern, oMatch, i)
  }

  ;if !EMB_Compact_Transpile then
  ;  for each match in matches
  ;    extract and remove match from code
  ;    replace /* with '
  ;    replace `r`n with `r`n'
  ;    replace */ with `r`n
  ;    re-insert new code where match was
  ;  next match
  ;Else
  ;  for each match in matches
  ;    remove match from code
  ;  next match
  ;End If

  if (!EMB_Compact_Transpile) {
    ;Loop over Matches
	loop, % Matches.Length()
    {
	  LoopVar := Matches.Length() - A_Index + 1
      subSrc := p_EMB_Core_BC_removeFromString(mbsource,Matches[LoopVar][1],Matches[LoopVar][2])
		subSrc := RegExReplace(subSrc,"\r\n","\n")
		subSrc := RegExReplace(subSrc,"\/\*\s*","'")
		subSrc := StrReplace(subSrc,"\n","\n'")
		subSrc := RegExReplace(subSrc,"\s*\*\/","\n")
		subSrc := StrReplace(subSrc,"\n","`r`n")
      p_EMB_Core_BC_insertIntoString(mbsource,subSrc,matches[LoopVar][1])
    }
  } else {
    p_EMB_Core_BC_removeFromString(mbsource,matches[A_Index][0],matches[A_Index][1])
  }

return mbsource
}

p_EMB_Core_BC_removeFromString(ByRef src,index,length){
  ;Supply function with an index and length of a string to extract
  ;The function will extract and return the string and remove it from the original src string.
  out_String := SubStr(src, index, length)
  src := SubStr(src,1,index - 1) . SubStr(src,index + length + 1)
  return out_String
}

p_EMB_Core_BC_insertIntoString(ByRef src,in_String,index){
  ;inserts a string "in_String" into src before the index supplied.
  ;Mid(s,1,Index-1) & "---" & Mid(s,Index)
  src := SubStr(src,1,index-1) . in_String . SubStr(src,index)
  return
}


Updated with more Unit tests

Updated with more Unit tests

#Include Unit_testing\Yunit.ahk
#Include Unit_testing\Window.ahk
;~ #Include Unit_testing\StdOut.ahk
#Include Unit_testing\JUnit.ahk
#Include Unit_testing\OutputDebug.ahk

Yunit.Use(YunitWindow, YunitJUnit, YunitOutputDebug).Test(Folder_Function_Check, File_Function_Check,INI_File_Tests,temp_File_Functions_Check,Macro_Update_Check_Functions)

class Folder_Function_Check
{
    Folder_Create()
    {
    Result := Folder_Create("Macro_Test_Folder")
              Yunit.assert(Result == 0, "Error Creating Folder on Root C drive")

                Result := FileExist("C:\Macro_Test_Folder")
                 FileRemoveDir, C:\Macro_Test_Folder, 1
                Yunit.assert(Result !="" , "Did not create the folder")
      }

Class Folder_Exists_Function
{
   Folder_Not_Exist_Check()
 {
Result := Folder_Exist_Check("Macro_Test_Folder_Not_Exist")
Yunit.assert(Result == "Macro_Test_Folder_Not_Exist - Folder_Not_Exist")
}

   Folder_Exists_Check()
    {
        FileCreateDir, C:\Macro_Test_Folder
        sleep 500
Result := Folder_Exist_Check("Macro_Test_Folder")
 FileRemoveDir, C:\Macro_Test_Folder, 1
         Yunit.assert(Result == "Macro_Test_Folder - Folder_Exist")
  }

}}

class File_Function_Check
{
  File_Create()
{

     Result := File_Create("Macro_Testing.ini" , "1")
     Yunit.assert(Result == 0 , "Error Creating File")
          Result := FileExist(A_Desktop "\Macro_Testing.ini")
          FileDelete, %A_Desktop%\Macro_Testing.ini
                Yunit.assert(Result !="" , "Function not creating the file")
}

Class File_Exist_Function
{

No_File_Exist_Check()
{
    Result := File_Exist_Check("Macro_Testing_not_exist.ini")
    Yunit.assert(Result == "Macro_Testing_not_exist.ini - File_Not_Exist")
}

File_Exist_Check()
{
    FileAppend, test, %A_Desktop%\Macro_Testing.ini
      Result := File_Exist_Check("Macro_Testing.ini", "1")
     FileDelete, %A_Desktop%\Macro_Testing.ini
      Yunit.assert(Result == "Macro_Testing.ini - File_Exist")
}}}

Class INI_File_Function_Tests
{

Ini_File_Read()
{

     Inifile := A_Desktop "\Macro_Testing.ini"
      FileAppend,,%inifile%
      sleep 500
Loop, 10
IniWrite,%A_index%,%inifile%,Testing,Test_Section%A_index%

    Load_ini_file(inifile)
    FileDelete, %inifile%
    Loop, 10
       Yunit.assert(Test_Section A_index == A_index)

 }

Ini_File_Write()
{
       Inifile := A_Desktop "\Macro_Testing.ini"
  FileAppend,,%inifile%

  Write_ini_file(inifile)
    Load_ini_file(inifile)
           FileDelete %inifile%
    Loop, 10
       Yunit.assert(Test_Section A_index == A_index)
       }
}

Class temp_File_Functions_Check
{
    Temp_File_Delete()
{
     File_Location := A_Desktop
    File_Name := "Unit_test_Temp_File.txt"
    FileAppend, Test, %File_Location%\%File_name%
    sleep 500
Result := Temp_File_Delete(File_Location,File_Name)
Yunit.assert(Result == File_Name " - File Found and Deleted")
}

Class Temp_File_Read_Function
{
    Test_For_Failure_to_Read()
{

    File_Location := A_Desktop
    File_Name := "Unit_test_Temp_File_Failure_To_Read.txt"

Result := Temp_File_Read(File_Location,File_Name)
Yunit.assert(Result == "Null")
}

Test_For_Successful_Read()
{
    File_Location := A_Desktop
    File_Name := "Unit_test_Temp_File.txt"
    FileAppend, Test, %File_Location%\%File_name%

Result := Temp_File_Read(File_Location,File_Name)
FileDelete, %File_location%\%File_Name%
Yunit.assert(Result == "Test")
}}}

Class Macro_Update_Check_Functions
{
    class Calculations
    {
        Calculate_Days_Since_Last_Update()
{
    Updatestatus := A_Now
    Newtime = -10
    EnvAdd, UpdateStatus, %Newtime%, days

        Days := Calculate_Days_Since_Last_Update(updatestatus)
        Yunit.assert(Days == "10")
 }  }

}

#Include Unit_testing\Yunit.ahk
#Include Unit_testing\Window.ahk
;~ #Include Unit_testing\StdOut.ahk
#Include Unit_testing\JUnit.ahk
#Include Unit_testing\OutputDebug.ahk

Yunit.Use(YunitWindow, YunitJUnit, YunitOutputDebug).Test(Folder_Function_Check, File_Function_Check)

class Folder_Function_Check
{

   _Folder_Exists__Test_FolderNotExist() ; Needs to have the "_" in front so it runs first
    {
Result := Folder_Exist_Check("Macro_Test_Folder")
Yunit.assert(Result == "Macro_Test_Folder - Folder_Not_Exist", "Folder Exists - Delete folder on Root C Drive and Rerun tests")
}

    Folder_Create()
    {
    Result := Folder_Create("Macro_Test_Folder")
              Yunit.assert(Result == 0, "Error Creating Folder on Root C drive")

                Result := FileExist("C:\Macro_Test_Folder")
                Yunit.assert(Result !="" , "Cannot find Folder on Root C drive")
      }

    Folder_Exists__Test_FolderExists()
    {
Result := Folder_Exist_Check("Macro_Test_Folder")
 FileRemoveDir, C:\Macro_Test_Folder, 1
         Yunit.assert(Result == "Macro_Test_Folder - Folder_Exist")
  }

}

class File_Function_Check
{
    Begin()
    {
     }

_File_Exist_Check__Test_NoFileExist() ; Needs to have the "_" in front so it runs first
{
    Result := File_Exist_Check("Macro_Testing.ini")
    Yunit.assert(Result == "Macro_Testing.ini - File_Not_Exist", "File exists, delete file in root C folder")
}

File_Create()
{

     Result := File_Create("Macro_Testing.ini" , "1")
     Yunit.assert(Result == 0 , "Error Creating File")
          Result := FileExist(A_Desktop "\Macro_Testing.ini")
                Yunit.assert(Result !="" , "Function not creating the file")
}

File_Exist_Check__Test_FileExist()
{
      Result := File_Exist_Check("Macro_Testing.ini", "1")
        FileDelete, %A_Desktop%\Macro_Testing.ini
    Yunit.assert(Result == "Macro_Testing.ini - File_Exist")
}

    End()
    {
    }
}


[~] removed useless win wait for non-XP os.