Chapter 14. Beyond Moon Travel: Advanced Topics

There are many more technologies in Carbon, and features in Apple’s programming tools, than what we’ve covered in this book. You’ve learned about the core technologies and features that most applications need, and have had an opportunity to put into practice what you’ve learned by creating the Moon Travel Planner application. But what if you want to write a more advanced application?

You’ll get a taste of three advanced topics that many developers have found useful:

  • Scriptable applications. An application that is scriptable is capable of responding to high-level events sent by scripts, other applications, or the Mac OS itself.

  • Threads and multiprocessing. These are useful technologies for applications that need to do more than one task at a time.

  • Tab controls. A tab control used along with a user pane provides a compact way to present information to the user.

We’ll provide an overview of each topic and give you enough information to get you started implementing each of these features.

Scriptable Applications

AppleScript is a scripting system that lets users control applications in the Mac OS without using a keyboard, mouse, or other input device. Think of it as plotting your course to the moon, then sitting back while the computer does all the work. An application is scriptable if it can perform actions in response to commands it receives. The biggest advantage of making your application scriptable is the flexibility it provides to users, who can write scripts consisting of simple, English-like statements, to:

  • Automate repetitive tasks

  • Use multiple applications to control complex workflows

  • Exercise application features with a creativity even the application’s designer may not have anticipated

Scripting power is multiplied because many parts of the Mac OS are scriptable. For example, you can write scripts that use:

  • The Finder to perform many kinds of operations on windows, files, folders, and other desktop objects

  • Sherlock to perform complex searches

  • QuickTime to manipulate movies and other media

  • ColorSync to control your color management environment

  • Desktop Printing to create and control desktop printers and automate the printing process

You can even streamline your development effort by writing scripts to automate QA testing for your application.

The following sections provide a brief overview of how AppleScript works and how to make your application scriptable.

How Scripting Works

Mac OS X supports the Open Scripting Architecture (OSA), a standard mechanism from Apple Computer to support scriptable applications. Two key parts of the OSA are Apple events and the AppleScript scripting language.

An Apple event is a type of high-level message an application can use to send commands or data to itself or to other applications. The Apple Event Manager supplies functions and data types that applications can use to create, send, and interpret Apple events.

The AppleScript scripting language provides a natural language terminology so that users can write scripts (or series of instructions) made up of simple, English-like sentences. The AppleScript component implements this scripting language. When an application calls an Apple Event Manager function to execute a script, the AppleScript component converts script statements into Apple events to send to the appropriate applications. Mac OS X includes the Script Editor, an application that works with the AppleScript component to let users write, compile, test, and store scripts. The Script Editor is located in /Applications/AppleScript.

Note

The Open Scripting Architecture supports multiple scripting languages with the same application interface. Any interested party can write a scripting component to support another scripting language, such as JavaScript. Also available are third party AppleScript script editors that supply additional features not found in Apple’s Script Editor application.

Here is an example of how a script can perform the same operations a user would perform to control an application. The Apple System Profiler is a scriptable Mac OS X application, located at /Applications/Utilities, that provides information about your computing environment. Figure 14.1 shows a user closing an Apple System Profiler window titled “ASP report 2-17-01” by using the mouse to choose the Close command from the File menu.

Closing an application window with the mouse

Figure 14-1. Closing an application window with the mouse

Example 14.1 shows a simple AppleScript script to close the same window.

Example 14-1. A Script that Closes an Application Window

tell application "Apple System Profiler"
    close window "ASP report 2-17-01"
end tell

You’ll look at how this script works below, but first you’ll need some additional information about AppleScript.

The Apple event object model provides a classification system that specifies items in a computing environment. Objects can range from a computer to a hard drive, an application, a window, or a word in a document. Each object belongs to a containment hierarchy. For example, the statement:

the third word in the first paragraph in the document MyReport

shows the containment for an object representing a specific word in a word-processing document.

A script can manipulate an object by sending commands to an application to get or set the object’s data or perform other operations on it. A scriptable application includes a scripting dictionary, which specifies English-language terms for the objects and commands it understands. Example 14.2 shows the scripting dictionary for the Apple System Profiler application.

The Apple System Profiler application’s scripting dictionary

Figure 14-2. The Apple System Profiler application’s scripting dictionary

You can display an application’s scripting dictionary, if it has one, by dragging the application icon onto the Script Editor icon or by using the Script Editor’s Open Dictionary command.

