Introduction

  The "attach" statement of the SymbEL language provides the user with the
  ability to extend the language into a shared library.  This capability has
  been very useful in providing access to system libraries.  It is not limited
  to system libraries, however.  The end user may develop shared libraries
  for their own purpose and create an include file with an "attach" statement
  so they can make use of new features.
  
  The purpose of the SE eXtensions package is to provide shared libraries
  that extend the language into areas that are of wide application.  Clearly,
  one such area is the Graphical User Interface (GUI).  This is the first
  extension that has been developed for SE.

A GUI Extension

  Anyone who has written code with the X Toolkit (libXt) and a Toolkit based
  Widget library (such as Athena or Motif) knows that this is dense and
  sometimes difficult to understand code.  Developing an SE extension that
  talks directly at this level would be impractical.  A higher layer that
  provided an interface to graphical entities without the complications of
  writing toolkit code is necessary.
  
  The method decided on was to define a user interface as an entity with
  graphical objects stacked vertically on top of each other, or horizontally
  next to each other from left to right, each optionally separated by a
  graphical separator.  The framework of the application could then be defined
  within the SE code as an array of structures.  By defining an array of
  structures with an aggregate initialization as the framework of the GUI,
  most of the programming required to define the appearance of the application
  would be done.

The "resource_t" Structure

  The data (called resources) necessary to construct an object for the GUI
  is contained in the "resource_t" structure defined in gui_lib.se.  This
  structure is:
  
    struct resource_t {
      int    r_type;          // R_BUTTON, etc.
      int    r_foreground;    // one of ST_WHITE, ST_BLUE, etc.
      int    r_background;    // ditto
      string r_label;         // label
      string r_data;          // additional data for the widget
      string r_callback;      // name of function to call if button is pressed
      string r_callback_data; // data to pass to callback
      string r_container;     // name of variable into which to store handle
    };

The "r_type" Member

  The "r_type" member is a numeric code representing what type of graphical
  object this member of the GUI should be.  The possible values for "r_type"
  are contained in the "gui_defines.h" file.  They are:
  
    /* r_type values */
    #define R_HFRAME        0 /* first member of horizontal frame or subframe */
    #define R_VFRAME        1 /* first member of vertical frame or subframe */
    #define R_BUTTON        2 /* button, when just one is needed */
    #define R_GROUP         3 /* just a group of objects */
    #define R_BGROUP        4 /* a button as a title with a group of objects */
    #define R_LGROUP        5 /* a label as a title with a group of objects */
    #define R_HSCALE        6 /* horizontal scale control */
    #define R_VSCALE        7 /* vertical scale control */
    #define R_MENUBAR       8 /* a menu bar */
    #define R_CASCADE       9 /* cascading menu option */
    #define R_OPTION       10 /* option in a cascading menu */
    #define R_HRADIOBOX    11 /* horizontal radio box */
    #define R_VRADIOBOX    12 /* vertical radio box */
    #define R_TBUTTON      13 /* toggle button for radio box logic */
    #define R_COUNTER      14 /* two arrow buttons and a text field */
    #define R_ROW          15 /* form with horizontal orientation */
    #define R_COLUMN       16 /* form with vertical orientation */
    #define R_VBAR         17 /* vertical bar with no labels */
    #define R_HBAR         18 /* horizontal bar with no labels */
    #define R_VBAR_CHART   19 /* vertical bar with label, min, max, value */
    #define R_HBAR_CHART   20 /* horizontal bar with label, min, max, value */
    #define R_LABEL        21 /* just a plain ol' label */
    #define R_TEXT         22 /* scrolled text widget */
    #define R_TFIELD       23 /* an editable text field */
    #define R_FSBOX        24 /* file selection box */
    #define R_SELBOX       25 /* plain selection box */
    #define R_EDITOR       26 /* an editable scrolled text widget */
    #define R_SEPARATOR    27 /* visual separator */
    #define R_STRIPCHART   28 /* the Athena widget */
    #define R_POINTCHART   29 /* a modified strip chart that draws lines */
    #define R_CLOCK        30 /* an xclock clock */
    #define R_END          -1 /* end of list of resources */

The "r_foreground" and "r_background" Members

  The "r_foreground" and "r_background" members are numeric codes representing
  what color the foreground and background should be.  This can be one of the
  standard colors defined in the "gui_defines.h" include file or a user defined
  color.  The only object type that does not use the foreground and background
  colors is the R_HFRAME and R_VFRAME objects.  To set these colors, the -fg
  and -bg options of the X Toolkit are used.  The standard colors are:
  
    #define ST_WHITE        0
    #define ST_BLUE         1
    #define ST_GREEN        2
    #define ST_AMBER        3
    #define ST_RED          4
    #define ST_BLACK        5
    #define ST_YELLOW       6
    #define ST_LIGHTBLUE    7
    #define ST_CYAN         8
    #define ST_MIDNIGHTBLUE 9
    #define ST_OLIVEGREEN   10
    #define ST_GOLDENROD    11
    #define ST_FIREBRICK    12
    #define ST_LIGHTGREY    13
    #define ST_NAVY         14
    #define ST_MAROON       15

