Standard Additions’ choose file, choose folder and choose file name commands are staples of AppleScript, and on the whole they serve us very well. But there are some areas where they come up short, and in such cases AppleScriptObjC offers a way of working around the limitations.
In the case of choose file name, the dialog includes the tags field, but offers no way for us to retrieve those values. It probably doesn’t matter in most cases, but some users are going to get annoyed if they spend time adding tags, only to have them ignored. Another minor issue is the way the default name is selected; choose file name shows with the whole name selected, rather than the bit before the extension selected.
In some cases, you may want the New Folder button not to appear. Also, choose file name does not give you the option of enforcing a particular extension.
And there’s another option I’ll come to later, where you can add extra stuff to the panel.
The choose file name command shows what is known as an NSSavePanel, and this code shows one directly. It has a lot more parameters, and you can set only those that matter to you.
Before you run the following code, a warning: ASObjC code that involves drawing stuff on screen, like this, has to be run in the foreground on what is known as the main thread. If it is run on a background thread, the likely result is that the app will crash. Scripts are nearly always run on the main thread – the one exception being in script editors, where they are usually run on background threads. So when you are trying out this code, you need to make sure you run it in the foreground.
In Script Editor, you run a script in the foreground by holding the control key when running it, or pressing control-command-R. You can see the Run command change names in the Script menu when you hold down the control key. In ASObjC Explorer you check the ‘Run in foreground’ checkbox at the bottom of the script window.
Because it is easy to forget when you are testing, especially in Script Editor, I’ve added some extra code that checks if it is running in the foreground, and if not shows a dialog and stops, rather than letting the app crash. This simply asks the NSThread class if the current code is on the main thread.
I haven’t written it as a handler because of the potentially varied number of arguments, but you could do so and put it in a script library.
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit" -- needed for NSSavePanel
-- check we are running in foreground
if not (current application's NSThread's isMainThread()) as boolean then
display alert "This script must be run from the main thread." buttons {"Cancel"} as critical
error number -128
end if
-- make panel
set savePanel to current application's NSSavePanel's savePanel()
tell savePanel
-- set main values
its setMessage:"Your message here." -- AS's prompt
its setNameFieldStringValue:"Untitled.txt" -- AS's default name
-- other values you *can* set
its setDirectoryURL:(current application's class "NSURL"'s fileURLWithPath:(POSIX path of (path to desktop))) -- AS's default directory
its setTitle:"Save File" -- Panel's title; default is "Save"
its setPrompt:"Save" -- Override name on button; default is "Save"
its setCanCreateDirectories:true -- whether New Folder button appears; default is true
its setAllowedFileTypes:{"txt"} -- extensions or UTIs; provide missing value if any are allowed
its setAllowsOtherFileTypes:false -- whether user can enter other file type; default is false
its setShowsHiddenFiles:false -- whether hidden files appear; default is false
its setTreatsFilePackagesAsDirectories:true -- whether packages are treated as files or folders; default is files
its setShowsTagField:true -- whether to show the tags field; default is true
its setTagNames:{"Important"} -- initial list of proposed tags; default is {}
end tell
-- show panel
set returnCode to savePanel's runModal()
-- eror if Cancel pressed
if returnCode is (current application's NSFileHandlingPanelCancelButton) then
error number -128
end if
-- get entered path and tags
set thePosixPath to (savePanel's |URL|()'s |path|()) as text
set theTags to savePanel's tagNames() as list
return {thePosixPath, theTags}
There are similar alternatives for choose file and choose folder, and I’ll cover them shortly.