OS X: Accessing the Main Menu of The Frontmost Application - macos

---edit---
there is an application called Keycue that performs this function.
---/edit---
---edit edit---
this is a duplicate. Get a list of all shortcuts from another application
---/edit edit---
I am struggling to write an application that takes the frontmost application's menu bar (i.e. If Safari is open, safari menu, if XCode is open, Xcode menu), and parses the shortcuts from it.
Things I have thus far tried, and failed with:
1: Spent a week learning applescript. Toyed with "System Events" to get menu bar, however there is no information I can glean there that will give me shortcut codes.
2: Considered trying to KVO, with NSWorkspace. Tried to get NSRunningApplication, but there is only a ownsMenuBar property, which is a BOOL, not an NSMenu.
3: Tried to get an NSApplication from NSWorkspace, NSBundle, and NSRunningApplication. All to no avail.
4: Tried to get NSMenu from applescript (No success.)
I think the next thing I would like to try is searching for the NSRunningApplication with a YES for ownsMenuBar, and then try to grab the corresponding NSApplication from.. somewhere. no idea where yet though.
So any suggestions??

Try:
tell application "System Events"
set frontProcess to name of first process whose frontmost = true
tell process frontProcess
get every menu item of menu 1 of menu bar item 2 of menu bar 1
end tell
end tell
EDIT
Once you have this list you can parse each menu item's attributes:
tell application "System Events"
set frontProcess to name of first process whose frontmost = true
tell process frontProcess
set myMenuItems to get every menu item of menu 1 of menu bar item 2 of menu bar 1
set myList to {}
repeat with aMenuItem in myMenuItems
set end of myList to aMenuItem's name
set end of myList to value of aMenuItem's attribute "AXMenuItemCmdChar"
set end of myList to value of aMenuItem's attribute "AXMenuItemCmdModifiers"
end repeat
end tell
end tell

Related

How to get the title of the current Aquamacs window using applescript

I'm trying to programmatically retrieve the major mode of aquamacs. I had a few ideas: get the menubar items, get the window title and parse it using regexes.
I've tried both, and ran into a problem, both the menubar and window arrays are empty, which makes it impossible to do this:
on test()
try
tell application "System Events"
if application "Aquamacs" exists then
-- display notification "It's aquamacs"
tell application "Aquamacs" to activate
if menu bar item "File" of menu bar 1 exists then
display notification "File exists"
--Fails when the file menu bar item clearly is there
else
display notification "File doesn't exist"
end if
else
display notification "It isn't aquamacs"
end if
end tell
end try
end test
test()
or this:
on getAppTitle(appN)
tell application appN
activate
end tell
tell application "System Events"
# Get the frontmost app's *process* object.
set frontAppProcess to first application process whose frontmost is true
end tell
# Tell the *process* to count its windows and return its front window's name.
tell frontAppProcess
if (count of windows) > 0 then --never runs because count is always zero
set window_name to name of every window
end if
end tell
end getAppTitle
getAppTitle("Aquamacs")
and then looking at the file extension.
I don't understand why there's such inconsistency between the system and AppleScript: it clearly has windows which definitely have titles, yet somehow are out of reach of scripts.
The problem is in your code!
In the first block of code, you have if menu bar item "File" of menu bar 1 exists then inside of a tell application "System Events" block without any designated application or application process to query for that information and why it fails.
In the first block of code, there is more then one way to fix it, and one is to change:
if menu bar item "File" of menu bar 1 exists then
To:
if menu bar item "File" of menu bar 1 of application process "Aquamacs" exists then
In the second block of code, tell frontAppProcess equates to:
tell application process "Aquamacs"
Which is why is fails and it needs to be:
tell application "Aquamacs"
In Script Editor with Aquamacs running, run the following code:
tell application "Aquamacs" to count windows
It will return a window count.

Automator script to automatic toggle menu setting in Simulator.app