To put the whole picture together, let’s look again at the script to close a window. Figure 14.3 shows a Script Editor window containing the previously shown script.

A Script Editor window

Figure 14-3. A Script Editor window

Here are some of the actions and interactions that allow the script to work:

  1. The user types the script into a Script Editor window and clicks the Run button.

  2. The Script Editor calls on the AppleScript component to compile and execute the script. Note that a user can perform these steps separately by clicking the Check Syntax button to compile the script, then the Run button to execute it. AppleScript key words, such as tell and end tell, are shown in bold when a script is compiled.

  3. The AppleScript component accesses the Apple System Profiler’s scripting dictionary.

  4. The AppleScript component calls on the Apple Event Manager to send a Close Apple event to the Apple System Profiler. Data in the event specifies the window object ASP report 2-17-01.

  5. The Apple System Profiler application calls Apple Event Manager functions to help it examine the Apple event and obtain the command (Close) and the object (the ASP report 2-17-01 window).

  6. The application closes the window.

  7. For some commands, an application may need to send an Apple event response. For example, if a word-processing application is told to get the third word in a document, it returns an Apple event containing that word.

  8. If there is a response, the AppleScript component interprets it and sends the result to the Script Editor.

  9. The user can display the result, if any, by opening the Script Editor’s Result window.

For these steps to work as described, the target application must support the specified commands and objects. As shown in Figure 14.2, the dictionary for the Apple System Profiler does in fact include both the Close command and the Window object.

Here’s another simple example of what an AppleScript script can accomplish. The script in Example 14.2 sets the numFiles variable to the count of the files in the system folder on the startup disk. The line that starts with -- is a comment and shows where you could add statements to process each of the files in the folder. The phrases system folder and startup disk represent two of several special folder and disk names the Finder understands.

Example 14-2. A Script that Counts the Files in a Folder

tell application "Finder"
    set numFiles to (count the files in the system folder of the startup disk)
    -- Perform some operation on the files.
end tell

AppleScript scripts can perform much more complex operations than those shown here. For example, a script could extract information from a database application, insert it into a spreadsheet document, and print a graph of the inserted data. Or you can write scripts to automate web site or network administration tasks.

AppleScript gets most of its power from the commands that applications implement, not from its own commands. A scriptable spreadsheet application knows all about spreadsheets, but AppleScript has no specific knowledge of spreadsheets (and doesn’t need any).

AppleScript, in fact, contains only a handful of built-in commands (Copy, Count, Get, Set, and Run), but defines many additional commands that applications can, or in some cases must, support. Table 14.1 shows the required commands and some of the more commonly supported optional commands. All applications that are good Mac OS X citizens must support the required commands, even if they add no additional scripting support.

Note

Scripting additions are files that extend AppleScript by providing additional commands or coercions you can use in scripts. A coercion converts a value from one class to another, such as from an integer value to a real value. Many standard coercions are built into AppleScript.

Each scripting addition can contain one or more commands. If a scripting addition is located in the /System/Library/ScriptingAdditions directory in Mac OS X, its commands are available to any script targeting an application on that computer.

A scripting addition is sometimes referred to as an osax, based on its file type of 'osax'. Mac OS X includes standard scripting additions that support actions ranging from working with URLs to speaking text.

Table 14-1. Required and Commonly Used AppleScript Commands

AppleScript CommandDescription
Must Be Supported by All Applications  
Launch

Launches an application without invoking its standard startup procedures.

 
Run

Launches an application and invokes its standard startup procedures.

 
OpenOpens one or more files. 
Reopen

Brings an already open application to the front and re-invokes its standard startup procedures.

 
PrintPrints one or more objects. 
QuitTerminates an application. 
Commonly Supported by Scriptable Applications  
GetReturns the value of an object. 
SetAssigns a value to an object. 
CloseCloses one or more objects. 
SaveSaves an object to a file. 
ExistsDetermines if an object exists. 
Count

Counts elements of a particular class in an object.

 
DeleteDeletes one or more objects. 
Duplicate

Copies an object or objects to a new location.

 
MakeCreates a new object. 
MoveMoves an object or objects. 

One of the advantages of the Apple event object model is that applications don’t need to create a large universe of commands, because the same command can work on many objects. For example, your word-processing application should not define a get word command and a get paragraph command. Instead, it should support the get command for any objects a user would reasonably want to get, which might include words, paragraphs, and other objects.

