update backup

update backup

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

SetKeyDelay , 30, 30 ; first is delay between keypresses, and second is press duration

#WinActivateForce
DetectHiddenWindows, On

;#MaxHotkeysPerInterval 200

; https://stackoverflow.com/questions/49009176/autohotkey-causing-control-key-to-get-stuck
; https://www.autohotkey.com/docs/commands/_MenuMaskKey.htm
#MenuMaskKey vkFF

Return() {
    For Each, Modifier in ["Shift","LWin","RWin","Alt","LControl", "RControl"]
        If GetKeyState(Modifier) And !GetKeyState(Modifier,"P")
            Send, {%Modifier% Up}
    Exit
}
global ShiftDownTicks := 0
global ShiftPressed := 0

Loop
{
    if (ShiftPressed = 0) && GetKeyState("Shift")
    {
        ShiftPressed := 1
        ShiftDownTicks := A_TickCount
    }
    Sleep, 50
}

Shift UP::
    sleep 10
    ElapsedTime := A_TickCount - ShiftDownTicks
    ;MsgBox %ShiftDownTicks% DOWN %ElapsedTime% Up
    If ElapsedTime < 250
    {
        Send {Escape}
    }
    ShiftPressed := 0
return()

Shift & F13::

^Tab::
    Send #{Tab}
return()

^!o::
IfWinActive, ahk_exe Obsidian.exe
    sleep 10
else
    WinShow ahk_exe Obsidian.exe
    WinActivate ahk_exe Obsidian.exe
    sleep 10
    WinSet Top
return()

^!g::
IfWinActive, ahk_exe msedge.exe
    sleep 10
else
    ; WinShow will cause the search bar in Edge shown
    ; in about://flags, enable `Use Aura for Find on page`
    ; can make it better, but still a bit messy, some time not
    ; getting focus
    WinShow ahk_exe msedge.exe
    WinActivate ahk_exe msedge.exe
    sleep 10
    WinSet Top
return()

^!m::
IfWinActive, ahk_exe chrome.exe
    sleep 10
else
    WinShow ahk_exe chrome.exe
    WinActivate ahk_exe chrome.exe
    sleep 10
    WinSet Top
return()

^!Space::
Process,close,explorer.exe
sleep 50 ;This sleep 5000 is to let you see what actually happens. Decrease it later
run, explorer.exe
WinWait, ahk_class CabinetWClass
WinClose ;close the new explorer window
return()

global ChineseLayout := 0

SetCapsLockState, AlwaysOff
+CapsLock::CapsLock

CapsLock::
    Send, #{space}
    ChineseLayout := !ChineseLayout
    If ChineseLayout = 1
        KeyboardLED(4, "on")
    Else
        KeyboardLED(4, "off")
return()

^CapsLock::
    ChineseLayout := 0
    KeyboardLED(4, "off")
return()

/*

    Keyboard LED control for AutoHotkey_L
        http://www.autohotkey.com/forum/viewtopic.php?p=468000#468000

    KeyboardLED(LEDvalue, "Cmd", Kbd)
        LEDvalue  - ScrollLock=1, NumLock=2, CapsLock=4
        Cmd       - on/off/switch
        Kbd       - index of keyboard (probably 0 or 2)

*/

KeyboardLED(LEDvalue, Cmd, Kbd=0)
{
  SetUnicodeStr(fn,"\Device\KeyBoardClass" Kbd)
  h_device:=NtCreateFile(fn,0+0x00000100+0x00000080+0x00100000,1,1,0x00000040+0x00000020,0)

  If Cmd= switch  ;switches every LED according to LEDvalue
   KeyLED:= LEDvalue
  If Cmd= on  ;forces all choosen LED's to ON (LEDvalue= 0 ->LED's according to keystate)
   KeyLED:= LEDvalue | (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
  If Cmd= off  ;forces all choosen LED's to OFF (LEDvalue= 0 ->LED's according to keystate)
    {
    LEDvalue:= LEDvalue ^ 7
    KeyLED:= LEDvalue & (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
    }

  success := DllCall( "DeviceIoControl"
              ,  "ptr", h_device
              , "uint", CTL_CODE( 0x0000000b     ; FILE_DEVICE_KEYBOARD
                        , 2
                        , 0             ; METHOD_BUFFERED
                        , 0  )          ; FILE_ANY_ACCESS
              , "int*", KeyLED << 16
              , "uint", 4
              ,  "ptr", 0
              , "uint", 0
              ,  "ptr*", output_actual
              ,  "ptr", 0 )

  NtCloseFile(h_device)
  return success
}

CTL_CODE( p_device_type, p_function, p_method, p_access )
{
  Return, ( p_device_type << 16 ) | ( p_access << 14 ) | ( p_function << 2 ) | p_method
}

NtCreateFile(ByRef wfilename,desiredaccess,sharemode,createdist,flags,fattribs)
{
  VarSetCapacity(objattrib,6*A_PtrSize,0)
  VarSetCapacity(io,2*A_PtrSize,0)
  VarSetCapacity(pus,2*A_PtrSize)
  DllCall("ntdll\RtlInitUnicodeString","ptr",&pus,"ptr",&wfilename)
  NumPut(6*A_PtrSize,objattrib,0)
  NumPut(&pus,objattrib,2*A_PtrSize)
  status:=DllCall("ntdll\ZwCreateFile","ptr*",fh,"UInt",desiredaccess,"ptr",&objattrib
                  ,"ptr",&io,"ptr",0,"UInt",fattribs,"UInt",sharemode,"UInt",createdist
                  ,"UInt",flags,"ptr",0,"UInt",0, "UInt")
  return % fh
}

NtCloseFile(handle)
{
  return DllCall("ntdll\ZwClose","ptr",handle)
}

SetUnicodeStr(ByRef out, str_)
{
  VarSetCapacity(out,2*StrPut(str_,"utf-16"))
  StrPut(str_,&out,"utf-16")
}

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

SetKeyDelay , 30, 30 ; first is delay between keypresses, and second is press duration

#WinActivateForce
DetectHiddenWindows, On

;#MaxHotkeysPerInterval 200

; https://stackoverflow.com/questions/49009176/autohotkey-causing-control-key-to-get-stuck
; https://www.autohotkey.com/docs/commands/_MenuMaskKey.htm
#MenuMaskKey vkFF

Return() {
    For Each, Modifier in ["Shift","LWin","RWin","Alt","LControl", "RControl"]
        If GetKeyState(Modifier) And !GetKeyState(Modifier,"P")
            Send, {%Modifier% Up}
    Exit
}
global ShiftDownTicks := 0
global ShiftPressed := 0

Loop
{
    if (ShiftPressed = 0) && GetKeyState("Shift")
    {
        ShiftPressed := 1
        ShiftDownTicks := A_TickCount
    }
    Sleep, 50
}

Shift UP::
    sleep 10
    ElapsedTime := A_TickCount - ShiftDownTicks
    MsgBox %ShiftDownTicks% DOWN %ElapsedTime% Up
    If ElapsedTime < 250
    {
        Send {Escape}
    }
    ShiftPressed := 0
return()

Shift & F13::

^Tab::
    Send #{Tab}
return()

^!o::
IfWinActive, ahk_exe Obsidian.exe
    sleep 10
else
    WinShow ahk_exe Obsidian.exe
    WinActivate ahk_exe Obsidian.exe
    sleep 10
    WinSet Top
return()

^!g::
IfWinActive, ahk_exe msedge.exe
    sleep 10
else
    ; WinShow will cause the search bar in Edge shown
    ; in about://flags, enable `Use Aura for Find on page`
    ; can make it better, but still a bit messy, some time not
    ; getting focus
    WinShow ahk_exe msedge.exe
    WinActivate ahk_exe msedge.exe
    sleep 10
    WinSet Top
return()

^!m::
IfWinActive, ahk_exe chrome.exe
    sleep 10
else
    WinShow ahk_exe chrome.exe
    WinActivate ahk_exe chrome.exe
    sleep 10
    WinSet Top
return()

^!Space::
Process,close,explorer.exe
sleep 50 ;This sleep 5000 is to let you see what actually happens. Decrease it later
run, explorer.exe
WinWait, ahk_class CabinetWClass
WinClose ;close the new explorer window
return()

global ChineseLayout := 0

SetCapsLockState, AlwaysOff
+CapsLock::CapsLock