I need to automate the process of enrolment of Face ID and Touch ID for my UITests. For this purpose, I'm working on an automator script.
My current automator script, which at the moment can automatic click "Enrolled" in the menu:
on run {input, parameters}
if application "Simulator" is running then
tell application "System Events"
set theName to name of the first process whose frontmost is true
end tell
tell application "Simulator" to activate
tell application "System Events"
tell process "Simulator"
tell menu bar 1
tell menu bar item "Hardware"
tell menu "Hardware"
tell menu item "Face ID"
tell menu "Face ID"
click (menu item "Face ID" where its name starts with "Matching")
end tell
end tell
end tell
end tell
end tell
end tell
end tell
tell application theName to activate
end if
return input
end run
The problem is as following. I need to determine, if the device already is enrolled. There is a checkmark, which shows the current state. I have tried to check if the checkmarks is there or not. But I have not been able to make it work yet.
So my question. How can I do, so the script only will press the 'Enrolled' menu item, if the checkmark isn't there?
There's an attribute of menu items called AXMenuItemMarkChar, which is either set to the character representing a check mark next to the menu item (if checked), or missing value.
use application "System Events"
tell application "Simulator" to activate
tell process "Simulator" to tell menu bar 1 to ¬
tell menu bar item "Hardware" to tell menu "Hardware" to ¬
tell menu item "Face ID" to tell menu "Face ID" to ¬
tell menu item "Enrolled"
if the value of the attribute "AXMenuItemMarkChar" is not "✓" then ¬
click it
delay 1 -- !important
return the value of the attribute "AXMenuItemMarkChar"
end tell
In my testing, the attribute returns the correct value if the Simulator app is in focus at the time. The return value of this script should always be "✓", because if the menu item isn't checked, then the script proceeds to click it and put a check mark next to it; and if the menu item is checked, then there's nothing to do but confirm that it is by getting the attribute's value.
One of the issues with your script is that you had the wrong reference to a non-existent menu item (menu item "Face ID" of menu "Face ID" of menu item "Face ID"), and then proceeded to filter it with a where clause against a name that had a different value; hence it would return no value, and you'd have no menu item to click.
Hopefully my script will work for you. Let me know if you have any problems.
The AppleScript contained in this answer was tested on MacOS 10.13. It is an example script to illustrate how to meet your objective. The delay command may need to be adjusted according to your system, and the script may benefit from some error-handling to make it robust.

Selecting Pop Up Menu Buttons in AppleScript

I want to automate clicking a specific pop down menu's item.
For Example, I want to change the Value of "Message receive Sound" to something else. How can I do this with AppleScript? And how can I do this with other pop down menus in AppleScript?
(To open the iMessage Settings menu, shown in the image, type CMD COMMA, once you open iMessage)
Note: I have successfully done this Automator, I just want to do it in applescript.
It's called GUI scripting. You have to identify the reference to the UI element(s).
GUI scripting strongly depends on the system version. If an update changes the UI structure the script will brake.
This selects the sound "Popcorn" in the sound popup menu. It's for El Capitan. In systems < 10.11 the UI elements may be different and the process name might be "iChat"
tell application "System Events"
tell process "Messages"
set frontmost to true
if not (exists (1st window whose value of attribute "AXIdentifier" is "MessagesPreferencesWindow")) then
keystroke "," using command down
repeat until exists (1st window whose value of attribute "AXIdentifier" is "MessagesPreferencesWindow")
delay 0.1
end repeat
end if
tell (1st window whose value of attribute "AXIdentifier" is "MessagesPreferencesWindow")
tell pop up button 4 of group 1
click
delay 0.2
click menu item "Popcorn" of menu 1
end tell
end tell
end tell
end tell

Speed up AppleScript UI scripting?

