get next-to-last window index with applescript - applescript

I'm having trouble reconciling these cases. 3 and 4 are exactly the same except for the application. Same for 5 and 6. I also substituted safari with brave. They seem to work the same. textedit also seems to work like the others. Is finder just a special case or is there something else going on?
# Case 1) Give tell a string, get last window index: succeeds.
tell application "finder"
get index of last window
end tell
# Case 2) Give tell a variable, get last window index: succeeds.
set the_application to "finder"
function_a(the_application)
on function_a(the_application)
tell application the_application
get index of last window
end tell
end function_a
# Case 3) Give tell a variable, get next-to-last window index for finder: fails.
# 651:656: execution error: Finder got an error: Can’t get window before Finder window id 63457. (-1728)
set the_application to "finder"
function_b(the_application)
on function_b(the_application)
tell application the_application
get index of window before last window
end tell
end function_b
# Case 4) Give tell a variable, get next-to-last window index for safari: succeeds.
set the_application to "safari"
function_c(the_application)
on function_c(the_application)
tell application the_application
get index of window before last window
end tell
end function_c
# Case 5) Give tell a string, get next-to-last window index: succeeds.
tell application "safari"
get index of window before last window
end tell
# Case 6) Give tell a string, get next-to-last window index: fails.
1420:1425: execution error: Finder got an error: Can’t get window before Finder window id 63457. (-1728)
tell application "finder"
get index of window before last window
end tell
# Case 7) Give tell a string, get next-to-last finder window index: succeeds.
tell application "finder"
get index of finder window before last window
end tell

Any scriptable application has an unique AppleScript dictionary.
Of course applications can have something in common like windows and documents but the implementation of the windows element and the treatment of indices are individual. There is no general convention how to do that. Even properties with the same name can have different four-character-codes (the internal terminology identifier) in different applications.
For example the NSPositionalSpecifier must be implemented explicitly in the target application to be able to use the before/after syntax.
In my option the effort to write reusable scripts for multiple applications is a waste of time.