The "r_label" Member

  The "r_label" member is a string that means different things to different
  objects.  For those objects that use the "r_label" member, this is what it
  means:
  
    R_BUTTON        The text on the button
    R_HSCALE        The label next to the scale
    R_VSCALE        The label next to the scale
    R_OPTION        The text for the option
    R_TBUTTON       The text for the button
    R_COUNTER       The text next to the counter
    R_VBAR_CHART    The text next to the bar
    R_HBAR_CHART    The text next to the bar
    R_LABEL         The text on the label
    R_SELBOX        The text over the list

The "r_data" Member

  Some of the objects require more resources than are provided by the members
  of the resource_t structure.  In this case, additional resources are placed in
  the "r_data" member.  This is done by creating a colon (:) separated list of
  values.  These values are most often integer values so sprintf() or itoa()
  can be used to create the string value that is used in "r_data".  If this
  list of additional resources is omitted, the library will use default values,
  but the results may not be what you expect.  The definition of the additional
  resources for those objects that use them are:
  
    R_BUTTON
      +------------------------------------+--------+---------------+
      |Description                         |  Type  |  Default Value|
      +====================================+========+===============+
      | If the "r_label" member is nil and |        |               |
      | "r_data" has a value, then it is   |        |               |
      | the path name of an X Bitmap file  |        |               |
      | to use as the label pixmap.        | string | nil           |
      +------------------------------------+--------+---------------+
  
    R_GROUP
    R_BGROUP
    R_LGROUP
      +----------------+------+--------------+
      |Description     | Type | Default Value|
      +================+======+==============+
      |Objects Per Row | int  |      10      |
      +----------------+------+--------------+
  
    R_HSCALE
    R_VSCALE
      +---------------+------+--------------+
      | Description   | Type | Default Value|
      +===============+======+==============+
      | Minimum value | int  |      1       |
      +---------------+------+--------------+
      | Maximum value | int  |     100      |
      +---------------+------+--------------+
      | Initial value | int  |      1       |
      +---------------+------+--------------+
  
    R_OPTION
      +------------------------------------+--------+---------------+
      |Description                         |  Type  |  Default Value|
      +====================================+========+===============+
      | If the "r_label" member is nil and |        |               |
      | "r_data" has a value, then it is   |        |               |
      | the path name of an X Bitmap file  |        |               |
      | to use as the label pixmap.        | string | nil           |
      +------------------------------------+--------+---------------+
  
    R_TBUTTON
      +------------------------------------+--------+---------------+
      |Description                         |  Type  |  Default Value|
      +====================================+========+===============+
      | If the "r_label" member is nil and |        |               |
      | "r_data" has a value, then it is   |        |               |
      | the path name of an X Bitmap file  |        |               |
      | to use as the label pixmap.        | string | nil           |
      +------------------------------------+--------+---------------+
  
    R_COUNTER
      +---------------+------+--------------+
      | Description   | Type | Default Value|
      +===============+======+==============+
      | Minimum value | int  |      1       |
      +---------------+------+--------------+
      | Maximum value | int  |     100      |
      +---------------+------+--------------+
      | Initial value | int  |      1       |
      +---------------+------+--------------+
  
    R_VBAR
    R_HBAR
    R_VBAR_CHART
    R_HBAR_CHART
      +---------------+------+---------------+
      | Description   | Type | Default Value |
      +===============+======+===============+
      | Width         | int  |       8       |
      +---------------+------+---------------+
      | Height        | int  |      64       |
      +---------------+------+---------------+
      | Minimum value | int  |       1       |
      +---------------+------+---------------+
      | Maximum value | int  |     100       |
      +---------------+------+---------------+
      | Initial value | int  |       1       |
      +---------------+------+---------------+

      These are the default values for a bar or bar chart that is vertically
      oriented.  For horizontal orientation the width and height values are
      reversed.
  
    R_LABEL
      +------------------------------------+--------+---------------+
      |Description                         |  Type  |  Default Value|
      +====================================+========+===============+
      | If the "r_label" member is nil and |        |               |
      | "r_data" has a value, then it is   |        |               |
      | the path name of an X Bitmap file  |        |               |
      | to use as the label pixmap.        | string | nil           |
      +------------------------------------+--------+---------------+
  
    R_TEXT
      +-------------+------+---------------+
      | Description | Type | Default Value |
      +=============+======+===============+
      | Width       | int  |     84        |
      +------------------------------------+
      | Height      | int  |     28        |
      +-------------+------+---------------+
  
    R_TFIELD
      +-------------+------+---------------+
      | Description | Type | Default Value |
      +=============+======+===============+
      | Width       | int  |     10        |
      +-------------+------+---------------+
  
    R_FSBOX
      +-------------+---------+---------------+
      | Description |  Type   | Default Value |
      +=============+=========+===============+
      | Directory   |  string | "."           |
      +-------------+---------+---------------+
      | Pattern     |  string | "*"           |
      +-------------+---------+---------------+
      | File Type   |  int    | R_ANY         |
      +-------------+---------+---------------+

      "File Type" can any one of F_REGULAR, F_DIRECTORY or F_ANY.
  
    R_EDITOR
      +-------------+------+---------------+
      | Description | Type | Default Value |
      +=============+======+===============+
      | Width       | int  |     84        |
      +------------------------------------+
      | Height      | int  |     28        |
      +-------------+------+---------------+

    R_STRIPCHART
      +------------+------+---------------+
      |Description | Type | Default Value |
      +============+======+===============+
      | Width      | int  |      50       |
      +------------+------+---------------+
      | Height     | int  |      50       |
      +------------+------+---------------+
      | Scale      | int  |       0       |
      +------------+------+---------------+
      | Jump       | int  |       0       |
      +------------+------+---------------+
      | Interval   | int  |       2       |
      +------------+------+---------------+

    R_POINTCHART
      +------------+------+---------------+
      |Description | Type | Default Value |
      +============+======+===============+
      | Width      | int  |      50       |
      +------------+------+---------------+
      | Height     | int  |      50       |
      +------------+------+---------------+
      | Scale      | int  |       0       |
      +------------+------+---------------+
      | Jump       | int  |       0       |
      +------------+------+---------------+
      | Interval   | int  |       2       |
      +------------+------+---------------+

      "Scale" is the minimum scale for the chart. "Jump" is the number
      of pixels to jump for each update. "Interval" is how often to
      update the chart.

    R_CLOCK
      +------------+------+---------------+
      |Description | Type | Default Value |
      +============+======+===============+
      | Width      | int  |      50       |
      +------------+------+---------------+
      | Height     | int  |     100       |
      +------------+------+---------------+
      | Update     | int  |       1       |
      +------------+------+---------------+
      | Chime      | int  |       0       |
      +------------+------+---------------+

      "Update" is the update interval for the sweep second hand.
      "Chime" is whether the clock should chime on the hour and half hour.
      If set to zero, the clock will not chime. If set to non-zero, it will
      chime once on the half hour and twice on the hour.


