Show context menu on Mac with keyboard using AppleScript and Automator - macos

I am trying to find a way to bring up the context menu in Finder on a Mac with Yosemite without touching the mouse/touchpad.
A context menu.
After extensive research on this issue, the only possible route seems to be using AppleScript with Automator, and assign keyboard shortcut to it.
The AppleScript below was found on stackoverflow, if I run it inside the Automator, it would bring up the context menu on one of the files on the desktop (not the file currently selected.)
tell application "System Events"
tell process "Finder"
set target_index to 1
set target to image target_index of group 1 of scroll area 1
tell target to perform action "AXShowMenu"
end tell
end tell
Automator screenshot
But I am having trouble getting it to work with keyboard shortcut.
Also I will need to make sure that it brings the menu for the currently selected file.
Can someone provide some insight about how this can be done?

You can read about the script below here: MacScripter / right click
# Copyright © 2012 - 2015 McUsr
run showRightClickMenu
script showRightClickMenu
on run
set mouseLoc to (do shell script "~/opt/bin/MouseTools -location")
set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
tell mouseLoc to set {mouseX, mouseY} to {it's text item 1, it's text item 2}
set {mouseX, mouseY} to {(mouseX as integer), 1200 - (mouseY as integer)}
tell application id "sevs"
set frontProcessName to name of every process whose frontmost is true
-- tell a to set aa to (get its name)
set wnCount to count of windows of process named frontProcessName
if wnCount > 0 then
tell window 1 of process named frontProcessName
set wnPos to its position
set wnsize to its size
end tell
set {wnX, wnY, wnWidth, wnHeight} to {item 1 of wnPos, item 2 of wnPos, item 1 of wnsize, item 2 of wnsize}
set {leftBound, RightBound, upperBound, lowerBound} to {wnX + 1, (wnX + wnWidth - 21), wnY + 50, (wnY + wnHeight - 51)}
if mouseX ≥ leftBound and mouseX ≤ RightBound then
else if mouseX < leftBound then
set mouseX to leftBound
else
set mouseX to RightBound
end if
if mouseY ≥ upperBound and mouseY ≤ lowerBound then
else if mouseY < upperBound then
set mouseY to upperBound
else
set mouseY to lowerBound
end if
end if
end tell
set mouseLoc to "c" & mouseX & " " & mouseY
do shell script "~/opt/bin/cliclick " & mouseLoc
set AppleScript's text item delimiters to astid
end run
end script

This will bring up the context menu of the currently selected file of the desktop:
tell application "Finder"
set sel to get the selection
if (sel is {}) then
log "Nothing selected! Can't proceed"
return
end if
set target_item_name to the name of (item 1 of sel)
end tell
tell application "System Events"
tell process "Finder"
tell group 1 of scroll area 1
set target to the first image whose value of attribute "AXFilename" is target_item_name
tell target to perform action "AXShowMenu"
end tell
end tell
end tell
*Tested on 10.8.5 in script editor

Related

Why is it reporting "Can't get value" when I ask for the title, not the value?