I will show you the solution for the Safari as example.
Case ONE (probably, it is your case): you Safari's settings is "open new windows as windows":
set the_application to "Safari"
function_c(the_application)
on function_c(the_application)
run script "tell application \"" & the_application & "\"
get index of window before last window
end tell"
end function_c
Case TWO (if you Safari's settings is "open new windows as tabs"):
set the_application to "Safari"
function_c(the_application)
on function_c(the_application)
run script "tell application \"" & the_application & "\"
get index of tab before last tab of window 1
end tell"
end function_c

The same as the code above, but for the multiple applications:
set the_application to choose from list {"Safari", "Finder", "TextEdit"}
if the_application is false then return
set the_application to item 1 of the_application
function_c(the_application)
on function_c(the_application)
run script "tell application \"" & the_application & "\"
get index of last window
end tell"
end function_c

Other beautiful example:
return {|Safari|:function_c("Safari"), |Finder|:function_c("Finder"), |TextEdit|:function_c("TextEdit")}
on function_c(the_application)
try
run script "tell application \"" & the_application & "\"
get index of last window
end tell"
on error
return missing value
end try
end function_c

Related

AppleScript: cannot get property of window

I'm a newbie in AppleScript.
I'm trying to write a short automator script that involves getting/setting window's miniaturized (or minimized for some apps apparently).
I'm totally lost at the point: Why the first attempt works but not the second one in the following codes?
# Run this, and switch to Google Chrome, within 3 seconds
delay 3
# This works
tell application "/Applications/Google Chrome.app/"
log (get minimized of first window)
end tell
tell application "System Events"
set process_bid to get the bundle identifier of (first application process whose frontmost is true)
set application_name to file of (application processes where bundle identifier is process_bid)
end tell
set front_app to POSIX path of (application_name as string)
# They're same
log (front_app = "/Applications/Google Chrome.app/")
# Then why is this not working?
tell application front_app
log (get minimized of first window)
end tell
The argument of tell application must be a literal (a constant) because the terminology of the application inside the tell block is evaluated at compile time.
You could add a using terms from block, then the argument of tell application can be a variable. But this requires the argument of the block to be a constant.
# Run this, and switch to Google Chrome, within 3 seconds
delay 3
# This works
tell application id "com.google.Chrome" to log (get minimized of first window)
tell application "System Events"
set process_bid to bundle identifier of (1st process whose frontmost is true)
end tell
# They're same
log (process_bid = "com.google.Chrome")
log (run script ("tell application id \"" & process_bid & "\" to minimized of window 1"))
Another (simple) example for testing:
set process_bid to "com.google.Chrome"
run script ("tell application id \"" & process_bid & "\" to minimized of window 1")

what means "tab group 1" in AppleScript, why not "tab group 2"?

I'm trying to understand code belong, and can't find anything explain what means "tab group 1"..
And I don't know how to debug to find the value with "tab group 1"
Btw, I test "tab group 0",its ok, but "tab group 2" its error..
set devices to {}
tell application "System Preferences"
reveal pane "声音"
end tell
tell application "System Events"
tell application process "System Preferences"
repeat until exists window "声音"
end repeat
tell tab group 1 of window "声音"
get properties
click radio button "输出"
tell table 1 of scroll area 1
set selected_row to (first UI element whose selected is true)
set currentOutput to value of text field 1 of selected_row as text
repeat with r in rows
try
set deviceName to value of text field 1 of r as text
set end of devices to deviceName
end try
end repeat
end tell
end tell
end tell
end tell
if application "System Preferences" is running then
tell application "System Preferences" to quit
end if
set text item delimiters to "‡"
set devicesStr to devices as text
set comm to "bash ./main.sh" & " \"" & devicesStr & "\"" & " \"" & currentOutput & "\"" & " output"
log comm
#do shell script comm
AppleScript GUI scripting involves working its way down through the view hierarchy of the application's windows. In this case, 'Tab Group [X]' means that there are at least [X] tab groups within the container at that level of the hierarchy, and you need to determine which one contains the lower-level element you're trying to access. Unfortunately, the elements of the view hierarchy aren't always immediately visible (there may be 'hidden' containers and such), and the hierarchy may change significantly from one app update to the next. That can be headache inducing.
You can debug this manually (with a little patience) by working your way down the hierarchy yourself until you find the elements you need, using a series of every UI element of.. commands. I.e., begin with:
tell application "System Events"
tell application process "System Preferences"
tell window "声音"
get every UI element
end tell
end tell
end tell
Then choose a likely UI element from the list it produces and add a new tell block. However, it's easier to use the Accessibility Inspector app, which gives you a look into the details of any applications view hierarchy. Accessibility Inspector is included with Xcode downloads (which is free, and worth having around); I don't know if there's a place to download it separately.
See Apple's Guide of Testing Accessibility.

Open/search Spotify track in Apple Music (with Applescript)

I'd like an easy way to switch from a Spotify release to the same release in Apple Music.
I already found a way to search for the currently playing Spotify track in the Apple Music web player with Applescript:
tell application "Spotify"
if player state is not stopped then
set currentArtist to artist of current track as string
set currentTrack to name of current track as string
open location "https://music.apple.com/search?term=" & currentArtist & " " & currentTrack
end if
end tell
I'd love to:
Open the search in the native Music.app, not the web player. Is this supported?
Ideally not do a search, but go straight to the same release. Maybe with ISRC codes?
Take any selected Spotify track, not just the currently playing one. Looking at the Spotify Applescript dictionary tells me this in not possible.
had a similar problem right now and quickly hacked it out. In my case I want to simply trigger a search on the Music app.
Opened Automator and created a new "Service".
Workflow receives current > "Text" > in "every application".
Here's the AppleScript:
on run {input, parameters}
tell application "Music"
activate
end tell
tell application "System Events"
tell process "Music"
set window_name to name of front window
set value of text field 1 of UI element 1 of row 1 of outline 1 of splitter group 1 of window window_name to input
keystroke ""
key code 36
end tell
end tell
return input
end run
I saved it as "Find on Music" in Automator and now I can select text, right click > Service > Find on Music and Music plops open and shows me the results for the selected text. I hope you can use some parts of it.
I just figured out how to pass text from wherever to the search field in Music, with help from daemon's answer, which no longer works. This should work for what you want to do in conjunction with what you have.
Replace your "open location" line with a variable name for your concatenated string. Add this code below yours and pass that variable in place of 'input' (in my case 'input' is text from any application, which I use to select text of an artist name in an email/webpage/message that I want to send to Music's search).
First it checks to see if the main Music window is open vs the MiniPlayer, and open it if not to enable search via cmd-O, the cmd-F to find, then passes the input and hits return:
tell application "Music"
activate
end tell
tell application "System Events"
if not (exists (window "Music" of process "Music")) then
tell process "Music"
keystroke "0" using command down
end tell
end if
tell process "Music"
keystroke "f" using command down
keystroke input
key code 36
end tell
end tell
So, something like this (I don't have Spotify to check that section, but this should work assuming your code there is correct):
tell application "Spotify"
if player state is not stopped then
set currentArtist to artist of current track as string
set currentTrack to name of current track as string
set spotTrack to currentArtist & " " & currentTrack
end if
end tell
tell application "Music"
activate
end tell
tell application "System Events"
if not (exists (window "Music" of process "Music")) then
tell process "Music"
keystroke "0" using command down
end tell
end if
tell process "Music"
keystroke "f" using command down
keystroke spotTrack
key code 36
end tell
end tell
The only thing I couldn't figure out is how to check if the search field is already in focus, because if it is, the cmd-F causes a system alert sound. Generally not an issue as typically you'll search and interact with something else before running this script again, so calling it good. :)

How to get name of frontmost app with AppleScript and use it to get the filepath

What I try to do:
When I'm in one of my text editors (TextEdit, Byword, FoldingText) I want this AppleScript to display the file path.
I figured asking for the frontmost window app get's me the apps name nice and easily and then I can ask for the POSIX path in the next step.
The Problem:
The script is already 99% there, but I'm missing something. When I try to use the variable of activeApp it doesn't work and I get this error:
Error Number:System Events got an error: Can’t get application {"TextEdit"}.
-1728
Here's the script:
tell application "System Events"
set activeApp to name of application processes whose frontmost is true
--This doesn't work either:
--do shell script "php -r 'echo urldecode(\"" & activeApp & "\");'"
tell application activeApp
set myPath to POSIX path of (get file of front document)
end tell
display dialog myPath
end tell
If I exchange activeApp with "TextEdit" everything works. Help would be appreciated.
Maybe there's something in here that helps: Get process name from application name and vice versa, using Applescript
Either get the path property of a document or use System Events to get value of attribute "AXDocument":
try
tell application (path to frontmost application as text)
(path of document 1) as text
end tell
on error
try
tell application "System Events" to tell (process 1 where frontmost is true)
value of attribute "AXDocument" of window 1
end tell
do shell script "x=" & quoted form of result & "
x=${x/#file:\\/\\/}
x=${x/#localhost} # 10.8 and earlier
printf ${x//%/\\\\x}"
end try
end try
The first method didn't work with Preview, TextMate 2, Sublime Text, or iChm, and the second method didn't work with Acorn. The second method requires access for assistive devices to be enabled.
You are asking for...
set activeApp to name of application processes whose frontmost is true
Notice "processes", that's plural meaning you can get several processes in response so applescript gives you a list of names. Even though only one application is returned it's still in list format. Also see that your error contains {"TextEdit"}. The brackets around the name mean it's a list, so the error is showing you the problem.
You can't pass a list of names to the next line of code. As such you have a couple of choices. 1) you can ask for only 1 process instead of all processes. That will return a string instead of a list. Try this code...
set activeApp to name of first application process whose frontmost is true
2) you can work with the list by using "item 1 of the list". Try this code...
set activeApps to name of application processes whose frontmost is true
set activeApp to item 1 of activeApps
Finally, you shouldn't be telling system events to tell the application. Separate those 2 tell blocks of code. Here's how I would write your code.
tell application "System Events"
set activeApp to name of first application process whose frontmost is true
end tell
try
tell application activeApp
set myPath to POSIX path of (get file of front document)
end tell
tell me
activate
display dialog myPath
end tell
on error theError number errorNumber
tell me
activate
display dialog "There was an error: " & (errorNumber as text) & return & return & theError buttons {"OK"} default button 1 with icon stop
end tell
end try
I can't promise the "get file of front document" code will work. That depends on the application. Not all applications will understand that request. That's why I used a try block. In any case though you can be certain you are addressing the proper application. Good luck.
I've been using this snippet for a while, seems to work for all Cocoa apps (not sure about X11):
set front_app to (path to frontmost application as Unicode text)
tell application front_app
-- Your code here
end tell
None of this seems to work with a compiled AppleScript saved as an application and placed on the Dock. Whenever you run the application, IT is the frontmost, not the application that is showing its front window. That application becomes inactive as my Applescript runs. How do I write an Applescript application that isn't active when it runs?
I may have found a solution to the problem listed above. Just tell the user to reactivate the desired application, and give them time.
tell application "Finder"
activate
say "Click front window of your application"
delay 5
set myapp to get name of first application process whose frontmost is true
-- etc.
-- your code
end tell

How to open a new window and multiple urls in Safari with apple script?

How do you open a new window in safari and then open multiple tabs with different urls in that window using apple script?
The way to create a new window in Safari is to use the make new document command:
make new document at end of documents with properties {URL:the_url}
This will create a new window with a single tab pointing to the_url and make that window frontmost. Note that make new window at end of windows doesn't work, and just errors out with "AppleEvent handler fails".
Similarly, to create a new tab within a window w, you can use make new tab:
make new tab at end of tabs of w with properties {URL:the_url}
This will create a new tab in window w at the end of the list of tabs; this tab will be pointing to the_url, and it won't be the current tab. Instead of explicitly saying tabs of w, you can also use a tell w block:
tell w
make new tab at end of tabs with properties {URL:the_url}
end tell
That way, tabs implicitly refers to tabs of w.
Putting this all together, we get the following script. Given a list of URLs in the_urls, it will open all of them in a new window; if the_urls is empty, it opens a window with a blank tab.
property the_urls : {¬
"http://stackoverflow.com", ¬
"http://tex.stackexchange.com", ¬
"http://apple.stackexchange.com"}
tell application "Safari"
if the_urls = {} then
-- If you don't want to open a new window for an empty list, replace the
-- following line with just "return"
set {first_url, rest_urls} to {"", {}}
else
-- `item 1 of ...` gets the first item of a list, `rest of ...` gets
-- everything after the first item of a list. We treat the two
-- differently because the first item must be placed in a new window, but
-- everything else must be placed in a new tab.
set {first_url, rest_urls} to {item 1 of the_urls, rest of the_urls}
end if
make new document at end of documents with properties {URL:first_url}
tell window 1
repeat with the_url in rest_urls
make new tab at end of tabs with properties {URL:the_url}
end repeat
end tell
end tell
tell application "Safari"
activate
set the URL of document 1 to "http://www.XXXXXXX.com"
my new_tab()
set the URL of document 1 to "http://www.XXXXXX.com"
end tell
on new_tab()
tell application "Safari" to activate
tell application "System Events"
tell process "Safari"
«event prcsclic» «class menI» "New Tab" of «class menE» "File" of «class mbar» 1
end tell
end tell
end new_tab
Replace the X's with whatever sites you want and keep repeating the code (my new_tab() and set the URL... lines) for each page you'd like to have open.
Referring to this page.
Correct me if this isn't what you were talking about.
Base on Pugmatt's answer I got the following to work...
on run {input, parameters}
tell application "Safari"
activate
make new document with properties {URL:"http://www.apple.com"}
my new_tab()
set the URL of document 1 to "http://www.example.com"
end tell
end run
on new_tab()
tell application "Safari" to activate
tell application "System Events"
tell process "Safari"
«event prcsclic» «class menI» "New Tab" of «class menE» "File" of «class mbar» 1
end tell
end tell
end new_tab
I'm not sure if this is the most efficient way of you this.

Resources