TweetFollow Us on Twitter

Sep 98 Getting Started

Volume Number: 14 (1998)
Issue Number: 9
Column Tag: Getting Started

Apple Events

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 )
{
   char      theChar;

   switch ( eventPtr->what )
   {
      case mouseDown: 
         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 the project by choosing Add Window from the Project menu. The entire AEHandler source code listing can be found in the source code walk-through. You can type it into the AEHandler.c file as you read the walk-through, or you can save a lot of effort by just downloading the whole project from MacTech's ftp site ftp://ftp.mactech.com/src/mactech/volume14_1998/14.09.sit.

Walking Through the Source Code

The AEHandler source code listing begins with a number of #defines -- most of which you'll be familiar with from previous examples.

/********************* constants *********************/

#define kBaseResID         128
#define kALRTResID         128
#define kWINDResID         128

#define kSleep            7
#define kMoveToFront      (WindowPtr)-1L
#define kGestaltMask      1L
#define kActivate         false
#define kVisible          true

#define kWindowStartX      20
#define kWindowStartY      50

#define mApple             kBaseResID
#define iAbout             1

#define mFile              kBaseResID+1
#define iClose             1
#define iQuit              3

As always, the global variable gDone indicates when it's time to exit the main event loop. Variables gNewWindowX and gNewWindowY serve as offsets to stagger open windows. And of course each application-defined function has its own prototype.

/********************** globals **********************/

Boolean      gDone;
short        gNewWindowX = kWindowStartX,
             gNewWindowY = kWindowStartY;

/********************* functions *********************/

void               ToolBoxInit( void );
void               MenuBarInit( void );
void               AEInit( void );
void               AEInstallHandlers( void );
pascal   OSErr   DoOpenApp(   AppleEvent *event, AppleEvent *reply, 
                                 long refcon );
pascal   OSErr   DoOpenDoc(   AppleEvent *event, AppleEvent *reply, 
                                 long refcon );
pascal   OSErr   DoPrintDoc(AppleEvent *event, AppleEvent *reply, 
                                 long refcon );
pascal   OSErr   DoQuitApp( AppleEvent *event, AppleEvent *reply, 
                                 long refcon );
void               OpenDocument( FSSpec *fileSpecPtr );
WindowPtr      CreateWindow( Str255 name );
void               DoError( Str255 errorString );
void               EventLoop( void );
void               DoEvent( EventRecord *eventPtr );
void               HandleMouseDown( EventRecord *eventPtr );
void               HandleMenuChoice( long menuChoice );
void               HandleAppleChoice( short item );
void               HandleFileChoice( short item );
void               DoUpdate( EventRecord *eventPtr );
void               DoCloseWindow( WindowPtr window );

The main() routine does its usual work, but here it also initializes Apple events before entering the main event loop.

/************************ main ***********************/

void   main( void )
{
   ToolboxInit();
   MenuBarInit();
   AEInit();
   
   EventLoop();
}

/******************** ToolBoxInit ********************/

void   ToolboxInit( void )
{
   InitGraf( &qd.thePort );
   InitFonts();
   InitWindows();
   InitMenus();
   TEInit();
   InitDialogs( 0L );
   InitCursor();
}

/******************** MenuBarInit ********************/

void   MenuBarInit( void )
{
   Handle            menuBar;
   MenuHandle      menu;
   
   menuBar = GetNewMBar( kBaseResID );
   SetMenuBar( menuBar );

   menu = GetMenuHandle( mApple );
   AddResMenu( menu, 'DRVR' );
   
   DrawMenuBar();
}

The Apple-defined constant gestaltAppleEventsAttr is a selector code that tells the Toolbox function Gestalt() to return information about the availability of Apple events on the user's machine. If Gestalt() fills feature with the Apple-defined constant gestaltAppleEventsPresent, then we know it's okay to include Apple event code in our program. If there's an error along the way, we call our own DoError() routine (discussed later) to clue the user in to the problem. AEInit() ends with a call to AEInstallHandler(), which is described next.

/********************** AEInit ***********************/

void   AEInit( void )
{
   OSErr   err;
   long      feature;
   
   err = Gestalt( gestaltAppleEventsAttr, &feature );
   if ( err != noErr )
      DoError( "\pError returned by Gestalt!" );
      
   if ( !( feature & ( kGestaltMask << 
                                    gestaltAppleEventsPresent ) ) )
      DoError( "\pThis Mac does not support Apple events..." );
   
   AEInstallHandlers();
}

Each Apple event handler needs to be installed. Looking at how that is done for one handler provides you with enough information to see how any handler is installed. Let's look at how our AEInstallHandlers() function installs the handler routine that's to process Open Application Apple events.

A call to the Toolbox routine AEInstallEventHandler() is made to specify that the application-defined routine DoOpenApp() (covered ahead) is the handler for Open Application events. The first argument to AEInstallEventHandler(), kCoreEventClass, defines the event class of the event to be handled. All four of the required Apple events are considered core events. The second argument, kAEOpenApplication, is an Apple-defined event ID that specifies which particular Apple event is to be handled. The third argument is a pointer to the application-defined function that is to handle this one type of Apple event. When passed a function name, the Toolbox function NewAEEventHandlerProc() returns the needed pointer. The fourth argument, 0L, is a reference value that the Apple Event Manager uses each time it invokes the event handler function. You can safely us a value of 0 here. The final argument is a Boolean value that specifies in which Apple event dispatch table (the means of correlating an Apple event with a handler) the handler should be added. A value of false here tells the Apple Event Manager to add the event handler to the application's own dispatch table as opposed to adding it to the system dispatch table (a table that holds handlers available to all applications). Should the call to AEInstallEventHandler() fail for any reason, we call our DoError() routine (discussed ahead), specifying which Apple event type was the source of the failure.

/***************** AEInstallHandlers *****************/

void   AEInstallHandlers( void )
{
   OSErr            err;
   
   err = AEInstallEventHandler( kCoreEventClass, 
               kAEOpenApplication, 
               NewAEEventHandlerProc( DoOpenApp ), 0L, false );
   if ( err != noErr )
      DoError( "\pError installing Open App handler..." );
   
   err = AEInstallEventHandler( kCoreEventClass, 
               kAEOpenDocuments,
               NewAEEventHandlerProc( DoOpenDoc ), 0L, false );
   if ( err != noErr )
      DoError( "\pError installing Open Doc handler..." );
      
   err = AEInstallEventHandler( kCoreEventClass, 
               kAEPrintDocuments,
               NewAEEventHandlerProc( DoPrintDoc ), 0L, false );
   if ( err != noErr )
      DoError( "\pError installing Print Doc handler..." );
      
   err = AEInstallEventHandler( kCoreEventClass, 
               kAEQuitApplication,
               NewAEEventHandlerProc( DoQuitApp ), 0L, false );
   if ( err != noErr )
      DoError( "\pError installing Quit App handler..." );
}

An Apple event handler has a clearly defined purpose. It extracts data from the Apple event, handles the specific action that the event requests, and returns an error result code to indicate whether or not the event was successfully handled. How the functionality of the event handler routine is implemented is up to you. Each handler has the same general format: it starts with the pascal keyword, has a return type of OSErr, and includes three parameters. The first parameter holds the Apple event to handle. The second parameter is available to hold information that might need to be returned to AEProcessAppleEvent() (recall that this is the routine that invokes the handler). The final parameter is a reference value that your application will typically ignore. DoOpenApp(), which is the event handler for an Open Application Apple event, provides an example:

/******************* DoOpenApp ***********************/

pascal OSErr   DoOpenApp(   AppleEvent *event, AppleEvent *reply, 
                                 long refcon )
{
   OpenDocument( nil );
   
   return noErr;
}

DoOpenApp() is invoked when the AEHandler application is launched. We've opted to have the program open a new window at startup. The application-defined routine OpenDocument() takes care of that task. Later we mention how OpenDocument() works, but there's no need to get into the nitty-gritty. The point has been made: the body of an event handler simply holds the typical Mac code that is needed to solve the task at hand. Once you know the format of an event handler, writing the routine itself is no different than writing any other function.

Some event handlers are easier to write than others. Consider our example program's handler for a Print Document Apple event:

/****************** DoPrintDoc ***********************/

pascal OSErr   DoPrintDoc(AppleEvent *event, AppleEvent *reply, 
                                 long refcon )
{
   return noErr;
}

The AEHandler program doesn't support printing, so we've defined the DoPrintDoc() function to do nothing more than return. So while the program technically does handle a Print Document Apple event, the effect is that the event is ignored. Before you cry "foul!", keep in mind that we haven't discussed the topic of printing in Getting Started -- so we really can't venture off down that road just yet. At least now our application can be considered set up and ready to respond to printing requests from the Finder. Should we get into printing in the future (and if demand warrants it, of course we will), we can add the printing code in the DoPrintDoc() routine.

A Quit Application Apple event is simple to handle -- all we need to do is set the global variable gDone to true. The AEHandler version of this handler also beeps to let you know that it was an Apple event rather than the Quit menu item that caused the application to quit -- your application's version of this handler won't need the call to SysBeep().

/******************* DoQuitApp ***********************/

pascal OSErr   DoQuitApp(   AppleEvent *event, AppleEvent *reply, 
                                 long refcon )
{
   SysBeep( 10 );
   gDone = true;
   
   return noErr;
}

Finally, it's on to a handler that has a little substance to it. If the user drops an AEHandler file onto the AEHandler icon in the Finder, then the AEHandler application receives an Open Document Apple event. Some Apple events are composed of parameters -- records which contain information to be used by the receiving application. An event's direct parameter is the one that the receiving application is to act upon. For the Open Document event, the direct parameter is a descriptor list of the files that are to be opened. A call to AEGetParamDesc() delivers that list to the handler. Here we're saving the list to the local variable docList:

err = AEGetParamDesc( event, keyDirectObject, typeAEList, 
                              &docList);

Next, we determine the number of entries in the list so that we know how many files are to be opened:

err = AECountItems( &docList, &numDocs );

A for loop opens each file in turn. A call to AEGetNthPtr() returns one item from the list. We do a little finagling to make sure that the returned item is received in the form of a file system specification, or FSSpec. The application-defined routine OpenDocument() does the actual opening of the file.

for ( i=1; i<=numDocs; i++ )
{
   err = AEGetNthPtr( &docList, i, typeFSS, &keywd, 
                            &returnedType, (Ptr)&fileSpec,
                            sizeof( fileSpec ), &actualSize );

   OpenDocument( &fileSpec );
}

Calling AEGetParamDesc() resulted in the Apple Event Manager creating a copy of the descriptor list for the program's use. We're done with that copy, so we'll deallocate the memory it occupied:

err = AEDisposeDesc( &docList );

We've just covered it piecemeal, now here's the DoOpenDoc() function in its entirety:

/******************* DoOpenDoc ***********************/

pascal OSErr   DoOpenDoc(   AppleEvent *event, AppleEvent *reply, 
                                 long refcon )
{
   OSErr       err;
   FSSpec      fileSpec;
   long        i, numDocs;
   DescType    returnedType;
   AEKeyword   keywd;
   Size        actualSize;
   AEDescList   docList = { typeNull, nil };

   err = AEGetParamDesc( event, keyDirectObject,
                     typeAEList, &docList);

   err = AECountItems( &docList, &numDocs );
   
   for ( i=1; i<=numDocs; i++ )
   {
      err = AEGetNthPtr( &docList, i, typeFSS, &keywd,
                     &returnedType, (Ptr)&fileSpec,
                     sizeof( fileSpec ), &actualSize );

      OpenDocument( &fileSpec );
   }

   err = AEDisposeDesc( &docList );

   return   err;
}

OpenDocument() selects an appropriate title for the about-to-be-opened window, then calls the application-defined routine CreateWindow() to actually create and display the new window.

/***************** OpenDocument **********************/

void   OpenDocument( FSSpec *fileSpecPtr )
{
   WindowPtr   window;
   
   if ( fileSpecPtr == nil )
      window = CreateWindow( "\p<Untitled>" );
   else
      window = CreateWindow( fileSpecPtr->name );
}

CreateWindow() opens a new window based on AEHandler's one WIND resource, sets the window's title, then offsets the window from the last-opened window. The bulk of CreateWindow() is code for staggering the new window. The newly opened window will be empty -- regardless of the contents of the file that's being opened. The code necessary to read the contents of the file and then display that information in a window is dependent on what your application does.

/***************** CreateWindow **********************/

WindowPtr   CreateWindow( Str255 name )
{
   WindowPtr   window;
   short         windowWidth, windowHeight;
   
   window = GetNewWindow( kWINDResID, nil, kMoveToFront );
   
   SetWTitle( window, name );
   
   MoveWindow( window, gNewWindowX, gNewWindowY, kActivate );
   
   gNewWindowX += 20;
   windowWidth = window->portRect.right - 
                              window->portRect.left;
   
   if ( gNewWindowX + windowWidth > 
               qd.screenBits.bounds.right )
   {
      gNewWindowX = kWindowStartX;
      gNewWindowY = kWindowStartY;
   }
      
   gNewWindowY += 20;
   windowHeight = window->portRect.bottom - 
                              window->portRect.top;
   
   if ( gNewWindowY + windowHeight > 
               qd.screenBits.bounds.bottom )
   {
      gNewWindowX = kWindowStartX;
      gNewWindowY = kWindowStartY;
   }
   
   ShowWindow( window );
   SetPort( window );
   
   return window;
}

If AEHandler encounters an error, it posts an alert that provides some error-specific information to help the user determine what went wrong. The DoError() routine displays this alert, then terminates the program. The Toolbox function ParamText() displays up to four strings in an alert -- the routine looks for occurrences of the strings "^0", "^1", "^2", and "^3" in any static text items in the frontmost alert or dialog box and replaces each with the four strings that were passed to ParamText(). In DoError() we only pass one string (the three "\p" values each representing empty strings) -- the string received in the errorString parameter to DoError(). Refer back to Figure 2 to see how this string will appear in the alert displayed by the subsequent call to StopAlert(). Including a DoError()-type routine in a program is a simple and effective way to handle errors -- consider incorporating such a function in any Mac program you write.

/********************* DoError ***********************/

void   DoError( Str255 errorString )
{
   ParamText( errorString, "\p", "\p", "\p" );
   
   StopAlert( kALRTResID, nil );
   
   ExitToShell();
}

The rest of the code takes care of event-handling, and should look quite familiar to you. That means we can dispense with the walk-through of it. Refer back to recent Getting Started columns for more information on event-handling.

/******************** EventLoop **********************/

void   EventLoop( void )
{      
   EventRecord      event;
   
   gDone = false;
   while ( gDone == false )
   {
      if ( WaitNextEvent( everyEvent, &event, kSleep, nil ) )
         DoEvent( &event );
   }
}

/********************* DoEvent ***********************/

void   DoEvent( EventRecord *eventPtr )
{
   char      theChar;
   
   switch ( eventPtr->what )
   {
      case mouseDown: 
         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;
   }
}

/***************** HandleMouseDown *******************/

void   HandleMouseDown( EventRecord *eventPtr )
{
   WindowPtr   window;
   short         thePart;
   long            menuChoice;
   thePart = FindWindow( eventPtr->where, &window );
   switch ( thePart )
   {
      case inMenuBar:
         menuChoice = MenuSelect( eventPtr->where );
         HandleMenuChoice( menuChoice );
         break;
      case inSysWindow : 
         SystemClick( eventPtr, window );
         break;
      case inGoAway:
         if ( TrackGoAway( window, eventPtr->where ) )
            DoCloseWindow( window );
         break;
      case inContent:
         SelectWindow( window );
         break;
      case inDrag : 
         DragWindow( window, eventPtr->where, 
                         &qd.screenBits.bounds );
         break;
   }
}

/***************** HandleMenuChoice ******************/

void   HandleMenuChoice( long menuChoice )
{
   short   menu;
   short   item;
   if ( menuChoice != 0 )
   {
      menu = HiWord( menuChoice );
      item = LoWord( menuChoice );
      switch ( menu )
      {
         case mApple:
            HandleAppleChoice( item );
            break;
         case mFile:
            HandleFileChoice( item );
            break;
      }
      HiliteMenu( 0 );
   }
}

/**************** HandleAppleChoice ******************/

void   HandleAppleChoice( short item )
{
   MenuHandle   appleMenu;
   Str255      accName;
   short      accNumber;
   switch ( item )
   {
      case iAbout:
         SysBeep( 20 );
         break;
      default:
         appleMenu = GetMHandle( mApple );
         GetItem( appleMenu, item, accName );
         accNumber = OpenDeskAcc( accName );
         break;
   }
}

/***************** HandleFileChoice ******************/

void   HandleFileChoice( short item )
{
   switch ( item )
   {
      case iClose:
         DoCloseWindow( FrontWindow() );
         break;
      case iQuit:
         gDone = true;
         break;
   }
}

/********************* DoUpdate **********************/

void   DoUpdate( EventRecord *eventPtr )
{
   WindowPtr   window;
   
   window = (WindowPtr)eventPtr->message;
   
   BeginUpdate(window);
   EndUpdate(window);
}

/****************** DoCloseWindow ********************/

void   DoCloseWindow( WindowPtr window )
{
   if ( window != nil )
      DisposeWindow( window );
}

Running AEHandler

Save your code, then choose Run from CodeWarrior's Project menu to build and then run the AEHandler application. An untitled window should appear. If it didn't, go back and check your SIZE resource to make sure the High-Level-Event Aware flag is set.

As you look through the code, you'll see that the untitled window is created by the Open Application handler. Now double click on the file test.text. A window titled test.text should appear. This window was created by the Open Documents handler.

With AEHandler still running, go into the Finder and select Shut Down from the Special menu. The Finder should bring AEHandler to the front and send it a Quit Application Apple event. Our Quit Application handler beeps once then sets gDone to true. When you quit normally (by choosing Quit from the AEHandler's File menu), you won't hear this beep.

Till Next Month

Become comfortable with the AEHandler code so you can have all your own Mac applications support at least the four required Apple events. Play around with the AEHandler code. Add error-handling code where appropriate (for instance, the Open Document handler can call AEDisposeDesc() in response to errors returned by calls to AEGetParamDesc(), AECountItems(), and AEGetNthPtr()). Add some code to the Open Document handler to open the specified file and display info about the file in its window (maybe the file's size). While you wait for the next column, read up on the Apple Event Manager in Inside Macintosh: Interapplication Communication. See you next month...

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Chromium 75.0.3770.142 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. Version 75.0.3770.142: Release notes were... Read more
Viber 11.1.0 - Send messages and make fr...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device, so... Read more
Wireshark 3.0.3 - Network protocol analy...
Wireshark is one of the world's foremost network protocol analyzers, and is the standard in many parts of the industry. It is the continuation of a project that started in 1998. Hundreds of... Read more
DEVONthink Pro 3.0beta4 - Knowledge base...
DEVONthink Pro is your essential assistant for today's world, where almost everything is digital. From shopping receipts to important research papers, your life often fills your hard drive in the... Read more
Adobe Creative Cloud 4.9.0.512 - Access...
Adobe Creative Cloud costs $20.99/month for a single app, or $52.99/month for the entire suite. Introducing Adobe Creative Cloud desktop applications, including Adobe Photoshop CC and Illustrator CC... Read more
SketchUp 19.1.174 - Create 3D design con...
SketchUp is an easy-to-learn 3D modeling program that enables you to explore the world in 3D. With just a few simple tools, you can create 3D models of houses, sheds, decks, home additions,... Read more
ClamXav 3.0.12 - Virus checker based on...
ClamXav is a popular virus checker for OS X. Time to take control ClamXAV keeps threats at bay and puts you firmly in charge of your Mac’s security. Scan a specific file or your entire hard drive.... Read more
BetterTouchTool 3.151 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
FontExplorer X Pro 6.0.9 - Font manageme...
FontExplorer X Pro is optimized for professional use; it's the solution that gives you the power you need to manage all your fonts. Now you can more easily manage, activate and organize your... Read more
Dropbox 77.4.131 - Cloud backup and sync...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keeps them up-to-date between systems... Read more

Latest Forum Discussions

See All

Upcoming visual novel Arranged shines a...
If you’re in the market for a new type of visual novel designed to inform and make you think deeply about its subject matter, then Arranged by Kabuk Games could be exactly what you’re looking for. It’s a wholly unique take on marital traditions in... | Read more »
TEPPEN guide - The three best decks in T...
TEPPEN’s unique take on the collectible card game genre is exciting. It’s just over a week old, but that isn’t stopping lots of folks from speculating about the long-term viability of the game, as well as changes and additions that will happen over... | Read more »
Intergalactic puzzler Silly Memory serve...
Recently released matching puzzler Silly Memory is helping its fans with their intergalactic journeys this month with some very special offers on in-app purchases. In case you missed it, Silly Memory is the debut title of French based indie... | Read more »
TEPPEN guide - Tips and tricks for new p...
TEPPEN is a wild game that nobody asked for, but I’m sure glad it exists. Who would’ve thought that a CCG featuring Capcom characters could be so cool and weird? In case you’re not completely sure what TEPPEN is, make sure to check out our review... | Read more »
Dr. Mario World guide - Other games that...
We now live in a post-Dr. Mario World world, and I gotta say, things don’t feel too different. Nintendo continues to squirt out bad games on phones, causing all but the most stalwart fans of mobile games to question why they even bother... | Read more »
Strategy RPG Brown Dust introduces its b...
Epic turn-based RPG Brown Dust is set to turn 500 days old next week, and to celebrate, Neowiz has just unveiled its biggest and most exciting update yet, offering a host of new rewards, increased gacha rates, and a brand new feature that will... | Read more »
Dr. Mario World is yet another disappoin...
As soon as I booted up Dr. Mario World, I knew I wasn’t going to have fun with it. Nintendo’s record on phones thus far has been pretty spotty, with things trending downward as of late. [Read more] | Read more »
Retro Space Shooter P.3 is now available...
Shoot-em-ups tend to be a dime a dozen on the App Store, but every so often you come across one gem that aims to shake up the genre in a unique way. Developer Devjgame’s P.3 is the latest game seeking to do so this, working as a love letter to the... | Read more »
Void Tyrant guide - Guildins guide
I’ve still been putting a lot of time into Void Tyrant since it officially released last week, and it’s surprising how much stuff there is to uncover in such a simple-looking game. Just toray, I finished spending my Guildins on all available... | Read more »
Tactical RPG Brown Dust celebrates the s...
Neowiz is set to celebrate the summer by launching a 2-month long festival in its smash-hit RPG Brown Dust. The event kicks off today, and it’s divided into 4 parts, each of which will last two weeks. Brown Dust is all about collecting, upgrading,... | Read more »

Price Scanner via MacPrices.net

Verizon is offering a 50% discount on iPhone...
Verizon is offering 50% discounts on Apple iPhone 8 and iPhone 8 Plus models though July 24th, plus save 50% on activation fees. New line required. The fine print: “New device payment & new... Read more
Get a new 21″ iMac for under $1000 today at t...
B&H Photo has new 21″ Apple iMacs on sale for up to $100 off MSRP with models available starting at $999. These are the same iMacs offered by Apple in their retail and online stores. Shipping is... Read more
Clearance 2017 15″ 2.8GHz Touch Bar MacBook P...
Apple has Certified Refurbished 2017 15″ 2.8GHz Space Gray Touch Bar MacBook Pros available for $1809. Apple’s refurbished price is currently the lowest available for a 15″ MacBook Pro. An standard... Read more
Clearance 12″ 1.2GHz MacBook on sale for $899...
Focus Camera has clearance 12″ 1.2GHz Space Gray MacBooks available for $899.99 shipped. That’s $400 off Apple’s original MSRP. Focus charges sales tax for NY & NJ residents only. Read more
Get a new 2019 13″ 2.4GHz 4-Core MacBook Pro...
B&H Photo has new 2019 13″ 2.4GHz MacBook Pros on sale for up to $150 off Apple’s MSRP. Overnight shipping is free to many addresses in the US: – 2019 13″ 2.4GHz/256GB 6-Core MacBook Pro Silver... Read more
AirPods with Wireless Charging Case now on sa...
Amazon has extended their Prime Day savings on Apple AirPods by offering AirPods with the Wireless Charging case for $169.99. That’s $30 off Apple’s MSRP, and it’s the cheapest price available for... Read more
New 2019 15″ MacBook Pros on sale for $200 of...
B&H Photo has the new 2019 15″ 6-Core and 8-Core MacBook Pros on sale for $200 off Apple’s MSRP. Overnight shipping is free to many addresses in the US: – 2019 15″ 2.6GHz 6-Core MacBook Pro Space... Read more
Amazon drops prices, now offers clearance 13″...
Amazon has new dropped prices on clearance 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros by $200 off Apple’s original MSRP, with prices now available starting at $1099. Shipping is free. Be sure to... Read more
2018 15″ MacBook Pros now on sale for $500 of...
Amazon has dropped prices on select clearance 2018 15″ 6-Core MacBook Pros to $500 off Apple’s original MSRP. Prices now start at $1899 shipped: – 2018 15″ 2.2GHz Touch Bar MacBook Pro Silver: $1899.... Read more
Price drop! Clearance 12″ 1.2GHz Silver MacBo...
Amazon has dropped their price on the recently-discontinued 12″ 1.2GHz Silver MacBook to $849.99 shipped. That’s $450 off Apple’s original MSRP for this model, and it’s the cheapest price available... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**696259BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 001076-Temecula-Store **Job Description:** The Read more
Business Development Manager, *Apple* Globa...
Business Development Manager, Apple Global Tampa, FL, US Requisition Number:73805 As a Global Apple Business Development Manager at Insight, you proactively Read more
*Apple* Systems Architect/Engineer, Vice Pre...
…its vision to be the world's most trusted financial group. **Summary:** Apple Systems Architect/Engineer with strong knowledge of products and services related to Read more
*Apple* IOS Systems Engineer - Randstad (Uni...
Apple IOS Systems Engineer **job details:** + location:Irvine, CA + salary:$45 - $55 per hour + date posted:Tuesday, July 16, 2019 + job type:Temp to Perm + Read more
Business Development Manager, *Apple* Globa...
Business Development Manager, Apple Global Tampa, FL, US Requisition Number:73805 As a Global Apple Business Development Manager at Insight, you proactively Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.