Chapter 4 Using Activities in ADL

4.5 Creating ADL Classes That Manage Activities

When application authors create their own classes, they often want these classes to manage activities. They can do it in two ways: design the new class so that it inherits from a class that already manages activities, or have the new class inherit from a wrapped class called ActivityManager that provides the general activity management mechanism to the new class. The first case applies when the base class you inherit from already manages the activity of interest or when you want to add a new activity to the base class. The second case gives you the flexibility to define an entirely new class that manages its own activities. We explore each of these cases below.

4.5.1 Inheriting An Existing Activity From a Class That Manages Activities

The first situation is the creation of a class that inherits from an existing class, such as XFtop, XFbutton, XFlabel or any of the other wrapped classes that have built-in activity management. In this case, the author does not need to do anything special. The inheritance mechanism in AM2 automatically gives the new class all the activity management capabilities of the class it inherits from.

Consider creating a subclass of the XFtop shell widget, which has the property that it always displays the (x,y) coordinates of the mouse in its upper left corner. This type of widget might be useful as a building block in a still image editor that allows the user to crop and scale photographs. The ADL class named LocationTop shown in Figure 4.8 accomplishes this.
A Class Inheriting from XFtop Class
class LocationTop:XFtop

{

XFlabel reportLabel {x=0; y=0; height=50; width=250;

recomputeSize=FALSE; alignment='left; label="";};

MouseNro {'Create, 'MouseDown, self, 'MouseLocation,

""} => downNro;

MouseNro {'Create, 'MouseUp, self, 'MouseLocation,

""} => upNro;

MouseNro {'Create, 'MouseMove, self, 'MouseLocation,

""} => moveNro;

upon Construct

{

{'Subscribe, &downNro} => self;

{'Subscribe, &upNro} => self;

{'Subscribe, &moveNro} => self;

{'Subscribe, &downNro} => reportLabel;

{'Subscribe, &upNro} => reportLabel;

{'Subscribe, &moveNro} => reportLabel;

}

on MouseLocation:any clientData,integer xval,integer yval

{

reportLabel.label = "x="+toString(xval) &

"y="+toString(yval);

}

};

This new ADL class inherits all the activity management capabilities of the XFtop wrapped class. Thus, it accepts Subscribe and Unsubscribe messages and sends messages to any subscribed target object when an event triggers an activity. All of these properties are the result of AM2's inheritance capabilities.

The NROs in this class subscribe to both self (the handle to the object itself) and the instance of XFlabel. This is necessary because the XFlabel object can be thought of as though it were "on top" of the instance of LocationTop, to which it belongs. Any mouse-related events on the label trigger the activities of the label. They are not passed through to the underlying XFtop. If lines 17 through 19 were removed from the class declaration, the label would act as a "hole" in the widget; events may trigger the activities of the label, but there are no registered NROs and the activities of the label would therefore have no effect.

4.5.2 Creating a New Activity

Note: Many AthenaMuse 2 users will not need to create classes that manage activities. This section can be skipped without any loss of continuity in the presentation.

In some situations, an application developer using ADL needs to add a new activity to a class that inherits activity management from a base class. Consider a class that displays a color palette. We would like such a class to have an activity called ColorSelected that provides the name of the chosen color in the message sent when an event triggers the activity. Since our color palette class inherits from the AM2 wrapped class XFlayout, which already manages activities, our new class automatically has the capability to manage any activity. We need only add the new activity and define what should be done when an event triggers the activity.

Figure 4.9 shows an implementation of the ColorPalette class. This ADL code is a simplified version of a more general color palette provided in the ADL standard library. In the simplified version the color palette has exactly eight colors, the buttons for these colors are fixed in size, and the layout of the eight colored buttons in the palette is horizontal. All of these restrictions are relaxed in the more complete version.
Example of Class With Added Activity
uses "nro.adl"@"StdLib";

class ColorPalette: XFlayout

{

/* This list provides the colors in the palette. */

list colorList = {'white, 'black,'red, 'blue, 'green,

'yellow, 'tan, 'gray};

list buttonArray<integer>;

list ActivityInfo = {{"ColorSelected", {"Color"}}};

upon Construct

{

height=30; width=240; borderWidth=1;

}

/* This method creates the buttons and the color patch */

on Init

{

integer count=0; handle hButton, hNro;

while (count < 8) {

hButton = new {'Create, self} =>XFbutton {height=30;

width=30; label=""; recomputeSize=FALSE;};

hButton->x = count*30;

count = count+1;

hButton->background = at(count, colorList);

hNro = new {'Create, 'Pressed, self, 'ColorChosen,

at (count, colorList)} => vanillaNro;

{'Subscribe, hNro} => hButton;

buttonArray[count] = {hButton, hNro};

}

}

/* This method deletes all allocated instances */

on Destroy

{

integer count=1;

while (count <= 8) {

delete at( 1, buttonArray[count]);

delete at( 2, buttonArray[count]);

count = count+1;

}

}

/*This method handles callback when button is pressed */

on ColorChosen: string cdata

{

{'TriggerNotification, 'ColorSelected, {cdata} }=>self;

}

}; /* end of class ColorPalette */

The ColorPalette class inherits from the XFlayout class, thereby inheriting its members, methods and activity management capabilities of that class. The member colorList in the ColorPalette class (defined on lines 5 and 6) provides the names of the colors in the palette. The other class member, buttonArray, is an array of lists. After an instance of a ColorPalette is constructed, each element of this array is a list of two handles: a handle to a button and a handle to an NRO for that button. The index for this array is an integer between one and eight, and corresponds to the positions of the elements in the colorList.

The most significant component of this example is line 9, which causes the initialization of the member ActivityInfo. This list is inherited from the base class XFlayout. The sublist in ActivityInfo provides the information about managing additional activities; each entry in the list corresponds to a different new activity. The general form for the sublists is:

{<name of activity>, {<keyname1>,<keyname2>, ...}}

Thus, in Figure 4.9 the name of the new activity is ColorSelected, and the single key provided to any target of this activity has a single entry called Color.

The Construct method for the ColorPalette class simply sets the default height and width of the palette. The Init method for the ColorPalette class starting at line 19 does most of the work of setting up the buttons and handles in the class. It uses the new operator to create instances of buttons and NROs from the heap, and then stores the handles to these buttons and NROs as elements of the buttonArray. It also subscribes an NRO to each button, using the background color in each of the buttons as the client data in the vanillaNro instance. Thus, when a user presses any of the eight buttons, the ColorChosen method of the ColorPalette receives a message with the color of the button as the client data.

The Destroy method starting on line 36 recovers the memory allocated for buttons and NROs when an instance of a ColorPalette is created. This method automatically receives a message whenever a ColorPalette is no longer valid or when the delete operator is applied to a handle to a ColorPalette object. The implementation of this method loops through the array of buttons and deletes the buttons and their corresponding NROs.

The method named ColorChosen is the target for the message sent when a user presses any of the eight buttons. This method simply calls the method TriggerNotification, which is inherited in any class derived from a class that manages activities. It is this method that then sends messages to every NRO subscribed to the activity. For example in line 49, the message takes the form:

   {'TriggerNotification, 'ColorSelected, {cdata} } => self;
Note that the TriggerNotification method always takes two arguments: a string giving the name of the activity that was triggered and a list of values transmitted to the target. In this case, the activity name is ColorSelected, and the list of values has the name of the selected color.

Figure 4.10 illustrates how the ColorPalette class might be used. The most notable aspect of this program is that once implemented, the activitymanagement capabilities of the ColorPalette class are treated exactly the same as the corresponding capabilities of standard wrapped classes such as XFbutton and XFtext.
Example Using the ColorPalette Class
anonymous: XFtop

{

ColorPalette myPalette{height=45;};

Nro {'Create, 'ColorSelected, self, 'EchoColor, ""} =>

selectNro;

XFlabel colorLabel {x=10; y=50; height=50; width=200;

recomputeSize=FALSE; label="";};

XFbutton exitButton {y=110; x=10; height=50; width=100;

label="Exit";};

upon Construct

{

exitButton.Pressed = {'Exit, theApp};

{'Subscribe, &selectNro} => myPalette;

}

on EchoColor: any cd, list names, list values

{

colorLabel.label = "Chosen color is" & at(1, values);

}

} myApplicaton {height=200; width=350;};

4.5.3 Creating Classes That Inherit From the ActivityManager Class

Note: Many AthenaMuse 2 users will not need to create classes that manage activities. This section can be skipped without any loss of continuity in the presentation.

Another situation of interest to the ADL programmer is the need to create a class that manages activities but does not inherit from a standard wrapped class that manages activities. In this situation, the programmer inherits from an ADL class called ActivityManager which supplies the needed functionality.

The ActivityManager class is an example of a class designed specifically for inheritance purposes. It has the following functionality:

Consider a situation where we want to create a general class of movable objects. Any object inheriting from this class has the property of tracking the motion of the mouse when clicked. In addition, any object that inherits from the Movable class has an activity called Update, triggered when the user releases the mouse while moving the object. The values provided when the event triggers the Update activity are the new (x,y) coordinates of the object. This type of generic class can be combined with specific user interface classes to create specific types of movable classes, such as movable buttons, labels, or text.

Classes such as Movable are intended for use as a base class in combination with other classes. For example, a class might inherit from both the XFlabel wrapped class and the Movable class, thereby inheriting the activities, members and methods of both base classes. This type of class is often referred to as a mix-in class.

Figure 4.11 displays the ADL code that implements the Movable class. The member ActivityInfo declares the name of the new activity (Update) and the list of keys for that activity. The example uses three instances of the MouseNro class to subscribe to the MouseUp, MouseDown and MouseMove activities.

The Construct method subscribes the three NROs to the object itself. Note that the Subscribe messages in this method on lines 12 through 14 are sent to the handle derived::self. The derived keyword in ADL indicates that the message goes to the object that is at the end of the chain of inheritance (i.e. the member of the "most derived" class).

This is necessary because the MouseDown, MouseUp and MouseMove activities are not activities managed by the Movable class. Rather, they are managed by whatever class we mix-in with the Movable class. Sending the Subscribe message to derived::self ensures that the NROs are subscribed to the correct activity manager.

Lines 17 through 22 implement the method named Down that receives a message when the MouseDown activity is triggered. This method stores the location of the mouse when the button pressed.

The method Move shown in lines 24 through 29 change the (x,y) coordinates of the object when the mouse has been moved. Note again that we must use the derived keyword before the attributes x and y because they do not belong to the Movable class; they must exist in the class derived from the Movable class.
A Class Inheriting from the ActivityManager Class
/* generic class for movable objects */

class Movable: ActivityManager

{

list ActivityInfo = { {'Update, {'x, 'y}} };

MouseNro {'Create, 'MouseDown, self, 'Down, NULL} => downNro;

MouseNro {'Create, 'MouseUp, self, 'Up, NULL} => upNro;

MouseNro {'Create, 'MouseDrag, self, 'Move, NULL} => moveNro;

integer oldX, oldY; /* used to store (x,y) of mouse press */

upon Construct

{

{'Subscribe, &downNro} => derived::self;

{'Subscribe, &moveNro} => derived::self;

{'Subscribe, &upNro} => derived::self;

}

/* This method is messaged when the MouseDown activity is triggered */

on Down: any cd, integer xval, integer yval, integer button,

boolean shift, boolean command, boolean modifier

{

oldX = xval; /* store (x,y) coordinates of mouse press */

oldY = yval;

}

/* This method is messaged when the MouseMove activity is triggered */

on Move: any cd, integer xval, integer yval, integer button,

boolean shift, boolean command, boolean modifier

{

derived::x=derived::x + xval-oldX; /* update x coordinate */

derived::y=derived::y + yval-oldY; /* update y coordinate */

}

/* This method is messaged when the MouseUp activity is triggered */

on Up: any cd, integer xval, integer yval, integer button,

boolean shift, boolean command, boolean modifier

{

{'TriggerNotification, 'Update,{derived::x,derived::y}}=>self;

}

}; /* end of class Movable */

The last method, Up, triggers the Update activity notification by sending the TriggerNotification message. It provides the new (x, y) coordinates of the object as part of them message, and then disarms the object. Figure 4.12 shows a simple case using the class.
An Example Using the Movable Class
/* Create a class of Movable buttons */

class MovableLabel : XFlabel, Movable

{

}; /* end of class MovableLabel */

/* Create top window */

anonymous: XFtop

{

MovableLabel newLabel {x=20; y=20; height=40;

width=100; label='MoveMe; borderWidth=1;};

XFlabel location {x=0; y=200; height=40; width=100; };

Nro {'Create, 'Update, self, 'ChangeLocation, NULL} => updateNro;

upon Construct

{

{'Subscribe, &updateNro} => newLabel;

location.label = "x=" + toString(location.x) &

"y=" + toString(location.y);

}

on ChangeLocation: any cd, list keys, list vals

{

location.label = "x=" + toString(at(1, vals)) &

"y=" + toString(at(2,vals));

}

} myTop {width=300; height=250; title="Movable Label";};

Lines 2 through 4 declare a new class named MovableLabel that inherits from both the XFlabel wrapped class and the user-defined Movable class. The new class needs no additional methods or members since it inherits all its useful functions from it base classes.

Lines 7 through 26 declare an anonymous class derived from the XFtop wrapped class. This class has an instance of a MovableLabel and an instance of an XFlabel. The latter of these two is used to display the (x,y) coordinates of the former. The NRO named updateNro subscribes to the Update activity. This NRO causes the method ChangeLocation to receive a message when the Update activity is triggered. This method, defined in lines 21 through 25, changes the value of the label named location to indicate the new coordinates of MovableLabel.


[12] In future releases of AM2, the ActivityInfo attribute may be implemented as a common attribute, i.e. an attribute that is shared by all members of the class. The value of this attribute should therefore not be changed.
4.5.1 - Inheriting An Existing Activity From a Class That Manages Activities
4.5.2 - Creating a New Activity
4.5.3 - Creating Classes That Inherit From the ActivityManager Class

AM2 Documentation - 19 NOV 1996

Generated with Harlequin WebMaker