Copyright (c) Hyperion Entertainment and contributors.

Difference between revisions of "BOOPSI 101"

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
m
Line 77: Line 77:
   
 
<syntaxhighlight>
 
<syntaxhighlight>
ULONG SetAttrs(APTR myobject, Tag1, Value1, ...);
+
uint32 IIntuition->SetAttrs( Object * object, Tag tag1, ... );
  +
uint32 IIntuition->SetAttrsA( Object * object, const struct TagItem * tagList );
 
 
</syntaxhighlight>
 
</syntaxhighlight>
   
  +
As an example, we can use the '''SetAttrs()''' function to reset the contents of our requester object and use it a second time to open a different requester for the user.
Because BOOPSI gadgets require some extra information about their display, they use a special version of this function, '''SetGadgetAttrs()''':
 
   
 
<syntaxhighlight>
 
<syntaxhighlight>
  +
result = IIntuition->SetAttrs( reqobj,
ULONG SetGadgetAttrs(struct Gadget *myobject, struct Window *w, struct Requester *r, Tag1, Value1, ...);
 
  +
REQ_TitleText, "Another Requesters Example",
</syntaxhighlight>
 
  +
REQ_BodyText, "This is my second requester\nOK ?",
  +
REQ_GadgetText, "_Foo|_Bar",
  +
TAG_DONE);
   
  +
