Toggling folder views

Hi there,

I’ve had a look at a few articles to try and find something that will help me with this but I’m not 100% sure if it’s what I need.

Basically what I’m trying to do is put together a script that will toggle a folder view in a list.
So let’s say I highlighted a folder, in a list of folders, in finder, I’d then run the script and it would effectively click on the little triangle at the side of the folder name. This would then show the contents. It would also toggle the same setting for certain folders that exist within that folder and whose names don’t change.

I’m not sure if I’m heading in the right direction with ‘container’ and ‘expandable’?

Any help would be appreciated, thanks in advance.

Nick

See the Finder’s Dictionary. What you are trying to change is called the “current view”, a property of a Finder Window. It’s constants are: ‘column view’, ‘group view’, ‘icon view’ and ‘list view’. You can find out which is showing by asking:

tell application "Finder" to set i to current view of window 1

which requires that a Finder window actually be showing, or that you bring it to the front first.

To set them, I’d use GUI scripting in the Finder’s “View” menu, but can’t help you with code for that - I’m hopeless at GUI scripting.

Hi Adam,

Thanks for your reply and help.

I think it’s not quite what I’m after. I already have the folder in ‘list’ view.
Under that I have a list of folders. What I’m trying to do is effectively click on the little triangle at the side of the folder name to reveal a list of the items contained within.

Thanks again Adam.

Any further help would be appreciated.

Thanks in advance,

Nick

This script by Kai does it:


tell application "Finder"
	activate
	tell front Finder window
		if not (exists) or (count containers) is 0 then return beep (* or invoke some kind of dialog/error *)
		set l to current view is not list view
		select (* in case some other type of window is frontmost *)
	end tell
end tell

tell application "System Events" to tell window 1 of application process "Finder"
	set t to it
	tell splitter group 1 to if exists then set t to it
	if l then key code 19 using command down
	key code 0 using command down
	key code 124 - (value of UI element 1 of group 1 of (first row of outline 1 of t's ¬
		scroll area -1 where value of UI element 1 of group 1 > -1)) using option down
end tell

tell application "Finder"
	reveal front Finder window's item 1 (* forces a scroll-up after contracting a long list *)
	select {} (* deselect *)
end tell

Hi there,

Having done some further research I’ve managed to get the code below doing what I want:-


tell application "System Events"
	tell process "Finder"
		click UI element 1 of group 1 of row 12 of outline 1 of scroll area 2 of splitter group 1 of window "pdf downloads"
	end tell
end tell

This code toggles the triangle and reveals a list of what’s contained in the relevant folder. On running the script again it unreveals the list.

Now I’m trying to get the result of the code below into the code above:-

tell application "Finder"
	set x to selection
end tell

I think the value of AXRow is what I need. The ‘row’ value in the top code corresponds with the position of the folder in the list i.e. position 12. I’ve tried setting up a few folders and changing this value and it seems to work.

Any help of how I can get the ‘row’ value of the selected item would be appreciated.

Thanks

Nick

Hi Adam,

Thanks for your reply, I just noticed after I’d posted my last one.

Thanks again for your help with this.

Cheers,

Nick

Hello

I downloaded the kai’s script and it doesn’ work on a french keyboard.

May you explain what are the used key code supposed to do?

keystroke works on every keyboard (at least occidental ones) while key codes are dedicated to some ones.
The one used here are OK for an american keyboard, not for a french one.

Yvan KOENIG

On my machine, KeyCode 19 (decimal) is the number 2, used as command-2 in the View menu of the Finder menu.

Hi Adam,
Thanks again for the posting of Kai’s code it’s been a big help.

I’m still trying to get the current row position of a selected folder in a list of folders. That will then allow me to toggle the view of just that folder.

Kai’s solution toggles multiple folders which in some circumstances will be useful, however the folder I’m working with has hundreds of folders in it and I don’t want to toggle the view for all of them.

Thanks again,

Nick

Hello

I have no luck with the kai’s script on my french system.

