TEACH PROPSHEET                                 Jonathan Meyer, Jun 1991
                                        Revised: Adrian Howard, Jun 1993


LIB * PROPSHEET is a high-level, general purpose tool for building
dialog boxes. It is designed to simplify the creation and management of
'Property Sheets' (pop-up windows containing settings.) However,
LIB * PROPSHEET is very flexible, and can be used to generate almost any
kind of dialog box with a mixture of labels and controls.


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

  1   Overview
  2   Building Sheets and Fields
      2.1   Creating New Property Sheets
      2.2   Hiding and Displaying Property Sheets
      2.3   Destroying Sheets
  3   Managing Fields
      3.1   Adding Fields
      3.2   Converters
      3.3   Accepters
      3.4   Formatters
      3.5   Field Defaults
      3.6   Field Sensitivity
  4   Linking Sheets To Variables
      4.1   Refreshing Fields From Variables
      4.2   Automated Refresh
      4.3   Refreshing on sysPOP
      4.4   Active Refresh
      4.5   Timer Refresh
  5   Advanced Programming Information
      5.1   Referring to Propsheet Objects
      5.2   Format Patterns
      5.3   Attribute List Summary
      5.4   Identifier Classes
      5.5   More About Property Boxes
      5.6   Managing Multiple Property Sheets
  6   Further Reading



-----------------------------------------------------------------------
1  Overview
-----------------------------------------------------------------------

A "Property Sheet" is a window that is used to display and edit
variables, which are displayed in "fields".

Each field has a field name and one or more user interface components
that are used to specify the value of the field. These user interface
components are things like sliders, text entry fields, option lists,
radio lists etc.

Each field has a notion of its value, a default value, and an indicator
of whether the field is sensitive to input or not. Additionally, fields
can have verification procedures so that they can check values when they
are entered, and conversion procedures that can do things like scale or
filter the field value. Current field values can be saved at any point,
and the last saved values can be restored.

Property sheets can be used anywhere where you wish to display or
control Pop-11 variables through a control panel. They are useful for
database entry forms, variable display and access, and general purpose
dialog boxes.

Because property sheets are widgets, it is possible to use all of the
standard X toolkit functions on the property sheets.



-----------------------------------------------------------------------
2  Building Sheets and Fields
-----------------------------------------------------------------------

This section introduces:

    Procedure(s)                    Description
    ------------                    -----------
    propsheet_init                  Initialising LIB * PROPSHEET
    propsheet_new_box               Creating a new top level box
    propsheet_new                   Creating a new property sheet
    propsheet_hide, propsheet_show  Making a property sheet (in)visible



2.1  Creating New Property Sheets
---------------------------------

The first stage of using LIB * PROPSHEET is to load the library and
initialise it. Calling propsheet_init will initialise the library and
start up a connection to the X server.

    uses propsheet;
    propsheet_init();

Next, we want a new 'top-level' property sheet container, called a
property box. The procedure propsheet_new_box will return such a
container. It takes several arguments, all of which can be false if the
user just wants to specify a default value.

So doing:

    vars mybox=propsheet_new_box('Properties', false, false, false);

will create a new top level property box, and assign it to mybox. Note
that this will not cause the property box to be displayed (see below.)

Each property box can contain multiple property sheets (or propsheets.)
Each property sheet can be given a title. Property sheets are tiled
vertically, and each sheet has a black rectangle drawn around it.

To create a property sheet, use:

    vars mysheet = propsheet_new('Basic Settings', mybox, false);



2.2  Hiding and Displaying Property Sheets
------------------------------------------

Before a newly created property box or sheet can be seen, it must be
"shown".

Two procedures, propsheet_hide and propsheet_show, are used to hide and
show property sheets and boxes (and fields.) All property sheets and
boxes start off in the "hidden" state, and so to display the sheet we
created above, we must do:

    propsheet_show(mysheet);
    propsheet_show(mybox);

Note that both propsheet_hide and propsheet_show can take lists, so it
might have been easier to do:

    propsheet_show([% mysheet, mybox %]);



2.3  Destroying Sheets
----------------------