CapsLock::
    Send, #{space}
    ChineseLayout := !ChineseLayout
    If ChineseLayout = 1
        KeyboardLED(4, "on")
    Else
        KeyboardLED(4, "off")
return()

^CapsLock::
    ChineseLayout := 0
    KeyboardLED(4, "off")
return()

/*

    Keyboard LED control for AutoHotkey_L
        http://www.autohotkey.com/forum/viewtopic.php?p=468000#468000

    KeyboardLED(LEDvalue, "Cmd", Kbd)
        LEDvalue  - ScrollLock=1, NumLock=2, CapsLock=4
        Cmd       - on/off/switch
        Kbd       - index of keyboard (probably 0 or 2)

*/

KeyboardLED(LEDvalue, Cmd, Kbd=0)
{
  SetUnicodeStr(fn,"\Device\KeyBoardClass" Kbd)
  h_device:=NtCreateFile(fn,0+0x00000100+0x00000080+0x00100000,1,1,0x00000040+0x00000020,0)

  If Cmd= switch  ;switches every LED according to LEDvalue
   KeyLED:= LEDvalue
  If Cmd= on  ;forces all choosen LED's to ON (LEDvalue= 0 ->LED's according to keystate)
   KeyLED:= LEDvalue | (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
  If Cmd= off  ;forces all choosen LED's to OFF (LEDvalue= 0 ->LED's according to keystate)
    {
    LEDvalue:= LEDvalue ^ 7
    KeyLED:= LEDvalue & (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
    }

  success := DllCall( "DeviceIoControl"
              ,  "ptr", h_device
              , "uint", CTL_CODE( 0x0000000b     ; FILE_DEVICE_KEYBOARD
                        , 2
                        , 0             ; METHOD_BUFFERED
                        , 0  )          ; FILE_ANY_ACCESS
              , "int*", KeyLED << 16
              , "uint", 4
              ,  "ptr", 0
              , "uint", 0
              ,  "ptr*", output_actual
              ,  "ptr", 0 )

  NtCloseFile(h_device)
  return success
}

CTL_CODE( p_device_type, p_function, p_method, p_access )
{
  Return, ( p_device_type << 16 ) | ( p_access << 14 ) | ( p_function << 2 ) | p_method
}

NtCreateFile(ByRef wfilename,desiredaccess,sharemode,createdist,flags,fattribs)
{
  VarSetCapacity(objattrib,6*A_PtrSize,0)
  VarSetCapacity(io,2*A_PtrSize,0)
  VarSetCapacity(pus,2*A_PtrSize)
  DllCall("ntdll\RtlInitUnicodeString","ptr",&pus,"ptr",&wfilename)
  NumPut(6*A_PtrSize,objattrib,0)
  NumPut(&pus,objattrib,2*A_PtrSize)
  status:=DllCall("ntdll\ZwCreateFile","ptr*",fh,"UInt",desiredaccess,"ptr",&objattrib
                  ,"ptr",&io,"ptr",0,"UInt",fattribs,"UInt",sharemode,"UInt",createdist
                  ,"UInt",flags,"ptr",0,"UInt",0, "UInt")
  return % fh
}

NtCloseFile(handle)
{
  return DllCall("ntdll\ZwClose","ptr",handle)
}

SetUnicodeStr(ByRef out, str_)
{
  VarSetCapacity(out,2*StrPut(str_,"utf-16"))
  StrPut(str_,&out,"utf-16")
}


update

update

VimDConfig:
    vim.SetPlugin("VimDConfig", "Array", "0.1", "VimDesktop的配置界面")
    vim.SetAction("<VimDConfig_Plugin>", "显示 VimDesktop 插件信息")
    vim.SetAction("<VimDConfig_Keymap>", "显示 VimDesktop 热键信息")
    vim.SetAction("<VimDConfig_EditConfig>", "打开 VimDesktop 配置文件")
return

<VimDConfig_Plugin>:
{
    GUI, VimDConfig_plugin:Destroy
    GUI, VimDConfig_plugin:Default
    GUI, VimDConfig_plugin:Font, s10, Microsoft YaHei

    GUI, VimDConfig_plugin:Add, GroupBox, x10 y10 w170 h440, 插件 &P
    GUI, VimDConfig_plugin:Add, ListView, x20 y35 w150 h400 grid altsubmit gVimDConfig_LoadActions, 名称
    for plugin, obj in vim.PluginList
        LV_Add("", plugin)

    GUI, VimDConfig_plugin:Add, GroupBox, x10 y460 w170 h70, 过滤 &F
    GUI, VimDConfig_plugin:Add, Edit, x20 y490 gsearch_plugin v_search

    GUI, VimDConfig_plugin:Add, GroupBox, x190 y10 w650 h520, 动作 &A
    GUI, VimDConfig_plugin:Add, ListView, glistview x200 y35 w630 h482 grid altsubmit, 序号|动作|描述（双击进入文件）

    LV_ModifyCol(1, "center")
    LV_ModifyCol(2, "left 250")
    LV_ModifyCol(3, "left 400")
    GUI, VimDConfig_plugin:Show
    return
}

VimDConfig_LoadActions:
{
    If A_GuiEvent = I
    {
        if not InStr(ErrorLevel, "S", true)
        {
            return
        }

        GUI, VimDConfig_plugin:ListView, sysListview321
        LV_GetText(plugin, A_EventInfo)
        GUI, VimDConfig_plugin:Default
        GUI, VimDConfig_plugin:ListView, sysListview322
        idx := 1
        LV_Delete()

        global current_plugin := ""
        for action, type in vim.ActionFromPlugin
        {
            If type = %plugin%
            {
                Desc := vim.GetAction(action)
                LV_Add("", idx, action, Desc.Comment)
                current_plugin .= idx "`t" action "`t" Desc.Comment "`n"
                idx++
            }
        }
    }
    return
}

<VimDConfig_Keymap>:
{
    menu, VimDConfig_keymap_menu, add
    menu, VimDConfig_keymap_menu, add, &Exit, VimDConfig_keymap_exit

    GUI, VimDConfig_keymap:Destroy
    GUI, VimDConfig_keymap:Default
    GUI, VimDConfig_keymap:Font, s10, Microsoft YaHei

    GUI, VimDConfig_keymap:Add, GroupBox, x10 y10 w200 h269, 插件 &P
    GUI, VimDConfig_keymap:Add, ListBox, x20 y35 w180 R12 center gVimDConfig_keymap_loadmodelist

    GUI, VimDConfig_keymap:Add, GroupBox, x10 y290 w200 h135, 模式 &M
    GUI, VimDConfig_keymap:Add, ListBox, x20 y315 w180 R5 center gVimDConfig_keymap_loadhotkey

    GUI, VimDConfig_keymap:Add, GroupBox, x10 y435 w200 h61, 过滤 &F
    GUI, VimDConfig_keymap:Add, Edit, gsearch_keymap v_search x20 y460 w180 h25

    GUI, VimDConfig_keymap:Add, GroupBox, x225 y10 w650 h486, 映射 &K
    GUI, VimDConfig_keymap:Add, Listview, glistview x235 y36 w630 h450 grid, 热键|动作（双击定位，右键双击编辑）|描述

    LV_ModifyCol(1, "left 100")
    LV_ModifyCol(2, "left 250")
    LV_ModifyCol(3, "left 400")

    VimDConfig_keymap_loadwinlist()
    VimDConfig_keymap_loadhotkey(VimDConfig_keymap_loadmodelist(thiswin))

    GUI, VimDConfig_keymap:show
    ControlFocus, Edit1, A
    return
}

<VimDConfig_EditConfig>:
    Run, %A_ScriptDir%\vimd.ini
return

VimDConfig_keymap_exit:
{
    GUI, VimDConfig_keymap:Destroy
    return
}

VimDConfig_keymap_loadwinlist()
{
    global vim
    list := "|全局"
    GUI, VimDConfig_keymap:Default
    for win, obj in vim.WinList
    {
        if vim.ExcludeWinList[win]
        {
            continue
        }

        ; Convert class name TTOTAL_CMD to plugin name TotalCommander
        if (win = "TTOTAL_CMD")
        {
            win := "TotalCommander"
        }
        list .= "|" win
    }
    GuiControl, , ListBox1, %list%
}

