UI Scripting and the "perform" command

Newbie question…

I’m trying to open a folder, select all items in it, then access the contextual menu. I’m using UI Scripting to try to accomplish this, but have not had any luck. At first, I thought I could use the following, but the “click” never seemed to happen:

tell application “System Events”
tell process “Finder”
set frontmost to true
key down control
delay 2
click selected
key down up
end tell
end tell

Then I tried using the “perform” command, but I can’t get it to work either:

tell application “System Events”
tell process “Finder”
set frontmost to true
perform action “AXShowMenu” of selected of window “Media” end tell
end tell

Any suggestions on getting the contextual menu to show so that I can select an item from it?

Thanks,

Tim

I don’t know, but just out of curiosity what are you trying to click in that contextual that can’t be accomplished with Finder’s dictionary?

I’m trying to access a Contextual Menu plug-in that only exists in that form. If I can find an application that does the same thing, I’ll switch to it.

Tim

Honestly I’ve never heard of someone successfully scripting a context menu, but maybe someone could offer you a work-around if we knew which plugin you’re targeting…

The Contextual Menu I’m trying to script is DVFileDateCM, which gives the date that a DV file was originally recorded. Unfortunately, I haven’t been able to find any other Mac program that does this.

It seems that in UIInspector, the is an “AXShowMenu” action for Finder items, but I can’t get it to perform.

Any help is appreciated.

Tim

OK well I can’t help with getting that metadata. Someone else might know, I imagine it could be scraped out of the file using a shell script or something. I took a second look at the Finder’s contextual menu and was able to select items in it through a somewhat goofy method. Perhaps you can modify this to get at what you need:

tell application "Finder" to activate
tell application "System Events" to tell process "Finder"
	-- this targets the first item of a window that is in icon view and has no toolbar 
	perform action "AXShowMenu" of UI element 1 of group 1 of scroll area 1 of window 1
	(* repeat 14 times
		key code 125 -- down arrow (highlights automator submenu)
	end repeat
	repeat 2 times
		key code 124 -- right arrow (opens automator submenu)
	end repeat
	key code 125 -- down arrow (selects automator workflow)
	keystroke return -- (runs automator workflow)
	*)
end tell

When I tested this, the automator action executed on the actual finder selection as opposed to the not-selected file that was targeted the AXShowMenu action (UI element 1) There may be other UI elements that could be targeted to get similar results.

In my case, it didn’t apply to the selected file.
To have a contextual menu appear on the selected file, use:


set uis to UI elements of group 1 of scroll area 1 of window 1 whose selected is true
	perform action "AXShowMenu" of item 1 of uis

Problem remains to apply contextual menu to a group selection…

A scripted “AXShowMenu” action certainly doesn’t appear to sense a multiple selection in the same way that a manual control-click does. (Hopefully, that won’t be a major problem for Tim.)

Other issues arise if the selection is on the desktop, or if the target window displays a toolbar or not. And since navigation of nested menu items seems to differ slightly from that in regular menus, landing a click on them can also be a bit tricky.

The following suggestion attempts to deal with some of these points (although I’ve no doubt there will be others)… :wink:

on finderSelection()
	set d to path to desktop
	tell application "Finder" to tell (get selection)
		if (count) is 0 then error number -128
		set f to beginning
		tell f's folder
			if it as alias is d then return {f's displayed name, 0}
			{f's displayed name, name}
		end tell
	end tell
end finderSelection

on tgt(t)
	tell application "System Events" to tell process "Finder"
		if t is 0 then return it
		tell window t
			if (count splitter groups) is 0 then return it
			splitter group 1
		end tell
	end tell
end tgt

to contextClick on m at {f, t}
	tell application "System Events" to tell process "Finder"
		-- set frontmost to true (* optional *)
		perform action "AXShowMenu" of UI element f of group 1 of scroll area -1 of my tgt(t)
		delay 0.2
		tell UI element 2
			repeat until exists
				delay 0.2
			end repeat
			repeat with i in m as list
				click menu item i
			end repeat
		end tell
	end tell
end contextClick

The above subroutines can be called with something like:

Or (for nested menu items):

kai,
Terrific ! Even

 contextClick on {"Stuffit", "Archive", "Stuff (.sitx)"} at finderSelection()

does its work…

Kai,

Your script looks like it works well. I’m going to mess around with it some more this weekend to see if I can select multiple files.

Thanks

Tim

As already mentioned Tim, it doesn’t seem possible (AFAICT) to select more than one Finder item and then script a multiple-item contextual menu command.

By this I mean that, if you manually control-click one of several selected Finder items - and then keep the control key depressed, some menu commands may be modified to reflect a multiple selection (“Get Info”, for example, becomes “Get Summary Info”). As things stand, it doesn’t seem possible to script an equivalent action through UI scripting.

However, if you merely wish to apply the same (regular) contextual command to all selected Finder items, then you might like to consider something like this variation of the above script:

on targetArea(FinderTarget)
	tell application "System Events" to tell process "Finder"
		if FinderTarget is 0 then return {group 1 of scroll area -1, false}
		tell window FinderTarget to if (count splitter groups) is 0 then
			set tgt to it
		else
			set tgt to splitter group 1
		end if
		tell tgt to if (count browsers) is 0 then
			tell scroll area -1 to if (count outlines) is 0 then
				{group 1, false}
			else
				{outline -1, true}
			end if
		else
			{list 1 of scroll area -1 of scroll area -1 of browser 1, false}
		end if
	end tell
end targetArea

on FinderSelection()
	set desktopAlias to path to desktop
	tell application "Finder" to tell (get selection)
		if (count) is 0 then error number -128
		tell beginning's folder
			if it as alias is desktopAlias then return my targetArea(0)
			my targetArea(name)
		end tell
	end tell
end FinderSelection

to contextClick on currentMenu at {mainTarget, listView}
	set menuList to currentMenu as list
	tell application "System Events" to tell process "Finder"
		--set frontmost to true (* optional *)
		repeat with currentTarget in (get mainTarget's UI elements whose selected is true)
			delay 0.2
			if listView then
				perform action "AXShowMenu" of UI element 1 of group 1 of currentTarget
			else
				perform action "AXShowMenu" of currentTarget
			end if
			delay 0.2
			tell UI element 2
				repeat until exists
					delay 0.2
				end repeat
				repeat with menuItem in menuList
					click menu item menuItem
				end repeat
			end tell
			delay 0.2
		end repeat
	end tell
end contextClick

Edit: Script revised to take account of different Finder window views, as mentioned (below) by Eelco.

The syntax for calling these handlers remains the same:

Or:

kai,

As my favorite Finder wd view is column view, the script needs some modification:

instead of:

However, this doesn’t quite work well.
While all contextual menus are called, only the first Finder item gets processed.

Eelco Houwink

Good point, Eelco. (Don’t you just hate UI scripting? ;))

I’ve revised the script above in an attempt to make it more tolerant in different situations. This stuff does tend to be a little fragile, but the script now seems to be working reasonably well here…

kai,

GUI & Finder - more than a nuisance couple !
You handled list/column views elegantly & correctly, but get it to work on my system (10.4.2 int’l) I needed to change:

into:

Upside effect: the script now works with both toolbar and toolbar-less Finder windows.
Thanks for your efforts !

Kai,

You’re correct about the fact that it doesn’t click multiple items the same way that a real click of multiple items does. However, that shouldn’t be a big problem for me… I’m going to try to add a loop that will control click each item, save the info that the contextual menu creates, then moves on to the next item. While I don;t know exactly how to do this in Applescript, I’m assuming it will go something like this:

-select all the items in a particular folder
-identify the number of selected items
-go thorugh a subroutine to control click and save the info that’s generated

Sound straight forward?

Tim

kai, Tim,

The attached script temporarily selects individual Finder items from the bunch,
now processing multiple items more reliably.
Also, that solves the problem of changing contxt. menu item names (or was that already solved?)

However, it comes at cost of performance.
So I guess you guys will come up with something smarter…


contextClick on "Get Info" at finderSelection()

on targetArea(FinderTarget)
	tell application "System Events" to tell process "Finder"
		if FinderTarget is 0 then return {group 1 of scroll area -1, false}
		tell window FinderTarget to if (count splitter groups) is 0 then
			set tgt to it
		else
			set tgt to splitter group 1
		end if
		tell tgt to if (count browsers) is 0 then
			tell scroll area -1 to if (count outlines) is 0 then
				{group 1, false}
			else
				{outline -1, true}
			end if
		else
			{list 1 of scroll area -2 of scroll area -1 of browser 1, false}
		end if
	end tell
end targetArea

on finderSelection()
	set desktopAlias to path to desktop
	tell application "Finder"
		tell (get selection)
			if (count) is 0 then error number -128
			tell beginning's folder
				if it as alias is desktopAlias then return my targetArea(0)
				my targetArea(name)
			end tell
		end tell
	end tell
end finderSelection

to contextClick on currentMenu at {mainTarget, listView}
	set menuList to currentMenu as list
	tell application "Finder" to set sell to (get the selection)
	tell application "System Events"
		set tt to (get mainTarget's UI elements whose selected is true)
		set i to 1
		repeat with currentTarget in tt
			tell process "Finder"
				set frontmost to true (* optional *)
				set tt to {}
				tell application "Finder" to select item i of sell
					delay 0.2
				if selected of currentTarget then
					if listView then
						perform action "AXShowMenu" of UI element 1 of group 1 of currentTarget
					else
						perform action "AXShowMenu" of currentTarget
					end if
					delay 0.2
					tell UI element 2
						repeat until exists
							delay 0.2
						end repeat
						repeat with menuItem in menuList
							click menu item menuItem
						end repeat
					end tell
					delay 0.2
					set i to i + 1
				end if
			end tell
		end repeat
	end tell
	tell application "Finder" to select sell
end contextClick

Ok guys…I’ve run into another snag (my newbieness is showing…)

I’m trying to insert a step where the information from the contextual menu will be saved into a new text file for each iteration. However, the script doesn’t seem to like me doing this. I’m inserting my code into the following loop:

                   repeat with menuItem in menuList
                       click menu item menuItem
                   end repeat

I’m going to keep trying by moving my “text file” code into the other loops, but am I missing something here?

Thanks,

Tim