This is the same code as the application from my last few questions but this version is rewritten to run under "Script Editor" for debugging help.
The error is generated by this code's last line. Checking with Accessibility Inspector, ALL of the menu items have NIL values that is why I specifically reference the title instead.
-- `menu_click`, by Jacob Rus, September 2006
--
-- Accepts a list of form: `{"Finder", "View", "Arrange By", "Date"}`
-- Execute the specified menu item. In this case, assuming the Finder
-- is the active application, arranging the frontmost folder by date.
on menuClick(mList)
local appName, topMenu, r
-- Validate our input
if mList's length < 3 then error "Menu list is not long enough"
-- Set these variables for clarity and brevity later on
set {appName, topMenu} to (items 1 through 2 of mList)
set r to (items 3 through (mList's length) of mList)
-- This overly-long line calls the menu_recurse function with
-- two arguments: r, and a reference to the top-level menu
tell application "System Events" to my menuClickRecurse(r, ((process appName)'s ¬
(menu bar 1)'s (menu bar item topMenu)'s (menu topMenu)))
end menuClick
on menuClickRecurse(mList, parentObject)
local f, r
-- `f` = first item, `r` = rest of items
set f to item 1 of mList
if mList's length > 1 then set r to (items 2 through (mList's length) of mList)
-- either actually click the menu item, or recurse again
tell application "System Events"
if mList's length is 1 then
click parentObject's menu item f
else
my menuClickRecurse(r, (parentObject's (menu item f)'s (menu f)))
end if
end tell
end menuClickRecurse
local destination, libraryName, choices
set destination to "/Users/bryandunphy/music"
set libraryName to "Testing.xml"
tell application "iTunes" to activate
menuClick({"iTunes", "File", "Library", "Export Library…"})
tell application "System Events" to set the value of the text field "Save As:" of window "iTunes" of process "iTunes" to libraryName
tell application "System Events" to tell process "iTunes" to tell its front window's group 1's pop up button 1 to click
tell application "System Events" to tell process "iTunes" to set choices to the title of every menu item of menu 1 of pop up button 1 of group 1 of its front window
repeat with ndx from 1 to count of choices
if the value of choices's item ndx is "" then
tell application "System Events" to tell process "iTunes" to select (the menu item of menu 1 of pop up button 1 of group 1 of its front window whose title is equal to item (ndx - 1) of choices)
end if
end repeat
Because the 'choices' variable contains a list of strings, not a list of menu items, so remove the value of.
You can use the index of the menu instead of the whose clause --> whose title is equal to (....).
repeat with ndx from 1 to count of choices
if choices's item ndx is "" then
tell application "System Events" to tell process "iTunes" to select (menu item (ndx - 1) of menu 1 of pop up button 1 of group 1 of its front window)
exit repeat
end if
end repeat

Why does this Applescript cause "Export Library.scpt: execution error: System Events got an error: Can’t get process "i". (-1728)"?

