Testers for Beta script: global search on UI elements

Some of you may have found that finding and referencing a specific element in GUI scripting is tedius.
For instance, a reference to a Finder file depends on the status of its Finder window (toolbar visible etc.)

Prefab UI Browser and similar utilities browse the overall hierarchy but do not offer a global search.

This script searches for a specific string in all of an application GUI elements.
However, this is not without (timeout, memory) issues…

You’re invited to test this out and improve this script:



-- GUIfinder rev. 0.5 17-10-2005 by Eelco Houwink,.
tell application "System Events"
	get system attribute "sysv"
	if result is greater than or equal to 4144 then -- 4144 is Mac OS X 10.3.0
		tell application "System Events" to get UI elements enabled
		if not result then
			tell me to (display dialog "Enable UI scripting in Universal Access" buttons {"OK"} default button 1 with icon 2)
			tell application "System Preferences"
				activate
				set current pane to pane "com.apple.preference.universalaccess"
			end tell
			return
		end if
	end if
	set Appz to name of application processes --whose visible is true
	set fApp to some application process whose frontmost is true
	beep
	tell me to set mainTarget to (choose from list Appz with prompt "   Search in GUI elements of:" OK button name "Select")
	if mainTarget is false then return
	set mainTarget to item 1 of mainTarget
	tell me to set searchterm to text returned of (display dialog "What to search for:" default answer "Apple" buttons {"Cancel", "OK"} default button 2 with icon 2)
	beep
	tell me to set doAppleMenu to button returned of (display dialog "Include Apple-menu.?
(may take longer and result in memory problems)" buttons {"Yes", "No"} default button 2 with icon 2)
	set {totNames, totUIs} to {{}, {}}
	tell process mainTarget
		set Mainlist to (get UI elements)
		set mainnames to {}
		repeat with h from 1 to number of items in Mainlist
			set hName to (class of item h of Mainlist & " " & name of item h of Mainlist) as string
			try
				if name of item h of Mainlist = "" or name of item h of Mainlist = " " then set hName to (class of item h of Mainlist & " " & h) as string
			on error
				try
					set hName to (class of item h of Mainlist & " " & h) as string
				on error
					set hName to ("resource item " & h) as string
				end try
			end try
			set mainnames to mainnames & hName
		end repeat
		beep
		tell me to set selResources to (choose from list mainnames with prompt "   Search in:" OK button name "Select" default items item 1 of mainnames with multiple selections allowed)
		if selResources is false then return
		set searchResources to {}
		repeat with doResource in selResources
			set x to my getIndex(doResource as string, mainnames)
			set founditem to item x of Mainlist
			set searchResources to searchResources & {founditem}
		end repeat
		repeat with a from 1 to number of items in searchResources
			set thename to name of (item a of searchResources)
			try
				if thename = "" then set thename to a
			on error
				set thename to a
			end try
			try
				say (class of (item a of searchResources) & " " & thename) as string
			end try
			set aUIs to (item a of searchResources)'s UI elements
			set totUIs to totUIs & aUIs
			if aUIs ≠ {} then
				set aUIname to name of (item a of searchResources)'s UI elements
				set totNames to totNames & aUIname
				repeat with b from 1 to number of items in aUIs
					if b = 1 and doAppleMenu = "No" and item 1 of aUIname = "Apple" then -- skip Apple menu
					else
						set bUIs to (item b of aUIs)'s UI elements
						set totUIs to totUIs & bUIs
						if bUIs ≠ {} then
							set bUIname to name of (item b of aUIs)'s UI elements
							set totNames to totNames & bUIname
							repeat with c from 1 to number of items in bUIs
								set cUIs to (item c of bUIs)'s UI elements
								set totUIs to totUIs & cUIs
								if cUIs ≠ {} then
									set cUIname to name of (item c of bUIs)'s UI elements
									set totNames to totNames & cUIname
									repeat with d from 1 to number of items in cUIs
										set dUIs to (item d of cUIs)'s UI elements
										set totUIs to totUIs & dUIs
										if dUIs ≠ {} then
											set dUIname to name of (item d of cUIs)'s UI elements
											set totNames to totNames & dUIname
											repeat with e from 1 to number of items in dUIs
												set eUIs to (item e of dUIs)'s UI elements
												set totUIs to totUIs & eUIs
												if eUIs ≠ {} then
													set eUIname to name of (item e of dUIs)'s UI elements
													set totNames to totNames & eUIname
													repeat with f from 1 to number of items in eUIs
														set fUIs to (item f of eUIs)'s UI elements
														set totUIs to totUIs & fUIs
														if fUIs ≠ {} then
															set fUIname to name of (item f of eUIs)'s UI elements
															set totNames to totNames & fUIname
															repeat with g from 1 to number of items in fUIs
																set gUIs to (item g of fUIs)'s UI elements
																set totUIs to totUIs & gUIs
																if gUIs ≠ {} then
																	set gUIname to name of (item g of fUIs)'s UI elements
																	set totNames to totNames & gUIname
																	repeat with h from 1 to number of items in gUIs
																		set hUIs to (item h of gUIs)'s UI elements
																		set totUIs to totUIs & hUIs
																		if hUIs ≠ {} then
																			set hUIname to name of (item h of gUIs)'s UI elements
																			set totNames to totNames & hUIname
																		end if
																	end repeat
																end if
															end repeat
														end if
													end repeat
												end if
											end repeat
										end if
									end repeat
								end if
							end repeat
						end if
					end if
				end repeat
			end if
		end repeat
		set founditems to {}
		set foundNamez to {}
		repeat with i from 1 to number of items in totNames
			if searchterm is in ((item i of totNames) as string) then
				set foundNamez to foundNamez & item i of totNames
				if get {item i of totUIs} is not in founditems then set founditems to founditems & {item i of totUIs}
			end if
		end repeat
		if founditems = {} then say "nothing found"
		set foundNamez to {}
		repeat with jj in founditems
			try
				if name of jj ≠ "" then set foundNamez to foundNamez & name of jj
			end try
		end repeat
		beep
		tell me to activate
		if foundNamez = {} then
			tell me to display dialog "Nothing found..!" buttons {"OK"} default button 1 with icon 1
			return ""
		end if
		tell me to set selectedNames to (choose from list foundNamez with prompt "Found in " & number of items of totNames & " items:" OK button name "Select" with multiple selections allowed)
		
		set selecteditems to {}
		repeat with selectedName in selectedNames
			set x to my getIndex(selectedName as string, foundNamez)
			set founditem to item x of founditems
			set selecteditems to selecteditems & {founditem}
		end repeat
	end tell
end tell
return {selectedNames, selecteditems}

on getIndex(i, l)
	repeat with n from 1 to count l
		if l's item n is i then return n
	end repeat
	0
end getIndex

It seems you could do with some recursion… :slight_smile:

			if aUIs ≠ {} then
				set aUIname to name of (item a of searchResources)'s UI elements
				set totNames to totNames & aUIname
				repeat with b from 1 to number of items in aUIs
					if b = 1 and doAppleMenu = "No" and item 1 of aUIname = "Apple" then -- skip Apple menu
					else
						set {u, n} to my getUIs(mainTarget, item b of aUIs)
						set totUIs to totUIs & u
						set totNames to totNames & n
					end if
				end repeat
			end if

		-- rest of script


on getUIs(mainTarget, thisElement)
	tell application "System Events" to tell process mainTarget
		set UIs to thisElement's UI elements
		set totUIs to UIs
		if UIs is {} then
			set totNames to {}
		else
			set totNames to name of thisElement's UI elements
			repeat with i from 1 to (count UIs)
				set {u, n} to my getUIs(mainTarget, item i of UIs)
				set totUIs to totUIs & u
				set totNames to totNames & n
			end repeat
		end if
		return {totUIs, totNames}
	end tell
end getUIs

Nigel,

Thanks, I knew there was to be some recursion like this, but couldn’t find it…

Eelco