result = IIntuition->IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );
Here myobject is a pointer to the BOOPSI object, '''w''' points to the gadget's window, '''r''' points to the gadget's requester, and the tag/value pairs are the attributes and their new values. The return value of '''SetAttrs()''' and '''SetGadgetAttrs()''' is class specific. In general, if the attribute change causes a visual change to some object, the '''SetAttrs()''' / '''SetGadgetAttrs()''' function should return a non-zero value, otherwise, these functions should return zero (see the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] for information on the return values for specific classes). The following is an example of how to set the current integer value and gadget ID of the gadget created in the '''NewObject()''' call above:
 
 
<syntaxhighlight>
 
IIntuition->SetGadgetAttrs(mystringgadget, mywindow, NULL,
 
STRINGA_LongVal, 75,
 
GA_ID, 2,
 
TAG_END);
 
 
</syntaxhighlight>
 
</syntaxhighlight>
   
  +
These lines of code will reset the contents of and reuse the requester object created in the chapter above.
This changes two of '''mystringgadget''' 's attributes. It changes the gadget's current integer value to 75 and it changes the gadget's ID number to 2.
 
   
  +
Not all object attributes can be set with '''SetAttrs()'''. Some classes are set up so that applications cannot change certain attributes. Whether or not a specific attribute is "settable" is class dependent. For more information about the attributes of specific classes, see the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] and the AutoDocs on each class.
Note that it is not OK to call '''SetGadgetAttrs()''' on a BOOPSI object that isn't a gadget, nor is it OK to call '''SetAttrs()''' on a BOOPSI gadget.
 
   
  +
Again there is variant of the '''SetAttrs()''' function to be used with BOOPSI gadgets: '''SetGadgetAttrs()'''.
Not all object attributes can be set with '''SetGadgetAttrs()''' / '''SetAttrs()'''. Some classes are set up so that applications cannot change certain attributes. For example, the imagery for the knob of a proportional gadget cannot be altered after the object has been created. Whether or not a specific attribute is "settable" is class dependent. For more information about the attributes of specific classes, see the [[BOOPSI_Class_Reference|BOOPSI Class Reference]].
 
   
 
----------------------------------------------
 
----------------------------------------------

Revision as of 01:00, 26 May 2013

In this section we will explore a simple example of creating, interacting with and disposing a simple BOOPSI object - a requester window. With these simple objects you can see the basic operations that are common to almost all BOOPSI objects. When using BOOPSI gadget objects there may be some changes in syntax (f.e., using SetGadgetAttrs() instead of SetAttrs()), but the concepts remain the same.

Creating an Object

The Intuition functions NewObject() / NewObjectA() create a BOOPSI object using either "stack" of tags or predefined a tag list and returns a pointer to a new BOOPSI object:

Object *object = NewObject(Class *cl, ClassID classID, Tag tag1, ...);
Object *object = NewObjectA(Class *cl, ClassID classID, const struct TagItem *tags);

As mentioned previously (see BOOPSI and Tags), the NewObject() function has a variant that uses predefined tag lists (NewObjectA()), but here we will continue with the more commonly used stack based variant.

In general, BOOPSI objects are "black boxes". This means the inner workings of BOOPSI objects are not visible to the application programmer, so the programmer does not know what goes on inside it. This really means the inner workings of these objects are none of your business. Unless otherwise documented, only use an object pointer as a handle to the object.

To create an object, NewObject() first needs to know whether we are creating an object from a public or private class. Typically any objects created from system gadgets are all public classes. In such cases, the cl argument (a pointer) would be set to NULL and classID argument set to the text name of the class (like "requesterclass" or "string.gadget"). If one wanted to create an object of a private class, the cl argument would be a pointer to that class and the classID would be ignored.

Regardless of whether the tag list is defined elsewhere or within this function call, some list of "tags" is next proivided to define the characteristics of the new object. Each "tag" consists of a pair of values - each with specifically named "attribute" and it's proposed "value". Some general examples of such tags might be:

     GA_Left,         100,
     REQ_TitleText,   "Testing...",
     WA_DragBar,      TRUE,

In the case of our requester example, we can use the NewObject{} function along with a "stack" of "tags" containing basic parameters to create a new requester object like this:

     reqobj = IIntuition->NewObject(NULL,"requester.class",
          REQ_Type,        REQTYPE_INFO,
          REQ_TitleText,   "Requesters Example",
          REQ_BodyText,    buffer,
          TAG_END);

If successful, the variable reqobj will contain a pointer to new a BOOPSI requester object. If unsuccessful, reqobj will be NULL. This is a place for a developer to apply some error trapping when an object couldn't be created - maybe our application ran out of memory? maybe some of the parameters (tags) were invalid?

Once the requester object has been created, we still won't see anything yet. Our new object is waiting in the wings for a BOOPSI message to show itself. This leads us to the next sections where we interact with the object we created.

Passing messages to an Object

To communicate with BOOPSI objects we send "BOOPSI Messages". Unlike traditional AmigaOS "Messages" used by the Exec, ARexx and for interprocess communications, BOOPSI messages are different and frequently rely on tag lists for their content. To "send" a BOOPSI Message to an existing object, we use the IDoMethod function (or its tag list based variant):

ULONG IDoMethod(Object *myobject, ULONG methodID, ...);
ULONG IDoMethodA(Object *myobject, Msg boopsimessage);

The return value is class-dependent. The first argument to both of these functions points to the object that will receive the BOOPSI message.

IDoMethod() then depends on the class and method being used for the contents of the rest of the message. Those elements may include a method name, tags, text or pointers. The class documentation will described those as well as whether the class expects any certain order of tags, etc.

For a simple example, we can ask the BOOPSI requester object reqobj (created above) to show itself by sending this message:

result = IIntuition->IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );

In this case, the RM_OPENREQ method of the Requester Class expects the elements of its orRequest structure. The class docs point us to a header file that explains we need to start with the MethodID and then pointers to a tag list, window and a screen structures. In our simple example above, we are passing no arguments, so our call has three NULL's for these elements.

When one calls IDoMethod()' with the RM_OPENREQ method on a requester object, the requester appears and the application waits for the user input. Given the simple "information" style requester we created (with default buttons), the result will simply numerically indicate which button was selected by the user.

With the IDoMethodA() variant, all of the above arguments have to be combined into a complete BOOPSI Message structure, boopsimessage. Again the layout of depends on the class and method. Every method's message starts off with a Msg (from <intuition/classusr.h>) and is followed by the class specific tags:

typedef struct {
    ULONG MethodID; /* Method-specific data may follow this field */
} *Msg;

As mentioned above, there are variants of the IDoMethod() functions for specific uses, such as the DoGadgetMethod() function. In that case, the function passes additional information pertinent to GUI gadget objects.

Setting an Object's Attributes

Every BOOPSI object is defined by a number of attributes. In the above example we defined our requester when we created the object with NewObject(). But an object's attributes are not necessarily static. An application can use the SetAttrs() function to change or set other attributes on an object:

     uint32 IIntuition->SetAttrs( Object * object, Tag tag1, ... );
     uint32 IIntuition->SetAttrsA( Object * object, const struct TagItem * tagList );

As an example, we can use the SetAttrs() function to reset the contents of our requester object and use it a second time to open a different requester for the user.

     result = IIntuition->SetAttrs( reqobj,
          REQ_TitleText,		"Another Requesters Example",
          REQ_BodyText,			"This is my second requester\nOK ?",
          REQ_GadgetText,		"_Foo|_Bar",
          TAG_DONE);
 
     result = IIntuition->IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );

These lines of code will reset the contents of and reuse the requester object created in the chapter above.

Not all object attributes can be set with SetAttrs(). Some classes are set up so that applications cannot change certain attributes. Whether or not a specific attribute is "settable" is class dependent. For more information about the attributes of specific classes, see the BOOPSI Class Reference and the AutoDocs on each class.

Again there is variant of the SetAttrs() function to be used with BOOPSI gadgets: SetGadgetAttrs().


UNDER CONSTRUCTION


Getting an Object's Attributes

The Intuition function GetAttr() asks an object what the value of a specific attribute is:

ULONG GetAttr(ULONG attrID, APTR myobject, ULONG *mydata);

where attrID is the attribute's ID number, myobject is the object to get the attribute from, and mydata points to a data area that will hold the attribute value. This function returns a 0 if the object doesn't recognize the attribute, otherwise it returns some non-zero value, the meaning of which depends on the class. In most cases, GetAttr() returns a 1 when it is successful.

Not all object attributes are obtainable using the GetAttr() function. Some classes are set up so that applications cannot query the state of certain attributes. For example, using the GA_Image attribute, an application can give a BOOPSI prop gadget (propgclass) an Image structure which the gadget uses as the imagery for its knob. This attribute is not "gettable" as there is no need for an application to have to ask the gadget for the structure that the application passed it in the first place. Whether or not a specific attribute is "gettable" is class dependent. For more information about the attributes of specific classes, see the BOOPSI Class Reference.

Disposing of an Object

When an application is done with an object it has to dispose of the object. To dispose of an object, use the Intuition function DisposeObject():

VOID DisposeObject(APTR boopsiobject);

where boopsiobject is a pointer to the BOOPSI object to be disposed. Note that some classes allow applications to connect child objects to a parent object so that when the application deletes the parent object, it automatically disposes of all of its children. Be careful not to dispose of an object that has already been disposed.


What About the BOOPSI Messages and Methods?

According to the "OOP Overview" section, for an object to perform a method, something has to pass it a BOOPSI message. The previous section discussed using Intuition functions to ask an object to do things like set and get attributes. The functions in the previous section seem to completely ignore all that material about methods and messages. What happened to the methods and messages?

Nothing; these functions don't ignore the OOP constructs, they just shield the programmer from them. Each of these functions corresponds to a BOOPSI method:

NewObject() OM_NEW
DisposeObject() OM_DISPOSE
SetAttrs()/SetGadgetAttrs() OM_SET
GetAttr() OM_GET
RefreshSetGadgetAttrs() OM_SET, GM_RENDER

These methods are defined on the rootclass level, so all BOOPSI classes inherit them. The Intuition functions that correspond to these methods take care of constructing and sending a BOOPSI message with the appropriate method ID and parameters.