aete Resources
Volume Number: | | 11
|
Issue Number: | | 7
|
Column Tag: | | Applescript
|
Making Sense of aete Resources
The inside scoop on building an applications scripting dictionary.
By Gary McGath, Hooksett, NH
An important step in making an application support Apple Events and scripting is the creation of an aete (Apple Event Terminology extension) resource. This resource defines the Apple Events and objects which your application understands, both for scripts and for applications that send you events. Unfortunately, the documentation for this resource is widely scattered; also, it has many magical features which are explained only in tech notes and journal articles, and probably others which are unknown outside Apple. This article tries to bring together the most important pieces of information so that the reader can put together an aete resource with a minimum of confusion and understand some of its subtleties.
To keep this article from digressing too far, Im assuming you have a general familiarity with Apple Events. The background you need can be found in Inside Macintosh: Interapplication Communication, chapters 3 through 6. If youve never worked with Apple Events, you should learn the basics before theres any point in trying to create aete resources.
Any application that responds to more than the basic four Apple Events, and any scripting addition, should have an aete resource. It should have only one of them, and its ID must be the same as the language code specified in the resource. If the target language is English, this means its ID must be 0.
An aete resource is organized as a hierarchy of information. It consists of one or more suites, each of which describes a distinct collection of Apple Event information. The hierarchy looks like this:
Suites
Suites are basic to the logical organization of Apple Events. If youve worked at all with Apple Events, at minimum youve run into the Required Suite and the Core Suite. The Required Suite consists of the four basic events which every application should support (Open Document, Open Application, Print, and Quit). The Core Suite describes a basic set of events which scriptable applications are expected to support. Other commonly used suites are the Text, Table, and Database suites, which respectively provide classes to describe text, two-dimensional arrays of data, and databases (including complex tables). You should try to work within Apples standard suites, defining your own only if they prove insufficient.
A suite consists of events, classes, comparison operators, and enumerations. Events and classes are the most important parts of a suite; events tell the world what your application can do, and classes tell what it can do it to.
Enumerations are an adjunct to events and classes; they are a form of data definition in which a parameter or property can take on one of several named values (e.g., Yes, No, and Cancel).
Comparison operators usually defined in aete resources when a new data type is defined. In addition, the aete resource is identical to the aeut resource, which defines the scripting dialect and needs this capability. For everyday purposes, youll probably not define one, and they arent discussed in this article.
Looking at the aeut resource can be useful for studying existing suites. This resource is found in the AppleScript dialect files, in the System Folder:Extensions:Scripting Additions:Dialects folder.
Describing The Resource
This article wont attempt to describe the byte-by-byte structure of one of the most complicated resource types Apple has devised. Normally, youll construct a resource with Rez or SARez, use a resource editor such as Resorcerer, or run a specialized application for aete resources such as my own forthcoming EightyRez (plug!). ResEdit is helpless in this situation; an aete template is available, but it will choke on any resource large enough to be useful.
The header of an aete resource specifies its version, language, and script. The version consists of a major and minor portion (e.g., version 1, revision 2) and is there simply for documentation. The language code must equal the ID of the resource. The script code refers to the way the Script Manager deals with text, not to AppleScript. For ordinary English, both of these should be 0. For other cases, see the discussion of the Script Manager in Inside Macintosh: Text.
Suites (Again)
There are five pieces of information associated with each suite: its ID, name, description, level, and version. The ID is an four-character ID, which are defined by Apple for all the standard suites. If you want to create a suite for your own application, you should give it a type which is the same as the applications signature.
Its important to remember that organization of events by suites doesnt draw any boundary lines in functionality; the Apple Event Manager doesnt even know about suites. Aside from being a convenience in organizing related events and classes, their only functional significance is that you can import whole suites by name from the aeut resource.
You may also want to create a singe suite which puts together pieces of standard suites without completely implementing any of them. In this case, you should give the suite an ID of '****'. If you implement a significant part of a suite, though, you should include it as a separate suite.
The suite ID tpnm, conventionally called Type Names, is magical. If you give a suite this ID, it wont appear in the Script Editors dictionary listing. This is useful for defining classes you want to hide from the user.
The name, description, level, and version of a suite are there simply for informational purposes; if you use a standard suite, the values in your aete resource should match those of the suite. Normally the level and version will both be 1.
If a suite is described in the systems aeut resource and you support it completely, all that you have to do is include this basic information about it; you dont have to list the events and classes which it supports. However, if you support only some of the events and classes in a suite, or if you support extensions to any of them (extra parameters, elements, or properties), then you must itemize them in the suite. If you include any events or classes in a suite, youre saying that you support only those events and classes.
Events
The two big subdivisions of a suite, as mentioned before, are events and classes. Events correspond to Apple Events, and classes to the objects which they operate on. To use a linguistic metaphor, events are the verbs of your Apple Event vocabulary, and classes are the nouns. An event consists of the following pieces:
Name
Description
Event class
ID
Reply type
Reply description
Reply flags
Direct parameter type
Direct parameter description
Direct parameter flags
Other parameters
The name is what AppleScript uses to refer to an event: examples are Quit, Make, Get Class Info, and Count. The event class and ID are four-character codes which identify an Apple Event as corresponding to this entry in the dictionary. Each event should have a unique name, unless you want to define synonyms for an events name. (If you do define a synonym, the ID, class, reply, and direct parameter should match precisely.) IDs should be unique across all suites and classes.
The description is simply a string for documentation, which will be displayed by the Script Editors dictionary display; the same applies to other descriptions found in various parts of the resource.
The event class generally corresponds to the ID of the suite that the event calls home. This is not necessarily the same as the suite the event is actually found in, since an event from one suite can be included in another suite. For example, the Core (or Standard) suite includes three events from the Required suite; these still have an event class of aevt, which is the ID of the Required suite, not core, which is the ID of the Core suite.
The reply type, another of those four-character codes, is the type of the object which will be returned to the events sender. In Apples Universal Headers, these types are defined in Apple Events.h, and their names all start with type (e.g., typeInteger, typeBoolean, typeAlias). Formally, these are of type descType, and they correspond to the descriptorType field of a descriptor record. If the event will not return a reply, the type should be null. If all you need to return to the sender is an error code, you do not need to specify a reply type other than null.
There are three reply flags that, when set, respectively designate that the reply is optional, that it is a list, and that it is enumerated. If the reply type is null, you should always set the Optional flag. If you set the Enumerated flag, this indicates that the reply type is not a standard type, but rather is the ID of an enumeration. You can use this when you want the reply to be one of a set of enumerated values. (Enumerations will be explained further on; for now all you need to know is that they allow a choice of four-character IDs.)
Next we come to the direct parameter. Every event has at least this one parameter (though it may be null). Since it is an object, the parameter looks very much like the reply. The only difference is that there is an extra flag defined which tells whether the event changes the applications state. This parameter really belongs to the event rather than being a parameter on its own, but was put in with the parameters flags because there was room.
In addition to the direct parameter, an event may have one or more additional parameters, which are separate structures in themselves. These parameters can have a name, a keyword, and several flags that the direct parameter doesnt have. These additional flags mark the parameter as named, masculine, feminine, or plural. The masculine and feminine flags are of interest only in languages with gendered grammar, such as French and German, and even the plural flag is obscure in its purpose.
Parameters do not have to be named, since they can be identified by their position in the event as long as no parameters are left out before them. However, if there is more than one optional parameter, all optional parameters should be named. Also, optional parameters should come after all required parameters, to minimize the chances of confusing AppleScript or the Apple Event Manager with omitted positional parameters.
The keyword of a parameter is the ubiquitous four-character code which is what the application will actually see when it gets the event. Obviously no two parameters of a given event should have the same name or keyword.
You may want to add parameters to an event for your application. For example, you might want to add a parameter to the save event to tell your application whether or not to make a backup copy of the document. For maximum compatibility with the rest of the AppleScript world, you should keep the standard parameter definitions unchanged and make your extra parameters optional, with reasonable defaults.
Classes
Classes define the kinds of objects which events can operate on. For various reasons, theyre somewhat more confusing than events. A class has the following pieces:
Name
ID
Description
Properties
Elements
The first three should be familiar by now. AppleScript refers to a class by its name, programs internally refer to it by its four-character ID, and the description is there for documentation. Curiously, a class has no flags, although it could really use a flag word. Instead, there is an odd convention in which a property is used just to provide flags for the class.
A class contains zero or more properties, followed by zero or more elements. The difference between properties and elements has been much discussed in the Apple Event literature, but is worth mentioning here one more time: An element is an item of information which is contained in an object, while a property is something which gives information about an object. Elements can include windows, text, and pictures; properties can include color, font, and size. Some borderline cases are hard to decide; what is most conveniently treated as a property in the usual case may be treated as an object and thus have element-like characteristics. If there can be more than one of something in an object (e.g., the characters in a text object, or pixels in a graphic), it should almost certainly be an element.
A property has the following pieces:
Name
ID
Class
Description
Flags
The name, ID, and description are as usual. The class of a property doesnt mean the class to which the property belongs, but refers to a different type of class entirely. This leads to such interesting linguistic constructions as the class of the classs property.
The class of a property indicates what type of object the property is, but just what this means is ill-defined. Sometimes it will be one of the object classes defined in AERegistry.h, sometimes it will be a descType (like typeInteger), and sometimes it will be unique to the application. What the class actually means is known only to the application itself. If it is a class name, it indicates what kind of object (using the Apple Event object model) is referred to by the property, without saying anything about the objects physical representation. For example, cGraphicLine (glin) indicates a graphic line, and cPixel cpxl indicates a pixel. How they might be stored is up to the application.
When an object is actually passed around by Apple Events, it becomes a parameter or reply of an event, and then its storage format is specified by the type of the event. When a parameter of an Apple Event is of type typeObjectSpecifier (obj ), then the object specifier contains information about the class of the object; this is the link between parameters, whose structure is known to the Apple Event Manager, and objects, whose structure is known only to the application.
As links go, this is a very tenuous one. In particular, theres no way to specify what kind of object a parameter of type obj refers to. You cant make an aete resource say that a point or a line is acceptable, but a document or a window isnt. This is a fact we have to live with, and should make up for in the documentation.
Some properties are magical. There is a special property ID, called kAESpecialClassProperties and represented as c@#!, which tells you that the property really isnt a property at all, but a place to store flags which describe the class itself. (The ID undoubtedly immortalizes the language some programmer used on discovering that the lack of class flags made this kludge necessary.)
A property with this class is the way that you create a plural synonym for a class (which is something you should do for all classes that may be used in scripts). Having created the singular class, you create another class which has the same ID, a plural version of the name (e.g., windows instead of window), no elements, and only one property. This property has ID c@#! and class type, and the property has its Plural flag set.
Another magic ID can be useful for eliminating redundant information. If the first property of a class has the ID c@#^, this signifies that the class inherits properties from another class. In this case its class is the ID of its base class, and the name of the property must be <Inherited>, including the angle brackets. Using this trick can make some aete resources significantly smaller, if there are a number of bulky classes which are largely similar.
A property can also belong to more than one class. To do this, you must set the Enumerated flag, and set the class to the ID of an enumeration. The enumerated set specifies the classes to which the property can belong. Alternatively, you can use the Wild Card class (****), which says that a property can be of any class.
(This may lead some hackers to wonder: If you use inheritance and enumeration in the same property, do you get multiple inheritance? If anyone is brash enough to try this, let me know if it works.)
If a property can be modified by an event, then the Read/Write flag should be set. If its value is a list (e.g., the styles which are applied to a text item), then the List of Values flag should be set. The Masculine and Feminine flags, once again, dont apply to English.
The elements of a class are simpler than properties. An element consists of a class, which is a four-character ID, and one or more key forms. The class of an element is the same kind of beast as the class of a property, except that it cant be enumerated or magical.
The key forms of an element indicate how it can be specified. There are seven standard key forms:
Absolute position indx
Name name
Property ID prop
Range rang
Relative position rele
Test test
Whose whos
Each of these consists simply of its four-character ID code, as given in the list above. To be useful, an element must have at least one key form; including several is not unusual. A full description of their function would be another article, one dealing with the Apple Event object model, but a partial explanation may help if you havent studied this aspect of Apple Events yet. Specification by absolute position locates an element by an ordinal number, for example, the 7th word. Specification by name locates it by name, for example, the window named Untitled5. Specification by range indicates a range of elements, such as characters 10 to 20. The others vary in complication, culminating in the powerful and seldom-supported whose, which allows combinations of criteria to be used in specifying an element or set of elements.
Enumerations
Finally, we come to enumerations, which have already been discussed as a way to specify a set of alternatives for the type of an events parameters or reply, and for the class of a classs property. (This is an interseting linguistic constrution as described above. Another way to say this is the class of a property of a class.)
An enumeration consists of a four-character ID and one or more enumerators (really two or more, to be at all useful). The ID of an enumeration can be plugged into the type of a parameter or reply, or the class of an element.
The enumerators each consist of a name, a four-character ID, and a description. You use the name of an enumerator in AppleScript to specify which choice or choices youve made in an enumerated parameter.
There are two similar terms to keep clear on: enumeration and enumerator. The enumeration is like a list of elements. An enumerator is one of those elements. For example, a hypothetical parameter called checkbox may take on the values checked or not checked. This might be implemented as an enumeration, named checkbox, whose enumerators are named checked and not checked. A script programmer would then write either checkbox checked or checkbox not checked to indicate the desired initial state of a checkbox; what would actually be passed to the application would be the ID of the enumerator (perhaps chek and nchk, respectively).
Conclusion
Creating an aete resource is only one piece of the process of making an application scriptable, though it has quirks enough by itself. Really understanding events and classes requires understanding where they fit in the Apple Event object model. Knowing when standard suites can be adapted for your application is important; doing this will not only save you effort, but make things simpler for anyone scripting the application.
By knowing the fine points of aete resources discussed this article, you may be able to make your custom suites more professional-looking and easier to use; having plural versions of classes, correctly set flags, and enumerations where appropriate will make it easier for other applications and scripts to control your application effectively. The result is less effort and more productivity by a method that has worked for humans long before it was applied to software: cooperation and division of labor.
References
Clark, Richard. Apple Event Objects and You, develop, Number 10, June 1992.
Eric M. Berdahl, Better Apple Event Coding Through Objects, develop, Number 12, December 1992.
Simone, Cal. Designing a Scripting Implementation, develop, Number 21, March 1995.