I tried to replace the “non-working” key code 19 . instruction by
keystroke “2” using {command down, option down, shift down} – what I must type by hand
without success.

So I tried to trigger directly the menu item by
click menu item 2 of menu 5 of menu bar 1

no success.

At last I move the instruction in the first tell block like that:

tell application “Finder”
activate
tell front Finder window
if not (exists) or (count containers) is 0 then return beep (* or invoke some kind of dialog/error )
set l to current view is not list view
select (
in case some other type of window is frontmost *)
if l then tell application “System Events” to tell application process “Finder” to click menu item 2 of menu 5 of menu bar 1
end tell
end tell

tell application “System Events” to tell application process “Finder” to tell window 1
set t to it
tell splitter group 1 to if exists then set t to it
log t
key code 0 using command down
key code 124 - (value of UI element 1 of group 1 of (first row of outline 1 of t’s ¬
scroll area -1 where value of UI element 1 of group 1 > -1)) using option down
end tell

tell application “Finder”
reveal front Finder window’s item 1 (* forces a scroll-up after contracting a long list )
select {} (
deselect *)
end tell

This time, the window really switches in columns mode but the trailing code doesn’t.

Here is the report:

tell application “Finder”
activate
exists Finder window 1
true
count every container of Finder window 1
1
get current view of Finder window 1
icon view
select Finder window 1
Finder window id 30
end tell
tell application “System Events”
click menu item 2 of menu 5 of menu bar 1 of application process “Finder”
menu item “Par liste” of menu “Présentation” of menu bar item “Présentation” of menu bar 1 of application process “Finder”
get window 1 of application process “Finder”
window “docs2print” of application process “Finder”
exists splitter group 1 of window 1 of application process “Finder”
true
get splitter group 1 of window 1 of application process “Finder”
splitter group 1 of window “docs2print” of application process “Finder”
(splitter group 1)
key code 0 using command down
get value of UI element 1 of group 1 of row 1 of outline 1 of last scroll area of splitter group 1 of window “docs2print” of application process “Finder” whose value of UI element 1 of group 1 > -1
0
key code 124 using option down
end tell
tell application “Finder”
reveal item 1 of Finder window 1
document file “AW6 pagination continue.scpt” of folder “docs2print” of folder “Desktop” of folder “yvankoenig” of folder “Users” of startup disk
select {}
current application
end tell

