Documentation | Manual: Scripting1
Chapter 17: Using Scriptable Objects
In this chapter, we are going to learn how to use scriptable objects.
This program for this chapter is called Scripting
.ext.
Scripting is a sort of "remote control" for other applications. It is implemented via the messaging system: you send a scriping Message and get a reply Message from the scriptable object. (Don't know what the messaging system is? Read up on the basics.)
The reply message will normally have B_REPLY
as its what
value, although
an unsuccessful message may get a MESSAGE_NOT_UNDERSTOOD
reply instead.
The reply message will always have an Int32
error
field; if all went
well, the value will be B_OK
; otherwise it will be an error code.
Typically, if there is a value to return, it will be returned through the
reply Message's result
field; its type will depend on the scripting
message.
Properties and Commands
Each Scriptable object has one or more named properties.
There are six scripting commands. Although it is possible to define your own, the Be Book recommends sticking to these standard commands.
The what
value of the scripting Message should be one of the command
constants.
In the list below, the value in parentheses is the code corresponding to the constant.
B_COUNT_PROPERTIES (PCNT)
Gets the number of instances of a property.
B_CREATE_PROPERTY (PCRT)
Creates a new instance of a property.
B_DELETE_PROPERTY (PDEL)
Deletes an instance of a property.
B_EXECUTE_PROPERTY (PEXE)
Executes an instance of a property.
B_GET_PROPERTY (PGET)
Gets the value of an instance of a property.
B_SET_PROPERTY (PSET)
Sets of the value of an instance of a property. The
data
field of the scripting Message should contain the new value for the property.
Specifiers
A scripting Message only contains a command (and in the case of
B_SET_PROPERTY
, a new value). In order to tell the target which property
to apply the command to, you need to add a specifier to the message.
A specifier is simply another message, but instead of using Message.AddMessage, you must use Message.AddSpecifier to add it to the scripting Message.
The what
value of the specifier should contain a specifier constant, and
it should have a String field called property
that contains the property
name.
Although you can construct your own specifier Message and add it to the scripting Message, there are additional versions of Message.AddMessage that allow you to add the most common specifiers without constructing the specifier Message yourself.
B_DIRECT_SPECIFIER
The property name alone is used to specify the property.
Perl
$message->AddSpecifier(property => $property);
Python
message.AddSpecifier(property = property)
B_NAME_SPECIFIER
In addition to the name of the property, the String name
of a particular
instance of the property is added to the specifier message.
Perl
$message->AddSpecifier( property => $property, name => $name, );
Python
message.AddSpecifier( property = property, name = name, )
B_ID_SPECIFIER
In addition to the name of the property, the Int32
id
of a particular
instance of the property is added to the specifier message.
There is no AddSpecifier shortcut for this specifier.
B_INDEX_SPECIFIER
In addition to the name of the property, the Int32
index
of a particular
instance of the property is added to the specifier message. That is, this
specifier will get the index
th instance of the property.
Perl
$message->AddSpecifier( property => $property, index => $index, );
Python
message.AddSpecifier( property = property, index = index, )
B_REVERSE_INDEX_SPECIFIER
Like B_INDEX_SPECIFIER
, but gets the index
th entry counting from the
end of the list.
There is no AddSpecifier shortcut for this specifier.
B_RANGE_SPECIFIER
Gets a number of instances of a property, specified by an index
and a
range
.
Perl
$message->AddSpecifier( property => $property, index => $index, range => $range, );
Python
message.AddSpecifier( property = property, index = index, range = range, )
B_REVERSE_RANGE_SPECIFIER
Like B_RANGE_SPECIFIER
, but starts at the index
th entry from the end of
the list. Some objects count forward from this index, while others count
backward.
There is no AddSpecifier shortcut for this specifier.
The specifier stack
Usually, you can only get a Messenger for an application, and not for any of the other objects that the application may contain. In order to send scripting Messages to other objects in an application, you need to add additional specifiers to the scripting Message.
Note
In some cases, it is possible to get Messengers for other objects via the scripting system; that is, the result field of a reply Message may contain a Messenger that targets one of these objects.
Specifiers are popped off the stack in the opposite order from which they were
added. For example, the following code gets the frame (a Rect) of the
second View of the Window named MainWin
.
Perl
my $message = HaikuR1::Message->new(B_GET_PROPERTY); $message->AddSpecifier( property => 'Frame' # get the Frame ); $message->AddSpecifier( property => 'View', # of the second View index => 1, # (0-based index) ); $message->AddSpecifier( property => 'Window', # of the Window name => 'MainWin', # named 'MainWin' );
Python
message = Message->new(B_GET_PROPERTY) message.AddSpecifier( property = 'Frame' # get the Frame ) message.AddSpecifier( property = 'View', # of the second View index = 1, # (0-based index) ) message.AddSpecifier( property = 'Window', # of the Window name = 'MainWin', # named 'MainWin' )
Putting it all together
The Tracker has a scripting interface that allows you to (among other things) open a folder Window.
Perl
my $messenger = HaikuR1::Messenger->new("application/x-vnd.Be-TRAK"); my $message1 = HaikuR1::Message->new(B_EXECUTE_PROPERTY); $message1->AddRef("data", "/boot/home"); $message1->AddSpecifier(property => "Folder"); my $reply1 = $messenger->SendMessage( message => $message1, synchronous => 1, ); $reply1->PrintToStream();
Python
messenger = Messenger("application/x-vnd.Be-TRAK") message1 = Message(B_EXECUTE_PROPERTY) message1.AddRef("data", "/boot/home") message1.AddSpecifier(property = "Folder") reply1 = messenger.SendMessage( message = message1, synchronous = 1, ) reply1.PrintToStream()
You can see from the printout of the reply that it actually contains a Messenger, which targets the new Window. But we're going to ignore that in order to demonstrate how to use the specifier stack.
Tracker Windows are named after the folder they display; so the Window that
Tracker just opened will be named home
. We can use that to get the Window,
then find its first child View, then get that View's Frame.
Perl
my $message2 = HaikuR1::Message->new(B_GET_PROPERTY); $message2->AddSpecifier( property => "Frame", ); $message2->AddSpecifier( property => "View", index => 0, ); $message2->AddSpecifier( property => "Window", name => "home", ); my $reply2 = $messenger->SendMessage( message => $message2, synchronous => 1, ); my $frame = $reply2->FindRect("result"); $frame->PrintToStream();
Python
message2 = Message(B_GET_PROPERTY) message2.AddSpecifier( property = "Frame", ) message2.AddSpecifier( property = "View", index = 0, ) message2.AddSpecifier( property = "Window", name = "home", ) reply2 = messenger.SendMessage( message = message2, synchronous = 1, ) frame = reply2.FindRect("result") frame.PrintToStream()
In addition to the standard Window scripting suite, Tracker Windows have their own suite, which allows you to get and count entries in that Window.
Perl
my $message3 = HaikuR1::Message->new(B_COUNT_PROPERTIES); $message3->AddSpecifier( property => "Entry", ); $message3->AddSpecifier( property => "Poses", ); $message3->AddSpecifier( property => "Window", name => "home", ); my $reply3 = $messenger->SendMessage( message => $message3, synchronous => 1, ); $reply3->PrintToStream(); my $count = $reply3->FindInt32("result");
Python
message3 = Message(B_COUNT_PROPERTIES) message3.AddSpecifier( property = "Entry", ) message3.AddSpecifier( property = "Poses", ) message3.AddSpecifier( property = "Window", name = "home", ) reply3 = messenger.SendMessage( message = message3, synchronous = 1, ) reply3.PrintToStream() count = reply3.FindInt32("result")
Suites
Each scriptable object has a special property named Suites
. The reply to
this scripting message will contain one or more String fields under suites
.
Each class that supports scripting adds its own suite, then sends the Message
on to the base class so it can add its suite.
Thus, the first suites
string will represent the class of the target, while
the second will represent the ancestor class, the third will represent that
class's ancestor, and so forth, back to Handler.
In addition, the reply message will contain one flattened PropertyInfo
object for each suite, under messages
. Through this object, you can examine
the suites supported by the target.