ƒIt's caused when run through osascript from terminal using the command line "osascript Export\ Library.scpt /Users/bryandunphy/Development/iTunesLibraryConsolidator testing.xml".
same script as my last (solved) question but posting it's entirety this time.
-- `menu_click`, by Jacob Rus, September 2006
--
-- Accepts a list of form: `{"Finder", "View", "Arrange By", "Date"}`
-- Execute the specified menu item. In this case, assuming the Finder
-- is the active application, arranging the frontmost folder by date.
on menuClick(mList)
local appName, topMenu, r
-- Validate our input
if mList's length < 3 then error "Menu list is not long enough"
-- Set these variables for clarity and brevity later on
set {appName, topMenu} to (items 1 through 2 of mList)
set r to (items 3 through (mList's length) of mList)
-- This overly-long line calls the menu_recurse function with
-- two arguments: r, and a reference to the top-level menu
tell application "System Events" to my menuClickRecurse(r, ((process appName)'s ¬
(menu bar 1)'s (menu bar item topMenu)'s (menu topMenu)))
end menuClick
on menuClickRecurse(mList, parentObject)
local f, r
-- `f` = first item, `r` = rest of items
set f to item 1 of mList
if mList's length > 1 then set r to (items 2 through (mList's length) of mList)
-- either actually click the menu item, or recurse again
tell application "System Events"
if mList's length is 1 then
click parentObject's menu item f
else
my menuClickRecurse(r, (parentObject's (menu item f)'s (menu f)))
end if
end tell
end menuClickRecurse
-- Created by Bryan Dunphy during January of 2017
--
-- select the folder "directory" in Window "WinName" of Application "appName" and then clicks the default button if requested
-- if WinName is "" then it uses the frontmost window (to allow for unnamed windows)
-- REQUIRES "on handleDir" and "on findRoot" to work!
-- ONLY call switchDir
-- "createIt" is a boolean that will create any missing directories if it is set to "true".
-- "selectDefault" is a boolean indicating whether or not to click the window's default button after selecting the specified directory
-- returns "true" or "false" to indicate success.
-- clicks "Cancel" button on failure
-- Always returns "true" if "createIt" is set to "true"
on switchDir(directory, winName, appName, createIt, selectDefault)
local dirs, delim
set delim to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
set dirs to every text item of directory
tell application "System Events" to tell process appName to set frontmost to true
my findRoot(appName, winName)
repeat with dir in dirs
if not handleDir(dir, winName, createIt) then
tell application "System Events" to tell process appName to tell button "Cancel" to click
return false
end if
end repeat
if selectDefault then keystroke return
return true
end switchDir
on handleDir(dir, winName, appName, createIt)
local foundIt
foundIt = false
local ndx
if winName is not "" then
repeat with ndx from 1 to (count of window winName's list 1)
if window winName's list 1's item ndx's value is equal to dir then
select window winName's list 1's item ndx
foundIt = true
exit repeat
end if
end repeat
else
repeat with ndx from 1 to (count of front window's list 1)
if front window's list 1's item ndx's value is equal to dir then
select front window's list 1's item ndx
foundIt = true
exit repeat
end if
end repeat
end if
if not foundIt then
if createIt then
if winName is not "" then
tell application "System Events" to tell process appName to tell window winName
tell button "New Folder"
click
repeat until window "New Folder" exists
delay 0.5
end repeat
set value of text field 1 of window "New Folder" to dir
tell button "Create" to click
return my handleDir(dir)
end tell
end tell
else
tell application "System Events" to tell process appName to tell its front window
tell button "New Folder"
click
repeat until window "New Folder" exists
delay 0.5
end repeat
set value of text field 1 of window "New Folder" to dir
tell button "Create" to click
return my handleDir(dir)
end tell
end tell
end if
end if
else
return foundIt
end if
end handleDir
on findRoot(appName, winName)
local rootName
if winName is not "" then
tell application "System Events" to tell process appName to tell window winName
tell pop up button 1
click
repeat until menu 1 exists
delay 0.5
end repeat
local ndx
repeat with ndx from 1 to (count of menu 1)
if the title of menu 1's menu item ndx is "" then
set rootName to the title of menu 1's menu item (ndx - 1)
select (menu 1's menu item (ndx - 1))
exit repeat
end if
end repeat
end tell
end tell
else
tell application "System Events" to tell process appName's front window
tell pop up button 1
click
repeat until menu 1 exists
delay 0.5
end repeat
local ndx
repeat with ndx from 1 to (count of menu 1)
if the title of menu 1's menu item ndx is "" then
set rootName to the title of menu 1's menu item (ndx - 1)
select (menu 1's menu item (ndx - 1))
exit repeat
end if
end repeat
end tell
end tell
end if
return rootName
end findRoot
on run (clp)
if clp's length is not 2 then error "Incorrect Parameters"
local destination, libraryName
set destination to clp's item 1
set libraryName to clp's item 2
menuClick("iTunes", "File", "Library", "Export Library…")
set value of parentObject's text field "Save As:" to (libraryName and ".xml")
tell pop up button 1 of group 1 of window "New iTunes Library" of process iTunes of application "System Events" to click
repeat with ndx from 1 to (count of parentObject's menu 1)
if title of menu item ndx is "" then
select menu item (ndx - 1)
exit repeat
end if
end repeat
my switchDir(destination, "iTunes", "iTunes", true, false)
set the value of text field "Save As:" of window "iTunes" to (libraryName + ".xml")
tell button "Save" of window "iTunes" to click
return (destination and "/" and libraryName and ".xml")
end run
Change:
menuClick("iTunes", "File", "Library", "Export Library…")
to:
menuClick({"iTunes", "File", "Library", "Export Library…"})
AppleScript's error reporting is truly awful (no tracebacks, for starters), and osascript is even worse than Script Editor for debugging. If you run the script in SE, it will at least highlight the line in your script where the error occurred. If that doesn't give you a clue, add log commands to report the script's progress. osascript will write logged messages to stderr. In SE, click the awful 'document' (show/hide log) icon at the bottom of the window, then select 'Messages'. If your time is worth more than $100, get yourself a copy of Script Debugger which also lets you add breakpoints and step through and inspect variables as the script runs.

Splitting full-screen apps with AppleScript

I have been using this script in Automator, which toggles apps between full-screen and windowed mode. I am a frequent user of split-screen applications (introduced in El Capitan), so is there any way to modify this script to enable split-screen? I know there's no keyboard shortcut for splitting, so this is definitely a shot in the dark.
I managed to cobble something together, working off an AppleScript/python thing that I found in this MacScripter post. It does the following:
Pulls a list of open application windows from System Events, and allows the user to select one or two (for full screen or split screen)
Launches "Mission Control"
GUI-scripts the Dock to find references to the various window-buttons and spaces in Mission Control that we need to access
Programmatically drags the buttons around to create a new fullscreen or splitscreen space
Clicks on the newly created space to activate it
You might be able to trim some time down on all the half-second delays, but Mission Control is expecting human interaction and handles things lazily. It will miss GUI requests that come too fast.
(* collect names of open app windows *)
tell application "System Events"
set windowNames to {}
set theVisibleProcesses to every process whose visible is true
repeat with thisProcess in theVisibleProcesses
set windowNames to windowNames & (name of every window of thisProcess whose role description is "standard window")
end repeat
end tell
(* choose 1 name for fullscreen, two names for split screen *)
set selectedItems to choose from list windowNames with title "Split It" with prompt "Choose items to add to split view." with multiple selections allowed without empty selection allowed
if selectedItems is false or (count of selectedItems) > 2 then return
set selectedWindow1 to item 1 of selectedItems
if (count of selectedItems) = 2 then
set selectedWindow2 to item 2 of selectedItems
end if
tell application "Mission Control"
launch
end tell
(*
The dock has a set of nested UI elements for Mission Control, with the following structure:
"Mission Control"'s group 1 (the base container)
group 1, group 2, .... (groups for each desktop)
buttons for open windows on each desktop
group "Spaces Bar"
a single button (the '+' buttan to add a new space)
a list
buttons for the desktops and any already-made spaces
*)
tell application "System Events"
tell process "Dock"'s group "Mission Control"'s group 1
tell group "Spaces Bar"'s list 1
set {p, s} to {position, size} of last UI element
set XTarget to (item 1 of p) + (item 1 of s) + 100
set YTarget to (item 2 of p) + (item 2 of s) + 100
end tell
tell group 1
set viewButton1 to button selectedWindow1
set {x, y} to get viewButton1's position
my mouseDrag(x, y, XTarget, YTarget, 0.5)
end tell
tell group "Spaces Bar"'s list 1
set {p, s} to {position, size} of last UI element
set XTarget to (item 1 of p) + (item 1 of s) + 10
set YTarget to (item 2 of p) + (item 2 of s) + 10
end tell
try
tell group 1
set viewButton2 to button selectedWindow2
set {x, y} to get viewButton2's position
my mouseDrag(x, y, XTarget, YTarget, 0.5)
end tell
end try
tell group "Spaces Bar"'s list 1
delay 0.5
set lastUI to last UI element
click lastUI
end tell
end tell
end tell
on mouseDrag(xDown, yDown, xUp, yUp, delayTime)
do shell script "
/usr/bin/python <<END
from Quartz.CoreGraphics import CGEventCreateMouseEvent
from Quartz.CoreGraphics import CGEventCreate
from Quartz.CoreGraphics import CGEventPost
from Quartz.CoreGraphics import kCGEventLeftMouseDown
from Quartz.CoreGraphics import kCGEventLeftMouseUp
from Quartz.CoreGraphics import kCGMouseButtonLeft
from Quartz.CoreGraphics import kCGHIDEventTap
from Quartz.CoreGraphics import kCGEventLeftMouseDragged
from Quartz.CoreGraphics import kCGEventMouseMoved
import time
def mouseEvent(type, posx, posy):
theEvent = CGEventCreateMouseEvent(None, type, (posx,posy), kCGMouseButtonLeft)
CGEventPost(kCGHIDEventTap, theEvent)
def mousemove(posx,posy):
mouseEvent(kCGEventMouseMoved, posx,posy);
def mousedrag(posx,posy):
mouseEvent(kCGEventLeftMouseDragged, posx, posy);
def mousedown(posxdown,posydown):
mouseEvent(kCGEventLeftMouseDown, posxdown,posydown);
def mouseup(posxup,posyup):
mouseEvent(kCGEventLeftMouseUp, posxup,posyup);
ourEvent = CGEventCreate(None);
mousemove(" & xDown & "," & yDown & ");
mousedown(" & xDown & "," & yDown & ");
time.sleep(" & (delayTime as text) & ");
mousedrag(" & xUp & "," & yUp & ");
time.sleep(" & (delayTime as text) & ");
mouseup(" & xUp & "," & yUp & ");
END"
end mouseDrag
I agree with Ted Wrigley's answer above, but I would make the following changes:
(...)
tell application "System Events"
tell process "Dock"'s group "Mission Control"'s group 1
tell group "Spaces Bar"'s list 1
set countSpaces to count of UI elements
set {p, s} to {position, size} of last UI element
set XTarget to (item 1 of p) + (item 1 of s) + (100 * countSpaces)
set YTarget to (item 2 of p) + (item 2 of s) + 100
end tell
(...)
If you want to run this script multiple times, you need to count the number of open Spaces to correctly find the right spot to drag the window to. Otherwise, the windows are always are always dragged to the 2nd position of the Spaces Bar.

Is it Possible to use AppleScript to access right click context?

I'd like to assign a right-click context to a keyboard shortcut using something like an AppleScript.
For example:
If I right click a folder in Dropbox it gives me a "Share Dropbox Link" menu. Is it possible to access this without using the mouse?
According to this post, it seems to be possible to do this within the system, using Universal Access.
You can use my script below for accessing the context menu, you'll need both MouseLocation, and cliclick that you'll find via Google.
You may want to edit it a little (and adjust the hardcoded paths to both MouseLocation, and cliclick), but it should really be no problem, I think you can use System Events and key code commands to navigate the context menu.
set mouseLoc to (do shell script "/usr/local/opt/MouseLocation")
set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ","}
tell mouseLoc to set {mouseX, mouseY} to {it's text item 1, it's text item 2}
set {mouseX, mouseY} to {(mouseX as integer), 1200 - (mouseY as integer)}
tell application "System Events"
set frontProcessName to name of every process whose frontmost is true
-- tell a to set aa to (get its name)
set wnCount to count of windows of process named frontProcessName
if wnCount > 0 then
tell window 1 of process named frontProcessName
set wnPos to its position
set wnsize to its size
end tell
set {wnX, wnY, wnWidth, wnHeight} to {item 1 of wnPos, item 2 of wnPos, item 1 of wnsize, item 2 of wnsize}
set {leftBound, RightBound, upperBound, lowerBound} to {wnX + 1, (wnX + wnWidth - 21), wnY + 50, (wnY + wnHeight - 51)}
if mouseX ≥ leftBound and mouseX ≤ RightBound then
else if mouseX < leftBound then
set mouseX to leftBound
log "a"
else
set mouseX to RightBound
log "b"
end if
if mouseY ≥ upperBound and mouseY ≤ lowerBound then
else if mouseY < upperBound then
set mouseY to upperBound
log "c"
else
set mouseY to lowerBound
log "d"
end if
end if
end tell
set mouseLoc to "c" & mouseX & " " & mouseY
do shell script "/usr/local/opt/cliclick " & mouseLoc
set AppleScript's text item delimiters to astid
Edit:
I didn't know that you couldn't find MouseLocation via Google anymore, I am sorry about that
If you have problems compiling the code found in post # 3 in this thread (http://www.macscripter.net/viewtopic.php?id=33468), then I recommend you use the MouseTool found here :(http://www.hamsoftengineering.com/codeSharing/MouseTools/MouseTools.html) instead, you would then need to change the first four lines of the script to something like:
set mouseLoc to (do shell script "MouseTools -location")
set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
tell mouseLoc to set {mouseX, mouseY} to {it's text item 1, it's text item 2}
set {mouseX, mouseY} to {(mouseX as integer),(mouseY as integer)}
(You'll have to adjust the path to MouseTools as well as to cliclick.)

Applescript for safari to click downloads

I have been trying to write some applescript that checks to see if the Downloads window is open in Safari and if it is to click and open the last file in the list which is the last file that was downloaded but have been having some issues using Accessibility Inspector I get the following :
<AXApplication: "Safari">
<AXWindow: "Downloads">
<AXScrollArea>
<AXList>
<AXGroup: "ExcelTest.xls">
<AXButton: "file icon">
Attributes:
AXRole: "AXButton"
AXRoleDescription: "button"
AXHelp: "Open"
AXFocused: "false"
AXParent: ""
AXWindow: ""
AXTopLevelUIElement: ""
AXPosition: "x=1062 y=396"
AXSize: "w=32 h=32"
AXDescription: "file icon"`
AXEnabled: "true"
Actions:
AXPress - press
I'm not sure how to access the scroll area and list to get at the button.
This works...
set downloadsIsFrontmost to false
tell application "Safari"
set theWindows to name of windows
if (item 1 of theWindows) is "Downloads" then
activate
set downloadsIsFrontmost to true
end if
end tell
if downloadsIsFrontmost then
tell application "System Events"
tell process "Safari"
set theGroups to groups of list 1 of scroll area 1 of window "Downloads"
set lastGroup to last item of theGroups
repeat 2 times
click button 1 of lastGroup
delay 0.05
end repeat
end tell
end tell
end if

Resources