I’m back to icon view ;-(((

I don’t understand where is the wrongdoer.

Yvan KOENIG (from FRANCE mardi 26 septembre 2006 20:50:07)

PS I will be back tomorrow

If anyone has any ideas on my last post I’d be grateful.

Cheers,

Nick

Analysis: the GUI Scripting part of kai’s script selects all the items in the window, identifies the current state of the disclosure triangle of the first folder in the list, and performs keycode 124 (right arrow) with option down if the state value’s 0. This discloses the entire contents of the folder and of any others that are selected. If the disclosure triangle value’s 1, the script performs keycode 123 (left arrow) with option down to undisclose (for want of a better word!) the entire contents of all selected folders. This version is adapted from it to demonstrate how it could work with just one selected folder. It’s not meant to be a complete script in itself as I’m not entirely sure how Nick wants it to work.

tell application "Finder"
	activate
	tell front Finder window
		if not (exists) or ((count containers) is 0) or (current view is not list view) then return beep (* or invoke some kind of dialog/error *)
	end tell
	set theFolder to item 1 of (get selection)
	if (theFolder's class is not folder) then return beep
	set selection to theFolder -- Make sure only one item is selected.
	set folderName to theFolder's name
end tell

tell application "System Events" to tell window 1 of application process "Finder"
	set t to it
	tell splitter group 1 to if exists then set t to it
	key code 124 - (value of UI element 1 of group 1 of (row 1 of outline 1 of t's ¬
		scroll area -1 where name of UI elements of group 1 contains folderName)) using option down
end tell

OK. Here’s a multiple-folder version. Select the folders whose disclosure states you want toggled and run the script. It works if you select subfolders in disclosed hierarchies too. I’m not happy about the workround to get full hierarchy disclosure after a restart, but maybe someone can think of a better way to do it:

-- Adapted from a script by kai.

on main()
	-- Check the front Finder window for existence, folders, and list view. Focus on it. Get the selection.
	tell application "Finder"
		activate
		tell front Finder window
			if not (it exists) or ((count containers) is 0) or (current view is not list view) then return beep (* or invoke some kind of dialog/error *)
			set windowTarget to its target
			select -- Ensure the focus is on this window.
		end tell
		set theSelection to selection -- The selection previously made in this window by the user.
	end tell
	
	-- Test the window's GUI structure for the container of the elements we need to find.
	tell application "System Events"
		tell window 1 of application process "Finder"
			set t to it
			tell splitter group 1
				if (it exists) then set t to it
			end tell
		end tell
	end tell
	
	-- Sort the selected items into those that have to be opened and those that have to be closed.
	set {toOpen, toClose} to {{}, {}}
	repeat with i from 1 to (count theSelection)
		tell application "Finder"
			set thisItem to item i of theSelection
			set isFolder to (thisItem's class is folder)
		end tell
		
		if (isFolder) then
			-- If this item's a folder, get its name.
			tell application "Finder" to set folderName to thisItem's name
			
			-- Get the current disclosure triangle status for the list-view line containing that name.
			-- 0 = triangle pointing right (folder contents hidden); 1 = triangle pointing down (folder contents revealed).
			tell application "System Events" to set triangleState to (value of UI element 1 of group 1 of (row 1 of outline 1 of t's ¬
				scroll area -1 where name of UI elements of group 1 contains folderName))
			
			-- Before sorting this item, check that it won't be hidden by the closure of a folder further up the hierarchy.
			-- If it will be, there's no point in including it, since the system will ignore it and it complicates the reselection process below.
			tell application "Finder"
				set wontBeHidden to (thisItem's container is windowTarget)
				if not (wontBeHidden) then
					-- This is a subfolder in a revealed hierarchy. Check the folders further up the hierarchy.
					-- Folders come before their subfolders in the 'entire contents' list, so any
					-- up-hierarchy ones that are to be closed in this run have already been identified.
					set theContainer to thisItem's container
					repeat until ({theContainer} is in toClose) or (theContainer is windowTarget)
						set theContainer to theContainer's container
					end repeat
					if (theContainer is windowTarget) then set wontBeHidden to true
				end if
				-- If this folder won't be hidden, add it to the appropriate list for opening or closing.
				if (wontBeHidden) then
					if (triangleState is 0) then
						set end of toOpen to thisItem
					else
						set end of toClose to thisItem
					end if
				end if
			end tell
		end if
	end repeat
	
	-- Bulk-toggle the folders to be closed, then those to be opened.
	toggle(toClose, 123) -- 123 is the key code for left arrow.
	toggle(toOpen, 124) -- 124 is the key code or right arrow.
	
	-- Display the window contents from the top and reselect the toggled folders.
	tell application "Finder"
		reveal item 1 of windowTarget
		set selection to toOpen & toClose -- 'select' would show any subfolders in a new window.
	end tell
end main

(* Bulk-perform all the toggling in a given direction. (Open or close). *)
on toggle(folderList, keyCode)
	if ((count folderList) > 0) then
		tell application "Finder" to set selection to folderList
		
		tell application "System Events"
			if (keyCode is 124) then
				-- After a restart, option-right-arrow doesn't reveal entire hierarchies immediately,
				-- but adds a layer at a time to each hierarchy as it's used successively on the
				-- hierarchy's top folder. Once the lowest level's reached, the entire hierarchy's
				-- revealed every time - except that the system remembers which subfolders
				-- have been opened or closed differently from the rest. To allow for post-restart
				-- situations, this handler does six option-right-arrows when revealing hierarchies.
				-- Increase the number if necessary.
				repeat 5 times
					key code keyCode using option down
					delay 0.2
				end repeat
			end if
			key code keyCode using option down
		end tell
	end if
end toggle

main()

Edit: Improved script substituted.

Thanks to Nigel Garvey comments I was able to adjust the kai’s script so that it works on a french keyboard.

I assumes that as is, it works also on US ones.


tell application "Finder"
	activate
	tell front Finder window
		if not (exists) or (count containers) is 0 then return beep (* or invoke some kind of dialog/error *)
		set l to current view is not list view
		select (* in case some other type of window is frontmost *)
	end tell
end tell

tell application "System Events" to tell application process "Finder"
	if l then click menu item 2 of menu 5 of menu bar 1 (* View > as list *)
	tell window 1
		set t to it
		tell splitter group 1 to if exists then set t to it
		keystroke "a" using {command down} (* Select All *)
		key code 124 - (value of UI element 1 of group 1 of (first row of outline 1 of t's ¬
			scroll area -1 where value of UI element 1 of group 1 > -1)) using option down
		(* send left or right arrow *)
	end tell
end tell

tell application "Finder"
	reveal front Finder window's item 1 (* forces a scroll-up after contracting a long list *)
	select {} (* deselect *)
end tell

Yvan KOENIG

I’ve now improved the script in my second post (#13) above.

Hi,

Using keyboard shortcuts might work also.

set right_arrow to ASCII character 29
tell application “Finder” to activate
tell application “System Events”
tell process “Finder”
keystroke right_arrow using command down
end tell
end tell

Edited: btw, you can fully expand with the Option key:

set right_arrow to ASCII character 29
tell application “Finder” to activate
tell application “System Events”
tell process “Finder”
keystroke right_arrow using {option down, command down}
end tell
end tell

To collapse the outline, use the left arrow (ascii character 28).

gl,

Hello

The problem was not with the left-right arrows.
It is with the key code 19 and key code 0 ones.

I had to replace them by

(for key code 19 )
if l then click menu item 2 of menu 5 of menu bar 1 (* View > as list *)

(for key code 0 )
keystroke “a” using {command down} (* Select All *)

As Nigel Garvey’s code is not using them there where no reason to speak about it.

I repeat my question: is there somewhere docs giving the keyboard’s code mappings ?

Yvan KOENIG (from FRANCE jeudi 28 septembre 2006 08:09:25)

Hi, Yvan.

I don’t know why kai used key codes rather than keystrokes here, but I’m sure he must have had a good reason. He also seems deliberately to have avoided scripting the Finder itself to set current view of front window to list view. :confused:

I don’t personally know of any documentation regarding key codes, but there’s a useful little freeware application called Full Key Codes that displays them, if you’re interested.

Hi Nigel,

Thanks for your help with my query and for taking the time to write something.
Your solution worked great thanks.

Having played around with what you’d written I then looked at the keycode thing and thought about something really simple and came up with this:-

tell application "Finder"
	activate
	select window "NAME OF WINDOW"
end tell

tell application "System Events"
	tell process "Finder"
		key code 124
		key code 125
		key code 125
		key code 125
		key code 125
		key code 125
		key code 125
		key code 124
	end tell
end tell

(*
123 - left arrow
124 - right arrow
125 - down arrow
126 - up arrow
*)

All you have to do is highlight the folder you want to toggle and run the script. This version opens the folders, you’d obviously need to do something that does the reverse.

Because the folder structure is the same in every case this solution works however I have had one or two anomolies. I think they can be ironed with a little checking. It’s a little bit of a dirty way of doing it but it seems to work and do exactly what I want.

Thanks again for all your help with this query.

Cheers,

Nick

Thank you Nigel Garvey

-1- I was so much involved with the key code versus keystroke problem that I forgot the easy road to switch the window mode ;-(

-2- Of course, I quickly downloaded the app.
So I now know that
key code 0 is “a” on US keyboard but is “q” on french ones.
So I insist to ask for scripters to use keystroke which is stable in lieu of key code which is not.

I met only once a problem with keystroke. For more than one year, on french keyboards, Mac OS X was sending a “q” when we asked it to keystroke “i” (and of course a “Q” when keystroking “I”).

Yvan KOENIG