Convenience Functions for the "r_data" Member.

  To simplify the process of creating this string for the "r_data" member,
  convenience functions have been created that build the string and return it.
  This will provide a notation for what resources are needed for each type of
  object and removes the need to know what the order of the arguments are.
  These are the function prototypes for those objects that use "r_data".
  
    R_HSCALE
    R_VSCALE
      string gui_scale_data(int minimum, int maximum, int initial)
  
    R_COUNTER
      string gui_counter_data(int minimum, int maximum, int initial)
  
    R_VBAR
    R_HBAR
    R_VBAR_CHART
    R_HBAR_CHART
      string gui_bar_data(int width, int height,
                          int minimum, int maximum, int initial)
  
    R_TEXT
      string gui_text_data(int width, int height)
  
    R_TFIELD
      string gui_tfield_data(int width)
  
    R_EDITOR
      string gui_editor_data(int width, int height)

    R_STRIPCHART
      string gui_stripchart_data(int width, int height, int scale,
                                 int jump, int interval)

    R_POINTCHART
      string gui_pointchart_data(int width, int height, int scale,
                                 int jump, int interval)

    R_CLOCK
      string gui_clock_data(int width, int height, int update, int chime)

  
The "r_callback" Member

  Some of the objects provide the user with the option of interacting with
  the graphical object defined.  In the case of an R_BUTTON, if the user
  presses and releases the button with the pointer, some action should take
  place.  This situation is handled by providing the name of a SymbEL function
  to call should the event take place.  The name of this function goes into the
  "r_callback" member.  If the value of "r_callback" is nil, then no callback
  is registered for that object.

  There are two objects that represent special cases with respect to callback
  handling.  These are the R_FSBOX and the R_SELBOX.  These two objects present
  the user with multiple buttons to press.  Two of these buttons are of
  interest to the application.  These are the "OK" button and the "Cancel"
  button.  In the case of these two objects, two callback values may be
  specified.  These two function names, as is the case with the "r_data"
  member, are separated by a colon (:).  If only one function name appears
  in "r_callback" then it is assumed to be the "OK" callback.

  Not all objects have callbacks, since there is not always an action
  associated with the object.  Or if there is, it is beyond the scope of this
  GUI interface to deal with the types of situations that may arise for that
  object.  These are the objects and events that use callbacks.

    R_BUTTON
      Event:  Button pressed and released.
  
    R_HSCALE
    R_VSCALE
      Event:  Slider value changed.
  
    R_OPTION
      Event:  This menu option was selected.
  
    R_TBUTTON
      Event:  This button has been toggled.
  
    R_COUNTER
      Event:  The counter value has been changed.
  
    R_TFIELD
      Event:  The "Enter" key has been pressed and released.

    R_FSBOX
      Event:  The "OK" button has been pressed and released.
      Event:  The "Cancel" button has been pressed and released.
  
    R_SELBOX
      Event:  The "OK" button has been pressed and released.
      Event:  The "Cancel" button has been pressed and released.

    R_STRIPCHART
    R_POINTCHART
      Event:  At each interval. The callback function differs from the
              standard callback. These callbacks return a double whereas
              the other callbacks are void. The value of the double returned
              should be between 0 as the minimum and the value set by the
              scale argument as the maximum.


Convenience Functions for the "r_callback" Member.

  To facilitate the creation of the callback string for the "r_callback"
  member in the case of the R_FSBOX and R_SELBOX objects, two convenience
  functions were created that build the appropriate string.  This will provide
  a notation for what callbacks are needed for each of these two objects.

  R_FSBOX
    string gui_fsbox_callbacks(string ok_callback, string cancel_callback);

  R_SELBOX
    string gui_selbox_callbacks(string ok_callback, string cancel_callback);


The Declaration of a Callback Function.

  All callback functions have the same declaration with the exception
  of the R_STRIPCHART and R_POINTCHART, which is described in the
  "r_callback member" section. This declaration should be:

  callback_function(ulong handle, string client_data, string call_data)
  {
    ...
  }

  This function returns no value and has 3 parameters. "handle" is the handle
  to the object that owns the callback.  "client_data" is the data that was
  placed in the "r_callback_data" member of the object declaration.
  "call_data" is data specific to the object.

  Not all of the objects will receive a value in the "call_data" parameter.
  For those objects that do not, this value will be "nil".  For those that
  do, the value will be:

    R_HSCALE
    R_VSCALE
      The current value of the slider (as a string, use atoi()).

    R_COUNTER
      The current value of the counter (as a string, use atoi()).

    R_TFIELD
      The contents of the text field that has been entered.

    R_FSBOX
      The name of the file selected.

    R_SELBOX
      The option from the list that was selected.

  There is one other case of the possible value of "call_data".  That case
  is described in the section on dialog boxes.


The "r_callback_data" Member.

  When the application needs a value to be passed to a callback function
  this value can be specified in the "r_callback_data" member.  When the
  callback function is called, the value specified here will be sent in
  the "client_data" parameter of the callback function.


The "r_container" Member.

  If this field has a value, it is the name of a global variable declared
  as a ulong which can be used as a holder for the handle to the data
  structure. This is a convenience so the programmer will not need to
  use the gui_get_member function to look up these handles.


The Structure of the GUI.

  Each object defined in the top-most level of the GUI may be either a
  single entity or a group that contains many other objects.  An example
  of this would be a menu bar.  The menu bar is itself an object that must
  be defined in the top level object.  But the options on the menu bar also
  must be defined.  Since there is no way to insert these sub-objects into the
  top level object through the aggregate initialization process, these
  sub-objects must be inserted through another process.  This process will be
  described when the GUI utility functions are defined.

  The types of objects that are groups are:

    R_GROUP
    R_BGROUP
    R_LGROUP
      The group, button group and label group objects can contain multiples of
      any object.  Most often, these are buttons.  Although the GUI library
      will allow the grouping of any type of object, the output may not be
      what is expected.  This is due to geometry management problems in the
      X Toolkit.

    R_MENUBAR
      The menu bar expects objects under it to be of type R_CASCADE or R_OPTION.
      Using any other type of object will cause undefined results.

    R_HRADIOBOX
    R_VRADIOBOX
      The radiobox expects objects under it to be of type R_TBUTTON.  Using any
      other type of object will cause undefined results.

    R_ROW
    R_COLUMN
      The row and column objects can contain multiples of any object.  The
      grouping of the objects under it will be horizontally oriented in the case
      of the row object and vertically oriented in the case of the column.


Building the GUI.

  Once the aggregate structure array declarations have been written, the code
  to build the GUI can be written.


The "gui_create_frame" Function.

  The first step in this building process is to initialized the GUI library
  and specify the top level array of structures that define the structure of
  the GUI.

    ulong gui_create_frame(int argc, string argv[], resource_t objects[]);

  The "gui_create_frame" function takes as parameters the "argc" and "argv"
  arguments passed in from the command line.  Any X Toolkit argument can be
  used and will have the expected effect on the application.  To change the
  name that appears in the title bar of the application change the "argv[0]"
  value to the desired string.

  The "objects" parameter to this function is the top-most level GUI
  description in the form of an array of structures of type "resource_t".
  The value returned by "gui_create_frame" is an opaque handle that can be
  used to get and put values into the GUI later in the program.

  "gui_create_frame" should only be called once.  To create other frames,
  another function is used.  That function will be described later.

The "gui_main_loop" Function.

  Once the GUI has been initialized, it can be started.  In the most simple
  case, only two function calls are required in the main program.  The first
  is the "gui_create_frame" function and the second is the "gui_main_loop"
  function.

    gui_main_loop(string callback, string callback_data, int interval);

  The "callback" and "callback_data" parameters specify what callback function
  to call and the "interval" parameter specifies, in seconds,  how often to
  call that function.  If "nil" is specified as a callback, then no callback
  is made.  If "callback" is not nil and the "interval" value is zero, then
  the callback is called only once as an initialization step.  When "callback"
  is not nil and interval is not zero, the callback function is called as
  an initialization step and then every "interval" seconds thereafter.

  Note that "gui_main_loop" does not return.  Exiting the program can be
  accomplished through callbacks.

  To change the callback interval while the program is running, the
  "gui_set_loop_interval" function can be used.

    gui_set_loop_interval(string callback, string callback_data, int interval);

  This will set the loop interval to "interval" seconds and call "callback"
  with "callback_data" when that time expires.  Any pending callback with
  the previous interval value will not be honored.


The First Program.

  At this point, the obligatory "Hello, World" program can be written:

  #include <string.se>
  #include <gui_lib.se>

  resource_t gui[3] = {
   /* type      fg          bg          label          */
    { R_VFRAME                                         },
    { R_LABEL,  ST_WHITE,   ST_BLUE,    "Hello, World" },
    { R_END                                            }
  };

  main(int argc, string argv[])
  {
    gui_create_frame(argc, argv, gui);
    gui_main_loop(nil, nil, 0);
  }

  This program will create a label with the text "Hello, World" in white on
  a blue background.  The program will need to be terminated using either the
  window manager or using Crtl-C from the command line.  Reviewing the program:

  #include <string.se>
  #include <gui_lib.se>

  The string.se include file is needed by gui_lib.se so it is included.  The
  gui_lib.se file contains all of the necessary declarations for the program.

   resource_t gui[3] = {
    { R_VFRAME                                         },

  This defines the frame of the GUI as having a vertical orientation.  Since
  no other members of the structure are needed the initialization ends at the
  "r_type" member.

    { R_LABEL,  ST_WHITE,   ST_BLUE,    "Hello, World" },

  This defines an R_LABEL object with an ST_WHITE foreground, an ST_BLUE
  background and containing the text "Hello, World".  There is no callback
  or callback data, so these members are omitted from the initialization.

    { R_END                                            }
  };

  This ends the structure.  If this is omitted, a subscript error will
  occur in the "gui_create_frame" function.

  main(int argc, string argv[])
  {
    gui_create_frame(argc, argv, gui);

  The frame is created and the return value of "gui_create_frame" is ignored
  since it is not used further in this program.

      gui_main_loop(nil, nil, 0);
  }

  "gui_main_loop" is called and the program is under way.  There is no callback
  function so the callback parameter is nil.


A Variation on the First Program.

  A slight modification can now be made to this program to demonstrate
  callbacks and exit the program more elegantly.  Instead of using a label,
  the program will use a button.  When this button is pressed and released,
  the program will exit.

  #include <string.se>
  #include <unistd.se>
  #include <gui_lib.se>
 
  resource_t gui[3] = {
   /* type      fg          bg          label           data cb        */
    { R_VFRAME                                                         },
    { R_BUTTON, ST_WHITE,   ST_BLUE,    "Hello, World", nil, "exit_cb" },
    { R_END                                                            }
  };
 
  main(int argc, string argv[])
  {
    gui_create_frame(argc, argv, gui);
    gui_main_loop(nil, nil, 0);
  }

  exit_cb(ulong handle, string client_data, string call_data)
  {
    exit(0);
  }

  Differences in this program are:

  #include <unistd.se>

  This is for the declaration of exit().

    { R_BUTTON, ST_WHITE,   ST_BLUE,    "Hello, World", nil, "exit_cb" },

  Now, instead of an R_LABEL, this is an R_BUTTON.  The "r_label" member
  remains the same but the "r_callback" member now has the value "exit_cb"
  which is the name of the function declared after "main".  When this button
  is pressed and released, the "exit_cb" function will be called.

  exit_cb(ulong handle, string client_data, string call_data)
  {
    exit(0);
  }

  This is the exit callback for the button.  Note the declaration of parameters
  standard to all callbacks.  They are not used, but are declared anyway.
  When this callback is called, the program exits.


Getting and Putting Objects and Sub-Objects.

  The next step is defining the functions used for getting and putting
  objects within a GUI and how to add sub-objects to groups.  The function
  for getting an object from a GUI is "gui_get_object".

    ulong gui_get_object(ulong handle, int index);

  In this function, the parameter "handle" is an opaque handle representing
  an object that is a group type object.  This can be the handle returned
  from "gui_create_frame" or the value returned from another call to
  "gui_get_object" when the object obtained is a group type object.
  The index value is the subscript for the upper level object that represents
  the sub-object of interest.

  Once the opaque handle for the group object has been obtained, placing
  sub-objects into the group can proceed.  This is accomplished with the
  "gui_add_group" function.

    gui_add_group(ulong handle, resource_t subobjects[]);

  The "handle" parameter is an opaque handle representing an object that is a
  group type object.  Typically, this is the result of a call to
  "gui_get_object".  the "subobjects" parameter is an array of structures
  defining the sub-objects that will be in the group.

  The difference between arrays of objects at the top-most level and arrays
  of sub-objects is that the top-level array needs a R_HFRAME or R_VFRAME as
  the first element of the array.  The sub-objects array does not need this.
  It simply enumerates a list of sub-objects to be added to the parent object.
  The R_END object is used, however, as the terminating member for the array.

An Example Program Using Sub-objects.

  This example program shows the use of group type objects and sub-objects.
  A menu bar with option buttons is a good example.

  #include <stdio.se>
  #include <string.se>
  #include <unistd.se>
  #include <gui_lib.se>
   
  /* a vertically oriented frame with just a menu bar */
  resource_t gui[3] = {
    { R_VFRAME                     },
    { R_MENUBAR, ST_WHITE, ST_BLUE },
    { R_END                        }
  };
  #define MENU_BAR_I  1    /* index of menu bar object in "gui" */

  /* three menu bar options, ending with the R_END terminator */
  resource_t menu_bar[4] = {
    { R_OPTION, ST_WHITE, ST_BLUE, "Start", nil, "start_cb" },
    { R_OPTION, ST_WHITE, ST_BLUE, "Stop",  nil, "stop_cb"  },
    { R_OPTION, ST_WHITE, ST_BLUE, "Exit",  nil, "exit_cb"  },
    { R_END                                                 }
  };
   
  main(int argc, string argv[])
  {
    ulong top_handle;
    ulong menubar_handle;

    /* initialize the application */
    top_handle = gui_create_frame(argc, argv, gui);

    /* get the handle to the menubar group object */
    menubar_handle = gui_get_object(top_handle, MENU_BAR_I);

    /* add a group of options to the menubar */
    gui_add_group(menubar_handle, menu_bar);

    /* go */
    gui_main_loop(nil, nil, 0);
  }
  
  start_cb(ulong handle, string client_data, string call_data)
  {
    puts("Start");
  }

  stop_cb(ulong handle, string client_data, string call_data)
  {
    puts("Stop");
  }

  exit_cb(ulong handle, string client_data, string call_data)
  {
    exit(0);
  }

Updating Objects and Sub-objects.

  After the objects for the GUI have been built, it is often desirable to
  change the attributes of the object.  For instance, changing the colors
  or the text contained in an object.  Updating objects and sub-objects
  can take place one at a time or in groups, much the way the GUI was built.
  The functions for updating objects are:

    gui_update_object(ulong handle, resource_t value);

  This is used for updating a single object such as a button.  The handle
  to the object is obtained by using "gui_get_object" and used as the
  "handle" parameter to "gui_update_object".  The "value" parameter is
  the structure containing the updated values to use in the object.

    gui_update_group(ulong handle, resource_t subobjects[]);

  This function is used to update a group of objects.  When sub-objects
  are added to a parent object the handle to the parent is obtained with
  the "gui_get_object" function and then the sub-objects are added with
  the "gui_add_group" function.  Updating many sub-objects at once is
  performed the same way.  The "handle" parameter is a handle to the parent
  object and the "subobjects" parameter is an array of objects that have
  been modified and need to be put in sync with the internal representations
  held by the GUI library.

  This program will change the button from blue to green and change the
  text appropriately when pressed and released.

  #include <string.se>
  #include <unistd.se>
  #include <gui_lib.se>
   
  resource_t gui[3] = {
   /* type      fg        bg       label   data cb          */
    { R_VFRAME                                              },
    { R_BUTTON, ST_WHITE, ST_BLUE, "Blue", nil, "change_cb" },
    { R_END                                                 }
  };
  #define BUTTON_I  1
   
  main(int argc, string argv[])
  {
    gui_create_frame(argc, argv, gui);
    gui_main_loop(nil, nil, 0);
  }
   
  change_cb(ulong handle, string client_data, string call_data)
  {
    switch(gui[BUTTON_I].r_background) {
    case ST_BLUE:
      gui[BUTTON_I].r_background = ST_GREEN;
      gui[BUTTON_I].r_foreground = ST_BLACK;
      gui[BUTTON_I].r_label = "Green";
      break;
    case ST_GREEN:
      gui[BUTTON_I].r_background = ST_BLUE;
      gui[BUTTON_I].r_foreground = ST_WHITE;
      gui[BUTTON_I].r_label = "Blue";
      break;
    }
    gui_update_object(handle, gui[BUTTON_I]);
  }

Building Additional Frames.

  GUI applications often need to use a "popup" to display information
  or to select something from a list.  The "gui_create_frame" function
  is used to initialize the system and create the top-most level of the
  application.  The function used to create additional frames is
  "gui_create_subframe".

    ulong gui_create_subframe(string title, resource_t objects[]);

  Since the system has already been initialized, there is no need for the
  "argc" and "argv" parameter that "gui_create_frame" uses.  Instead, the
  "title" parameter specifies what should be placed on the title bar of
  the popup sub-frame.  The "objects" parameter is the same insofar as it
  specifies the structure of the sub-frame.

  Once the sub-frame is created, it needs to be "popped up" and "popped down".
  These are accomplished by "gui_popup_subframe" and "gui_popdown_subframe"
  respectively.

    gui_popup_subframe(ulong handle);
    gui_popdown_subframe(ulong handle);

  The "handle" parameter to these two functions is the opaque handle returned
  by the "gui_create_subframe" call.  A common error is to specify a callback
  function for a button contained on a sub-frame and then use the "handle"
  parameter of the callback function in the call to "gui_popdown_subframe".
  The "handle" parameter is the opaque handle to the button that was pressed,
  not the sub-frame.  In order to get the handle to the frame that was popped
  up, the "gui_get_frame" function is used.

    ulong gui_get_frame(ulong handle);

  This function will search upwards looking for the frame that the button
  is contained within.  That handle will be the handle to the sub-frame.
  That value can then be used to pop down the sub-frame.

  Here is a simple example.

  #include <string.se>
  #include <unistd.se>
  #include <gui_lib.se>
 
  resource_t gui[3] = {
   /* type      fg          bg          label    data cb         */
    { R_VFRAME                                                   },
    { R_BUTTON, ST_WHITE,   ST_BLUE,    "PopUp", nil, "popup_cb" },
    { R_END                                                      }
  };
 
  main(int argc, string argv[])
  {
    gui_create_frame(argc, argv, gui);
    gui_main_loop(nil, nil, 0);
  }

  popup_cb(ulong handle, string client_data, string call_data)
  {
    resource_t popup[3] = {
     /* type      fg          bg          label      data cb           */
      { R_VFRAME                                                       },
      { R_BUTTON, ST_WHITE,   ST_BLUE,    "PopDown", nil, "popdown_cb" },
      { R_END                                                          }
    };
    ulong popup_handle;

    /* since local vars are static in SymbEL, this will be done only once */
    if (popup_handle == 0) {
      popup_handle = gui_create_subframe("POP!", popup);
    }

    /* the sub-frame is created.  Pop it up. */
    gui_popup_subframe(popup_handle);
  }

  popdown_cb(ulong handle, string client_data, string call_data)
  {
    /* The handle is to the button.  Get the handle to the sub-frame */
    ulong popup_handle = gui_get_frame(handle);

    /* pop it down */
    gui_popdown_subframe(popup_handle);
  }

Using Dialog Boxes.

  Motif provides pre-built dialog boxes which display information and errors
  in a popup.  The GUI library provides convenience functions for popping up
  these dialog boxes.  The functions are:

    gui_warning_dialog(string format, ...);

  This is meant to provide the user with a warning.  The user is presented with
  one button labeled "OK". The dialog can be dismissed by pressing and releasing
  this button and the application can go on its way.

    gui_fatal_dialog(string format, ...);

  This is a more severe problem.  The user is presented with a dialog with
  one button labeled "EXIT".  When this button is pressed and released, the
  application will exit.

    gui_info_dialog(string format, ...);

  This is meant to provide the user with an informational message.  The user
  is presented with one button labeled "OK". The dialog can be dismissed by
  pressing and releasing this button and the application can go on its way.
  The difference between this and the warning dialog is that the symbol
  presented in the dialog is a lower case "i" instead of an exclamation point.

    gui_yes_or_no_dialog(string callback, string callback_data,
                         string format, ...);

  This dialog allows the user to select a yes or no answer.  The dialog
  contains two buttons labeled "Yes" and "No".  When either of these is
  pressed and released the function specified by "callback" is called with
  "callback_data" sent in the "client_data" parameter.  Data sent in the
  the "call_data" parameter of the callback will be one of two values:

    ANS_YES   This is the string "yes", meaning the "Yes" button was selected.
    ANS_NO    This is the string "no", meaning the "No" button was selected.

  The callback function takes the standard parameters of all callback functions
  as described in the section on the declaration of callback functions.


Object Support Functions.

  Certain objects require additional support functions for their operation.
  The usefulness of others is improved by the addition of support functions.
  Those objects that have support functions and the declaration and description
  of those functions follows.

    R_TEXT
    R_EDITOR
        gui_append_text(ulong handle, string text);

      Append the text specified in "text" to the object referred to by "handle".
      The output window will be adjusted so the end of the text is viewable.

        gui_clear_text(ulong handle);

      Clear the text from the object referred to by "handle".  This will result
      in the window containing no text.

        gui_set_text(ulong handle, string text);

      Set the text for the object referred to by "handle" to the text specified
      by "text".  The output window will be adjusted so the beginning of the
      text is viewable.

        gui_get_text(ulong handle, string buf, int buf_size);

      Retrieve the current contents of the object referred to by "handle"
      and place the results in "buf" but no more than "buf_size" bytes.

        gui_load_text_file(ulong handle, string file_name);

      Load "file_name" into the object referred to by "handle".  The output
      window will be adjusted so the beginning of the text is viewable.

        gui_save_text_file(ulong handle, string file_name);

      Save the current contents of the object referred to by "handle" in the
      file "file_name".

    R_SELBOX
        gui_add_list_items(ulong handle, string items[], int nitems);

      Add a list of items to the selection box referred to by "handle".  The
      list is sent in "items" and "nitems" is the number of items in the list.

Icon and Bitmap Support.

  Support functions have been added to the GUI library that facilitate the
  creation of bitmaps for use as icons.  It is also possible to make the
  icon blink.  The functions for accomplishing this are:

    ulong gui_load_bitmap(string file_name,
                          int foreground, int background);

  Load the bitmap specified by "file_name" using the specified foreground and
  background colors.  Currently, only black and white can be used for icon
  colors.  This will likely change.

    gui_set_icon(ulong icon, ulong icon_mask);

  Set the application icon to the bitmap specified by "icon".  If there is
  an icon mask, this is specified by "icon_mask".  Otherwise, "icon_mask"
  should be 0.

    gui_blink_icon(ulong blink_icon, ulong blink_mask, int on_off);

  Cause the icon to blink or stop blinking depending on whether "on_off" is
  non-zero or zero, respectively.  "blink_icon" is a bitmap that is displayed
  as the "blinking icon".  "blink_mask" is a mask for that icon.  The GUI
  library will alternate the application icon (and mask if specified) with
  "blink_icon" (and "blink_mask" if specified) once every second.


