An Alternative Menu (A Quick Access To Various Features)

Post your working scripts, libraries and tools for AHK v1.1 and older
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

Post by SundayProgrammer » 14 Dec 2021, 15:39

what's new in this version:

(1) added a few dozens more shapes for extra spots.

(2) and don't really want to scroll through them with the listbox, hence rewritten the user interface for picking a shape. and take the chance to make it more wysiwyg. also added a color palette as part of the new interface.

(2a) shapes list and color palette are arranged in two windows.

(2b) if the color shown is not what you want, either click on the color palette to get the one you want, or type it in (or choose one from) the combobox. if you make it by combobox instead of color palette, you need to hit space/backtick to apply it. even though it's not as convenient as by color palette, one advantage combobox over color palette is that you can use common color names such as blue, green, red, yellow, ... whereas color palette will only provide hex code.

(2c) if the color palette window obstructs the shape you want, either move it aside, or close it (whichever appropriate). otherwise, just click the shape directly to complete the process. no need to close it beforehand.

(2d) the color palette comes from a png file as attached.
[spoiler][attachment=0]ColorPal.png[/attachment][/spoiler]

(3) miscellaneous: a, size of each of the shapes are tuned so that they all roughly take up same amount of space on screen in terms of width. b, consequently, updated the "save spots" (script) menu button as it's been bumped by that. c, added a "hide spots" and a "show spots" menu button. d, default size changed to 110 points (it was 120 back then).

Here is the menu buttons (script) mentioned. You may update your menu text.

Code: Select all

Save Spots	{Save All Spots}	(Script)
Hide Spots	{Hide All Extra Spots}	(Script)
Show Spots	{Show All Extra Spots}	(Script)
;
{Script Save All Spots}
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	tmm := A_TitleMatchMode
	SetTitleMatchMode, RegEx
	log := ""
	WinGet, wList, List, [sS]pot$
	Loop, % wList
	{	hwnd := wList%A_Index%
		WinGetTitle, wt, ahk_id %hwnd%
		If RegExMatch(wt, "^B\d+spot$") or wt = "mSpot"
		{	WinGetPos, x, y,,, ahk_id %hwnd%
			log .= "`nTitle`t= " wt "`n" "Pos`t= " x "," y
			If not (wt = "mSpot")
			{	ControlGetText, s, Static6, ahk_id %hwnd%
			Loop, 5
			{	ControlGetText, t, Static%A_Index%, ahk_id %hwnd%
				If (A_Index = 1) and StrLen(s)
					log .= "`nText" A_Index "`t= " t "'" s
				Else log .= "`nText" A_Index "`t= " StrReplace(t, "`n", "`t")
			}
			}log .= "`n----------"
		}
	}If StrLen(log)
		a.Call("var", "Set:menu.Save", "{SpotsList@" A_Now "}`n----------" log "`n{/SpotsList}"), a.Call("menu.Append")
	SetTitleMatchMode, %tmm%
	DetectHiddenWindows, %dhw%
{/Script}

{Script Hide All Extra Spots}
	ToggleState = Hide
	#Include Toggle All Extra Spots#
{/Script}

{Script Show All Extra Spots}
	ToggleState = Show
	#Include Toggle All Extra Spots#
{/Script}

{Script Toggle All Extra Spots}
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	tmm := A_TitleMatchMode
	SetTitleMatchMode, RegEx
	WinGet, wList, List, [sS]pot$
	Loop, % wList
	{	hwnd := wList%A_Index%
		WinGetTitle, wt, ahk_id %hwnd%
		If RegExMatch(wt, "^B\d+spot$")
			If ToggleState = Hide
				WinHide, %wt%
			Else WinShow, %wt%
	}SetTitleMatchMode, %tmm%
	DetectHiddenWindows, %dhw%
{/Script}

here is the upated script.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

