Sep 98 Getting Started
Volume Number: 14
Issue Number: 9
Column Tag: Getting Started
by Dave Mark and Dan Parks Sydow
How a Mac program handles Apple events
An Apple event is a high-level event that allows a program to communicate with
another program, with the Finder, or even with itself. The program that issues the
Apple event is referred to as the client application, while the program that receives
and responds to the event is called the server application. Apple events are especially
important when the Finder needs to communicate with a program. For instance, when
the user opens a document by dragging its icon onto the icon of the application that
created it, an Apple event is involved. In such a case the Finder launches the
application (if it's not already running) and then sends an Open Document Apple event
to the program to tell the program to open the dragged document. In this type of
communication the Finder is the client and the application is the server. This month,
we'll look at how Apple events make this type of common and important
Finder-application communication possible. And, of course, we'll look at how you can
incorporate this behavior into your own Mac applications.
The Required Apple Events
In the very old days (we're talking pre-System 7 here), when a user double-clicked
on a document the Finder first looked up the document's creator and type in its desktop
database to figure out which application to launch. It then packaged information about
the document (or set of documents if the user double-clicked on more than one) in a
data structure, launched the appropriate application, and passed the data structure to
the application. To access this data structure, the application called the routine
CountAppFiles() (to find out how many documents it needs to open or print) then, for
each one, it called GetAppFiles() (to get the information necessary to open the file)
and either opened or printed the file. This model is no longer supported -- it's been
out of date for quite a while. In System 7, and now Mac OS 8, when a user opens a
document the Finder still uses the file's creator and type to locate the right application
to launch. Once the application is launched, however, things differ. Now, the Finder
sends the program a series of Apple events.
• If the application was launched by itself, with no documents, the Finder
sends it an Open Application Apple event. This tells the application to do its
standard initialization and assume that no documents were opened. In response
to an Open Application Apple event, the application will usually (but not
necessarily) create a new, untitled document.
• If a document or set of documents were used to launch the application, the
Finder packages descriptions of the documents in a data structure known as a
descriptor, adds the descriptor to an Open Document Apple event, then sends
the event to the application. When the application gets an Open Document
event, it pulls the list of documents from the event and opens each document.
• If the user asked the Finder to print, rather than open, a document or set
of documents, the Finder sends a Print Document Apple event instead of an
Open Document event. The same descriptor procedure as used for an Open
Document Apple event is followed, but in response to a Print Document Apple
event the application prints rather than opens the document.
• Finally, if the Finder wants an application to quit (perhaps the user
selected Shut Down from the Special menu) it sends the application a Quit
Application Apple event. When the application gets a Quit Application event, it
does whatever housekeeping it needs to do in preparation for quitting, then
sets the global flag that allows it to drop out of the main event loop and exit.
These events are the four required Apple events. As the name implies, your application
is expected to handle these events. For brevity, and to isolate individual programming
topics, previous Getting Started examples didn't include Apple event code. To be
considered a user-friendly, well-behaved program, your full-featured Mac
application must handle these events.
There are a couple of other situations besides the above-mentioned scenarios where
your application might receive one of the required Apple events. For starters, any
application can package and send an Apple event. If you own a copy of CE Software's
QuicKeys, you've got everything you need to build and send Apple events. If you have
the AppleScript extension installed on your Mac, you can use Apple's Script Editor
application to write scripts that get translated into Apple events. If you make your
application recordable (so that the user can record your application's actions using the
Script Editor, or any other Apple event recording application) you'll wrap all of your
program's actions in individual Apple events. This means that when the user selects
Open from the File menu, you'll send yourself an Open Document Apple event. If the
user quits, you'll send yourself a Quit Application event.
In addition to the events described above, there are other situations in which the
Finder will send you one of the four required Apple events. If the user double-clicks on
(or otherwise opens) one of your application's documents, the Finder will package the
document in an Open Document Apple event and send the event to your application. The
same is true for the Print Document Apple event.
The user can also drag a document onto your application's icon. If your application is
set up to handle that type of document, your application's icon will invert and, when
the user releases the mouse button, the Finder will embed the document in an Open
Document Apple event and send the event to your application. Note that this technique
can be used to launch your application or to request that your application open a
document once it is already running.
Apple Event Handlers
Apple events are placed in an event queue, much like the events you already know,
love, and process, such as mouseDown, activateEvt, and updateEvt. So far, the events
you've been handling have all been low-level events -- the direct result of a user's
actions. The user uncovers a portion of a window, an updateEvt is generated. The user
clicks the mouse button, a mouseDown is generated. Apple events, on the other hand,
are known as high-level events -- the result of interprocess communication instead of
user-process communication. As you process events retrieved by WaitNextEvent(),
you'll take action based on the value in the event's what field. If the what field contains
the constant updateEvt, you'll call your update handling routine, etc. If the what field
contains the constant kHighLevelEvent, you'll pass the event to the routine
AEProcessAppleEvent(). Here's a typical event-handling routine that supports Apple
events:
void DoEvent( EventRecord *eventPtr )
switch ( eventPtr->what )
HandleMouseDown( eventPtr );
break;
case keyDown:
case autoKey:
theChar = eventPtr->message & charCodeMask;
if ( (eventPtr->modifiers & cmdKey) != 0 )
HandleMenuChoice( MenuKey( theChar ) );
break;
case updateEvt:
DoUpdate( eventPtr );
break;
case kHighLevelEvent:
AEProcessAppleEvent( eventPtr );
break;
}
}
AEProcessAppleEvent() is a powerful routine whose purpose is to identify the type of
Apple event that is to be processed, and to begin processing that event by passing the
event to an Apple event handler. An Apple event handler is a routine you've written
specifically to handle one type of Apple event. If your program supports the four
required Apple event types, you'll be writing four Apple event handler routines. This
month's example program provides an example of each of these handlers.
Writing an Apple event handler isn't enough -- you also need to install it. You install a
handler by passing its address (in the form of a universal procedure pointer, or UPP)
to the Toolbox routine AEInstallEventHandler(). This installation takes place early in
your program -- typically just after Toolbox initialization and the setting up of your
program's menu bar. Once the handlers are installed, your work is done -- when your
program receives an Apple event the call to AEProcessAppleEvent() automatically
calls the appropriate handler.
AEHandler This month's example program, AEHandler, can be launched like any other
Mac application: by double-clicking on its icon. When launched in this way the
program does nothing more than display an empty window. AEHandler can also be
launched by dragging and dropping an AEHandler file onto the application icon. Using
this second program-starting method opens the dropped file. Dragging and dropping a
file on the application icon of the already-running AEHandler program also results in
the file being opened. And unlike a program that doesn't support the required Apple
events, AEHandler knows how to quit itself when Shut Down is selected from the
desktop's Special menu. When you run AEHandler you'll note that there's not much to
see. For the example program we aren't interested in a fancy interface, though -- so
you aren't getting shortchanged. What AEHandler is doing behind the scenes is far more
interesting: Apple events are responsible for all above-mentioned features. This
month's program, then, serves as a skeleton you can use to add the required Apple
events to your own programs. Creating the AEHandler Resources Start off by creating a
folder called AEHandler in your development folder. Launch ResEdit and create a new
file called AEHandler.rsrc in the AEHandler folder. Create the menu-related resources
-- by now you should be used to creating menu bar and menu resources. The MBAR
resource has an ID of 128, and it references the three MENU resources shown in
Figure 1. Figure 1. The three MENUs used by AEHandler. Now create a WIND resource
with an ID of 128. The coordinates of the window aren't critical -- we used a top of
50, a left of 10, a bottom of 100, and a right of 310. Use the standard document proc
(leftmost in a ResEdit editing pane). The AEHandler program includes some
error-checking code. Should a problem arise, the program posts an alert that holds a
message descriptive of the problem. This alert is defined by an ALRT with an ID of
128, a top of 40, left of 40, bottom of 155, and right of 335. Next, create a
corresponding DITL with an ID of 128 and two items. Item 1 is an OK button with a top
of 85, a left of 220, a bottom of 105, and a right of 280. Item 2 is a static text item
just like the one shown in Figure 2. Make sure to include the caret and zero characters
in the Text field. Figure 2. The static text item for the error alert. That covers the
standard resources. Next come the resources that link specific document types to our
application and that tie a set of small and large icons to our application. The Finder
uses these resources to display an icon that represents our application in different
situations (a large icon when the app is on the desktop, a small icon to display in the
right-most corner of the menu bar when our app is front-most). The Finder uses the
non-icon resources to update its desktop database. Create a new BNDL resource with a
resource ID of 128. When the BNDL editing window appears in ResEdit, select Extended
View from the BNDL menu. This gives you access to some additional fields. Put your
application's four-byte signature in the Signature field. Every time you create a new
application, you'll have to come up with a four-character string unique to your
application. To verify that the signature is unique, you'll need to send it to Apple at
their Creator/File Type Registration web site
(http://developer.apple.com/dev/cftype/). If you don't have a signature handy, and
don't feel like going online to find an unused one to register, feel free to temporarily
use one of ours for now. The signature 'DMDS' is one we've registered, but don't (and
won't) use for any distributed application -- so you won't run into any conflicts with
other programs on your Mac. Now fill in the remaining two fields near the top of the
BNDL resource. As shown in Figure 3, the ID is set to 0 and the © String field holds a
copyright string that will appear in the Finder's Get Info window for your application.
Figure 3. The AEHandler BNDL resource. Finish off the BNDL resource by adding
information about each type of file that the Finder should associate with the AEHandler
program. Select New File Type from the Resource menu. Use the specifications in
Figure 3 to fill out the information for the APPL file type. This ties the first row of
icons to the application itself. To edit the icons, double-click on the icon area and
ResEdit will open an icon family editing window. Back in the BNDL editing window,
select New File Type again to add a second row of file types to the BNDL window. This
time use the specs in Figure 3 to fill out the info for files of type TEXT. By doing this,
we've told the finder that files with the signature 'DMDS' and of type 'TEXT' belong to
the application AEHandler. Once again, double-click on the icon family to edit the
individual icons. If your application will support file types belonging to other
applications, create file type entries in the BNDL resource for them as well, but don't
edit the icons -- leave them blank. Finally, be aware that the Finder uses the file type
entries to determine what files can drop launch your application. Right now, the
Finder will only let you drop-launch files with the signature 'DMDS' and of type 'TEXT'
on AEHandler. To make AEHandler respond to all file types, create a new file type entry
with the file type '****'. Don't edit the icons -- leave them blank. That's it for the
AEHandler.rsrc file -- but not for ResEdit. Save your changes to AEHandler.rsrc and
close the resource file. While still in ResEdit, create a new resource file called
test.text. Select Get Info for test.text from ResEdit's File menu. When the info window
appears, set the file's type to TEXT and its creator to whatever signature you used (if
you used ours, it's DMDS). That's it. Save your changes, quit ResEdit, and get set to
create the project. Creating the AEHandler Project Launch CodeWarrior and create a
new project based on the MacOS:C_C++:MacOS Toolbox:MacOS Toolbox Multi-Target
stationary. Uncheck the Create Folder check box. Name the project AEHandler.mcp and
specify that the project be placed in the AEHandler folder. Immediately edit the creator
information in the target panel of the project settings dialog box (select the project
settings item from the Edit menu, click on 68K Target or PPC Target in the scrollable
list, and then type the four-character creator code in the Creator edit box). Set the
project's creator to the creator you used ('DMDS' if you've followed our suggestion).
Next, be sure that the isHighLevelEventAware flag is set in the 'SIZE' Flags popup
menu. By default it should be. If it isn't, select it -- if it's not set, the Apple Event
Manager won't call your handlers! Remove the SillyBalls.c and SillyBalls.rsrc files
from, and add the AEHandler.rsrc file to, the project. This project doesn't make use of
any of the standard ANSI libraries, so feel free to clean up the project window by
removing the ANSI Libraries folder. Next, choose New from the File menu to create a
new source code window. Save it with the name AEHandler.c, then add the new file to