-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathui-editor.ahk
238 lines (215 loc) · 8.15 KB
/
ui-editor.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
; This script shows a GUI for setting the default .ahk editor.
#requires AutoHotkey v2.0
#NoTrayIcon
#SingleInstance Off
#include launcher.ahk
#include inc\CommandLineToArgs.ahk
class EditorSelectionGui extends AutoHotkeyUxGui {
__new(cmdLine) {
super.__new("Select an editor")
lv := this.AddListMenu('vEds LV0x40 w300', ["Editor"])
this.IconList := il := IL_Create(,, true)
lv.SetImageList(il, 0)
for app in this.Apps := GetEditorApps() {
try
icon := IL_Add(il, app.exe)
catch
icon := -1
lv.Add('Icon' icon, app.name)
}
this.SelectEditorByCmd(cmdLine)
lv.AutoSize(8)
lv.GetPos(&x, &y, &w, &h)
x += w
y += h
this.AddText('xm w' w ' y' y, "Command line")
this.AddEdit('xm wp r2 -WantReturn vCmd', cmdLine).OnEvent('Change', 'CmdChanged')
this.AddText('xm h25 18 w' w)
this.AddPicture('x56 Icon-81 w16 yp+4', "imageres.dll")
this.AddLink('x76 yp-1', "<a>Editors with AutoHotkey support</a>")
.OnEvent('Click', 'ShowHelpEditors')
this.AddButton('xm w80', "&Browse").OnEvent('Click', 'Browse')
this.AddButton('Default yp w80 x' x - 160 - this.MarginY, "&OK").OnEvent('Click', 'Confirm')
this.AddButton('yp w80 x' x - 80, "&Cancel").OnEvent('Click', (c, *) => c.Gui.Hide())
this["Eds"].OnEvent("ItemFocus", "EditorSelected")
this.CmdChanged()
}
ShowHelpEditors(*) {
if FileExist(chm := ROOT_DIR '\v2\AutoHotkey.chm')
Run 'hh.exe "ms-its:' chm '::docs/misc/Editors.htm"'
else
Run 'https://www.autohotkey.com/docs/v2/misc/Editors.htm'
}
Browse(*) {
app := this.FileSelect(3,,, "Apps (*.exe; *.ahk)")
if app = ""
return
this['Cmd'].Value := this.GetAppCmd(app)
this.CmdChanged()
}
GetAppCmd(app) {
SplitPath app,,, &ext
if ext != "ahk"
return Format('"{1}" "%l"', app)
lp := GetLaunchParameters(app, true)
if !lp.exe
return ""
; Try to use a path that will work if the user installs a new version and removes this one
; (rather than invoking the launcher every time the edit verb is executed).
adaptivePath := RegExReplace(lp.exe.Path, lp.v = 1 ? '\\v1[^\\]*(?=\\[^\\]*$)' : '\\v2\K[^\\]+(?=\\[^\\]*$)')
trace '![Launcher] ' adaptivePath
try
adaptiveExe := GetExeInfo(adaptivePath)
if !IsSet(adaptiveExe) || VerCompare(adaptiveExe.Version, lp.v) < 0
adaptivePath := ""
cmd := Format('"{}"', FileExist(adaptivePath) ? adaptivePath : lp.exe.Path)
for sw in lp.switches
cmd .= ' ' sw
return cmd .= Format(' "{}" "%l"', app)
}
EditorSelected(lv, index) {
this['Cmd'].Value := this.Apps[index].cmd
this.CmdChanged()
}
CmdChanged(ctrl:=unset, *) {
if IsSet(ctrl) && n := this['Eds'].GetNext()
this['Eds'].Modify(n, '-Select')
cmd := this['Cmd'].Value
this['OK'].Enabled := cmd != "" && FindExecutable(CommandLineToArgs(cmd)[1]) != ""
}
SelectEditorByCmd(cmd) {
if cmd = ""
return
for app in this.Apps {
if app.cmd = cmd {
this['Eds'].Modify(A_Index, 'Focus Select')
return
}
}
; App not in the list, so add it.
args := CommandLineToArgs(cmd)
try {
exe := GetExeInfo(FindExecutable(args.RemoveAt(1)))
app := {cmd: cmd, exe: exe.Path, name: exe.Description}
if SubStr(app.name, 1, 10) = "AutoHotkey" && (ahk := ahkArg(args)) {
; The app itself appears to be a script, so show the script name.
app.name := ahk
}
this.Apps.Push(app)
try
icon := IL_Add(this.IconList, app.exe, IsSet(ahk) && ahk ? 2 : 1)
catch
icon := -1
this['Eds'].Add('Focus Select Icon' icon, app.name)
}
ahkArg(args) {
for arg in args {
if SubStr(arg, 1, 1) = '/'
continue
return SubStr(arg, -4) = '.ahk' ? arg : ''
}
return ''
}
}
Confirm(*) {
this.Hide()
this.OnConfirm(this['Cmd'].Value)
}
}
class DefaultEditorGui extends EditorSelectionGui {
__new(scriptToEdit:=unset) {
cmd := RegRead('HKCR\AutoHotkeyScript\shell\edit\command',, '')
if InStr(cmd, A_LineFile)
cmd := ''
super.__new(cmd)
if IsSet(scriptToEdit)
this.ScriptToEdit := scriptToEdit
}
OnConfirm(cmd) {
RegWrite(cmd, 'REG_SZ', 'HKCU\Software\Classes\AutoHotkeyScript\shell\edit\command')
if this.HasProp('ScriptToEdit')
Run('edit "' this.ScriptToEdit '"')
}
}
GetEditorApps() {
apps := []
apps.byName := Map(), apps.byName.CaseSense := 'off'
addAssoc(assoc, flag, src) {
static ASSOCSTR_COMMAND := 1
static ASSOCSTR_EXECUTABLE := 2
static ASSOCSTR_FRIENDLYAPPNAME := 4
try {
name := AssocQueryString(flag, ASSOCSTR_FRIENDLYAPPNAME, assoc)
exe := AssocQueryString(flag, ASSOCSTR_EXECUTABLE, assoc)
}
if !IsSet(exe) || name ~= 'i)^AutoHotkey|^Ahk2Exe'
return
try
cmd := AssocQueryString(flag, ASSOCSTR_COMMAND, assoc)
catch
cmd := '"' exe '" "%l"'
else if cmd = ""
return
if !(InStr(cmd, "%1") || InStr(cmd, "%l"))
cmd .= ' "%l"'
if name = "NOTEPAD.EXE"
name := "Notepad"
if apps.byName.Has(name)
return
app := {cmd: cmd, exe: exe, name: name}
apps.byName[name] := app
apps.Push(app)
}
addAppExe(app, src) {
static ASSOCF_INIT_BYEXENAME := 2 ; 0x2
addAssoc app, ASSOCF_INIT_BYEXENAME, src
}
addProgId(app, src) {
addAssoc app, 0, src
}
for ext in ["ahk", "txt"] {
owl := "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\." ext "\OpenWithList"
mru := RegRead(owl, "MRUList", "")
Loop Parse mru {
if app := RegRead(owl, A_LoopField, "")
addAppExe app, "Explorer\." ext "\OWL"
}
Loop Reg "HKCR\." ext "\OpenWithProgIds", "V" {
addProgId A_LoopRegName, "HKCR\." ext "\OWPI"
}
Loop Reg "HKCR\." ext "\OpenWithList", "K" {
if !InStr(A_LoopRegName, "Ahk2Exe")
addAppExe A_LoopRegName, "HKCR\." ext "\OWL"
}
}
addAppExe "notepad.exe", "explicit"
return apps
}
; Return the path of an executable file, if given something that could be executed
; in a shell verb (excluding args). Although shell verb commands require an exe,
; they do also look in \App Paths\, like ShellExecute, unlike CreateProcess.
FindExecutable(name) {
if SubStr(name, -4) != ".exe" ; For odd cases like 'notepad "%1"'
name .= ".exe"
if FileExist(name)
return name
if InStr(name, "\") || InStr(name, ":")
return ""
buf := Buffer(260*2)
if DllCall("shell32\FindExecutable", "str", name, "ptr", 0, "ptr", buf, "uint") > 32 ; "Returns a value greater than 32 if successful"
return StrGet(buf)
static AppPaths := '\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\'
path := RegRead('HKCU' AppPaths name,, '') || RegRead('HKLM' AppPaths name,, '')
; The system permits quotes, and some applications are registered that way.
return StrReplace(path, '"')
}
AssocQueryString(flags, strtype, assoc) {
DllCall("shlwapi\AssocQueryStringW", "int", flags, "int", strtype, "wstr", assoc
, "ptr", 0, "ptr", 0, "uint*", &bufsize := 0, "hresult")
buf := Buffer(bufsize * 2)
DllCall("shlwapi\AssocQueryStringW", "int", flags, "int", strtype, "wstr", assoc
, "ptr", 0, "ptr", buf, "uint*", &bufsize, "hresult")
return StrGet(buf, "UTF-16")
}
if A_LineFile = A_ScriptFullPath
DefaultEditorGui.Show(A_Args*)