Note

See AppleScript in a Nutshell by O’Reilly & Associates for documentation covering the AppleScript language and information on scriptable control panels, extensions, and applications in Mac OS 9 and Mac OS X.

The AppleScript Language Guide, available in Carbon Help in the Project Builder Help menu, provides a comprehensive description of the commands, objects, expressions, control statements, and other components of the AppleScript Language.

You can also find lots of scripting information in AppleScript Help, available in the Help Center (in the Finder Help menu).

Making Your Application Scriptable

In previous sections, you saw some of the powerful advantages users can gain from scriptable applications and got an overview of how scripting works in Mac OS X. Traveling to the moon generally involves rockets, but making your application scriptable isn’t rocket science, although it can be somewhat complex. The following sections provide a brief description of the key steps you take to create a scriptable application.

  1. Design scripting in from the beginning

  2. Identify the objects and commands your application will support

  3. Create an 'aete' resource to describe your application’s scripting support

  4. Write handlers for the Apple events you support

  5. Install your Apple event handlers

  6. Process incoming Apple events

As you may have noticed, there are many similarities in working with Apple events and Carbon events. The following sections will point out some of these similarities.

Design scripting in from the beginning

Good scripting support starts with good application design. Before you’ve written a line of code, you need to think about the ways a user might want to control your application with scripts. You should design for the user first and work out the implementation details later.

As part of your design, work to separate program actions from user interface items. A scripter wants to accomplish a result (open the document "MyFile"), not necessarily perform the user interface actions they would use in the application (open the File menu; choose Open...; navigate to the file; and so on).

Note

If you’re already following the traditional model/view/controller paradigm in designing your application, you’re well on your way to making it scriptable. Model/view/controller is an established design methodology that has the goal of separating an application into a model (the application’s objects and data), the view (the user interface, or how the data is presented), and the controller (the component that handles physical manipulation of the view, but knows nothing of the underlying data).

The next section describes some concrete steps to help determine the Apple event objects and commands your application will work with.

Identify the objects and commands you will support

Similar to the process you used for Carbon events in Chapter 6, you need to identify the Apple events your application will support (in addition to the required commands shown in Table 14.1). To do that, you’ll identify the objects and commands a scripter could use to control your application. The following steps can help with that task:

  • Write some simple sentence fragments (or better yet, have users write them) that describe what your application does. Turn the sentences into command statements. You should start to see patterns of common usage for objects and commands, with nouns typically representing objects and verbs representing commands your application can use:

    open a TCP/IP connection 
    scale a shape 
    create a new customer record
    tell the front window to scale the first oval by 25 percent
  • Identify properties of the objects in your application. A customer has a name and address; a shape may have a color and a list of points; and so on.

  • Identify the kinds of objects each command can operate on. The rotate command works on a shape, not a window. The set command may work with many objects (and many properties).

  • Examine the relationships of the objects you’ve identified and start laying out your object model. Remember that the object model should reflect the containment relationships of the objects (the third oval in the window "MyShapes").

You may have to work through these steps many times before you’re satisfied with your collection of objects and commands. Again, remember that you’re designing for users, so get their input as early as you can. Once you’re satisfied, you’re ready to incorporate the information into an 'aete' resource for your application.

Create an ‘aete’ resource that describes your scripting support

Every scriptable application must include an Apple event terminology extension, or 'aete', resource. The 'aete' resource is the scripting dictionary for your application. It describes the Apple events that your application supports and the user terminology that corresponds to those events. The 'aete' resource allows the AppleScript scripting component to interpret scripts correctly and send the appropriate Apple events to your application during script execution.

Apple events contain data in the form of attributes and parameters, stored as Apple event descriptors, an opaque data type. Every Apple event has at least two attributes, an event class and an event ID, which together specify the type of event. The information in an 'aete' resource associates the English language terms a scripter will understand with the event class and event ID values your application and AppleScript work with.

You can find constants for many standard event classes and event IDs, as well as for the scripting terminology used by AppleScript itself, in the header files AERegistry.r, ASRegistry.r, and AppleEvents.r. When you build your 'aete' resource, you use constant values from these header files to identify standard commands and objects. For application-specific objects and commands, you define your own event class and event ID values.

Note