I'm using NetShade as a proxy service and thought I could try to automate the switching between the different proxies as a nice start for my first AppleScript script.
The NetShade-app has no AppleScript support, so I have to use UI scripting. After a few tries (and some posts here) I managed to have a script, that switches the proxies via the menu bar item (here is a picture of it, since I can't post it inline due to reputation limit).
Unfortunately my code is extremely slow (≈6sec), which makes it kind of impractical as a script. The first menu opens immediately, but the selection of the sub-menu and the proxy server takes several seconds.
I'm using the following code:
set theProxy to "Netshade US 4"
tell application "System Events" to tell process "NetShade"
tell menu bar item 1 of menu bar 2
click
tell menu item "NetShade Proxy" of menu 1
click
tell menu item theProxy of menu 1
click
end tell
end tell
end tell
end tell
I already tried to add ignoring application responses, like suggested in a different thread (link), but that didn't help.
So finally my questions:
Is there a way to speed the process up? Maybe even a way to do all this in the background, without showing the menu items?
P.S.: I'm running OS X 10.9.1
Summary of the fix
To remove delay you need to do two things:
(I) Identify the click which is causing the delay and enclose only that line in the ignoring application responses block as shown below. In my case, it was click bt after which the execution was going into a wait mode for 5 to 6 seconds.
ignoring application responses
click bt
end ignoring
(II) I then also had to kill System Events to and start it again using the following commands.
do shell script "killall System\\ Events"
delay 0.1
-- Rest of the code to click stuff or send keycodes
This resolved the delay issue.
Details
I was having the same problem where I created a script to connect/disconnect my bluetooth headset through AppleScript. The script is given below.
tell application "System Events" to tell process "SystemUIServer"
set bt to (first menu bar item whose description is "bluetooth") of menu bar 1
click bt
tell (first menu item whose title is "SBH80") of menu of bt
click
tell menu 1
if exists menu item "Disconnect" then
click menu item "Disconnect"
else
click menu item "Connect"
end if
end tell
end tell
end tell
The script was working fine but had a problem where it would wait for 5 to 6 seconds after executing "click bt" above. I modified the code as follows and it is working absolutely fine now without any delay.
tell application "System Events" to tell process "SystemUIServer"
set bt to (first menu bar item whose description is "bluetooth") of menu bar 1
ignoring application responses
click bt
end ignoring
end tell
do shell script "killall System\\ Events"
delay 0.1
tell application "System Events" to tell process "SystemUIServer"
tell (first menu item whose title is "SBH80") of menu of bt
click
tell menu 1
if exists menu item "Disconnect" then
click menu item "Disconnect"
else
click menu item "Connect"
end if
end tell
end tell
end tell

In Applescript, how can I find out if a menu item is selected/focused?

I have a script for OS X 10.5 that focuses the Search box in the Help menu of any application. I have it on a key combination and, much like Spotlight, I want it to toggle when I run the script. So, I want to detect if the search box is already focused for typing, and if so, type Esc instead of clicking the Help menu.
Here is the script as it stands now:
tell application "System Events"
tell (first process whose frontmost is true)
set helpMenuItem to menu bar item "Help" of menu bar 1
click helpMenuItem
end tell
end tell
And I'm thinking of something like this:
tell application "System Events"
tell (first process whose frontmost is true)
set helpMenuItem to menu bar item "Help" of menu bar 1
set searchBox to menu item 1 of menu of helpMenuItem
if (searchBox's focused) = true then
key code 53 -- type esc
else
click helpMenuItem
end if
end tell
end tell
... but I get this error:
Can’t get focused of {menu item 1 of menu "Help" of menu bar item "Help" of menu bar 1 of application process "Script Editor" of application "System Events"}.
So is there a way I can get my script to detect whether the search box is already focused?
I solved my problem by working around it. I still don't know how to check if a menu item is selected though, so I will leave this topic open.
You need to use attribute AXMenuItemMarkChar.
Example:
tell application "System Events"
tell process "Cisco Jabber"
set X to (value of attribute "AXMenuItemMarkChar" of menu item "Available" of menu "Status" of menu item "Status" of menu "File" of menu bar item "File" of menu bar 1) is "✓" -- check if Status is "Availible"
end tell
end tell
If the menu item is checked, the return value is ✓, otherwise it is missing value.
Note: This test only works if the application whose menus are being inspected is currently frontmost.
The built in key shortcut Cmd-? (Cmd-Shift-/) already behaves like this. It moves key focus to the help menu's search field if it is not already focused, and otherwise dismisses the menu.
Using /Developer/Applications/Utilities/Accessibility Tools/Accessibility Inspector.app you can use the built-in accessibility system to look at properties of the UI element under the mouse. Take special note of the cmd-F7 action to lock focus on an element and the Refresh button. Sadly the element and property names don't directly match those in the script suite, but you can look at the dictionary for System Events or usually guess the right terminology.
Using this you can determine two things. First, the focused property isn't on the menu item, but rather there is a text field within the menu item that is focused. Second, the menu item has a selected property.
With this, I came up with:
tell application "System Events"
tell (first process whose frontmost is true)
set helpMenuItem to menu bar item "Help" of menu bar 1
-- Use reference form to avoid building intermediate object specifiers, which Accessibility apparently isn't good at resolving after the fact.
set searchBox to a reference to menu item 1 of menu of helpMenuItem
set searchField to a reference to text field 1 of searchBox
if searchField's focused is true then
key code 53 -- type esc
else
click helpMenuItem
end if
end tell
end tell
Though this still doesn't work. The key event isn't firing as far as I can tell, so something may still be hinky with the focused property on the text field.
Anyway, your click again solution seems much easier.
I just came across the need to do this myself for some file processing in Illustrator.
Here is what I came up with:
tell application "Adobe Illustrator"
activate
tell application "System Events"
tell process "Illustrator"
set frontmost to true
set activeMenuItem to enabled of menu item "Unlock All" of menu "Object" of menu bar item "Object" of menu bar 1
if activeMenuItem is true then
tell me to beep 3
else
tell me to beep 2
end if
end tell
end tell
end tell
Done.
This worked with no problem and could be used to iterate a file. I'll probably have to do this many more times in my future automation.
Good luck!
This worked for me to toggle between two menu items, based on which one is selected, using the "selected" property:
tell application "System Preferences"
reveal anchor "keyboardTab" of pane "com.apple.preference.keyboard"
end tell
tell application "System Events" to tell process "System Preferences"
tell pop up button 2 of tab group 1 of window 1
click
delay 0.2
set appControl to menu item "App Controls" of menu 1
set fKeys to menu item "F1, F2, etc. Keys" of menu 1
if selected of appControl is true then
click fKeys
else
click appControl
end if
end tell
end tell

Resources