Important: The information in this document is obsolete and should not be used for new development.
Dispatching Apple Events
An application may receive Apple events from itself, from another application, from the operating system, or from a script. MacApp uses a global Apple event dispatcher object to send Apple events to the objects specified by the events. The Apple event dispatcher object works together with callbacks from the Apple Event Manager and its OSL component to dispatch Apple events. If you have built your application with MacApp's support for attached scripts, dispatching is a two-stage process:
If you have built your application without MacApp's support for attached scripts, MacApp skips the predispatch step. Appendix A describes how to use the
- MacApp's predispatch callback routine dispatches Apple events to be handled by scripts attached to objects in the application.
- If an Apple event isn't handled by a script, MacApp's standard callback routine dispatches the event to its specified object.
-Attachableoption to include MacApp's attachability code when building your application with the MABuild system.Any object that needs to respond to Apple events mixes in the multiple inheritance class
MScriptableObject. Many MacApp classes, includingTApplication,TDocument,TWindow, andTFile, already mix inMScriptableObject, so you don't need to mix it in again for any class that descends from one of these classes.Classes that support scripting generally override methods of
MScriptableObjectto support a hierarchy of contained objects, to handle specific Apple events, and to get and set object properties. For example, the IconEdit sample application supports setting the color of an icon with an Apple event, so theTIconDocumentclass overrides theGetObjectPropertyandSetObjectPropertymethods to respond when the specified property ispColor.Initializing Scripting
MacApp initialization is described in detail in Chapter 4, "Launching and Terminating an Application." During initialization of the application, theIApplicationmethod calls theInitUScriptingroutine to initialize the UScripting unit.MacApp's global
InitUScriptingroutine calls theInitUScriptingmethod of theTOSADispatcherclass. That method terminates applications that are not aware of high-level events. Otherwise,InitUScriptingcalls the Apple Event Manager routineAEObjectInitto initialize the OSL. If a global Apple event dispatcher object has not already been created,InitUScriptingcreates one, stores a reference to it inTOSADispatcher::fgDispatcher(a global static field), and calls theIOSADispatcherinitialization method.The
IOSADispatchermethod does some standard initialization tasks, then calls two of its own methods to install various Apple -event- handler routines and other callback routines:
this->InstallDispatchHandlers(); this->InstallObjectCallbacks();It is also possible to initialize MacApp's scripting support to use a custom subclass ofTOSADispatcher. For details, see "Recipe--Installing a Custom Subclass of TOSADispatcher," beginning on page 371.InstallDispatchHandlers
TheInstallDispatchHandlersmethod looks for any application resources of type'aedt'(Apple event dispatch table). An'aedt'resource is an array of entries, each of which maps an Apple event class and ID pair to a MacApp command-number constant. For example, MacApp defines an'aedt'resource for the four Finder Apple events that all applications are required to support:
resource 'aedt' (kAEDispatchTable)
{
{
'aevt', 'ansr', cAppleEventReply;
'aevt', 'oapp', cFinderNew;
'aevt', 'odoc', cFinderOpen;
'aevt', 'pdoc', cFinderPrint;
'aevt', 'quit', cFinderQuit;
}
};This resource also defines the
cAppleEventReplycommand number, used to identify asynchronous replies.For each entry in each
'aedt'resource,InstallEventDispatchTablescalls the Apple Event Manager routineAEInstallEventHandler, passing a universal procedure pointer that specifies theDispatchHandlerGluemethod as the event handler to call when an Apple event with the specified class and ID information is received. As a result, all Apple events handled by the application are dispatched to the same method. However, before an Apple event is dispatched to theDispatchHandlerGluemethod, it is first predispatched to a different callback routine, described in "InstallPreDispatchHandler" below.InstallObjectCallbacks
TheInstallObjectCallbacksmethod calls the Apple Event Manager routineAEInstallObjectAccessor.InstallObjectCallbackspasses a universal procedure pointer that specifies theObjectAccessorGluemethod as the object-accessor routine the Apple Event Manager'sAEResolveroutine should call when resolving an Apple event object specifier. Although it is possible to install multiple object-accessor routines, MacApp uses just this one.The
InstallObjectCallbacksmethod also calls the Apple Event Manager routineAESetObjectCallbacks, passing universal procedure pointers that specifyTOSADispatchermethods as callback routines for
Each of these "glue" methods sets up a failure handler, then calls a similarly named method of the
- comparing objects (the
CompareObjectsGluemethod)- counting objects (the
CountObjectsGluemethod)- disposing of tokens (the
DisposeTokenGluemethod)- working with mark tokens (the
AdjustMarksGlue,GetMarkTokenGlue, andMarkObjectGluemethods)
TOSADispatcherclass. For example,CompareObjectsGluecallsCompareObjectsandAdjustMarksGluecallsAdjustMarks. These callback glue methods may also be called by the OSL'sAEResolveroutine when resolving an Apple event object specifier.InstallPreDispatchHandler
If you build your application to include MacApp's attachability code, the first time you call a method such asMScriptableObject:ReadOSAScriptorMScriptableObject::SetOSAScript, theInstallPreDispatchHandlermethod is called.InstallPreDispatchHandlercalls the Apple Event Manager routineAEInstallSpecialHandler, passing a universal procedure pointer that specifies thePreDispatchHandlerGluemethod as the event handler to call when predispatching Apple events. MacApp uses this routine to predispatch Apple events to objects with attached scripts.The global
TOSADispatcherobject keeps a reference to the number of attached scripts in the application. If that number goes to 0, the predispatch handler is uninstalled.Apple Event Dispatching
Once MacApp's UScripting unit has been initialized, the application is ready to dispatch any Apple event it knows how to handle. Apple events are dispatched in one of two ways:
- When the application receives a high-level event from an external source, it calls the Apple Event Manager routine
AEProcessAppleEvent. TheAEProcessAppleEventroutine examines the application's dispatch table. If the Apple event is one the application can handle,AEProcessAppleEventcalls the application's predispatch handler routine, using the callbacks installed by the application.- When the application sends an Apple event to itself, the Apple Event Manager calls back immediately to the Apple event dispatcher object, without going through the event loop and the
AEProcessAppleEventroutine. The Apple event dispatcher object dispatches the event to the specified object, or to the default object if none is specified.
Predispatching Apple Events to Attached Scripts
ThePreDispatchHandlerGluemethod first calls itsGetAppleEventCommandNumbermethod, which calls the Apple Event Manager routineAEGetEventHandlerto get a MacApp command number to associate with the event. Command number information is available from a table set up by theInstallDispatchHandlersmethod, based on the application's'aedt'resources (as described in "InstallDispatchHandlers" on page 151).If the
PreDispatchHandlerGluemethod is able to retrieve the command number information, it calls thePreDispatchHandlermethod. That method attempts toresolve the target object for the event. If the event does not specify a target object, MacApp uses the application object as the default target. ThePreDispatchHandlermethod calls theHandleOSAEventmethod of the target object.HandleOSAEventchecks whether the object has an attached script and, if it does, gives the script a chance to process the Apple event.Dispatching Apple Events Directly to an Apple Event Object
If the application does not include MacApp's attachability code, or if an Apple event is not handled by an object with an attached script, the Apple Event Manager makes a callback to the Apple event dispatcher object'sDispatchHandlerGluemethod, which does the following:
The
- If the Apple event is a reply message, the
DispatchHandlerGluemethod calls a method to determine whether the application is waiting for a reply to an asynchronous Apple event. If so, and if the Apple event is a reply to a pending client command, the method posts the pending command to respond to the reply. See "The TClientCommand Class" on page 157 for a more detailed explanation of MacApp's mechanism for sending an Apple event to an external process.- Otherwise, the
DispatchHandlerGluemethod calls theDispatchHandlermethod, passing a MacApp command number value.
DispatchHandlermethod performs several actions:
Although this is a rather complicated algorithm, for most Apple events the
- It calls a method to get the direct (Apple event) object of the event or, if an object can't be resolved, to return the default target object (normally the application object).
- If the target object appears to be a container for a list of scriptable objects, the
DispatchHandlermethod gets the target's container object.- If the target object has a pending command that matches the Apple event, the
DispatchHandlermethod returns the errorerrAEEventNotHandled, indicating that the Apple event was not handled. This happens when the application is in the process of performing a command, the command has sent an Apple event to record its action, and the Apple event is not intercepted by an attached script. Therefore, as an optimization, we can skip processing the Apple event and let the command perform the desired action. For more information, see "Command Objects and Apple Events," beginning on page 120.- Otherwise, if the target is a container object,
DispatchHandlercalls theDoAEOnContainedObjectsmethod of the target object, which does one of the following:
- If the object is a scriptable object list, it creates a
TSetPropertyCommandobject to set the specified property for the objects in the list. TheTSetPropertyCommandis described in the next section.- Otherwise, it calls the
DoScriptCommandmethod of the container (target) object. TheDoScriptCommandmethod uses the passed command number to decide how to handle the Apple event.
- If the target is not a container object,
DispatchHandlercalls theHandleScriptCommandmethod of the target object.HandleScriptCommandis a method ofMScriptableObject, which first gives any attached behavior objects a chance to handle the Apple event and then, if none does, calls theDoScriptCommandmethod. TheDoScriptCommandmethod uses the passed command number to decide how to handle the Apple event.
DispatchHandlermethod ends up performing the following actions:
The
- It resolves the target object specified by the Apple event (or uses the default target, normally the application object).
- It calls the
DoScriptCommandmethod of the resolved target object, passing among other things a MacApp command-number constant.
MScriptableObject::DoScriptCommandmethod calls other methods ofMScriptableObjectto handle many specific commands. For example, it callsDoAECloseto handle acAECloseorcAEQuitcommand,DoAEMovefor acAEMovecommand,DoAEOpenfor acAEOpencommand, and so on. MacApp classes such asTApplication,TDocument, andTWindowoverride these methods to open or close windows or documents, quit the application, and so on.Your classes that support scripting mix in the
MScriptableObjectclass, so they can override these methods too. If a class handles additional commands that aren't supported byMScriptableObject, it can override theDoScriptCommandmethod. For more information on adding scripting support to your classes, see Chapter 14, "Working With Scripting."
- Note
- An application may need to create a document that is invisible to the AppleScript interface. For more information, see "A Note on Ghost Documents," beginning on page 184.
![]()
The TSetPropertyCommand Class
MacApp makes it easy for your application to set object properties based on received Apple events. A class that wishes to set a property in response to an Apple event overrides theGetObjectPropertymethod, theSetObjectPropertymethod, and possibly theGetSetPropertyInfomethod. MacApp does the rest.When MacApp receives a Set Data event specifying a property to be set, it creates a
TSetPropertyCommandobject to actually set the property. The command's initialization method calls theGetSetPropertyInfomethod of the target object to obtain information about the command, including
The
- the command number
- whether the command can be undone
- whether the command changes its context (the document, window, or other object the command operates on)
- a reference to the command's context
GetSetPropertyInfomethod of theMScriptableObjectclass provides default values that may work for your scriptable class--if not, you provide an override method.For example, in the IconEdit sample application, which uses Apple events to set an icon's color, the
TIconDocumentclass overrides theGetSetPropertyInfomethod to supply a command number (cSetColor), and to specify that the context is the icon document, the command is undoable, and the command changes the document.TIconDocumentalso overridesGetObjectPropertyto return the current icon color andSetObjectPropertyto set the icon color.The
TSetPropertyCommandsets the property for a list of one or more objects. Before setting the new property, theDoItmethod calls theGetObjectPropertymethod for each object in the list. The command object stores the original property setting so that it can restore the property to its original state for undo.The next section describes how a
TSetPropertyCommandcommand is created. For more information on getting and setting properties in your application, see "Recipe--Setting Object Properties With Apple Events," beginning on page 355.The target specified by an Apple event can be a C++ object (such as a window) or a simpler Apple event object (such as a paragraph of text). The specified operation may also refer to a C++ object (such as activating a window) or to an Apple event object (such as deleting the third word in a paragraph).
The TPropertyAccessor ClassTo resolve the target object specified by an Apple event, the OSL may call the global Apple event dispatcher object's
ObjectAccessormethod, passing information about the current contained object. (Note that the OSL actually calls theObjectAccessorGluemethod, which in turn callsObjectAccessor.) TheObjectAccessormethod calls theGetContainedObjectmethod of the current contained object, passing the desired object type, form, and other information.When the Apple event's specified operation describes a property of the current object, the
MScriptableObject:GetContainedObjectmethod returns an object of typeTPropertyAccessor. The property accessor object knows which property was specified and keeps a reference to the current object. By returning a property accessor object to handle the event, MacApp can automate the process of responding to Set Property Apple events for classes you have defined, as long as your classes override theGetObjectPropertyandSetObjectPropertymethods.Once a target object has been resolved, the global Apple event dispatcher calls the object's
HandleScriptCommandmethod, which eventually calls the target'sDoScriptCommandmethod. For aTPropertyAccessorobject, theDoScriptCommandmethod creates aTSetPropertyCommandcommand object (described in the previous section) and calls itsProcessmethod to immediately execute the command.
- IMPORTANT
- See "The Command Context for Classes That Handle Apple Events," beginning on page 118, for important information on supplying a context for a command object.
![]()
The TClientCommand Class
MacApp provides theTClientCommandclass as a base class for commands that send an Apple event to an external process. When any command object is posted, the application calls the command'sIsReadyToPostmethod. TheTClientCommandclass overrides this method to send its Apple event immediately on posting.TClientCommandcan handle any of three sending modes:
When you create a command based on the
kAENoReply- The constant
kAENoReplyis used when no reply to the sent Apple event is expected. TheIsReadyToPostmethod returnsTRUE, and the command is posted to the command queue. When the command is executed, theDoItmethod sends the Apple event. No reply is generated.kAEWaitReply- The constant
kAEWaitReplyis used when the command should wait for a reply to the sent Apple event. TheIsReadyToPostmethod sends the Apple event and waits for a reply. It then returnsTRUE, so the command is posted to the command queue. The defaultDoItmethod does nothing. Your subclass should overrideDoItto perform an operation based on the Apple event reply.kAEQueueReply- The constant
kAEQueueReplyis used when an asynchronous reply is expected to the sent Apple event. TheIsReadyToPostmethod sends the Apple event, tells the global Apple event dispatcher object that the command is pending, then returnsFALSEso the command is not posted to the command queue. When a reply is received at some later point, the global Apple event dispatcher object posts the command. Again, the defaultDoItmethod does nothing. Your subclass can overrideDoItto perform an operation based on the Apple event reply.TClientCommandclass, you supply theIClientCommandmethod with an Apple event for the command to send. The sending mode of the apple event is one of the three constants described above:kAENoReply,kAEWaitReply, orkAEQueueReply.