Other Functions.

  Other support functions for changing the appearance of a GUI are:

    gui_grey_option(ulong handle, int grey);

  This function will render an option unselectable.  An option can be either
  on a menu bar, in a cascading menu, or a button.  "handle" refers to the
  handle to the object in question and "grey" is a boolean that specifies
  whether the option should be "greyed out" or not.

    gui_add_color(string name, int code);

  This function allows new colors to be added to a GUI.  "name" is the string
  name of a color, such as "aquamarine".  "code" is a unique numeric code that
  will represent the color.

    gui_set_medium_font(ulong handle, string font_name);
    gui_set_bold_font(ulong handle, string font_name);
    gui_set_text_font(ulong handle, string font_name);

  These functions change the three fonts that the GUI toolkit uses.  The
  "handle" parameter is a handle to an object retrieved with the
  "gui_get_object" function.  The "font_name" parameter is the name of
  a valid X11 font.  The names of all fonts available can be displayed
  by using the standard X11 program "xlsfonts".

  The most widely used font is the medium font.  The text font is used
  in the R_TEXT, R_EDITOR and R_TFIELD objects as well as in the list and
  text field portions of the R_FSBOX and R_SELBOX objects.  The bold font is
  used for the label or button in an R_LGROUP and R_BGROUP object respectively.
  Everything else is medium font.

  Not pertaining to appearance is the function:

    gui_realize_frame(ulong handle);

  This function is necessary in order to perform certain tasks before
  calling "gui_main_loop" or "gui_popup_subframe".  These tasks are

    * setting fonts in objects
    * creating and setting icons
    * "greying out" labels, buttons or options

  "gui_realize_frame" does not cause the GUI to appear on the screen, it
  simply causes all of the internal X Toolkit code to be called to create
  the internal data structures that represent the GUI.

  This example program demonstrates some of these functions.

  #include <unistd.se>
  #include <string.se>
  #include <gui_lib.se>
  
  /* codes for my own colors */
  #define ST_FIREBRICK   ST_USER_COLOR
  #define ST_GOLDENROD   ST_USER_COLOR+1
  #define ST_AQUAMARINE  ST_USER_COLOR+2
  #define ST_YELLOW      ST_USER_COLOR+3
  #define ST_SLATEBLUE   ST_USER_COLOR+4

  /* new fonts */
  #define NCS_BOLD_I_12 \
    "-adobe-new century schoolbook-bold-i-normal--12-120-75-75-p-76-iso8859-1"
  #define SC_BOLD_R_14 \
    "-schumacher-clean-bold-r-normal--14-140-75-75-c-80-iso8859-1"
  
  resource_t gui[5] = {
   /* type      fg             bg            label   data callback  */
    { R_HFRAME                                                      },
    { R_BUTTON, ST_FIREBRICK,  ST_GOLDENROD, "One",  nil, "one"     },
    { R_BUTTON, ST_AQUAMARINE, ST_BLACK,     "Two",  nil, "two"     },
    { R_BUTTON, ST_YELLOW,     ST_SLATEBLUE, "Exit", nil, "exit_cb" },
    { R_END                                                         }
  };
  #define ONE_I    1
  #define TWO_I    2
  
  ulong uno;
  ulong due;
  
  main(int argc, string argv[])
  {
    ulong frame = gui_create_frame(argc, argv, gui);
  
    /* add my own colors */
    gui_add_color("firebrick",     ST_FIREBRICK);
    gui_add_color("palegoldenrod", ST_GOLDENROD);
    gui_add_color("aquamarine",    ST_AQUAMARINE);
    gui_add_color("yellow2",       ST_YELLOW);
    gui_add_color("slateblue",     ST_SLATEBLUE);
  
    /* grab these for later use */
    uno = gui_get_object(frame, ONE_I);
    due = gui_get_object(frame, TWO_I);
  
    /* cause the widgets to be built so I can grey them out */
    gui_realize_frame(frame);

    /* grey out the One button */
    gui_grey_option(uno, True);

    /* set the font for this button */
    gui_set_medium_font(uno, NCS_BOLD_I_12);
    gui_set_medium_font(due, SC_BOLD_R_14);

    gui_main_loop(nil, nil, 0);
  }
  
  one(ulong handle, string client_data, string call_data)
  {
    /* grey "one" and ungrey "two" */
    gui_grey_option(uno, True);
    gui_grey_option(due, False);
  }
  
  two(ulong handle, string client_data, string call_data)
  {
    /* grey "two" and ungrey "one" */
    gui_grey_option(uno, False);
    gui_grey_option(due, True);
  }
  
  exit_cb(ulong handle, string client_data, string call_data)
  {
    exit(0);
  }


More Examples.

  To see more examples of how to write a GUI with the SE GUI library, see
  the programs in the "examples" directory.  xit.se is an especially good
  example since it uses menu bars, popups, file selection boxes, and text
  utilities.