The procedure propsheet_destroy can be used to destroy a property sheet,
box, or field. When you have finished this tutorial, do:

    propsheet_destroy(mybox);



-----------------------------------------------------------------------
3  Managing Fields
-----------------------------------------------------------------------

This section will introduce you to:

    Procedure                   Description
    ---------                   -----------
    propsheet_field             Creates fields
    propsheet_field_value       Accesses & updates fields
    propsheet_field_default     Accesses & updates field default values
    propsheet_field_converter   Used in automatic field data conversion
    propsheet_field_accepter    Field verification hooks
    propsheet_sensitive         Setting whether fields accept input



3.1  Adding Fields
------------------

At this stage, a blank property sheet should be visible your screen. If
the property sheet has a pushpin, it would be sensible to click the
pushpin in now.

To add fields to the property sheet, use propsheet_field. This takes a
property sheet and a special "format" argument. The format argument is
of the form:

    [ fieldname pattern (attribute, attribute...) ]

The fieldname is a word or string. The pattern describes what type of
field is to be used. The attributes are used to control other aspects of
the field, such as linked variables and default values.

To create a simple text entry field, just do:

    propsheet_field(mysheet, [Filename '']);

We can access and update the current value of the new field using the
propsheet_field_value procedure:

    propsheet_field_value(mysheet, "Filename") =>
    ** ''

This is the same as using the * class_apply for the property sheet, so
we can more simply write:

    mysheet("Filename") =>

Each field can also be accessed using a number, where the number is the
index position of the field on the sheet. So:

    mysheet(1) == mysheet("Filename") =>
    ** <true>

You can find out the index of a field by using propsheet_field_number
like this:

    propsheet_field_number(mysheet, "Filename")=>
    ** 1

We can also assign to fields:

    'hello' -> mysheet("Filename");

Note that typing into the field on the display, (and pressing return or
tab to accept the field) will cause its value to be updated.

To add more fields, call propsheet_field with other field names. Note
that propsheet_field can take a list of formats at once, allowing you to
create more than one field at a time.

The following will create an example of several of the available field
types:

    propsheet_hide(mysheet);        ;;; hide sheet while we change it
    propsheet_field(mysheet, [
        [String 'a']                ;;; a text field
        [Number 3 kilobytes]        ;;; a number field
        [Range 1-64 (default=3)]    ;;; a slider with a range 1-64
        [Switch true ]              ;;; a toggle switch
        [List  oneof [cat dog rat] (default=2)] ;;; a radio list
        [Options menuof [yes no unset] ]        ;;; a menu
        [Flags someof {bold italic}]            ;;; an option list
    ]);
    propsheet_show(mysheet);

Note that if you use the same fieldname twice, the property sheet will
destroy the current field with that name and create a new field with the
specified attributes and pattern. If you specify an empty pattern for a
field, the field will be destroyed altogether.

Each of the fields has a value that reflects its type --- so a boolean
field is either true or false. An options field is the list of options
(words or strings) that are set. A radio field will have the value the
the field that is selected (as a word or string), and so forth.

We can print out all of the current values by doing:

    vars i;
    [%for i from 1 to propsheet_length(mysheet) do
        mysheet(i)
    endfor;%] =>
    ** [hello a 3 3 <true> dog yes []]



3.2  Converters
---------------

Converters are procedures that are fired every time a program accesses
or updates a property sheet field. Converter procedures are of the form:

    convert_p(setting_value) -> value
    -> convert_p(value) -> setting_value

It is often useful to provide a converter for a field --- this will
convert between the value of the field as it is displayed and some other
Pop-11 value. For example, we could have a radio list with the two
options "On" and "Off", with a converter that turns "On" and "Off" into
the Pop-11 booleans true and false:

    ;;; sheet value -> pop11 value
    define onoff_convert(val);
         val == "On"
    enddefine;
    ;;;
    ;;; pop11 value -> sheet value
    define updaterof onoff_convert(val);
         if val then "On" else "Off" endif;
    enddefine;

    propsheet_field(mysheet, [
        Toggle [On Off]
        (converter = onoff_convert)
    ]);

    mysheet("Toggle") =>
    ** <true>

    false -> mysheet("Toggle")


To change the converter of an existing field, use the procedure
propsheet_field_converter:

    propsheet_field_converter(mysheet, "Toggle") =>
    ** <procedure onoff_convert>

    false -> propsheet_field_converter(mysheet, "Toggle");
    mysheet("Toggle") =>
    ** Off



3.3  Accepters
--------------

Accepters work at a lower level than converters --- an accepter is a
procedure which is fired whenever a value of a field is changed.
Accepters can be used to for example convert a string entry for a date
into a standard form, or to refuse illegal field values.

Accepters take the form:

    accept_p(propsheet, name, value) -> value_or_undef

An accepter is passed a property sheet (propsheet), a field name (name),
and the new value (value) for that field (in internal form, before
passing through any converter for that field.) The accepter should
return either:

    #  value, if it is acceptable
    #  any other valid value for that field
    #  the special item propsheet_undef to restore the previous value

Accepters are called from within a piece of callback code, and should
therefore return a valid result.

For example, the following accepter will refuse any changes:

    define refuse(sheet, field, value) -> value;
        propsheet_undef -> value;
    enddefine;

Set this for all of the fields by doing:

    vars i;
    for i from 1 to propsheet_length(mysheet) do
        refuse -> propsheet_field_accepter(mysheet, i)
    endfor;

Now try changing some field... you can't! To restore the fields, use:

    for i from 1 to propsheet_length(mysheet) do
        false -> propsheet_field_accepter(mysheet, i)
    endfor;

One additional factor to consider when writing accepters is the
(protected) propsheet_acceptreason variable. This variable is always one
of the following words:

    "activate"
        the field is being activated (eg. by the user pressing return or
        selecting it with the mouse.)

    "lose_focus"
        The value of the field was changed, and the field has lost
        the input focus.

    "slider_moved"
        A numeric range is being changed by the user manipulating the
        slider.

    "increment"
    "decrement"
        A numeric field is being changed by the user activating the
        up or down arrow buttons.

The "increment" and "decrement" reasons are especially useful if you
wish to make the arrows next to a numeric field cause the number in the
field to change by more than one. The following accepter changes the
number in 10's (don't forget to take into account the fact that the
default action for the increment and decrement buttons will change the
number by 1):

    define jump_accepter(sheet, name, value) -> value;
        lvars sheet, name, value;
        if propsheet_acceptreason == "increment" then
            value + 9
        elseif propsheet_acceptreason == "decrement" then
            value - 9
        else
            value
        endif -> value;
    enddefine;

    jump_accepter -> propsheet_field_accepter(mysheet, "Number");



3.4  Formatters
---------------

Each numeric range, number or string type field can define a
"formatter". The formatter acts as the coercion between the raw text
string and the Poplog string or number (and visa-versa.) The formatter
takes the form:

    format_p(text_string) -> value
    -> format_p(value) -> text_string

Formatters are used when you want to modify things such as pop_pr_places
and pop_pr_radix to control how a number is printed in a numeric field
or range. It can also be used if you wish to specify some other
procedure than strnumber to perform conversion between strings and
numbers.

The formatter is called whenever the value of a text-field widget's
string is set, or when it is retrieved. This is generally just before
and just after the accepter hook is called for the field:

    format_p(value) -> value;
    accepter_p(propsheet, name, value) -> value;
    value -> format_p() -> value;

The default formatter is called after the user supplied formatter
(allowing you to write a formatter procedure that does nothing.) On
numeric fields, the default formatter does:

    define formatter(value) -> value;
        unless value.isnumber then strnumber(value) -> value endunless;
    enddefine;
    ;;;
    define updaterof formatter(value) -> value;
        unless value.isstring then value sys_>< '' -> value endunless;
    enddefine;

For example, to make the "Number" field display the number in hex, you
can use:

    define hex_formatter(value) -> value;
        strnumber('16:' >< value) -> value;
    enddefine;

    define updaterof hex_formatter(value) -> value;
        lvars value;
        dlocal pop_pr_radix = 16;
        sprintf(value, '%p') -> value;
    enddefine;

    hex_formatter -> propsheet_field_formatter(mysheet, "Number");

    12 -> mysheet("Number")