VimDConfig_keymap_loadmodelist:
    VimDConfig_keymap_loadmodelist()
    GUI, VimDConfig_keymap:Default
    LV_delete()
    GuiControl, Choose, ListBox2, |normal
return

VimDConfig_keymap_loadmodelist(win = "")
{
    global vim
    GUI, VimDConfig_keymap:Default
    If not strlen(win)
        ControlGet, win, Choice, , ListBox1
    If win = 全局
        winObj := vim.GetWin()
    Else
        winObj := vim.GetWin(win)
    for mode, obj in winObj.modeList
        mlist .= "|" mode
    GuiControl, , ListBox2, %mlist%
    return win
}

VimDConfig_keymap_loadhotkey:
    GUI, VimDConfig_keymap:Default
    ControlGet, win, Choice, , ListBox1
    win := RegExMatch(win, "^全局$") ? "" : win

    ; Convert plugin name TotalCommander to class name TTOTAL_CMD
    if (win = "TotalCommander")
    {
        win := "TTOTAL_CMD"
    }

    ControlGet, mode, Choice, , ListBox2
    VimDConfig_keymap_loadhotkey(win, mode)
return

VimDConfig_keymap_loadhotkey(win, mode = "")
{
    global vim
    global current_keymap := ""
    If strlen(mode)
    {
        winObj  := vim.GetWin(win)
        modeobj := winObj.modeList[mode]
    }
    Else
        modeobj := vim.GetMode(win)
    GUI, VimDConfig_keymap:Default
    LV_delete()
    for key, i in modeobj.keymapList
    {
        ; Type = 1 : Function
        if (vim.GetAction(i).Type = 1)
        {
            ActionDescList := vim.GetAction(i).Comment
            actionDesc := StrSplit(%ActionDescList%[key], "|")
            LV_ADD("", Key, actionDesc[1], actionDesc[2])
            current_keymap .= Key "`t" actionDesc[1] "`t" actionDesc[2] "`n"
        }
        else
        {
            LV_Add("", ReGexReplace(Key, "<S-(.*)>", "$1"), i, vim.GetAction(i).Comment)
            current_keymap .= Key "`t" i "`t" vim.GetAction(i).Comment "`n"
        }
    }
}

listview:
    if A_GuiEvent = DoubleClick
    {
        ;~ ToolTip You double-clicked row number %A_EventInfo%.
        LV_GetText(SelectedAction, A_EventInfo, 2)
        LV_GetText(SelectedDesc, A_EventInfo, 3)
        SearchFileForEdit(SelectedAction, SelectedDesc, false)
    }
    else if A_GuiEvent = R
    {
        LV_GetText(SelectedAction, A_EventInfo, 2)
        LV_GetText(SelectedDesc, A_EventInfo, 3)
        SearchFileForEdit(SelectedAction, SelectedDesc, true)
    }

return

SearchFileForEdit(Action, Desc, EditKeyMapping)
{
    IsUserCmd := RegExMatch(Action, "^(run|key|dir|tccmd|wshkey)$")
    if (IsUserCmd || EditKeyMapping)
    {
        SearchLine := "=" Action
        if (IsUserCmd || Action == "function")
        {
            SearchLine := Action "|" Desc
        }

        Loop, Read, %A_ScriptDir%\vimd.ini
        {
            if (InStr(A_LoopReadLine, SearchLine))
            {
                EditFile(A_ScriptDir "\vimd.ini", A_Index)
                return
            }
        }

        EditFile(A_ScriptDir "\vimd.ini")
        return
    }

    label := Action ":"
    if (Action == "function")
    {
        label := Desc
    }

    Loop, %A_ScriptDir%\plugins\*.ahk, , 1
    {
        Loop, Read, %A_LoopFileFullPath%
        {
            if (InStr(A_LoopReadLine, label) == 1)
            {
                EditFile(A_LoopFileFullPath, A_Index)
                return
            }
        }
    }

    Loop, %A_ScriptDir%\core\*.ahk, , 1
    {
        Loop, Read, %A_LoopFileFullPath%
        {
            if (InStr(A_LoopReadLine, label) = 1)
            {
                EditFile(A_LoopFileFullPath, A_Index)
                return
            }
        }
    }
}

EditFile(editPath, line := 1)
{
    editorArgs := {}
    editorArgs["notepad"] := "/g $line $file"
    editorArgs["notepad2"] := "/g $line $file"
    editorArgs["sublime_text"] := "$file:$line"
    editorArgs["vim"] := "+$line $file"
    editorArgs["gvim"] := "--remote-silent-tab +$line $file"
    editorArgs["everedit"] := "-n$line $file"
    editorArgs["notepad++"] := "-n$line $file"
    editorArgs["EmEditor"] := "-l $line $file"
    editorArgs["uedit32"] := "$file/$line"
    editorArgs["Editplus"] := "$file -cursor $line"
    editorArgs["textpad"] := "$file($line)"
    editorArgs["pspad"] := "$file /$line"
    editorArgs["ConTEXT"] := "$file /g1:$line"
    editorArgs["scite"] := "$file -goto:$line"

    Global editor

    If not FileExist(editor)
    {
        MsgBox, 请配置 vimd.ini 中 [config] 中的 editor ，并重启 vimd ！
        return
    }

    ; 根据编辑器 exe 名称获取打开参数
    SplitPath, editor, , , OutExtension, OutNameNoExt
    args := editorArgs[OutNameNoExt]
    StringReplace, args, args, $line, %line%
    StringReplace, args, args, $file, "%editPath%"
    target := editor " " args
    if (OutExtension = "sh")
    {
        target := "sh.exe " target
    }

    run, %target%
}

search_keymap:
    global current_keymap
    search_to_display(current_keymap)
return

search_plugin:
    global current_plugin
    search_to_display(current_plugin)
return

search_to_display(lines)
{
    Gui Submit, nohide
    GuiControlGet, OutputVar, , _search

    text := StrSplit(lines, "`n")

    LV_Delete() ; 清理不掉，第二次加载后，都成了重复的了，不知道怎么处理
    GuiControl, -Redraw, listview ; 重新启用重绘 (上面把它禁用了)
    for k, v in text
    {
        if v =
            continue
        if Instr(v, OutputVar)
        {
            list := StrSplit(v, "`t")
            LV_Add("", list[1], list[2], list[3])
        }
    }
    GuiControl, +Redraw, listview ; 重新启用重绘 (上面把它禁用了)
}

VimDConfig:
    vim.SetPlugin("VimDConfig", "Array", "0.1", "VimDesktop的配置界面")
    vim.SetAction("<VimDConfig_Plugin>", "显示 VimDesktop 插件信息")
    vim.SetAction("<VimDConfig_Keymap>", "显示 VimDesktop 热键信息")
    vim.SetAction("<VimDConfig_EditConfig>", "打开 VimDesktop 配置文件")
return

<VimDConfig_Plugin>:
{
    GUI, VimDConfig_plugin:Destroy
    GUI, VimDConfig_plugin:Default
    GUI, VimDConfig_plugin:Font, s10, Microsoft YaHei

    GUI, VimDConfig_plugin:Add, GroupBox, x10 y10 w170 h440, 插件 &P
    GUI, VimDConfig_plugin:Add, ListView, x20 y35 w150 h400 grid altsubmit gVimDConfig_LoadActions, 名称
    for plugin, obj in vim.PluginList
        LV_Add("", plugin)

    GUI, VimDConfig_plugin:Add, GroupBox, x10 y460 w170 h70, 过滤 &F
    GUI, VimDConfig_plugin:Add, Edit, x20 y490 gsearch_plugin v_search

    GUI, VimDConfig_plugin:Add, GroupBox, x190 y10 w650 h520, 动作 &A
    GUI, VimDConfig_plugin:Add, ListView, glistview x200 y35 w630 h482 grid altsubmit, 序号|动作|描述（双击进入文件）

    LV_ModifyCol(1, "center")
    LV_ModifyCol(2, "left 250")
    LV_ModifyCol(3, "left 400")
    GUI, VimDConfig_plugin:Show
    return
}

