« Beginning AppleScript Studio
With AppleScript Studio, all this has changed. In fact, a -Studio application is created with Project Builder and Interface Builder, so there's no need to pick up another IDE if you do OS X programming.
I'll walk you through some of the steps I've gone through to develop my current project: RemoteTunes. The application, when it's done, will allow you to control iTunes on a remote computer. For now, let's just let it control a local copy.
This tutorial assumes some knowledge of AppleScript, Project Builder and Interface Builder.
// Getting Started
The first thing to do to build a project, is to, well, start Project Builder. Create a new project (File -> New Project), and in the Assistant, pick "AppleScript Application" under "Application".
You'll get a Project Builder project window, and if you expand a few items in the Groups && Files area, here's what you'll see:
Figure 1: The Groups && Files View
Scripts: This is where the AppleScripts for the program are located.
Resources: Things like your nib (interface) files, credits, icon, and plist files can go here. One notable item is the AppleScriptKit.asdictionary file. This is a big part of what allows the development of -Studio applications.
Other Sources: Typically this is your main.m file, which is a Cocoa/Objective-C file.
Frameworks: This contains the frameworks, such as Cocoa, AppleScriptKit, Foundation and AppKit that your -Studio project will link against.
Products: The finished, compiled application.
// The Interface
Come on, this is a Mac. Let's build an interface! I'm going to assume here that you know a bit about Interface Builder. If not, some playing around with it will show you most of what you need to know (Hint: Command-Shift-I brings up the Info window, which is very useful). If enough requests come in, I'll write an IB tutorial! Double-click the MainMenu.nib file in the above list. This will load Interface Builder. Size the window to 425 by 125 pixels.
In the Info window for "Window", set the Window Title to "RemoteTunes":
Figure 2: The Window Attributes
Next, from the Cocoa-Views part of the Palettes window, drag in 3 "System Font Text" NSTextField variants. Next, drag in 3 NSButtons. These are the main interface elements for now. Arrange them so they look something like this:
Figure 3: The Main Layout
Note that the "System Font Text" elements were centered in the Info window, under the Attributes section. It looks like this:
Figure 4: Centering Elements
Next, we should title things, so we don't have lots of Buttons and System Font Text items lying around. To rename an item in the interface, double-click on it and type. From left to right, name the buttons "Back", "Forward" and "Play/Pause". The three text fields should be "Artist", "Album" and "Title", from top to bottom and left to right. We've gotten pretty far, but our application still has no way of identifying the UI elements! So, in the Info window, select "AppleScript" from the popup menu (or hit Command-6). For the first ("Back") button, type "backButton" in the name field. Next, expand the "Action" triangle and check "clicked". Below, under "Scripts", check "RemoteTunes.applescript". Do the same for the other two buttons, naming them "forwardButton" and "ppButton". For the text areas, simply name them "artistText", "albumText" and "titleText", without checking any boxes. We have a few more things to do, and you'll see why later. Click anywhere in the main window that's not occupied by a UI element. Back in the Info window, give the window an AppleScript name "mainWindow" and check "awakeFromNib", which is under "Nib". Make sure you check "RemoteTunes.applescript" as well. Next, go to the window called "MainMenu.nib" and click on the item called "File's Owner". Back in the Info window, associate this with "idle" and "should quit when last window closed", which are under "Application". Make sure you check "RemoteTunes.applescript" for these also.
At this point, click the "Edit Script" button at the bottom of the Info window. This will take you back to Project Builder, but don't start coding just yet...
// A Word about Event Handlers
When you click a button, you expect it to do something. Well, in AppleScript Studio, the way to tell your application that clicking on a button should do something is done via Event Handlers. You associate UI elements (which can be anything in Interface Builder) with certain events, and then your application knows to call these event handlers to call the event handler when that action (such as clicking on a button) is performed.
Handler, by the way, is the AppleScript word for what's knows as a "function" or "method" in other languages. It lets you group code together and reuse it in multiple places.
// Let's Write Some Code!
So, you're in Project Builder, and you see things like this in the editor:
on clicked theObject (*Add your script here.*) end clicked
Sure, you'd like to add your script there, but what do you write? The purpose of the three buttons should be pretty obvious, so let's code those first. Remember how we associated the buttons with the "clicked" handler? Here"s where we tell the application what to do when a button is clicked. Replace (*Add your script here.*) right after on clicked theObject with this:
if the name of theObject is equal to "backButton" then using terms from application "iTunes" tell application "iTunes" previous track end tell end using terms from else if the name of theObject is equal to "forwardButton" then using terms from application "iTunes" tell application "iTunes" next track end tell end using terms from else if the name of theObject is equal to "ppButton" then using terms from application "iTunes" tell application "iTunes" playpause end tell end using terms from end if
What's this do? Well, let's start with the first line. theObject is a generic way to refer to the item (in this case, a button) that caused the clicked handler to be called. So, we see that if the name of span class="code">theObject is "backButton" (this is what we gave our Back button as a name in the AppleScript part of the Info window, remember?), it executes a block of code. using terms from allows us to use words from the specified application"s scripting dictionary, and a tell block is what allows inter-application communication. The words previous track signal to iTunes that it should go back one track. Like C and other languages, you must close your blocks in the opposite way you opened them, so here we close the tell, then the using terms from and the if in that order.
We do the same thing with the "forwardButton", noting that next track tells iTunes to play the next track. Again, the blocks are closed, and we get to the third and final if statement. playpause tells iTunes to either play if it is currently paused, or pause if it's currently playing. This is much easier than writing your own code to get the "player state" as it's called, and then call either play or pause. (I know, I did it this way first!)
// Time To Feel Good
At this point, RemoteTunes works. Granted, it's not very functional, but it works. To prove it to yourself, choose Build && Run from the Build menu. If all went well, you should see your application come up after a bit of compile time, and the three buttons should work. If iTunes wasn't running, it's been opened by the application. If the application doesn't even build (you'll know because your text will turn colors when it does), make sure you typed (or copied && pasted) correctly what was above. If it does build and run, but the buttons don't work, go back to Interface Builder and make sure the names are typed in correctly and that each button is associated with the "clicked" event and the "RemoteTunes.applescript" item.
// Give Some Feedback
Those text fields are there for a reason! They'll display the artist, album and track of the current song iTunes is playing. So let's make them do it. This information will probably change when a button is clicked, so enter the following code after the "end if" but before the "end clicked":
using terms from application "iTunes" tell application "iTunes" if exists (database ID of current track) then set cur_song to the name of the current track set cur_artist to the artist of the current track set cur_album to the album of the current track else set cur_song to "" set cur_artist to "No song currently playing." set cur_album to "" end if end tell end using terms from tell window "mainWindow" set the contents of text field "titleText" to cur_song set the contents of text field "artistText" to cur_artist set the contents of text field "albumText" to cur_album end tell
We've got two parts to this code: the first gets information from iTunes. It checks to see that there is a current track, by checking for the attribute of every track called the database ID. If there's no track playing or paused, then there"s no current database ID, so we can't get any information, so in the else block we set three variables to default values. If, however, there is a song playing, then we get certain information about that song from iTunes. Here, name, artist, album and current track are special words for iTunes, using normal AppleScript syntax.
The contents of the second block are new to AppleScript Studio. We're telling the window we gave an AppleScript name of "mainWindow" to to do three very similar things. Each one of the text areas in our application has an attribute called contents, which is modifiable. So, if we say set the contents of text field "albumText" to cur_artist we're setting the text that's displayed by that text area. The reason for the tell block is that AppleScript Studio refers to things in a hierarchy, so it knows which "titleText" we'd be talking about if we had more than one.
At this point, you may want to build and run your application again, because it's so cool.
// A Few More Things
Remember how we associated our window with "awake from nib"? This is an event that is sent when the nib file is loaded on application launch. If iTunes is already playing a song, let's have it display the information for the song currently playing. The easiest way to do this, for now, is to copy and paste the above code sample over the (*Add your script here.*) after on awake from nib theObject. Now, every time the application is launched, the information will be updated.
Don't put anything else on your clipboard just yet, though! Remember how we also associated our application (via the File's Owner) with "idle" and "should quit when last window closed"? These do cool things as well. So, in the spirit of the above instructions, paste the same code into the on idle handler. The last line before end idle needs to read return 5, but you can replace the 5 with any number. This is the amount of time, in seconds, that the application will wait before calling this code again. With this, the information in the window will be updated every so often, which is helpful when the track changes naturally and the information should be updated. Inside the on should quit when last window closed block, simply type return true. This way, when the last (and only, in our case) window of the application is closed, the application itself will quit. You could return false, but this is the default behavior anyway.
So, AppleScript is powerful. AppleScript Studio is not only more powerful, but it's fun, too. At least, to programmers it is. At this point, you're no -Studio expert, but you're on your way. Some good online resources are:
http://www.lists.apple.com/mailman/listinfo/applescript-studio - the AppleScript Studio mailing list
http://www.lists.apple.com/mailman/listinfo/applescript-users - the AppleScript Users mailing list
When you're in Project Builder, you can also get information on what keywords an application has by going to Open Dictionary in the File menu and picking the application in the list that comes up.
In /Developer/Documentation/CoreTechnologies/AppleScriptStudio/StudioReference/ is a PDF called "studioreference.pdf" which is very useful for reference, or just browsing if you're learning new things.
For more information on AppleScript itself, see /Developer/Documentation/Carbon/pdf/AppleScriptLanguageGuide.pdf
And one shameless self-plug: On my website I'll be documenting my experiences with both AppleScript Studio and a bit of Cocoa. Enjoy!