3.5  Field Defaults
-------------------

Each field, when it is created, is given a default value. Clicking on
the "Set Defaults" button, or calling propsheet_set_defaults will
restore all of the fields to their default values. The default value is
the value the field displays when it is first created. Field format
patterns are often used to specify the default:

    Pattern         Class Default
    -------         -------------
    Strings         The value of the string is the default

    Numbers         The number is the default value

    Lists           The first element of the list is the default

    Numeric Ranges  The first number in the range is the default, though
                    ranges can be specified using a low-default-high
                    pattern, eg. 1-10-100


Additionally, programs can change the default value registered with any
field. The procedure propsheet_field_default lets you do this.

    propsheet_field_default(mysheet, "Range") =>
    ** 3

    'asdasd' -> propsheet_field_default(mysheet, "String");

Try pressing the "Set Defaults" button.

A field can be given a unique undef record as a special 'no default'
value. If the field default is propsheet_undef, clicking on "Set
Defaults" will have no effect --- the propsheet_set_defaults procedure
will simply skip the field.

For fields that do have a default value, it is possible to set the field
to the default value by assigning propsheet_undef to that field:

    propsheet_undef -> mysheet("String");



3.6  Field Sensitivity
----------------------

The procedure propsheet_sensitive is used to access or update whether a
field (or an option in a field) can receive input. A whole field can be
set to read only by doing:

    false -> propsheet_sensitive(mysheet, 1);

and restored using:

    true -> propsheet_sensitive(mysheet, 1);

Also, individual items of any fields containing a list of buttons or
options can be disabled. For example, the second item on the "Options"
menu can be disabled by doing:

    false -> propsheet_sensitive(mysheet, "Options", 2);



-----------------------------------------------------------------------
4  Linking Sheets To Variables
-----------------------------------------------------------------------

An important facility provided by LIB * PROPSHEET is the ability to link
fields in a property sheet with Pop-11 variables.

NOTE: Some of the following examples refer to variables using their name
(as a word) and not their * IDENT. These examples will therefore not
work with * SECTIONS, or for any other variable type than global vars.
In all cases where such names are used, idents (as returned by identof
or sys_current_ident) could have been used instead, but for simplicity
such examples have been omitted.

For example, based on the property sheet we built above:

    vars string, range, flags;
    ident string -> propsheet_field_ident(mysheet, "String");
    ident range  -> propsheet_field_ident(mysheet, "Range");
    ident flags  -> propsheet_field_ident(mysheet, "Flags");

Once the fields are linked, then property sheet "Apply" button on the
bottom of the property box comes into effect. The "Apply" button calls
the procedure propsheet_apply, which flushes all of the current values
of the property sheet fields (via any specified converter procedures)
into linked variables.

Enter some values in the fields, select the "Apply" button, and then do:

    [% string, range, flags %] =>

You should get a list like:

    ** [hello 1 [bold]]

What about the "Reset" button? The "Reset" button by default calls
propsheet_reset, which sets the displays of all of the fields to the
values they had when propsheet_save was last called for those fields.
Since by default selecting "Apply" will call propsheet_save after the
apply, "Reset" will normally restore fields to the value they had on the
last "Apply."



4.1  Refreshing Fields From Variables
-------------------------------------

Changes can go in the other direction as well --- ie. changes to
variables can be flushed out to the property sheet, updating the fields
in the sheet. There are several different ways that this can be
achieved. The easiest method is the "manual" one --- every time you want
the value of a variable to be flushed out to the property sheet, call
propsheet_refresh:

    'hello' -> string; propsheet_refresh(ident string);

Note that propsheet_refresh can take a variety of other arguments:

    ;;; refresh a field
    propsheet_refresh(mysheet, "Range");

    ;;; refresh a whole sheet
    propsheet_refresh(mysheet);

    ;;; refresh a whole property box
    propsheet_refresh(mybox);

    ;;; refresh list of things
    propsheet_refresh([^mysheet ^mybox]);

Each call to propsheet_refresh resets the indicated fields to show the
current value of a linked variable, or the default value if there is no
linked variable.



4.2  Automated Refresh
----------------------

As well as the manual refresh described above, there are also three
forms of automated refresh:

    sysPOP  Code is planted by sysPOP to refresh fields
    active  Fields are refreshed from updater calls on active variables
    timer   Fields are refreshed from variables once a second

Each of these is accessed by first setting the "ident class" of a
variable, using propsheet_ident_class. Setting the ident class of a
variable can be done at compile time, before any propsheets have been
built.



4.3  Refreshing on sysPOP
-------------------------

    "sysPOP" -> propsheet_ident_class(wident);

The automatic "sysPOP" refresh class will modify the VM sysPOP
instruction so that it plants additional code to refresh any fields
associated with variables that have assignments to them. This is an
effective way of monitoring every change to a variable. The drawback is
that the monitoring will only take place for code that is compiled after
the assignment to the ident class of the variable, so it is not an
effective way of keeping track of a variable if you already have
compiled code that assigns things to the variable.

For example, do:

    "sysPOP" -> propsheet_ident_class(ident string);

Now, every time a sysPOP encounters an assignment to the variable string
it will plant code to update the field associated with string. So the
following will automatically set string:

    'ab' -> string;



4.4  Active Refresh
-------------------

    "active" -> propsheet_ident_class(wident);

The automatic "active" refresh class works by building a closure out of
an active variable's update procedure that first calls the update
procedure and then refreshes the field associated with the variable. So
the following example will define an active variable switch, which is
tied to the "Switch" field in our property sheet.

    lvars current_val = false;
    define active switch;
        current_val;
    enddefine;
    ;;;
    define updaterof active switch(val);
        val -> current_val;
    enddefine;

    "active" -> propsheet_ident_class(ident switch);
    "switch" -> propsheet_field_ident(mysheet, "Switch");

    false -> switch;
    true -> switch;



4.5  Timer Refresh
------------------

    "timer" -> propsheet_ident_class(wident);

As a last resort, the automatic timer refresh class is used to perform
automatic updating of variables that are not active and already have
code compiled that assigns to them (for example, system variables.) The
timer refresh works by scanning a list of identifiers once a second and
refreshing all of the identifiers that have changed since the last scan.

For example, the following will start monitoring the popgcratio variable
in the "Range" field that we created.

    "timer" -> propsheet_ident_class("popgcratio");
    "popgcratio" -> propsheet_field_ident(mysheet, "Range");

    ;;; get initial value
    propsheet_refresh("popgcratio");

    10 -> popgcratio;
    30 -> popgcratio;

    "manual" -> propsheet_ident_class("popgcratio");

The following example shows how to track memory usage in a numeric
field:

    "timer" -> propsheet_ident_class("popmemused");
    "popmemused" -> propsheet_field_ident(mysheet, "Number");
    propsheet_refresh("popmemused");    ;;; to get initial value



-----------------------------------------------------------------------
5  Advanced Programming Information
-----------------------------------------------------------------------



5.1  Referring to Propsheet Objects
-----------------------------------

Most of the procedures provided by LIB * PROPSHEET can take several
kinds of arguments:

    Argument    Description
    --------    -----------
    propsheet   A property sheet
    propbox     A box of property sheets
    propfield   A property sheet field
    propitem    A single element of a field (eg a menu button)

For procedures that take a propfield, this field can either be referred
to by passing its propsheet and its name (as a word or string), or by
passing its propsheet and a numeric index (the location of the field on
the sheet.)

Similarly, a propitem is either the name of a button or option on a
list, or the index number of the item.



5.2  Format Patterns
--------------------