VimDConfig_LoadActions:
{
    If A_GuiEvent = I
    {
        if not InStr(ErrorLevel, "S", true)
        {
            return
        }

        GUI, VimDConfig_plugin:ListView, sysListview321
        LV_GetText(plugin, A_EventInfo)
        GUI, VimDConfig_plugin:Default
        GUI, VimDConfig_plugin:ListView, sysListview322
        idx := 1
        LV_Delete()

        global current_plugin := ""
        for action, type in vim.ActionFromPlugin
        {
            If type = %plugin%
            {
                Desc := vim.GetAction(action)
                LV_Add("", idx, action, Desc.Comment)
                current_plugin .= idx "`t" action "`t" Desc.Comment "`n"
                idx++
            }
        }
    }
    return
}

<VimDConfig_Keymap>:
{
    menu, VimDConfig_keymap_menu, add
    menu, VimDConfig_keymap_menu, add, &Exit, VimDConfig_keymap_exit

    GUI, VimDConfig_keymap:Destroy
    GUI, VimDConfig_keymap:Default
    GUI, VimDConfig_keymap:Font, s10, Microsoft YaHei

    GUI, VimDConfig_keymap:Add, GroupBox, x10 y10 w200 h269, 插件 &P
    GUI, VimDConfig_keymap:Add, ListBox, x20 y35 w180 R12 center gVimDConfig_keymap_loadmodelist

    GUI, VimDConfig_keymap:Add, GroupBox, x10 y290 w200 h135, 模式 &M
    GUI, VimDConfig_keymap:Add, ListBox, x20 y315 w180 R5 center gVimDConfig_keymap_loadhotkey

    GUI, VimDConfig_keymap:Add, GroupBox, x10 y435 w200 h61, 过滤 &F
    GUI, VimDConfig_keymap:Add, Edit, gsearch_keymap v_search x20 y460 w180 h25

    GUI, VimDConfig_keymap:Add, GroupBox, x225 y10 w650 h486, 映射 &K
    GUI, VimDConfig_keymap:Add, Listview, glistview x235 y36 w630 h450 grid, 热键|动作（双击定位，右键双击编辑）|描述

    LV_ModifyCol(1, "left 100")
    LV_ModifyCol(2, "left 250")
    LV_ModifyCol(3, "left 400")

    VimDConfig_keymap_loadwinlist()
    VimDConfig_keymap_loadhotkey(VimDConfig_keymap_loadmodelist(thiswin))

    GUI, VimDConfig_keymap:show
    ControlFocus, Edit1, A
    return
}

<VimDConfig_EditConfig>:
    Run, %A_ScriptDir%\vimd.ini
return

VimDConfig_keymap_exit:
{
    GUI, VimDConfig_keymap:Destroy
    return
}

VimDConfig_keymap_loadwinlist()
{
    global vim
    list := "|全局"
    GUI, VimDConfig_keymap:Default
    for win, obj in vim.WinList
    {
        if vim.ExcludeWinList[win]
        {
            continue
        }

        ; Convert class name TTOTAL_CMD to plugin name TotalCommander
        if (win = "TTOTAL_CMD")
        {
            win := "TotalCommander"
        }
        list .= "|" win
    }
    GuiControl, , ListBox1, %list%
}

VimDConfig_keymap_loadmodelist:
    VimDConfig_keymap_loadmodelist()
    GUI, VimDConfig_keymap:Default
    LV_delete()
    GuiControl, Choose, ListBox2, |normal
return

VimDConfig_keymap_loadmodelist(win = "")
{
    global vim
    GUI, VimDConfig_keymap:Default
    If not strlen(win)
        ControlGet, win, Choice, , ListBox1
    If win = 全局
        winObj := vim.GetWin()
    Else
        winObj := vim.GetWin(win)
    for mode, obj in winObj.modeList
        mlist .= "|" mode
    GuiControl, , ListBox2, %mlist%
    return win
}

VimDConfig_keymap_loadhotkey:
    GUI, VimDConfig_keymap:Default
    ControlGet, win, Choice, , ListBox1
    win := RegExMatch(win, "^全局$") ? "" : win

    ; Convert plugin name TotalCommander to class name TTOTAL_CMD
    if (win = "TotalCommander")
    {
        win := "TTOTAL_CMD"
    }

    ControlGet, mode, Choice, , ListBox2
    VimDConfig_keymap_loadhotkey(win, mode)
return

VimDConfig_keymap_loadhotkey(win, mode = "")
{
    global vim
    global current_keymap := ""
    If strlen(mode)
    {
        winObj  := vim.GetWin(win)
        modeobj := winObj.modeList[mode]
    }
    Else
        modeobj := vim.GetMode(win)
    GUI, VimDConfig_keymap:Default
    LV_delete()
    for key, i in modeobj.keymapList
    {
        ; Type = 1 : Function
        if (vim.GetAction(i).Type = 1)
        {
            ActionDescList := vim.GetAction(i).Comment
            actionDesc := StrSplit(%ActionDescList%[key], "|")
            LV_ADD("", Key, actionDesc[1], actionDesc[2])
            current_keymap .= Key "`t" actionDesc[1] "`t" actionDesc[2] "`n"
        }
        else
        {
            LV_Add("", Key, i, vim.GetAction(i).Comment)
            current_keymap .= Key "`t" i "`t" vim.GetAction(i).Comment "`n"
        }
    }
}

listview:
    if A_GuiEvent = DoubleClick
    {
        ;~ ToolTip You double-clicked row number %A_EventInfo%.
        LV_GetText(SelectedAction, A_EventInfo, 2)
        LV_GetText(SelectedDesc, A_EventInfo, 3)
        SearchFileForEdit(SelectedAction, SelectedDesc, false)
    }
    else if A_GuiEvent = R
    {
        LV_GetText(SelectedAction, A_EventInfo, 2)
        LV_GetText(SelectedDesc, A_EventInfo, 3)
        SearchFileForEdit(SelectedAction, SelectedDesc, true)
    }

return

SearchFileForEdit(Action, Desc, EditKeyMapping)
{
    IsUserCmd := RegExMatch(Action, "^(run|key|dir|tccmd|wshkey)$")
    if (IsUserCmd || EditKeyMapping)
    {
        SearchLine := "=" Action
        if (IsUserCmd || Action == "function")
        {
            SearchLine := Action "|" Desc
        }

        Loop, Read, %A_ScriptDir%\vimd.ini
        {
            if (InStr(A_LoopReadLine, SearchLine))
            {
                EditFile(A_ScriptDir "\vimd.ini", A_Index)
                return
            }
        }

        EditFile(A_ScriptDir "\vimd.ini")
        return
    }

    label := Action ":"
    if (Action == "function")
    {
        label := Desc
    }

    Loop, %A_ScriptDir%\plugins\*.ahk, , 1
    {
        Loop, Read, %A_LoopFileFullPath%
        {
            if (InStr(A_LoopReadLine, label) == 1)
            {
                EditFile(A_LoopFileFullPath, A_Index)
                return
            }
        }
    }

    Loop, %A_ScriptDir%\core\*.ahk, , 1
    {
        Loop, Read, %A_LoopFileFullPath%
        {
            if (InStr(A_LoopReadLine, label) = 1)
            {
                EditFile(A_LoopFileFullPath, A_Index)
                return
            }
        }
    }
}

EditFile(editPath, line := 1)
{
    editorArgs := {}
    editorArgs["notepad"] := "/g $line $file"
    editorArgs["notepad2"] := "/g $line $file"
    editorArgs["sublime_text"] := "$file:$line"
    editorArgs["vim"] := "+$line $file"
    editorArgs["gvim"] := "--remote-silent-tab +$line $file"
    editorArgs["everedit"] := "-n$line $file"
    editorArgs["notepad++"] := "-n$line $file"
    editorArgs["EmEditor"] := "-l $line $file"
    editorArgs["uedit32"] := "$file/$line"
    editorArgs["Editplus"] := "$file -cursor $line"
    editorArgs["textpad"] := "$file($line)"
    editorArgs["pspad"] := "$file /$line"
    editorArgs["ConTEXT"] := "$file /g1:$line"
    editorArgs["scite"] := "$file -goto:$line"

    Global editor

    If not FileExist(editor)
    {
        MsgBox, 请配置 vimd.ini 中 [config] 中的 editor ，并重启 vimd ！
        return
    }

    ; 根据编辑器 exe 名称获取打开参数
    SplitPath, editor, , , OutExtension, OutNameNoExt
    args := editorArgs[OutNameNoExt]
    StringReplace, args, args, $line, %line%
    StringReplace, args, args, $file, "%editPath%"
    target := editor " " args
    if (OutExtension = "sh")
    {
        target := "sh.exe " target
    }

    run, %target%
}

