; ============================================================================= ; ; 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 #include #include #include 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 ; ============================================================================= ; CopyAllItem(): ; 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