WWDC2000 Session 154

Transcript

Kind: captions
Language: en
I'm Chris Espinosa if you were at the
earlier session apples
you've heard a lot for me
if I press the forward button and the
Machine just dings what does that mean
[Music]
this session is going to be a lot
different than the overview this morning
this is a very code oriented session if
you are looking to learn how to script
in applescript now would be a fine time
to debark this aircraft we are going to
be talking about C++ code we're going to
be talking about classes we're going to
be talking about a lot of things that
are only of interest to people who are
writing applications that want to be
scriptable if you want if you are a
scripter don't hurt yourself by trying
to listen to me go to ww a calm /
applescript and download some of south
ago IANS very very fine applescript
guidebook modules on how to write apple
scripts for those of the rest of you if
you want to learn how to make a
scriptable application in c or c++
you're in the right place good what
you're going to learn in this session is
why there are relatively few good
scriptable applications on the Macintosh
platform because it's hard there are a
lot of bad scriptable applications on
the Macintosh platform because it's
easier to make a bad scriptable
application than a good scriptable
application frameworks can help
frameworks can help by doing a lot of
the work for you frameworks can hurt
frameworks can hurt because you assume
they're doing a lot of the work for you
when they're not so we're going to tell
what it does for you versus what you
have to do yourself we're going to talk
a lot about power plants there has been
a very good power plant solution out
there Greg Dow put a lot of work into
making some good scripting classes in
power plant and it's the most widely
distributed and widely used application
framework for Macintosh we're going to
be focusing a lot on that and then we'll
be covering some alternatives one of the
alternatives I want to stress is a
relatively new one
which DTS put up just last week is the
more OSL package if you're familiar with
more files more Apple events more
toolbox from dps which basically fills
in for developers the functionality that
developers want in the OS that the OS
designers didn't put there well this is
another in that long and esteem series
of software code bases that does just
that DPS has done a wonderful job of
responding to what the most frequently
asked developer gripes about how to
write scriptable applications are and
filling that in in a very coherent and
manageable way great piece of work and
I'm going to be walking you through that
let's start with some background because
I know that some of you are approaching
script ability for the first time
applescript and the open scripting
architecture osa is a way for a high
level language a user written script to
send events to applications so that
applications can act on those events and
execute them and the biggest problem
that we find the biggest sin that is
committed is applications that simply
expose a flat programmers API to the
scripting language a whole lot of events
and not a lot of information because
what we found over the years with what
is popular with scriptures in
applescript is they don't just want to
send a bunch of events to applications
they want to get and set information
within that application and the
information they want to get in set is
the information on the user's model the
conf the conceptual model of the
information inside that application
rather than clicking a checkbox or
pushing a button or choosing a menu item
they want to get the value of something
or they want to create a new user object
somewhere in the document it's not you
know an automation like a macro maker or
playback or recording it's more like we
want to make queries on the data in your
application make intelligent decisions
based on that data and then do something
that
manipulates the data not manipulating
the application because most of the
really really good process automation
and workflow solutions that are built
using Apple script are very very
data-driven not command driven and that
this is why the object model is so
important and what is the object model
the object model is the way that osa
languages access the user-visible
information in your application and it
applies a human terminology to the
programmatic elements inside your
application okay the two biggest problem
two biggest things people do is one is
that they just provide an event
interface and no object interface so
that the users can't see the objects
because of all these events and two is
that they fail to give human names to
the objects within their applications
and just basically propagate the C++
identifier is up to the scripture levels
and that that's confusing to people an
object is can be but is not necessarily
your concept of a programmatic object
inside your application if you look at
the model-view-controller paradigm for
developing an object-oriented
application what we're really talking
about is the model we're not talking
about the view we're not talking about
how it's drawn on the screen and we're
not talking about the controller how
it's manipulated we're talking about the
existence of the object and the
properties that that object has those
properties can be read and set
preferably from the scripting language
it's of a designated class so it can be
told apart from other things of that
class and that class may be in a
hierarchy and it can share some things
or inherit some things from other
classes it can be a container of other
objects like a window can contain data
elements or a folder can contain files
or a page can contain graphic objects or
a paragraph can contain words or
characters and it can be contained by
other objects as well we script objects
and the best scriptable applications are
those that use as few verbs as possible
to script
all of the objects in your applications
that's a subjective statement but I lay
claim to it the way that applescript and
OSA languages designate to your
application what they're trying to act
on is through this structure called an
object specifier and an object
specifiers possibly the most heinous
data structure you're going to come
across in McIntosh programming it can be
incredibly complex it can be vague and
ambiguous it can be very detailed in
inappropriate places and there are 247
different variants that you have to
special case to make sure that when
somebody sends you an object specifier
you understand really what they're
talking about what it does is it drills
down from the top level of your
application down to a specific piece of
data in your application and can get
there through multiple of routes and you
don't have necessarily have to have a
strict containment tree so it can really
go through pretty bizarre ways let me
show you I'll show you a little later
how complex and how weird object
specifiers can be the way you get an
object specifier is as an apple event
record that is deeply nested it's going
to look on the top level like oh this is
easy it it's an apple event record with
four parts to it but then one of those
parts is an object specifier which has
four parts to it which can be an object
specifier that has four parts to it and
and it can go on down and down and down
the four parks are basically the
container of the of object that's the
from part the type of data that's being
requested which is the want how to
select a value from the container which
is the key form and we'll be talking a
lot about key forms and then using that
key form which one to pick okay so if I
say I want a folder one of startup disk
that's basically from is startup disk
want is a folder form is by absolute
position and selection data is one okay
so that's a surly simple object
specifier but they can be more complex
for example the object specifier the
modification date a file to through file
for of folder applications of startup
disk is nested kind of like this and the
top level on the outside you have you're
looking for the property you want a
property the selection is the code for
that property which is the modification
date and the container is you want the
property of a file so the next thing is
you want a file selected by the key form
range the key form range is going to may
make two parts come you want the
selection data which itself is a record
with the start and a stop and the starts
going to be you want forum index you
want a file from the current container
index to and the stock is going to be
you want to file index it's actually
wrong that should be index for see how
easy it is to make a mistake so you want
start at to stop at four from the
current container and then the last
thing the from goes all the way down to
the bottom there and is I by name the
selection a folder the selection data is
quote applications from the startup disk
and the startup disks container is null
which means it's rooted at the
application so there are all told 123456
object specifiers nested to to create
this apple this this object specifier
from an applescript and then this is a
parameter to an event which can be get
the modification date set the
modification date or this could be just
a clause in a you know delete every file
whose modification date you know etc so
its object specifiers can be arbitrarily
complex and if you tried to write code
yourself to parse this you'd go fairly
nuts I know because people have
here are the basic key forms you know
one of the reasons is the combinatorial
problems of all of your containers
against all of your object versus all of
the key forms and so before you go into
that you really need to know what the
key forms are the most familiar one is
by numeric index file one first page
that's the form absolute position and
the selection data you're going to get
is usually an integer which is very nice
now one of the things to remember is
that you are in control of this so you
can be expecting any selection data you
wanted form absolute position so you
could make form absolute position any
arbitrary thing you could make it a
string as long as long as it identifies
what you're talking about applescript
will tend to do that in the format
absolute position if if you say for if
you say five window quote untitled or
folder quote preferences that's going to
trigger the by name key form form name
and your selection data for that is
usually a string and it's usually going
to be a string in the system script we
don't have Unicode selection yet for
that so it's going to be a string in the
system script those are the two main
ones and and if all you support is by
numeric index and by name and you
support them well that's okay for
certain classes of unique ID is going to
be interesting but it really had better
be unique form unique ID means I want
this thing regardless of what its index
is because its index may change and what
its name is because its name may change
I mean to think of this if you have if
you're writing a script that's trying to
script a finder folder and you want to
change its name and when you change its
name its index changes in the list
because the finder automatically
resources the list by name you know how
are you going to refer to that thing
again if you've changed its name and if
you don't know what the name is and
don't know what the new index is you're
really going to need to hold on to
something and that's what the unique ID
is for so what you'd ride in the finder
is set X to ID of folder foo set name of
folder foo to bar and then it's no
longer folder foo its folder bar and its
index is different but the idea is the
same and then you can say folder ID em
and you'll be able to work on that
folder again those are the simple key
forms the more complex ones tandel are
before after another element that's
relative position this is how you insert
things in the list or a pin things to
list it's usually used with text that's
form relative position and form relative
position is basically a tuple of a
absolute position or any other reference
any other object specifier and then a
tag that that says before or after okay
as a range of items is a pair of the
start element and the stop element which
both can be any arbitrary object
specifiers and that's how you do you
know page 1 through 3 of in quark for
example or and then satisfying a test
which is the most interesting one it's
every folder whose name starts with or
ends with dot jpg you know a very useful
thing to do in the finder that's the
form test in the form whose in order to
make your object model really usable by
all of these all of these key forms can
be compiled by the applescript language
and so users can use these you should
document in your scripting terminology
in your dictionary which objects support
which key forms just to not mislead
people applescript will pay no attention
to that it will try to compile all of
these key forms regardless of what you
say in your terminology is generate
these object specifiers it'll send them
to your application and your application
will either say I don't support this key
form or do it right now too often
applications don't say I don't support
this key form and they don't do it right
and they just take a wild stab and
return an error and what we if there's
one thing I'd like to come out of this
session is people at least being honest
with scripters both in their terminal
and in their implementation what key
forms are support supported on what
objects and that's a hard thing to do
because you have to know them all and
know all their combinations because all
of the example users can come up with
wonderful wonderful phrase as an
applescript that your application just
can't handle like name of every track of
a movie one who's enabled is true or
modification date of every file of
folders 1 through 2 of every folder
whose name starts with a all of these
generate valid object specifiers and
your application if you say you've got
an object modeling you've got all these
objects you should be able to handle
things like this how do you handle this
the process of handling these is
something called object resolution and
basically when an event comes in and
there's an object specifier in its
parameter your first task is to resolve
that object to some indication of what
the thing is in your application that
that object specifier represents what
you have to do is have to descend in the
structure until you find that from null
which means oh that's the root object
and then you have to back out looking at
each key form and selection data in turn
and then maybe walking back down and
doing it again if it's like a rainstorm
and you need to descend into those
object specifiers and figure out what
they mean descending from that root just
basically unpack the object specifier
until you get back to the oh and what
they eventually want is the modification
date or the you know file or though
whatever so when you you walk until you
find no and then you walk out until you
get to the outermost object specifier
and then you're done but that's a hard
process it's recursive and it's got a
lot of different cases sometimes the
intermediate thing that's generated will
be a list like a range so that at the
next level you have to iterate over that
range and do the same thing to it like
the modification date of every file you
can find every file and then you have to
iterate over every file to get the
modification date of that a lot of rules
that you have to follow in order to do
it right there arbitrarily complex you
have to have baked
typically a three-dimensional matrix of
switch cases you have to have a switch
for every containment relationship you
have to have a switch for every key form
supported by each containment
relationship and you have to have a
switch for every potential data type of
the selection data you know the form
name could have a name it could have the
absolute position could be an integer
the absolute position could be something
other than an integer your ranges could
be object specifiers it could be
absolute position and there is our no
good general rules about how to handle
the weird corner cases a lot of this
stuff isn't written down anywhere
there's something provided with Apple
system software called the object
support library which has a routine
called AE resolve which purports to help
you resolve object specifiers it does
this recursive walk down and walk back
what you have to do is you have to
invent a lot of handlers registers those
handlers with the OSL and then handle
the information as they call you back
you have to create this token scheme
whereby you associate the information in
your application with these with these
callbacks and so when you get called
back you get past a token that says oh
they're talking about this thing they're
talking about this property of that
object they're talking about this object
and you have to manage that token scheme
yourself including storing and disposing
the tokens it's really a pain and then
the OSL doesn't really do your key forms
for you you have to do a lot of work
yourself and so if you just rely on what
OSL provides you end up with a very very
sparse partial implementation so what do
you do if it's so hard if it's so
complicated and the system software
gives you so little support what do you
do well you do it badly which is what
most of you have done and I don't blame
you but there are tools now that help
you do it well if your scripting with
the framework the general process is
that you create model objects in C++
that reflect your users view your
scriptures view of your application okay
so if you have a graphic object concept
that a user can draw a graphic object
you make a graphic object class in C++
and maybe you know you don't really have
a graphic objectclass and C++ in your
implementation but just create one whose
implementation then specializes to you
know Circle Square line or whatever but
create something that you can hang off
scripting then the general paradigm and
this is used in almost every framework
that off our prescriptive ility is that
any incoming event like make new delete
move whatever that operates on an object
will become a call to a member function
on that object and any Apple event
properties like the size the dimensions
the visible the color the descriptor can
get at are just represented as data
members of that object and then your
elements your containment relationships
become collection class collections on
that object and so basically what you do
is what I'd recommend doing is you start
with your user model create your
terminology then design a bunch of C++
objects that implement and C++ view of
that terminology wire them up on the top
side to the scripting engine of that
framework on the bottom side to your
actual implementation object and then
try it out and see how it works and you
may have to do a little tuning to get it
all right but that's the order that i
really recommend you start in from start
from the user model of your application
design your scripting terminology
implement C++ objects that are
structured like that terminology then
wire them up to the scripting engine and
then wire them to your actual
implementation so that the script
commands can flow all the way from the
Scriptures view of the world to your
actual objects in your C++ application
and so this is how it works in power
plant if you get power plant off the
shelf and this has been true for years
what you can do is if you have a C++
class and you derive it from the L model
object class that becomes the equivalent
of an apple event object model class and
so in your terminology if it has class
window if you look in power plant the
window class and power plant derives
from L model object and so it looks like
a window to applescript the applescript
window class the events are member
functions so just as the window l model
object in power plant has a lot of
internal C++ method functions that
member functions that it responds to it
also responds to member functions that
correlate to the standard suite of Apple
event objects makes new delete for
windows it's closed save save as so
there's a correlation between the events
that come in and the member functions on
that class the parameters or arguments
for those member functions properties of
objects correlate to the data members on
those classes power plant implements the
containment relationships using their
collection classes called el lifts and
so everything every object that descends
from El model object has a number of L
list as basically FML list attached to
it which is all the things that that
object contains and so a window if you
have a window that's populated with
widgets and widgets also descend from l
model object then your window class is
going to have an l list of widget
objects and then each widget object is
going to have a data member that's that
called M supermodel and that's going to
be the window it belongs to ok so that's
how you do the containment relationship
with the l list on the container and the
M supermodel on the contained and this
makes it very very
easy once you have your object model
designed and your terminology designed
to go in and write all these C++ classes
that derive for male model object and
have L lifts and have them super model
data members and that makes your
internal structure resemble the
terminology that you've defined and
power plant helps you out a lot in this
by doing some of the stuff automatically
especially the event registration
basically first you you start with your
terminology resource and I hope all of
you have have learned how to create an
editor terminology resource we could
have a special clinic just on that
because it's pretty hard but you add an
apple event object model class to your
terminology resource and you invent a
four character code for your class w idg
for your widget okay then you create the
C++ class that has L model objects as a
public ancestor and then you redefine
the constructor on your w idg object on
your widget object so it takes an L
model object reference to its container
and you pass that pointer to the AL
model object constructor and when
somebody says make new widget your
constructor gets called and it just
happens automatically it's very nice
it's a very clean system in the
constructor you've set the model kind to
that for character code that you've
defined in your terminology and that's
how it gets linked and then in each
container that has that class is an
element this is the containment
relationship you call set uses sub-model
list to true and that says oh I'm a
container I contain these things and
then you'll get method calls you'll get
member function calls on your objects
that correspond to apple event calls
that's sort of like handle if somebody
makes a new thing of you like if you say
make new widget that container object
will get a call handle create element
and that says oh you know this container
the window wants a new widget created in
it then there you go here's here's the
new widget object that's been created
here's the container that it's being
created in do what you need to do in
your low level code to draw it or
initialize it or whatever you need to do
once the object exists then getting and
setting the class properties are very
straightforward you again define your
properties in your a été you create a
four character code for each property on
each object and then there's a method on
each class called get AE property you
just override that method and you switch
off the floor character code and there
you go so if you have a win if you have
a widget that has color size shape
property you have three four character
codes for those your get AE property
method will be called on your widget
class it'll be called with the parameter
that is the property code you just
switch off that and say oh if I'm
getting the size i return this if I'm
getting the shape I return this if I'm
getting the color I return this then you
can go in and do the appropriate
manipulation to get the right data you
create an AE desk with the value and
then you return that as the return value
of get a property it's very
straightforward again here's an example
of it i mean you switch on the in
property do three cases this is content
filled and line width for example and
you just get the appropriate value
straight from the object where you
synthesize it if it's necessary and then
you call 80 create desk to put it into
the right form that correlates to the
class that you defined it as in your
terminology you know if your terminology
said that your contents are q directa
our rectangle re CT then you'd better
return it as a rectangle that would be
good setting class properties is similar
what they're what's going to come in to
your set AE property is a property
switch in a value in an 80 desk you
write a switch statement that switches
off the end property then you extract
the value from the AE desk and coercive
if necessary to the right one and then
you set it
is it can't be set you return an error
if the set is successful then you return
silently so here's a set IE property
switch notice something in here there's
a refresh call basically if your
application has a user interface when a
scriptor sets properties on an object
you may want to update your visual
display you may want your model to talk
to your view to refresh the image if
somebody set the bounds of something you
wanted to redraw it so you have to call
refresh to redraw it how to kee forms
work in power plant power plant
implements most key forms for free if
you if it because l LIF are indexed list
power plant knows how to get you know
widget for of window to because it just
goes down the L list and gets the fourth
widget from that and sends them to that
object form name works if you have a P
name property a special case if you have
a name property form name works
everything else you really have to do
yourself there's this method called get
model token self which passes in the key
form and you pretty much have to figure
out from there how it works mostly
they're pretty easy there's some sample
code on the web I can give you a
reference to it that implements form
range doesn't implement it well but it
basically implements it form unique ID
is fairly straightforward who's clauses
are kind of difficult though and how to
events work well the good news is that
power plant implements exists make
delete count duplicate and move the
basic standard event for any object that
defends from L model object so these
come for free which is really nice you
just have to implement the constructors
and destructors duplicate is fairly
simple the difficult part for duplicate
is that you've got to set the initial
like making you've got to set the
appropriate initial values for
so for duplicate you've got to get the
get the properties that are important of
the object you're duplicating and then
set them you know if you duplicate an
object all the properties are not going
to be identical the index will be
different unique ID will be different
for example so if you're duplicating a
widget you need to get the important
properties of widget of the old widget
and then set the properties of the new
widget to though to that set skipping
the ones that are defined to be unique
like the name perhaps like the index
like the unique ID and then you've got a
handle create element event in each
container you know create have to happen
into place if you're creating a widget
in a window the widget has to know it's
being created and the window has to know
that a new widget has been created in it
in order to get all the updating to
happen right here's some code and this
will be on the slides on the CD if you
want to read it if it's too hard to read
up here but basically this is the create
element element event you switch on what
class you're creating if your if your
container can handle multiple different
classes created in it then you know if
it's if you're gonna create event to
create this then you do a new that if
that than you to a new this if it's not
then you throw an error now what about
custom events I mean it's all well and
good to have an application that is just
objects in the standard events and I
really really applaud you if you can
make an application that is nothing but
objects in the standard events but you
might want to do your own custom event I
mean the finder does for like reset and
restart and shut down that's pretty easy
to once again you start by defining the
events in your terminology resource and
inventing a four character code and it's
actually a pair of four character codes
that event class and even type what you
do in power plant is power plant has a
resource called the aedt resource and
the aedt resource is nothing but a table
that maps event class code
there's two long ends and that's all it
is is just a table of event class code
and long it and what happens is that
when an apple event comes into a power
plant application it gets looked up in
this table the longing is generated and
then you switch off of that long in
because it's too hard to switch off of
244 care codes so it basically switches
off the long it so if you want to create
a custom event you know like a refresh
event or something like that you event
invented the clasp creatorcode for it
you associate it with the number and
then you in the switch statement in
handle apple event you switch off of
that number and then when that case is
called you just execute your code their
net and you do that on every object that
can handle that event ok so if every
object has its own refresh event you
might want to have that handle apple
event method and have that case in the
handle apple event method for each
object that handles a refresh event or
you might want to have it for example if
you've got a number of objects that can
be refreshed you might want to just do
it once and have all the other
refreshable objects inherit from that so
that you know you just assume the
inherited capability and in many of the
cases for the standard events that's
exactly what power plant does is it
doesn't implement it in the object
itself but it just hands it off to the
class that inherits from so here's an
example here's a rotate event you switch
on the apple event number if the case is
eight you rotate then you call rotate if
not then you call the inherited handle
apple event so that you pass it up to
the the superclass to see if it can
handle it here's some things to remember
about power plant number one is that
power plant applications are by default
scriptable in the most stupid possible
way in that they support a small number
of standard events on the window class
only
but they expose a full in some cases too
full Apple event terminology that
promises more than it actually delivers
so for example the if you just build the
demo power plant application it says it
supports make new delete count create
whatever on a whole bunch of objects and
none of that support is actually in the
code and that's bad so the first thing I
want you to do is to trim or throw away
the terminology resource in your power
plant application treat it as if it's
just you know start from scratch design
your script ability then write a
terminology resource from scratch and
then implement that terminology that you
could find because if you don't don't do
that what's going to happen is the
default terminology resource that gets
compiled into your power plant
application is going to promise that it
supports all these events with all these
objects which just aren't there and it's
a source of great frustration for a lot
of scriptures that you know they get a
new application and they first thing
they do is they drag it onto the script
editor to see whether it's scriptable
and this dictionary pops up and it's
great I'll look at all these events
there the standard suite they got all
these objects and you look at it and
it's just power plant and that's all it
is and then you know it's not going to
work so please from your power plant
dictionary the second thing is I cannot
possibly do power plant scripting
justice in the time I have in this
presentation but luckily on codewarrior
pro and actually every version of cobra
or back for several years there's been a
very good introduction to doing script
ability and power plant in the power
plant advanced topics book that's in PDF
form on the power plant tool
documentation CD in the code warrior
documentation CD it's there it's good
read it study it these slides were
basically stolen shamelessly from there
it's good documentation and it will help
get your head around the whole Apple
events thing so if you
mundt to code and power plant go to
power plant advanced topics trim daet
resource start with designing what you
want the user to see build your
terminology build some C++ objects that
reflect that then wire them up both to
the scripting interface through L model
object and then to your actual
implementation that's the way to go
about it but what if I don't want to use
power plant well you've got options one
of the things you can do is you can
manually extract L model object and the
l list class and some of the other
classes that are hanging on you can just
extract them from power plant and
compile them into your application now
there are a lot of little threads that
you may have to pick up of things that
power plant depends upon but the power
plant classes are extractable from power
plant itself so you don't have to use
their window model you don't have to use
their drawings model their load and save
you can just extract the classes and
adapt them and use them as a model in
your own application could be a lot of
work but it does save you a lot of work
so that's one thing you can do there's
another framework an old venerable
framework called sprocket by Steve cific
that hasn't been updated for guests for
years now but it's still available
online at code welkom I believe and it
supports some some decent apple event
scripting it's some helper classes that
that help you get your head around it
it's not terribly up to date but it's a
start if you want to commit to using
objective c or java and deploying on mac
OS 10 only you can use the coco
framework there's some very good script
ability in cocoa and you can go to the
cocoa advanced topics presentation which
is at three o'clock today to talk about
that or you can use the mac app
framework and i'm sure Tom Becker will
be very happy to show you what script
abilities in the mac app framework but
there's one more opportunity which has
just presented itself coming from our
DTS group called more OSL moro SL is
not a framework per se it's more like a
support library that you can use from a
sea application or a C++ application its
carbon ready which if you want to deploy
on Mac OS 9 and a quest 10 is very nice
it'll get you there faster and it
deploys all the way back to mac OS 8.5
and compiles all the way back to
codewarrior pro 2 so those of you who
are sticking back on earlier versions of
code warrior and don't need don't want
to use latest and greatest are not cut
off from this functionality it supports
it really does support doing a good
object model really like the way that it
goes about doing it and it's
comprehensive it's tested and it comes
with a good sample application that
shows you how it works and I'm going to
be demoing that a little while you can
get it on developer apple com / sample
code it's a little deeper in there but
there's a at the moment there's a
top-level link to it very small quick
download go for it here's the theory of
operation it's a little different from
the way the power plant thing works you
have to define your user classes in your
user events and I recommend you do it
the same way you start with the user
model and then you define your
terminology and figure out what your
object model classes an object model
events are going to be but then you make
tables you make a table of all your
classes and you make a table of all your
events and then you make a table for
each class of what events apply to that
class and you build those in straight
see struts fans of straight see struts
you create an event table entry for each
event on each class and it maps the
event codes to classes they operate on
and then it also has some bits that
declare what's supposed to happen with
the direct parameter whether it's
required or optional whether this
generates a reply or not things like
that so you said a couple flags in these
tables so basically the first thing your
application does is creates all these
tables that defines a script ability
then you then more OSL does all of the
registry with the object support
librarian with the apple of
handler for you it does it registers its
handlers so you don't have to so when an
event handler gets called or an object
access or gets called moro FL takes
control and it will call back to you
when it needs to but you don't have to
interact directly with the operating
system which is very nice and in many
cases the generic handlers the generic
event handlers from where else L will do
what you want with no further
intervention which is actually pretty
cool when your application process is an
apple event more OSL resolves the direct
cram parameter it calls o SI resolve for
you it dispatches the event to the event
handler for your class directly it's
called class first dispatching it's it's
the right way to do it and then the
generic handler handles the event unless
you've defined a specific handler for
that event on your class in which case
your specific handler gets called so let
me show you just basically a class table
entry there are three main parts to it
there's an event ID which is you know
here's the event that's coming in and
then a couple of pointers to other
tables a table for the properties and a
table for all the events on that class
and then for each class you must supply
a couple of callback routines and you
can supply others and just leave them
null if you're not supplying them for
each class you must write a counter
routine that counts how many of those
class there are in a container and then
you can provide accessors that are get a
class by unique ID you know gets get me
from my container by unique ID by index
by name and then there's get me or
something of me there's a set me or
something of me and then there's a
coerced token and the tokens are pretty
much the way that the OSL works the
token is a representation of the
internal object and tokens in more OSL
are basically just pointers they can be
a class pointer they can be a pointer
that can be a handle but it's basically
32 bits that uniquely identify something
in your application
so then there's an event table entry and
the event table is very straightforward
it's just the the the four character
codes for the event in the event class
whether or not the direct object is
required and whether or not there's a
result in what action to take on the
result and then in each class there's a
table for weather where's the handler
for the exists event the count event to
get data event and the feds data event
okay and then as you add more custom
events to the event table you have to
provide corresponding events in the
class event handlers I mean it's really
dumb dispatching the event table the
master one and every class event table
has to be in parallel that is if you add
you know a rotate event to the event
table entry you have to add a rotate
event in the same position in every
class event handler even if most of many
of them are null because you can't
rotate those but the one you have one
that does support rotate that's the
pointer to the rotate routine ok so the
class event handler table should look
like the event table entry customized
for each class and then the property
table off of each classes class is very
straightforward it's property code and
the bunch of flags that say what it does
and the property access is actually done
through the classes getter setter event
with the same kind of switch statement
if you provide a P properties property a
property's property in your terminology
which is you know the way things are
done now Laurel OSL will automatically
iterate over all of the properties in
your property table and get them or set
them on mass which is great it saves you
the trouble of doing that if you provide
a P inherits property in your
terminology and you put it in your
property table entry more OSL will
automatically look in your container
classes for properties that you don't
support directly so if I have you know
like in the finder there's a item and a
file and items have names files don't
have names files have like modification
dates but they inherit names so
in the in Morro South Carlin's there
won't be a property table entry for name
in the file object and more OSL will see
the inherits and say oh I'm looking for
the name property I don't look in the
file object I look in the item object
that inherits from and it'll do the
right thing for you this is really very
nice in terms of event handler more OSL
does exist counts get object and set
object for you it implements those
generically and in most cases you really
won't need to do anything to change your
override those most of the key forms are
handled automatically and this is good
for two reasons one is that you you just
don't have to implement like form range
and several of the form test cases and
second is you can read the source code
and see how it ought to work if you so
desire to see how it ought to work it's
really great the only one that's not
handled automatically in the current
version of OSL is the form relative
position before after and amend let me
talk a little bit about tokens like I
said tokens are the way that you relate
an object specify a resolved object
specifier to something in your
application and the token is basically a
token code and object code property code
and then a pointer to something you know
and this is a simple token definition
that pointer to something can be like a
window pointer pointer to a toolbox
structure it can be a pointer to one of
your C or C++ data structures or it can
be a C++ object it's just 32 bits that
your application if given a pointer can
say oh this is a pointer and the object
code reminds me what it's a pointer to
that means I can do something with this
data okay i can create it i can delete
it i can get information from it
whatever the only important thing about
a pointer is that your application
understands what it is a token tokens
never leave your app they're never sent
back to Apple script they're never sent
anybody else so all that a token has to
do is be created by and understandable
by your application
properties getters and setters it's very
straightforward together if if somebody
gives you a property token your getter
ought to return and appropriate value if
somebody says I want this property of
this object you've got to return an AE
desk that's that value it's all there is
to it setter the same thing if somebody
says here's up here's a token of an
object here's the property and here's
the new value I want you to set it to
you should set that property somewhere
in your application and return error or
no that's all you need to do if there
are other parameters on an apple event
you have to extract them manually with
good old a you get Prem desk and there's
a useful more OSL routine to coerce it
if it happens to be an object specifier
it'll do the resolution for you you know
this is useful for things like set name
of file one to name a file to in that
case the two parameter is an object
specifier name of file too and so what
it happens is the set command will
resolve the direct parameter name of
object one and give you a token and then
it'll be set token to object specifier
and then your senator will have to say
well you know I don't know what this is
it's an object specifier so you hand it
off to coerce object mosl course object
desk and it will take the two parameter
the object specifier and it will call
back inside your application and resolve
that and get the name of file to and
return the string and saying okay this
is what you should set it to set name of
set this token to this string and then
you just do it because then you know
what it is more ofl does deep object
resolution which is really cool it's for
cases like first file of every folder of
every disc that's going to be a list and
what that's going to be a list of is on
every disc for every folder at the top
level of every disc
you want the first file of each of those
folders and or the name of the first
file of each of those folders and so
Mauro SL has an object resolution
algorithm that goes deep into those
structures and creates one long list
from that we could go into it it's well
documented in the more OSL documentation
so without further ado if we could have
demo machine one up great when you
download more OSL one of the first
things you notice is you get a lot of
the more is better classes with you you
get more Apple events more windows more
text details the demo application relies
on a bunch of those but there are two
main things the more OSL files
relatively small most of the work is
done in this 140 k txt file called moro
slc there are a couple of separate files
to the string comparisons which are a
heinous and be the cause of many bug
reports which quinn is filed basically
saying applescript should either do this
itself or have callbacks to do this
because as you might know it's very hard
for your application to do string
comparisons the same way Apple script
does string comparisons and that could
be unexpected to users so we're taking
that feedback and trying to do something
with it there's a small source code file
to manipulate tokens small source code
file that some helper routines but
really more OSL is itself a relatively
small piece of code the second thing
you'll find is a fully functional test
application in codewarrior called test
more OSL and the delightful thing about
disc is it has almost no user interface
at all so you won't be tempted to try to
play with the UI it is mostly a
scriptable application and so if we go
to code warrior and open up should have
more or less test tomorrow I shall die
there we go here's the test application
and there's one source file to the test
application and the terminology resource
and that's about it and then the rest
are the more OSL sources and the test
applications one file and what it does
is it creates a model of a you have the
application the application has windows
and each window can have nodes in it and
nodes are just simple rectangles and
nodes can have subnodes in them and that
is the user model so what we did was we
created a user model windows nodes
subnodes then did a terminology for it
and came up with all the appropriate for
character codes for all of the events in
the class and things like that and then
built those tables and wrote those
accesses like we talked about so for
example in the test moro slc file here's
the here are all of the routines on the
application because the application
contains windows there's a getter setter
counter and access by index routine and
those are listed in its appropriate
tables there's the events it handles
open reopen oak open document quit and
make so basically you have one routine
for the getter setter counter and access
and then one routine for each event it
takes and the routines are pretty simple
so here's the application get er I
thought I'd set the text size on this
and hit power plant there we go
so here's your application getter and it
gets a token in and if the token is a
property then you switch on that
property and if it's the version then
you get the version property and return
it if it's a unique ID then you return
to unique ID and otherwise you say no
such object it's a very simple getter so
then there's a window class and the
window has its getter setter and its
event it responds to which is closed and
so here's the handler for the window
closed event which is basically handles
the saving in parameter so it gets back
and then closes the window with or
without the save options it's a very
simple handler so here's the document
inside the window and the document
handles make new ok and so this routine
handles the make event when it
determines we want to make a document
and it creates a new properties record
and this actually uses more some of the
more is better window classes in order
to put the window up on the screen but
it basically creates the classes for
that and then here are some of the
custom objects here's the node and
here's your node access by index since
more since more OSL doesn't have like an
L list structure it has to manage its
own data structures itself it doesn't
have collection classes to fall back on
so it has it just basically has a index
list of nodes with your node pointers
and then it it finds it by index and if
it doesn't find it then it says doesn't
have an element of this type so you can
see that the once you set up the more
the more 0 itself structure actually
implementing the apple event
an object model handling very straight
forward for every event and these
specific things you have to do for every
class you write one routine whose input
to these and outputs your needs and then
the rest of the stuff is just basically
handled for you and it makes it a lot
simpler let me show you this in action
will is it running little debug this and
run it and we can open up the script one
of the things I love about this is that
it comes with a test script which has
some particularly heinous cases in it I
mean it really exercises most of the
code paths with within the test
application and within more OSL itself
so for example here's a lot of things
that you should be able to send from
applescript to your application and had
your application handle you know set
front most of window to true and make
new document with properties and set
position of no dispute but set position
of some element of some document to a
list make a new window with properties
you know these are a bunch of things
that you should support in your code if
you have classes that support that
support them and so we're going to open
up the event log here show the event
results and it's compile this gets the
application
there are a lot of test cases in here
it's a big script and we're going to
execute it against that application and
here are all the test cases we're going
to run
and we're going to run it before the app
lament times out and you can see as they
scroll by here are a whole lot of object
model events going to this application
there's Gators there setters there's
create new there's delete there is move
there's duplicate and that relatively
simple application okay it doesn't do
anything with nodes it does very little
with windows but that relatively simple
application is handling all of these
appli- and handling them well you can
see some of the stuff we're doing here
set set the properties property of a
window to a list of properties which is
knife set names of windows get names of
startup disks exist files get ideas last
window here the some key form which is
supposedly take a random element from
this list of element that's fully
implemented so if we can go back to the
slide machine and we'll wrap up here
some other nice things about it the
application and window classes are
populated so if you're using the other
more is better window classes then then
you can just get that functionality for
free for things like normal application
and window properties and it knows about
file objects which are another thing
that Quinn filed a lot of bugs on is
that sometimes applescript will send
your application queries about
system-wide objects like the name of a
file given an FS spec and it expects you
to handle that to be able to get the
name of an ex FS back and return it and
if you don't know that you're supposed
to do that and nobody tells you you're
supposed to do that it's a pain in the
button it will result in a user bug
report but more more OSL does that for
you ok here's some notable emissions
there's really no object management like
I said there are no collection classes
for your objects so you need to manage
your objects yourself and you need to
have the actual routines
to do make new delete move and duplicate
the objects yourself I mean the routines
will call you but since it's not C++
it's not going to fire off constructors
and destructors automatically you have
to write that code yourself the print
event is not implemented selections and
insertion points generally the text
classes are not supported you'll have to
do that yourself like I said form
relative position isn't implemented but
we're going to try to twist somebody's
arm to get that in and no support for
things like properties of a built-in
type like if the result of a get
operation is a string and you ask for
the class of that if you ask for class
of version of application it won't
handle that right but then again I know
of relatively few applications that do
and it also doesn't support class
properties whose values are records or
lifts and that's a philosophical
decision and we're going to have a
philosophical discussion about it so in
summary if you want to use power plant
power plant can provide easy scripting
for your scriptable application there
are some things you know a lot of things
you have to do yourself but you can
really get started by just over right
you know inheriting from defending from
L model objects and overriding a few
handlers and you can get a scripting
implementation started what I really
recommend you do is you to trash the
default dictionary implementation and
you start from the beginning rather than
just trying to like monkey with the
sample application for mower mowers more
OSL if you don't have a framework based
application is really most appropriate
for it it's the fastest way to get the
basics done because all you do is write
a few handlers install them in tables
and there you go it assumes you're doing
your own object management which most of
you are doing anyway and it's very well
comment and very well tested and we have
some investment in it in that you know
when we come out with a new dictate of
how a new Apple script features supposed
to be implemented like the properties
property will try to get that into more
OSL as sample
okay we have a couple of sessions coming
up in a very short period of time I
think it's at three-thirty a feedback
forum across the hall way on the other
end for general feedback on applescript
if you want to know more about doing
scripting and cocoa you should go to the
cocoa in depth session instead which is
cross the street at specific auditorium
long walk too bad but Mike Harris will
give you a good show and what you can do
in cocoa scripting same contact
information for the last presentation
but for codewarrior and power plant i
recommend you contact Metro works
technical support and for questions
about more OSL DTF at apple com thanks
very much for your attention yes
you