The format part of the pattern specifies how the field is to be
displayed.

    'string'
            A Text field. The string's value specifies the default.

    10
    10 Units
            A numeric field. The value specifies the default. Can
            contain reals. Displayed with an increment and a decrement
            button.

    true
    false
            A check-box. The value specifies which boolean is the
            default

    1 - 10
    1 - 5 - 10
    1 - 10 Units
    1 - 5 - 10 Units
            A numeric Range. The first value is the default, or the
            middle value if three numbers are specified.

    [a b c]
    [a b c] [d e f]
    oneof [a b c]
    oneof {a b c}
            An exclusive list of possible values, the first is the
            default. Exactly one the the items "a", "b" or "c" is
            selected. The items "a", "b", and "c" can be words or
            strings. The allowNone attribute can be specified to allow
            zero elements to be selected. The value returned by this
            field when zero elements are selected is false. Multiple
            rows can be specified by passing several lists one after the
            other. If the "oneof" specifier is used, you can pass either
            lists or vectors. Lists have the default specifier or
            "oneof".


    menuof [a b c]
    menuof [a b c] [d e f]
    menuof {a b c}
            A menu list. As with the exclusives, but only a single item
            is displayed at one time, next to a menu-button which allows
            the user to choose the other items. "a", "b", and "c" are
            either words or strings. The value of the field is the
            current item that is displayed next to the menu button.
            Multiple rows can be specified by passing several lists one
            after the other. Lists or vectors can be used.


    {a b c}
    {a b c} {d e f}
    someof {a b c}
    someof [a b c]
            An options list. Each of the items is like a "bit" which can
            be toggled on and off. "a", "b", and "c" are either words or
            strings. The value of the field is the list of options which
            are set on (or nil if none are set.) You can specify
            multiple rows by using several vectors one after the other.
            You can use lists or vectors if you use the "someof"
            specifier. Vectors have the default specifier of "someof".



5.3  Attribute List Summary
---------------------------

The following is the full list of attributes that can be specified to a
property sheet field:

    Attributes
    ----------
    allowNone
    nodefault
    noinput
    converter = val or # val
    accepter = val
    default = val
    ident = val or ident = val :class



5.4  Identifier Classes
-----------------------

The macro propsheet_id can be used to get the current identifier
associated with a word:

    propsheet_id string =>
    <ident ''>

Additionally, propsheet_id can be used to specify the ident class of a
variable at the same time as getting its ident:

    (propsheet_id string:sysPOP) =>
    <ident ''>

This is especially useful when you want to specify identifiers to be
linked to fields in field attribute lists --- since the ^ command in a
list expands macros at compile time, you can do things like:

    define setfields();
        propsheet_field(
            mysheet,
            [Bang true (ident= ^propsheet_id string:sysPOP)]
        );
    enddefine;

This says:

    Set the variable of the field "Bang" to be the current
    identifier associate with string, and make the class of that
    identifier "sysPOP".

This will set the propsheet_ident_class of the variable string to
"sysPOP" when the procedure is compiled. It will also correctly deal
with sections, and also allow type 3 lvars variables to be linked to
fields.



5.5  More About Property Boxes
------------------------------

Property Boxes are the containers of property sheets. When you create a
property box, you can specify its title, a parent or "owner" widget, a
list of button labels (as strings or words) that the box should have,
and a procedure to call when one of the buttons gets clicked on:

    propsheet_new_box(title, parent, button_callback, buttons)

Passing false for any of these fields will pick up its default as
follows:

    Field       Default
    -----       -------
    title       nullstring
    parent      A new xtApplicationShellWidget
    callback    (see below)
    buttons     [Apply Reset 'Set Defaults' Dismiss]

The default button callback routine matches the buttons to procedures as
follows:

    Button          Procedure
    ------          ---------
    Apply           propsheet_apply
    Reset           propsheet_reset
    Set Defaults    propsheet_set_defaults
    Dismiss         propsheet_hide

You can specify your own button callback routine. It should be a
procedure of the form:

    button_callback(propbox, button) -> accept

Where button is the string or word label of the button. accept should be
a boolean. If it is true then (if the window is unpinned) the window
will be dismissed after the button callback. Return false if there is
some error in the form that should be corrected, and so the form should
not be dismissed.

You can also take an existing widget and "import" it, ie. convert it
into a new valid property box. A property box can be any composite
widget --- each property sheet is made a direct child of the property
box.

