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.
-Attachable
option 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
MScriptableObject
to 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 theTIconDocument
class overrides theGetObjectProperty
andSetObjectProperty
methods 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, theIApplication
method calls theInitUScripting
routine to initialize the UScripting unit.MacApp's global
InitUScripting
routine calls theInitUScripting
method of theTOSADispatcher
class. That method terminates applications that are not aware of high-level events. Otherwise,InitUScripting
calls the Apple Event Manager routineAEObjectInit
to initialize the OSL. If a global Apple event dispatcher object has not already been created,InitUScripting
creates one, stores a reference to it inTOSADispatcher::fgDispatcher
(a global static field), and calls theIOSADispatcher
initialization method.The
IOSADispatcher
method 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
TheInstallDispatchHandlers
method 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
cAppleEventReply
command number, used to identify asynchronous replies.For each entry in each
'aedt'
resource,InstallEventDispatchTables
calls the Apple Event Manager routineAEInstallEventHandler
, passing a universal procedure pointer that specifies theDispatchHandlerGlue
method 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 theDispatchHandlerGlue
method, it is first predispatched to a different callback routine, described in "InstallPreDispatchHandler" below.InstallObjectCallbacks
TheInstallObjectCallbacks
method calls the Apple Event Manager routineAEInstallObjectAccessor
.InstallObjectCallbacks
passes a universal procedure pointer that specifies theObjectAccessorGlue
method as the object-accessor routine the Apple Event Manager'sAEResolve
routine 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
InstallObjectCallbacks
method also calls the Apple Event Manager routineAESetObjectCallbacks
, passing universal procedure pointers that specifyTOSADispatcher
methods 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
CompareObjectsGlue
method)- counting objects (the
CountObjectsGlue
method)- disposing of tokens (the
DisposeTokenGlue
method)- working with mark tokens (the
AdjustMarksGlue
,GetMarkTokenGlue
, andMarkObjectGlue
methods)
TOSADispatcher
class. For example,CompareObjectsGlue
callsCompareObjects
andAdjustMarksGlue
callsAdjustMarks
. These callback glue methods may also be called by the OSL'sAEResolve
routine 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:ReadOSAScript
orMScriptableObject::SetOSAScript
, theInstallPreDispatchHandler
method is called.InstallPreDispatchHandler
calls the Apple Event Manager routineAEInstallSpecialHandler
, passing a universal procedure pointer that specifies thePreDispatchHandlerGlue
method 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
TOSADispatcher
object 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
. TheAEProcessAppleEvent
routine examines the application's dispatch table. If the Apple event is one the application can handle,AEProcessAppleEvent
calls 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
AEProcessAppleEvent
routine. 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
ThePreDispatchHandlerGlue
method first calls itsGetAppleEventCommandNumber
method, which calls the Apple Event Manager routineAEGetEventHandler
to get a MacApp command number to associate with the event. Command number information is available from a table set up by theInstallDispatchHandlers
method, based on the application's'aedt'
resources (as described in "InstallDispatchHandlers" on page 151).If the
PreDispatchHandlerGlue
method is able to retrieve the command number information, it calls thePreDispatchHandler
method. 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. The
PreDispatchHandler
method calls theHandleOSAEvent
method of the target object.HandleOSAEvent
checks 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'sDispatchHandlerGlue
method, which does the following:
The
- If the Apple event is a reply message, the
DispatchHandlerGlue
method 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
DispatchHandlerGlue
method calls theDispatchHandler
method, passing a MacApp command number value.
DispatchHandler
method 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
DispatchHandler
method gets the target's container object.- If the target object has a pending command that matches the Apple event, the
DispatchHandler
method 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,
DispatchHandler
calls theDoAEOnContainedObjects
method of the target object, which does one of the following:
- If the object is a scriptable object list, it creates a
TSetPropertyCommand
object to set the specified property for the objects in the list. TheTSetPropertyCommand
is described in the next section.- Otherwise, it calls the
DoScriptCommand
method of the container (target) object. TheDoScriptCommand
method uses the passed command number to decide how to handle the Apple event.
- If the target is not a container object,
DispatchHandler
calls theHandleScriptCommand
method of the target object.HandleScriptCommand
is a method ofMScriptableObject
, which first gives any attached behavior objects a chance to handle the Apple event and then, if none does, calls theDoScriptCommand
method. TheDoScriptCommand
method uses the passed command number to decide how to handle the Apple event.
DispatchHandler
method 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
DoScriptCommand
method of the resolved target object, passing among other things a MacApp command-number constant.
MScriptableObject::DoScriptCommand
method calls other methods ofMScriptableObject
to handle many specific commands. For example, it callsDoAEClose
to handle acAEClose
orcAEQuit
command,DoAEMove
for acAEMove
command,DoAEOpen
for acAEOpen
command, and so on. MacApp classes such asTApplication
,TDocument
, andTWindow
override these methods to open or close windows or documents, quit the application, and so on.Your classes that support scripting mix in the
MScriptableObject
class, so they can override these methods too. If a class handles additional commands that aren't supported byMScriptableObject
, it can override theDoScriptCommand
method. 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 theGetObjectProperty
method, theSetObjectProperty
method, and possibly theGetSetPropertyInfo
method. MacApp does the rest.When MacApp receives a Set Data event specifying a property to be set, it creates a
TSetPropertyCommand
object to actually set the property. The command's initialization method calls theGetSetPropertyInfo
method 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
GetSetPropertyInfo
method of theMScriptableObject
class 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
TIconDocument
class overrides theGetSetPropertyInfo
method 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.TIconDocument
also overridesGetObjectProperty
to return the current icon color andSetObjectProperty
to set the icon color.The
TSetPropertyCommand
sets the property for a list of one or more objects. Before setting the new property, theDoIt
method calls theGetObjectProperty
method 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
TSetPropertyCommand
command 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 Class
To resolve the target object specified by an Apple event, the OSL may call the global Apple event dispatcher object's
ObjectAccessor
method, passing information about the current contained object. (Note that the OSL actually calls theObjectAccessorGlue
method, which in turn callsObjectAccessor
.) TheObjectAccessor
method calls theGetContainedObject
method 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:GetContainedObject
method 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 theGetObjectProperty
andSetObjectProperty
methods.Once a target object has been resolved, the global Apple event dispatcher calls the object's
HandleScriptCommand
method, which eventually calls the target'sDoScriptCommand
method. For aTPropertyAccessor
object, theDoScriptCommand
method creates aTSetPropertyCommand
command object (described in the previous section) and calls itsProcess
method 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 theTClientCommand
class 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'sIsReadyToPost
method. TheTClientCommand
class overrides this method to send its Apple event immediately on posting.TClientCommand
can handle any of three sending modes:
When you create a command based on the
kAENoReply
- The constant
kAENoReply
is used when no reply to the sent Apple event is expected. TheIsReadyToPost
method returnsTRUE
, and the command is posted to the command queue. When the command is executed, theDoIt
method sends the Apple event. No reply is generated.kAEWaitReply
- The constant
kAEWaitReply
is used when the command should wait for a reply to the sent Apple event. TheIsReadyToPost
method sends the Apple event and waits for a reply. It then returnsTRUE
, so the command is posted to the command queue. The defaultDoIt
method does nothing. Your subclass should overrideDoIt
to perform an operation based on the Apple event reply.kAEQueueReply
- The constant
kAEQueueReply
is used when an asynchronous reply is expected to the sent Apple event. TheIsReadyToPost
method sends the Apple event, tells the global Apple event dispatcher object that the command is pending, then returnsFALSE
so 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 defaultDoIt
method does nothing. Your subclass can overrideDoIt
to perform an operation based on the Apple event reply.TClientCommand
class, you supply theIClientCommand
method 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
.