search_keymap:
    global current_keymap
    search_to_display(current_keymap)
return

search_plugin:
    global current_plugin
    search_to_display(current_plugin)
return

search_to_display(lines)
{
    Gui Submit, nohide
    GuiControlGet, OutputVar, , _search

    text := StrSplit(lines, "`n")

    LV_Delete() ; 清理不掉，第二次加载后，都成了重复的了，不知道怎么处理
    GuiControl, -Redraw, listview ; 重新启用重绘 (上面把它禁用了)
    for k, v in text
    {
        if v =
            continue
        if Instr(v, OutputVar)
        {
            list := StrSplit(v, "`t")
            LV_Add("", list[1], list[2], list[3])
        }
    }
    GuiControl, +Redraw, listview ; 重新启用重绘 (上面把它禁用了)
}


MGenServer: Do not show message when unable to close MGenServer, just rerun check again

SetTitleMatchMode, RegEx
; Maximum time to allow status.txt not being updated before restarting MGenServer
status_timeout = 30

Log(fname, st) {
  TimeString = %A_YYYY%-%A_MM%-%A_DD% %A_Hour%:%A_Min%:%A_Sec%
  FileAppend, %TimeString% %st%`n, %fname%
}

FileOverWrite(fname, st) {
  TimeString = %A_YYYY%-%A_MM%-%A_DD% %A_Hour%:%A_Min%:%A_Sec%
  file := FileOpen(fname, "w")
  if IsObject(file) {
    file.Write(TimeString . " " . st . "`n")
    file.Close()
  }
}

ProcessExists(Name){
	Process,Exist,%Name%
	return Errorlevel
}

RestartServer() {
  Process, Close, MGenServer.exe
  Process, WaitClose, MGenServer.exe, 20
  if ErrorLevel
    return
  Run, MGenServer.exe
}

Loop {
  Sleep, 500

  If !ProcessExists("MGenServer.exe")
  {
    Sleep, 5000
    If !ProcessExists("MGenServer.exe")
    {
      RestartServer()
    }
  }

  IfExist, server\status.txt
  {
    FileGetTime, myTime, server\status.txt
    var1 = %A_Now%
    EnvSub, var1, %myTime%, Seconds
    If var1 > %status_timeout%
    {
      Sleep, 5000
      FileGetTime, myTime, server\status.txt
      var1 = %A_Now%
      EnvSub, var1, %myTime%, Seconds
      If var1 > %status_timeout%
      {
        RestartServer()
      }
    }
  }
  else {
    Sleep, 5000
    IfNotExist, server\status.txt
    {
      RestartServer()
    }
  }

  IfWinExist, Rendering to file\.\.\.
  {
    ControlGetText, Progress, Estimated Remaining, Rendering to file\.\.\.
    FileOverwrite("C:\ReaperBuf\progress.txt", Progress)
    Continue
  }

  IfWinExist, ODBC Data Source Configuration
  {
    WinGetTitle, Title
    ControlClick, OK, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
  }

  IfWinExist, MIDI File Import
  {
    WinGetTitle, Title
    ControlClick, OK, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
  }

  IfWinExist, Render Warning
  {
    WinGetTitle, Title
    ControlClick, Overwrite, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
    Continue
  }

  IfWinExist, Finished in 0
  {
    WinGetTitle, Title
    ControlClick, Close, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
    Continue
  }

  IfWinExist, Render to File
  {
    WinGetTitle, Title
    ControlClick, Button16, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
  }

}

SetTitleMatchMode, RegEx
; Maximum time to allow status.txt not being updated before restarting MGenServer
status_timeout = 30

Log(fname, st) {
  TimeString = %A_YYYY%-%A_MM%-%A_DD% %A_Hour%:%A_Min%:%A_Sec%
  FileAppend, %TimeString% %st%`n, %fname%
}

FileOverWrite(fname, st) {
  TimeString = %A_YYYY%-%A_MM%-%A_DD% %A_Hour%:%A_Min%:%A_Sec%
  file := FileOpen(fname, "w")
  if IsObject(file) {
    file.Write(TimeString . " " . st . "`n")
    file.Close()
  }
}

ProcessExists(Name){
	Process,Exist,%Name%
	return Errorlevel
}

RestartServer() {
  Process, Close, MGenServer.exe
  Process, WaitClose, MGenServer.exe, 20
  if ErrorLevel ; The PID still exists.
    MsgBox The process MGenServer did not close within 20 seconds.
  Run, MGenServer.exe
}

Loop {
  Sleep, 500

  If !ProcessExists("MGenServer.exe")
  {
    Sleep, 5000
    If !ProcessExists("MGenServer.exe")
    {
      RestartServer()
    }
  }

  IfExist, server\status.txt
  {
    FileGetTime, myTime, server\status.txt
    var1 = %A_Now%
    EnvSub, var1, %myTime%, Seconds
    If var1 > %status_timeout%
    {
      Sleep, 5000
      FileGetTime, myTime, server\status.txt
      var1 = %A_Now%
      EnvSub, var1, %myTime%, Seconds
      If var1 > %status_timeout%
      {
        RestartServer()
      }
    }
  }
  else {
    Sleep, 5000
    IfNotExist, server\status.txt
    {
      RestartServer()
    }
  }

  IfWinExist, Rendering to file\.\.\.
  {
    ControlGetText, Progress, Estimated Remaining, Rendering to file\.\.\.
    FileOverwrite("C:\ReaperBuf\progress.txt", Progress)
    Continue
  }

  IfWinExist, ODBC Data Source Configuration
  {
    WinGetTitle, Title
    ControlClick, OK, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
  }

  IfWinExist, MIDI File Import
  {
    WinGetTitle, Title
    ControlClick, OK, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
  }

  IfWinExist, Render Warning
  {
    WinGetTitle, Title
    ControlClick, Overwrite, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
    Continue
  }

  IfWinExist, Finished in 0
  {
    WinGetTitle, Title
    ControlClick, Close, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
    Continue
  }

  IfWinExist, Render to File
  {
    WinGetTitle, Title
    ControlClick, Button16, %Title%,,,, NA
    Log("C:\ReaperBuf\windows.log", Title)
  }

}


DC 插件：新增 <DC_PasteFileContent>，将剪切板内容粘贴到文件中

DoubleCommander:
    global DC_Name := "DoubleCommander"
    global DC_Class := "TTOTAL_CMD"
    global DC := "ahk_class " . DC_Class
    global DC_Dir := "c:\mine\app\doublecmd"
    global DC_Path := DC_Dir . "\doublecmd.exe --no-splash"
    ; 用于记录文件打开对话框所属窗体
    global DC_CallerId := 0

    Vim.SetWin(DC_Name, DC_Class, "doublecmd.exe")
    Vim.Mode("normal", DC_Name)
    Vim.BeforeActionDo("DC_ForceInsertMode", DC_Name)
return

DC_ForceInsertMode() {
    ControlGetFocus, Ctrl
    ; Edit 用于底部命令行
    ; Windows1 或 Windows3 用于磁盘列表
    ; Button 用于各种确认窗口，影响正常使用
    if (InStr(Ctrl, "Edit") || Ctrl == "Window1" || Ctrl == "Window3")  {
        return true
    }

    WinGet, MenuID, ID, AHK_CLASS #32768
    if (MenuID != "") {
        return true
    }

    return false
}

DC_Run(Cmd) {
    ControlSetText, Edit1, % Cmd, % DC
    ControlSend, Edit1, {Enter}, % DC
}

DC_RunGet(Cmd, SaveClipboard := True) {
    if (SaveClipboard) {
        ClipSaved := ClipboardAll
    }

    Clipboard := ""

    DC_Run(Cmd)

    ClipWait, 1

    if (SaveClipboard) {
        Result := Clipboard

        Clipboard := ClipSaved
        ClipSaved := ""

        return Result
    }

    return Clipboard
}

; 返回值 [1]: left/right [2]: 左侧面板所占比例 0-100
DC_GetPanelInfo() {
    return StrSplit(DC_RunGet("cm_CopyPanelInfoToClip"), " ")
}

DC_ExecuteToolbarItem(ID) {
    DC_Run("cm_ExecuteToolbarItem ToolItemID=" . ID)
}

