Skip to content

Instantly share code, notes, and snippets.

@nod5
Last active February 16, 2022 06:55
Show Gist options
  • Save nod5/73d7ea232ede7ab6762833d5442fef5d to your computer and use it in GitHub Desktop.
Save nod5/73d7ea232ede7ab6762833d5442fef5d to your computer and use it in GitHub Desktop.
First attempt at hotkey tool to quickly change path in file dialogue windows (Save As, Open, ...) to that of existing Explorer windows
#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%
#SingleInstance force
ListLines, Off
SetBatchLines, -1
SetWinDelay, -1
SetControlDelay, -1
; string anywhere in title
SetTitleMatchMode, 2
; SaveAsPathHelper.ahk
; by nod5
; 2020-03-10
; -------------------------------------------------
; Ctrl+G in a standard file dialogue window ("Save As", "Save File", "Open"):
; Cycle save path to that of existing Explorer window paths
; -------------------------------------------------
; Ctrl+G in File Explorer:
; update most recent file dialogue window with Explorer path
; -------------------------------------------------
; Ctrl+O in File Explorer:
; update most recent "Open" file dialogue window with
; folder path and filename to first selected file in Explorer
; -------------------------------------------------
; Alt+Tab from file dialogue window to Explorer window and back within 5 seconds:
; update file dialogue window with Explorer path
; -------------------------------------------------
; note: add/remove window title strings in the window group as needed
; See https://www.autohotkey.com/docs/commands/GroupAdd.htm
; note: change vOpenWindow if needed in non-english locale
; -------------------------------------------------
GroupAdd, vSaveAsWindow, Save File ahk_class #32770
GroupAdd, vSaveAsWindow, Save As ahk_class #32770
GroupAdd, vSaveAsWindow, Save ahk_class #32770
GroupAdd, vSaveAsWindow, Open ahk_class #32770
vOpenWindow := "Open"
Hotkey, IfWinActive, ahk_group vSaveAsWindow
Hotkey, ^g, cycle_existing_explorer_win_paths, On
Hotkey, ~!Tab, file_dialogue_alt_tab, On
Hotkey, IfWinActive, ahk_class CabinetWClass
Hotkey, ^g, active_explorer_path_to_file_dialogue, On
Hotkey, ^o, first_selected_explorer_file_to_open_file_dialogue, On
Hotkey, IfWinActive
Return
;Esc:: ExitApp
; -------------------------------------------------
; Notes on Interacting with File Dialogue Windows in Win 10
; -------------------------------------------------
; File Dialogue windows have tricky path controls
;
; When not yet clicked/edited the control is
; ToolbarWindow324 (save) or ToolbarWindow323 (open)
; that control has clickable path crumbs: (C:\) > Windows > System32
; When clicked/edited the control becomes Edit2
; that control is a regular text edit control
; We can always read path from ToolbarWindow324 (or ToolbarWindow323)
; - ControlGetText, vPath, ToolbarWindow324, A
; -> "Adress: C:\some folder"
; We can never set path to ToolbarWindow324 (or ToolbarWindow323)
; - no effect: ControlSetText, ToolbarWindow324, C:\, A
; - no effect: ControlSetText, ToolbarWindow324, Adress: C:\, A
; We can read/write Edit2 *if* control was clicked/activated once since window creation
; - do one of:
; ControlClick, ToolbarWindow324, A (better, since no blue selection flicker)
; Send ^l
; - after that this works:
; ControlSetText, Edit2, C:\new path\somewhere, A
; ControlSend, Edit2, {Enter}, A
; -------------------------------------------------
cycle_existing_explorer_win_paths:
; get current path, example: "Address: C:\some folder"
vControlName := WinActive(vOpenWindow) ? "ToolbarWindow323" : "ToolbarWindow324"
ControlGetText, vPath, % vControlName, A
; remove prefix "Address: "
If (SubStr(vPath, 1, 9) = "Address: ")
vPath := SubStr(vPath, 10)
; get paths for all open File Explorer windows
aArray := ExplorerWindowsPaths()
; get last used used path
vIndex := 1
For Key, Val in aArray
{
If (Val != vPath) or !InStr(FileExist(vPath), "D")
continue
vIndex := A_Index
break
}
; cycle to the next path (wraps around)
vNewIndex := vIndex + 1 > aArray.Length() ? 1 : vIndex + 1
vNewPath := aArray[vNewIndex]
If InStr(FileExist(vNewPath), "D")
SetSaveAsPath(vNewPath, vOpenWindow)
Return
active_explorer_path_to_file_dialogue:
vNewPath := ActiveExplorerPath()
; activate most recently used file dialogue window and set new path
WinActivate, ahk_group vSaveAsWindow
If WinActive("ahk_group vSaveAsWindow")
SetSaveAsPath(vNewPath, vOpenWindow)
Return
first_selected_explorer_file_to_open_file_dialogue:
vFileSelected := ExplorerGetFirstSelectedFilepath()
if !FileExist(vFileSelected)
Return
; activate most recently used file dialogue window and set new path
WinActivate, ahk_group vSaveAsWindow
If WinActive(vOpenWindow " ahk_group vSaveAsWindow")
{
SplitPath, vFileSelected, vFileName, vFolder
SetSaveAsPath(vFolder, vOpenWindow)
ControlSetText, Edit1, % vFileName, A
}
Return
file_dialogue_alt_tab:
vThisFileDialogue := WinExist("A")
t := A_TickCount
vAltTabPath := ""
SetTimer, alt_tab_timer, 50
sleep 5
Return
alt_tab_timer:
If (A_TickCount > t + 5000) or !WinExist("ahk_id " vThisFileDialogue)
SetTimer, alt_tab_timer, off
Else If WinActive("ahk_class CabinetWClass")
vAltTabPath := ActiveExplorerPath()
Else If vAltTabPath and WinActive("ahk_id " vThisFileDialogue)
{
SetTimer, alt_tab_timer, off
SetSaveAsPath(vAltTabPath, vOpenWindow)
}
Return
; function: set new path in active Save As window and focus filename input control
SetSaveAsPath(vNewPath, vOpenWindow := "Open") {
; string anywhere in title
SetTitleMatchMode, 2
vControlName := WinActive(vOpenWindow) ? "ToolbarWindow323" : "ToolbarWindow324"
; note: We can read/write Edit2 *if* prior clicked/edited once since window creation
ControlGetText, vEdit2Text, Edit2, A
if !vEdit2Text
{
; Click control ToolbarWindow323/324 to activate Edit2
; note: "NA may improve reliability"
; note: click x0 y0 (control top left) because default click in control center
; and that fails if ">" path divider there
; https://www.autohotkey.com/docs/commands/ControlClick.htm
ControlClick, % vControlName, A, , , , NA x0 y0
; small sleep required
sleep 5
}
ControlGetText, vEdit2Text, Edit2, A
if !vEdit2Text
Return
ControlFocus, % vControlName, A
sleep 5
; set new path
ControlSetText, Edit2, % vNewPath, A
ControlSend, Edit2, {Enter}, A
ControlFocus, Edit1, A
sleep 5
}
; function: return array with folder paths to all existing Explorer windows
; ref https://docs.microsoft.com/en-us/windows/win32/shell/folderitem-path
ExplorerWindowsPaths() {
aExplorerPaths := []
for window in ComObjCreate("Shell.Application").Windows
{
path := window.Document.Folder.Self.Path
; skip if start with "::" (for example Control Panel window)
if (SubStr(path, 1, 2) != "::")
aExplorerPaths.Push(path)
}
Return aExplorerPaths
}
; function: get active window Explorer folder path
; based on https://www.autohotkey.com/boards/viewtopic.php?p=301644
; ref https://docs.microsoft.com/en-us/windows/win32/shell/folderitem-path
ActiveExplorerPath() {
explorerHwnd := WinActive("ahk_class CabinetWClass")
if (explorerHwnd)
for window in ComObjCreate("Shell.Application").Windows
if (window.hwnd == explorerHwnd)
{
path := window.Document.Folder.Self.Path
;if start with "::" (for example Control Panel window) then return nothing
return SubStr(path, 1, 2) = "::" ? "" : path
}
}
; function: ExplorerGetFirstSelectedFilepath(win_id := "")
; return file path of first selected file in win_id (or active) Explorer window
; note: gets items in alphanum ascending sort order
ExplorerGetFirstSelectedFilepath(win_id := "") {
win_id := win_id ? win_id : WinActive("A")
if !WinExist("ahk_class CabinetWClass ahk_id " win_id)
return
for window in ComObjCreate("Shell.Application").Windows
{
if (window.HWND != win_id )
continue
sfv := window.Document
; note: gets items in alphanum ascending sort order
; ref https://docs.microsoft.com/en-us/windows/desktop/shell/shellfolderview-selecteditems
items := sfv.SelectedItems
for item in items
{
file := item.Path
break
}
item := ""
items := ""
sfv := ""
break
}
window := ""
return file
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment