Getting the selection of any Finder window

This may not be something we’d need to do every day. But just occasionally, it might be handy to be able to get the selection of a Finder window, without having to bring it to the front.

A recent discussion about this prompted me to dust off a routine that I’d used some time back to do just that. Since the technique needs to check a number of aspects of the user interface, this is hardly a one-liner. I’m posting it here, just in case it’s of any general use to someone.

The method combines information from Finder and System Events, and relies on a fair amount of GUI scripting (which therefore needs to be enabled).

If such a script is to be distributed, it makes sense to include a GUI checking/enabling routine, to help make the user’s life a bit easier:


to |check/enable GUI scripting|()
	if (system attribute "sysv") < 4138 then display dialog ¬
		"This script requires the installation of Mac OS X 10.3 or higher." buttons {"Cancel"} default button 1 with icon 2
	tell application "System Events" to if not UI elements enabled then
		tell me to display dialog ¬
			"This script requires the built-in Graphic User Interface Scripting architecture of Mac OS X, which is currently disabled." & ¬
			return & return & "Enable GUI Scripting now? (You may be asked to enter your password.)" buttons ¬
			{"Cancel", "Enable"} default button 2 with icon 2
		set UI elements enabled to true
		if not UI elements enabled then error number -128
	end if
end |check/enable GUI scripting|

|check/enable GUI scripting|()


So, with that little job taken care of, here’s the script:


script Finder_selection
	on icon_view(t)
		tell application "System Events" to value of attribute "AXFilename" of ¬
			(group 1's UI elements of t's scroll area -1 where it is selected)
	end icon_view
	on list_view(t)
		tell application "System Events" to value of attribute "AXFilename" of (group 1's ¬
			first UI element of (outline 1's rows of t's scroll area -1 where it is selected) ¬
			where "AXFilename" is in name of attributes)
	end list_view
	on column_view(t)
		tell application "System Events" to tell (list 1's UI elements of (last scroll area of t's browser 1's ¬
			scroll area 1 where true is in selected of list 1's UI elements) where it is selected)
			if (count) is 1 and value of item 1's attribute "AXFinderIsContainer" then return 0
			value of attribute "AXFilename"
		end tell
	end column_view
	on prime_target(v, n)
		tell application "System Events" to tell (application process "Finder"'s window n ¬
			whose description is "standard window")
			if v then return splitter group 1
			it
		end tell
	end prime_target
	on window_index(n)
		tell application "Finder"
			if n is 0 then return items whose name is in my ¬
				icon_view(application process "Finder" of application "System Events")
			tell Finder window n
				if not (exists) then return missing value
				set t to my prime_target(toolbar visible, n)
				set v to current view
				if v is icon view then
					items whose name is in my icon_view(t)
				else if v is list view then
					items whose name is in my list_view(t)
				else -- v is list view
					set l to my column_view(t)
					if l is 0 then return target
					items whose name is in l
				end if
			end tell
		end tell
	end window_index
end script

Finder_selection's window_index(2) -- specify 0 for desktop selection


That’s really cool Kai. I knew one of you experts had a way to do this. It blows my mind that you’ve played with GUI scripting in this way. Man… there’s so much to learn and so little time. Thanks for this!

Hello.

This code which Kai Edwards made is now broken because of some updates to the Finder at least in Snow Leopard.

I really hope one of you GUI scripting experts takes the case and updates it, because this is a brilliant piece of code which I think many will find useful.

Thanks

Kai may have been a scripting genius, but he couldn’t write a legible script for toffee! :confused:

OK. Only the “list view” section and the special case of “column view” appear not to be working in SL and Kai didn’t know about flow view, of course. Here’s an SL version ” but it’s only been tested with the side bar showing.

Edit: The script that was here is now superseded by the script in post #13 below.

Hello Mr Nigel


tell application "Quicksilver" to show large type "Thank You!"

I guess I’ll have something to get my hands dirty then. I haven’t touched UIElementInspector for several months, (not that I miss it) I’ll download a trial version of Prefabs UIBrowser as well. Maybe I can’t make the script usable with side Panels but I’ll try. Hopes some mail box owner won’t be feeling flamed or stalked by the time this is over. :slight_smile:

I which to point an annoyng problem.

It seems that kai missed a detail.


      tell application "System Events" to tell (application process "Finder"'s window n ¬
           whose description is "standard window")

the description attribute is a localized string so its script is dedicated to users running the machine in English.

It would be easy to make the script free of that


tell application "System Events" to tell (application process "Finder"'s window n ¬
           whose value of attribute "AXSubrole" is "AXStandardWindow")

This string is not localized.

Yvan KOENIG (VALLAURIS, France) mercredi 4 août 2010 21:25:04

Here is a synthesis of both versions so, it may be used underr old OSes as well as under 10.6 and higher.
I also edited the instruction which was linked to Englih.

I ran it on my French System flawlessly.


-- By Kai Edwards, partially adapted for SL by Nigel Garvey.
script Finder_selection
	on icon_view(t)
		set major_OS to get system attribute "sysv"
		if major_OS is less than 4192 then (*
is Mac OS X 10.5.x or less *)
			tell application "System Events" to value of attribute "AXFilename" of ¬
				(group 1's UI elements of t's scroll area -1 where it is selected)
		else (*
is Mac OS X 10.6 or higher *)
			tell application "System Events" to return value of attribute "AXFilename" of ¬
				(group 1's UI elements of t's scroll area -1 where it is selected)
		end if
	end icon_view
	on list_view(t)
		set major_OS to get system attribute "sysv"
		if major_OS is less than 4192 then (*
is Mac OS X 10.5.x or less *)
			tell application "System Events" to value of attribute "AXFilename" of (group 1's ¬
				first UI element of (outline 1's rows of t's scroll area -1 where it is selected) ¬
				where "AXFilename" is in name of attributes)
		else (*
is Mac OS X 10.6 or higher *)
			tell application "System Events" to return value of text field 1 of (rows of first outline of last scroll area of t where it is selected)
		end if
	end list_view
	
	on column_view(t)
		set major_OS to get system attribute "sysv"
		if major_OS is less than 4192 then (*
is Mac OS X 10.5.x or less *)
			tell application "System Events" to tell (list 1's UI elements of (last scroll area of t's browser 1's ¬
				scroll area 1 where true is in selected of list 1's UI elements) where it is selected)
				if (count) is 1 and value of item 1's attribute "AXFinderIsContainer" then return 0
				value of attribute "AXFilename"
			end tell
		else (*
is Mac OS X 10.6 or higher *)
			tell application "System Events"
				tell (last scroll area of t's browser 1's scroll area 1 where selected of list 1's UI elements contains true)
					tell (list 1's UI elements where it is selected)
						if ((count) is 1) and ((value of item 1) = name of (get value of attribute "AXWindow" of item 1)) then
							return 0
						else
							return value
						end if
					end tell
				end tell
			end tell
		end if
	end column_view
	
	on prime_target(v, n)
		tell application "System Events" to tell (application process "Finder"'s window n ¬
			whose value of attribute "AXSubrole" is "AXStandardWindow")
			if v then return splitter group 1
			it
		end tell
	end prime_target
	
	on window_index(n)
		set major_OS to get system attribute "sysv"
		if major_OS is less than 4192 then (*
is Mac OS X 10.5.x or less *)
			tell application "Finder"
				if n is 0 then return items whose name is in my icon_view(application process "Finder" of application "System Events")
				tell Finder window n
					if not (exists) then return missing value
					set t to my prime_target(toolbar visible, n)
					set v to current view
					if v is icon view then
						items whose name is in my icon_view(t)
					else if v is list view then
						items whose name is in my list_view(t)
					else -- v is list view
						set l to my column_view(t)
						if l is 0 then return target
						items whose name is in l
					end if
				end tell
			end tell
		else (*
is Mac OS X 10.6 or higher *)
			tell application "Finder"
				if (n is 0) then return items whose name is in my icon_view(application process "Finder" of application "System Events")
				tell Finder window n
					if not (exists) then return missing value
					set t to my prime_target(toolbar visible, n)
					set v to current view
					if (v is icon view) then
						return items whose name is in my icon_view(t)
					else if (v is list view) then
						return items whose name is in my list_view(t)
					else if (v is list view) then
						set l to my column_view(t)
						if (l is 0) then return target
						return items whose name is in l
					else -- v is flow view
						-- For development by someone with enough interest.
					end if
				end tell
			end tell
		end if
	end window_index
end script

Finder_selection's window_index(2) -- specify 0 for desktop selection

Yvan KOENIG (VALLAURIS, France) mercredi 4 août 2010 21:47:56

Hello Yvan!


tell application "Quicksilver" to show large type "Merci beaucoup Yvan. Vous êtes un véritable maître de l'interface graphique, ce que je savais à coup sûr. Quelle merveille que vous l'avez fait! Je vous dois!"

Hello.

There appear to be one flaw, I haven’t tested for any others yet. But when I run it with english localization, I get a list of filenames back, so the names of construct used to extract the results from the [b]list_view/b handler doesn’t work properly. Before doing any changes, as it would be very nice if the code worked everywhere, I wonder if this works for you just as it is, -which I guess it does. :slight_smile:

This is just to illustrate how I got the results:


on list_view(t)
        set major_OS to get system attribute "sysv"
        if major_OS is less than 4192 then (*
is Mac OS X 10.5.x or less *)
            tell application "System Events" to value of attribute "AXFilename" of (group 1's ¬
                first UI element of (outline 1's rows of t's scroll area -1 where it is selected) ¬
                where "AXFilename" is in name of attributes)
        else (*
is Mac OS X 10.6 or higher *)
            -- tell application "System Events" to return value of text field 1 of (rows of first outline of last scroll area of t where it is selected)
            tell application "System Events" to set mcvar to value of text field 1 of (rows of first outline of last scroll area of t where it is selected)
            log "mcvar " & mcvar
            return mcvar
            
        end if
    end list_view

This produced this output:

tell application "System Events" get value of text field 1 of every row of outline 1 of last scroll area of splitter group 1 of window "/Users/McUsr/Documents/CurrentProjects" of application process "Finder" whose selected = true --> {"DockParameters.txt"} end tell (*mcvar DockParameters.txt*) tell application "Finder" get every item of Finder window 2 whose {"DockParameters.txt"} contains name --> {} end tell
i’m unsure if there is any easy way to fix this. Tomorrow I’ll test the same case with Norwegian Localization.
And the other cases as well. Later this evening. I also suspect that the reasons for the results I get is because I run it from within script editor.

Edit:I hope this can easily be fixed, by just removing the name of construct in the window index handler.
(It will then still only return the names of the files, and not references, -is there any possibility of having it return references in this case?

The problem is that I have installed a custom skin to give my Mac a nicer look (and faster GUI), so maybe my machine is not the right machine to do this on, I have on the other hand never experienced any problems with the skin so far.

Hello.

This time I have tested icon view with English localization.

This is how I have changed the [b]icon_view/b handler.


    on icon_view(t)
        set major_OS to get system attribute "sysv"
        if major_OS is less than 4192 then (*
is Mac OS X 10.5.x or less *)
            tell application "System Events" to value of attribute "AXFilename" of ¬
                (group 1's UI elements of t's scroll area -1 where it is selected)
        else (*
is Mac OS X 10.6 or higher *)
            (*
        --    tell application "System Events" to return value of attribute "AXFilename" of ¬
                (group 1's UI elements of t's scroll area -1 where it is selected)
*)
            
            tell application "System Events" to set mcvar to value of attribute "AXFilename" of ¬
                (group 1's UI elements of t's scroll area -1 where it is selected)
            log mcvar
            return mcvar
            
        end if
    end icon_view

tell application "System Events" get value of attribute "AXFilename" of every UI element of group 1 of last scroll area of splitter group 1 of window "/Users/McUsr/Documents/CurrentProjects" of application process "Finder" whose selected = true --> error number -1719 from «class sgrp» 1 of last «class scra» of «class splg» 1 of window "/Users/McUsr/Documents/CurrentProjects" of «class pcap» "Finder" Result: error "System Events got an error: Can't get group 1 of last scroll area of splitter group 1 of window \"/Users/McUsr/Documents/CurrentProjects\" of application process \"Finder\". Invalid index." number -1719 from group 1 of last scroll area of splitter group 1 of window "/Users/McUsr/Documents/CurrentProjects" of application process "Finder"
Edit:Wading through System events properties of the actual finder window, I see that there may be a spoon, but there sure is no group for that window, so the line below fails equisitely. :slight_smile: Doesn’t it do that on your machines? The groups of the splitter group 1 is counted to be 0 when I look at the windows properties via the “explore Systemevents” option in Script Debuggr. My window is a "Normal Finder window, with a toolbar, visible side bar and icon view of course.
Next question:Is there any other property which is suitable to go through to access the selected files than the group property?

At last but not least: Is there any cocoa document or such you can recommend as a read up on this. :smiley: It would be nice to have some background information.

tell application "System Events" to set mcvar to value of attribute "AXFilename" of ¬ (group 1's UI elements of t's scroll area -1 where it is selected)
[b]Looking at Yvan’s code, I believe I have found a fix for the situation. Tomorrow night is another day! :slight_smile:

Edit:[/b]This is kind of cool right now! :slight_smile: I have figured out that in order to get to the selected files (in a finder window with a sidebar (couldn’t tell the difference really), I could go via lists1 of lists of splitter group 1.
-But here there are several options, there is the Static Texts, the group AXSelectedChildren contains zero elements albeit there is one icon selected there. There is also one group UiElements that also contains 6 elements. (As the two previous), I wonder how and if I can get any selected items out of one of these groups.

If I peruse further downwards in the Explorer Window of Script Debugger I also notice that there is an Entire contents, where the static texts of list1 of splitter group 1 is referenced, so I guess that would be the one to go for, in order to return references of files.
Tomorrow.

Hello.

Here is the result of the column view from the event log when run through Script Editor. -I get the same results when running through Script Debugger.

tell application "System Events" get splitter group 1 of window 2 of application process "Finder" whose value of attribute "AXSubrole" = "AXStandardWindow" --> splitter group 1 of window "/Users/tommybollman/Documents/CurrentProjects/McUsr" of application process "Finder" end tell tell application "Finder" get current view of Finder window 2 --> column view end tell
Edit:After having found a little snag in the [b]window_index/b handler, the column view handler works for me! :smiley:

OK. Here’s a revision which handles cover flow view as well. It works on both my Tiger and Snow Leopard machines. It’s also (I hope) a bit easier to edit should that become necessary in the future:

Edit: icon_view() and prime_target() handlers modified in the light of the following posts.


-- By Kai Edwards, adapted to work with SL too by Nigel Garvey and Yvan Koenig.
script Finder_selection
	property sl : missing value
	
	on icon_view(t)
		-- 'UI element 1' here is specifically a group, but it sometimes changes to the generic class in Snow Leopard, under circumstances yet to be determined.
		tell application "System Events" to return value of attribute "AXFilename" of (UI elements of UI element 1 of last scroll area of t where it is selected)
	end icon_view
	
	on list_view(t)
		tell application "System Events"
			tell (rows of first outline of last scroll area of t where it is selected)
				if (sl) then
					return value of attribute "AXFilename" of text field 1
				else
					return value of attribute "AXFilename" of UI element 1 of group 1
				end if
			end tell
		end tell
	end list_view
	
	on column_view(t)
		tell application "System Events"
			tell (UI elements of list 1 where it is selected) of (last scroll area of scroll area 1 of browser 1 of t where (selected of list 1's UI elements contains true))
				if (it exists) then
					set l to value of attribute "AXFilename"
					if (((count l) is 1) and ¬
						(((sl) and (l contains name of (get value of item 1's attribute "AXWindow")) or ¬
							((not sl) and (value of item 1's attribute "AXFinderIsContainer"))))) then -- :-)
						return 0
					else
						return l
					end if
				else
					return {}
				end if
			end tell
		end tell
	end column_view
	
	on flow_view(t)
		tell application "System Events" to return value of attribute "AXFilename" of text field 1 of (rows of outline 1 of scroll area 2 of splitter group 1 of t where it is selected)
	end flow_view
	
	on prime_target(v, n)
		tell application "System Events" to tell (application process "Finder"'s window n whose subrole is "AXStandardWindow")
			if v then return splitter group 1
			it
		end tell
	end prime_target
	
	on window_index(n)
		set sl to ((system attribute "sysv") ≥ 4192) -- True if Snow Leopard or later.
		tell application "Finder"
			if (n is 0) then return items whose name is in my icon_view(application process "Finder" of application "System Events")
			tell Finder window n
				if not (exists) then return missing value
				set t to my prime_target(toolbar visible, n)
				set v to current view
				if (v is icon view) then
					return items whose name is in my icon_view(t)
				else if (v is list view) then
					return items whose name is in my list_view(t)
				else if (v is column view) then
					set l to my column_view(t)
					if (l is 0) then return target
					return items whose name is in l
				else -- v is flow view (Snow Leopard only)
					return items whose name is in my flow_view(t)
				end if
			end tell
		end tell
	end window_index
end script

Finder_selection's window_index(1) -- specify 0 for desktop selection

Hello Nigel.

And big Thank You! :smiley:

Every thing works perfectly, except for the icon view.
There is something about that icon view I don’t understand, or can make work on my machine.

Since both you and Yvan have the similar code I guess there is something about my skin.

However. When I get a reference from UiBrowser it looks like this:


image 5 of UI element 1 of scroll area 2 of splitter group 1 of window 1

And here is the output from the Accessibility Inspector:

[code]<AXApplication: “Finder”>
<AXWindow: “/Users/McUsr/Library/Scripts/Experiments”>


<AXGrid: “Image browser”>
<AXImage: “EntityListerOLD.scpt”>

Attributes:
AXRole: “AXImage”
AXRoleDescription: “image”
AXParent: “<AXGrid: “Image browser”>”
AXWindow: “<AXWindow: “/Users/McUsr/Library/Scripts/Experiments”>”
AXTopLevelUIElement: “<AXWindow: “/Users/McUsr/Library/Scripts/Experiments”>”
AXSize: “w=121 h=54”
AXPosition: “x=952 y=677”
AXEnabled: “true”
AXFocused: “false”
AXTitle: “EntityListerOLD.scpt”
AXFilename: “EntityListerOLD.scpt”
AXURL: “file://localhost/Users/McUsr/Library/Scripts/Experiments/EntityListerOLD.scpt”
AXSelected (W): “true”[/code]
There is no group here, which may be related to the skin. But say I wanted to produce a piece of code that worked on my machine. But when I try things like this, -which it is natural for me to deduce that would work. -It just doesn’t.


tell application "System Events" to return attribute "AXImage" of (UI element 1 of t's scroll area -1 where it is selected)

”> System Events got an error: Can't get UI element 1 of last scroll area of splitter group 1 of window "/Users/McUsr/Desktop" of application process "Finder" whose selected = true. Invalid index.

I wonder if any of you have any idea as to how I should reformulate that line to make it work?
Or if there is any thing else to it, -that the “group 1” is something intrinsic that I just need to add to make it work, or something?

Well, that’s odd, McUsr. It worked with icon view on my SL machine immediately after I read your post above, but then it started to error as you describe. Analysis shows that ‘group 1’ has become the more generic ‘UI element 1’. Changing this class in the icon_view() handler makes the script work again (in SL) for me. Does it make any difference for you?

on icon_view(t)
	tell application "System Events" to return value of attribute "AXFilename" of (UI elements of UI element 1 of last scroll area of t where it is selected)
end icon_view

I’ll monitor what’s going on and adjust the script accordingly when I’m happy with the fix.

Hello Nigel.

Very big difference, it works perfectly! :slight_smile: And I have learned that Ui elements is what is contained within an Ui Element 1.

Thank you very much. I think the only way to learn this “lore” is to rip different scripts apart, and try to understand what happens with them with Ui Browser and the Accessibility Inspector.

Thank you very much for your efforts with the black arts! This is an overly useful handler when you need it, The alternative is a rather annoying affaire.

There are no emoticons for a Cheshire - grin. My jaws are aching!

And as for the toolbars: I’ll toggle it on if it were off before calling this routine! :slight_smile:

Yvan pointed out in post #7 that the ‘description’ of a process window is localised. So, whereas the relevant value on my machines is “standard window”, it’s “Fenêtre standard” on his. He suggested filtering instead for the value of attribute “AXSubrole”, which is “AXStandardWindow” whatever the localisation of the computer. I somehow overlooked this in my later posts, so my apologies to Yvan.

The prime_target() handler should therefore look like this:


on prime_target(v, n)
	tell application "System Events" to tell (application process "Finder"'s window n ¬
		whose value of attribute "AXSubrole" is "AXStandardWindow")
		if v then return splitter group 1
		it
	end tell
end prime_target

This works fine on my Snow Leopard machine, but my Tiger one doesn’t like the filter in that form for some reason. What works on both is:


on prime_target(v, n)
	tell application "System Events" to tell (application process "Finder"'s window n ¬
		whose value of attributes contains "AXStandardWindow")
		if v then return splitter group 1
		it
	end tell
end prime_target

Or indeed:


on prime_target(v, n)
	tell application "System Events" to tell (application process "Finder"'s window n ¬
		whose subrole is "AXStandardWindow")
		if v then return splitter group 1
		it
	end tell
end prime_target

I’ve changed to this last version in the script in post #13.

Edit: I’ve decided to forget my earlier reservations in this post about information windows being passed by the filter in prime_target(). You have to know the index of the relevant window anyway in order to use the script, so whether it’s a Finder window or anything else is your own lookout. :slight_smile:

Hello.

That’s right! :slight_smile: I was just about to propose that!

Thank you Nigel, this is great!