TEACH OPENLOOK                                      Tom Khabaza Dec 1990
                                         Updated: Adrian Howard Mar 1992
                                        Updated: Julian Clinton Nov 1993

This teach file contains a tutorial to demonstrate the creation and
manipulation of some widgets from the Open Look widget set. The tutorial
in this file can also be found in the Poplog User Guide, in the X
Windows chapter.

In order to run this tutorial, you must be running Poplog on a system
which provides the "Xol" (or "OLIT") widget set libraries, and be
running an X Window Server which has OpenLook fonts. See HELP *OPENLOOK
for details of the Poplog OpenLook widget set interface.

This teach file is essentially the same as the tutorial found in
TEACH *MOTIF.

The complete source code for this tutorial is in LIB *XolTutorial


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

 -- The Task
 -- The Application Code
 -- Loading The X-related Libraries
 -- Loading Open Look Widget Classes
 -- Initialising the Open Look Widget Set
 -- Creating a base window
 -- Creating Widgets
 -- The Role Of "Callback Routines"
 -- Updating The Resources
 -- Adding Callbacks To The Widgets
 -- Initialising The Program
 -- Updating The Slider From A Program
 -- Destroying The Widgets


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

We want to be able to examine, display and modify the (integer) values
of a collection of variables. We are going to build a simple control
panel using the X Toolkit Intrinsics and the Open Look widget set to
perform this task.

The panel contains a single button and a "slider".  The button is
labelled with a variable name and its value. The slider displays the
same value, and can also be used to set it using the mouse.  Pressing
the button changes the variable displayed; repeated pressing cycles
through the complete set of variables.


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

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

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

In order to "cycle" through the variables stored in variable_list,
another global variable stores the current position in the list,
initially set to the beginning.  Another holds the name of the variable
currently selected:

    vars    current_variable,
            current_variable_list = variable_list;

We then provide a routine for selecting the current variable.  If
current_variable_list is null, then we should "wrap round", starting
again at the beginning of variable_list.  The head of
current_variable_list becomes the current variable, and the tail becomes
the new current_variable_list (for use next time).

    define new_current_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-related Libraries ------------------------------------

A number of X-related libraries are needed for this application.  First
we load the general library for making other X-related libraries
available:

    uses popxlib;

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

    uses xt_widget;
    uses xt_callback;
    uses xt_event;

Finally, we will define a set of general X-related constants and
structures provided in Poplog to make coercion between X types and
Poplog types more convenient. Here we will compile them using
* loadinclude:

    loadinclude xpt_coretypes;


-- Loading Open Look Widget Classes -----------------------------------

Widget classes are accessed through a number of constants defined in
autoloadable libraries. Individual widget classes are accessed through
these constants --- thus the Open Look Slider widget class can be made
accessible by:

    uses xolSliderWidget;   ;;; defines xolSliderWidget constant
    xolSliderWidget =>
    ** <Xol WidgetClass SliderWidget>

(Notice that we refer to this class as the "SliderWidget" class rather
than just the "Slider" class, since we may at other times need to access
a "SliderGadget" class.)

The following code thus loads the BaseWindowShellWidget,
ControlAreaWidget, OblongButtonWidget and SliderWidget classes:

    exload_batch;
    uses
        xolBaseWindowShellWidget,
        xolControlAreaWidget,
        xolOblongButtonWidget,
        xolSliderWidget;
    endexload_batch;

This is surrounded by the brackets "exload_batch ... endexload_batch" to
ensure that then external loading of procedures and data-structures
takes place in a single "batch", that is in a single call to the linker,
for increased efficiency.

Open Look also has a number of constants which are typically used as
attribute specifiers (e.g. -OL_HORIZONTAL-, -OL_VERTICAL-). These are
defined in an include file, "XolConstants.ph". In this example, we will
compile the file rather than simply including it as it would be in a
source file:

    loadinclude XolConstants;    ;;; rather than: include XolConstants