DC_OpenPath(Path, InNewTab := true, LeftOrRight := "") {
    LeftOfRight := DC_GetPanelInfo()[1]
    if (LeftOfRight == "right") {
        LeftOrRight := "-R"
    } else {
        LeftOrRight := "-L"
    }

    if (InNewTab) {
        Run, %DC_Path% -C -T "%LeftOrRight%" "%Path%"
    } else {
        Run, %DC_Path% -C "%LeftOrRight%" "%Path%"
    }
}

; funcend

<DC_Test>:
    ; DC_ExecuteToolbarItem("{700FF494-B939-48A3-B248-8823EB366AEA}")
    DC_Run("cm_About")
return

<DC_Restart>:
    WinClose, % DC
    WinWaitClose, % DC, , 2

    Run, % DC_Path

    WinWaitActive, % DC

    if (!WinExist(DC)) {
        MsgBox, 重启失败 %ErrorMessage%
    }
return

<DC_Toggle_50_100>:
    PanelInfo := DC_GetPanelInfo()

    if (Abs(50 - PanelInfo[2]) < 10) {
        if (PanelInfo[1] == "left") {
            DC_Run("cm_PanelsSplitterPerPos splitpct=100")
        } else {
            DC_Run("cm_PanelsSplitterPerPos splitpct=0")
        }
    } else {
        DC_Run("cm_PanelsSplitterPerPos splitpct=50")
    }
return

<DC_PrevParallelDir>:
    DC_Run("cm_ExecuteToolbarItem ToolItemID={D682D989-E31B-4774-A95F-FCAAC0723803}")

    Sleep, 50
return

<DC_NextParallelDir>:
    DC_Run("cm_ExecuteToolbarItem ToolItemID={5CFC019B-CF36-4029-B3F6-3F1AAE3FE462}")

    Sleep, 50
return

<DC_CreateNewFile>:
    ControlGetFocus, TLB, % DC
    ControlGetPos, Xn, Yn, , , % TLB, % DC

    Menu, NewFileMenu, Add
    Menu, NewFileMenu, DeleteAll
    Menu, NewFileMenu, Add , S >> 快捷方式, <DC_CreateFileShortcut>
    Menu, NewFileMenu, Icon, S >> 快捷方式, %A_WinDir%\system32\Shell32.dll, 264
    Menu, NewFileMenu, Add

    Loop, % DC_Dir . "\shellnew\*.*" {
        Ft := SubStr(A_LoopFileName, 1, 1) . " >> " . A_LoopFileName
        Menu, NewFileMenu, Add, % Ft, DC_NewFileMenuAction
        Menu, NewFileMenu, Icon, % Ft, %A_WinDir%\system32\Shell32.dll
    }

    Menu, NewFileMenu, Show, % Xn, % Yn + 2
return

DC_NewFileMenuAction:
    Filename := RegExReplace(A_ThisMenuItem, ".\s>>\s")
    FilePath := DC_Dir . "\ShellNew\" . Filename

    Gui, Destroy
    Gui, Add, Text, x12 y20 w50 h20 +Center, 模板源
    Gui, Add, Edit, x72 y20 w300 h20 Disabled, % FilePath
    Gui, Add, Text, x12 y50 w50 h20 +Center, 新建文件
    Gui, Add, Edit, x72 y50 w300 h20, % Filename
    Gui, Add, Button, x162 y80 w90 h30 gDC_NewFileOk default, 确认(&S)
    Gui, Add, Button, x282 y80 w90 h30 gDC_NewFileClose , 取消(&C)
    Gui, Show, w400 h120, 新建文件

    if (InStr(Filename, ".")) {
        ; 只选定扩展名之外的文件名
        PostMessage, 0x0B1, 0, % InStr(Filename, ".") - 1, Edit2, A
    }
return

DC_NewFileClose:
    Gui, Destroy
return

DC_NewFileOK:
    GuiControlGet, SrcFilePath, , Edit1
    GuiControlGet, NewFilename, , Edit2

    DstPath := DC_RunGet("cm_CopyCurrentPathToClip")

    if (InStr(DstPath, "`r")) {
        DstPath := SubStr(DstPath, 1, InStr(DstPath, "`r") - 1)
    } else if (DstPath == "") {
        return
    }

    NewFilePath := DstPath . NewFilename
    if (FileExist(NewFilePath)) {
        MsgBox, 4, 新建文件, 新建文件已存在，是否覆盖？
        IfMsgBox No
            return
    }

    FileCopy, % SrcFilePath, % NewFilePath, 1

    ; 虽然不是完全匹配，基本也能用了
    DC_Run("cm_QuickSearch text=" . NewFilename)

    Gui, Destroy
return

<DC_Toggle>:
    if (WinExist(DC)) {
        WinGet, Ac, MinMax, % DC
        if (Ac == -1) {
            WinActivate, % DC
        } else {
            if (!WinActive(DC)) {
                WinActivate, % DC
            } else {
                WinMinimize, % DC
            }
        }
    } else {
        Run, % DC_Path
        WinWait, % DC

        if (!WinActive(DC)) {
            WinActivate, % DC
        }
    }
return

<DC_MarkFile>:
    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")
    Result := """" . StrReplace(SelectedFiles, "`r`n", """ 🖥`r`n""") . """ 🖥`r`n"

    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileAppend, % Result, % DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion", UTF-8-RAW

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Result := ""
return