For imported property boxes, propsheet_show does * XtManageChild and
propsheet_hide does * XtUnmanageChild.

For example, under OLIT we can create a xolControlAreaWidget and then
convert it into a property box like this:

    ;;; Load appropriate libraries
    uses popxlib;
    uses propsheet;
    uses xt_widget;
    uses xtApplicationShellWidget;
    uses Xol;
    uses xolControlAreaWidget;

    ;;; Initialise X & LIB * PROPSHEET
    XptDefaultSetup();
    propsheet_init();

    ;;; Create an application shell
    vars shell = XtAppCreateShell(
        'test', 'Test', xtApplicationShellWidget, XptDefaultDisplay, []
    );

    ;;; Create a composite widget
    vars box_widget = XtCreateManagedWidget(
        'box', xolControlAreaWidget, shell, []
    );

    ;;; Import the widget as a propbox
    vars box = propsheet_import_box(box_widget);

    ;;; Create a shell
    vars sheet = propsheet_new('Test Sheet', box, [ [Value ''] ]);

    ;;; Show the box and sheet
    propsheet_show([%sheet, box%]);

    ;;; Realize the shell on screen.
    XtRealizeWidget(shell);

In a similar way, under Motif you could import a xmPanedWindowWidget
like this:

    ;;; Load appropriate libraries
    uses popxlib;
    uses propsheet;
    uses xt_widget;
    uses xtApplicationShellWidget;
    uses Xm;
    uses xmPanedWindowWidget;

    ;;; Initialise X & LIB * PROPSHEET
    XptDefaultSetup();
    propsheet_init();

    ;;; Create an application shell
    vars shell = XtAppCreateShell(
        'test', 'Test', xtApplicationShellWidget, XptDefaultDisplay, []
    );

    ;;; Create a paned-window widget
    vars box_widget = XtCreateManagedWidget(
        'box', xmPanedWindowWidget, shell, []
    );

    ;;; Import the widget as a propbox
    vars box = propsheet_import_box(box_widget);

    ;;; Create a shell
    vars sheet = propsheet_new('Test Sheet', box, [ [Value ''] ]);

    ;;; Show the box and sheet
    propsheet_show([%sheet, box%]);

    ;;; Realize the shell on screen.
    XtRealizeWidget(shell);



5.6  Managing Multiple Property Sheets
--------------------------------------

It is often useful to be able to display more than one property sheet in
a property box --- either sequentially, so that the box is used to
present a sequence of forms, or in a tiled fashion, so that one window
contains several categories of controls. The "hide" and "show"
procedures let you do this.

One convenient way to manage multiple property sheets is with the menu
list control. The following example uses the menu list, in conjunction
with an accepter procedure, to display multiple property sheets:

    vars basic_sheet, advanced_sheet;
    define select_sheets(ps, button, val) -> val;
        lvars ps, button, val;
        if val == "Basic" then
            propsheet_show(basic_sheet);
            propsheet_hide(advanced_sheet);
        else
            propsheet_hide(basic_sheet);
            propsheet_show(advanced_sheet);
        endif;
    enddefine;

    vars exbox =
        propsheet_new_box('Calc', false, false, [Apply Dismiss]);

    vars select_sheet = propsheet_new(
        false, exbox,
        [   [Settings [[Basic] Advanced]
            (nodefault, accepter= ^select_sheets)]
        ]
    );

    vars basic_sheet = propsheet_new(
        'Basic Options', exbox,
        [ [Value ''] [Result '' (noinput)] [Action [Add Subtract]] ]
    );

    vars advanced_sheet = propsheet_new(
        'Advanced Options', exbox,
        [ [Memory [on off]] [Gang [- a b c]] [Writeable true]]
    );

    propsheet_show([%select_sheet, basic_sheet, exbox%]);



-----------------------------------------------------------------------
6  Further Reading
-----------------------------------------------------------------------

    HELP  * X               - An overview of POPLOG X facilities
    TEACH * RC_GRAPHIC      - A high-level graphics library
    TEACH * RC_GRAPHPLOT    - A graph drawing package
    REF * PROPSHEET         - Reference manual for LIB * PROPSHEET



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