use vim bindings

use vim bindings

;#### wheelctrl.ahk
EnvGet, DebugP, DEBUG
If (DebugP)
{
  TrayTip, Loading %A_ScriptName% , Debug Mode On
}
Else
{
  Menu, Tray, NoIcon
}

#Persistent
return
;---------------------------------------------------------------
MButton::
  Scale := 10
  MouseGetPos, X0, Y0
  fn := Func("SendWheelEvent").bind(Y0, Scale)
  SetTimer, % fn, 100
return

MButton Up::
  MouseGetPos, X1, Y1
  SetTimer, % fn, Off
  if (X1 == X0)  ; trigger middle click if cursor is not moved.
    MouseClick, Middle
return

SendWheelEvent(Y0, Scale)
{
  MouseGetPos, X, NewY
  Delta := NewY - Y0
  NumWheel := abs(Delta // Scale)
  If (NumWheel == 0)
    return
  WheelMove := (Delta > 0 ?  "WheelDown" : "WheelUp")

  Loop % NumWheel {
    Click, %WheelMove%
  }

  MouseMove, X, Y0
  ;Traytip, evt: %WheelMove% x %NumWheel%, mouse moving
  ;ListVars
  return
}

MouseOperation(op, acc:=1)
{
  step := 15 * acc
  if (op == "UP") {
    MouseMove, 0, -step, 0, R
  } else if (op == "DOWN") {
    MouseMove, 0,  step, 0, R
  } else if (op == "LEFT") {
    MouseMove, -step, 0, 0, R
  } else if (op == "RIGHT") {
    MouseMove,  step, 0, 0, R
  } else if (op == "LCLICK") {
    Click
  } else if (op == "RCLICK") {
    Click, right
  } else if (op == "WHEELUP") {
    Click, WheelUp
  } else if (op == "WHEELDOWN") {
    Click, WheelDown
  }
}

;---------------------------------------------------------------
;--- sound volume control
#PgUp::Send {Volume_Up 2}
#PgDn::Send {Volume_Down 2}

;--- keyboard mouse
#If GetKeyState("CapsLock", "T")
~Space::Shift
k::MouseOperation("UP")
j::MouseOperation("DOWN")
h::MouseOperation("LEFT")
l::MouseOperation("RIGHT")
f::MouseOperation("LCLICK")
s::MouseOperation("RCLICK")
y::MouseOperation("WHEELUP")
h::MouseOperation("WHEELDOWN")
;-- accelerated --
+i::MouseOperation("UP", 5)
+k::MouseOperation("DOWN", 5)
+j::MouseOperation("LEFT", 5)
+l::MouseOperation("RIGHT", 5)
#If ;GetKeyState("CapsLock", "T")

;--- IME switch
;; SC029::Send {vkF3sc029}

#If DebugP
!^r::Reload

;#### wheelctrl.ahk
EnvGet, DebugP, DEBUG
If (DebugP)
{
  TrayTip, Loading %A_ScriptName% , Debug Mode On
}
Else
{
  Menu, Tray, NoIcon
}

#Persistent
return
;---------------------------------------------------------------
MButton::
  Scale := 10
  MouseGetPos, X0, Y0
  fn := Func("SendWheelEvent").bind(Y0, Scale)
  SetTimer, % fn, 100
return

MButton Up::
  MouseGetPos, X1, Y1
  SetTimer, % fn, Off
  if (X1 == X0)  ; trigger middle click if cursor is not moved.
    MouseClick, Middle
return

SendWheelEvent(Y0, Scale)
{
  MouseGetPos, X, NewY
  Delta := NewY - Y0
  NumWheel := abs(Delta // Scale)
  If (NumWheel == 0)
    return
  WheelMove := (Delta > 0 ?  "WheelDown" : "WheelUp")

  Loop % NumWheel {
    Click, %WheelMove%
  }

  MouseMove, X, Y0
  ;Traytip, evt: %WheelMove% x %NumWheel%, mouse moving
  ;ListVars
  return
}

MouseOperation(op, acc:=1)
{
  step := 15 * acc
  if (op == "UP") {
    MouseMove, 0, -step, 0, R
  } else if (op == "DOWN") {
    MouseMove, 0,  step, 0, R
  } else if (op == "LEFT") {
    MouseMove, -step, 0, 0, R
  } else if (op == "RIGHT") {
    MouseMove,  step, 0, 0, R
  } else if (op == "LCLICK") {
    Click
  } else if (op == "RCLICK") {
    Click, right
  } else if (op == "WHEELUP") {
    Click, WheelUp
  } else if (op == "WHEELDOWN") {
    Click, WheelDown
  }
}

;---------------------------------------------------------------
;--- sound volume control
#PgUp::Send {Volume_Up 2}
#PgDn::Send {Volume_Down 2}

;--- keyboard mouse
#If GetKeyState("CapsLock", "T")
~Space::Shift
i::MouseOperation("UP")
k::MouseOperation("DOWN")
j::MouseOperation("LEFT")
l::MouseOperation("RIGHT")
f::MouseOperation("LCLICK")
s::MouseOperation("RCLICK")
y::MouseOperation("WHEELUP")
h::MouseOperation("WHEELDOWN")
;-- accelerated --
+i::MouseOperation("UP", 5)
+k::MouseOperation("DOWN", 5)
+j::MouseOperation("LEFT", 5)
+l::MouseOperation("RIGHT", 5)
#If ;GetKeyState("CapsLock", "T")

;--- IME switch
;; SC029::Send {vkF3sc029}

#If DebugP
!^r::Reload


fix issue #6 - JS not always running on page load

; Webapp.ahk
; -------------------------------------------------------
; "Webapps made easy!"
; Library for developing web-based apps with AutoHotkey.
; Released under the MIT License
; More info here: https://github.com/joedf/Webapp.ahk
; -------------------------------------------------------
;
; AutoHotkey Tested Version: 1.1.24.03
; Dev Platform:   Windows 10 x64
; Author:         Joe DF  |  http://joedf.ahkscript.org  |  joedf@ahkscript.org
; Date:           15:40 2016/11/19
; Revision:       2

#NoEnv
SetWorkingDir %A_ScriptDir%

; https://autohotkey.com/board/topic/62512-permanently-turn-off-that-annoying-hotkey-limit/
#MaxHotkeysPerInterval 500
#UseHook

__Webapp_jsonf := "webapp.json"
FileRead,data,%__Webapp_jsonf%
j:=JSON_ToObj(data)

try {
	__Webapp_Name := __Webapp_DefaultVar(j.name,"My App")
	__Webapp_Width := __Webapp_DefaultVar(j.width,640)
	__Webapp_height := __Webapp_DefaultVar(j.height,480)
	__Webapp_protocol := __Webapp_DefaultVar(j.protocol,"app")
	__Webapp_Nav_sounds := __Webapp_DefaultVar(j.Nav_sounds,0)
	__Webapp_FullScreen := __Webapp_DefaultVar(j.fullscreen,0)
	__Webapp_DPI_Aware := __Webapp_DefaultVar(j.DPI_Aware,1)
	__Webapp_ZoomLevel := __Webapp_DefaultVar(j.ZoomLevel,100)
	__Webapp_AllowZoomLevelChange := __Webapp_DefaultVar(j.AllowZoomLevelChange,1)
	__Webapp_protocol_call := __Webapp_DefaultVar(j.protocol_call,"app_call")
		if !IsFunc(__Webapp_protocol_call)
			throw "Function Name '" . __Webapp_protocol_call . "' does not exist."
		__Webapp_protocol_call:=Func(__Webapp_protocol_call)
	__Webapp_NavComplete_call := __Webapp_DefaultVar(j.NavComplete_call,"app_page")
		if !IsFunc(__Webapp_NavComplete_call)
			throw "Function Name '" . __Webapp_NavComplete_call . "' does not exist."
		__Webapp_NavComplete_call:=Func(__Webapp_NavComplete_call)
	__Webapp_html_url := __Webapp_DefaultVar(j.html_url,"index.html")
		if !FileExist(__Webapp_html_url)
			throw "File '" . __Webapp_html_url . "' does not exist."
		__Webapp_html_url := getFileFullPath(__Webapp_html_url)
} catch e {
	err:= e . "`nPlease verify the '" . __Webapp_jsonf . "' file."
	if (A_LastError)
		err .= "`n" . Trim(GetErrorMessage(A_LastError),"`r`n") . "`nA_LastError = " . A_LastError
	ErrorExit(err)
}

OnExit,__Webapp_GuiClose
Gui __Webapp_:New
Gui __Webapp_:Margin, 0, 0
if (!__Webapp_DPI_Aware)
	Gui __Webapp_:-DPIScale
Gui __Webapp_:+LastFound +Resize +Hwnd__Webapp_GuiHwnd

; Register Messages
OnMessage(WM_KEYDOWN := 0x100, "gui_KeyDown", 2)
if (!__Webapp_AllowZoomLevelChange) {
	OnMessage(WM_MOUSEWHEEL = 0x20A, "gui_WheelScroll")

	Hotkey, IfWinActive, ahk_id %__Webapp_GuiHwnd%
		Hotkey, ^WheelDown, __Webapp_DoNothing
		Hotkey, ^WheelUp, __Webapp_DoNothing
	Hotkey, If
}

Gui __Webapp_:Add, ActiveX, v__Webapp_wb w%__Webapp_Width% h%__Webapp_height%, Shell.Explorer
SetWBClientSite()
__Webapp_wb.silent := true ;Surpress JS Error boxes
;__Webapp_wb.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible' content='IE=edge'>")
__Webapp_wb.Navigate("file://" . __Webapp_html_url)

if (InStr(__Webapp_Nav_sounds,"false") || __Webapp_Nav_sounds==0) {
	; Thanks MrBubbles
	; https://autohotkey.com/boards/viewtopic.php?p=117029#p117029
	DllCall("urlmon\CoInternetSetFeatureEnabled"
			,"Int",  21 ; FEATURE_DISABLE_NAVIGATION_SOUNDS
			,"UInt", 0x00000002 ; SET_FEATURE_ON_PROCESS
			,"Int", 1)
}

ComObjConnect(__Webapp_wb, __Webapp_wb_events)
__Webapp_w := __Webapp_wb.Document.parentWindow
__Webapp_wf := Func("JS_AHK")
__Webapp_w.AHK := __Webapp_wf
;Wait for IE to load the page, before we connect the event handlers
while __Webapp_wb.readystate != 4 or __Webapp_wb.busy
	sleep 10

Gui __Webapp_:Show, w%__Webapp_Width% h%__Webapp_height%, %__Webapp_Name%
Gui __Webapp_:Default
GroupAdd, __Webapp_windows, ahk_id %__Webapp_GuiHwnd%

setZoomLevel( __Webapp_ZoomLevel )

if (__Webapp_FullScreen) {
	setFullscreen(__Webapp_FullScreen)
}
goto,__Webapp_AppStart
return

~LButton:: ;//cheap fix to reattach JS_AHK
gosub,__Webapp_JS_AHK_Attach
return

__Webapp_JS_AHK_Attach:
	 __Webapp_wb.Document.parentWindow.AHK := __Webapp_wf
return

__Webapp_GuiSize:
	GuiControl, __Webapp_:Move, __Webapp_wb, W%A_GuiWidth% H%A_GuiHeight%
return

__Webapp_GuiEscape:
	MsgBox 0x34, Webapp.ahk, Are you sure you want to quit?
	IfMsgBox No
		return
__Webapp_GuiClose:
	;make sure taskbar is back on exit
	WinShow, ahk_class Shell_TrayWnd
	WinShow, Start ahk_class Button
	Gui __Webapp_:Destroy
	ExitApp
return

__Webapp_DoNothing:
return

class __Webapp_wb_events
{
	;for more events and other, see http://msdn.microsoft.com/en-us/library/aa752085

	 ;blocked all navigation, we want our own stuff happening
	NavigateComplete2(wb, NewURL) {
		; wb.Stop() ;not needed in this one.
	}
	DownloadComplete(wb, NewURL) {
		wb.Stop()
	}
	DocumentComplete(wb, NewURL) {
		global __Webapp_NavComplete_call
		__Webapp_NavComplete_call.call(NewURL)
		wb.Stop()
	}
	NavigateError(wb, NewURL) {
		wb.Stop()
	}

	BeforeNavigate2(wb, NewURL)
	{
		wb.Stop() ;blocked all navigation, we want our own stuff happening

		global __Webapp_protocol
		global __Webapp_protocol_call

		;parse the url
		if (__Webapp_protocol == "*") ;if * then all goes to the function call, ie #nofilter ;)
			__Webapp_protocol_call.call(NewURL)
		else {
			if (InStr(NewURL,__Webapp_protocol "://")==1) { ;if url starts with "myapp://"
				what := SubStr(NewURL,Strlen(__Webapp_protocol)+4) ;get stuff after "myapp://"
				__Webapp_protocol_call.call(what)
			}
		}
		;else do nothing
	}
}

/* Autoconnect inputs by Phatricko - https://autohotkey.com/boards/viewtopic.php?p=102421#p102421
loop % wb.document.getElementsByTagName("input").length{
    ;ComObjConnect needs a unique variable for each iteration for all to work
    button%a_index% := wb.document.getElementsByTagName("input")[A_Index-1]
    id := button%a_index%.id
    ComObjConnect(button%a_index%, id . "_")
}
*/

__Webapp_DefaultVar(a,b) {
	if Instr(a,"false",false)
		return false
	if !StrLen(a)
		return b
	return a
}

;javascript:AHK('Func') --> Func()
JS_AHK(func, prms*) {
	wb := getDOM()
	; Stop navigation prior to calling the function, in case it uses Exit.
	wb.Stop()
	return %func%(prms*)
}

getWindow() {
	wb := getDOM()
	return wb.document.parentWindow
}

getDOM(){
	global __Webapp_wb
	return __Webapp_wb
}

setAppName(n) {
	global __Webapp_Name
	__Webapp_Name := __Webapp_DefaultVar(n,"My App")
	Gui, __Webapp_:Show, , %__Webapp_Name%
}

setFullscreen(bool) {
	global __Webapp_GuiHwnd
	global __Webapp_Width
	global __Webapp_height
	global __Webapp_FullScreen

	if (bool) {
		;Save old GUI values
		WinGetPos,nFS_x,nFS_y,__Webapp_Width,__Webapp_height,ahk_id %__Webapp_GuiHwnd%

		;Set New GUI Values
		SysGet, caption, 31
		SysGet, borderH, 32
		SysGet, borderV, 33
		FS_x := 0 - (borderH)
		FS_y := 0 - (caption) - (borderV)
		FS_w := A_ScreenWidth + (borderH*2)
		FS_h := A_ScreenHeight + (caption) + (borderV*2)

		;hide taskbar
		;WinSet,AlwaysOnTop,On,ahk_id %__Webapp_GuiHwnd%
		WinHide, ahk_class Shell_TrayWnd
		WinHide, Start ahk_class Button

		WinMove,ahk_id %__Webapp_GuiHwnd%,,%FS_x%,%FS_y%,%FS_w%,%FS_h%
		WinActivate,ahk_id %__Webapp_GuiHwnd% ;avoid bug
		__Webapp_FullScreen:=true ;FULLSCREEN = TRUE
	} else {
		nFS_x := (A_ScreenWidth//2) - (__Webapp_Width//2)
		nFS_y := (A_ScreenHeight//2) - (__Webapp_height//2)
		nFS_w := __Webapp_Width
		nFS_h := __Webapp_height
		WinMove,ahk_id %__Webapp_GuiHwnd%,,%nFS_x%,%nFS_y%,%nFS_w%,%nFS_h%

		;show taskbar
		;WinSet,AlwaysOnTop,Off,ahk_id %__Webapp_GuiHwnd%
		WinShow, ahk_class Shell_TrayWnd
		WinShow, Start ahk_class Button

		WinActivate,ahk_id %__Webapp_GuiHwnd% ;avoid bug
		__Webapp_FullScreen:=false ;FULLSCREEN = FALSE
	}
}

setZoomLevel(z) {
	global __Webapp_DPI_Aware

	; https://stackoverflow.com/a/738254/883015
	OLECMDID_OPTICAL_ZOOM := 63
	OLECMDID_OPTICAL_GETZOOMRANGE := 64
	OLECMDEXECOPT_DONTPROMPTUSER :=  2 ;?  https://social.msdn.microsoft.com/Forums/ie/en-US/21d2bc41-d182-4af6-81e9-6e06793058b9
	wb := getDOM()

	if (__Webapp_DPI_Aware) {
		DPI_Scale := A_ScreenDPI/96
		z := z * DPI_Scale
	}

	; ensure INT
	z := z & 0xFFFFFFFF

	wb.ExecWB( OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, z, 0)
}

ErrorExit(errMsg) {
	MsgBox 16, Webapp.ahk, %errMsg%
	ExitApp 1
}

;{ other stuff

GetErrorMessage(error_code="") {
	VarSetCapacity(buf, 1024) ; Probably won't exceed 1024 chars.
	if DllCall("FormatMessage", "uint", 0x1200, "ptr", 0, "int", error_code!=""
		? error_code : A_LastError, "uint", 1024, "str", buf, "uint", 1024, "ptr", 0)
	return buf
}

getFileFullPath(f) {
	Loop, Files, %f%
	{
		if (A_LoopFileLongPath)
			return A_LoopFileLongPath
	}
}

/*  Complex workaround to override "Active scripting" setting
 *  and ensure scripts can run within the WebBrowser control.
 */

global WBClientSite

SetWBClientSite()
{
	interfaces := {
	(Join,
		IOleClientSite: [0,3,1,0,1,0]
		IServiceProvider: [3]
		IInternetSecurityManager: [1,1,3,4,8,7,3,3]
	)}
	unkQI      := RegisterCallback("WBClientSite_QI", "Fast")
	unkAddRef  := RegisterCallback("WBClientSite_AddRef", "Fast")
	unkRelease := RegisterCallback("WBClientSite_Release", "Fast")
	WBClientSite := {_buffers: bufs := {}}, bufn := 0,
	for name, prms in interfaces
	{
		bufn += 1
		bufs.SetCapacity(bufn, (4 + prms.MaxIndex()) * A_PtrSize)
		buf := bufs.GetAddress(bufn)
		NumPut(unkQI,       buf + 1*A_PtrSize)
		NumPut(unkAddRef,   buf + 2*A_PtrSize)
		NumPut(unkRelease,  buf + 3*A_PtrSize)
		for i, prmc in prms
			NumPut(RegisterCallback("WBClientSite_" name, "Fast", prmc+1, i), buf + (3+i)*A_PtrSize)
		NumPut(buf + A_PtrSize, buf + 0)
		WBClientSite[name] := buf
	}
	wb := getDOM()
	if pOleObject := ComObjQuery(wb, "{00000112-0000-0000-C000-000000000046}")
	{   ; IOleObject::SetClientSite
		DllCall(NumGet(NumGet(pOleObject+0)+3*A_PtrSize), "ptr"
			, pOleObject, "ptr", WBClientSite.IOleClientSite, "uint")
		ObjRelease(pOleObject)
	}
}

WBClientSite_QI(p, piid, ppvObject)
{
	static IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
	static IID_IOleClientSite := "{00000118-0000-0000-C000-000000000046}"
	static IID_IServiceProvider := "{6d5140c1-7436-11ce-8034-00aa006009fa}"
	iid := _String4GUID(piid)
	if (iid = IID_IOleClientSite || iid = IID_IUnknown)
	{
		NumPut(WBClientSite.IOleClientSite, ppvObject+0)
		return 0 ; S_OK
	}
	if (iid = IID_IServiceProvider)
	{
		NumPut(WBClientSite.IServiceProvider, ppvObject+0)
		return 0 ; S_OK
	}
	NumPut(0, ppvObject+0)
	return 0x80004002 ; E_NOINTERFACE
}

WBClientSite_AddRef(p)
{
	return 1
}

WBClientSite_Release(p)
{
	return 1
}

WBClientSite_IOleClientSite(p, p1="", p2="", p3="")
{
	if (A_EventInfo = 3) ; GetContainer
	{
		NumPut(0, p1+0) ; *ppContainer := NULL
		return 0x80004002 ; E_NOINTERFACE
	}
	return 0x80004001 ; E_NOTIMPL
}

WBClientSite_IServiceProvider(p, pguidService, piid, ppvObject)
{
	static IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
	static IID_IInternetSecurityManager := "{79eac9ee-baf9-11ce-8c82-00aa004ba90b}"
	if (_String4GUID(pguidService) = IID_IInternetSecurityManager)
	{
		iid := _String4GUID(piid)
		if (iid = IID_IInternetSecurityManager || iid = IID_IUnknown)
		{
			NumPut(WBClientSite.IInternetSecurityManager, ppvObject+0)
			return 0 ; S_OK
		}
		NumPut(0, ppvObject+0)
		return 0x80004002 ; E_NOINTERFACE
	}
	NumPut(0, ppvObject+0)
	return 0x80004001 ; E_NOTIMPL
}

WBClientSite_IInternetSecurityManager(p, p1="", p2="", p3="", p4="", p5="", p6="", p7="", p8="")
{
	if (A_EventInfo = 5) ; ProcessUrlAction
	{
		if (p2 = 0x1400) ; dwAction = URLACTION_SCRIPT_RUN
		{
			NumPut(0, p3+0)  ; *pPolicy := URLPOLICY_ALLOW
			return 0 ; S_OK
		}
	}
	return 0x800C0011 ; INET_E_DEFAULT_ACTION
}

_String4GUID(pGUID)
{
	VarSetCapacity(String,38*2)
	DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
	Return	String
}

/*  Fix keyboard shortcuts in WebBrowser control.
 *  References:
 *    http://www.autohotkey.com/community/viewtopic.php?p=186254#p186254
 *    http://msdn.microsoft.com/en-us/library/ms693360
 */

gui_KeyDown(wParam, lParam, nMsg, hWnd) {
	global __Webapp_AllowZoomLevelChange

	wb := getDOM()

	if (Chr(wParam) ~= "[A-Z]" || wParam = 0x74) ; Disable Ctrl+O/L/F/N and F5.
		return

	; joedf: optionally disable ctrl zooming
	if (!__Webapp_AllowZoomLevelChange) {
		if(GetKeyState("CTRL") && (wParam=187 || wParam=189)) ;CTRL+ CTRL-
			return
	}

	pipa := ComObjQuery(wb, "{00000117-0000-0000-C000-000000000046}")
	VarSetCapacity(kMsg, 48), NumPut(A_GuiY, NumPut(A_GuiX
	, NumPut(A_EventInfo, NumPut(lParam, NumPut(wParam
	, NumPut(nMsg, NumPut(hWnd, kMsg)))), "uint"), "int"), "int")
	Loop 2
	r := DllCall(NumGet(NumGet(1*pipa)+5*A_PtrSize), "ptr", pipa, "ptr", &kMsg)
	; Loop to work around an odd tabbing issue (it's as if there
	; is a non-existent element at the end of the tab order).
	until wParam != 9 || wb.Document.activeElement != ""
	ObjRelease(pipa)
	if r = 0 ; S_OK: the message was translated to an accelerator.
		return 0
}

gui_WheelScroll(wParam, lParam) {
	if(GET_KEYSTATE_WPARAM(wParam)==8)
		return 1
}

GET_KEYSTATE_WPARAM(wparam) {
	return (wparam & 0xFFFF)
}

;}

;-----end-----

; Webapp.ahk
; -------------------------------------------------------
; "Webapps made easy!"
; Library for developing web-based apps with AutoHotkey.
; Released under the MIT License
; More info here: https://github.com/joedf/Webapp.ahk
; -------------------------------------------------------
;
; AutoHotkey Tested Version: 1.1.24.03
; Dev Platform:   Windows 10 x64
; Author:         Joe DF  |  http://joedf.ahkscript.org  |  joedf@ahkscript.org
; Date:           15:40 2016/11/19
; Revision:       2

#NoEnv
SetWorkingDir %A_ScriptDir%

; https://autohotkey.com/board/topic/62512-permanently-turn-off-that-annoying-hotkey-limit/
#MaxHotkeysPerInterval 500
#UseHook

__Webapp_jsonf := "webapp.json"
FileRead,data,%__Webapp_jsonf%
j:=JSON_ToObj(data)

try {
	__Webapp_Name := __Webapp_DefaultVar(j.name,"My App")
	__Webapp_Width := __Webapp_DefaultVar(j.width,640)
	__Webapp_height := __Webapp_DefaultVar(j.height,480)
	__Webapp_protocol := __Webapp_DefaultVar(j.protocol,"app")
	__Webapp_Nav_sounds := __Webapp_DefaultVar(j.Nav_sounds,0)
	__Webapp_FullScreen := __Webapp_DefaultVar(j.fullscreen,0)
	__Webapp_DPI_Aware := __Webapp_DefaultVar(j.DPI_Aware,1)
	__Webapp_ZoomLevel := __Webapp_DefaultVar(j.ZoomLevel,100)
	__Webapp_AllowZoomLevelChange := __Webapp_DefaultVar(j.AllowZoomLevelChange,1)
	__Webapp_protocol_call := __Webapp_DefaultVar(j.protocol_call,"app_call")
		if !IsFunc(__Webapp_protocol_call)
			throw "Function Name '" . __Webapp_protocol_call . "' does not exist."
		__Webapp_protocol_call:=Func(__Webapp_protocol_call)
	__Webapp_NavComplete_call := __Webapp_DefaultVar(j.NavComplete_call,"app_page")
		if !IsFunc(__Webapp_NavComplete_call)
			throw "Function Name '" . __Webapp_NavComplete_call . "' does not exist."
		__Webapp_NavComplete_call:=Func(__Webapp_NavComplete_call)
	__Webapp_html_url := __Webapp_DefaultVar(j.html_url,"index.html")
		if !FileExist(__Webapp_html_url)
			throw "File '" . __Webapp_html_url . "' does not exist."
		__Webapp_html_url := getFileFullPath(__Webapp_html_url)
} catch e {
	err:= e . "`nPlease verify the '" . __Webapp_jsonf . "' file."
	if (A_LastError)
		err .= "`n" . Trim(GetErrorMessage(A_LastError),"`r`n") . "`nA_LastError = " . A_LastError
	ErrorExit(err)
}

OnExit,__Webapp_GuiClose
Gui __Webapp_:New
Gui __Webapp_:Margin, 0, 0
if (!__Webapp_DPI_Aware)
	Gui __Webapp_:-DPIScale
Gui __Webapp_:+LastFound +Resize +Hwnd__Webapp_GuiHwnd

; Register Messages
OnMessage(WM_KEYDOWN := 0x100, "gui_KeyDown", 2)
if (!__Webapp_AllowZoomLevelChange) {
	OnMessage(WM_MOUSEWHEEL = 0x20A, "gui_WheelScroll")

	Hotkey, IfWinActive, ahk_id %__Webapp_GuiHwnd%
		Hotkey, ^WheelDown, __Webapp_DoNothing
		Hotkey, ^WheelUp, __Webapp_DoNothing
	Hotkey, If
}

Gui __Webapp_:Add, ActiveX, v__Webapp_wb w%__Webapp_Width% h%__Webapp_height%, Shell.Explorer
SetWBClientSite()
__Webapp_wb.silent := true ;Surpress JS Error boxes
;__Webapp_wb.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible' content='IE=edge'>")
__Webapp_wb.Navigate("file://" . __Webapp_html_url)

if (InStr(__Webapp_Nav_sounds,"false") || __Webapp_Nav_sounds==0) {
	; Thanks MrBubbles
	; https://autohotkey.com/boards/viewtopic.php?p=117029#p117029
	DllCall("urlmon\CoInternetSetFeatureEnabled"
			,"Int",  21 ; FEATURE_DISABLE_NAVIGATION_SOUNDS
			,"UInt", 0x00000002 ; SET_FEATURE_ON_PROCESS
			,"Int", 1)
}

ComObjConnect(__Webapp_wb, __Webapp_wb_events)
__Webapp_w := __Webapp_wb.Document.parentWindow
__Webapp_wf := Func("JS_AHK")
__Webapp_w.AHK := __Webapp_wf
;Wait for IE to load the page, before we connect the event handlers
while __Webapp_wb.readystate != 4 or __Webapp_wb.busy
	sleep 10

Gui __Webapp_:Show, w%__Webapp_Width% h%__Webapp_height%, %__Webapp_Name%
Gui __Webapp_:Default
GroupAdd, __Webapp_windows, ahk_id %__Webapp_GuiHwnd%

setZoomLevel( __Webapp_ZoomLevel )

if (__Webapp_FullScreen) {
	setFullscreen(__Webapp_FullScreen)
}
goto,__Webapp_AppStart
return

~LButton:: ;//cheap fix to reattach JS_AHK
gosub,__Webapp_JS_AHK_Attach
return

__Webapp_JS_AHK_Attach:
	 __Webapp_wb.Document.parentWindow.AHK := __Webapp_wf
return

__Webapp_GuiSize:
	GuiControl, __Webapp_:Move, __Webapp_wb, W%A_GuiWidth% H%A_GuiHeight%
return

__Webapp_GuiEscape:
	MsgBox 0x34, Webapp.ahk, Are you sure you want to quit?
	IfMsgBox No
		return
__Webapp_GuiClose:
	;make sure taskbar is back on exit
	WinShow, ahk_class Shell_TrayWnd
	WinShow, Start ahk_class Button
	Gui __Webapp_:Destroy
	ExitApp
return

__Webapp_DoNothing:
return

class __Webapp_wb_events
{
	;for more events and other, see http://msdn.microsoft.com/en-us/library/aa752085

	 ;blocked all navigation, we want our own stuff happening
	NavigateComplete2(wb, NewURL) {
		; wb.Stop() ;not needed in this one.
		global __Webapp_NavComplete_call
		__Webapp_NavComplete_call.call(NewURL)
	}
	DownloadComplete(wb, NewURL) {
		wb.Stop()
	}
	DocumentComplete(wb, NewURL) {
		wb.Stop()
	}
	NavigateError(wb, NewURL) {
		wb.Stop()
	}

	BeforeNavigate2(wb, NewURL)
	{
		wb.Stop() ;blocked all navigation, we want our own stuff happening

		global __Webapp_protocol
		global __Webapp_protocol_call

		;parse the url
		if (__Webapp_protocol == "*") ;if * then all goes to the function call, ie #nofilter ;)
			__Webapp_protocol_call.call(NewURL)
		else {
			if (InStr(NewURL,__Webapp_protocol "://")==1) { ;if url starts with "myapp://"
				what := SubStr(NewURL,Strlen(__Webapp_protocol)+4) ;get stuff after "myapp://"
				__Webapp_protocol_call.call(what)
			}
		}
		;else do nothing
	}
}

/* Autoconnect inputs by Phatricko - https://autohotkey.com/boards/viewtopic.php?p=102421#p102421
loop % wb.document.getElementsByTagName("input").length{
    ;ComObjConnect needs a unique variable for each iteration for all to work
    button%a_index% := wb.document.getElementsByTagName("input")[A_Index-1]
    id := button%a_index%.id
    ComObjConnect(button%a_index%, id . "_")
}
*/

__Webapp_DefaultVar(a,b) {
	if Instr(a,"false",false)
		return false
	if !StrLen(a)
		return b
	return a
}

;javascript:AHK('Func') --> Func()
JS_AHK(func, prms*) {
	wb := getDOM()
	; Stop navigation prior to calling the function, in case it uses Exit.
	wb.Stop()
	return %func%(prms*)
}

getWindow() {
	wb := getDOM()
	return wb.document.parentWindow
}

getDOM(){
	global __Webapp_wb
	return __Webapp_wb
}

setAppName(n) {
	global __Webapp_Name
	__Webapp_Name := __Webapp_DefaultVar(n,"My App")
	Gui, __Webapp_:Show, , %__Webapp_Name%
}

setFullscreen(bool) {
	global __Webapp_GuiHwnd
	global __Webapp_Width
	global __Webapp_height
	global __Webapp_FullScreen

	if (bool) {
		;Save old GUI values
		WinGetPos,nFS_x,nFS_y,__Webapp_Width,__Webapp_height,ahk_id %__Webapp_GuiHwnd%

		;Set New GUI Values
		SysGet, caption, 31
		SysGet, borderH, 32
		SysGet, borderV, 33
		FS_x := 0 - (borderH)
		FS_y := 0 - (caption) - (borderV)
		FS_w := A_ScreenWidth + (borderH*2)
		FS_h := A_ScreenHeight + (caption) + (borderV*2)

		;hide taskbar
		;WinSet,AlwaysOnTop,On,ahk_id %__Webapp_GuiHwnd%
		WinHide, ahk_class Shell_TrayWnd
		WinHide, Start ahk_class Button

		WinMove,ahk_id %__Webapp_GuiHwnd%,,%FS_x%,%FS_y%,%FS_w%,%FS_h%
		WinActivate,ahk_id %__Webapp_GuiHwnd% ;avoid bug
		__Webapp_FullScreen:=true ;FULLSCREEN = TRUE
	} else {
		nFS_x := (A_ScreenWidth//2) - (__Webapp_Width//2)
		nFS_y := (A_ScreenHeight//2) - (__Webapp_height//2)
		nFS_w := __Webapp_Width
		nFS_h := __Webapp_height
		WinMove,ahk_id %__Webapp_GuiHwnd%,,%nFS_x%,%nFS_y%,%nFS_w%,%nFS_h%

		;show taskbar
		;WinSet,AlwaysOnTop,Off,ahk_id %__Webapp_GuiHwnd%
		WinShow, ahk_class Shell_TrayWnd
		WinShow, Start ahk_class Button

		WinActivate,ahk_id %__Webapp_GuiHwnd% ;avoid bug
		__Webapp_FullScreen:=false ;FULLSCREEN = FALSE
	}
}

setZoomLevel(z) {
	global __Webapp_DPI_Aware

	; https://stackoverflow.com/a/738254/883015
	OLECMDID_OPTICAL_ZOOM := 63
	OLECMDID_OPTICAL_GETZOOMRANGE := 64
	OLECMDEXECOPT_DONTPROMPTUSER :=  2 ;?  https://social.msdn.microsoft.com/Forums/ie/en-US/21d2bc41-d182-4af6-81e9-6e06793058b9
	wb := getDOM()

	if (__Webapp_DPI_Aware) {
		DPI_Scale := A_ScreenDPI/96
		z := z * DPI_Scale
	}

	; ensure INT
	z := z & 0xFFFFFFFF

	wb.ExecWB( OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, z, 0)
}

ErrorExit(errMsg) {
	MsgBox 16, Webapp.ahk, %errMsg%
	ExitApp 1
}

;{ other stuff

GetErrorMessage(error_code="") {
	VarSetCapacity(buf, 1024) ; Probably won't exceed 1024 chars.
	if DllCall("FormatMessage", "uint", 0x1200, "ptr", 0, "int", error_code!=""
		? error_code : A_LastError, "uint", 1024, "str", buf, "uint", 1024, "ptr", 0)
	return buf
}

getFileFullPath(f) {
	Loop, Files, %f%
	{
		if (A_LoopFileLongPath)
			return A_LoopFileLongPath
	}
}

/*  Complex workaround to override "Active scripting" setting
 *  and ensure scripts can run within the WebBrowser control.
 */

global WBClientSite

SetWBClientSite()
{
	interfaces := {
	(Join,
		IOleClientSite: [0,3,1,0,1,0]
		IServiceProvider: [3]
		IInternetSecurityManager: [1,1,3,4,8,7,3,3]
	)}
	unkQI      := RegisterCallback("WBClientSite_QI", "Fast")
	unkAddRef  := RegisterCallback("WBClientSite_AddRef", "Fast")
	unkRelease := RegisterCallback("WBClientSite_Release", "Fast")
	WBClientSite := {_buffers: bufs := {}}, bufn := 0,
	for name, prms in interfaces
	{
		bufn += 1
		bufs.SetCapacity(bufn, (4 + prms.MaxIndex()) * A_PtrSize)
		buf := bufs.GetAddress(bufn)
		NumPut(unkQI,       buf + 1*A_PtrSize)
		NumPut(unkAddRef,   buf + 2*A_PtrSize)
		NumPut(unkRelease,  buf + 3*A_PtrSize)
		for i, prmc in prms
			NumPut(RegisterCallback("WBClientSite_" name, "Fast", prmc+1, i), buf + (3+i)*A_PtrSize)
		NumPut(buf + A_PtrSize, buf + 0)
		WBClientSite[name] := buf
	}
	wb := getDOM()
	if pOleObject := ComObjQuery(wb, "{00000112-0000-0000-C000-000000000046}")
	{   ; IOleObject::SetClientSite
		DllCall(NumGet(NumGet(pOleObject+0)+3*A_PtrSize), "ptr"
			, pOleObject, "ptr", WBClientSite.IOleClientSite, "uint")
		ObjRelease(pOleObject)
	}
}

WBClientSite_QI(p, piid, ppvObject)
{
	static IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
	static IID_IOleClientSite := "{00000118-0000-0000-C000-000000000046}"
	static IID_IServiceProvider := "{6d5140c1-7436-11ce-8034-00aa006009fa}"
	iid := _String4GUID(piid)
	if (iid = IID_IOleClientSite || iid = IID_IUnknown)
	{
		NumPut(WBClientSite.IOleClientSite, ppvObject+0)
		return 0 ; S_OK
	}
	if (iid = IID_IServiceProvider)
	{
		NumPut(WBClientSite.IServiceProvider, ppvObject+0)
		return 0 ; S_OK
	}
	NumPut(0, ppvObject+0)
	return 0x80004002 ; E_NOINTERFACE
}

WBClientSite_AddRef(p)
{
	return 1
}

WBClientSite_Release(p)
{
	return 1
}

WBClientSite_IOleClientSite(p, p1="", p2="", p3="")
{
	if (A_EventInfo = 3) ; GetContainer
	{
		NumPut(0, p1+0) ; *ppContainer := NULL
		return 0x80004002 ; E_NOINTERFACE
	}
	return 0x80004001 ; E_NOTIMPL
}

WBClientSite_IServiceProvider(p, pguidService, piid, ppvObject)
{
	static IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
	static IID_IInternetSecurityManager := "{79eac9ee-baf9-11ce-8c82-00aa004ba90b}"
	if (_String4GUID(pguidService) = IID_IInternetSecurityManager)
	{
		iid := _String4GUID(piid)
		if (iid = IID_IInternetSecurityManager || iid = IID_IUnknown)
		{
			NumPut(WBClientSite.IInternetSecurityManager, ppvObject+0)
			return 0 ; S_OK
		}
		NumPut(0, ppvObject+0)
		return 0x80004002 ; E_NOINTERFACE
	}
	NumPut(0, ppvObject+0)
	return 0x80004001 ; E_NOTIMPL
}

WBClientSite_IInternetSecurityManager(p, p1="", p2="", p3="", p4="", p5="", p6="", p7="", p8="")
{
	if (A_EventInfo = 5) ; ProcessUrlAction
	{
		if (p2 = 0x1400) ; dwAction = URLACTION_SCRIPT_RUN
		{
			NumPut(0, p3+0)  ; *pPolicy := URLPOLICY_ALLOW
			return 0 ; S_OK
		}
	}
	return 0x800C0011 ; INET_E_DEFAULT_ACTION
}

_String4GUID(pGUID)
{
	VarSetCapacity(String,38*2)
	DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
	Return	String
}

/*  Fix keyboard shortcuts in WebBrowser control.
 *  References:
 *    http://www.autohotkey.com/community/viewtopic.php?p=186254#p186254
 *    http://msdn.microsoft.com/en-us/library/ms693360
 */

gui_KeyDown(wParam, lParam, nMsg, hWnd) {
	global __Webapp_AllowZoomLevelChange

	wb := getDOM()

	if (Chr(wParam) ~= "[A-Z]" || wParam = 0x74) ; Disable Ctrl+O/L/F/N and F5.
		return

	; joedf: optionally disable ctrl zooming
	if (!__Webapp_AllowZoomLevelChange) {
		if(GetKeyState("CTRL") && (wParam=187 || wParam=189)) ;CTRL+ CTRL-
			return
	}

	pipa := ComObjQuery(wb, "{00000117-0000-0000-C000-000000000046}")
	VarSetCapacity(kMsg, 48), NumPut(A_GuiY, NumPut(A_GuiX
	, NumPut(A_EventInfo, NumPut(lParam, NumPut(wParam
	, NumPut(nMsg, NumPut(hWnd, kMsg)))), "uint"), "int"), "int")
	Loop 2
	r := DllCall(NumGet(NumGet(1*pipa)+5*A_PtrSize), "ptr", pipa, "ptr", &kMsg)
	; Loop to work around an odd tabbing issue (it's as if there
	; is a non-existent element at the end of the tab order).
	until wParam != 9 || wb.Document.activeElement != ""
	ObjRelease(pipa)
	if r = 0 ; S_OK: the message was translated to an accelerator.
		return 0
}

gui_WheelScroll(wParam, lParam) {
	if(GET_KEYSTATE_WPARAM(wParam)==8)
		return 1
}

GET_KEYSTATE_WPARAM(wparam) {
	return (wparam & 0xFFFF)
}

;}

;-----end-----


Change hotkey of Media_Play_Pause to Win+Z.

#Include, %A_ScriptDir%
#InstallKeybdHook
#SingleInstance, Force

#Include, CommonObjectFunctions.ahk
#Include, Win32KeyboardFunctions.ahk
#Include, ToggleKeyboard.ahk

; ====================================================================

#Include, Config.ahk

InitKeyboardsData()

SetTitleMatchMode, RegEx
SetCapsLockState, AlwaysOff

; ====================================================================

#If, WinOPlayerPlayPause = 1

#SC02C::Send, {Media_Play_Pause}

#If, FixPersianSymbolKeys = 1

$+1::Send, {!}
$+2::Send, {@}
$+3::Send, {#}
$+4::Send, {$}
;$+5::Send, {`%}
$+6::Send, {^}
;$+7::Send, {&}
$+8::Send, {*}

#If, DisableInsert = 1

Insert::Return

#If,

; ====================================================================

#If, BackTickLanguageChange = 1 Or FixPersianSymbolKeys = 1

$`::
	Send, {``}
	If (BackTickLanguageChange = 1)
	{
		ToggleKeyboard("")
	}
	Return

#If,

Capslock::
	ToggleKeyboard("")
	Return

<^Capslock::
	ToggleKeyboard("Left")
	Return

>^Capslock::
	ToggleKeyboard("Right")
	Return

+Capslock::
	ToggleKeyboard("Normal")
	Return

!Capslock::
	T := GetKeyboardLayout()
	H := Format("{1:016X}", T)
	H := SubStr(H, 9)
	MsgBox, 0x%H%
	Return

Capslock & Tab::
	If (GetKeyState("Capslock", "T") = 1)
	{
		SetCapsLockState, AlwaysOff
	}
	Else
	{
		SetCapsLockState, AlwaysOn
	}
	Return

Capslock & `::
	BackTickLanguageChange := 1 - BackTickLanguageChange
	MsgBox, BackTickLanguageChange = %BackTickLanguageChange%
	Return

Capslock & Insert::
	DisableInsert := 1 - DisableInsert
	MsgBox, DisableInsert = %DisableInsert%
	Return

Capslock & \::
	FixPersianSymbolKeys := 1 - FixPersianSymbolKeys
	MsgBox, FixPersianSymbolKeys = %FixPersianSymbolKeys%
	Return

; ====================================================================

#Include, GitGui.ahk

#Include, %A_ScriptDir%
#InstallKeybdHook
#SingleInstance, Force

#Include, CommonObjectFunctions.ahk
#Include, Win32KeyboardFunctions.ahk
#Include, ToggleKeyboard.ahk

; ====================================================================

#Include, Config.ahk

InitKeyboardsData()

SetTitleMatchMode, RegEx
SetCapsLockState, AlwaysOff

; ====================================================================

#If, WinOPlayerPlayPause = 1

; #o::
#SC01F::Send, {Media_Play_Pause}

#If, FixPersianSymbolKeys = 1

$+1::Send, {!}
$+2::Send, {@}
$+3::Send, {#}
$+4::Send, {$}
;$+5::Send, {`%}
$+6::Send, {^}
;$+7::Send, {&}
$+8::Send, {*}

#If, DisableInsert = 1

Insert::Return

#If,

; ====================================================================

#If, BackTickLanguageChange = 1 Or FixPersianSymbolKeys = 1

$`::
	Send, {``}
	If (BackTickLanguageChange = 1)
	{
		ToggleKeyboard("")
	}
	Return

#If,

Capslock::
	ToggleKeyboard("")
	Return

<^Capslock::
	ToggleKeyboard("Left")
	Return

>^Capslock::
	ToggleKeyboard("Right")
	Return

+Capslock::
	ToggleKeyboard("Normal")
	Return

!Capslock::
	T := GetKeyboardLayout()
	H := Format("{1:016X}", T)
	H := SubStr(H, 9)
	MsgBox, 0x%H%
	Return

Capslock & Tab::
	If (GetKeyState("Capslock", "T") = 1)
	{
		SetCapsLockState, AlwaysOff
	}
	Else
	{
		SetCapsLockState, AlwaysOn
	}
	Return

Capslock & `::
	BackTickLanguageChange := 1 - BackTickLanguageChange
	MsgBox, BackTickLanguageChange = %BackTickLanguageChange%
	Return

Capslock & Insert::
	DisableInsert := 1 - DisableInsert
	MsgBox, DisableInsert = %DisableInsert%
	Return

Capslock & \::
	FixPersianSymbolKeys := 1 - FixPersianSymbolKeys
	MsgBox, FixPersianSymbolKeys = %FixPersianSymbolKeys%
	Return

; ====================================================================

#Include, GitGui.ahk


Fix ClickFunction on history command

Fix ClickFunction on history command

#NoEnv
#SingleInstance, Force
#NoTrayIcon

FileEncoding, utf-8
SendMode Input
SetWorkingDir %A_ScriptDir%

; 自动生成的命令文件
global g_SearchFileList := A_ScriptDir . "\Conf\SearchFileList.txt"
; 配置文件
global g_ConfFile := A_ScriptDir . "\Conf\RunZ.ini"
; 自动写入的配置文件
global g_AutoConfFile := A_ScriptDir . "\Conf\RunZ.auto.ini"

if !FileExist(g_ConfFile)
{
    FileCopy, %g_ConfFile%.help.txt, %g_ConfFile%
}

if (FileExist(g_AutoConfFile ".EasyIni.bak"))
{
    MsgBox, % "发现上次写入配置的备份文件：`n"
        . g_AutoConfFile . ".EasyIni.bak"
        . "`n确定则将其恢复，否则请手动检查文件内容再继续"
    FileMove, % g_AutoConfFile ".EasyIni.bak", % g_AutoConfFile
}
else if (!FileExist(g_AutoConfFile))
{
    FileAppend, % "; 此文件由 RunZ 自动写入，如需手动修改请先关闭 RunZ ！`n`n[Auto]`n[Rank]`n[History]" , % g_AutoConfFile
}

global g_Conf := class_EasyINI(g_ConfFile)
global g_AutoConf := class_EasyINI(g_AutoConfFile)

; 当前输入命令的参数，数组，为了方便没有添加 g_ 前缀
global Arg
; 不能是 RunZ.ahk 的子串，否则按键绑定会有问题
global g_WindowName := "RunZ    "
; 所有命令
global g_Commands
; 当搜索无结果时使用的命令
global g_FallbackCommands
; 编辑框当前内容
global g_CurrentInput
; 当前匹配到的第一条命令
global g_CurrentCommand
; 当前匹配到的所有命令
global g_CurrentCommandList
; 是否启用 TCMatch
global g_EnableTCMatch = TCMatchOn(g_Conf.Config.TCMatchPath)
; 列表第一列的首字母或数字
global g_FirstChar := Asc(g_Conf.Gui.FirstChar)
; 在列表中显示的行数
global g_DisplayRows := g_Conf.Gui.DisplayRows
; 在列表中显示的文字列数（多出的会被截断）
global g_DisplayCols := g_Conf.Gui.DisplayCols
; 命令使用了显示框
global g_UseDisplay
; 历史命令
global g_HistoryCommands
; 运行命令时临时设置，避免因为自身退出无法运行需要提权的软件
global g_DisableAutoExit
; 当前的命令在搜索结果的行数
global g_CurrentLine
; 使用备用的命令
global g_UseFallbackCommands
global g_InputArea := "Edit1"
global g_OutputArea := "Edit3"
global g_CommandArea := "Edit5"

if (g_Conf.Gui.ShowTrayIcon)
{
    Menu, Tray, Icon
    Menu, Tray, NoStandard
    if (!g_Conf.Config.ExitIfInactivate)
    {
        Menu, Tray, Add, 显示(&S), ActivateWindow
        Menu, Tray, Default, 显示(&S)
        Menu, Tray, Click, 1
    }
    Menu, Tray, Add, 配置(&C), EditConfig
    Menu, Tray, Add, 帮助(&H), KeyHelp
    Menu, Tray, Add,
    Menu, Tray, Add, 重启(&R), RestartRunZ
    Menu, Tray, Add, 退出(&X), ExitRunZ
}

Menu, Tray, Icon, %A_ScriptDir%\RunZ.ico

if (FileExist(g_SearchFileList))
{
    LoadFiles()
}
else
{
    GoSub, ReloadFiles
}

Gui, Font, % "s" g_Conf.Gui.FontSize, % g_Conf.Gui.FontName
Gui, Add, Edit, % "gProcessInputCommand vSearchArea"
        . " w" g_Conf.Gui.WidgetWidth " h" g_Conf.Gui.EditHeight,
Gui, Add, Edit, w0 h0 ReadOnly,
Gui, Add, Edit, % "ReadOnly vDisplayArea "
        . (g_Conf.Gui.HideDisplayAreaVScroll ? "-VScroll " : "")
        . " w" g_Conf.Gui.WidgetWidth " h" g_Conf.Gui.DisplayAreaHeight
        , % SearchCommand("", true)

if (g_Conf.Gui.ShowCurrentCommand)
{
    Gui, Add, Edit, w0 h0 ReadOnly,
    Gui, Add, Edit, % "ReadOnly"
        . " w" g_Conf.Gui.WidgetWidth " h" g_Conf.Gui.EditHeight,
}

if (g_Conf.Gui.HideTitle)
{
    Gui -Caption
}

Gui, Show, , % g_WindowName

if (g_Conf.Config.WindowAlwaysOnTop)
{
    WinSet, AlwaysOnTop, On, A
}

if (g_Conf.Config.ExitIfInactivate)
{
    OnMessage(0x06, "WM_ACTIVATE")
}

Hotkey, IfWinActive, % g_WindowName
; 如果是 ~enter，有时候会响
Hotkey, enter, RunCurrentCommand

Hotkey, esc, EscFunction
Hotkey, !f4, ExitRunZ

Hotkey, tab, TabFunction
Hotkey, f1, Help
Hotkey, +f1, KeyHelp
Hotkey, f2, EditConfig
Hotkey, ^q, RestartRunZ
Hotkey, ^l, ClearInput
Hotkey, ^d, OpenCurrentFileDir
Hotkey, ^x, DeleteCurrentFile
Hotkey, ^s, ShowCurrentFile
Hotkey, ^r, ReloadFiles
Hotkey, ^h, DisplayHistoryCommands
Hotkey, ^n, IncreaseRank
Hotkey, ^p, DecreaseRank
Hotkey, ^f, NextPage
Hotkey, ^b, PrevPage
Hotkey, ^i, HomeKey
Hotkey, ^o, EndKey
Hotkey, ^j, NextCommand
Hotkey, ^k, PrevCommand
Hotkey, down, NextCommand
Hotkey, up, PrevCommand
Hotkey, ~lbutton, ClickFunction
Hotkey, rbutton, OpenContextMenu

; 剩余按键 e g j m t w

Loop, % g_DisplayRows
{
    key := Chr(g_FirstChar + A_Index - 1)
    ; lalt +
    Hotkey, !%key%, RunSelectedCommand
    ; tab +
    Hotkey, ~%key%, RunSelectedCommand
    ; shift +
    Hotkey, ~+%key%, GotoCommand
}

; 用户映射的按键

for key, label in g_Conf.Hotkey
{
    if (label != "Default")
    {
        Hotkey, %key%, %label%
    }
    else
    {
        Hotkey, %key%, Off
    }
}

Hotkey, IfWinActive

for key, label in g_Conf.GlobalHotkey
{
    if (label != "Default")
    {
        Hotkey, %key%, %label%
    }
    else
    {
        Hotkey, %key%, Off
    }
}

if (g_Conf.Config.SaveInputText && g_AutoConf.Auto.InputText != "")
{
    Send, % g_AutoConf.Auto.InputText
}

if (g_Conf.Config.SaveHistory)
{
    g_HistoryCommands := Object()
    LoadHistoryCommands()
}

return

Default:
return

RestartRunZ:
    SaveAutoConf()
    Reload
return

Test:
    MsgBox, 测试
return

HomeKey:
    Send, {home}
return

EndKey:
    Send, {End}
return

NextPage:
    ControlFocus, %g_OutputArea%
    Send, {pgdn}
return

PrevPage:
    ControlFocus, %g_OutputArea%
    Send, {pgup}
return

ActivateWindow:
    Gui, Show, , % g_WindowName
return

getMouseCurrentLine()
{
    MouseGetPos, , mouseY, , classnn,
    if (classnn != g_OutputArea)
    {
        return -1
    }

    ControlGetPos, , y, , h, %g_OutputArea%
    lineHeight := h / g_DisplayRows
    index := Ceil((mouseY - y) / lineHeight)
    return index
}

ClickFunction:
    if (g_UseDisplay)
    {
        return
    }

    index := getMouseCurrentLine()
    if (index < 0)
    {
        return
    }

    if (g_CurrentCommandList[index] != "")
    {
        ChangeCommand(index - 1, true)
    }

    ControlFocus, %g_InputArea%
    Send, {end}

    if (g_Conf.Config.ClickToRun)
    {
        GoSub, RunCurrentCommand
    }
return

OpenContextMenu:
    currentCommandText := "运行命令："
    if (!g_CurrentLine > 0)
    {
        currentCommandText .= Chr(g_FirstChar)
    }
    else
    {
        currentCommandText .= Chr(g_FirstChar + g_CurrentLine - 1)
    }
    Menu, ContextMenu, Add, %currentCommandText%, RunCurrentCommand
    Menu, ContextMenu, Add, 编辑配置(&E), EditConfig
    Menu, ContextMenu, Add, 重载文件(&S), ReloadFiles
    Menu, ContextMenu, Add, 显示历史(&H), DisplayHistoryCommands
    Menu, ContextMenu, Add, 显示帮助(&A), Help
    Menu, ContextMenu, Add, 重新启动(&R), RestartRunZ
    Menu, ContextMenu, Add, 退出程序(&X), ExitRunZ
    Menu, ContextMenu, Show
    Menu, ContextMenu, DeleteAll
return

TabFunction:
    ControlGetFocus, ctrl,
    if (ctrl == g_InputArea)
    {
        ; 定位到一个隐藏编辑框
        ControlFocus, Edit2
    }
    else
    {
        ControlFocus, %g_InputArea%
    }
return

EscFunction:
    ; 如果是后台运行模式，只关闭窗口，不退出程序
    if (g_Conf.Config.RunInBackground)
    {
        Gui, Hide
    }
    else
    {
        GoSub, ExitRunZ
    }
return

NextCommand:
    ChangeCommand(1)
return

PrevCommand:
    ChangeCommand(-1)
return

GotoCommand:
    ControlGetFocus, ctrl,
    if (ctrl == g_InputArea)
    {
        return
    }

    index := Asc(SubStr(A_ThisHotkey, 0, 1)) - g_FirstChar + 1

    if (g_CurrentCommandList[index] != "")
    {
        ChangeCommand(index - 1, true)
    }
return

ChangeCommand(step, resetCurrentLine = false)
{
    ControlGetText, g_CurrentInput, %g_InputArea%

    if (resetCurrentLine || SubStr(g_CurrentInput, 1, 1) != "@")
    {
        g_CurrentLine := 1
    }

    row := g_CurrentCommandList.Length()
    if (row > g_DisplayRows)
    {
        row := g_DisplayRows
    }

    g_CurrentLine := Mod(g_CurrentLine + step, row)
    if (g_CurrentLine == 0)
    {
        g_CurrentLine := row
    }

    ; 重置当前命令
    g_CurrentCommand := g_CurrentCommandList[g_CurrentLine]

    ; 修改输入框内容
    currentChar := Chr(g_FirstChar + g_CurrentLine - 1)
    newInput := "@" currentChar " "

    if (g_UseFallbackCommands)
    {
        if (SubStr(g_CurrentInput, 1, 1) == "@")
        {
            newInput .= SubStr(g_CurrentInput, 4)
        }
        else
        {
            newInput .= g_CurrentInput
        }
    }

    ControlGetText, result, %g_OutputArea%
    result := StrReplace(result, ">| ", " | ")
    if (currentChar == Chr(g_FirstChar))
    {
        result := currentChar ">" SubStr(result, 3)
    }
    else
    {
        result := StrReplace(result, "`n" currentChar " | ", "`n" currentChar ">| ")
    }

    DisplaySearchResult(result)

    ControlSetText, %g_InputArea%, %newInput%, %g_WindowName%
    Send, {end}
}

GuiClose()
{
    if (!g_Conf.Config.RunInBackground)
    {
        GoSub, ExitRunZ
    }
}

SaveAutoConf()
{
    if (g_Conf.Config.SaveInputText)
    {
        g_AutoConf.DeleteKey("Auto", "InputText")
        g_AutoConf.AddKey("Auto", "InputText", g_CurrentInput)
    }

    if (g_Conf.Config.SaveHistory)
    {
        g_AutoConf.DeleteSection("History")
        g_AutoConf.AddSection("History")

        for index, element in g_HistoryCommands
        {
            if (element != "")
            {
                g_AutoConf.AddKey("History", index, element)
            }
        }
    }

    Loop
    {
        g_AutoConf.Save()

        if (!FileExist(g_AutoConfFile))
        {
            MsgBox, 配置文件 %g_AutoConfFile% 写入后丢失，请检查磁盘并点确定来重试
        }
        else
        {
            break
        }
    }
}

ExitRunZ:
    SaveAutoConf()
    ExitApp
return

GenerateSearchFileList()
{
    FileDelete, %g_SearchFileList%

    searchFileType := g_Conf.Config.SearchFileType

    for dirIndex, dir in StrSplit(g_Conf.Config.SearchFileDir, " | ")
    {
        if (InStr(dir, "A_") == 1)
        {
            searchPath := %dir%
        }
        else
        {
            searchPath := dir
        }

        for extIndex, ext in StrSplit(searchFileType, " | ")
        {
            Loop, Files, %searchPath%\%ext%, R
            {
                if (g_Conf.Config.SearchFileExclude != ""
                        && RegexMatch(A_LoopFileLongPath, g_Conf.Config.SearchFileExclude))
                {
                    continue
                }
                FileAppend, file | %A_LoopFileLongPath%`n, %g_SearchFileList%,
            }
        }
    }
}

ReloadFiles:
    GenerateSearchFileList()

    LoadFiles()
return

ProcessInputCommand:
    ControlGetText, g_CurrentInput, %g_InputArea%

    SearchCommand(g_CurrentInput)
return

SearchCommand(command = "", firstRun = false)
{
    g_UseDisplay := false
    result := ""
    ; 供去重使用
    fullResult := ""
    commandPrefix := SubStr(command, 1, 1)

    if (commandPrefix == ";" || commandPrefix == ":")
    {
        if (commandPrefix == ";")
        {
            g_CurrentCommand := g_FallbackCommands[1]
        }
        else if (commandPrefix == ":")
        {
            g_CurrentCommand := g_FallbackCommands[2]
        }

        g_CurrentCommandList := Object()
        g_CurrentCommandList.Push(g_CurrentCommand)
        result .= Chr(g_FirstChar) ">| " . g_CurrentCommand
        DisplaySearchResult(result)
        return result
    }
    else if (commandPrefix == "@")
    {
        ; 搜索结果被锁定，直接退出
        return
    }
    else if (InStr(command, " ") && g_CurrentCommand != "")
    {
        ; 输入包含空格时锁定搜索结果
        return
    }

    g_CurrentCommandList := Object()

    order := g_FirstChar

    for index, element in g_Commands
    {
        if (InStr(fullResult, element "`n"))
        {
            continue
        }

        splitedElement := StrSplit(element, " | ")

        if (splitedElement[1] == "file")
        {
            SplitPath, % splitedElement[2], , fileDir, , fileNameNoExt

            ; 只搜索和展示不带扩展名的文件名
            elementToSearch := fileNameNoExt
            elementToShow := "file | " . fileNameNoExt

            if (splitedElement.Length() >= 3)
            {
                elementToSearch .= " " . splitedElement[3]
                elementToShow .= "（" . splitedElement[3] . "）"
            }

            elementToShow := SubStr(elementToShow, 1, g_DisplayCols)

            if (g_Conf.Config.SearchFullPath)
            {
                ; TCMatch 在搜索路径时只搜索文件名，强行将 \ 转成空格
                elementToSearch := StrReplace(fileDir, "\", " ") . " " . elementToSearch
            }
        }
        else
        {
            elementToShow := SubStr(element, 1, g_DisplayCols)
            elementToSearch := splitedElement[2]

            if (splitedElement.Length() >= 3)
            {
                elementToSearch .= " " . splitedElement[3]
            }
        }

        if (command == "" || MatchCommand(elementToSearch, command))
        {
            fullResult .= element "`n"
            g_CurrentCommandList.Push(element)

            if (order == g_FirstChar)
            {
                g_CurrentCommand := element
                result .= Chr(order++) . ">| " . elementToShow
            }
            else
            {
                result .= "`n" Chr(order++) . " | " . elementToShow
            }

            if (order - g_FirstChar >= g_DisplayRows)
            {
                break
            }
            ; 第一次运行只加载 function 类型
            if (firstRun && (order - g_FirstChar >= g_DisplayRows - 4))
            {
                result .= "`n`n现有 " g_Commands.Length() " 条命令。"
                result .= "`n`n键入内容 搜索，回车 执行当前命令，Alt + 字母 执行，F1 帮助，Esc 退出。"

                break
            }
        }
    }

    if (result == "")
    {
        g_UseFallbackCommands := true
        g_CurrentCommand := g_FallbackCommands[1]
        g_CurrentCommandList := g_FallbackCommands

        for index, element in g_FallbackCommands
        {
            if (index == 1)
            {
                result .= Chr(g_FirstChar - 1 + index++) . ">| " . element
            }
            else
            {
                result .= "`n"
                result .= Chr(g_FirstChar - 1 + index++) . " | " . element
            }
        }
    }
    else
    {
        g_UseFallbackCommands := false
    }

    result := StrReplace(result, "file | ", "文件 | ")
    result := StrReplace(result, "function | ", "功能 | ")
    result := StrReplace(result, "cmd | ", "命令 | ")

    DisplaySearchResult(result)
    return result
}

DisplaySearchResult(result)
{
    DisplayText(result)

    if (g_CurrentCommandList.Length() == 1 && g_Conf.Config.RunIfOnlyOne)
    {
        RunCommand(g_CurrentCommand)
    }

    if (g_Conf.Gui.ShowCurrentCommand)
    {
        commandToShow := SubStr(g_CurrentCommand, InStr(g_CurrentCommand, " | ") + 3)
        ControlSetText, %g_CommandArea%, %commandToShow%, %g_WindowName%
    }
}

ClearInput:
    ControlSetText, %g_InputArea%, , %g_WindowName%
    ControlFocus, %g_InputArea%
return

RunCurrentCommand:
    if (GetInputState() == 1)
    {
        Send, {enter}
    }

    if (g_CurrentInput != "")
    {
        RunCommand(g_CurrentCommand)
    }
return

ParseArg:
    commandPrefix := SubStr(g_CurrentInput, 1, 1)

    if (commandPrefix == ";" || commandPrefix == ":")
    {
        Arg := SubStr(g_CurrentInput, 2)
        return
    }
    else if (commandPrefix == "@")
    {
        ; 处理调整过顺序的命令
        Arg := SubStr(g_CurrentInput, 4)
        return
    }

    ; 用空格来判断参数
    if (InStr(g_CurrentInput, " ") && !g_UseFallbackCommands)
    {
        Arg := SubStr(g_CurrentInput, InStr(g_CurrentInput, " ") + 1)
    }
    else
    {
        Arg := g_CurrentInput
    }
return

MatchCommand(Haystack, Needle)
{
    if (g_EnableTCMatch)
    {
        return TCMatch(Haystack, Needle)
    }

    return InStr(Haystack, Needle)
}

RunCommand(originCmd)
{
    GoSub, ParseArg

    g_UseDisplay := false
    g_DisableAutoExit := true

    splitedOriginCmd := StrSplit(originCmd, " | ")
    ; 去掉括号内的注释
    cmd := StrSplit(splitedOriginCmd[2], "（")[1]

    if (splitedOriginCmd[1] == "file")
    {
        if (InStr(cmd, ".lnk"))
        {
            ; 处理 32 位 ahk 运行不了某些 64 位系统 .lnk 的问题
            FileGetShortcut, %cmd%, filePath
            if (!FileExist(filePath))
            {
                filePath := StrReplace(filePath, "C:\Program Files (x86)", "C:\Program Files")
                if (FileExist(filePath))
                {
                    cmd := filePath
                }
            }
        }

        Run, %cmd%
    }
    else if (splitedOriginCmd[1] == "function")
    {
        if (splitedOriginCmd.Length() >= 3)
        {
            Arg := splitedOriginCmd[3]
        }

        if (IsLabel(cmd))
        {
            GoSub, %cmd%
        }
    }
    else if (splitedOriginCmd[1] == "cmd")
    {
        RunWithCmd(cmd)
    }

    if (g_Conf.Config.SaveHistory && cmd != "DisplayHistoryCommands")
    {
        if (splitedOriginCmd.Length() == 2 && Arg != "")
        {
            g_HistoryCommands.InsertAt(1, originCmd " | " Arg)
        }
        else if (originCmd != "")
        {
            g_HistoryCommands.InsertAt(1, originCmd)
        }

        if (g_HistoryCommands.Length() > g_Conf.Config.HistorySize)
        {
            g_HistoryCommands.Pop()
        }
    }

    if (g_Conf.Config.AutoRank)
    {
        IncreaseRank(originCmd)
    }

    g_DisableAutoExit := false

    if (g_Conf.Config.RunOnce && !g_UseDisplay)
    {
        GoSub, EscFunction
    }
}

IncreaseRank(cmd, show = false, inc := 1)
{
    splitedCmd := StrSplit(cmd, " | ")

    if (splitedCmd.Length() >= 3 && splitedCmd[1] == "function")
    {
        ; 去掉参数
        cmd := splitedCmd[1]  " | " splitedCmd[2]
    }

    cmdRank := g_AutoConf.GetValue("Rank", cmd)
    if cmdRank is integer
    {
        g_AutoConf.DeleteKey("Rank", cmd)
        cmdRank += inc
    }
    else
    {
        cmdRank := inc
    }

    if (cmdRank > 0 && cmd != "")
    {
        g_AutoConf.AddKey("Rank", cmd, cmdRank)
    }
    else
    {
        cmdRank := 0
    }

    if (show)
    {
        ToolTip, 调整 %cmd% 的权重到 %cmdRank%
        SetTimer, RemoveToolTip, 800
    }
}

; 比较耗时，必要时才使用，也可以手动编辑 RunZ.auto.ini
CleanupRank:
    ; 先把 g_Commands 里的 Rank 信息清掉
    LoadFiles(false)

    for command, rank in g_AutoConf.Rank
    {
        cleanup := true
        for index, element in g_Commands
        {
            if (InStr(element, command) == 1)
            {
                cleanup := false
                break
            }
        }
        if (cleanup)
        {
            g_AutoConf.DeleteKey("Rank", command)
        }
    }

    Loop
    {
        g_AutoConf.Save()

        if (!FileExist(g_AutoConfFile))
        {
            MsgBox, 配置文件 %g_AutoConfFile% 写入后丢失，请检查磁盘并点确定来重试
        }
        else
        {
            break
        }
    }

    LoadFiles()
return

RunSelectedCommand:
    if (SubStr(A_ThisHotkey, 1, 1) == "~")
    {
        ControlGetFocus, ctrl,
        if (ctrl == g_InputArea)
        {
            return
        }
    }

    index := Asc(SubStr(A_ThisHotkey, 0, 1)) - g_FirstChar + 1

    RunCommand(g_CurrentCommandList[index])
return

IncreaseRank:
    if (g_CurrentCommand != "")
    {
        IncreaseRank(g_CurrentCommand, true)
        LoadFiles()
    }
return

DecreaseRank:
    if (g_CurrentCommand != "")
    {
        IncreaseRank(g_CurrentCommand, true, -1)
        LoadFiles()
    }
return

LoadFiles(loadRank := true)
{
    g_Commands := Object()
    g_FallbackCommands := Object()

    if (loadRank)
    {
        rankString := ""
        for command, rank in g_AutoConf.Rank
        {
            if (StrLen(command) > 0)
            {
                rankString .= rank "`t" command "`n"
            }
        }

        if (rankString != "")
        {
            Sort, rankString, R N

            Loop, Parse, rankString, `n
            {
                if (A_LoopField == "")
                {
                    continue
                }

                g_Commands.Push(StrSplit(A_loopField, "`t")[2])
            }
        }
    }

    for key, value in g_Conf.Command
    {
        if (value != "")
        {
            g_Commands.Push(key . "（" . value "）")
        }
        else
        {
            g_Commands.Push(key)
        }
    }

    if (FileExist(A_ScriptDir "\Conf\UserFunctions.ahk"))
    {
        userFunctionLabel := "UserFunctions"
        if (IsLabel(userFunctionLabel))
        {
            GoSub, %userFunctionLabel%
        }
        else
        {
            MsgBox, 未在 %A_ScriptDir%\Conf\UserFunctions.ahk 中发现 %userFunctionLabel% 标签，请修改！
        }
    }

    GoSub, Functions

    Loop, Read, %g_SearchFileList%
    {
        g_Commands.Push(A_LoopReadLine)
    }

    if (g_Conf.Config.LoadControlPanelFunctions)
    {
        Loop, Read, %A_ScriptDir%\Core\ControlPanelFunctions.txt
        {
            g_Commands.Push(A_LoopReadLine)
        }
    }
}

DisplayText(text)
{
    textToDisplay := StrReplace(text, "`n", "`r`n")
    ControlSetText, %g_OutputArea%, %textToDisplay%, %g_WindowName%
}

DisplayResult(result)
{
    DisplayText(result)
    g_UseDisplay := true
}

LoadHistoryCommands()
{
    for key, value in g_AutoConf.History
    {
        if (StrLen(value) > 0)
        {
            g_HistoryCommands.Push(value)
        }
    }
}

DisplayHistoryCommands:
    g_UseDisplay := false
    result := ""
    g_CurrentCommandList := Object()
    g_CurrentLine := 1

    for index, element in g_HistoryCommands
    {
        if (index == 1)
        {
            result .= SubStr(Chr(g_FirstChar + index - 1) . ">| " . element, 1, g_DisplayCols) "`n"
            g_CurrentCommand := element
        }
        else
        {
            result .= SubStr(Chr(g_FirstChar + index - 1) . " | " . element, 1, g_DisplayCols) "`n"
        }

        g_CurrentCommandList.Push(element)
    }

    result := StrReplace(result, "file | ", "文件 | ")
    result := StrReplace(result, "function | ", "功能 | ")
    result := StrReplace(result, "cmd | ", "命令 | ")

    DisplayText(result)
return

@(label, info, fallback = false, key = "")
{
    if (!IsLabel(label))
    {
        MsgBox, 未找到 %label% 标签，请检查 %A_ScriptDir%\Conf\UserFunctions.ahk 文件格式！
        return
    }

    g_Commands.Push("function | " . label . "（" . info . "）")
    if (fallback)
    {
        g_FallbackCommands.Push("function | " . label . "（" . info . "）")
    }

    if (key != "")
    {
        Hotkey, %key%, %label%
    }
}

RunAndGetOutput(command)
{
    tempFileName := "RunZ.stdout.log"
    fullCommand = bash -c "%command% &> %tempFileName%"

    if (!FileExist("c:\msys64\usr\bin\bash.exe"))
    {
        fullCommand = %ComSpec% /C "%command% > %tempFileName%"
    }

    RunWait, %fullCommand%, %A_Temp%, Hide
    FileRead, result, %A_Temp%\%tempFileName%
    FileDelete, %A_Temp%\%tempFileName%
    return result
}

RunWithCmd(command)
{
    if (FileExist("c:\msys64\usr\bin\mintty.exe"))
    {
        Run, % "mintty -e sh -c '" command "; read'"
    }
    else
    {
        Run, % ComSpec " /C " command " & pause"
    }
}

OpenPath(filePath)
{
    if (!FileExist(filePath))
    {
        return
    }

    if (FileExist(g_Conf.Config.TCPath))
    {
        TCPath := g_Conf.Config.TCPath
        Run, %TCPath% /O /A /L="%filePath%"
    }
    else
    {
        SplitPath, filePath, , fileDir, ,
        Run, explorer "%fileDir%"
    }
}

GetAllFunctions()
{
    result := ""

    for index, element in g_Commands
    {
        if (InStr(element, "function | ") == 1 and !InStr(result, element "`n"))
        {
            result .= element "`n"
        }
    }

    result := StrReplace(result, "function | ", "功能 | ")

    return result
}

OpenCurrentFileDir:
    filePath := StrSplit(g_CurrentCommand, " | ")[2]
    OpenPath(filePath)
return

DeleteCurrentFile:
    filePath := StrSplit(g_CurrentCommand, " | ")[2]

    if (!FileExist(filePath))
    {
        return
    }

    FileRecycle, % filePath
    GoSub, ReloadFiles
return

ShowCurrentFile:
    clipboard := StrSplit(g_CurrentCommand, " | ")[2]
    ToolTip, % clipboard
    SetTimer, RemoveToolTip, 800
return

RemoveToolTip:
    ToolTip
    SetTimer, RemoveToolTip, Off
return

WM_ACTIVATE(wParam, lParam)
{
    if (g_DisableAutoExit)
    {
        return
    }

    if (wParam >= 1) ; 窗口激活
    {
        return
    }
    else if (wParam <= 0) ; 窗口非激活
    {
        SetTimer, ToExit, 50
    }
}

ToExit:
    if (!WinExist("RunZ.ahk"))
    {
        GoSub, EscFunction
    }

    SetTimer, ToExit, Off
return

KeyHelpText()
{
    return ""
    . "Win + j 显示窗口`n"
    . "键入内容 搜索，回车 执行，Alt + 字母 执行，Esc 退出`n"
    . "按 Tab 后再按 字母或数字 也可执行字母对应功能`n"
    . "按 Tab 后 Shift + 字母或数字 定位到对应功能`n"
    . "Ctrl + j 移动到下一条命令`n"
    . "Ctrl + k 移动到上一条命令`n"
    . "Ctrl + f 翻到下一页`n"
    . "Ctrl + b 翻到上一页`n"
    . "Win  + j 激活窗口`n"
    . "Ctrl + h 显示历史记录`n"
    . "Ctrl + n 可增加当前功能的权重`n"
    . "Ctrl + p 可减少当前功能的权重`n"
    . "Ctrl + l 清除编辑框内容`n"
    . "Ctrl + r 重新创建待搜索文件列表`n"
    . "Ctrl + q 重启`n"
    . "Ctrl + d 用 TC 打开第一个文件所在目录`n"
    . "Ctrl + s 显示并复制当前文件的完整路径`n"
    . "Ctrl + x 删除当前文件`n"
    . "Ctrl + i 移动光标当行首`n"
    . "Ctrl + o 移动光标当行尾`n"
    . "F2       编辑配置文件`n`n"
}

UrlDownloadToString(url)
{
    static whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    whr.Open("GET", url, true)
    whr.Send()
    whr.WaitForResponse()
    return whr.ResponseText
}

; 修改自万年书妖的 Candy 里的 SksSub_UrlEncode 函数，用于转换编码。感谢！
UrlEncode(url, enc = "UTF-8")
{
    enc := trim(enc)
    If enc=
        return url
    formatInteger := A_FormatInteger
    SetFormat, IntegerFast, H
    VarSetCapacity(buff, StrPut(url, enc))
    Loop % StrPut(url, &buff, enc) - 1
    {
        byte := NumGet(buff, A_Index-1, "UChar")
        encoded .= byte > 127 or byte <33 ? "%" Substr(byte, 3) : Chr(byte)
    }
    SetFormat, IntegerFast, %formatInteger%
    return encoded
}

; 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
}

#include %A_ScriptDir%\Lib\EasyIni.ahk
#include %A_ScriptDir%\Lib\TCMatch.ahk
#include %A_ScriptDir%\Lib\Eval.ahk
#include %A_ScriptDir%\Lib\JSON.ahk
#include %A_ScriptDir%\Lib\Kanji\Kanji.ahk
#include %A_ScriptDir%\Lib\Gdip.ahk
#include %A_ScriptDir%\Core\Functions.ahk
#include %A_ScriptDir%\Core\ReservedFunctions.ahk
; 用户自定义命令
#include *i %A_ScriptDir%\Conf\UserFunctions.ahk

#NoEnv
#SingleInstance, Force
#NoTrayIcon

FileEncoding, utf-8
SendMode Input
SetWorkingDir %A_ScriptDir%

; 自动生成的命令文件
global g_SearchFileList := A_ScriptDir . "\Conf\SearchFileList.txt"
; 配置文件
global g_ConfFile := A_ScriptDir . "\Conf\RunZ.ini"
; 自动写入的配置文件
global g_AutoConfFile := A_ScriptDir . "\Conf\RunZ.auto.ini"

if !FileExist(g_ConfFile)
{
    FileCopy, %g_ConfFile%.help.txt, %g_ConfFile%
}

if (FileExist(g_AutoConfFile ".EasyIni.bak"))
{
    MsgBox, % "发现上次写入配置的备份文件：`n"
        . g_AutoConfFile . ".EasyIni.bak"
        . "`n确定则将其恢复，否则请手动检查文件内容再继续"
    FileMove, % g_AutoConfFile ".EasyIni.bak", % g_AutoConfFile
}
else if (!FileExist(g_AutoConfFile))
{
    FileAppend, % "; 此文件由 RunZ 自动写入，如需手动修改请先关闭 RunZ ！`n`n[Auto]`n[Rank]`n[History]" , % g_AutoConfFile
}

global g_Conf := class_EasyINI(g_ConfFile)
global g_AutoConf := class_EasyINI(g_AutoConfFile)

; 当前输入命令的参数，数组，为了方便没有添加 g_ 前缀
global Arg
; 不能是 RunZ.ahk 的子串，否则按键绑定会有问题
global g_WindowName := "RunZ    "
; 所有命令
global g_Commands
; 当搜索无结果时使用的命令
global g_FallbackCommands
; 编辑框当前内容
global g_CurrentInput
; 当前匹配到的第一条命令
global g_CurrentCommand
; 当前匹配到的所有命令
global g_CurrentCommandList
; 是否启用 TCMatch
global g_EnableTCMatch = TCMatchOn(g_Conf.Config.TCMatchPath)
; 列表第一列的首字母或数字
global g_FirstChar := Asc(g_Conf.Gui.FirstChar)
; 在列表中显示的行数
global g_DisplayRows := g_Conf.Gui.DisplayRows
; 在列表中显示的文字列数（多出的会被截断）
global g_DisplayCols := g_Conf.Gui.DisplayCols
; 命令使用了显示框
global g_UseDisplay
; 历史命令
global g_HistoryCommands
; 运行命令时临时设置，避免因为自身退出无法运行需要提权的软件
global g_DisableAutoExit
; 当前的命令在搜索结果的行数
global g_CurrentLine
; 使用备用的命令
global g_UseFallbackCommands
global g_InputArea := "Edit1"
global g_OutputArea := "Edit3"
global g_CommandArea := "Edit5"

if (g_Conf.Gui.ShowTrayIcon)
{
    Menu, Tray, Icon
    Menu, Tray, NoStandard
    if (!g_Conf.Config.ExitIfInactivate)
    {
        Menu, Tray, Add, 显示(&S), ActivateWindow
        Menu, Tray, Default, 显示(&S)
        Menu, Tray, Click, 1
    }
    Menu, Tray, Add, 配置(&C), EditConfig
    Menu, Tray, Add, 帮助(&H), KeyHelp
    Menu, Tray, Add,
    Menu, Tray, Add, 重启(&R), RestartRunZ
    Menu, Tray, Add, 退出(&X), ExitRunZ
}

Menu, Tray, Icon, %A_ScriptDir%\RunZ.ico

if (FileExist(g_SearchFileList))
{
    LoadFiles()
}
else
{
    GoSub, ReloadFiles
}

Gui, Font, % "s" g_Conf.Gui.FontSize, % g_Conf.Gui.FontName
Gui, Add, Edit, % "gProcessInputCommand vSearchArea"
        . " w" g_Conf.Gui.WidgetWidth " h" g_Conf.Gui.EditHeight,
Gui, Add, Edit, w0 h0 ReadOnly,
Gui, Add, Edit, % "ReadOnly vDisplayArea "
        . (g_Conf.Gui.HideDisplayAreaVScroll ? "-VScroll " : "")
        . " w" g_Conf.Gui.WidgetWidth " h" g_Conf.Gui.DisplayAreaHeight
        , % SearchCommand("", true)

if (g_Conf.Gui.ShowCurrentCommand)
{
    Gui, Add, Edit, w0 h0 ReadOnly,
    Gui, Add, Edit, % "ReadOnly"
        . " w" g_Conf.Gui.WidgetWidth " h" g_Conf.Gui.EditHeight,
}

if (g_Conf.Gui.HideTitle)
{
    Gui -Caption
}

Gui, Show, , % g_WindowName

if (g_Conf.Config.WindowAlwaysOnTop)
{
    WinSet, AlwaysOnTop, On, A
}

if (g_Conf.Config.ExitIfInactivate)
{
    OnMessage(0x06, "WM_ACTIVATE")
}

Hotkey, IfWinActive, % g_WindowName
; 如果是 ~enter，有时候会响
Hotkey, enter, RunCurrentCommand

Hotkey, esc, EscFunction
Hotkey, !f4, ExitRunZ

Hotkey, tab, TabFunction
Hotkey, f1, Help
Hotkey, +f1, KeyHelp
Hotkey, f2, EditConfig
Hotkey, ^q, RestartRunZ
Hotkey, ^l, ClearInput
Hotkey, ^d, OpenCurrentFileDir
Hotkey, ^x, DeleteCurrentFile
Hotkey, ^s, ShowCurrentFile
Hotkey, ^r, ReloadFiles
Hotkey, ^h, DisplayHistoryCommands
Hotkey, ^n, IncreaseRank
Hotkey, ^p, DecreaseRank
Hotkey, ^f, NextPage
Hotkey, ^b, PrevPage
Hotkey, ^i, HomeKey
Hotkey, ^o, EndKey
Hotkey, ^j, NextCommand
Hotkey, ^k, PrevCommand
Hotkey, down, NextCommand
Hotkey, up, PrevCommand
Hotkey, ~lbutton, ClickFunction
Hotkey, rbutton, OpenContextMenu

; 剩余按键 e g j m t w

Loop, % g_DisplayRows
{
    key := Chr(g_FirstChar + A_Index - 1)
    ; lalt +
    Hotkey, !%key%, RunSelectedCommand
    ; tab +
    Hotkey, ~%key%, RunSelectedCommand
    ; shift +
    Hotkey, ~+%key%, GotoCommand
}

; 用户映射的按键

for key, label in g_Conf.Hotkey
{
    if (label != "Default")
    {
        Hotkey, %key%, %label%
    }
    else
    {
        Hotkey, %key%, Off
    }
}

Hotkey, IfWinActive

for key, label in g_Conf.GlobalHotkey
{
    if (label != "Default")
    {
        Hotkey, %key%, %label%
    }
    else
    {
        Hotkey, %key%, Off
    }
}

if (g_Conf.Config.SaveInputText && g_AutoConf.Auto.InputText != "")
{
    Send, % g_AutoConf.Auto.InputText
}

if (g_Conf.Config.SaveHistory)
{
    g_HistoryCommands := Object()
    LoadHistoryCommands()
}

return

Default:
return

RestartRunZ:
    SaveAutoConf()
    Reload
return

Test:
    MsgBox, 测试
return

HomeKey:
    Send, {home}
return

EndKey:
    Send, {End}
return

NextPage:
    ControlFocus, %g_OutputArea%
    Send, {pgdn}
return

PrevPage:
    ControlFocus, %g_OutputArea%
    Send, {pgup}
return

ActivateWindow:
    Gui, Show, , % g_WindowName
return

getMouseCurrentLine()
{
    MouseGetPos, , mouseY, , classnn,
    if (classnn != g_OutputArea)
    {
        return
    }

    ControlGetPos, , y, , h, %g_OutputArea%
    lineHeight := h / g_DisplayRows
    index := Ceil((mouseY - y) / lineHeight)
    return index
}

ClickFunction:
    index := getMouseCurrentLine()
    if (g_CurrentCommandList[index] != "")
    {
        ChangeCommand(index - 1, true)
    }

    ControlFocus, %g_InputArea%
    Send, {end}

    if (g_Conf.Config.ClickToRun)
    {
        GoSub, RunCurrentCommand
    }
return

OpenContextMenu:
    currentCommandText := "运行命令："
    if (!g_CurrentLine > 0)
    {
        currentCommandText .= Chr(g_FirstChar)
    }
    else
    {
        currentCommandText .= Chr(g_FirstChar + g_CurrentLine - 1)
    }
    Menu, ContextMenu, Add, %currentCommandText%, RunCurrentCommand
    Menu, ContextMenu, Add, 编辑配置(&E), EditConfig
    Menu, ContextMenu, Add, 重载文件(&S), ReloadFiles
    Menu, ContextMenu, Add, 显示历史(&H), DisplayHistoryCommands
    Menu, ContextMenu, Add, 显示帮助(&A), Help
    Menu, ContextMenu, Add, 重新启动(&R), RestartRunZ
    Menu, ContextMenu, Add, 退出程序(&X), ExitRunZ
    Menu, ContextMenu, Show
    Menu, ContextMenu, DeleteAll
return

TabFunction:
    ControlGetFocus, ctrl,
    if (ctrl == g_InputArea)
    {
        ; 定位到一个隐藏编辑框
        ControlFocus, Edit2
    }
    else
    {
        ControlFocus, %g_InputArea%
    }
return

EscFunction:
    ; 如果是后台运行模式，只关闭窗口，不退出程序
    if (g_Conf.Config.RunInBackground)
    {
        Gui, Hide
    }
    else
    {
        GoSub, ExitRunZ
    }
return

NextCommand:
    ChangeCommand(1)
return

PrevCommand:
    ChangeCommand(-1)
return

GotoCommand:
    ControlGetFocus, ctrl,
    if (ctrl == g_InputArea)
    {
        return
    }

    index := Asc(SubStr(A_ThisHotkey, 0, 1)) - g_FirstChar + 1

    if (g_CurrentCommandList[index] != "")
    {
        ChangeCommand(index - 1, true)
    }
return

ChangeCommand(step, resetCurrentLine = false)
{
    ControlGetText, g_CurrentInput, %g_InputArea%

    if (resetCurrentLine || SubStr(g_CurrentInput, 1, 1) != "@")
    {
        g_CurrentLine := 1
    }

    row := g_CurrentCommandList.Length()
    if (row > g_DisplayRows)
    {
        row := g_DisplayRows
    }

    g_CurrentLine := Mod(g_CurrentLine + step, row)
    if (g_CurrentLine == 0)
    {
        g_CurrentLine := row
    }

    ; 重置当前命令
    g_CurrentCommand := g_CurrentCommandList[g_CurrentLine]

    ; 修改输入框内容
    currentChar := Chr(g_FirstChar + g_CurrentLine - 1)
    newInput := "@" currentChar " "

    if (g_UseFallbackCommands)
    {
        if (SubStr(g_CurrentInput, 1, 1) == "@")
        {
            newInput .= SubStr(g_CurrentInput, 4)
        }
        else
        {
            newInput .= g_CurrentInput
        }
    }

    ControlGetText, result, %g_OutputArea%
    result := StrReplace(result, ">| ", " | ")
    if (currentChar == Chr(g_FirstChar))
    {
        result := currentChar ">" SubStr(result, 3)
    }
    else
    {
        result := StrReplace(result, "`n" currentChar " | ", "`n" currentChar ">| ")
    }

    DisplaySearchResult(result)

    ControlSetText, %g_InputArea%, %newInput%, %g_WindowName%
    Send, {end}
}

GuiClose()
{
    if (!g_Conf.Config.RunInBackground)
    {
        GoSub, ExitRunZ
    }
}

SaveAutoConf()
{
    if (g_Conf.Config.SaveInputText)
    {
        g_AutoConf.DeleteKey("Auto", "InputText")
        g_AutoConf.AddKey("Auto", "InputText", g_CurrentInput)
    }

    if (g_Conf.Config.SaveHistory)
    {
        g_AutoConf.DeleteSection("History")
        g_AutoConf.AddSection("History")

        for index, element in g_HistoryCommands
        {
            if (element != "")
            {
                g_AutoConf.AddKey("History", index, element)
            }
        }
    }

    Loop
    {
        g_AutoConf.Save()

        if (!FileExist(g_AutoConfFile))
        {
            MsgBox, 配置文件 %g_AutoConfFile% 写入后丢失，请检查磁盘并点确定来重试
        }
        else
        {
            break
        }
    }
}

ExitRunZ:
    SaveAutoConf()
    ExitApp
return

GenerateSearchFileList()
{
    FileDelete, %g_SearchFileList%

    searchFileType := g_Conf.Config.SearchFileType

    for dirIndex, dir in StrSplit(g_Conf.Config.SearchFileDir, " | ")
    {
        if (InStr(dir, "A_") == 1)
        {
            searchPath := %dir%
        }
        else
        {
            searchPath := dir
        }

        for extIndex, ext in StrSplit(searchFileType, " | ")
        {
            Loop, Files, %searchPath%\%ext%, R
            {
                if (g_Conf.Config.SearchFileExclude != ""
                        && RegexMatch(A_LoopFileLongPath, g_Conf.Config.SearchFileExclude))
                {
                    continue
                }
                FileAppend, file | %A_LoopFileLongPath%`n, %g_SearchFileList%,
            }
        }
    }
}

ReloadFiles:
    GenerateSearchFileList()

    LoadFiles()
return

ProcessInputCommand:
    ControlGetText, g_CurrentInput, %g_InputArea%

    SearchCommand(g_CurrentInput)
return

SearchCommand(command = "", firstRun = false)
{
    result := ""
    ; 供去重使用
    fullResult := ""
    commandPrefix := SubStr(command, 1, 1)

    if (commandPrefix == ";" || commandPrefix == ":")
    {
        if (commandPrefix == ";")
        {
            g_CurrentCommand := g_FallbackCommands[1]
        }
        else if (commandPrefix == ":")
        {
            g_CurrentCommand := g_FallbackCommands[2]
        }

        g_CurrentCommandList := Object()
        g_CurrentCommandList.Push(g_CurrentCommand)
        result .= Chr(g_FirstChar) ">| " . g_CurrentCommand
        DisplaySearchResult(result)
        return result
    }
    else if (commandPrefix == "@")
    {
        ; 搜索结果被锁定，直接退出
        return
    }
    else if (InStr(command, " ") && g_CurrentCommand != "")
    {
        ; 输入包含空格时锁定搜索结果
        return
    }

    g_CurrentCommandList := Object()

    order := g_FirstChar

    for index, element in g_Commands
    {
        if (InStr(fullResult, element "`n"))
        {
            continue
        }

        splitedElement := StrSplit(element, " | ")

        if (splitedElement[1] == "file")
        {
            SplitPath, % splitedElement[2], , fileDir, , fileNameNoExt

            ; 只搜索和展示不带扩展名的文件名
            elementToSearch := fileNameNoExt
            elementToShow := "file | " . fileNameNoExt

            if (splitedElement.Length() >= 3)
            {
                elementToSearch .= " " . splitedElement[3]
                elementToShow .= "（" . splitedElement[3] . "）"
            }

            elementToShow := SubStr(elementToShow, 1, g_DisplayCols)

            if (g_Conf.Config.SearchFullPath)
            {
                ; TCMatch 在搜索路径时只搜索文件名，强行将 \ 转成空格
                elementToSearch := StrReplace(fileDir, "\", " ") . " " . elementToSearch
            }
        }
        else
        {
            elementToShow := SubStr(element, 1, g_DisplayCols)
            elementToSearch := splitedElement[2]

            if (splitedElement.Length() >= 3)
            {
                elementToSearch .= " " . splitedElement[3]
            }
        }

        if (command == "" || MatchCommand(elementToSearch, command))
        {
            fullResult .= element "`n"
            g_CurrentCommandList.Push(element)

            if (order == g_FirstChar)
            {
                g_CurrentCommand := element
                result .= Chr(order++) . ">| " . elementToShow
            }
            else
            {
                result .= "`n" Chr(order++) . " | " . elementToShow
            }

            if (order - g_FirstChar >= g_DisplayRows)
            {
                break
            }
            ; 第一次运行只加载 function 类型
            if (firstRun && (order - g_FirstChar >= g_DisplayRows - 4))
            {
                result .= "`n`n现有 " g_Commands.Length() " 条命令。"
                result .= "`n`n键入内容 搜索，回车 执行当前命令，Alt + 字母 执行，F1 帮助，Esc 退出。"

                break
            }
        }
    }

    if (result == "")
    {
        g_UseFallbackCommands := true
        g_CurrentCommand := g_FallbackCommands[1]
        g_CurrentCommandList := g_FallbackCommands

        for index, element in g_FallbackCommands
        {
            if (index == 1)
            {
                result .= Chr(g_FirstChar - 1 + index++) . ">| " . element
            }
            else
            {
                result .= "`n"
                result .= Chr(g_FirstChar - 1 + index++) . " | " . element
            }
        }
    }
    else
    {
        g_UseFallbackCommands := false
    }

    result := StrReplace(result, "file | ", "文件 | ")
    result := StrReplace(result, "function | ", "功能 | ")
    result := StrReplace(result, "cmd | ", "命令 | ")

    DisplaySearchResult(result)
    return result
}

DisplaySearchResult(result)
{
    DisplayText(result)

    if (g_CurrentCommandList.Length() == 1 && g_Conf.Config.RunIfOnlyOne)
    {
        RunCommand(g_CurrentCommand)
    }

    if (g_Conf.Gui.ShowCurrentCommand)
    {
        commandToShow := SubStr(g_CurrentCommand, InStr(g_CurrentCommand, " | ") + 3)
        ControlSetText, %g_CommandArea%, %commandToShow%, %g_WindowName%
    }
}

ClearInput:
    ControlSetText, %g_InputArea%, , %g_WindowName%
    ControlFocus, %g_InputArea%
return

RunCurrentCommand:
    if (GetInputState() == 1)
    {
        Send, {enter}
    }

    if (g_CurrentInput != "")
    {
        RunCommand(g_CurrentCommand)
    }
return

ParseArg:
    commandPrefix := SubStr(g_CurrentInput, 1, 1)

    if (commandPrefix == ";" || commandPrefix == ":")
    {
        Arg := SubStr(g_CurrentInput, 2)
        return
    }
    else if (commandPrefix == "@")
    {
        ; 处理调整过顺序的命令
        Arg := SubStr(g_CurrentInput, 4)
        return
    }

    ; 用空格来判断参数
    if (InStr(g_CurrentInput, " ") && !g_UseFallbackCommands)
    {
        Arg := SubStr(g_CurrentInput, InStr(g_CurrentInput, " ") + 1)
    }
    else
    {
        Arg := g_CurrentInput
    }
return

MatchCommand(Haystack, Needle)
{
    if (g_EnableTCMatch)
    {
        return TCMatch(Haystack, Needle)
    }

    return InStr(Haystack, Needle)
}

RunCommand(originCmd)
{
    GoSub, ParseArg

    g_UseDisplay := false
    g_DisableAutoExit := true

    splitedOriginCmd := StrSplit(originCmd, " | ")
    ; 去掉括号内的注释
    cmd := StrSplit(splitedOriginCmd[2], "（")[1]

    if (splitedOriginCmd[1] == "file")
    {
        if (InStr(cmd, ".lnk"))
        {
            ; 处理 32 位 ahk 运行不了某些 64 位系统 .lnk 的问题
            FileGetShortcut, %cmd%, filePath
            if (!FileExist(filePath))
            {
                filePath := StrReplace(filePath, "C:\Program Files (x86)", "C:\Program Files")
                if (FileExist(filePath))
                {
                    cmd := filePath
                }
            }
        }

        Run, %cmd%
    }
    else if (splitedOriginCmd[1] == "function")
    {
        if (splitedOriginCmd.Length() >= 3)
        {
            Arg := splitedOriginCmd[3]
        }

        if (IsLabel(cmd))
        {
            GoSub, %cmd%
        }
    }
    else if (splitedOriginCmd[1] == "cmd")
    {
        RunWithCmd(cmd)
    }

    if (g_Conf.Config.SaveHistory && cmd != "DisplayHistoryCommands")
    {
        if (splitedOriginCmd.Length() == 2 && Arg != "")
        {
            g_HistoryCommands.InsertAt(1, originCmd " | " Arg)
        }
        else if (originCmd != "")
        {
            g_HistoryCommands.InsertAt(1, originCmd)
        }

        if (g_HistoryCommands.Length() > g_Conf.Config.HistorySize)
        {
            g_HistoryCommands.Pop()
        }
    }

    if (g_Conf.Config.AutoRank)
    {
        IncreaseRank(originCmd)
    }

    g_DisableAutoExit := false

    if (g_Conf.Config.RunOnce && !g_UseDisplay)
    {
        GoSub, EscFunction
    }
}

IncreaseRank(cmd, show = false, inc := 1)
{
    splitedCmd := StrSplit(cmd, " | ")

    if (splitedCmd.Length() >= 3 && splitedCmd[1] == "function")
    {
        ; 去掉参数
        cmd := splitedCmd[1]  " | " splitedCmd[2]
    }

    cmdRank := g_AutoConf.GetValue("Rank", cmd)
    if cmdRank is integer
    {
        g_AutoConf.DeleteKey("Rank", cmd)
        cmdRank += inc
    }
    else
    {
        cmdRank := inc
    }

    if (cmdRank > 0 && cmd != "")
    {
        g_AutoConf.AddKey("Rank", cmd, cmdRank)
    }
    else
    {
        cmdRank := 0
    }

    if (show)
    {
        ToolTip, 调整 %cmd% 的权重到 %cmdRank%
        SetTimer, RemoveToolTip, 800
    }
}

; 比较耗时，必要时才使用，也可以手动编辑 RunZ.auto.ini
CleanupRank:
    ; 先把 g_Commands 里的 Rank 信息清掉
    LoadFiles(false)

    for command, rank in g_AutoConf.Rank
    {
        cleanup := true
        for index, element in g_Commands
        {
            if (InStr(element, command) == 1)
            {
                cleanup := false
                break
            }
        }
        if (cleanup)
        {
            g_AutoConf.DeleteKey("Rank", command)
        }
    }

    Loop
    {
        g_AutoConf.Save()

        if (!FileExist(g_AutoConfFile))
        {
            MsgBox, 配置文件 %g_AutoConfFile% 写入后丢失，请检查磁盘并点确定来重试
        }
        else
        {
            break
        }
    }

    LoadFiles()
return

RunSelectedCommand:
    if (SubStr(A_ThisHotkey, 1, 1) == "~")
    {
        ControlGetFocus, ctrl,
        if (ctrl == g_InputArea)
        {
            return
        }
    }

    index := Asc(SubStr(A_ThisHotkey, 0, 1)) - g_FirstChar + 1

    RunCommand(g_CurrentCommandList[index])
return

IncreaseRank:
    if (g_CurrentCommand != "")
    {
        IncreaseRank(g_CurrentCommand, true)
        LoadFiles()
    }
return

DecreaseRank:
    if (g_CurrentCommand != "")
    {
        IncreaseRank(g_CurrentCommand, true, -1)
        LoadFiles()
    }
return

LoadFiles(loadRank := true)
{
    g_Commands := Object()
    g_FallbackCommands := Object()

    if (loadRank)
    {
        rankString := ""
        for command, rank in g_AutoConf.Rank
        {
            if (StrLen(command) > 0)
            {
                rankString .= rank "`t" command "`n"
            }
        }

        if (rankString != "")
        {
            Sort, rankString, R N

            Loop, Parse, rankString, `n
            {
                if (A_LoopField == "")
                {
                    continue
                }

                g_Commands.Push(StrSplit(A_loopField, "`t")[2])
            }
        }
    }

    for key, value in g_Conf.Command
    {
        if (value != "")
        {
            g_Commands.Push(key . "（" . value "）")
        }
        else
        {
            g_Commands.Push(key)
        }
    }

    if (FileExist(A_ScriptDir "\Conf\UserFunctions.ahk"))
    {
        userFunctionLabel := "UserFunctions"
        if (IsLabel(userFunctionLabel))
        {
            GoSub, %userFunctionLabel%
        }
        else
        {
            MsgBox, 未在 %A_ScriptDir%\Conf\UserFunctions.ahk 中发现 %userFunctionLabel% 标签，请修改！
        }
    }

    GoSub, Functions

    Loop, Read, %g_SearchFileList%
    {
        g_Commands.Push(A_LoopReadLine)
    }

    if (g_Conf.Config.LoadControlPanelFunctions)
    {
        Loop, Read, %A_ScriptDir%\Core\ControlPanelFunctions.txt
        {
            g_Commands.Push(A_LoopReadLine)
        }
    }
}

DisplayText(text)
{
    textToDisplay := StrReplace(text, "`n", "`r`n")
    ControlSetText, %g_OutputArea%, %textToDisplay%, %g_WindowName%
}

DisplayResult(result)
{
    DisplayText(result)
    g_UseDisplay := true
}

LoadHistoryCommands()
{
    for key, value in g_AutoConf.History
    {
        if (StrLen(value) > 0)
        {
            g_HistoryCommands.Push(value)
        }
    }
}

DisplayHistoryCommands:
    result := ""
    g_CurrentCommandList := Object()
    g_CurrentLine := 1

    for index, element in g_HistoryCommands
    {
        if (index == 1)
        {
            result .= SubStr(Chr(g_FirstChar + index - 1) . ">| " . element, 1, g_DisplayCols) "`n"
            g_CurrentCommand := element
        }
        else
        {
            result .= SubStr(Chr(g_FirstChar + index - 1) . " | " . element, 1, g_DisplayCols) "`n"
        }

        g_CurrentCommandList.Push(element)
    }

    result := StrReplace(result, "file | ", "文件 | ")
    result := StrReplace(result, "function | ", "功能 | ")
    result := StrReplace(result, "cmd | ", "命令 | ")

    DisplayResult(result)
return

@(label, info, fallback = false, key = "")
{
    if (!IsLabel(label))
    {
        MsgBox, 未找到 %label% 标签，请检查 %A_ScriptDir%\Conf\UserFunctions.ahk 文件格式！
        return
    }

    g_Commands.Push("function | " . label . "（" . info . "）")
    if (fallback)
    {
        g_FallbackCommands.Push("function | " . label . "（" . info . "）")
    }

    if (key != "")
    {
        Hotkey, %key%, %label%
    }
}

RunAndGetOutput(command)
{
    tempFileName := "RunZ.stdout.log"
    fullCommand = bash -c "%command% &> %tempFileName%"

    if (!FileExist("c:\msys64\usr\bin\bash.exe"))
    {
        fullCommand = %ComSpec% /C "%command% > %tempFileName%"
    }

    RunWait, %fullCommand%, %A_Temp%, Hide
    FileRead, result, %A_Temp%\%tempFileName%
    FileDelete, %A_Temp%\%tempFileName%
    return result
}

RunWithCmd(command)
{
    if (FileExist("c:\msys64\usr\bin\mintty.exe"))
    {
        Run, % "mintty -e sh -c '" command "; read'"
    }
    else
    {
        Run, % ComSpec " /C " command " & pause"
    }
}

OpenPath(filePath)
{
    if (!FileExist(filePath))
    {
        return
    }

    if (FileExist(g_Conf.Config.TCPath))
    {
        TCPath := g_Conf.Config.TCPath
        Run, %TCPath% /O /A /L="%filePath%"
    }
    else
    {
        SplitPath, filePath, , fileDir, ,
        Run, explorer "%fileDir%"
    }
}

GetAllFunctions()
{
    result := ""

    for index, element in g_Commands
    {
        if (InStr(element, "function | ") == 1 and !InStr(result, element "`n"))
        {
            result .= element "`n"
        }
    }

    result := StrReplace(result, "function | ", "功能 | ")

    return result
}

OpenCurrentFileDir:
    filePath := StrSplit(g_CurrentCommand, " | ")[2]
    OpenPath(filePath)
return

DeleteCurrentFile:
    filePath := StrSplit(g_CurrentCommand, " | ")[2]

    if (!FileExist(filePath))
    {
        return
    }

    FileRecycle, % filePath
    GoSub, ReloadFiles
return

ShowCurrentFile:
    clipboard := StrSplit(g_CurrentCommand, " | ")[2]
    ToolTip, % clipboard
    SetTimer, RemoveToolTip, 800
return

RemoveToolTip:
    ToolTip
    SetTimer, RemoveToolTip, Off
return

WM_ACTIVATE(wParam, lParam)
{
    if (g_DisableAutoExit)
    {
        return
    }

    if (wParam >= 1) ; 窗口激活
    {
        return
    }
    else if (wParam <= 0) ; 窗口非激活
    {
        SetTimer, ToExit, 50
    }
}

ToExit:
    if (!WinExist("RunZ.ahk"))
    {
        GoSub, EscFunction
    }

    SetTimer, ToExit, Off
return

KeyHelpText()
{
    return ""
    . "Win + j 显示窗口`n"
    . "键入内容 搜索，回车 执行，Alt + 字母 执行，Esc 退出`n"
    . "按 Tab 后再按 字母或数字 也可执行字母对应功能`n"
    . "按 Tab 后 Shift + 字母或数字 定位到对应功能`n"
    . "Ctrl + j 移动到下一条命令`n"
    . "Ctrl + k 移动到上一条命令`n"
    . "Ctrl + f 翻到下一页`n"
    . "Ctrl + b 翻到上一页`n"
    . "Win  + j 激活窗口`n"
    . "Ctrl + h 显示历史记录`n"
    . "Ctrl + n 可增加当前功能的权重`n"
    . "Ctrl + p 可减少当前功能的权重`n"
    . "Ctrl + l 清除编辑框内容`n"
    . "Ctrl + r 重新创建待搜索文件列表`n"
    . "Ctrl + q 重启`n"
    . "Ctrl + d 用 TC 打开第一个文件所在目录`n"
    . "Ctrl + s 显示并复制当前文件的完整路径`n"
    . "Ctrl + x 删除当前文件`n"
    . "Ctrl + i 移动光标当行首`n"
    . "Ctrl + o 移动光标当行尾`n"
    . "F2       编辑配置文件`n`n"
}

UrlDownloadToString(url)
{
    static whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    whr.Open("GET", url, true)
    whr.Send()
    whr.WaitForResponse()
    return whr.ResponseText
}

; 修改自万年书妖的 Candy 里的 SksSub_UrlEncode 函数，用于转换编码。感谢！
UrlEncode(url, enc = "UTF-8")
{
    enc := trim(enc)
    If enc=
        return url
    formatInteger := A_FormatInteger
    SetFormat, IntegerFast, H
    VarSetCapacity(buff, StrPut(url, enc))
    Loop % StrPut(url, &buff, enc) - 1
    {
        byte := NumGet(buff, A_Index-1, "UChar")
        encoded .= byte > 127 or byte <33 ? "%" Substr(byte, 3) : Chr(byte)
    }
    SetFormat, IntegerFast, %formatInteger%
    return encoded
}

; 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
}

#include %A_ScriptDir%\Lib\EasyIni.ahk
#include %A_ScriptDir%\Lib\TCMatch.ahk
#include %A_ScriptDir%\Lib\Eval.ahk
#include %A_ScriptDir%\Lib\JSON.ahk
#include %A_ScriptDir%\Lib\Kanji\Kanji.ahk
#include %A_ScriptDir%\Lib\Gdip.ahk
#include %A_ScriptDir%\Core\Functions.ahk
#include %A_ScriptDir%\Core\ReservedFunctions.ahk
; 用户自定义命令
#include *i %A_ScriptDir%\Conf\UserFunctions.ahk