[AutoHotkey v2] Link XY (track selection) to run the specified program to open the current item

What other productivity software are you working with...
Post Reply
Norn
Posts: 504
Joined: 24 Oct 2021 16:10

[AutoHotkey v2] Link XY (track selection) to run the specified program to open the current item

Post by Norn »

This script allows you to use other program in a way similar to QuickLook or Seer.
Modify the specified program and hotkey:

Code: Select all

program := "D:\Tools\BandiView\BandiView.exe"   ; Specify the path of the program to run
myHotkey := "space"                             ; Specify the toggle hotkey as Space
Note: The specified program must be set to single-instance mode.

Code: Select all

;============================
;Function : Link XY (track selection) to run the specified program to open the current item (toggle with Space key)
;Created  : 2024-05-28
;Version  : v1.6 (2026-01-15 Ken)
;============================
#Requires AutoHotkey v2
#SingleInstance Force
Persistent


program := "D:\Tools\BandiView\BandiView.exe"   ; Specify the path of the program to run
myHotkey := "space"                             ; Specify the toggle hotkey as Space
;myHotkey := "f11"


if !fileExist(program) {
	trayTip("Please modify the program path in the script!`n" program, "Program does not exist")
	exitApp
}
splitPath program, &programName


HotIfWinActive('ahk_class ThunderRT6FormDC')
Hotkey myHotkey, ViewerOpen

HotIfWinActive("ahk_exe " programName)
Hotkey myHotkey, ViewerClose


; Receive the message returned by XYplorer
OnMessage(0x4a, Receive_WM_COPYDATA)
dataReceived := "", pictureBox := "", xyWin := ""


ViewerOpen(*)
{
	isPictureBox

	if (winExist("ahk_exe " programName) && pictureBox)
		winClose("ahk_exe " programName)
	else if pictureBox {
		LinkProgram
	} else send "{" . myHotkey . "}"
}

ViewerClose(*)
{
	if winActive("ahk_exe " programName)
		winClose("ahk_exe " programName)
}

ViewerActivate()
{
	if winExist("ahk_exe " programName)
		winActivate("ahk_exe " programName)
}

#HotIf winActive(xyWin) && winExist("ahk_exe " programName)   ; When the XY window is active and the specified program window exists, track selection on the following key presses
up::LinkProgram
down::LinkProgram
left::LinkProgram
right::LinkProgram
~LButton::isPictureBox(), LinkProgram()   ; Do not alter left mouse button behavior
home::LinkProgram
end::LinkProgram
#HotIf

#HotIf winExist(xyWin) && winActive("ahk_exe " programName)   ; When the specified program window is active and the XY window exists, track selection on the following key presses
up::LinkProgram
down::LinkProgram
left::LinkProgram
right::LinkProgram
~LButton::isPictureBox(), LinkProgram()   ; Do not alter left mouse button behavior
home::LinkProgram
end::LinkProgram

#HotIf winExist(xyWin) && winExist("ahk_exe " programName)   ; When both the specified program window and the XY window exist, track selection on the following key presses
~LButton::isPictureBox(), LinkProgram()   ; Do not alter left mouse button behavior
#HotIf


LinkProgram(*) {
	static lastItem := ""

	try {
		; Send the current key to XY
		if !(A_ThisHotkey = myHotkey && myHotkey != "space") {
			CreateHoleToWindow(xyWin) ; create hole
			SendMessage(WM_ACTIVATE := 0x0006 , 1, xyWin,, xyWin)
			if !(A_ThisHotkey = myHotkey && myHotkey != "space")
				ControlSend('{' A_ThisHotkey '}', pictureBox, xyWin)   ; In details mode no control is required, but in thumbnail mode it is required; unify by always specifying
			CreateHoleToWindow() ; remove hole
		}
	}

	sleep 1
	if item := XY_Get() {
		if !(item = lastItem && winExist("ahk_exe " programName))
			try {
				run '"' program '" "' item '"'
				lastItem := item
				setTimer(ViewerActivate.bind(), -1000)
			} catch
				trayTip("Please modify the program path in the script!`n" program, "Program does not exist")
    }
}


isPictureBox() {
	pictureBox := ""
	if A_ThisHotkey = "~LButton" {
		mouseGetPos ,, &win, &ctrl, 2
		if winGetClass(win) != "ThunderRT6FormDC"
			return
		focusedCtrl := ctrl
		xyWin := win
	} else {
		xyWin := winActive('ahk_class ThunderRT6FormDC')
		focusedCtrl := ControlGetFocus(xyWin)
	}
	try pictureBox := RegExMatch(ControlGetClassNN(focusedCtrl, xyWin), "^ThunderRT6PictureBoxDC\d+$") ? focusedCtrl : ""
	global pictureBox, xyWin
}