-- Initialising the Open Look Widget Set ------------------------------

Before creating any Open Look widgets, we must perform various
initialisations. The Poplog X Toolkit must be initialised, as must the
Open Look widget set (See REF *SYSTEM and HELP *OPENLOOK for details of
Poplog startup under X). In addition we must create a connection with
the X server, and create an initial application context.  These
functions are performed by the single call:

    XptDefaultSetup();


-- Creating a base window ---------------------------------------------

We now create an Open Look "base window" (in X Toolkit terminology, a
top-level application shell) in which to place other widgets.  The base
window is created by the standard Intrinsics routine -XtAppCreateShell-,
which 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.

An "ArgList" is an X Toolkit structure containing a set of attribute
value pairs; in this case the attributes are "resources", that is slots
in a widget used to hold its attributes. ArgLists may be used to specify
various properties of a widget or an application shell, for example at
the point where it is created.

    vars basewin =  XtAppCreateShell('panel', 'Demo',
                        xolBaseWindowShellWidget,
                        XptDefaultDisplay,
                        [{allowShellResize ^false}]);

The application name is used as the title for the window created; the
application class name is used by the X resource database manager, but
we can ignore it here. In this example, the ArgList is used to set the
base window's allowShellResize resource to the boolean -false-,
indicating that this window should not respond to resize requests from
its children.


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

We now create three widgets, one from each of the classes loaded
previously.  To create a widget we must supply a name (a string), the
class of the widget, the widget's "parent" and an ArgList specifying the
widget's initial resources.  Widgets are arranged hierarchically, with
every widget (except for top-level shells) having a "parent" widget.
Normally each widget, when displayed, will be visually "inside" its
parent widget (where the parent is visible).

Of the widget classes loaded above, the "control area" class needs some
explanation.  A control area is an Xol widget used to contain and manage
other widgets.  Widgets placed within a control area are automatically
arranged in a sensible default arrangement with sufficient space and no
overlap.  By default, resizing actions are performed when necessary; in
the present example this will have no effect because resizing has been
disabled in the parent window.

The Poplog X Toolkit routine -XtCreateManagedWidget- is used to create
the widgets.  -XtCreateManagedWidget- corresponds to the standard X
Toolkit routine used to create widgets that respond appropriately to
management from their parent and window manager:

    vars
        control =   XtCreateManagedWidget('mycontrol',
                        xolControlAreaWidget,
                        basewin, []),
        button =    XtCreateManagedWidget('mybutton',
                        xolOblongButtonWidget,
                        control, []),
        slider =    XtCreateManagedWidget('myslider',
                        xolSliderWidget,
                        control,
                        [{width 100} {orientation ^OL_HORIZONTAL}
                            {sliderMax 50} ]);

The control area is created with the base window as its parent; the
button and slider both have the control area as their parent.  Thus the
control area is used to manage to two visible widgets.  The control area
and button are both created with empty ArgLists; that is they have no
special resource settings.  The slider is given three non-default
resource settings: a width of 100 pixels, a horizontal orientation and a
maximum value of 50.  The value of the constant -OL_HORIZONTAL- is
provided by the Open Look widget set library loaded by -XptWidgetSet-
(supplying -OL_VERTICAL- would specify a vertical orientation.)


-- The Role Of "Callback Routines" ------------------------------------

Having created the widgets for our control panel, we now specify how
they should behave.  This is achieved by supplying the widgets with
"callbacks", that is procedures to be called when the widget is
manipulated with the mouse.  Each callback has a name indicating the
circumstances under which the procedure is called.  For example, the
"select" callback is called when the associated widget is selected with
the mouse, and the "sliderMoved" callback is called when the associated
slider is moved using the mouse.


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