<DC_UnMarkFile>:
    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")
    DescriptPath := DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion"

    ; 有时取消选定会失效，改 20 也一样，不清楚怎么修复
    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileRead, Content, % DescriptPath

    Loop, Parse, SelectedFiles, `n, `r
    {
        Content := StrReplace(Content, """" . A_LoopField . """ 🖥`r`n")
    }

    FileDelete, % DescriptPath

    if (Content != "") {
        FileAppend, % Content, % DescriptPath, UTF-8-RAW
    }

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Content := ""
return

<DC_AddComment>:
    InputBox, Content, , 请输入注释, , 375, 125
    if ErrorLevel
        return

    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")

    Result := """" . StrReplace(SelectedFiles, "`r`n", """ " . Content . "`r`n""") . """ " . Content . "`r`n"

    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileAppend, % Result, % DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion", UTF-8-RAW

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Result := ""
return

<DC_RemoveComment>:
    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")
    DescriptPath := DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion"

    ; 有时取消选定会失效，改 20 也一样，不清楚怎么修复
    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileRead, Content, % DescriptPath

    Loop, Parse, SelectedFiles, `n, `r
    {
        Content := RegexReplace(Content, "m)^""?" . A_LoopField . """? .*")
    }

    ; \K：前边的不算，重新开始匹配
    Content := RegexReplace(Content, "(^|\R)\K\R+")

    FileDelete, % DescriptPath

    if (Content != "") {
        FileAppend, % Content, % DescriptPath, UTF-8-RAW
    }

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Content := ""
return

<DC_EditComment>:
    Run, % editor . " """ . DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion"""
return

<DC_ToggleShowInfo>:
    Vim.GetWin(DC_Name).SetInfo(!Vim.GetWin(DC_Name).info)
return

<DC_CopyFileContent>:
    FileRead, Clipboard, % DC_RunGet("cm_CopyFullNamesToClip", false)
return

<DC_PasteFileContent>:
    Filename := DC_RunGet("cm_CopyFullNamesToClip")
    FileAppend, % Clipboard, % Filename, UTF-8-RAW
return

<DC_CopyFilenamesOnly>:
    Result := DC_RunGet("cm_CopyFullNamesToClip")

    SplitPath, Result, OutFileName, , , OutFilenameNoExt

    if (InStr(FileExist(Result), "D")) {
        Clipboard := OutFileName
    } else if (InStr(Result, ".")) {
        Clipboard := OutFilenameNoExt
    }
return

<DC_CreateFileShortcut>:
    FilePath := DC_RunGet("cm_CopyFullNamesToClip")

    OutVar := FileExist(FilePath)
    if (InStr(OutVar, "D")) {
        FileCreateShortcut, % FilePath, % FilePath . ".lnk", % RegExReplace(FilePath, "\\[^\\]*$")
    } else if (OutVar != "") {
        FileCreateShortcut, % FilePath, % RegExReplace(FilePath, "\.[^.]*$", ".lnk"), % RegExReplace(FilePath, "\\[^\\]*$")
    }
return

<DC_Focus>:
    IfWinExist, % DC
        Winactivate, % DC
    else {
        Run, % DC_Path
        WinWait, % DC
        IfWinNotActive, % DC
            WinActivate, % DC
    }
return

<DC_MapKeys>:
	Vim.Mode("normal", DC_Name)
    Vim.Map("<S-Enter>", "<DC_SelectedCurrentDir>", DC_Name)
    Vim.Map("<Esc>", "<DC_ReturnToCaller>", DC_Name)
return

<DC_UnMapKeys>:
	Vim.Mode("normal", DC_Name)
    Vim.Map("<S-Enter>", "<Default>", DC_Name)
    Vim.Map("<Esc>", "<Default>", DC_Name)
return

; 返回调用者
<DC_ReturnToCaller>:
    gosub <DC_UnMapKeys>

    WinActivate, ahk_id %DC_CallerId%

    DC_CallerId := 0
return

; 非 TC 窗口按下后激活 TC 窗口
; TC 窗口按下后复制当前选中文件返回原窗口后粘贴
<DC_OpenDCDialog>:
    WinGetClass, Name, A

    ; 在 DC 按下快捷键时，激活调用窗体并执行粘贴操作
    if (Name == DC_Class) {
        if (DC_CallerId != 0) {
            gosub <DC_Selected>
            return
		}
    } else {
        DC_CallerId := WinExist("A")
        if (DC_CallerId == 0) {
            return
		}

        gosub <DC_Focus>
        gosub <DC_MapKeys>
    }
return

<DC_Selected>:
    gosub <DC_UnMapKeys>

    if (DC_CallerId == 0) {
        return
    }

    ; 避免发送回车时受同时按下的 Win 等键影响
    ; MsgBox, , , 处理中, 0.3
    ; 通过修改 DC 代码解决

    Pwd := DC_RunGet("cm_CopyCurrentPathToClip", false)

    Filename := DC_RunGet("cm_CopyNamesToClip", false)

    WinActivate, ahk_id %DC_CallerId%
    WinWait, ahk_id %DC_CallerId%
    DC_CallerId := 0

    if (!InStr(Filename, "`n")) {
        Clipboard := Pwd . Filename
        Send, {Home}
        Send, ^v
        Send, {Enter}

        return
    }

    ; 多选

    Files := ""
    Loop, Parse, FIlename, `n, `r
        Files .= """" . A_LoopField  . """ "

    ; 第一步：跳转到当前路径
    Clipboard := Pwd
    Send, ^a
    Send, ^v
    Send, {Enter}
    sleep, 100

    ; 第二步：提交文件名
    Clipboard := Files
    Send, ^v
    Send, {Enter}
return

<DC_SelectedCurrentDir>:
    gosub <DC_UnMapKeys>

    if (DC_CallerId == 0) {
        return
    }

    DC_Run("cm_CopyCurrentPathToClip")

    WinActivate, ahk_id %DC_CallerId%
    WinWait, ahk_id %DC_CallerId%
    DC_CallerId := 0

    Send, {Home}
    Send, ^v
    Send, {Enter}
return

DoubleCommander:
    global DC_Name := "DoubleCommander"
    global DC_Class := "TTOTAL_CMD"
    global DC := "ahk_class " . DC_Class
    global DC_Dir := "c:\mine\app\doublecmd"
    global DC_Path := DC_Dir . "\doublecmd.exe --no-splash"
    ; 用于记录文件打开对话框所属窗体
    global DC_CallerId := 0

    Vim.SetWin(DC_Name, DC_Class, "doublecmd.exe")
    Vim.Mode("normal", DC_Name)
    Vim.BeforeActionDo("DC_ForceInsertMode", DC_Name)
return

DC_ForceInsertMode() {
    ControlGetFocus, Ctrl
    ; Edit 用于底部命令行
    ; Windows1 或 Windows3 用于磁盘列表
    ; Button 用于各种确认窗口，影响正常使用
    if (InStr(Ctrl, "Edit") || Ctrl == "Window1" || Ctrl == "Window3")  {
        return true
    }

    WinGet, MenuID, ID, AHK_CLASS #32768
    if (MenuID != "") {
        return true
    }

    return false
}

DC_Run(Cmd) {
    ControlSetText, Edit1, % Cmd, % DC
    ControlSend, Edit1, {Enter}, % DC
}

DC_RunGet(Cmd, SaveClipboard := True) {
    if (SaveClipboard) {
        ClipSaved := ClipboardAll
    }

    Clipboard := ""

    DC_Run(Cmd)

    ClipWait, 1

    if (SaveClipboard) {
        Result := Clipboard

        Clipboard := ClipSaved
        ClipSaved := ""

        return Result
    }

    return Clipboard
}

; 返回值 [1]: left/right [2]: 左侧面板所占比例 0-100
DC_GetPanelInfo() {
    return StrSplit(DC_RunGet("cm_CopyPanelInfoToClip"), " ")
}

DC_ExecuteToolbarItem(ID) {
    DC_Run("cm_ExecuteToolbarItem ToolItemID=" . ID)
}

DC_OpenPath(Path, InNewTab := true, LeftOrRight := "") {
    LeftOfRight := DC_GetPanelInfo()[1]
    if (LeftOfRight == "right") {
        LeftOrRight := "-R"
    } else {
        LeftOrRight := "-L"
    }

    if (InNewTab) {
        Run, %DC_Path% -C -T "%LeftOrRight%" "%Path%"
    } else {
        Run, %DC_Path% -C "%LeftOrRight%" "%Path%"
    }
}

; funcend

<DC_Test>:
    ; DC_ExecuteToolbarItem("{700FF494-B939-48A3-B248-8823EB366AEA}")
    DC_Run("cm_About")
return

<DC_Restart>:
    WinClose, % DC
    WinWaitClose, % DC, , 2

    Run, % DC_Path

    WinWaitActive, % DC

    if (!WinExist(DC)) {
        MsgBox, 重启失败 %ErrorMessage%
    }
return

<DC_Toggle_50_100>:
    PanelInfo := DC_GetPanelInfo()

    if (Abs(50 - PanelInfo[2]) < 10) {
        if (PanelInfo[1] == "left") {
            DC_Run("cm_PanelsSplitterPerPos splitpct=100")
        } else {
            DC_Run("cm_PanelsSplitterPerPos splitpct=0")
        }
    } else {
        DC_Run("cm_PanelsSplitterPerPos splitpct=50")
    }
return

<DC_PrevParallelDir>:
    DC_Run("cm_ExecuteToolbarItem ToolItemID={D682D989-E31B-4774-A95F-FCAAC0723803}")

    Sleep, 50
return

<DC_NextParallelDir>:
    DC_Run("cm_ExecuteToolbarItem ToolItemID={5CFC019B-CF36-4029-B3F6-3F1AAE3FE462}")

    Sleep, 50
return

<DC_CreateNewFile>:
    ControlGetFocus, TLB, % DC
    ControlGetPos, Xn, Yn, , , % TLB, % DC

    Menu, NewFileMenu, Add
    Menu, NewFileMenu, DeleteAll
    Menu, NewFileMenu, Add , S >> 快捷方式, <DC_CreateFileShortcut>
    Menu, NewFileMenu, Icon, S >> 快捷方式, %A_WinDir%\system32\Shell32.dll, 264
    Menu, NewFileMenu, Add

    Loop, % DC_Dir . "\shellnew\*.*" {
        Ft := SubStr(A_LoopFileName, 1, 1) . " >> " . A_LoopFileName
        Menu, NewFileMenu, Add, % Ft, DC_NewFileMenuAction
        Menu, NewFileMenu, Icon, % Ft, %A_WinDir%\system32\Shell32.dll
    }

    Menu, NewFileMenu, Show, % Xn, % Yn + 2
return

DC_NewFileMenuAction:
    Filename := RegExReplace(A_ThisMenuItem, ".\s>>\s")
    FilePath := DC_Dir . "\ShellNew\" . Filename

    Gui, Destroy
    Gui, Add, Text, x12 y20 w50 h20 +Center, 模板源
    Gui, Add, Edit, x72 y20 w300 h20 Disabled, % FilePath
    Gui, Add, Text, x12 y50 w50 h20 +Center, 新建文件
    Gui, Add, Edit, x72 y50 w300 h20, % Filename
    Gui, Add, Button, x162 y80 w90 h30 gDC_NewFileOk default, 确认(&S)
    Gui, Add, Button, x282 y80 w90 h30 gDC_NewFileClose , 取消(&C)
    Gui, Show, w400 h120, 新建文件

    if (InStr(Filename, ".")) {
        ; 只选定扩展名之外的文件名
        PostMessage, 0x0B1, 0, % InStr(Filename, ".") - 1, Edit2, A
    }
return

DC_NewFileClose:
    Gui, Destroy
return

DC_NewFileOK:
    GuiControlGet, SrcFilePath, , Edit1
    GuiControlGet, NewFilename, , Edit2

    DstPath := DC_RunGet("cm_CopyCurrentPathToClip")

    if (InStr(DstPath, "`r")) {
        DstPath := SubStr(DstPath, 1, InStr(DstPath, "`r") - 1)
    } else if (DstPath == "") {
        return
    }

    NewFilePath := DstPath . NewFilename
    if (FileExist(NewFilePath)) {
        MsgBox, 4, 新建文件, 新建文件已存在，是否覆盖？
        IfMsgBox No
            return
    }

    FileCopy, % SrcFilePath, % NewFilePath, 1

    ; 虽然不是完全匹配，基本也能用了
    DC_Run("cm_QuickSearch text=" . NewFilename)

    Gui, Destroy
return

<DC_Toggle>:
    if (WinExist(DC)) {
        WinGet, Ac, MinMax, % DC
        if (Ac == -1) {
            WinActivate, % DC
        } else {
            if (!WinActive(DC)) {
                WinActivate, % DC
            } else {
                WinMinimize, % DC
            }
        }
    } else {
        Run, % DC_Path
        WinWait, % DC

        if (!WinActive(DC)) {
            WinActivate, % DC
        }
    }
return

<DC_MarkFile>:
    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")
    Result := """" . StrReplace(SelectedFiles, "`r`n", """ 🖥`r`n""") . """ 🖥`r`n"

    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileAppend, % Result, % DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion", UTF-8-RAW

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Result := ""
return

<DC_UnMarkFile>:
    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")
    DescriptPath := DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion"

    ; 有时取消选定会失效，改 20 也一样，不清楚怎么修复
    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileRead, Content, % DescriptPath

    Loop, Parse, SelectedFiles, `n, `r
    {
        Content := StrReplace(Content, """" . A_LoopField . """ 🖥`r`n")
    }

    FileDelete, % DescriptPath

    if (Content != "") {
        FileAppend, % Content, % DescriptPath, UTF-8-RAW
    }

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Content := ""
return

<DC_AddComment>:
    InputBox, Content, , 请输入注释, , 375, 125
    if ErrorLevel
        return

    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")

    Result := """" . StrReplace(SelectedFiles, "`r`n", """ " . Content . "`r`n""") . """ " . Content . "`r`n"

    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileAppend, % Result, % DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion", UTF-8-RAW

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Result := ""
return

<DC_RemoveComment>:
    SelectedFiles := DC_RunGet("cm_CopyNamesToClip")
    DescriptPath := DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion"

    ; 有时取消选定会失效，改 20 也一样，不清楚怎么修复
    Sleep, 10
    DC_Run("cm_MarkUnmarkAll")

    FileRead, Content, % DescriptPath

    Loop, Parse, SelectedFiles, `n, `r
    {
        Content := RegexReplace(Content, "m)^""?" . A_LoopField . """? .*")
    }

    ; \K：前边的不算，重新开始匹配
    Content := RegexReplace(Content, "(^|\R)\K\R+")

    FileDelete, % DescriptPath

    if (Content != "") {
        FileAppend, % Content, % DescriptPath, UTF-8-RAW
    }

    DC_Run("cm_Refresh")

    SelectedFiles := ""
    Content := ""
return

<DC_EditComment>:
    Run, % editor . " """ . DC_RunGet("cm_CopyCurrentPathToClip") . "descript.ion"""
return

<DC_ToggleShowInfo>:
    Vim.GetWin(DC_Name).SetInfo(!Vim.GetWin(DC_Name).info)
return

<DC_CopyFileContent>:
    Fileread, Clipboard, % DC_RunGet("cm_CopyFullNamesToClip", false)
return

<DC_CopyFilenamesOnly>:
    Result := DC_RunGet("cm_CopyFullNamesToClip")

    SplitPath, Result, OutFileName, , , OutFilenameNoExt

    if (InStr(FileExist(Result), "D")) {
        Clipboard := OutFileName
    } else if (InStr(Result, ".")) {
        Clipboard := OutFilenameNoExt
    }
return

<DC_CreateFileShortcut>:
    FilePath := DC_RunGet("cm_CopyFullNamesToClip")

    OutVar := FileExist(FilePath)
    if (InStr(OutVar, "D")) {
        FileCreateShortcut, % FilePath, % FilePath . ".lnk", % RegExReplace(FilePath, "\\[^\\]*$")
    } else if (OutVar != "") {
        FileCreateShortcut, % FilePath, % RegExReplace(FilePath, "\.[^.]*$", ".lnk"), % RegExReplace(FilePath, "\\[^\\]*$")
    }
return

<DC_Focus>:
    IfWinExist, % DC
        Winactivate, % DC
    else {
        Run, % DC_Path
        WinWait, % DC
        IfWinNotActive, % DC
            WinActivate, % DC
    }
return

<DC_MapKeys>:
	Vim.Mode("normal", DC_Name)
    Vim.Map("<S-Enter>", "<DC_SelectedCurrentDir>", DC_Name)
    Vim.Map("<Esc>", "<DC_ReturnToCaller>", DC_Name)
return

<DC_UnMapKeys>:
	Vim.Mode("normal", DC_Name)
    Vim.Map("<S-Enter>", "<Default>", DC_Name)
    Vim.Map("<Esc>", "<Default>", DC_Name)
return

; 返回调用者
<DC_ReturnToCaller>:
    gosub <DC_UnMapKeys>

    WinActivate, ahk_id %DC_CallerId%

    DC_CallerId := 0
return

; 非 TC 窗口按下后激活 TC 窗口
; TC 窗口按下后复制当前选中文件返回原窗口后粘贴
<DC_OpenDCDialog>:
    WinGetClass, Name, A

    ; 在 DC 按下快捷键时，激活调用窗体并执行粘贴操作
    if (Name == DC_Class) {
        if (DC_CallerId != 0) {
            gosub <DC_Selected>
            return
		}
    } else {
        DC_CallerId := WinExist("A")
        if (DC_CallerId == 0) {
            return
		}

        gosub <DC_Focus>
        gosub <DC_MapKeys>
    }
return

<DC_Selected>:
    gosub <DC_UnMapKeys>

    if (DC_CallerId == 0) {
        return
    }

    ; 避免发送回车时受同时按下的 Win 等键影响
    ; MsgBox, , , 处理中, 0.3
    ; 通过修改 DC 代码解决

    Pwd := DC_RunGet("cm_CopyCurrentPathToClip", false)

    Filename := DC_RunGet("cm_CopyNamesToClip", false)

    WinActivate, ahk_id %DC_CallerId%
    WinWait, ahk_id %DC_CallerId%
    DC_CallerId := 0

    if (!InStr(Filename, "`n")) {
        Clipboard := Pwd . Filename
        Send, {Home}
        Send, ^v
        Send, {Enter}

        return
    }

    ; 多选

    Files := ""
    Loop, Parse, FIlename, `n, `r
        Files .= """" . A_LoopField  . """ "

    ; 第一步：跳转到当前路径
    Clipboard := Pwd
    Send, ^a
    Send, ^v
    Send, {Enter}
    sleep, 100

    ; 第二步：提交文件名
    Clipboard := Files
    Send, ^v
    Send, {Enter}
return

<DC_SelectedCurrentDir>:
    gosub <DC_UnMapKeys>

    if (DC_CallerId == 0) {
        return
    }

    DC_Run("cm_CopyCurrentPathToClip")

    WinActivate, ahk_id %DC_CallerId%
    WinWait, ahk_id %DC_CallerId%
    DC_CallerId := 0

    Send, {Home}
    Send, ^v
    Send, {Enter}
return