-
Notifications
You must be signed in to change notification settings - Fork 0
/
FindClassesByText.au3
473 lines (365 loc) · 16.5 KB
/
FindClassesByText.au3
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
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
; =============================================================================
;
; Find Classes by Text v1.2
; Written by Alex Peters, 29/May/2013
;
; Lists the ClassNameNNs of a window grouped by the displayed text. Useful for
; determining the ClassNameNN of e.g. a text control when AutoIt Window Info
; cannot help (due for instance to overlapping controls on the window).
;
; =============================================================================
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <TreeViewConstants.au3>
#include <WindowsConstants.au3>
Opt('GUIOnEventMode', True)
Opt('MustDeclareVars', True)
Opt('WinWaitDelay', 0)
; Variables to be accessed by event-handling functions.
Global $GUIHandle, $TreeHandle, $CaptureBtnHandle, $CopyItemBtnHandle
Global $CopyAllBtnHandle
Global $CapturedTitle = '[No window captured yet; click to capture]'
Global $InCaptureMode = False
Global $Capturing = False
Global $TextClasses
Global $SelectedItem
; GUI positioning constants.
Global Const $PADDING = 12
Global Const $BTN_HEIGHT = 40
Global Const $COPY_BTN_WIDTH = 60
; =============================================================================
PrepareGUI()
While True
WinWaitNotActive($GUIHandle)
; Another window is active. Capture it if appropriate.
If $InCaptureMode Then
Local $CapturedWindow = WinGetHandle('[ACTIVE]')
Beep(400, 50)
$Capturing = True
UpdateControlStates()
; Grab title for display on button.
$CapturedTitle = WinGetTitle($CapturedWindow)
; Get the information and build a TreeView.
$TextClasses = WinGetClassesByText($CapturedWindow)
BuildTree()
; Return to normal operation mode.
ToggleCaptureMode()
EndIf
WinWaitActive($GUIHandle)
WEnd
; =============================================================================
; PrepareGUI():
; Creates and shows the GUI and its base controls.
; =============================================================================
Func PrepareGUI()
; Create the window.
$GUIHandle = GUICreate('Find Classes by Text v1.2', _
@DesktopWidth / 2, @DesktopHeight / 2, Default, Default, _
BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_MAXIMIZEBOX))
GUISetOnEvent($GUI_EVENT_RESIZED, 'Event_GUIResize')
GUISetOnEvent($GUI_EVENT_CLOSE, 'Event_GUIClose')
; Create the Capture button.
$CaptureBtnHandle = GUICtrlCreateButton('', _
Default, Default, Default, Default, $BS_MULTILINE)
GUICtrlSetResizing($CaptureBtnHandle, _
$GUI_DOCKLEFT + $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT)
GUICtrlSetOnEvent($CaptureBtnHandle, 'ToggleCaptureMode')
; Create the Copy Item button.
$CopyItemBtnHandle = GUICtrlCreateButton('Copy' & @LF & '&item', _
Default, Default, Default, Default, $BS_MULTILINE)
GUICtrlSetResizing($CopyItemBtnHandle, _
$GUI_DOCKWIDTH + $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT)
GUICtrlSetOnEvent($CopyItemBtnHandle, 'CopySelectedItem')
; Create the Copy All button.
$CopyAllBtnHandle = GUICtrlCreateButton('Copy' & @LF & '&all', _
Default, Default, Default, Default, $BS_MULTILINE)
GUICtrlSetResizing($CopyAllBtnHandle, _
$GUI_DOCKWIDTH + $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT)
GUICtrlSetOnEvent($CopyAllBtnHandle, 'CopyAllItems')
; Arrange everything nicely.
RepositionControls()
; Ensure that the GUI reflects the initial state correctly.
UpdateControlStates()
; Show the GUI.
GUISetState()
EndFunc
; =============================================================================
; BuildTree():
; Creates a TreeView control containing the specified text snippets and
; associated ClassNameNNs.
; =============================================================================
Func BuildTree()
; Delete any existing TreeView; this is the easiest way to get rid of all
; existing window data.
$SelectedItem = ''
If $TreeHandle <> '' Then GUICtrlDelete($TreeHandle)
; Create a new TreeView.
$TreeHandle = GUICtrlCreateTreeView(0, 0, 0, 0, _
$GUI_SS_DEFAULT_TREEVIEW, $WS_EX_CLIENTEDGE)
GUICtrlSetResizing($TreeHandle, $GUI_DOCKBORDERS)
; Keep everything nicely arranged.
RepositionControls()
; Populate with text snippets and associated ClassNameNNs.
For $I = 1 To $TextClasses[0][0]
Local $TextNode = GUICtrlCreateTreeViewItem( _
Escape($TextClasses[$I][0]), $TreeHandle)
GUICtrlSetOnEvent(-1, 'Event_TreeViewItemSelect')
Local $Classes = StringSplit($TextClasses[$I][1], @LF)
For $J = 1 To $Classes[0]
GUICtrlCreateTreeViewItem($Classes[$J], $TextNode)
GUICtrlSetOnEvent(-1, 'Event_TreeViewItemSelect')
Next
Next
EndFunc
; =============================================================================
; RepositionControls():
; Aligns the GUI controls nicely after a resize or after a new control is
; created.
; =============================================================================
Func RepositionControls()
Local Const $Area = WinGetClientSize($GUIHandle)
Local Const $MinLeft = $PADDING
Local Const $MaxLeft = $Area[0] - $PADDING
Local Const $MinTop = $PADDING
Local Const $MaxWidth = $MaxLeft - $MinLeft
Local Const $MaxHeight = $Area[1] - 2 * $PADDING
GUICtrlSetPos($CaptureBtnHandle, _
$MinLeft, $MinTop, _
$MaxWidth - 2 * $COPY_BTN_WIDTH - 2 * $PADDING, $BTN_HEIGHT)
GUICtrlSetPos($CopyItemBtnHandle, _
$MaxLeft - 2 * $COPY_BTN_WIDTH - $PADDING, $MinTop, _
$COPY_BTN_WIDTH, $BTN_HEIGHT)
GUICtrlSetPos($CopyAllBtnHandle, _
$MaxLeft - $COPY_BTN_WIDTH, $MinTop, _
$COPY_BTN_WIDTH, $BTN_HEIGHT)
If $TreeHandle Then GUICtrlSetPos($TreeHandle, _
$MinLeft, $MinTop + $BTN_HEIGHT + $PADDING, _
$MaxWidth, $MaxHeight - $BTN_HEIGHT - $PADDING)
EndFunc
; =============================================================================
; UpdateControlStates():
; Enables/disables controls as appropriate when the script's state changes
; (e.g. capturing data, entering capture mode).
; =============================================================================
Func UpdateControlStates()
If $InCaptureMode Then
If $Capturing Then
GUICtrlSetState($CaptureBtnHandle, $GUI_DISABLE)
GUICtrlSetData($CaptureBtnHandle, 'Capturing...')
Else
GUICtrlSetData($CaptureBtnHandle, _
'[Activate window to be captured or click to cancel]')
EndIf
GUICtrlSetState($CopyItemBtnHandle, $GUI_DISABLE)
GUICtrlSetState($CopyAllBtnHandle, $GUI_DISABLE)
If $TreeHandle Then GUICtrlSetState($TreeHandle, $GUI_DISABLE)
Else
GUICtrlSetData($CaptureBtnHandle, $CapturedTitle)
GUICtrlSetState($CaptureBtnHandle, $GUI_ENABLE)
If $SelectedItem Then
GUICtrlSetState($CopyItemBtnHandle, $GUI_ENABLE)
Else
GUICtrlSetState($CopyItemBtnHandle, $GUI_DISABLE)
EndIf
If IsArray($TextClasses) AND $TextClasses[0][0] Then
GUICtrlSetState($CopyAllBtnHandle, $GUI_ENABLE)
Else
GUICtrlSetState($CopyAllBtnHandle, $GUI_DISABLE)
EndIf
If $TreeHandle Then GUICtrlSetState($TreeHandle, $GUI_ENABLE)
EndIf
EndFunc
; =============================================================================
; Event_GUIClose():
; Called when the GUI is asked to close (e.g. when the user clicks the X).
; =============================================================================
Func Event_GUIClose()
Exit
EndFunc
; =============================================================================
; Event_GUIResize():
; Called after the user has completed a resize operation on the GUI.
; =============================================================================
Func Event_GUIResize()
RepositionControls()
EndFunc
; =============================================================================
; Event_TreeViewItemSelect():
; Called after the user has selected a TreeView item.
; =============================================================================
Func Event_TreeViewItemSelect()
$SelectedItem = @GUI_CtrlId
UpdateControlStates()
EndFunc
; =============================================================================
; CopySelectedItem():
; Called when the user clicks the "Copy Item" button. Copies the text of
; the selected TreeView item to the clipboard.
; =============================================================================
Func CopySelectedItem()
ClipPut(GUICtrlRead($SelectedItem, 1))
EndFunc
; =============================================================================
; CopyAllItems():
; Called when the user clicks the "Copy All" button. Copies the entire
; content of the TreeView to the clipboard.
; =============================================================================
Func CopyAllItems()
Local $Output = 'Control ClassNameNNs grouped by text for window ' _
& Escape($CapturedTitle) & @CRLF & @CRLF
Local $I, $J
For $I = 1 To $TextClasses[0][0]
$Output &= Escape($TextClasses[$I][0]) & @CRLF
Local $Classes = StringSplit($TextClasses[$I][1], @LF)
For $J = 1 To $Classes[0]
$Output &= @TAB & $Classes[$J] & @CRLF
Next
$Output &= @CRLF
Next
ClipPut($Output)
EndFunc
; =============================================================================
; ToggleCaptureMode():
; Called when the Capture button is clicked, and enters or exits capturing
; mode as appropriate. Also called when data capturing is complete.
; =============================================================================
Func ToggleCaptureMode()
$InCaptureMode = NOT $InCaptureMode
$Capturing = False
UpdateControlStates()
EndFunc
; ==============================================================================
; WinGetClassesByText():
; Returns a text/class list in the form of a two-dimensional array. Element
; [0][0] contains a count of following text/class pairs. Element [X][0]
; holds the text and element [X][1] holds an @LF-delimited list of
; ClassNameNNs sharing that text.
; ==============================================================================
Func WinGetClassesByText($Title, $Text = '')
Local $Classes = WinGetControlIDs($Title, $Text)
Local $Texts[$Classes[0] + 1][2]
$Texts[0][0] = 0
For $I = 1 To $Classes[0]
AddClass($Texts, ControlGetText($Title, $Text, $Classes[$I]), $Classes[$I])
Next
Return $Texts
EndFunc
; ==============================================================================
; WinGetControlIDs():
; Returns an array of ClassNameNNs for a window where element 0 is a count.
; ==============================================================================
Func WinGetControlIDs($sTitle, $sText = '')
Local $avClasses[1], $iCounter, $sClasses, $sClassStub, $sClassStubList
; Request an unnumbered class list.
$sClassStubList = WinGetClassList($sTitle, $sText)
; Return an empty response if no controls exist.
; Additionally set @Error if the specified window was not found.
If $sClassStubList = '' Then
If @Error Then SetError(1)
$avClasses[0] = 0
Return $avClasses
EndIf
; Prepare an array to hold the numbered classes.
ReDim $avClasses[StringLen($sClassStubList) - _
StringLen(StringReplace($sClassStubList, @LF, '')) + 1]
; The first element will contain a count.
$avClasses[0] = 0
; Count each unique class, enumerate them in the array and remove them from
; the string.
Do
$sClassStub = _
StringLeft($sClassStubList, StringInStr($sClassStubList, @LF))
$iCounter = 0
While StringInStr($sClassStubList, $sClassStub)
$avClasses[0] += 1
$iCounter += 1
$avClasses[$avClasses[0]] = _
StringTrimRight($sClassStub, 1) & $iCounter
$sClassStubList = _
StringReplace($sClassStubList, $sClassStub, '', 1)
WEnd
Until $sClassStubList = ''
Return $avClasses
EndFunc
; ==============================================================================
; AddClass():
; Adds a class to a text entry in the given text/class list. If the given
; text is not already contained then a new element is created.
; ==============================================================================
Func AddClass(ByRef $Texts, $Text, $Class)
For $I = 1 To $Texts[0][0]
If $Text == $Texts[$I][0] Then
$Texts[$I][1] &= @LF & $Class
Return
EndIf
Next
; This point is reached if the text doesn't already exist in the list.
$Texts[0][0] += 1
$Texts[$Texts[0][0]][0] = $Text
$Texts[$Texts[0][0]][1] = $Class
EndFunc
; ==============================================================================
; Escape($Input):
; Returns an escaped version of $Input such that Execute(Escape($Input))
; would return $Input. Intended to make some special character sequences
; easier to spot, and also to allow any string to be conveyed meaningfully
; on a single line.
; ==============================================================================
Func Escape(Const ByRef $InputStr)
; AutoIt representations of certain special character sequences. Longer
; sequences must be defined AFTER shorter ones contained by them in order
; to be correctly honoured (i.e. @CRLF after @CR).
Local $Macros[4] = [ '@CR', '@CRLF', '@LF', '@TAB' ]
; The output string to be built incrementally and eventually returned.
Local $OutputStr = ''
; Process the input string as a series of optional string literals followed
; by optional macros. Start at the beginning and keep going until all of
; the input string has been examined.
Local $StartPos = 1
While $StartPos <= StringLen($InputStr)
; Find the first position in this part of the string where literal
; string data ends. Assume at first that the entire remainder of the
; string is one big literal. If we find a macro, remember which one.
Local $LiteralEndPos = StringLen($InputStr) + 1
Local $Macro = ''
; Look for each macro and remember which one appears first. Check them
; all, because e.g. @CRLF would have the same position as @CR.
Local $I
For $I = 0 To UBound($Macros) - 1
Local $ThisMacroPos _
= StringInStr($InputStr, Execute($Macros[$I]), 0, 1, $StartPos)
If $ThisMacroPos = 0 Then ContinueLoop
If $LiteralEndPos > 0 AND $ThisMacroPos > $LiteralEndPos _
Then ContinueLoop
$LiteralEndPos = $ThisMacroPos
$Macro = $Macros[$I]
Next
; Escape the string literal if there is one.
Local $Literal _
= StringMid($InputStr, $StartPos, $LiteralEndPos - $StartPos)
If $Literal <> '' Then
If $OutputStr <> '' Then $OutputStr &= ' & '
If _
StringInStr($Literal, '"') _
AND NOT StringInStr($Literal, "'") _
Then
; It contains double quotes but not single quotes.
; Surround it with single quotes.
$OutputStr &= "'" & $Literal & "'"
Else
; It contains no quotes, or just single, or both.
; Surround it with doubles and escape any doubles within.
$OutputStr &= '"' & StringReplace($Literal, '"', '""') & '"'
EndIf
$StartPos = $LiteralEndPos
EndIf
; If we found a macro earlier then write it out.
If $Macro <> '' Then
If $OutputStr <> '' Then $OutputStr &= ' & '
$OutputStr &= $Macro
$StartPos += StringLen(Execute($Macro))
EndIf
WEnd
If $OutputStr == '' Then $OutputStr = '""'
Return $OutputStr
EndFunc