The callback routines attached to both widgets will change the display
by updating the resources of the widgets.  The resources to be updated
are the label of the button and the value of the slider.  The former is
always set to the current variable name concatenated with its value and
separated by a space.  The latter is always set to the value of the
current variable.  Separate routines provide these two functions:

    uses xpt_coretypes;

    define set_button_label;
        current_variable sys_>< ' ' sys_>< valof(current_variable)
            -> XptValue(button, XtN label, TYPESPEC(:XptString));

        XptAppTryEvents(XptDefaultAppContext); ;;; flush any output
    enddefine;

    define set_slider_value;
        valof(current_variable) -> XptValue(slider, XtN sliderValue);
    enddefine;

Resources are updated using the Poplog X Toolkit routine -XptValue-.
This takes a widget, a resource name and an (optional) coercion type. If
the coercion type is not provided, it defaults to "int" - the correct
type in -set_slider_value-. However, in -set_button_label- we are
dealing with a string, so the "TYPESPEC(:XptString)" coercion argument
is used. The resource name argument should be a string. The -XtN-
convenience macro is used here to return a string, so repeated use of
the same resource name will not create a new string each time.

Note the call to -XptAppTryEvents- in -set_button_label-. This is used
to flush the output queue of the X server connection, causing changes to
the buttons label to become visible immediately.


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

Every callback routine is called with three arguments: the widget whose
callback is being invoked, the "client data" supplied when the callback
is added to the widget, and the "call data" indicating the event which
caused the callback.  In the case of the button's callback, none of this
data is used; we simply call -new_current_variable- to move on to the
next variable, -set_button_label- to give the button its new label, and
-set_slider_value- to set the slider value to the value of the new
variable:

    define switch_variable_callback(widget, clientdata, calldata);
        lvars widget clientdata calldata;
        new_current_variable();
        set_button_label();
        set_slider_value();
    enddefine;

This is attached to the select callback of the button using the X
Toolkit routine -XtAddCallback-:

    XtAddCallback(button, 'select', switch_variable_callback,
        'Data for button select callback');

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 used by the callback routines, and
is therefore effectively a dummy argument.

The second callback procedure, -set_variable_callback-, is used to set
the value of the selected variable when the slider is moved.  Here, the
call data argument is used to access the new value given by the position
of the slider.  This argument contains a Poplog "external pointer"
object, pointing to a machine integer.  This integer is extracted and
coerced into a Poplog integer using the "exacc :int" construct (see
REF *EXTERNAL); the result is assigned to the current variable and the
button label is set appropriately.

    define set_variable_callback(widget, clientdata, calldata);
        lvars setting widget clientdata calldata;
        exacc :int calldata -> valof(current_variable);
        set_button_label();
    enddefine;

This is attached to the slider's sliderMoved callback thus:

    XtAddCallback(slider, 'sliderMoved', set_variable_callback,
        'Data for slider sliderMoved callback');


-- Initialising The Program -------------------------------------------

We now "realise" the widgets, making them visible on the screen:

    XtRealizeWidget(basewin);

Finally, we set up the current variable and set the button's label:

    new_current_variable();
    set_button_label();

The control panel now appears on the display and responds to mouse
events in the manner that we have specified.


-- Updating The Slider 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 slider gets updated at the
same time.

    define update_slider(x);
        lvars x;
        x -> valof(current_variable);
        set_button_label();
        set_slider_value();
    enddefine;

Then try:

    update_slider(45);


-- Destroying The Widgets ---------------------------------------------

The control panel continues to exist as long as the variables basewin,
control, slider and button contain the widgets that comprise it.  If
these objects are discarded, by assigning another value to the variables
thus

    false ->> basewin ->> control ->> slider -> button;

then the widgets will be destroyed at the next garbage-collection.
Alternatively, the widgets can be explicitly destroyed using
-XtDestroyWidget- thus:

    XtDestroyWidget(basewin);

Destroying a widget will cause all its children to be destroy as well,
thus we only need to call -XtDestroyWidget- once with the top level
widget.



--- C.x/x/pop/teach/openlook
--- Copyright University of Sussex 1990. All rights reserved. ----------