$RButton::keyRButton()
$RButton up::Return
#IfWinActive, Select A Batch Please
	Space::
	Enter::
	`::menu.lsPick()
	~Down::
	~Up::menu.DownUp := A_TickCount
#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	If not StrLen(this.Spots)
		this.Spots := this.sgnHistory := "Spots"
	If not StrLen(this.Color)
		this.Color := this.fscHistory := "B2D6F3"
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos) or (hwnd = "mSpot")
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd) or (hwnd = "mSpot")
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	Passed := False
			If RegExMatch(m, "\s*\[([^\]]+)]", mm)
			If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
			If pos := InStr(mm1, ":")
				If not RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) or not RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
					Continue
				Else Passed := True
			Else If not RegExMatch(sWin, Trim(mm1))
				Continue
			Else Passed := True
			If Passed
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	this.do(theText, TT.GetText(h))
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
do(theText, t) {
	If RegExMatch(t, "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") this.HandleInclude(mm1),, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") this.HandleInclude(m),, A_AhkPath)
}
HandleInclude(s) {
	If RegExMatch(s, "is)#\s*Include\s+[^#]+#")
	{	RegExReplace(s, "is)#\s*Include\s+[^#]+#",, n), pos := 1, m := ""
		Loop, %n%
			pos := RegExMatch(s, "is)#\s*Include\s+\K[^#]+(?=#)", m, pos + StrLen(m)), RegExMatch(this.t, "is){\s*Script\s+" Trim(m) "\s*}(.+?){\s*/\s*Script\s*}", mm), s := RegExReplace(s, "is)#\s*Include\s+" Trim(m) "\s*#", RegExMatch(mm1, "is)#\s*Include\s+[^#]+#") ? this.HandleInclude(mm1) : mm1)
	}
	Return s
}
ExtraSpot() {
	btn := "B" A_TickCount
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	ControlGetText, m, %moc%, %mow%
	MouseGetPos,,,, h, 2
	t := agent.Call("TT.GetText", h)
	menu.Gui_OnEscape()
	Color := this.Color, gName := this.Spots
	s := (A_ScreenHeight > A_ScreenWidth) ? "◆|◼|◉|◈'120|▣|◙|▲|▼|◀|▶|◢|◥|◣|◤|➤'95|●|●'150|🛑'70|☗'115|⛊'95|⛘'95|❦'130|☙'105|🖤'70|➽'95|🡅|🡇|🡄|🡆|♝'100|♥'130|♠'130|♦'130|♣'130|♜'100|♞'100|⚈'115|⚉'115|⛇'100|☻'130|✅'70|⚑'130|★'115|✦'115|✹|✸|✷|✶'150|✱|🎩'70|🚚'70|🍩'70|⛟'95" : "◙|◆|◼|◉|◈'120|▣|◀|▼|▶|▲|➤'95|➽'95|◤|◣|⛘'95|◥|◢|☗'115|⛊'95|●|●'150|🛑'70|♝'100|♜'100|♞'100|🡅|🡇|🡄|🡆|🎩'70|🍩'70|✶'150|🖤'70|❦'130|☙'105|♥'130|♠'130|♦'130|♣'130|☻'130|🚚'70|⛇'100|⛟'95|✅'70|⚈'115|⚉'115|⚑'130|★'115|✦'115|✹|✸|✷|✱", this.Shapes := s
	Gui, ps:New, +LastFound +AlwaysOnTop +Labelmenu.psOn
	Loop, Parse, s, |
	{	If (A_ScreenHeight > A_ScreenWidth)
			x := 20 + 130 * Mod(A_Index - 1, 6) , y := -40 + 140 * ((A_Index - 1) // 6)
		Else x := 5 + 115 * Mod(A_Index - 1, 11) , y := -10 + 140 * ((A_Index - 1) // 11)
		If RegExMatch(A_LoopField, "^([^']+)'(\d+)$", ss)
		{	Gui, Font, s%ss2% c%Color%, Consolas
			Gui, Add, Text, x%x% y%y% BackgroundTrans, %ss1%
		}Else
		{	Gui, Font, s110 c%Color%, Consolas
			Gui, Add, Text, x%x% y%y% BackgroundTrans, %A_LoopField%
		}OnSuch := this.Pick.Bind(this)
		GuiControl +g, Static%A_Index%, % OnSuch
	}Gui, Color, Black
	Gui, Show,, Select A Shape Please
	;Progress, zh0 w400 c10 fs18, `nSelect A Shape Please`n,, Hit [Space] or [Enter] to continue OR it will go on in 5 seconds, Segoe UI
	;MsgBox,,,, 5
	;Progress, Off
	WinSet, Transparent, 64
	Gui, cp:New, +LastFound +AlwaysOnTop +Labelmenu.cpOn
	Gui, Margin, 20, 20
	Gui, Add, Picture, x25 y25 w372 h238, ColorPal.png
	OnSuch := this.PickColor.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, +Delimiter`n
	Gui, Font, s30, Consolas
	Gui, Add, Text, cWhite, Color Code
	Gui, Add, ComboBox, w372 hwndHEdit1
	GuiControl,, ComboBox1, % "`n" this.fscHistory
	OnSuch := this.EditColor.Bind(this)
	GuiControl +g, ComboBox1, % OnSuch
	ControlSetText,, %Color%, ahk_id %HEdit1%
	Gui, Add, Text, cWhite, Group Name
	Gui, Add, ComboBox, w372 hwndHEdit2
	GuiControl,, ComboBox2, % "`n" this.sgnHistory
	OnSuch := this.EditSpots.Bind(this)
	GuiControl +g, ComboBox2, % OnSuch
	ControlSetText,, %gName%, ahk_id %HEdit2%
	Gui, Color, Black
	Gui, Show,, Color Palette
	var("Set:Shape@pool", False), this.CurItem := "●"
	While, not var("Shape@pool") and WinExist("Select A Shape Please")
		Sleep, 100
	If not var("Shape@pool")
		var("Set:Shape@pool", this.CurItem)
	Gui, %btn%:New, hwndHspot
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Color := this.Color
	shape := var("Shape@pool")
	If RegExMatch(shape, "^([^']+)'(\d+)$", ss)
	{	Gui, Font, s%ss2%, Consolas
		Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %ss1%
	}Else
	{	Gui, Font, s110, Consolas
		Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %shape%
	}OnSuch := this.xHit.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, Add, Text, Hidden, %m%
	Gui, Add, Text, Hidden, %t%
	Gui, Add, Text, Hidden, % this.Spots
	Gui, Add, Text, Hidden, %Color%
	If RegExMatch(shape, "^[^']+'\d+$")
		Gui, Add, Text, Hidden, %ss2%
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100
	Gui, Show, x%x% NoActivate, %btn%spot
	GroupAdd, % this.Spots, % "ahk_id " . WinExist()
	this.wt := btn "spot"
	ControlGet, sid, hwnd,, Static1, %btn%spot
	this.xHit(sid + 0) ; Init sPos
}
xHit(hwnd := "") {
	static sPos := []
	If SubStr(hwnd, -3) = "spot"
	{	wt := hwnd
		WinGetPos, x, y,,, %wt%
		ControlGet, hwnd, hwnd,, Static1, %wt%
		sPos[hwnd + 0] := x "," y
		Return
	}Else If not StrLen(sPos[hwnd])
	{	WinGetPos, x, y,,, % this.wt
		sPos[hwnd] := x "," y
		Return
	}SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	WinGetActiveTitle, wt
	var("Set:wName@pool", wName := SubStr(wt, 1, -4))
	If x "," y = sPos[hwnd]
	{	GuiControlGet, theText,, Static2
		GuiControlGet, t,, Static3
		Gui, %wName%:Hide
		menu.do(theText, t)
	}Else sPos[hwnd] := x "," y
	SetTimer, DeactivateIt, -500
	Return
	DeactivateIt:
		wName := var("wName@pool")
		Gui, %wName%:Hide
		Gui, %wName%:Show, NoActivate
		Return
}
Pick(hwnd := "") {
	WinGet, t, ControlListHwnd, A
	StrReplace(SubStr(t, 1, InStr(t, Format("0x{:x}", hwnd))), "`n",, c)
	Choice := RegExReplace(SubStr(this.Shapes, InStr(this.Shapes, "|",,, c) + 1), "[^|]+\K|.+$")
	var("Set:Shape@pool", Choice)
	IfWinExist, Color Palette
		this.cpOnClose()
	this.psOnClose()
}
PickColor() {
	MouseGetPos, x, y
	PixelGetColor, Color, x, y, RGB
	Color := SubStr(Color, -5)
	ControlGet, HEdit, hwnd,, Edit1, A
	ControlSetText,, %Color%, ahk_id %HEdit%
	this.ColorFill(Color)
}
ColorFill(Color) {
	s := this.Shapes
	Loop, Parse, s, |
	{	If RegExMatch(A_LoopField, "^([^']+)'(\d+)$", ss)
			Gui, ps:Font, s%ss2% c%Color%, Consolas
		Else Gui, ps:Font, s110 c%Color%, Consolas
		GuiControl, ps:Font, Static%A_Index%
	}
}
EditColor() {
	static vTemp := ""
	GuiControlGet, v,, ComboBox1
	ControlGet, HEdit, hwnd,, Edit1, A
	DllCall("User32\SendMessage", "Ptr", HEdit, "UInt", 0xB0, "UIntP", slcs, "UIntP", slce, "Ptr")
	If (slce = slcs) and ((k := SubStr(v, slce, 1)) = "``" or (k = A_Space))
	{	Color := SubStr(v, 1, slce - 1)
		If (slce < StrLen(v))
			Color .= SubStr(v, slce + 1)
		ControlSetText,, %Color%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
		If StrLen(Color)
			this.ColorFill(Color)
	}Else If (slce > slcs)
		SendMessage, 0xB1, slce, slce,, ahk_id %HEdit%	;EM_SETSEL
	If RegExMatch(v, "\W")
	{	ControlSetText,, %vTemp%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
	}Else vTemp := v
}
EditSpots() {
	GuiControlGet, v,, ComboBox2
	ControlGet, HEdit, hwnd,, Edit2, A
	DllCall("User32\SendMessage", "Ptr", HEdit, "UInt", 0xB0, "UIntP", slcs, "UIntP", slce, "Ptr")
	If (slce = slcs) and RegExMatch(SubStr(v, slce, 1), "\W")
	{	gName := SubStr(v, 1, slce - 1)
		If (slce < StrLen(v))
			gName .= SubStr(v, slce + 1)
		ControlSetText,, %gName%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
	}Else If (slce > slcs)
		SendMessage, 0xB1, slce, slce,, ahk_id %HEdit%	;EM_SETSEL
}
cpOnEscape() {
	menu.cpOnClose()
}
cpOnClose() {
	ControlGetText, Color, Edit1
	If StrLen(Color)
	{	menu.Color := Color
		If not StrLen(menu.fscHistory)
			menu.fscHistory := Color
		Else If not RegExMatch(menu.fscHistory, "im`a)^\Q" Color "\E$")
			menu.fscHistory := Color "`n" menu.fscHistory
	}ControlGetText, gName, Edit2
	If StrLen(gName)
	{	menu.Spots := gName
		If not StrLen(menu.sgnHistory)
			menu.sgnHistory := gName
		Else If not RegExMatch(menu.sgnHistory, "im`a)^\Q" gName "\E$")
			menu.sgnHistory := gName "`n" menu.sgnHistory
	}Gui, cp:Destroy
}
psOnEscape() {
	menu.psOnClose()
}
psOnClose() {
	IfWinExist, Color Palette
		menu.cpOnClose()
	Gui, ps:Destroy
}
Flick() {
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	tmm := A_TitleMatchMode
	SetTitleMatchMode, RegEx
	WinGet, wList, List, [sS]pot$
	Loop, % wList
	{	hwnd := wList%A_Index%
		WinGetTitle, wt, ahk_id %hwnd%
		If RegExMatch(wt, "^B\d+spot$") or wt = "mSpot"
		{	WinGetPos, x,,,, ahk_id %hwnd%
			WinMove, ahk_id %hwnd%,, (A_ScreenWidth - 110) - x
			If wt = mSpot
				this.HitEvent(wt)
			Else this.xHit(wt)
		}
	}SetTitleMatchMode, %tmm%
	DetectHiddenWindows, %dhw%
}
Append() {
	t := "`n`n" this.Save
	FileAppend, %t%, MenuText.txt, UTF-8
	MsgBox, Saved
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
}
LoadSpots() {
	t := var("_menu_@pool")
	tt := RegExReplace(RegExReplace(t, "im`a)^[ \t]*(?!{SpotsList@\w+}).*\R?"), "{SpotsList@(\w+)}", "$1"), RegExReplace(tt, "im`a)^[ \t]*\w+[ \t]*$",, mCount)
	If mCount > 1
	{	ReverseByLine(tt)
		Gui, losp:New, +ToolWindow +Delimiter`n
		Gui, Font, s30, Consolas
		Gui, Add, ListBox, r15, %tt%
		OnSuch := this.lsPick.Bind(this)
		GuiControl +g, ListBox1, % OnSuch
		Gui, Show,, Select A Batch Please
		this.Batch := False, RegExMatch(tt, "\w+", m), this.CurItem := m
		While, not this.Batch and WinExist("Select A Batch Please")
			Sleep, 100
		If not this.Batch
			this.Batch := this.CurItem
		Gui, losp:Destroy
	}Else RegExMatch(tt, "\w+", m), this.Batch := m
	RegExMatch(this.Batch, "\w+", b), RegExMatch(t, "is){SpotsList@" b "}\K.+?(?={/SpotsList})", m)
	Loop, Parse, m, `n
	{	LoopField := SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
		If not RegExMatch(LoopField, "[^\-\s]")
			If StrLen(wi)
			{	If (wt = "mSpot") {
Gui, mSpot:New
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
Gui, Font, s150, Consolas
Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
OnSuch := this.HitEvent.Bind(this)
GuiControl +g, Static1, % OnSuch
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, mSpot
this.HitEvent(wt) ; Init sPos
OnMessage(0x7E, "WM_DISPLAYCHANGE")
OnMessage(0x06, "WM_ACTIVATE")
wt := x := y := wi := ""
				}Else{
btn := SubStr(wt, 1, -4)
Gui, %btn%:New, hwndHspot
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
If RegExMatch(shape, "^([^']+)'(\d+)$", ss)
{	Gui, Font, s%ss2%, Consolas
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %ss1%
}Else
{	Gui, Font, s110, Consolas
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %shape%
}OnSuch := this.xHit.Bind(this)
GuiControl +g, Static1, % OnSuch
Gui, Add, Text, Hidden, %blbl%
Gui, Add, Text, Hidden, %ttip%
Gui, Add, Text, Hidden, %gname%
Gui, Add, Text, Hidden, %Color%
If RegExMatch(shape, "^[^']+'\d+$")
	Gui, Add, Text, Hidden, %ss2%
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, %wt%
GroupAdd, %gname%, % "ahk_id " . WinExist()
this.wt := wt
ControlGet, sid, hwnd,, Static1, %wt%
this.xHit(sid) ; Init sPos
wt := x := y := shape := blbl := ttip := gname := color := wi := ""
				}
			}Else{}
		Else If RegExMatch(LoopField, "Title\t= \K.*$", mm)
			wt := mm, wi := mm
		Else If RegExMatch(LoopField, "Pos\t= \K.*$", mm)
			RegExMatch(mm, ".*(?=,)", x), RegExMatch(mm, ",\K.*$", y), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text1\t= \K.*$", mm)
			shape := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text2\t= \K.*$", mm)
			blbl := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text3\t= \K.*$", mm)
			ttip := StrReplace(mm, "`t", "`n"), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text4\t= \K.*$", mm)
			gname := mm, wi .= "`n" mm, glist .= "`n" mm
		Else If RegExMatch(LoopField, "Text5\t= \K.*$", mm)
			color := mm, wi .= "`n" mm, clist .= "`n" mm
	}v := this.sgnHistory glist
	Sort, v, U
	this.sgnHistory := v, v := this.fscHistory clist
	Sort, v, U
	this.fscHistory := v
}
lsPick(hwnd := "") {
	GuiControlGet, Choice,, ListBox1
	If StrLen(Choice)
	{	this.CurItem := Choice
		If A_TickCount - (StrLen(this.DownUp) ? this.DownUp : 0) < 100
			Return
	}Else If A_ThisHotkey in Space,$Enter,``
		Choice := this.CurItem
	this.Batch := Choice
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

keyRButton() {
	MouseGetPos, xb4, yb4, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	beg := A_TickCount
	While, GetKeyState("RButton", "P")
	{	MouseGetPos, x, y
		If SubStr(mow, -3) = "Spot" and moc = "Static1" and (Abs(x - xb4) > 5 or Abs(y - yb4) > 5)
		{	If mow = mSpot
				menu.Flick()
			Else
			{	WinGetPos, x,,,, %mow%
				WinMove, %mow%,, (A_ScreenWidth - 110) - x
				menu.xHit(mow)
			}beg := False
			Break
		}Else If A_TickCount - beg > 1000
		{	IfWinNotExist, Touch Friendly Menu
			IfWinExist, mSpot
				If (mow = "mSpot") and (moc = "Static1")
				{	Gui, mSpot:Destroy
					beg := False
				}Else{}
			Else menu.Spot(), menu.HitEvent("mSpot"), beg := False
			SoundBeep
			Break
		}
	}If not beg
		Return
	If SubStr(mow, -3) = "Spot" and moc = "Static1"
		If mow = mSpot
		{	Spots := menu.Spots
			WinGet, c, Count, ahk_group %Spots%
			dhw := A_DetectHiddenWindows
			DetectHiddenWindows, On
			WinGet, ca, Count, ahk_group %Spots%
			DetectHiddenWindows, %dhw%
			If (ca > c)
			{	aw := WinExist("A")
				WinShow, ahk_group %Spots%
				WinActivate, ahk_id %aw%
				WinWaitActive
			}Else If ca
				WinHide, ahk_group %Spots%
			Else Gui, mSpot:Destroy
		}Else ;WinClose, %mow%
		{	GuiName := SubStr(mow, 1, -4)
			Gui, %GuiName%:Destroy
		}
	Else If mow = Touch Friendly Menu
		If SubStr(moc, 1, 6) = "Button"
			menu.ExtraSpot()
		Else{}
	Else Send, {RButton}
}

ReverseByLine(ByRef text, delimiter := "`n") {	;written by SundayProgrammer
	array := StrSplit(SubStr(text, 0) = delimiter ? SubStr(text, 1, StrLen(text) - 1) : text, delimiter), mi := array.MaxIndex()
	Loop, % mi
		If A_Index > 1
			text .= array[mi - A_Index + 1] delimiter
		Else text := array[mi] delimiter
}

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
that's it for now.

Attachments
ColorPal.png
ColorPal.png (35.27 KiB) Viewed 945 times

SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

Post by SundayProgrammer » 04 Jan 2022, 14:00

what's new in this version:

(1) fixed a bug which occurred only when mspot was destroyed, yet spotmenu was called somehow (might be through other means such as extraspot, or some codes added to the script, or from any other scripts) that had caused a redundant window was created.

(2) added a preventive measure to get around an intermittent problem (realized lately) which caused touch gesture scrolling off positions sometimes.

(3) extended the coverage of the optional preceding declaration (a feature introduced back in the 5th post of this thread). it covers also the plain text lines now in addition to the menu item lines.

here is the updated script.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

$RButton::keyRButton()
$RButton up::Return
#IfWinActive, Select A Batch Please
	Space::
	Enter::
	`::menu.lsPick()
	~Down::
	~Up::menu.DownUp := A_TickCount
#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	If menu.mSpotExist()
	{	WinGetPos, x, y,,, mSpot
		If (x > A_ScreenWidth - 50)
			x := A_ScreenWidth - 100
		If (y > A_ScreenHeight - 125)
			y := A_ScreenHeight - 175
		Gui, mSpot:Show, x%x% y%y% NoActivate
	}
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	If not StrLen(this.Spots)
		this.Spots := this.sgnHistory := "Spots"
	If not StrLen(this.Color)
		this.Color := this.fscHistory := "B2D6F3"
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos) or (hwnd = "mSpot")
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd) or (hwnd = "mSpot")
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	If this.CondiPassed(m, sWin, sCtl)
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			Else Continue
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If this.CondiPassed(A_LoopField, sWin, sCtl)
		{	aLoopField := Trim(RegExReplace(A_LoopField, "\s*\[([^\]]+)]"))
			Gui, Add, Text, x25 cWhite, % SubStr(aLoopField, 0) = "`r" ? SubStr(aLoopField, 1, -1) : aLoopField
			nl := True
		}Else Continue
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
	SendMessage, 0x0115, 7, 0,, A	;WM_VSCROLL
	SendMessage, 0x0115, 6, 0,, A	;WM_VSCROLL
}
CondiPassed(m, sWin, sCtl) {
	If RegExMatch(m, "\s*\[([^\]]+)]", mm)
	If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
	If pos := InStr(mm1, ":")
		Return RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) and RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
	Else Return RegExMatch(sWin, Trim(mm1))
	Return True
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	If this.mSpotExist()
		Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