You can work with an 'aete' resource as a text file that you edit with any word processor, or as a compiled resource you edit with a resource editing application. Apple and third parties provide resource editors. One way to get started is to use a resource editor application to grab an existing 'aete' from one of Mac OS X’s scriptable applications, such as Apple System Profiler, then use the editor to modify it for your application.

Write handlers for the Apple events you support

As with Carbon events and the Carbon Event Manager, scriptable applications rely on a callback mechanism that dispatches Apple events to handler functions. You’ll need to write a handler for each type of Apple event your application handles. In this section and the following section you’ll learn how to add support for one of the required Apple events described previously, the Open Documents event to the Moon Travel Planner application.

Example 14.3 shows the format for an Apple event handler.

Example 14-3. An Apple Event Handler Callback Declaration

OSErr MyAEEventHandlerCallback (
    const AppleEvent *theAppleEvent,
    AppleEvent *reply,
    UInt32 handlerRefcon);

The first parameter is a pointer to the Apple event to handle, the second parameter is a pointer to the default reply Apple event provided by the Apple Event Manager, and the third parameter is a reference constant your application can use to supply additional information to your handler. The reference constant can be a simple four-byte value, a pointer to a block of data, or it can be NULL if you don’t need it.

Example 14.4 shows how the Moon Travel Planner might implement an Open Documents event handler. The reason all well-behaved Mac OS X applications should support the Open Documents Apple event is that when a user selects one or more of an application’s documents in the Finder and double-clicks, the Finder sends the application an Apple event describing a list of files that it should open. To respond properly to this Apple event, your application extracts the file descriptions from the Apple event and opens the documents.

Example 14-4. An Open Documents Apple Event Handler for the Moon Travel Planner

OSStatus MTPHandleOpenAppleEvent(const AppleEvent * appleEvt, 
        AppleEvent * reply, SInt32 refcon)
{
    AEDesc fileList = {typeNull, NULL};                                  // 1
    OSErr err;
    SInt32 itemsInList;
    FSRef fileToOpen;
    HFSUniStr255 theFileName;
    CFStringRef fileNameCFString;   
    WindowRef newWindow;
    
    err = AEGetParamDesc(appleEvt, keyDirectObject,                      // 2
                         typeAEList, &fileList);
    if (err == noErr) 
    {
        err = AECountItems(&fileList, &itemsInList);                     // 3
        if (err == noErr && itemsInList > 0) 
        {
            OSType     keyword;
            OSType     returnedType;
            Size    actualSize;
            short i;
            for (i = 1; i <= itemsInList; i++)                            // 4
            {
                err = AEGetNthPtr(&fileList, i, typeFSRef, &keyword,      // 5
                       &returnedType, &fileToOpen, 
                       sizeof(FSRef), &actualSize);
                    
                if (err == noErr) 
                {                                
                    err = FSGetCatalogInfo(&fileToOpen, kFSCatInfoNone,   // 6
                            NULL, &theFileName, NULL, NULL);
                    fileNameCFString =  CFStringCreateWithCharacters (    // 7
                                            NULL, 
                                            theFileName.unicode, 
                                            theFileName.length);
                                            
                    newWindow = MTPDoNewItinerary( fileNameCFString);     // 8
                    
                    ShowWindow(newWindow);                                // 9
                    
                    err  = MTPReadFile(&fileToOpen, newWindow);           // 10
                }                               
            }
        }
    }
    return err;
}

Here’s how the MTPHandleOpenAppleEvent function works:

  1. It initializes an Apple event descriptor record to hold the list of references to files to open. This line shows the approved style for initializing a descriptor record.

    Note

    Though an apple event descriptor is just an opaque data structure of some type, descriptors have traditionally been known as descriptor records.

  2. It calls the Apple Event Manager function AEGetParamDesc, to extract a list of descriptor records (one for each file to be opened). It passes the values keyDirectObject and typeAEList to tell the function what kind of information to look for, and the parameter fileList to return the list.

  3. If no error occurs, it calls the Apple Event Manager function AECountItems, to count the number of file descriptor records in the list.

  4. If no error occurs, it sets up a loop to iterate over each item in the list.

  5. It calls the Apple Event Manager function AEGetNthPtr, to extract an FSRef record that describes the current file to be opened. It passes the value typeFSRef to indicate the type of information it is expecting, and the address of a variable of type FSRef for the returned value. If the specified record in the file list contains an FSRef for a file to open or any value that can be coerced (converted) to an FSRef, the MTPHandleOpenAppleEvent function automatically returns the desired type. This handler could examine the returnedType parameter to verify that the returned value is indeed the expected type, but it’s a trusting little function and may end up lost in space someday.

  6. If no error occurs, it calls the File Manager function FSGetCatalogInfo to get the name of the file to be opened as a Unicode string.

  7. It calls the Core Foundation function CFStringCreateWithCharacters to get the file name as a CFStringRef.

  8. It calls the function MTPDoNewItinerary, shown in Chapter 11, to create a new itinerary window.

  9. It calls the Carbon function ShowWindow to make the window visible.

  10. It calls the function MTPReadFile, shown in Chapter 11, to read the itinerary information from the file and display it in the window.

