TEACH ATHENA                                    Andreas Schoter Jul 1991
                                         Updated: Adrian Howard Mar 1992

This teach files contains a worked example showing how to create a
simple control panel from some of the widgets in the Athena widget set.
For full details of the Athena widget set see REF *ATHENA.

In order to run this tutorial you must be running POPLOG on a system
which provides the "Xaw" (Athena) widget set libraries and be running an
X Window Server which is capable of supporting Athena applications.  If
you are unsure about this consult your system manager.

This teach files follows the same format and provides an essentially
similar demonstration program to those in TEACH *MOTIF and *OPENLOOK.

The complete source code for this tutorial is in LIB *XawTutorial.


         CONTENTS - (Use <ENTER> g to access required sections)

 -- The Task
 -- The Application Code
 -- Loading The X Libraries
 -- Loading Athena Widget Classes
 -- Initialising The Toolkit
 -- Creating A Top-Level Window
 -- Creating Widgets
 -- Using Callbacks
 -- Updating The Resources
 -- Adding Callbacks To Widgets
 -- Initialising The Demonstration
 -- Updating The ScrollBar From A Program
 -- Further Documentation


-- The Task -----------------------------------------------------------

We want to be able to generate an application to graphically display and
control the integer values of a set of variables.  To do this we shall
build a simple control panel using the POPLOG X Toolkit and the Athena
widget set.