mSpotExist() {
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	MenuSpotExists := WinExist("mSpot")
	DetectHiddenWindows, %dhw%
	Return MenuSpotExists
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	this.do(theText, TT.GetText(h))
	If this.mSpotExist()
		Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
do(theText, t) {
	If RegExMatch(t, "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") this.HandleInclude(mm1),, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") this.HandleInclude(m),, A_AhkPath)
}
HandleInclude(s) {
	If RegExMatch(s, "is)#\s*Include\s+[^#]+#")
	{	RegExReplace(s, "is)#\s*Include\s+[^#]+#",, n), pos := 1, m := ""
		Loop, %n%
			pos := RegExMatch(s, "is)#\s*Include\s+\K[^#]+(?=#)", m, pos + StrLen(m)), RegExMatch(this.t, "is){\s*Script\s+" Trim(m) "\s*}(.+?){\s*/\s*Script\s*}", mm), s := RegExReplace(s, "is)#\s*Include\s+" Trim(m) "\s*#", RegExMatch(mm1, "is)#\s*Include\s+[^#]+#") ? this.HandleInclude(mm1) : mm1)
	}
	Return s
}
ExtraSpot() {
	btn := "B" A_TickCount
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	ControlGetText, m, %moc%, %mow%
	MouseGetPos,,,, h, 2
	t := agent.Call("TT.GetText", h)
	menu.Gui_OnEscape()
	Color := this.Color, gName := this.Spots
	s := (A_ScreenHeight > A_ScreenWidth) ? "◆|◼|◉|◈'120|▣|◙|▲|▼|◀|▶|◢|◥|◣|◤|➤'95|●|●'150|🛑'70|☗'115|⛊'95|⛘'95|❦'130|☙'105|🖤'70|➽'95|🡅|🡇|🡄|🡆|♝'100|♥'130|♠'130|♦'130|♣'130|♜'100|♞'100|⚈'115|⚉'115|⛇'100|☻'130|✅'70|⚑'130|★'115|✦'115|✹|✸|✷|✶'150|✱|🎩'70|🚚'70|🍩'70|⛟'95" : "◙|◆|◼|◉|◈'120|▣|◀|▼|▶|▲|➤'95|➽'95|◤|◣|⛘'95|◥|◢|☗'115|⛊'95|●|●'150|🛑'70|♝'100|♜'100|♞'100|🡅|🡇|🡄|🡆|🎩'70|🍩'70|✶'150|🖤'70|❦'130|☙'105|♥'130|♠'130|♦'130|♣'130|☻'130|🚚'70|⛇'100|⛟'95|✅'70|⚈'115|⚉'115|⚑'130|★'115|✦'115|✹|✸|✷|✱", this.Shapes := s
	Gui, ps:New, +LastFound +AlwaysOnTop +Labelmenu.psOn
	Loop, Parse, s, |
	{	If (A_ScreenHeight > A_ScreenWidth)
			x := 20 + 130 * Mod(A_Index - 1, 6) , y := -40 + 140 * ((A_Index - 1) // 6)
		Else x := 5 + 115 * Mod(A_Index - 1, 11) , y := -10 + 140 * ((A_Index - 1) // 11)
		If RegExMatch(A_LoopField, "^([^']+)'(\d+)$", ss)
		{	Gui, Font, s%ss2% c%Color%, Consolas
			Gui, Add, Text, x%x% y%y% BackgroundTrans, %ss1%
		}Else
		{	Gui, Font, s110 c%Color%, Consolas
			Gui, Add, Text, x%x% y%y% BackgroundTrans, %A_LoopField%
		}OnSuch := this.Pick.Bind(this)
		GuiControl +g, Static%A_Index%, % OnSuch
	}Gui, Color, Black
	Gui, Show,, Select A Shape Please
	;Progress, zh0 w400 c10 fs18, `nSelect A Shape Please`n,, Hit [Space] or [Enter] to continue OR it will go on in 5 seconds, Segoe UI
	;MsgBox,,,, 5
	;Progress, Off
	WinSet, Transparent, 64
	Gui, cp:New, +LastFound +AlwaysOnTop +Labelmenu.cpOn
	Gui, Margin, 20, 20
	Gui, Add, Picture, x25 y25 w372 h238, ColorPal.png
	OnSuch := this.PickColor.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, +Delimiter`n
	Gui, Font, s30, Consolas
	Gui, Add, Text, cWhite, Color Code
	Gui, Add, ComboBox, w372 hwndHEdit1
	GuiControl,, ComboBox1, % "`n" this.fscHistory
	OnSuch := this.EditColor.Bind(this)
	GuiControl +g, ComboBox1, % OnSuch
	ControlSetText,, %Color%, ahk_id %HEdit1%
	Gui, Add, Text, cWhite, Group Name
	Gui, Add, ComboBox, w372 hwndHEdit2
	GuiControl,, ComboBox2, % "`n" this.sgnHistory
	OnSuch := this.EditSpots.Bind(this)
	GuiControl +g, ComboBox2, % OnSuch
	ControlSetText,, %gName%, ahk_id %HEdit2%
	Gui, Color, Black
	Gui, Show,, Color Palette
	var("Set:Shape@pool", False), this.CurItem := "●"
	While, not var("Shape@pool") and WinExist("Select A Shape Please")
		Sleep, 100
	If not var("Shape@pool")
		var("Set:Shape@pool", this.CurItem)
	Gui, %btn%:New, hwndHspot
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Color := this.Color
	shape := var("Shape@pool")
	If RegExMatch(shape, "^([^']+)'(\d+)$", ss)
	{	Gui, Font, s%ss2%, Consolas
		Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %ss1%
	}Else
	{	Gui, Font, s110, Consolas
		Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %shape%
	}OnSuch := this.xHit.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, Add, Text, Hidden, %m%
	Gui, Add, Text, Hidden, %t%
	Gui, Add, Text, Hidden, % this.Spots
	Gui, Add, Text, Hidden, %Color%
	If RegExMatch(shape, "^[^']+'\d+$")
		Gui, Add, Text, Hidden, %ss2%
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100
	Gui, Show, x%x% NoActivate, %btn%spot
	GroupAdd, % this.Spots, % "ahk_id " . WinExist()
	this.wt := btn "spot"
	ControlGet, sid, hwnd,, Static1, %btn%spot
	this.xHit(sid + 0) ; Init sPos
}
xHit(hwnd := "") {
	static sPos := []
	If SubStr(hwnd, -3) = "spot"
	{	wt := hwnd
		WinGetPos, x, y,,, %wt%
		ControlGet, hwnd, hwnd,, Static1, %wt%
		sPos[hwnd + 0] := x "," y
		Return
	}Else If not StrLen(sPos[hwnd])
	{	WinGetPos, x, y,,, % this.wt
		sPos[hwnd] := x "," y
		Return
	}SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	WinGetActiveTitle, wt
	var("Set:wName@pool", wName := SubStr(wt, 1, -4))
	If x "," y = sPos[hwnd]
	{	GuiControlGet, theText,, Static2
		GuiControlGet, t,, Static3
		Gui, %wName%:Hide
		menu.do(theText, t)
	}Else sPos[hwnd] := x "," y
	SetTimer, DeactivateIt, -500
	Return
	DeactivateIt:
		wName := var("wName@pool")
		Gui, %wName%:Hide
		Gui, %wName%:Show, NoActivate
		Return
}
Pick(hwnd := "") {
	WinGet, t, ControlListHwnd, A
	StrReplace(SubStr(t, 1, InStr(t, Format("0x{:x}", hwnd))), "`n",, c)
	Choice := RegExReplace(SubStr(this.Shapes, InStr(this.Shapes, "|",,, c) + 1), "[^|]+\K|.+$")
	var("Set:Shape@pool", Choice)
	IfWinExist, Color Palette
		this.cpOnClose()
	this.psOnClose()
}
PickColor() {
	MouseGetPos, x, y
	PixelGetColor, Color, x, y, RGB
	Color := SubStr(Color, -5)
	ControlGet, HEdit, hwnd,, Edit1, A
	ControlSetText,, %Color%, ahk_id %HEdit%
	this.ColorFill(Color)
}
ColorFill(Color) {
	s := this.Shapes
	Loop, Parse, s, |
	{	If RegExMatch(A_LoopField, "^([^']+)'(\d+)$", ss)
			Gui, ps:Font, s%ss2% c%Color%, Consolas
		Else Gui, ps:Font, s110 c%Color%, Consolas
		GuiControl, ps:Font, Static%A_Index%
	}
}
EditColor() {
	static vTemp := ""
	GuiControlGet, v,, ComboBox1
	ControlGet, HEdit, hwnd,, Edit1, A
	DllCall("User32\SendMessage", "Ptr", HEdit, "UInt", 0xB0, "UIntP", slcs, "UIntP", slce, "Ptr")
	If (slce = slcs) and ((k := SubStr(v, slce, 1)) = "``" or (k = A_Space))
	{	Color := SubStr(v, 1, slce - 1)
		If (slce < StrLen(v))
			Color .= SubStr(v, slce + 1)
		ControlSetText,, %Color%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
		If StrLen(Color)
			this.ColorFill(Color)
	}Else If (slce > slcs)
		SendMessage, 0xB1, slce, slce,, ahk_id %HEdit%	;EM_SETSEL
	If RegExMatch(v, "\W")
	{	ControlSetText,, %vTemp%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
	}Else vTemp := v
}
EditSpots() {
	GuiControlGet, v,, ComboBox2
	ControlGet, HEdit, hwnd,, Edit2, A
	DllCall("User32\SendMessage", "Ptr", HEdit, "UInt", 0xB0, "UIntP", slcs, "UIntP", slce, "Ptr")
	If (slce = slcs) and RegExMatch(SubStr(v, slce, 1), "\W")
	{	gName := SubStr(v, 1, slce - 1)
		If (slce < StrLen(v))
			gName .= SubStr(v, slce + 1)
		ControlSetText,, %gName%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
	}Else If (slce > slcs)
		SendMessage, 0xB1, slce, slce,, ahk_id %HEdit%	;EM_SETSEL
}
cpOnEscape() {
	menu.cpOnClose()
}
cpOnClose() {
	ControlGetText, Color, Edit1
	If StrLen(Color)
	{	menu.Color := Color
		If not StrLen(menu.fscHistory)
			menu.fscHistory := Color
		Else If not RegExMatch(menu.fscHistory, "im`a)^\Q" Color "\E$")
			menu.fscHistory := Color "`n" menu.fscHistory
	}ControlGetText, gName, Edit2
	If StrLen(gName)
	{	menu.Spots := gName
		If not StrLen(menu.sgnHistory)
			menu.sgnHistory := gName
		Else If not RegExMatch(menu.sgnHistory, "im`a)^\Q" gName "\E$")
			menu.sgnHistory := gName "`n" menu.sgnHistory
	}Gui, cp:Destroy
}
psOnEscape() {
	menu.psOnClose()
}
psOnClose() {
	IfWinExist, Color Palette
		menu.cpOnClose()
	Gui, ps:Destroy
}
Flick() {
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	tmm := A_TitleMatchMode
	SetTitleMatchMode, RegEx
	WinGet, wList, List, [sS]pot$
	Loop, % wList
	{	hwnd := wList%A_Index%
		WinGetTitle, wt, ahk_id %hwnd%
		If RegExMatch(wt, "^B\d+spot$") or wt = "mSpot"
		{	WinGetPos, x,,,, ahk_id %hwnd%
			WinMove, ahk_id %hwnd%,, (A_ScreenWidth - 110) - x
			If wt = mSpot
				this.HitEvent(wt)
			Else this.xHit(wt)
		}
	}SetTitleMatchMode, %tmm%
	DetectHiddenWindows, %dhw%
}
Append() {
	t := "`n`n" this.Save
	FileAppend, %t%, MenuText.txt, UTF-8
	MsgBox, Saved
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
}
LoadSpots() {
	t := var("_menu_@pool")
	tt := RegExReplace(RegExReplace(t, "im`a)^[ \t]*(?!{SpotsList@\w+}).*\R?"), "{SpotsList@(\w+)}", "$1"), RegExReplace(tt, "im`a)^[ \t]*\w+[ \t]*$",, mCount)
	If mCount > 1
	{	ReverseByLine(tt)
		Gui, losp:New, +ToolWindow +Delimiter`n
		Gui, Font, s30, Consolas
		Gui, Add, ListBox, r15, %tt%
		OnSuch := this.lsPick.Bind(this)
		GuiControl +g, ListBox1, % OnSuch
		Gui, Show,, Select A Batch Please
		this.Batch := False, RegExMatch(tt, "\w+", m), this.CurItem := m
		While, not this.Batch and WinExist("Select A Batch Please")
			Sleep, 100
		If not this.Batch
			this.Batch := this.CurItem
		Gui, losp:Destroy
	}Else RegExMatch(tt, "\w+", m), this.Batch := m
	RegExMatch(this.Batch, "\w+", b), RegExMatch(t, "is){SpotsList@" b "}\K.+?(?={/SpotsList})", m)
	Loop, Parse, m, `n
	{	LoopField := SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
		If not RegExMatch(LoopField, "[^\-\s]")
			If StrLen(wi)
			{	If (wt = "mSpot") {
Gui, mSpot:New
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
Gui, Font, s150, Consolas
Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
OnSuch := this.HitEvent.Bind(this)
GuiControl +g, Static1, % OnSuch
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, mSpot
this.HitEvent(wt) ; Init sPos
OnMessage(0x7E, "WM_DISPLAYCHANGE")
OnMessage(0x06, "WM_ACTIVATE")
wt := x := y := wi := ""
				}Else{
btn := SubStr(wt, 1, -4)
Gui, %btn%:New, hwndHspot
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
If RegExMatch(shape, "^([^']+)'(\d+)$", ss)
{	Gui, Font, s%ss2%, Consolas
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %ss1%
}Else
{	Gui, Font, s110, Consolas
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %shape%
}OnSuch := this.xHit.Bind(this)
GuiControl +g, Static1, % OnSuch
Gui, Add, Text, Hidden, %blbl%
Gui, Add, Text, Hidden, %ttip%
Gui, Add, Text, Hidden, %gname%
Gui, Add, Text, Hidden, %Color%
If RegExMatch(shape, "^[^']+'\d+$")
	Gui, Add, Text, Hidden, %ss2%
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, %wt%
GroupAdd, %gname%, % "ahk_id " . WinExist()
this.wt := wt
ControlGet, sid, hwnd,, Static1, %wt%
this.xHit(sid) ; Init sPos
wt := x := y := shape := blbl := ttip := gname := color := wi := ""
				}
			}Else{}
		Else If RegExMatch(LoopField, "Title\t= \K.*$", mm)
			wt := mm, wi := mm
		Else If RegExMatch(LoopField, "Pos\t= \K.*$", mm)
			RegExMatch(mm, ".*(?=,)", x), RegExMatch(mm, ",\K.*$", y), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text1\t= \K.*$", mm)
			shape := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text2\t= \K.*$", mm)
			blbl := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text3\t= \K.*$", mm)
			ttip := StrReplace(mm, "`t", "`n"), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text4\t= \K.*$", mm)
			gname := mm, wi .= "`n" mm, glist .= "`n" mm
		Else If RegExMatch(LoopField, "Text5\t= \K.*$", mm)
			color := mm, wi .= "`n" mm, clist .= "`n" mm
	}v := this.sgnHistory glist
	Sort, v, U
	this.sgnHistory := v, v := this.fscHistory clist
	Sort, v, U
	this.fscHistory := v
}
lsPick(hwnd := "") {
	GuiControlGet, Choice,, ListBox1
	If StrLen(Choice)
	{	this.CurItem := Choice
		If A_TickCount - (StrLen(this.DownUp) ? this.DownUp : 0) < 100
			Return
	}Else If A_ThisHotkey in Space,$Enter,``
		Choice := this.CurItem
	this.Batch := Choice
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

keyRButton() {
	MouseGetPos, xb4, yb4, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	beg := A_TickCount
	While, GetKeyState("RButton", "P")
	{	MouseGetPos, x, y
		If SubStr(mow, -3) = "Spot" and moc = "Static1" and (Abs(x - xb4) > 5 or Abs(y - yb4) > 5)
		{	If mow = mSpot
				menu.Flick()
			Else
			{	WinGetPos, x,,,, %mow%
				WinMove, %mow%,, (A_ScreenWidth - 110) - x
				menu.xHit(mow)
			}beg := False
			Break
		}Else If A_TickCount - beg > 1000
		{	IfWinNotExist, Touch Friendly Menu
			IfWinExist, mSpot
				If (mow = "mSpot") and (moc = "Static1")
				{	Gui, mSpot:Destroy
					beg := False
				}Else{}
			Else menu.Spot(), menu.HitEvent("mSpot"), beg := False
			SoundBeep
			Break
		}
	}If not beg
		Return
	If SubStr(mow, -3) = "Spot" and moc = "Static1"
		If mow = mSpot
		{	Spots := menu.Spots
			WinGet, c, Count, ahk_group %Spots%
			dhw := A_DetectHiddenWindows
			DetectHiddenWindows, On
			WinGet, ca, Count, ahk_group %Spots%
			DetectHiddenWindows, %dhw%
			If (ca > c)
			{	aw := WinExist("A")
				WinShow, ahk_group %Spots%
				WinActivate, ahk_id %aw%
				WinWaitActive
			}Else If ca
				WinHide, ahk_group %Spots%
			Else Gui, mSpot:Destroy
		}Else ;WinClose, %mow%
		{	GuiName := SubStr(mow, 1, -4)
			Gui, %GuiName%:Destroy
		}
	Else If mow = Touch Friendly Menu
		If SubStr(moc, 1, 6) = "Button"
			menu.ExtraSpot()
		Else{}
	Else Send, {RButton}
}

ReverseByLine(ByRef text, delimiter := "`n") {	;written by SundayProgrammer
	array := StrSplit(SubStr(text, 0) = delimiter ? SubStr(text, 1, StrLen(text) - 1) : text, delimiter), mi := array.MaxIndex()
	Loop, % mi
		If A_Index > 1
			text .= array[mi - A_Index + 1] delimiter
		Else text := array[mi] delimiter
}

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
that's it for now.

SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

Post by SundayProgrammer » 13 Jan 2022, 08:00

here is a way to stick the extraspot(s) with a specific window.
https://www.autohotkey.com/board/topic/76008-change-ownership-of-windows/

for example:

Code: Select all

{Script Spots Pairing}
	WinSet, AlwaysOnTop, Off, B136801343spot
	WinSet, AlwaysOnTop, Off, B136833171spot
	WinSet, AlwaysOnTop, Off, B136613656spot
	WinSet, AlwaysOnTop, Off, B136545109spot
	h1 := winexist("B136801343spot")
	h2 := winexist("B136833171spot")
	h3 := winexist("B136613656spot")
	h4 := winexist("B136545109spot")
	settitlematchmode, 2
	p1 := winexist("Sublime Text")
	SetOwner(h1, p1)
	SetOwner(h2, p1)
	SetOwner(h3, p1)
	SetOwner(h4, p1)
	Return
	SetOwner(hwnd, newOwner) {
		static GWL_HWNDPARENT := -8
		If A_PtrSize = 8
			DllCall("SetWindowLongPtr", "ptr", hwnd, "int", GWL_HWNDPARENT, "ptr", newOwner)
		Else
			DllCall("SetWindowLong", "int", hwnd, "int", GWL_HWNDPARENT, "int", newOwner)
	}
{/Script}

SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

Post by SundayProgrammer » 18 Jan 2022, 08:13

whenever i watch youtube without a mouse (all i have is just a touchscreen), the most troublesome operation to me is having difficulty to tap the three-dot menu accurately with my finger. almost every one-out-of-three i have it missed and accidentally have the video opened instead. so annoying.

to tackle the problem, i've written up this script. it creates a full screen semi-transparent mask on top of everything, and after i tap on the video thumbnail, it automatically locates and clicks the corresponding three-dot, and hence i can open up the menu easily even with my rusty finger.

note: it locates the target by pixelsearch, and the color codes i used may not match with the scheme you are in.

Code: Select all

3-Dot	{Locate-n-Click YouTube 3-dot Button}	(Script)
;
{Script Locate-n-Click YouTube 3-dot Button}
	PosCursor()

	MouseGetPos, x, y, w
	WinGetTitle, t, ahk_id %w%
	If not InStr(t, "YouTube")
		ExitApp
	If r := ScanBlock(x, y, 209, 128)
		Goto, Action
	Else If r := ScanBlock(x, y - 111, 209, 111)
		Goto, Action
	Else Goto, Certify
	Action:
		xx := r.x, yy := r.y
		MouseClick, Left, xx, yy
		Goto, TheEnd
	Certify:
		MsgBox, Oops... target not found
	TheEnd:
		ExitApp
	ScanBlock(x, y, w, h) {
			PixelSearch, xx, yy, x, y, x + w, y + h, 0xFFFFFF,, Fast
		HandleResult:
			If ErrorLevel
				Return False
			Else Goto, Verify
		Verify:
			If not AllMatched(xx, yy)
			{	sx := xx, sy := yy
				If (sx < x + w)
				{	PixelSearch, xx, yy, sx + 1, sy, x + w, sy, 0xFFFFFF,, Fast
					If ErrorLevel
						Goto, SubVerify
					Else Goto, Verify
				}Else Goto, SubVerify
			}Return {"x":xx, "y":yy + 6}
		SubVerify:
			If (sy < y + h)
			{	PixelSearch, xx, yy, x, sy + 1, x + w, y + h, 0xFFFFFF,, Fast
				Goto, HandleResult
			}Else Return False
	}
	AllMatched(x, y) {
		PixelGetColor, c, x, y + 3
		If c = 0x181818
		{	PixelGetColor, c, x, y + 6
			If c = 0xFFFFFF
			{	PixelGetColor, c, x, y + 9
				If c = 0x181818
				{	PixelGetColor, c, x, y + 12
					If c = 0xFFFFFF
						Return True
				}
			}
		}Return False
	}
	#Include Enable Positioning Mouse Pointer With A Touchscreen Tap#
{/Script}

{Script Enable Positioning Mouse Pointer With A Touchscreen Tap}
	PosCursor() {
		Gui, mask:New, +LastFound
		Gui, Show, w%A_ScreenWidth% h%A_ScreenHeight%, Touch Mask
		WinSet, Transparent, 64
		OnMessage(0x201, "maskHit")
		WinWaitClose, Touch Mask
	}
	maskGuiEscape() {
		maskHit()
	}
	maskHit() {
		Gui, mask:Destroy
		maskGuiClose()
	}
	maskGuiClose() {
		OnMessage(0x201, "")
	}
{/Script}

SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

Post by SundayProgrammer » 23 Jan 2022, 02:28

what's new in this version:

added a statistical aid which includes:
- a "frequently used" menu buttons section
- a "recently used" menu buttons section
- a menu button to toggle this feature on/off

here is the toggle.

Code: Select all

Toggle s.Aid	Toggle Statistical Aid	(Script)	a.Call("var", "Set:menu.StatisticAid", a.Call("var", "menu.StatisticAid") ? False : True)|| ||a.Call("menu.SpotMenu", a.Call("var", "_menu_@pool"))

here is the updated script.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

$RButton::keyRButton()
$RButton up::Return
#IfWinActive, Select A Batch Please
	Space::
	Enter::
	`::menu.lsPick()
	~Down::
	~Up::menu.DownUp := A_TickCount
#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	If menu.mSpotExist()
	{	WinGetPos, x, y,,, mSpot
		If (x > A_ScreenWidth - 50)
			x := A_ScreenWidth - 100
		If (y > A_ScreenHeight - 125)
			y := A_ScreenHeight - 175
		Gui, mSpot:Show, x%x% y%y% NoActivate
	}
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	If not StrLen(this.Spots)
		this.Spots := this.sgnHistory := "Spots"
	If not StrLen(this.Color)
		this.Color := this.fscHistory := "B2D6F3"
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos) or (hwnd = "mSpot")
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd) or (hwnd = "mSpot")
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	If this.StatisticAid
	{	FileRead, aFrq, MenuLog.txt
		uFrq := RegExReplace(aFrq, "m)^\d{14}\t")
		Sort, uFrq, U
		Loop, Parse, uFrq, `n
			If StrLen(A_LoopField)
				StrReplace(aFrq, A_LoopField,, lc), ruFrq .= SubStr(100000 + lc, 2) "`t" A_LoopField "`n"
		Sort, ruFrq, R
		ruFrq := RegExReplace(ruFrq, "m)^\d{5}\t"), StrReplace(ruFrq, "`n",, lc), Frq := "Frequently Used :-`n" (lc > 20 ? SubStr(ruFrq, 1, InStr(ruFrq, "`n",,, 20)) : ruFrq)
		StrReplace(aFrq, "`n",, lc), L100 := lc > 100 ? SubStr(aFrq, InStr(aFrq, "`n",,, lc - 100) + 1) : aFrq
		If lc > 200
		{	FileDelete, MenuLog.txt
			FileAppend, %L100%, MenuLog.txt, UTF-8
		}ReverseByLine(L100)
		uL100 := RegExReplace(L100, "m)^\d{14}\t")
		Sort, uL100, U
		Loop, Parse, uL100, `n
			If StrLen(A_LoopField)
				ruL100 .= SubStr(L100, InStr(L100, A_LoopField) - 15, 15) A_LoopField "`n"
		Sort, ruL100, R
		ruL100 := RegExReplace(ruL100, "m)^\d{14}\t"), StrReplace(ruL100, "`n",, lc), L20 := "Recently Used :-`n" (lc > 20 ? SubStr(ruL100, 1, InStr(ruL100, "`n",,, 20)) : ruL100)
		t := Frq L20 t
	}Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	If this.CondiPassed(m, sWin, sCtl)
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			Else Continue
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If this.CondiPassed(A_LoopField, sWin, sCtl)
		{	aLoopField := Trim(RegExReplace(A_LoopField, "\s*\[([^\]]+)]"))
			Gui, Add, Text, x25 cWhite, % SubStr(aLoopField, 0) = "`r" ? SubStr(aLoopField, 1, -1) : aLoopField
			nl := True
		}Else Continue
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
	SendMessage, 0x0115, 7, 0,, A	;WM_VSCROLL
	SendMessage, 0x0115, 6, 0,, A	;WM_VSCROLL
}
CondiPassed(m, sWin, sCtl) {
	If RegExMatch(m, "\s*\[([^\]]+)]", mm)
	If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
	If pos := InStr(mm1, ":")
		Return RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) and RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
	Else Return RegExMatch(sWin, Trim(mm1))
	Return True
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	If this.mSpotExist()
		Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
mSpotExist() {
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	MenuSpotExists := WinExist("mSpot")
	DetectHiddenWindows, %dhw%
	Return MenuSpotExists
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	this.do(theText, TT.GetText(h))
	If this.mSpotExist()
		Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
do(theText, t) {
	If this.StatisticAid
	{	tt := StrReplace(t, "`n", "`t")
		FileAppend, %A_Now%`t%theText%`t%tt%`n, MenuLog.txt, UTF-8
	}If RegExMatch(t, "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") this.HandleInclude(mm1),, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") this.HandleInclude(m),, A_AhkPath)
}
HandleInclude(s) {
	If RegExMatch(s, "is)#\s*Include\s+[^#]+#")
	{	RegExReplace(s, "is)#\s*Include\s+[^#]+#",, n), pos := 1, m := ""
		Loop, %n%
			pos := RegExMatch(s, "is)#\s*Include\s+\K[^#]+(?=#)", m, pos + StrLen(m)), RegExMatch(this.t, "is){\s*Script\s+" Trim(m) "\s*}(.+?){\s*/\s*Script\s*}", mm), s := RegExReplace(s, "is)#\s*Include\s+" Trim(m) "\s*#", RegExMatch(mm1, "is)#\s*Include\s+[^#]+#") ? this.HandleInclude(mm1) : mm1)
	}
	Return s
}
ExtraSpot() {
	btn := "B" A_TickCount
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	ControlGetText, m, %moc%, %mow%
	MouseGetPos,,,, h, 2
	t := agent.Call("TT.GetText", h)
	menu.Gui_OnEscape()
	Color := this.Color, gName := this.Spots
	s := (A_ScreenHeight > A_ScreenWidth) ? "◆|◼|◉|◈'120|▣|◙|▲|▼|◀|▶|◢|◥|◣|◤|➤'95|●|●'150|🛑'70|☗'115|⛊'95|⛘'95|❦'130|☙'105|🖤'70|➽'95|🡅|🡇|🡄|🡆|♝'100|♥'130|♠'130|♦'130|♣'130|♜'100|♞'100|⚈'115|⚉'115|⛇'100|☻'130|✅'70|⚑'130|★'115|✦'115|✹|✸|✷|✶'150|✱|🎩'70|🚚'70|🍩'70|⛟'95" : "◙|◆|◼|◉|◈'120|▣|◀|▼|▶|▲|➤'95|➽'95|◤|◣|⛘'95|◥|◢|☗'115|⛊'95|●|●'150|🛑'70|♝'100|♜'100|♞'100|🡅|🡇|🡄|🡆|🎩'70|🍩'70|✶'150|🖤'70|❦'130|☙'105|♥'130|♠'130|♦'130|♣'130|☻'130|🚚'70|⛇'100|⛟'95|✅'70|⚈'115|⚉'115|⚑'130|★'115|✦'115|✹|✸|✷|✱", this.Shapes := s
	Gui, ps:New, +LastFound +AlwaysOnTop +Labelmenu.psOn
	Loop, Parse, s, |
	{	If (A_ScreenHeight > A_ScreenWidth)
			x := 20 + 130 * Mod(A_Index - 1, 6) , y := -40 + 140 * ((A_Index - 1) // 6)
		Else x := 5 + 115 * Mod(A_Index - 1, 11) , y := -10 + 140 * ((A_Index - 1) // 11)
		If RegExMatch(A_LoopField, "^([^']+)'(\d+)$", ss)
		{	Gui, Font, s%ss2% c%Color%, Consolas
			Gui, Add, Text, x%x% y%y% BackgroundTrans, %ss1%
		}Else
		{	Gui, Font, s110 c%Color%, Consolas
			Gui, Add, Text, x%x% y%y% BackgroundTrans, %A_LoopField%
		}OnSuch := this.Pick.Bind(this)
		GuiControl +g, Static%A_Index%, % OnSuch
	}Gui, Color, Black
	Gui, Show,, Select A Shape Please
	;Progress, zh0 w400 c10 fs18, `nSelect A Shape Please`n,, Hit [Space] or [Enter] to continue OR it will go on in 5 seconds, Segoe UI
	;MsgBox,,,, 5
	;Progress, Off
	WinSet, Transparent, 64
	Gui, cp:New, +LastFound +AlwaysOnTop +Labelmenu.cpOn
	Gui, Margin, 20, 20
	Gui, Add, Picture, x25 y25 w372 h238, ColorPal.png
	OnSuch := this.PickColor.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, +Delimiter`n
	Gui, Font, s30, Consolas
	Gui, Add, Text, cWhite, Color Code
	Gui, Add, ComboBox, w372 hwndHEdit1
	GuiControl,, ComboBox1, % "`n" this.fscHistory
	OnSuch := this.EditColor.Bind(this)
	GuiControl +g, ComboBox1, % OnSuch
	ControlSetText,, %Color%, ahk_id %HEdit1%
	Gui, Add, Text, cWhite, Group Name
	Gui, Add, ComboBox, w372 hwndHEdit2
	GuiControl,, ComboBox2, % "`n" this.sgnHistory
	OnSuch := this.EditSpots.Bind(this)
	GuiControl +g, ComboBox2, % OnSuch
	ControlSetText,, %gName%, ahk_id %HEdit2%
	Gui, Color, Black
	Gui, Show,, Color Palette
	var("Set:Shape@pool", False), this.CurItem := "●"
	While, not var("Shape@pool") and WinExist("Select A Shape Please")
		Sleep, 100
	If not var("Shape@pool")
		var("Set:Shape@pool", this.CurItem)
	Gui, %btn%:New, hwndHspot
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Color := this.Color
	shape := var("Shape@pool")
	If RegExMatch(shape, "^([^']+)'(\d+)$", ss)
	{	Gui, Font, s%ss2%, Consolas
		Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %ss1%
	}Else
	{	Gui, Font, s110, Consolas
		Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %shape%
	}OnSuch := this.xHit.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, Add, Text, Hidden, %m%
	Gui, Add, Text, Hidden, %t%
	Gui, Add, Text, Hidden, % this.Spots
	Gui, Add, Text, Hidden, %Color%
	If RegExMatch(shape, "^[^']+'\d+$")
		Gui, Add, Text, Hidden, %ss2%
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100
	Gui, Show, x%x% NoActivate, %btn%spot
	GroupAdd, % this.Spots, % "ahk_id " . WinExist()
	this.wt := btn "spot"
	ControlGet, sid, hwnd,, Static1, %btn%spot
	this.xHit(sid + 0) ; Init sPos
}
xHit(hwnd := "") {
	static sPos := []
	If SubStr(hwnd, -3) = "spot"
	{	wt := hwnd
		WinGetPos, x, y,,, %wt%
		ControlGet, hwnd, hwnd,, Static1, %wt%
		sPos[hwnd + 0] := x "," y
		Return
	}Else If not StrLen(sPos[hwnd])
	{	WinGetPos, x, y,,, % this.wt
		sPos[hwnd] := x "," y
		Return
	}SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	WinGetActiveTitle, wt
	var("Set:wName@pool", wName := SubStr(wt, 1, -4))
	If x "," y = sPos[hwnd]
	{	GuiControlGet, theText,, Static2
		GuiControlGet, t,, Static3
		Gui, %wName%:Hide
		menu.do(theText, t)
	}Else sPos[hwnd] := x "," y
	SetTimer, DeactivateIt, -500
	Return
	DeactivateIt:
		wName := var("wName@pool")
		Gui, %wName%:Hide
		Gui, %wName%:Show, NoActivate
		Return
}
Pick(hwnd := "") {
	WinGet, t, ControlListHwnd, A
	StrReplace(SubStr(t, 1, InStr(t, Format("0x{:x}", hwnd))), "`n",, c)
	Choice := RegExReplace(SubStr(this.Shapes, InStr(this.Shapes, "|",,, c) + 1), "[^|]+\K|.+$")
	var("Set:Shape@pool", Choice)
	IfWinExist, Color Palette
		this.cpOnClose()
	this.psOnClose()
}
PickColor() {
	MouseGetPos, x, y
	PixelGetColor, Color, x, y, RGB
	Color := SubStr(Color, -5)
	ControlGet, HEdit, hwnd,, Edit1, A
	ControlSetText,, %Color%, ahk_id %HEdit%
	this.ColorFill(Color)
}
ColorFill(Color) {
	s := this.Shapes
	Loop, Parse, s, |
	{	If RegExMatch(A_LoopField, "^([^']+)'(\d+)$", ss)
			Gui, ps:Font, s%ss2% c%Color%, Consolas
		Else Gui, ps:Font, s110 c%Color%, Consolas
		GuiControl, ps:Font, Static%A_Index%
	}
}
EditColor() {
	static vTemp := ""
	GuiControlGet, v,, ComboBox1
	ControlGet, HEdit, hwnd,, Edit1, A
	DllCall("User32\SendMessage", "Ptr", HEdit, "UInt", 0xB0, "UIntP", slcs, "UIntP", slce, "Ptr")
	If (slce = slcs) and ((k := SubStr(v, slce, 1)) = "``" or (k = A_Space))
	{	Color := SubStr(v, 1, slce - 1)
		If (slce < StrLen(v))
			Color .= SubStr(v, slce + 1)
		ControlSetText,, %Color%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
		If StrLen(Color)
			this.ColorFill(Color)
	}Else If (slce > slcs)
		SendMessage, 0xB1, slce, slce,, ahk_id %HEdit%	;EM_SETSEL
	If RegExMatch(v, "\W")
	{	ControlSetText,, %vTemp%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
	}Else vTemp := v
}
EditSpots() {
	GuiControlGet, v,, ComboBox2
	ControlGet, HEdit, hwnd,, Edit2, A
	DllCall("User32\SendMessage", "Ptr", HEdit, "UInt", 0xB0, "UIntP", slcs, "UIntP", slce, "Ptr")
	If (slce = slcs) and RegExMatch(SubStr(v, slce, 1), "\W")
	{	gName := SubStr(v, 1, slce - 1)
		If (slce < StrLen(v))
			gName .= SubStr(v, slce + 1)
		ControlSetText,, %gName%, ahk_id %HEdit%
		SendMessage, 0xB1, slcs - 1, slce - 1,, ahk_id %HEdit%	;EM_SETSEL
	}Else If (slce > slcs)
		SendMessage, 0xB1, slce, slce,, ahk_id %HEdit%	;EM_SETSEL
}
cpOnEscape() {
	menu.cpOnClose()
}
cpOnClose() {
	ControlGetText, Color, Edit1
	If StrLen(Color)
	{	menu.Color := Color
		If not StrLen(menu.fscHistory)
			menu.fscHistory := Color
		Else If not RegExMatch(menu.fscHistory, "im`a)^\Q" Color "\E$")
			menu.fscHistory := Color "`n" menu.fscHistory
	}ControlGetText, gName, Edit2
	If StrLen(gName)
	{	menu.Spots := gName
		If not StrLen(menu.sgnHistory)
			menu.sgnHistory := gName
		Else If not RegExMatch(menu.sgnHistory, "im`a)^\Q" gName "\E$")
			menu.sgnHistory := gName "`n" menu.sgnHistory
	}Gui, cp:Destroy
}
psOnEscape() {
	menu.psOnClose()
}
psOnClose() {
	IfWinExist, Color Palette
		menu.cpOnClose()
	Gui, ps:Destroy
}
Flick() {
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	tmm := A_TitleMatchMode
	SetTitleMatchMode, RegEx
	WinGet, wList, List, [sS]pot$
	Loop, % wList
	{	hwnd := wList%A_Index%
		WinGetTitle, wt, ahk_id %hwnd%
		If RegExMatch(wt, "^B\d+spot$") or wt = "mSpot"
		{	WinGetPos, x,,,, ahk_id %hwnd%
			WinMove, ahk_id %hwnd%,, (A_ScreenWidth - 110) - x
			If wt = mSpot
				this.HitEvent(wt)
			Else this.xHit(wt)
		}
	}SetTitleMatchMode, %tmm%
	DetectHiddenWindows, %dhw%
}
Append() {
	t := "`n`n" this.Save
	FileAppend, %t%, MenuText.txt, UTF-8
	MsgBox, Saved
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
}
LoadSpots() {
	t := var("_menu_@pool")
	tt := RegExReplace(RegExReplace(t, "im`a)^[ \t]*(?!{SpotsList@\w+}).*\R?"), "{SpotsList@(\w+)}", "$1"), RegExReplace(tt, "im`a)^[ \t]*\w+[ \t]*$",, mCount)
	If mCount > 1
	{	ReverseByLine(tt)
		Gui, losp:New, +ToolWindow +Delimiter`n
		Gui, Font, s30, Consolas
		Gui, Add, ListBox, r15, %tt%
		OnSuch := this.lsPick.Bind(this)
		GuiControl +g, ListBox1, % OnSuch
		Gui, Show,, Select A Batch Please
		this.Batch := False, RegExMatch(tt, "\w+", m), this.CurItem := m
		While, not this.Batch and WinExist("Select A Batch Please")
			Sleep, 100
		If not this.Batch
			this.Batch := this.CurItem
		Gui, losp:Destroy
	}Else RegExMatch(tt, "\w+", m), this.Batch := m
	RegExMatch(this.Batch, "\w+", b), RegExMatch(t, "is){SpotsList@" b "}\K.+?(?={/SpotsList})", m)
	Loop, Parse, m, `n
	{	LoopField := SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
		If not RegExMatch(LoopField, "[^\-\s]")
			If StrLen(wi)
			{	If (wt = "mSpot") {
Gui, mSpot:New
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
Gui, Font, s150, Consolas
Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
OnSuch := this.HitEvent.Bind(this)
GuiControl +g, Static1, % OnSuch
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, mSpot
this.HitEvent(wt) ; Init sPos
OnMessage(0x7E, "WM_DISPLAYCHANGE")
OnMessage(0x06, "WM_ACTIVATE")
wt := x := y := wi := ""
				}Else{
btn := SubStr(wt, 1, -4)
Gui, %btn%:New, hwndHspot
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
If RegExMatch(shape, "^([^']+)'(\d+)$", ss)
{	Gui, Font, s%ss2%, Consolas
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %ss1%
}Else
{	Gui, Font, s110, Consolas
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %shape%
}OnSuch := this.xHit.Bind(this)
GuiControl +g, Static1, % OnSuch
Gui, Add, Text, Hidden, %blbl%
Gui, Add, Text, Hidden, %ttip%
Gui, Add, Text, Hidden, %gname%
Gui, Add, Text, Hidden, %Color%
If RegExMatch(shape, "^[^']+'\d+$")
	Gui, Add, Text, Hidden, %ss2%
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, %wt%
GroupAdd, %gname%, % "ahk_id " . WinExist()
this.wt := wt
ControlGet, sid, hwnd,, Static1, %wt%
this.xHit(sid) ; Init sPos
wt := x := y := shape := blbl := ttip := gname := color := wi := ""
				}
			}Else{}
		Else If RegExMatch(LoopField, "Title\t= \K.*$", mm)
			wt := mm, wi := mm
		Else If RegExMatch(LoopField, "Pos\t= \K.*$", mm)
			RegExMatch(mm, ".*(?=,)", x), RegExMatch(mm, ",\K.*$", y), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text1\t= \K.*$", mm)
			shape := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text2\t= \K.*$", mm)
			blbl := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text3\t= \K.*$", mm)
			ttip := StrReplace(mm, "`t", "`n"), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text4\t= \K.*$", mm)
			gname := mm, wi .= "`n" mm, glist .= "`n" mm
		Else If RegExMatch(LoopField, "Text5\t= \K.*$", mm)
			color := mm, wi .= "`n" mm, clist .= "`n" mm
	}v := this.sgnHistory glist
	Sort, v, U
	this.sgnHistory := v, v := this.fscHistory clist
	Sort, v, U
	this.fscHistory := v
}
lsPick(hwnd := "") {
	GuiControlGet, Choice,, ListBox1
	If StrLen(Choice)
	{	this.CurItem := Choice
		If A_TickCount - (StrLen(this.DownUp) ? this.DownUp : 0) < 100
			Return
	}Else If A_ThisHotkey in Space,$Enter,``
		Choice := this.CurItem
	this.Batch := Choice
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

keyRButton() {
	MouseGetPos, xb4, yb4, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	beg := A_TickCount
	While, GetKeyState("RButton", "P")
	{	MouseGetPos, x, y
		If SubStr(mow, -3) = "Spot" and moc = "Static1" and (Abs(x - xb4) > 5 or Abs(y - yb4) > 5)
		{	If mow = mSpot
				menu.Flick()
			Else
			{	WinGetPos, x,,,, %mow%
				WinMove, %mow%,, (A_ScreenWidth - 110) - x
				menu.xHit(mow)
			}beg := False
			Break
		}Else If A_TickCount - beg > 1000
		{	IfWinNotExist, Touch Friendly Menu
			IfWinExist, mSpot
				If (mow = "mSpot") and (moc = "Static1")
				{	Gui, mSpot:Destroy
					beg := False
				}Else{}
			Else menu.Spot(), menu.HitEvent("mSpot"), beg := False
			SoundBeep
			Break
		}
	}If not beg
		Return
	If SubStr(mow, -3) = "Spot" and moc = "Static1"
		If mow = mSpot
		{	Spots := menu.Spots
			WinGet, c, Count, ahk_group %Spots%
			dhw := A_DetectHiddenWindows
			DetectHiddenWindows, On
			WinGet, ca, Count, ahk_group %Spots%
			DetectHiddenWindows, %dhw%
			If (ca > c)
			{	aw := WinExist("A")
				WinShow, ahk_group %Spots%
				WinActivate, ahk_id %aw%
				WinWaitActive
			}Else If ca
				WinHide, ahk_group %Spots%
			Else Gui, mSpot:Destroy
		}Else ;WinClose, %mow%
		{	GuiName := SubStr(mow, 1, -4)
			Gui, %GuiName%:Destroy
		}
	Else If mow = Touch Friendly Menu
		If SubStr(moc, 1, 6) = "Button"
			menu.ExtraSpot()
		Else{}
	Else Send, {RButton}
}

ReverseByLine(ByRef text, delimiter := "`n") {	;written by SundayProgrammer
	array := StrSplit(SubStr(text, 0) = delimiter ? SubStr(text, 1, StrLen(text) - 1) : text, delimiter), mi := array.MaxIndex()
	Loop, % mi
		If A_Index > 1
			text .= array[mi - A_Index + 1] delimiter
		Else text := array[mi] delimiter
}

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
that's it for now.

SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

Post by SundayProgrammer » 31 Jan 2022, 00:17

sometimes i would like to move some windows to the other virtual desktop so that my main workspace becomes cleaner in pursuing the current task.

here is the script to move the active window to the other virtual desktop.

Code: Select all

vMove	{Move It To The Other Virtual Desktop}	(Script)
;
{Script Move It To The Other Virtual Desktop}
	Send, #{Tab}
	WinWaitActive, Task View
	Sleep, 1000	;Allow some time for it to ready itself
	Send, {AppsKey}
	If StageChecked(0)
	{	Send, {Down}
		If StageChecked(1)
		{	Send, {Down}
			If StageChecked(2)
			{	Send, {Right}
				If StageChecked(3)
				{	Send, {Enter}
					If StageChecked(4)
						Send, {Esc}
				}
			}
		}
	}
	ExitApp
	StageChecked(Stage) {
		static r
		beg := A_TickCount
		If Stage = 0
			Loop
				If timeout := TimeExceeded(beg)
					Break
			Until r := ScanBlock(200, 200, 0, 200)
		Else If Stage between 1 and 2
			Loop
				PixelGetColor, c, r.x, r.y + Stage * 34
			Until (c = 0xFFFFFF) or timeout := TimeExceeded(beg)
		Else If Stage = 3
			Loop
				PixelGetColor, c, r.x + 300, r.y + (Stage - 1) * 34 + 5
			Until (c = 0xFFFFFF) or timeout := TimeExceeded(beg)
		Else If Stage = 4
			Loop
				PixelGetColor, c, r.x + 300, r.y + (Stage - 2) * 34 + 5
			Until not (c = 0xFFFFFF) or timeout := TimeExceeded(beg)
		If timeout
		{	MsgBox, Oops...`, Stage %Stage% Timeout
			Return False
		}Else Return True
	}
	TimeExceeded(beg) {
		Return (A_TickCount - beg > 5000)
	}
	ScanBlock(x, y, w, h) {
			PixelSearch, xx, yy, x, y, x + w, y + h, 0xFFFFFF,, Fast
		HandleResult:
			If ErrorLevel
				Return False
			Else Goto, Verify
		Verify:
			If not AllMatched(xx, yy)
			{	sx := xx, sy := yy
				If (sx < x + w)
				{	PixelSearch, xx, yy, sx + 1, sy, x + w, sy, 0xFFFFFF,, Fast
					If ErrorLevel
						Goto, SubVerify
					Else Goto, Verify
				}Else Goto, SubVerify
			}Return {"x":xx, "y":yy}
		SubVerify:
			If (sy < y + h)
			{	PixelSearch, xx, yy, x, sy + 1, x + w, y + h, 0xFFFFFF,, Fast
				Goto, HandleResult
			}Else Return False
	}
	AllMatched(x, y) {
		PixelGetColor, c, x + 1, y
		If c = 0xFFFFFF
		{	PixelGetColor, c, x + 2, y
			If c = 0xFFFFFF
			{	PixelGetColor, c, x + 50, y
				If c = 0xFFFFFF
				{	PixelGetColor, c, x + 100, y
					If c = 0xFFFFFF
					{	PixelGetColor, c, x + 200, y
						If c = 0xFFFFFF
							Return True
					}
				}
			}
		}Return False
	}
{/Script}
edited: adjusted a careless mistake.

Post Reply

Return to “Scripts and Functions (v1)”