I find that I want to save macros for future use. I name my macros with the extension ".ahm". It would be nice if your program had an open/save/saveas feature for creating/using/editing macros. The user could name therm whatever they wanted. The user can associate the .ahm with the AutoHotkey exe so the macro can run on double click.
Another nice feature is to add an edit box so the user can input the number of loops to execute instead of needing to interrupt an infinite loop.
You may want to consider forking your design to keep a much simpler macro-maker for those that do not need the complexity that goes along with dealing with images. An image-capable version could be the second version. Simplicity, for most people, will minimize the number of 'second order' problems that might cause people to stop using the tool.
Here is your V3.0 with my embellishments. BTW, I tend to want to convert your Goto statements to Gosub and indent with tabs instead of spaces. Just preference.
Code: Select all
/*
-----------------------------------
Macro Record v3.0 By FeiYue
Description:
1. This script records the mouse and keyboard
actions and then plays back.
2. Use the mouse to shake on the left edge of the screen
to show or hide the GUI window, It reduces the use of hotkeys.
you can change and insert code during the recording process,
When the GUI window is displayed, the recording is automatically paused,
and even minimize GUI window, it is paused.
When the Gui window is hidden,the recording will continue automatically.
3. If you want to stop playback process,
please press the Pause hotkey first,
and then click the Stop button in the GUI.
4. You can press the Ctrl key individually to display a menu,
Press the Ctrl key again to select the items in the menu.
You can switch the relative coordinate mode, add search
the picture code, mouse movement code, delay code,
color matching code, and cursor matching code.
5. If you want to record the mouse action in ControlClick mode,
You can let the CtrlClick CheckBox be selected.
Menu, etc. window non client area cannot use ControlClick,
Even in the window client area, some places are not good.
Sometimes you need to change the number of Clicks to 2,
for the first time target control gets the focus, but
watch for any side effects. (Beginners are not recommended)
6. If you need to find and batch replacement,
you can click the Edit button, Edit by Notepad, closed Notepad,
the new content will automatically update to the GUI edit box.
Pause Button --> Pause Record/Play (Better to use hotkey)
Record Button --> Record Mouse/Keyboard/Window
Stop Button --> Stop Record/Play (Save To LogFile)
Play Button --> Play LogFile
Edit Button --> Edit LogFile
Coord Win CheckBox --> Coordinate relative to active window
Add Titile CheckBox --> Additional title when recording window
CtrlClick CheckBox --> Additional ControlClick code
Loop Play CheckBox --> Loop playback
Pause Hotkey --> Pause Record/Play
-----------------------------------
*/
#NoEnv
#SingleInstance force
SetBatchLines, -1
CoordMode, Mouse
CoordMode, Pixel
CoordMode, ToolTip
;LogFile = %A_Temp%\~Record.txt
LogFile = %A_ScriptDir%\~Record.ahm
;----------------------------
Gui, +AlwaysOnTop +Resize +Hwndgui_id
Gui, Color, DDEEFF
Gui, Margin, 15, 15
Gui, Font, s14, Verdana
s=Open,SaveAs,Pause,Record,Stop,Play,Edit,Clear,Exit
For i,v in StrSplit(s, ",")
Gui, Add, Button, w125 h40 gRun, %v%
Gui, Add, CheckBox, wp h30 vCoordWin Checked, Coord Win
Gui, Add, CheckBox, wp hp vAddTitle, Add Title
Gui, Add, CheckBox, wp hp vCtrlClick, CtrlClick
Gui, Add, CheckBox, wp hp vLoopPlay, Loop Play
Gui, Add, Edit, yp+40 wp h30 vLoopCount, 1
Gui, Add, Text, yp+30 wp hp, % " times"
Gui, Add, Edit, % "ym w650 h" . (40*7+30*4+15*10)
. " -Wrap HScroll vMyEdit HwndMyEditHwnd"
Gui, Show, Hide, Macro Record v3.0
OnExit, SaveEditExit
Gosub, ReadEdit
SwitchGUI()
;----------------------------
Hotkey, *Pause, Pause
;----------------------------
return
SwitchGUI() {
Gui, Switch:+AlwaysOnTop -Caption +ToolWindow
+E0x08000000 ; WS_EX_NOACTIVATE = 0x08000000
Gui, Switch:Color, White
Gui, Switch:Show, NA x0 y0 w1 h%A_ScreenHeight%
OnMessage(0x200, "WM_MOUSEMOVE")
SetTimer, SwitchOnTop, 2000
return
SwitchOnTop:
ListLines, Off
Gui, Switch:+AlwaysOnTop
return
}
WM_MOUSEMOVE() {
ListLines, Off
static Time, OkTime
if (A_Gui="Switch") and (Time:=A_TickCount)>OkTime
{
OkTime:=Time+500
Pause, Off
SetTimer, GuiShow, -10
}
}
GuiSize:
if ErrorLevel=1
return
w:=A_GuiWidth-170, h:=A_GuiHeight-30
GuiControl, Move, MyEdit, w%w% h%h%
return
ReadEdit:
FileRead, s, %LogFile%
GuiControl,, MyEdit, %s%
s=
return
SaveEdit:
GuiControlGet, s,, MyEdit
FileDelete, %LogFile%
FileAppend, %s%, %LogFile%
s=
return
SaveEditExit:
Gosub, SaveEdit
ExitApp
Clear:
GuiControlGet, s,, MyEdit
s:=s="" ? olds : (olds:=s)/0
GuiControl,, MyEdit, %s%
GuiControl, Focus, MyEdit
return
Open:
Gui, -AlwaysOnTop
tmp := Dlg_FileSelect("", "*.ahm")
if (FileExist(tmp))
{
LogFile := tmp
Gosub ReadEdit
}
Gui, +AlwaysOnTop
Return
SaveAs:
Gui, -AlwaysOnTop
;FileSelectFile, tmp, S8, %LogFile%,, *.ahm
tmp := Dlg_FileSelect(LogFile, "*.ahm",, "S8")
if (tmp)
{
LogFile := tmp . (InStr(tmp, ".ahm") ? "" : ".ahm")
Gosub SaveEdit
}
Gui, +AlwaysOnTop
Return
Exit:
ExitApp
Run:
if IsLabel(s:=A_GuiControl)
;Goto, %s%
Gosub %s%
return
;========== Hotkey Label ==========
GuiShow:
Suspend, Permit
IfWinExist, ahk_id %gui_id%
{
;Goto, GuiClose
Gosub GuiClose
Return
}
Suspend, On
Gui, Show
GuiControl, Focus, MyEdit
if !Recording
return
Logs:=Trim(Logs, "`n")
IfEqual, Logs,, return
GuiControlGet, s,, MyEdit
; ____________________________
;__/____ set boiler plate ____\____
/*
This creates a code block as text in the variable 'r'.
The construct to concatenate the text uses a Join continuation
with the '%' option, i.e. considers the percent symbol as a literal
character in the text to be joined and NOT a variable reference.
*/
r := ""
if !InStr(s, "WinWait(title, text=")
r=%r%
(%
WinWait(title, text="")
{
CoordMode, ToolTip
ToolTip, Waiting... %title%, 80, 0
WinWaitActive, %title%, %text%, 3
if ErrorLevel
{
WinWait, %title%, %text%
WinActivate
Sleep, 500
}
ToolTip
}
)
if !InStr(s, "Click(coordwin, x, y, other=")
r=%r%
(%
Click(coordwin, x, y, other="")
{
if (r:=coordwin ? "Window":"Screen")!=A_CoordModeMouse
CoordMode, Mouse, %r%
Click, %x%, %y%, %other%
Sleep, InStr(other,"R") ? 500 : 100
}
)
if !InStr(s, "Send(key=")
r=%r%
(%
Send(key="")
{
IfEqual, key,, return
Send, {Blind}%key%
Sleep, 200
}
)
if !InStr(s, "GetColor(coordwin, x, y, other=")
r=%r%
(%
GetColor(coordwin, x, y, other="RGB")
{
if (r:=coordwin ? "Window":"Screen")!=A_CoordModePixel
CoordMode, Pixel, %r%
PixelGetColor, color, x, y, %other%
return, color
}
)
;__ _______________________________ ____
; \____ end of boiler plate ____/
Logs:=StrReplace(Logs, "+-", "-")
Logs:=StrReplace(r "`n" Logs "`n", "`n", "`r`n")
ControlSend,, {Home}, ahk_id %MyEditHwnd%
Control, EditPaste, %Logs%,, ahk_id %MyEditHwnd%
ControlSend,, {Left}{Right}, ahk_id %MyEditHwnd%
Logs:=s:=r:=""
return
GuiClose:
WinMinimize, ahk_id %gui_id%
Gui, Hide
Suspend, Off
GuiControlGet, AddTitle
GuiControlGet, CtrlClick
return
Record:
Suspend, Permit
GuiControl, Focus, MyEdit
if (Recording or Playing)
return
Gosub, GuiClose
GuiControl, Disable, Record
Recording:=1, IsPaused:=0, SetHotkey(1)
Logs:=oldtt:=""
GuiControlGet, CoordWin
ToolTip, % " Recording ", 0, 0
return
Stop:
Suspend, Permit
GuiControl, Focus, MyEdit
if Recording
{
IfWinNotExist, ahk_id %gui_id%
Gosub, GuiShow
GuiControl, Enable, Record
SetHotkey(0), Recording:=0, IsPaused:=0
ToolTip
return
}
LoopPlay:=0
SetTitleMatchMode, 2
DetectHiddenWindows, On
SplitPath, LogFile, FileName
WinGet, list, List, %FileName% ahk_class AutoHotkey
Loop, %list%
{
id:=list%A_Index%
if (id=A_ScriptHwnd) or !WinExist("ahk_id " id)
Continue
WinGet, pid, PID
WinClose
WinWaitClose,,, 3
if ErrorLevel
Process, Close, %pid%
}
return
Play:
Suspend, Permit
GuiControl, Focus, MyEdit
if Playing
return
if Recording
Gosub, Stop
Gosub, SaveEdit
Gosub, GuiClose
Playing:=1
ToolTip, % " Playing ", 0, 0
ListLines, Off
GuiControlGet, LoopPlay
GuiControlGet, LoopCount
Loop % (tmp := LoopPlay ? LoopCount : 1)
{
RunWait, %A_AhkPath% "%LogFile%"
if !LoopPlay
Break
}
ListLines, On
ToolTip, % " DONE... Macro was run " . tmp . " times."
Sleep, 2500 * 2
Tooltip
Playing:=0
return
Edit:
Suspend, Permit
GuiControl, Focus, MyEdit
Gui, -AlwaysOnTop
Gosub, SaveEdit
FileGetTime, time1, %LogFile%
RunWait, notepad++.exe "%LogFile%"
FileGetTime, time2, %LogFile%
if (time1!=time2)
Gosub, ReadEdit
Gui, +AlwaysOnTop
return
Pause:
Suspend, Permit
GuiControl, Focus, MyEdit
if Recording
{
IsPaused:=!IsPaused, SetHotkey(!IsPaused)
ToolTip, % IsPaused ? " Record Pause "
: " Recording ", 0, 0
return
}
SetTitleMatchMode, 2
DetectHiddenWindows, On
SplitPath, LogFile, FileName
WinGet, list, List, %FileName% ahk_class AutoHotkey
Loop, %list%
{
id:=list%A_Index%
if (id=A_ScriptHwnd) or !WinExist("ahk_id " id)
Continue
PostMessage, 0x111, 65306
}
return
;========== Function and Label ==========
SetHotkey(f=0)
{
; These keys are already used as hotkeys
static ExcludeKeys:="Pause"
ListLines, Off
f:=f ? "On":"Off"
For i,k in StrSplit("NumpadEnter|Home|End|PgUp"
. "|PgDn|Left|Right|Up|Down|Delete|Insert", "|")
{
k:=Format("sc{:X}", GetKeySC(k))
Hotkey, ~*%k%, LogKey, %f% UseErrorLevel
}
Loop, 254
{
k:=GetKeyName(vk:=Format("vk{:X}", A_Index))
if k not in ,Control,Alt,Shift,%ExcludeKeys%
Hotkey, ~*%vk%, LogKey, %f% UseErrorLevel
}
SetTimer, CheckWindow, % f="On" ? 100 : f
ListLines, On
}
LogKey:
Critical
k:=GetKeyName(vk:=SubStr(A_ThisHotkey,3))
k:=StrReplace(k,"Control","Ctrl"), r:=SubStr(k,2)
if r in Win,Alt,Ctrl,Shift,Button
if IsLabel(k)
{
;Goto, %k%
Gosub %k%
Return
}
; Some input auto completion and send the left or right
; key for the cursor center, excluding these key records
if (k="NumpadLeft" or k="NumpadRight")
and !GetKeyState(k,"P")
return
k:=StrLen(k)>1 ? "{" k "}" : k~="\w" ? k : "{" vk "}"
Log(k,1)
return
LCtrl: ; Individually press Ctrl to get multiple information
RCtrl:
if CoordWin=1
CoordMode, Mouse, Window
MouseGetPos, Ctrl_X, Ctrl_Y
CoordMode, Mouse, Screen
MouseGetPos, Ctrl_ScreenX, Ctrl_ScreenY
LWin:
RWin:
LAlt:
RAlt:
LShift:
RShift:
Log("{" . (InStr(k,"Win") ? k:r) . " Down}",1)
Critical, Off
KeyWait, %A_ThisLabel%
Critical
k:=A_ThisLabel, r:=SubStr(k,2), LastTime:=A_TickCount
Log("{" . (InStr(k,"Win") ? k:r) . " Up}",1)
;----------------------------
if InStr(k,"Ctrl")
and SubStr(Logs,1-22)="{Ctrl Down}{Ctrl Up}"")"
{
r:=SubStr(Logs, InStr(Logs,"`n",0,0)+1)
Logs:=r="Send(""{Ctrl Down}{Ctrl Up}"")"
? SubStr(Logs,1,-StrLen(r))
: SubStr(Logs,1,-22) . """)"
;Goto, CtrlMenu
Gosub CtrlMenu
}
return
CtrlMenu:
PixelGetColor, color, Ctrl_ScreenX, Ctrl_ScreenY, RGB
Suspend, On
Gui, CtrlMenu:+LastFoundExist
IfWinNotExist
{
Gui, CtrlMenu:New
Gui, +AlwaysOnTop -Caption +ToolWindow
+LastFound +Hwndctrl_id +E0x08000000
Gui, Margin, 0, 0
Gui, Color, DDEEFF
Gui, Font, s14, Verdana
s=Relative Screen,Relative Window
,Relative Origin,Add ImageSearch
,Add MouseMove,Add Sleep 1000
,Add if Color,Add if Cursor
For i,v in StrSplit(s, ",", " ")
Gui, Add, Button, w250 h40, %v%
Gui, Show, Hide, CtrlMenu
}
WinGetPos,,, W, H
MouseGetPos, X, Y
X:=X>A_ScreenWidth-W-5 ? X-W-2 : X+2
Y:=Y>A_ScreenHeight-H-5 ? Y-H-2 : Y+2
Gui, CtrlMenu:Show, NA x%X% y%Y%
KeyWait, Ctrl, D
MouseGetPos,,, id, class
Gui, CtrlMenu:Hide
KeyWait, Ctrl
Suspend, Off
;----------------------------
if (id!=ctrl_id) or !InStr(class,"Button")
return
X:=CoordWin=2 ? "X+" (Ctrl_ScreenX-originX) : Ctrl_X
Y:=CoordWin=2 ? "Y+" (Ctrl_ScreenY-originY) : Ctrl_Y
i:=SubStr(class,7)
if i=1
CoordWin=0
else if i=2
CoordWin=1
else if i=3
{
CoordWin:=2
originX:=Ctrl_ScreenX, originY:=Ctrl_ScreenY
Log(";-- Origin`nX:=" originX ", Y:=" originY)
}
else if i=4
Log(";-- Origin`nCoordMode, Pixel`nImageSearch, X, Y
, 0, 0, A_ScreenWidth, A_ScreenHeight, Pic")
else if i=5
Log(";-- `nClick(" (CoordWin=1) ", " X ", " Y ", 0)")
else if i=6
Log("Sleep, 1000")
else if i=7
Log("if (GetColor(" (CoordWin=1) ", " X
. ", " Y ")=""" color """)`n{`n`n}")
else if i=8
Log("if (A_Cursor=""" A_Cursor """)`n{`n`n}")
return
LButton:
RButton:
MButton:
if CoordWin=1
CoordMode, Mouse, Window
MouseGetPos, %k%_X, %k%_Y
CoordMode, Mouse, Screen
MouseGetPos, X, Y, id
%k%_ScreenX:=X, %k%_ScreenY:=Y
if (CtrlClick and WinExist("A")=id)
{
WinGetPos, WinX, WinY
i:=X-WinX, j:=Y-WinY, r:=SubStr(k,1,1)
s=; ControlClick, x%i% y%j%, `% "%oldtt%"
,, %r%, 1, NA`n
} else s=
Log(";-- `n" s "Click(" (CoordWin=1) ", "
. (CoordWin=2 ? "X+" (X-originX) : %k%_X) ", "
. (CoordWin=2 ? "Y+" (Y-originY) : %k%_Y)
. ", """ . SubStr(k,1,1) . " D"")")
%k%_T:=A_TickCount
SetTimer, CheckWindow, Off
Critical, Off
KeyWait, %A_ThisLabel%
Critical
SetTimer, CheckWindow, On
k:=A_ThisLabel
MouseGetPos, X, Y
if (A_TickCount-%k%_T)>200
i:=X-%k%_ScreenX, j:=Y-%k%_ScreenY
else i:=j:=0
r:=SubStr(Logs, InStr(Logs,"`n",0,0)+1)
if InStr(r,"Click")=1 and InStr(r," D"")")
and Abs(i)+Abs(j)<5
Logs:=SubStr(Logs,1,-4) . """)"
else
Log("Click(" (CoordWin=1) ", "
. (CoordWin=2 ? "X+" (X-originX) : %k%_X+i) ", "
. (CoordWin=2 ? "Y+" (Y-originY) : %k%_Y+j)
. ", """ . SubStr(k,1,1) . " U"")")
return
CheckWindow:
ListLines, Off
if A_IsSuspended
return
Critical
WinGetTitle, tt, A
WinGetClass, tc, A
if (tt="" and tc="") or (tc="" and !AddTitle)
return
tt:=(AddTitle ? SubStr(tt,1,50):"")
. (tc ? " ahk_class " tc:"")
tt:=RegExReplace(Trim(tt), "[;``]", "``$0")
tt:=StrReplace(tt,"""","""""")
if (tt=oldtt)
return
oldtt:=tt
r:=SubStr(Logs, InStr(Logs,"`n",0,0)+1)
if InStr(r,"WinWait")=1
Logs:=SubStr(Logs,1,-StrLen(r))
Log("WinWait(""" . tt . """)")
return
Log(str, Keyboard=0)
{
global Logs, LastTime
Delay:=(Time:=A_TickCount)-LastTime, LastTime:=Time
r:=SubStr(Logs, InStr(Logs,"`n",0,0)+1)
if (Keyboard and InStr(r,"Send")=1 and Delay<1000)
{
Logs:=SubStr(Logs,1,-2) . str """)"
return
}
Logs:=RTrim(Logs,"`n") . "`n`n"
. (Keyboard ? "Send(""" str """)" : str)
}
;============ The End =============
;