The control panel contains a scale on which the value of the current
variable will be displayed (and which can be used to modify the
variable's value) and two buttons.  One button will be used to switch
between variables and will display the name of the current variable and
its value.  Repeatedly clicking on this button will cycle through the
list of variables.  The second button will be a 'QUIT' button for
terminating the demonstration.


-- The Application Code -----------------------------------------------

The names of the variables to be manipulated by the control panel are
stored in a list in the global variable -variable_list-, and the
variables themselves are also global, initialised to zero:

    vars    variable_list = [a b c d e],
             a=0, b=0, c=0, d=0, e=0;

To enable cycling through the variable list we use another global
variable to store the current position in the list, and another to store
those variables left for cycling through.  At the outset the current
variable is initialised to the first variable in -variable_list-, and
the list of variables remaining is initialised to the tail of
-variable_list-:

    vars    current_variable = hd(variable_list),
             current_variable_list = tl(variable_list);

When we get to the end of the list we must wrap around to the beginning
of the list and start again.  Selecting the next variable to display is
handled by the following procedure:

    define get_next_variable();
         if current_variable_list == [] then
             variable_list -> current_variable_list;
         endif;
         hd(current_variable_list) -> current_variable;
         tl(current_variable_list) -> current_variable_list;
    enddefine;


-- Loading The X Libraries --------------------------------------------

It is necessary to load several X libraries to run this application.
First we must load the general library that makes the other X libraries
available:

    uses popxlib;

We also require the libraries for widget handling, callback manipulation
and widget information:

    uses xt_widget;
    uses xt_callback;
    uses xt_widgetinfo;


-- Loading Athena Widget Classes --------------------------------------

Widget classes are accessed through the XptWidgetSet table of widget
sets. The expression

    XptWidgetSet("Athena")

refers to the property table representing the Athena widget set.
Individual widget classes are accessed through this table.  Thus to
access the Athena Scrollbar widget class we would use:

    XptWidgetSet("Athena")("ScrollbarWidget")

Note that we must refer to "ScrollbarWidget" and not just "Scrollbar"
because at other time we might need to refer to "ScrollbarObject"
classes.

-XptWidgetSet- will automatically load the general purpose parts of the
Athena widget set plus any facilities that are necessary for handling
Scrollbar widgets.  This is handled by a dependency tree which lists
what each widget class requires.

In addition to the Athena widgets we will also need a widget from the X
Toolkit widget set.  This is the ApplicationShellWidget which forms the
top-level window for our application.  The following code loads all the
widget classes we require for our control panel:

    exload_batch;

         ;;; load Widget Set
         constant AWS = XptWidgetSet("Athena");

         ;;; load required Widget Classes
         constant
             AppShell=XptWidgetSet("Toolkit")("ApplicationShellWidget"),
             Box = AWS("BoxWidget"),
             ScrollBar = AWS("ScrollbarWidget"),
             Command = AWS("CommandWidget");

    endexload_batch;

First it loads the general Athena widget set table, then loads the
ApplicationShellWidget from the Toolkit, then accesses the Athena
BoxWidget, ScrollbarWidget and CommandWidget through the Athena table.

Note that these commands are bracketed inside "exload_batch" and
"endexload_batch".  This ensures that the external loading of procedures
and data-structures takes place in a single call to the linker.  This
increases efficiency.


-- Initialising The Toolkit -------------------------------------------

Before creating any widgets we must ensure that various initialisations
have been performed: the POPLOG X Toolkit must be initialised and there
must be a connection with the X Server. These functions are performed by
the call:

    XptDefaultSetup();

Note that if you started POPLOG with the "%x" flag then this procedure
will have been automatically called for you.  (See REF *SYSXSETUP for
details).


-- Creating A Top-Level Window ----------------------------------------

We now create a top-level application shell which will be used to hold
the other widgets that we require.  This is the application shell and it
is created by a call to the Toolkit procedure -XtAppCreateShell-. This
procedure takes the application name, the application class name, the
application shell widget class, the display on which the shell is to
appear, and an ArgList.

    vars shell = XtAppCreateShell('demo', 'Demo', AppShell,
                     XptDefaultDisplay, []);

The first two arguments (the application name and class) are passed as
strings: the first will be used as the title of the window created, and
the second (which we can ignore here) is used by the X resource manager.
The third argument is the application shell widget class which we loaded
above. The variable passed as the fourth (display) argument is
automatically initialised by the setup procedure -XptDefaultSetup- (see
REF *XptDefaultDisplay.) The fifth argument is the ArgList: this is a
list of attribute-value pairs which are used to specify certain
parameters ("resources") for the widget. In the case of the -shell-
widget we do not need to set any resources, so the ArgList is empty.


-- Creating Widgets ---------------------------------------------------

We can now create the widgets that we need to build the control panel.
This is done by using the Toolkit procedure -XtCreateManagedWidget-
which takes four arguments: a name for the widget, the class of the
widget, the widget's parent, and an ArgList specifying any initial
values for the widget's resources.  In the case of our control panel we
shall first create an instance of the BoxWidget. This is a special
widget used to contain other widgets.

    vars box = XtCreateManagedWidget('box', Box, shell,
                 [{orientation ^XtOrientHorizontal}]);

Here we see the widget's name is the string 'box' and its class is
the previously loaded BoxWidget.  Widgets are organised hierarchically,
and every widget (except for top-level shells) must have a parent widget
(in this instance the parent of the BoxWidget is the top-level shell).
Each widget, when displayed will appear inside its parent widget, thus
we shall use the BoxWidget as the parent of the rest of the widgets in
the application.

The -box- widget has also set its "orientation" resource to
-XtOrientHorizontal- in its ArgList. This means that the children of the
widget will be laid out horizontally. Specifying -XtOrientVertical- (the
default if the resource is not specified) would cause them to be laid
out vertically. Both -XtOrientHorizontal- and -XtOrientVertical- are
created when the widget classes were loaded with -XptWidgetSet-.

Next, we will create an instance of a CommandWidget. This will be the
button for switching between variables:

    vars varButton = XtCreateManagedWidget('varButton', Command, box,
                     [  {label 'a 0 '}
                        {resize ^false}
                        {justify ^XtJustifyLeft}
                     ]);

Its name is 'varButton' and it's parent is the previously defined -box-
widget. It sets the values of three resources in it's ArgList. The
"label" resource is set to the string 'a 0 ', this will be the initial
message displayed in the widget when it is placed on screen (the extra
space is needed for when the value of the variable has more than two
digits.) The "resize" resource is set to -false-, this stops the widget
changing size when its label is changed. Finally the "justify" resource
is set to -XtJustifyLeft- which will cause the label to be displayed on
the left edge of the widget (alternatives are -XtJustifyCenter- [the
default] and -XtJustifyRight-.)

The next widget we shall create is an instance of the ScrollbarWidget:

    vars scrollbar =
        XtCreateManagedWidget('scrollbar', ScrollBar, box,
            [{length 200}
             {minimumThumb 20}
             {orientation ^XtOrientHorizontal}
            ]);

It's name is 'scrollbar' and it's parent is also -box-. The ArgList
specifies three resources, the length of the scrollbar ("length") is set
to 200 pixels, the size of the thumb of the scrollbar ("minimumThumb")
is set to 20 pixels, and the orientation is set to be horizontal.

Finally we create the quit button which is also an instance of the
CommandWidget.

     vars quitButton =
        XtCreateManagedWidget('quitButton', Command, box,
            [{label 'QUIT'}]);

It's name is 'quitButton', it's parent is -box-, and it's "label"
resource is set to the string 'QUIT'.

The Box widget will display it's children from left to right, in the
order they were created. There are more complex widgets for controlling
layout (for example the Athena FormWidget) which give more control over
where widgets are placed.


-- Using Callbacks ----------------------------------------------------

Having created the widgets for the control panel we must specify how
they should behave.  This is done by providing the widgets with
"callback" procedures. These procedures are are invoked when you use the
mouse to interact with the widget.  For example the procedure
-switch_callback- (defined below) is associated with the callback list
of the -varButton- CommandWidget --- this procedure (and any other
procedures on that widget's callback list) is called when the mouse is
clicked in that widget.  (See REF *XT_CALLBACK for more details on
callbacks in POPLOG).


-- Updating The Resources ---------------------------------------------

The callback routines attached to the ScrollbarWidget and the variable
selecting CommandWidget will change the display by updating the
resources of those widgets.  The resource to be updated for the
Scrollbar is "topOfThumb" which specifies the position of the slider in
terms of a float (0 =< X <= 1) which represents how far along the
scrollbar the slider thumb is.  The resource to be updated for the
variable selecting button is "label" which contains a string to be
displayed in the button.  Two procedures are provided to handle this
task:

     define update_thumb();
         (valof(current_variable) / 50.0)
             -> XptValue(scrollbar,XtN topOfThumb, "float");
     enddefine;

     define update_label();
         current_variable sys_>< ' ' sys_>< valof(current_variable)
             -> XptValue(varButton, XtN label, TYPESPEC(:XptString));
     enddefine;

Resources of widgets are accessed via the procedure -XptValue- which
takes a widget (whose resource is to be accessed), the name of the
resource to be accessed, and an option coercion specification.  If no
coercion type is specified the default is "int".  However, in the first
case we must specify "float", and in the second we are dealing with a
string and must create the correct typespec as shown.  Note that the
resource name is specified using the macro -XtN- which creates a fixed
address string.  This ensures that every time you refer to "XtN label"
the string passed as the resource name (i.e. 'label') is always the same
string (not merely a similar string).  You should also do this when
specifying strings a values for resources (i.e. as the radioData for a
ToggleWidget) as some resources do string comparison simply by comparing
pointer values.  It also improves efficiency as a new string is not
created every time.

The first procedure uses the value of the -current_variable- to update
the position of the thumb in the scrollbar window.  Because the value of
the variable is assumed to be an integer in the range 0--50 and the
scrollbar expects a float, the conversion can be performed by simply
dividing by 50.

The second procedure builds a string from the name of the current
variable and its value and uses this to update the label resource for
the widget -varButton-. The effect of this will be to cause the string
to be displayed as the new label of the button.


-- Adding Callbacks To Widgets ----------------------------------------

Every callback procedure is called with three arguments: the name of the
widget whose callback is being invoked, the "client data" which is
specified when the procedure is added to the callback list of the
widget, and the "call data" which indicates the event which caused the
callback.

In the case of the variable switching button's callback none of these
arguments are used, we simply call -get_next_variable- to switch to the
next variable in the list, then call -update_label- and -update_thumb-
to update the label of the button and the slider of the scrollbar
respectively.  The following procedure does this:

     define switch_callback(widget, client_data, call_data);
         lvars widget, client_data, call_data;
             get_next_variable();
             update_label();
             update_thumb();
     enddefine;

It is added to the callback list of the relevant button using the X
Toolkit procedure -XtAddCallback- as shown:

     XtAddCallback(varButton, XtN callback, switch_callback, false);

The final argument of this call is the "client data" passed to the
callback routine when it is called.  This allows the callback routine to
distinguish between widgets in cases where the same callback routine has
been used for more than one widget.  However, in the present
comparatively simple case this is not required, and it is therefore
effectively a dummy argument.

The second callback used in this demonstration sets the value of the
-current_variable- when the slider thumb is moved.  Here the call data
argument of the callback procedure is used to access the position of the
thumb of the scrollbar.  The call data in this case is an external
pointer to a float and it must be accessed in the correct manner for
POPLOG to be able to make sense of it.  Note the typespec specifying
that -call_data- is of type "float" (see REF *L_TYPESPEC) and the use of
-exacc- (see REF *EXACC) to get at the information being passed in
-call_data-.  The suitable coerced value is assigned to the value of the
-current_variable- and the label of the button is updated appropriately.

     define scroll_callback(widget, client_data, call_data);
         lvars widget, client_data, call_data;
         l_typespec call_data :float;
         intof((exacc call_data) * 50) -> valof(current_variable);
         update_label();
     enddefine;

This procedure is added to the scrollbar's callback list as follows:

    XtAddCallback(scrollbar, XtN jumpProc, scroll_callback, false);

where jumpProc is the name of the callback list used by the
ScrollbarWidget for notifying of changes in the thumb position caused by
the second mouse button being pressed.

The final callback used in this demonstration is used to terminate the
demonstration.  This is handled by the following procedure:

    define exit_callback(widget, client_data, call_data);
        lvars widget, client_data, call_data;
        XtDestroyWidget(shell);
    enddefine;

The call to -XtDestroyWidget- will destroy the -shell- widget. When a
widget it destroyed, all it's descendents are also destroyed, therefore
destroying the top-level widget is sufficient to destroy all the widgets
in the application.

    XtAddCallback(quitButton, XtN callback, exit_callback, false);


-- Initialising The Demonstration -------------------------------------

We can now "realize" the widgets to make them visible on the screen:

    XtRealizeWidget(shell);

The control panel will now appear on the screen and will respond to
mouse events.  Clicking on the first button will cycle through the
variables displaying the value of each in turn, clicking button 2 on the
mouse in the scroll bar will alter the value of the current variable,
and clicking the mouse in the QUIT button will terminate the
demonstration program.


-- Updating The ScrollBar From A Program ------------------------------

The appearance of the widgets can be altered directly from POPLOG. For
example, the following procedure could be used to update the value of
-current_variable-, and to ensure that the scrollbar gets updated at the
same time.

    define update_var(x);
        lvars x;
        x -> valof(current_variable);
        update_label();
        update_thumb();
    enddefine;

Then try:

    update_var(45);


-- Further Documentation ----------------------------------------------

REF *ATHENA --- Full details of the Athena widget set



--- C.x/x/pop/teach/athena
--- Copyright University of Sussex 1991. All rights reserved. ----------
