Interactive Object Architecture
1.0b14
UserLand Software, Inc.
©
copyright 1993, UserLand Software, Inc.
UserLand
Software is the developer of the UserLand Frontier scripting system. The
company is located at 400 Seaport Court, Redwood City, CA 94063. 415-369-6600,
415-369-6618 (fax). UserLand, Frontier, Frontier Runtime and Frontier Extras
are trademarks of UserLand Software, Inc. Other product names may be trademarks
or registered trademarks of their owners.
Email:
userland.dts@applelink.apple.com. If youÕre an AppleLink user, check out the
UserLand Discussion Board under the Third Parties icon. CompuServe users enter
GO USERLAND at any ! prompt.
Comments,
questions and suggestions are welcome!
Confidentiality
This
specification and the IOA Toolkit is UserLand Confidential.
It
is being disclosed to a very small group of people, assembled in
Section/Library 15 at GO USERLAND on CompuServe.
IOA
may at some time be an open specification, but it is not open today. We want
your feedback, but you may not share this information with anyone, and may not
discuss it outside of Section 15 on CompuServe.
Interface
design tools is a competitive market. IOA is a competitive advantage. Any
breach of confidentiality could seriously hurt UserLand Software. WeÕre
trusting you.
Keep
it confidential.
Dave
Winer
UserLand
Software
Introduction
IOA
is an acronym for Interactive Object Architecture. ItÕs a specification and a
Toolkit. The purpose of IOA is to open up Card Editor/Iowa Runtime so that
UserLand and others can add new object types.
IOA
objects build on AppleÕs Component Manager. You must be running either System
7.1 or QuickTime 1.0 or greater to build or run IOA components. All source
examples are provided in THINK C, as is the IOA Toolkit.
Writing
IOA components is not for script writers. You must be a system-level C or
Pascal programmer with good debugging skills. Writing IOA components is very
similar to writing a custom menu definition handler or control definition. But
the IOA protocol is more comprehensive and powerful, so thereÕs more to
understand.
To
install: copy the folder containing the IOA Toolkit into your Frontier SDK
folder, which should be a sub-folder in the folder that contains your THINK C
application.
Start
by looking at one of the sample objects provided with the IOA Toolkit. Each
object implements a set of callback routines that perform operations like
drawing the object, handling a mouse click or a keystroke, or recalculating the
value of an object. Details are provided in the IOA Callbacks section, below.
Some
callbacks are optional. In some cases, the default behavior is what you want.
The IOA Toolkit automatically fills in defaults for you. Look at ioa.c to see
what the default callbacks do. See the Config Records section, below for more
info.
Each
object also sets a group of boolean flags that describe the objectÕs behavior
in relationship to other objects, and in relationship to the framework.
All
callbacks receive a handle to a record that stores all information about the
object, a hdlobject. The object record contains handles to strings containing
the objectÕs value, name, and script. The objectrect field tells you where to
draw the object. There are many more fields in each object record. See ÒObject
RecordsÓ for details.
Contents of the resource fork of an IOA
component
Open
up one of the IOA files with ResEdit or Resorcerer. HereÕs a list of the
resources that are contained in a typical file:
¥ CODE
63
Contains the code of the IOA component. ThereÕs no absolute
requirement that this be resource 63, but if you use a different resource
number, be sure you update the thng resource accordingly.
¥ ICN#
128
Not sure what the Component Manager does with this. But
itÕs a requirement that each component have an icon.
¥ SICN
130
The icon for the object type. It appears in the tool
palette in Card Editor. It must be in resource 130.
¥ STR
128
The short description for this object type. Displayed in
the Things! Control Panel, supplied by Apple.
¥ STR
129
The long description for this object type. Displayed in the
Things! Control Panel, supplied by Apple.
¥ thng
128
Ties all the resources in this file together. See the
Component Manager docs for details.
¥ vers
1
Normal version resource.
¥ vers
2
Normal version resource.
You
can add other resources to your file, and access them using
OpenComponentResFile and CloseComponentResFile. See ioa.c for an example of
their use.
The
file type must be thng. The creator can be anything you like. All
UserLand-supplied objects have a creator of LAND.
Object Records
All
the information about each object is stored in an object record.
The
record contains handles to the objectÕs name, value, script and error message.
It tells you where to draw the object.
HereÕs
the declaration for object records from ioa.h:
typedef struct tyobject {
struct tyobject **nextobject; /*next object in the object
list*/
struct tyobject **nextselectedobject; /*part of the
selection list structure*/
struct tyobject **childobjectlist; /*for group objects*/
struct tyobject **nextinthread; /*flat list of objects, for
run mode*/
struct tycard **owningcard; /*each object points back to
the card it appears in*/
tyobjecttype objecttype; /*a button, checkbox, static text,
etc.*/
Rect objectrect; /*the display rectangle for the object*/
Handle objectname; /*the variable name for this object*/
Handle objectvalue; /*the string that's displayed in the
object*/
Handle objectscript; /*the script that runs when the object
is hit*/
Handle objecterrormessage; /*error generated when the
object was calculated*/
Handle objectdata; /*a refcon handle*/
boolean objectflag: 1; /*for buttons, true --> bold,
checkboxes, the box is checked*/
boolean objectvisible: 1; /*if false, the object isn't
drawn*/
boolean objectenabled: 1; /*if false the object is disabled*/
boolean objecttransparent: 1; /*if true, don't erase the
rect before drawing object*/
boolean objectautosize: 1; /*enables auto-resizing*/
boolean objectinval: 1; /*indicates the object needs
re-display*/
boolean objecthasframe: 1; /*do we draw a frame for the
object?*/
boolean objecttmpbit: 1; /*for any temporary use at all*/
boolean objectselected: 1; /*if true, the object is part of
the selection list*/
OSType objectlanguage; /*component subtype for linked
script's interpreter*/
Handle objecteditrecord;
short objectfont;
short objectfontsize;
short objectstyle;
tyjustification objectjustification;
RGBColor objectfillcolor;
RGBColor objecttextcolor;
RGBColor objectframecolor;
short objectdropshadowdepth;
short objectlinespacing;
short objectindentation;
short sorttag; /*used for sorting the object list by
location*/
} tyobject, *ptrobject, **hdlobject;
Most
objects need to access or set only a few of the fields of the object record.
Here are some comments on each of the fields, and when youÕre likely to use
them.
nextobject
Often, thereÕs no need to access this field directly, since
IOAvisitobjects is provided to traverse an object list. Radio button objects
use this field to traverse the objectÕs list, turning off all the other radio
button types.
nextselectedobject,
childobjectlist, nextinthread
These are the ÒnextÓ pointers for three list structures:
the selection list, a group objects list (nil if the objecttype grouptype)
and the flat thread linking all the objects together.
Few if any IOA components will need to access these
handles.
owningcard
This field is provided in case your object needs to get
information about the card that contains the object. See the declaration of
tycard in ioa.h for details.
objecttype
You know that objecttype is your object type. If
youÕre visiting other objects in the object list, you will want to check the
objecttype field to determine the type of each object youÕre looking at. ItÕs
cool to make changes to your own objects, but tread very carefully in changing
the attributes of other peopleÕs objects.
objectrect
A very important field. It determines where you do your
drawing and editing. In most cases, objects only draw inside their object
rectangle. But certain types (e.g. bold buttons) draw outside of their
rectangle. This is a historic (ie deeply ingrained) feature, itÕs not likely to
change.
objectname
This is used by the framework to resolve references to
other objectÕs values in scripts.
objectvalue
Usually this is the text you display when drawing your
object. Some objects display a
function of the object value. Icons for example, convert the value to a number,
and display the icon family resource with that number.
objectdata
This is entirely up to you. ItÕs a handle, with no type.
When the card is saved, the handle is packed and written to the file. If you
have any handles linked into this record, they will not be saved, but
the address of the handle will be saved. This is always the wrong value. Nil
out linked handles in your postUnpack callback.
objectflag
This boolean is set from the first command in the Object
menu in Card Editor. It can be used to toggle your object, e.g.
checked/unchecked checkboxes. Or bold/not bold buttons. ItÕs up to you to
determine how the flag is interpreted, or not interpreted.
You can change whatÕs displayed in the Object menu when
your object is selected by setting the objectFlagName field of the objectConfig
record. ItÕs described below.
objectvisible, objectenabled
If the objectvisible boolean is false, the framework wonÕt
call your draw routine. It presets the text color to gray if objectenabled is
false, before calling your draw routine.
objecttransparent
Normally, youÕll erase the rectangle containing the object
before drawing it. If objecttransparent is true, you should not erase the
rectangle.
objectinval
Set this true at any time to force a redraw of the object
the next time thru the main event loop. If your object changes the state of any
other object, it should set its inval boolean true.
objectautosize
Set this true in your init callback, or before editing, to
activate IowaÕs automatic resizing feature on this object.
objecttmpbit
Can be used for whatever purpose you like. But donÕt depend
on its value remaining constant after you return from your callback.
objectselected
This boolean is true if the object is in the selection
list. It can only be true in Card Editor.
objectlanguage
Is the 4-character identifier of the scripting component
that runs the script in objectscript.
objecteditrecord
An edit buffer thatÕs non-nil only when the objectÕs text
is being edited. Not sure what an object can do with this handle. It is used
extensively in the framework.
objecthasframe, objectfont,
objectfontsize, objectstyle, objectjustification, objectfillcolor,
objecttextcolor, objectframecolor, objectdropshadowdepth
These attributes are dealt with automatically by the
framework. You should not change their values, and when drawing your object,
you donÕt need to set up the Mac toolbox to use these values -- itÕs already
been done for you by the framework.
objectlinespacing,
objectindentation
These attributes are waiting for the day when we have a
better text editor wired into editable text objects and static text objects.
sorttag
A temporary field used when sorting lists. You can use it
like the tmpbit if you absolutely have to.
Config Records
Iowa
needs certain basic info in order to manage your object. One of the calls you
must implement is the getConfigRecord callback. It returns an objectConfig
record, which contains the following fields:
char objectTypeName [32];
A string of at most 31 characters containing the typeÕs
name. ItÕs used in displaying information about the object in the 2click dialog
in Card Editor.
long objectTypeID;
The type number assigned to this object.
Object yypes are 32-bit signed numbers. To avoid the need
for an object ÒreunionÓ utility, IOA developers should ask UserLand for a
unique object type, or range of object types. ThereÕs no requirement that you
tell us what your object does. All types less than 128 are reserved.
1/19/93: HereÕs a list of current IOA object types
implemented by UserLand Software:
grouptype = 1 /*implemented internally in framework*/
checkboxtype = 2
radiobuttontype = 3
picturetype = 4
statictexttype = 5
edittexttype = 6
buttontype = 7 /*note two missing types, following*/
recttype = 10
linetype = 11
ovaltype = 12
icontype = 13
formulatype = 14
popuptype = 15
clonetype = 16 /*implemented internally in
framework*/
char objectFlagName [32];
Different objects use the objectflag field of the object
record in different ways. Checkboxes and radio buttons are on if their flag is
true, off if the flag is false. Buttons are bold if the flag is true.
If you donÕt make use of the flag field, leave this string
empty. Otherwise set it to explain your interpretation of the objectflag. When
only objects of your type are selected, the first item in the Object menu will
reflect your interpretation of the objectflag field. In all other cases it will
display as ÒFlag.Ó
Handle hsmallicon;
ioa.c automatically fills this field in by reading SICN 130
from the componentÕs resource fork.
boolean frameWhenEditing;
If true, the framework draws a frame around the object when
its value is being edited.
boolean canEditValue;
If true, when the user presses the Enter key with the
object selected, the value of the object is edited. For example, static text
objects definitely want to allow the value to be edited. Icon objects donÕt.
boolean toggleFlagWhenHit;
If true, when the object is clicked in runmode, we invert
its objectflag value, and redraw the object.
Checkboxes and radiobuttons both have this bit turned on.
boolean mutuallyExclusive;
If true, when Card Runner starts up the card:
¥ All objects of this type are turned off,
their objectflag is set to false.
¥ The ÒfirstÓ mutually exclusive object in
each group is turned on. Ordering is visual. The topmost, leftmost object is
the first object in a group.
¥ As usual, the top-level of the card is
considered to be a group.
boolean speaksForGroup;
When Iowa is determining the value of a named group object,
it scans the group looking for objects that are willing to speak on behalf of
the group. When determining the value of a group, the value of the group is the
value of the first speaker whose objectflag is on.
Radio button objects are speakers. The value of the first
(and only) radio button in a group thatÕs turned on is the value of the group.
This flag makes it possible for other object types to
behave like radio buttons.
boolean handlesMouseTrack;
Set this true if your object type needs to handle run-mode
mouse tracking for itself. Popup menus and editable text objects handle their
own mouse tracking.
If your object doesnÕt handle mouse tracking, Card Runner
handles tracking for you. HereÕs how:
¥ It sets the tracking field of the card
record to true.
¥ While the mouse is down, it calls your
drawObject callback repeatedly. If the mouse is pointing in your object
rectangle, the trackerpressed field of the card record is set true, otherwise
itÕs false.
¥ A handle to the card record is linked
into each object record, in the owningcard field.
In your drawObject callback you should watch for this and
draw your object ÒhotÓ if the conditions are met:
hdlcard hc = (**h).owningcard;
if ((**hc).tracking && (**hc).trackerpressed)
draw it hot
else
draw it normally
See radio.c and checkbox.c for examples.
boolean editableInRunMode;
Set this to true if your object can be edited by the user
when the card is running.
If so, your editObject callback will be called when the
user clicks on the object, or when the user moves to your object with the tab
key. editObject takes a boolean parameter indicating whether you should
activate your editor or deactivate it.
boolean isFontAware;
Set this to true if you care about the font, size and style
of your text. If you donÕt care, the Edit menuÕs styling sub-menus will be
disabled when your object is selected. Pictures, icons, lines, rectangles and
ovals set this false. Other object types set it true.
This flag controls the ÒglobalÓ styling of the object. For
example, you can set the font of an entire text object to Geneva, but you
cannot selectively style a single word in a text object.
If more than one object is selected, and one or more is
font-aware, the menus are enabled, but a font command will only set the
attributes of objects that are font-aware.
If you are not font-aware, you can depend on your objectÕs
objectfont, objectfontsize, objectstyle and objectjustification remaining
unchanged.
boolean alwaysIdle;
If this flag is set true, your idleObject callback will be
called on every idle loop when the card is running, even if it isnÕt currently
being edited.
ItÕs very rare for objects to set this bit, in fact, none
of the UserLand-supplied objects (1/19/93) do. But itÕs forseeable that some
object might want to get a shot at the processor whenever the machine is idle.
IOA Callbacks
HereÕs
a description of each of the callback routines implemented by each IOA
component:
void getConfigRecord
(tyconfigrecord *);
This is handled automatically by the IOA Toolkit in ioa.c,
itÕs called when the component receives its kComponentOpenSelect message. It
sets defaults for callbacks and flags and then calls your setupconfig routine,
which every IOA component must implement.
boolean initObject (tyobject *);
Fill in the fields of the object record with the defaults
for your object type.
Return true if you want the object to be edited, false
otherwise.
Before youÕre called, the object fields were set to default
values for all new objects. The objecttype field was set to your type, and
objectautosize was set true if the card designer clicked to create the new
object, false if the objectÕs rectangle was determined by the user dragging to
create the new object.
If your object needs to be recalculated after the object
record is allocated, set the object recordÕs objecttmpbit field to true. The
object will be recalcÕd before Iowa returns to its main event loop. (Popup
menus use this feature).
void drawObject (hdlobject);
Draw the object using its objectrect and objectvalue and
perhaps other information stored in the object record.
Normally, after you draw the object, if itÕs selected, the
framework will draw a selection frame and a ÒnubÓ around the objectÕs
rectangle. If your object looks bad with a frame around it, set the objectÕs
objecttmpbit true, and no frame will be drawn. A nub is always drawn. [Frame
object types set objecttmpbit true.]
boolean clickObject (hdlobject,
hdlobject, Point, boolean);
The user clicked in your object while the card is running.
The first object youÕre passed is called listhead, itÕs the
first object in the group containing the object, or if itÕs not contained in a
group, the first object in the card. Some object types need to operate on other
objects, for example radio buttons turn off all radio buttons except for the
one that was clicked on.
The second hdlobject is the one that was clicked on.
The third parameter is the mouse location when the mouse
was clicked.
The fourth parameter is true if the shift key is down,
false if it is up.
Return true if you changed the value of any object, false
otherwise.
boolean editObject (hdlobject,
boolean);
Called when the user clicks on your object in run mode, or
when the user moves into your object with tab key. In both these cases, the
boolean parameter is true, indicating that you should start editing the object.
It is also called with the boolean set false when the user
clicks on a different object to edit it, or tabs out of your
object.
You can use the objecteditrecord field of your object
record to store a record describing the editing state of your object. (Not sure
about this. DW 1/23/93)
boolean idleObject (hdlobject)
Called in run mode when your object is the active editing
object while waiting for the user to do something.
Editable text objects flash the insertion point.
boolean setObjectCursor
(hdlobject, Point);
In run mode, the mouse cursor is pointing at your object.
Set the cursor accordingly.
Return false if you want the default arrow cursor.
The second parameter is the current mouse location, in case
your choice of cursor depends on where in the object the mouse is pointing.
Your setObjectCursor handler will only be called if the
object is enabled.
boolean keystrokeObjectCallback
(hdlobject, char);
Called in run mode when the user enters a keystroke when
your object is the active editing object.
Editable text objects insert the character at the insertion
location.
void cleanupObject (hdlobject,
short, short, Rect *);
Change the rectangle so the object can exactly display its contents.
We pre-compute the height and width of the text value of
the object, and pass them as the second and third parameters. Many objects
determine their size as a function of the size of the text (e.g. checkboxes,
buttons). But you can ignore these values if you want.
Return the object rectangle. DonÕt worry about its
position, just its size. After you return, the framework takes care of aligning
the corners of the rectangle to the grid.
boolean recalcObject (hdlobject,
boolean);
If your objectÕs value isnÕt calculated, just return true.
The boolean parameter is true if this is a major recalc,
false if itÕs a minor one. If it is, you have to decide whether it gets
calculated only when the card starts up (a major recalc) or whenever the value
of another object changes (a minor recalc).
Examples: popup menus are evaluated when the card starts
up. Same with text objects, checkboxes and radio buttons. Formulas recalc on
major and minor recalcs.
boolean canReplicateObject
(hdlobject);
Called when the user hits the Return key with your object
selected. Look at the fields of the object record to determine if the Return
key should be used to create a new object.
Most objects simply return true without checking the
fields. Exceptions are static text objects and editable text objects.
void getObjectInvalRect
(hdlobject, Rect *);
Return the rectangle that you want invalidated to force a
redraw of the object. Usually this is just (**h).objectrect, but in some cases
(e.g. bold buttons) itÕs necessary to invalidate more than the objectÕs
rectangle.
boolean getObjectEditRect
(hdlobject, Rect *);
Return false if the object canÕt be edited.
Otherwise return the editing rectangle for the object. ItÕs
usually derived from the objectrect field of the hdlobject.
boolean getValueForScript
(hdlobject, Handle *);
Return a handle containing the value of the object, in text
form, to be substituted in a script.
Although your code shouldnÕt care, getValueForScript is
only called when your object has a name and itÕs referenced by the script
linked into another object.
For example, a radio button object returns true if itÕs
turned on, false if not. An editable text object returns the current text of
the object, in double quotes.
The syntax of your returned value should match conventions
across scripting languages. The constants true and false are recognized by both
Frontier and AppleScript. Quoted strings use the same syntax in both systems.
In Frontier 2.1, list constants will be supported, so itÕs safe to return one,
for example: {ÒRedÓ, ÒGreenÓ, ÒBlueÓ}.
Doug says that this should return an Apple Event descriptor
instead. What do you think?
boolean postUnpackObject
(hdlobject);
After the framework unpacks an object from a packed card,
you have a shot at the object record.
For example, popup objects nil out a handle that gets saved
in its data record and intializes other fields.
The returned boolean value is ignored.
boolean debugObject (hdlobject,
Str255);
Return true if the fields of the object record are
consistent. If they are not consistent, return false, and set the string to
your error message. This is used for debugging the Iowa framework.
Current implementation of all UserLand-supplied objects
simply returns true.
boolean catchReturn (hdlobject);
The user just hit the Return key in run mode. Does your
object want to catch it? Bold buttons do.
Return true if you want to handle it. Return false if you
want to pass it on.
If you return true, the framework highlights your object,
waits three clock ticks, waits for the Return key to come back up, then
un-highlights the object. Then Iowa calls your clickObject handler.
Only enabled objects are considered for handling the Return
key.
For bold buttons, we thought about making it conditional on
whether or not there was an active text object, but decided to leave this to
the card designer. If you want to catch the Return key, use a bold button. If
you want to pass the Return key into editable text, use a normal button.
We left this as a callback instead of a flag, because itÕs
at least conceivable that the catching of Return keys might be conditional for
some future object type.
Random Notes
setupconfig
Every IOA component must implement a routine called
setupconfig.
You should fill in the fields of the config record,
link in your callbacks. You can also initialize globals. The resource fork of
your component file is open while your setupconfig routine is running, so itÕs
a good time to load stuff from a resource.
Internal object
types
Two object types, groups and clones, are implemented
internally in the framework, since they are structural to cards, and rely on a
much more intimate relationship with the Iowa framework than other object
types.
Detail: Card Editor also knows how to paste a
PicHandle into a picture object. If a picture object is selected when the user
has selected a single picture object, the PicHandle replaces the picture. If
nothing is selected, a new picture object is created. We hope in a future version of the spec to remove this
special knowledge of picture objects from Card Editor.
Two utilities
from Apple
The Things! Control
Panel is a must-have for component developers. ItÕs a simple browser for all
components you have loaded. ItÕs available on AppleÕs Developer CDs.
Another utility, Reinstaller, allows you to drag/drop new component files onto
it. They are installed. Be sure to close Things! before doing so, since it
seems to make it crash. But this saves you from having to do a restart just to
install a new component. ItÕs available on AppleÕs Developer CDs.
Note: at some point we will provide component management
verbs for Frontier, or a UCMD, so it will be possible to do custom installers
as scripts. Reinstaller is a nice start, but it really should be a droplet.
Component Manager
access from Frontier scripts
You can register or unregister components from
Frontier scripts using the Components UCMD. ItÕs in the Extras folder in the
Iowa 1.0b14 release.
New feature in
Iowa 1.0b14
Added an EventRecord to the definition of a hdlcard.
This allows IOA components to pass on the record to a toolkit, such as
QuickTime or the List Manager. ItÕs set in the main event loop of Card Editor
and in the main event handler in Iowa Runtime. When your component is called,
(**(**h).owningcard).macevent contains the last event received thru
WaitNextEvent.