forked from 7plus/7plus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FastFolders.ahk
335 lines (318 loc) · 13.4 KB
/
FastFolders.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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#include *i %A_ScriptDir%\Navigate.ahk
#include *i %A_ScriptDir%\MiscFunctions.ahk
Class CFastFolders extends CArray
{
MinIndex()
{
return 0
}
MaxIndex()
{
return 9
}
}
ClearStoredFolder(Slot)
{
global FastFolders
;Slot+=1
FastFolders[Slot].Path := ""
FastFolders[Slot].Name := ""
RefreshFastFolders()
}
;Assigns a new folder to a FastFolder slot and updates registry
UpdateStoredFolder(Slot, Path = "")
{
global FastFolders
;Fast folder slots are 0-based externally but 1 based in the FastFolders array
;Slot += 1
if(Path)
FastFolders[Slot].Path := Path
else
{
FastFolders[Slot].Path := Navigation.GetPath()
FastFolders[Slot].Name := Navigation.GetDisplayName()
}
if(!FastFolders[Slot].Name)
{
SplitPath, Path , split
FastFolders[Slot].Name := split
}
RefreshFastFolders()
}
;Removes and re-adds all FastFolder Buttons
RefreshFastFolders()
{
if(Settings.Explorer.FastFolders.ShowInFolderBand)
RemoveAllExplorerButtons("IsFastFolderButton")
AddAllButtons(Settings.Explorer.FastFolders.ShowInFolderBand, Settings.Explorer.FastFolders.ShowInPlacesBar)
}
;Adds all FastFolder Buttons
AddAllButtons(ToFolderBand, ToPlacesBar)
{
global FastFolders
Loop 10
{
;Fast folder slots are 0-based externally
pos := A_Index - 1
if(FastFolders[pos].Path)
{
if(pos <= 4 && ToPlacesBar) ;update placesbar
{
value := FastFolders[pos].Path
RegWrite, REG_SZ,HKCU,Software\Microsoft\Windows\CurrentVersion\Policies\comdlg32\Placesbar, Place%pos%,%value%
}
}
}
if(ToFolderBand)
{
;Explorer folder band bar buttons are added in a separate process since this takes a few seconds
if(FastFolders.WorkerThread)
FastFolders.WorkerThread.Repeat := true ;Mark for repetition with new data
else
{
Debug("starting new worker thread to refresh FastFolder buttons")
FastFolders.WorkerThread := new CWorkerThread("AddButtonsToFolderBandBar", 0, 0, 1)
FastFolders.WorkerThread.OnFinish.Handler := "FastFolders_WorkerThread_OnFinish"
FastFolders.WorkerThread.Start(FastFolders)
FastFolders.WorkerThread.WaitForStart(5)
}
}
}
;Called on main thread when the Fast Folders worker thread has finished
FastFolders_WorkerThread_OnFinish(WorkerThread, Result)
{
global FastFolders
Debug("Fast Folder buttons refresh finished")
if(WorkerThread.Repeat)
{
RemoveAllExplorerButtons("IsFastFolderButton")
Debug("starting new worker thread to refresh FastFolder buttons")
FastFolders.WorkerThread := new CWorkerThread("AddButtonsToFolderBandBar", 0, 0, 1)
FastFolders.WorkerThread.OnFinish.Handler := "FastFolders_WorkerThread_OnFinish"
FastFolders.WorkerThread.Start(FastFolders)
FastFolders.WorkerThread.WaitForStart(5)
}
else
;Remove the worker thread to indicate that a new one is needed next time
FastFolders.Remove("WorkerThread")
}
;Worker function executed in seperate thread
AddButtonsToFolderBandBar(WorkerThread, FastFolders)
{
Loop 10
if(FastFolders[A_Index - 1].Path)
AddButton("", FastFolders[A_Index - 1].Path, "", (A_Index - 1) ":" FastFolders[A_Index - 1].Name, "", "Both", 2) ;7plus now uses AHK=2 key in registry to indicate FastFolder buttons
}
;Callback function for determining if a specific registry key was created by 7plus
IsFastFolderButton(Command, Name, Tooltip, ahk)
{
return ahk = 2 || RegExMatch(Name, "^\d+:") ;RegexMatch is legacy code for buttons which don't have ahk=2 set
}
;Removes all buttons created with this script. Function can be the name of a function with these arguments: func(command, Title, tooltip, ahk) and it can be used to tell the script if an entry may be deleted
RemoveAllExplorerButtons(function = "")
{
BaseKey := "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderTypes"
;go into view folders (clsid)
Loop, HKLM, %BaseKey%, 2, 0
{
clsid := A_LoopRegName
;code below needs to be executed for two folders each, [selected item / no selected item]
Keys := {TasksItemsSelected : "", TasksNoItemsSelected : ""}
for Key, v in Keys
{
;go into numbered folders of single buttons
Loop, HKLM, %BaseKey%\%clsid%\%Key%, 2, 0
{
ButtonNumber := A_LoopRegName
;This function will only remove buttons created by 7plus which have an additional AHK key
RegRead, ahk, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%, AHK
;go into clsid folder
Loop, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%, 2, 0
{
skip := false
RegRead, value, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%\%A_LoopRegName%, InfoTip
RegRead, Title, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%\%A_LoopRegName%, Title
RegRead, cmd, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%\%A_LoopRegName%\shell\InvokeTask\command
;Custom skip function code
if(IsFunc(function))
if(!%function%(cmd, Title, value, ahk))
{
skip := true
break
}
}
if(skip)
continue
if(ahk)
RegDelete, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%
}
}
}
}
;Removes a button. Command can either be a real command (with arguments), a path or a function with three arguments (command, key, param) which identifies the proper key
RemoveButton(Command, param="")
{
if(!IsFunc(Command) && InStr(Command,"\",0,strlen(Command)))
StringTrimRight, Command, Command, 1
BaseKey := "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderTypes"
ButtonFound := false
;go into view folders (clsid)
Loop, HKLM, %BaseKey%, 2, 0
{
clsid := A_LoopRegName
;code below needs to be executed for two folders each, [selected item / no selected item]
Keys := {TasksItemsSelected : "", TasksNoItemsSelected : ""}
for Key, v in Keys
{
;Local variable inside this loop for telling found state of the selected/no selected folders
maxnumber := -1
;Loop through all buttons of this view (reg loop goes backwards apparently)
Loop, HKLM, %BaseKey%\%clsid%\%Key%, 2, 0
{
ButtonNumber := A_LoopRegName
maxnumber := max(ButtonNumber, maxnumber)
;Keys created by 7plus have an "AHK" key added to them to make sure that only Keys related to 7plus are modified
RegRead, ahk, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%, AHK
if(ahk)
{
;go into 2nd clsid folder
Loop, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%, 2, 0
{
RegRead, value, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%\%A_LoopRegName%, InfoTip
;Check if the current key is the correct one (possibly with a caller-defined function)
if((!IsFunc(Command) && value = Command) || (IsFunc(Command) && %Command%(value, BaseKey "\" clsid "\" Key "\" ButtonNumber "\" A_LoopRegName "\shell\InvokeTask\command", param)))
{
;If the key is correct, it may be deleted
RegDelete, HKLM, %BaseKey%\%clsid%\%Key%\%ButtonNumber%
ButtonFound := true
;after item has been deleted, we need to move the higher ones down by one
if(maxnumber > ButtonNumber)
{
i := ButtonNumber + 1
while i <= maxnumber
{
j := i - 1
Runwait, reg copy HKLM\%BaseKey%\%clsid%\%Key%\%i% HKLM\%BaseKey%\%clsid%\%Key%\%j% /s /f, , Hide
regdelete, HKLM, %BaseKey%\%clsid%\%Key%\%i%
i++
}
}
break 2
}
}
}
}
}
}
if(!ButtonFound)
Debug("Explorer button not found: " (param.Extends("CEvent") ? param.Name : Command))
return ButtonFound
}
;Adds a button. You may specify a command (and possibly an argument) or a path, and a name which should be used.
;Other parameters are a ToolTip
AddButton(Command, path, Args = "", Name = "", Tooltip = "", AddTo = "Both", ahk = 1, IterationsSelected = "", IterationsNoSelected = "")
{
Debug("addbutton command " command " path " path " args " args " name " name)
if(A_IsCompiled)
ahk_path := """" A_ScriptDir "\7plus.exe"""
else
ahk_path := """" A_AhkPath """ """ A_ScriptFullPath """"
icon := "%SystemRoot%\System32\shell32.dll,3" ;Icon is not working, probably not supported by explorer, some ms entries have icons defined but they don't show up either
if(Command)
{
if(!Name)
{
SplitPath, Command , Name
if(Name = "")
Name := Command
}
icon := Command ",1"
description := command
command .= " " args
}
if(path)
{
;Remove trailing backslash
if(InStr(path,"\", 0, strlen(path)))
StringTrimRight, path, path, 1
if(!name)
{
SplitPath, path , Name
if(Name = "")
Name := path
}
Command := ahk_path " """ path """"
description := path
}
if(!command && !path && args) ;args only, use start 7plus with -id param
{
Command := """" (A_IsCompiled ? A_ScriptPath : A_AhkPath """ """ A_ScriptFullPath) """ -id:" args
description := Tooltip
}
SomeCLSID := "{" . uuid(false) . "}"
;These arrays store the next free index of the buttons in the FolderTypes subfolders for speed improvements
if(!IsObject(IterationsSelected))
IterationsSelected := []
if(!IsObject(IterationsNoSelected))
IterationsNoSelected := []
BasePath := "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderTypes"
;go into view folders (clsid)
Loop, HKLM, %BasePath%, 2, 0
{
if(AddTo = "Both" || AddTo = "Selected")
AddButton_Write(BasePath "\" A_LoopRegName "\TasksItemsSelected", SomeCLSID, command, Name, Description, Icon, ahk, A_LoopRegName, IterationsSelected)
if(AddTo = "Both" || AddTo = "NoSelected")
AddButton_Write(BasePath "\" A_LoopRegName "\TasksNoItemsSelected", SomeCLSID, command, Name, Description, Icon, ahk, A_LoopRegName, IterationsNoSelected)
}
}
;Writes the data for a single button (for selected or no-selected state)
AddButton_Write(Path, SomeCLSID, command, Title, InfoTip, Icon, AHK, Name, Iterations)
{
;figure out first free key number
if(!Iterations.HasKey(Name))
{
Iterations[Name] := 0
Loop, HKLM, %Path%, 2, 0
Iterations[Name]++
}
Path .= "\" Iterations[Name]
;Marker for easier recognition of ahk-added entries
RegWrite, REG_SZ, HKLM, %Path%, AHK, %AHK%
Path .= "\" SomeCLSID
;Write reg keys
RegWrite, REG_EXPAND_SZ, HKLM, %Path%, Icon, %icon%
RegWrite, REG_SZ, HKLM, %Path%, InfoTip, %InfoTip%
RegWrite, REG_SZ, HKLM, %Path%, Title, %Title%
RegWrite, REG_SZ, HKLM, %Path%\shell\InvokeTask\command, , %command%
Iterations[Name]++
}
;Checks if a particular button exists
FindButton(function, param)
{
if(!IsFunc(function))
return false
BasePath := "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderTypes"
;go into view folders (clsid)
Loop, HKLM, %BasePath%, 2, 0
{
regkey := A_LoopRegName
maxnumber := -1
;loop through selected item number folders (loop goes backwards)
Loop, HKLM, %Basepath%\%regkey%\TasksItemsSelected, 2, 0
{
numberfolder := A_LoopRegName
RegRead, ahk, HKLM, %Basepath%\%regkey%\TasksItemsSelected\%numberfolder%, AHK
if(ahk)
{
;go into clsid folder
Loop, HKLM, %Basepath%\%regkey%\TasksItemsSelected\%numberfolder%, 2, 0
{
RegRead, value, HKLM, %Basepath%\%regkey%\TasksItemsSelected\%numberfolder%\%A_LoopRegName%\shell\InvokeTask\command
if(%function%(value, Basepath "\" regkey "\TasksItemsSelected\" numberfolder "\" A_LoopRegName "\shell\InvokeTask\command", param))
return true
}
}
}
}
return false
}