XY_Get()
{
    xyQueryScript := '::$return = <curitem>`; copydata ' A_ScriptHwnd ', "$return", 2`;'

    if xyHwnd := GetXYHWND()
        Send_WM_COPYDATA(xyHwnd, xyQueryScript)
	else return

	global dataReceived
	XYdataReceived := dataReceived
	dataReceived := ""

    return XYdataReceived
}


GetXYHWND() {
    static xyClass := 'ahk_class ThunderRT6FormDC'

    if WinExist(xyClass) {
        for xyid in WinGetList(xyClass) {
			focusedCtrl := ""
			try focusedCtrl := ControlGetClassNN(ControlGetFocus(xyid))
            if (WinGetControls(xyid).length > 10 && focusedCtrl != "ThunderRT6PictureBoxDC3" && RegExMatch(focusedCtrl, "^ThunderRT6PictureBoxDC\d+$"))
                return xyid
		}
    }
}


Send_WM_COPYDATA(xyHwnd, message) {
	size := StrLen(message) * 2                                  ; UTF-16 byte size

	COPYDATA := Buffer(A_PtrSize * 3)                            ; Allocate COPYDATASTRUCT
	NumPut("Ptr", 4194305, COPYDATA, 0)                          ; dwData: application-defined identifier
	NumPut("UInt", size, COPYDATA, A_PtrSize)                    ; cbData: data size in bytes
	NumPut("Ptr", StrPtr(message), COPYDATA, A_PtrSize * 2)      ; lpData: pointer to UTF-16 string

	return DllCall("User32.dll\SendMessageTimeout", "Ptr", xyHwnd, "UInt", 74, "Ptr", 0, "Ptr", COPYDATA, "UInt", 2, "UInt", TimeOut:=5000, "PtrP", Result:=0, "Ptr")
}


Receive_WM_COPYDATA(wParam, lParam, *) {
    global dataReceived := StrGet(
        NumGet(lParam + 2 * A_PtrSize, 'Ptr'),   ; COPYDATASTRUCT.lpData, ptr to a str presumably
        NumGet(lParam + A_PtrSize, 'UInt') / 2   ; COPYDATASTRUCT.cbData, count bytes of lpData, /2 to get count chars in unicode str
    )
	return true
}



; https://www.autohotkey.com/boards/viewtopic.php?p=576829#p576829
CreateHoleToWindow(WinTitle := "") {
    static holed := []
    if !WinTitle && holed.Length {
        for hWnd in holed
            WinSetRegion(, hWnd)
        holed := []
        return
    }
    if IsWindowPartiallyVisible(WinTitle)
        return
    local hWnd := IsInteger(WinTitle) ? WinTitle : WinExist(WinTitle)
    WinGetPos(&X, &Y, &W, &H, hWnd), mX := X + W//2, mY := Y + H//2
    while (thWnd := WindowFromPoint(mX, mY)) != hWnd {
        WinGetPos(&tX, &tY, &tW, &tH, thWnd)
        rX := mX - tX, rY := mY - tY
        WinSetRegion("0-0 0-" tH " " tW "-" tH " " tW "-0 0-0 " rX "-" rY " " rX "-" (rY+1) " " (rX+1) "-" (rY+1) " " (rX+1) "-" rY " " rX "-" rY, thWnd)
        holed.Push(thWnd)
    }
}

IsWindowPartiallyVisible(WinTitle:="") { 
    local hWnd := IsInteger(WinTitle) ? WinTitle : WinExist(WinTitle)
    WinGetPos(&X, &Y, &W, &H, hWnd)
    return (hWnd == WindowFromPoint(X, Y)) || (hwnd == WindowFromPoint(X, Y+H-1)) || (hwnd == WindowFromPoint(X+W-1, Y)) || (hwnd == WindowFromPoint(X+W-1, Y+H-1))
}

WindowFromPoint(X, Y?) { ; by SKAN and Linear Spoon
    return DllCall("GetAncestor", "Ptr", DllCall("user32.dll\WindowFromPoint", "Int64", IsSet(Y) ? (Y << 32 | (X & 0xFFFFFFFF)) : X), "UInt", 2, "Ptr")
}
Windows 11 24H2 @100% 2560x1440

Post Reply