Your application can implement a Print Documents event handler with much of the same code shown in Example 14.4. As with the Open Documents event, the Apple event contains a list of one or more references to files that your application should print. To do so, you extract the file references as shown here, do any preparation you might need for printing, then call the MTPDoPrint function to display the Print dialog.

Note

Most Apple event handlers call Apple Event Manager functions to extract parameter and attribute information from the event. Parameters and attributes in Apple events are not the same as parameters to C function calls. Every Apple event parameter is an Apple event descriptor, a self-typed data structure that can represent files, aliases, numbers, strings, URLs, enumerated constants, and many other values. The underlying data type for the Apple event descriptor is the AEDesc structure. The event descriptor is an opaque type, but the Apple Event Manager provides functions for accessing its data.

Apple events can have a variable number of parameters, some of which are optional and some of which can contain additional nested descriptors. But the Apple Event Manager doesn’t define a large number of function calls with varying parameter lists to supply Apple event parameter values. Instead, your event handler routines extract Apple event parameters from an event by calling the AEGetKeyDesc function, which copies the parameter (if present) into an AEDesc structure. You use the AEGetDescData function to extract data from a descriptor structure.

When a parameter consists of an object specifier record (an Apple event descriptor that specifies an Apple event model object), your handler should call the AEResolve function to start the process of resolving the object specifier. For example, the specifier created from the statement:

third word in the second paragraph in the document "MyReport"

resolves to an Apple event descriptor that identifies the specified word.

Note

Resolving object specifiers is a detailed process that’s beyond the scope of this book. Apple supplies two helper libraries, the Object Support Library (OSL) and MoreOSL, that can help your application handle object specifier resolution. See the Apple Event Manager documentation in Carbon Help (available in the Project Builder Help menu) for more information on the Object Support Library. For information on MoreOSL, see the Sample Code area in the Apple developer web site (listed in Appendix A).

Install your Apple event handlers

As you do with Carbon events, your application must install its Apple event handlers. You do so by calling the function AEInstallEventHandler. Example 14.5 shows how to install the Open Documents event handler described previously.

Example 14-5. Installing an Open Documents Event Handler

OSErr err;
err = AEInstallEventHandler( 
            kCoreEventClass,
            kAEOpenDocuments,
            NewAEEventHandlerUPP((AEEventHandlerProcPtr) MTPHandleOpenAppleEvent), 
            NULL, 
            false);

The first two parameters specify an event class and event ID that identify the type of Apple event. The third parameter is a pointer to your event handler. You create this pointer by calling the function NewAEEventHandlerUPP. The fourth parameter has the value NULL, indicating that this event handler doesn’t need any information passed to it in its refcon parameter. And the final parameter has the value false, indicating that this handler is an application handler, not a special type of handler.

Process incoming Apple events

If your application uses the Carbon event model, as the Moon Travel Planner application does, and calls RunApplicationEventLoop, you won’t need to do anything special to receive Apple events. The Carbon Event Manager will automatically direct Apple events sent to your application to the Apple event handlers you have installed. Your handlers then perform the necessary operations to handle the events they receive.

In addition, applications that use the Carbon event model don’t need to write handlers for the required Run or Quit Apple events, because they’re handled automatically.

AppleScript is a mature and powerful scripting language that lets users streamline application tasks and allows applications to communicate with each other using Apple events. The Apple Event Manager allows your application to create and interpret Apple events, and you can use various third-party applications to help you to create and debug scripts. Most important commercial applications are AppleScript-savvy, so yours should be no exception.

Note

When you’re ready to add scripting support, you’ll find documentation for AppleScript and the Apple Event Manager in the Interapplication Communication documentation in Carbon Help (available in the Project Builder Help menu).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.120.136