DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T p

⟦3eb81c641⟧ TextFile

    Length: 82984 (0x14428)
    Types: TextFile
    Names: »pr0b«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦526ad3590⟧ »EUUGD11/gnu-31mar87/X.V10.R4.tar.Z« 
        └─⟦2109abc41⟧ 
            └─ ⟦this⟧ »./X.V10R4/Toolkit/Xr/usr/doc/pr0b« 

TextFile

.IN "Building a Field Editor"
.ds Ch Building a Field Editor
.nr H1 2
.so aformat
.P
The following sections contain the guidelines a programmer should follow
when developing a new field editor. Some of the details describing how an
editor works are also included.
.P
Under normal circumstances, the underlying structure of field editors is
fairly constant, regardless of what specific actions the editor may perform.
Knowing this, it can be an easy task to take an existing editor,
change the editor specific portion of it, and thus easily create
a new field editor.
Adhering to the guidelines presented in this chapter will help
to make this possible in the future also.

.IN "Naming conventions"
.H 2 "Naming Conventions"
.P
Each editor must supply a single 
entry point, through which all action and status requests are received.
A unique name needs to be assigned to this entry point;
preferably one which describes the function of the editor.
By convention, the name must be at least 2 words,
which will be compressed together, with the first letter
of each word capitalized, and the prefix 'Xr' added to the front.  For example:
.BL
.LI
The raster editor is called XrRasterEdit()
.LI
The raster select editor is called XrRasterSelect()
.LI
The titlebar editor is called XrTitleBar()
.LE
.P
Throughout this document, the notation "XrEditorName" will be used;
it refers to the name assigned to this primary editor entry point.
.P
.IN "Editor entry-point naming"
Besides a convention regarding the naming of the editor entry point,
there is also a convention regarding how the editor entry point is
defined.
All editors MUST adhere to this convention, else they will not
work correctly!
The proper syntax for defining an editor's entry point is:
.sp
.nf
.in 8
xrEditor * XrEditorName (editorName, message, data)
.sp
where:
.in
.in 10
"editorName" is a pointer to an editor structure; 
the name of this structure should be the same as
the editor's name, except that the 'Xr' prefix is dropped, 
and the first letter is in the lower case.

"message" is an integer value, which specifies
the action to be performed by the editor.

"data" is a pointer to something; the editor can
coerce this pointer to point to whatever it likes,
depending upon the requested action.
.in
.fi
.SP
.P
Some examples of editor entry point declarations are:
.in 8
.SP
xrEditor * XrRasterEdit (rasterEdit, message, data)
.SP
xrEditor * XrScrollBar (scrollBar, message, data)
.in
.P
.in 17
.nf
xrEditor * rasterEdit, *scrollBar;
INT32      message;
INT8     * data;
.fi
.in

.H 3 "Data Structure Naming"
.SP
Almost every editor has a need to define several data structures;
some are strictly internal, and thus used only by the editor,
while others define structures which are used by application
programs to pass data to the editor.
There are two structures in particular, for which a naming
convention has been defined;
one of these also has a portion of it's structural organization
defined.
.P
The first structure of interest is the structure by which
an editor is first initialized.
This structure will normally contain all of the information
needed by the editor, to create a single instance of itself.
To indicate that this structure contains initialization data for 
a particular editor, the name of the structure will be constructed
by taking the editor name [XrEditorName], 
changing the first character to lower case, and then appending the word
"Info" to the end of it. 
For example, the initialization structure used by the XrScrollBar() editor
is called "xrScrollBarInfo".  
The actual organization of this structure is partially fixed,
to make it easier for code to access information in these structures,
without forcing them to know the complete layout of the structure.
For instance, a non-editor related code module may want to know what 
window the requested editor instance is to be tied to, without
knowing what the actual type of editor it is.
For this reason, the first five fields of an "Info" structure
must be organized in the following fashion, and must use the
field names shown:
.SP
.CO
     typedef struct {
          Window    editorWindowId;
          RECTANGLE editorRect;
          INT8      editorState;
          INT32     editorFGColor;
          INT32     editorBGColor;
           .
           .
      } xrEditorNameInfo;
.CE
.SP
After the first five fields, the editor writer is free to add any
fields he desires, which might be required to initialize the editor.
.P
The second structure of interest is the internal structure
used by the editor to keep track of data and state information for a given
editor instance.  
Although the information in this structure is directly available to
an application program, they should be discouraged from accessing
it directly; instead, they should use the mechanism provided by
the editor, to access this data.
The convention for naming this structure involves taking the
name of the editor [XrEditorName], 
changing the first character to lower case, and then appending the word "Data"
to the end of it.  The actual organization of this structure
is left entirely up to the editor writer, since the editor should
be the only one interested in accessing this data directly.
A sample declaration would be:
.SP
.CO
     typedef struct {
           .
           .
           .
     } xrEditorNameData;
.CE
.P
Any other data structures needed by an editor should, if possible,
follow the naming convention of appending a descriptive word to
the end of the editor name.


.H 3 "Define Naming"
.SP
Since the Xr library toolbox facilities will be used by a large selection
of programs, it is a good idea to make all possible attempts to avoid
naming collisions between toolbox defines and the defines declared
by the program using toolbox.
To aid in meeting this goal, all defines declared by an Xrlib components,
whether it be an intrinsic or a field editor, must have the following
format:
.sp
.nf
.in 5
#define XrDEFINENAME    <value>
.in
.fi
.sp
Following this guideline should help avoid naming conflicts.


.H 3 "Variable Naming"
.SP
For the most part, editor writers are free to use their own judgement
when naming local variables within their editor modules.
However, as all good software developers know, using a name which
in one fashion or another describes how the variable is used,
greatly increases the readability of a program.
There will be several local variable naming conventions, which
will be used throughout the field editors. 
These include the following rules:
.SP
.BL
.LI
When a pointer to an editor's "Info" structure [xrEditorNameInfo] is used,
it's name should be constructed by taking the lower case initials of
the editor name, and appending the word "InfoPtr" to it. 
For example, when the XrScrollBar() editor needs a pointer 
to an "Info" structure, it will  define a local variable as follows:
.SP
      xrScrollBarInfo *sbInfoPtr
.LI
When a pointer to an editor's "Data" structure [xrEditorNameData] is used,
it's name should be constructed by taking the lower case initials of the
editor's name, and appending the work "DataPtr" to it.
For example, when the XrScrollBar() editor needs a pointer to it's
data structure, it will define a local variable as follows:
.SP
      xrScrollBarData *sbDataPtr
.LI
Most editors allow event information to be passed into it's
main editing routine.  In reality, it's a pointer to the event
information which is received by the editor.
By convention, a variable with the following declaration is used
to save this pointer: 'xrEvent *input'.
The local variable named 'input' is then used by all editors when
referring to the event information.
.LE

.IN "Coding conventions"
.H 2 "Coding Conventions"
.P
When defining a structure needed by an editor, the convention is to 'typedef' 
the structure, thus making it easier to create an instance
of that structure.  As an example, the "Info" structure needed by an
editor will have the following declaration:
.SP
.CO
     typedef struct {
          Window      editorWindowId;
          RECTANGLE   editorRect;
          INT8        editorState;
          INT32       editorFGColor;
          INT32       editorBGColor;
           .
           .
     } xrEditorNameInfo;
.CE
.P
Then, to create an instance of this structure, the following line of 
code will suffice:
.SP
.nf
.in 8
xrEditorNameInfo editorNameInfo;
.in
.fi

.IN "Memory management"
.H 2 "Memory Management"
.P
The Xr library toolbox intrinsics provide an application with the means
for supplying its own memory management facilities.
If an application does not choose to supply these routines, then the
standard ones (malloc(), realloc(), calloc() and free()) will be used.
.P
If an application does choose to supply its own memory management facilities,
then it must provide four new entry points, which perform the identical
functions as malloc(), realloc(), calloc() and free().
.P
The redefining of these functions is allowed only once; this is at
the time the application initializes the Xrlib toolbox code, by means
of the XrInit() intrinsic.
.P
What all of this means to a field editor is that they MUST NEVER
invoke malloc(), realloc(), calloc() or free() directly!
Instead, they MUST use the four global function pointers supplied
by the Xrlib intrinsics.
By following this rule, the field editor should continue to function
correctly, regardless of which memory management scheme is being used.
.P
The four global function pointers are outlined below:
.SP
.nf
.in 5
char * (*xrMalloc)();
int    (*xrFree)();
char * (*xrRealloc)();
char * (*xrCalloc)();
.in
.fi
.P
As an example, to use the xrMalloc() function pointer, the
following code will work:
.SP
.CO
.in 5
{
   char * buffer;

   buffer = (*xrMalloc)(1024);
}
.in
.CE

.H 2 "Drawing and Buffer Flushing"
.P
Field editors should refrain from issuing XFlush() requests,
if possible.
This call can slow done the performance of an application,
if issued too often.
We have taken the strategy that when an editor draws an
instance, it will not automatically flush the output
buffer afterwards.
Rather, it will be up to the application to issue the XFlush()
request, when it wants; this allows the application to create
several editors, before flushing the output buffer.
As a side, when an application calls the XrInput() routine, to
obtain an input event, the output buffer is automatically flushed.

.IN "Messages, field editor"
.H 2 "Field Editor Messages"
.P
As was mentioned in an earlier section of this document,
each editor provides only a single entry point,
by which all work requests must be received. 
Since an editor must be capable of processing a relatively
large number of requests, a parameter is provided by each
editor's entry point, which allows a program to specify
the action it would like performed.
.P
There are several messages which are required to be understood by
all editors.
All editors
MUST accept these messages, and respond in the described fashion.
.P
The sub-sections which follows will give a detailed description
of each of these required messages.  As a quick reference,
the required messages are:
.SP
.in 8
.nf
MSG_NEW
MSG_SIZE
MSG_MOVE
MSG_GETSTATE
MSG_SETSTATE
MSG_REDRAW
MSG_EDIT
MSG_FREE
.fi
.in
.P
MSG_SIZE can be called at any time, 
even before an editor instance has been created.
However, before any of the other messages can be invoked,
you must first create the editor instance, using the MSG_NEW message.
This is because the MSG_NEW message returns the pointer to the
editor structure, which is required by all of the other messages,
except for MSG_SIZE.  All editors must enforce this rule, by verifying
that the pointer to an editor structure, which is passed in as the
first parameter, is not NULL.
.P
The discussion which follows will also include one non-required
message: MSG_RESIZE.
.SP
.DS 1
The entry point into the editor is defined as follows:

   xrEditor * XrEditorName (editorName, message, data)

            xrEditor * editorName;
            INT32      message;
            INT8     * data;

.DE
Each of the messages is responsible for converting the 'data'
parameter into a pointer to the desired structure.
Unless noted, all messages return a non-NULL pointer,
if the message was successfully handled.
If the message failed, then a NULL pointer is returned.

.H 3 "MSG_NEW"
.SP
The MSG_NEW message is the mechanism by which an application program
can request an instance of an editor be created.  Internally, the
steps involved in creating an instance of an editor are as follows:
.BL_.LI
Allocate a block of memory to hold the data required by the
editor to process the instance; this memory will stay around
for the duration of the instance's lifetime.
.LI
Determine the size of the rectangle needed to contain the
instance, and fill the data structure with the information.
.LI
Attach the editor instance to the specified editor window;
this is the call which creates the editor structure, whose pointer
is returned to the application program when a successful
status is returned.
.LI
Draw the editor instance in the specified window.
.LE
.P
Before this message is issued, the application must first fill out
an 'Info' structure, which will be used by the editor to
construct the new instance.
Each editor is free to determine what data it requires with this call;
however, the first five fields of the 'Info' structure are fixed
for all editors, and are laid out as follows:
.SP
.CO
        typedef struct {
             Window     editorWindowId;
             RECTANGLE  editorRect;
             INT8       editorState;
             INT32      editorFGColor;
             INT32      editorBGColor;
                   .
                   .
        } xrEditorNameInfo;
.CE
.SP
.VL 15
.LI editorWindowId:
This is the window which is used for displaying the editor instance.
.LI editorRect:
This defines the rectangle into which the editor instance will
be drawn.  Any time an input event occurs within this region,
the editor associated with this rectangle will be notified.
.LI editorState:
Allows an application program to define the initial
state of an editor instance.  
At the present time, only two state characteristics are defined. They
are XrVISIBLE and XrSENSITIVE.  XrVISIBLE signals to the editor
that the instance should be drawn within the window; when cleared,
the instance will not be drawn. 
XrSENSITIVE, when set along with XrVISIBLE, indicates that the editor
should be notified anytime an input event occurs within the
instance's rectangle; if either XrSENSITIVE or XrVISIBLE is cleared, the
editor will not be notified of input events.
.LI editorFGColor
Allows an application program to specify the foreground color
to be used when drawing the editor instance.  If the application has set
this value to -1, the default foreground color should be used. This can be
referenced using the Xrlib global xrForegroundColor.
.LI editorBGColor
Allows an application program to specify the background color to
be used when drawing the editor instance.  If the application has set this
value to -1, the default background color should be used. This can be
referenced using the Xrlib global xrBackgroundColor.
  
.LE
.P
Upon successful completion of this message, a pointer to the newly
created editor structure associated with this instance will be
returned.  This value should be saved by the calling program,
since this pointer is a required parameter for all future calls
to the editor, when in regard to this instance.
.P
A sample of the proper calling sequence is outlined below:
.SP
.nf
.in 8
xrEditor *enEditorPtr;
xrEditorNameInfo enInfo;

enEditorPtr = XrEditorName (NULL, MSG_NEW, &enInfo);
.in
.fi

.H 3 "MSG_FREE"
.SP
When an editor instance is no longer needed by an application program,
the MSG_FREE message is used to notify the editor.
The 'data' parameter is not used by this message, so the calling program
should set this to NULL.
The important parameter is the pointer to the 
editor structure [editorName], which was returned when the
instance was first created.  This structure is used by the editor
to determine what instance is no longer needed.
Internally, the following steps are involved:
.BL
.LI
If the instance is visible, then the area occupied by the instance
should be filled with the window's background tile, thus making
the instance invisible.
.LI
The block of memory which was allocated at create time, and
used to hold the data describing this instance, is freed up.
.LI
The editor structure is removed from the list of editors attached
to the specified window, and the editor structure is destroyed.
.LE
.P
NOTE: After issuing a MSG_FREE message, the editor pointer is no
longer valid, and should not be used; it is up to the application
program, not the editor however, to enforce this.

.H 3 "MSG_SIZE"
.SP
All editors must allow this message to be issued at anytime,
even if an instance has not been created. 
This message is used primarily by programs to determine the size
of the rectangular region needed to enclose the specified
editor instance.  The program will normally fill out a copy of
the editor's 'Info' structure with information describing a
hypothetical editor instance.  The editor will use this information
to calculate the size of the rectangle, and will return the rectangle
size (in 0 based format) in the 'editorRect' portion of the 'Info'
structure.  This allows an application program to then offset the
rectangle to whatever location is desired, and to then issues a
MSG_NEW message to create the real editor instance.
.P
When this message is issued, an editor should not allocate any 
permanent data structures, nor should it return a pointer to an 
editor structure.
It's only job is to return the size of the rectangle!
.P
The amount and type of information required by an editor to determine
the size of the rectangle is very editor specific.  This information
will be covered in later sections, when each editor is discussed.
.P
For this request, the first parameter (the instance pointer) is not
used, and should be set to NULL.

.H 3 "MSG_REDRAW"
.SP
Most editors will allow an application to modify the
current value of the editor.
When an application does modify the value of an editor
instance, the editor will need to be notified, so that
the instance can be redrawn to match the new value.
Just what is performed when a redraw request is
received, is very editor specific.
.P
All editors will require an additional piece of information,
describing what type of redraw to perform; this will be interpreted as a
32 bit integer value.
Again, this value is editor specific.
It may include such options as redrawing the whole instance,
or just redrawing a particular portion of an instance.
.P
At the present time, the following redraw modes are defined:
.VL 25
.LI XrREDRAW_ALL
This will tell the editor to completely redraw the specified
editor instance.  The action performed should be very similar
to what was done when the editor was first displayed.
.LI XrREDRAW_ACTIVE
This will tell the editor to that a new active selection has
been specified for the instance.  The editor must then redraw
only those portions which are necessary, to reflect this.
.LI XrREDRAW_SLIDE
For those editors which have some form of a slide mechanism,
this message will tell the editor that a new slide position
has been specified.
The editor must then redraw only those portions which are
necessary, to show the new slide position.
.LE
.DS 0
A sample of the proper calling sequence is outlined below:

     xrEditor   *enEditorPtr;

     XrEditorName (enEditorPtr, MSG_REDRAW, XrREDRAW_ALL);
.DE

.H 3 "MSG_GETSTATE"
.SP
Each editor maintains a copy of the current state for each of it's 
instances; this refers to the setting of the XrVISIBLE and XrSENSITIVE
characteristics.  Upon request, this information will be returned to an
application program.  A pointer to an 8 bit integer should be 
passed as a parameter when making this call; the current state flags,
which are saved in a field within the editor structure, will be
returned in the pointed to 8 bit value.
The editor structure pointer, passed in as the first parameter,
is used to determine which instance to return state
information about.
.P
A sample of the proper calling sequence is outlined below:
.SP
.DS 1
     INT8       state_flags;
     xrEditor * enEditorPtr;

     XrEditorName (enEditorPtr, MSG_GETSTATE, &state_flags);
.DE

.H 3 "MSG_SETSTATE"
.SP
This message allows an application to modify the state flags (XrSENSITIVE 
and XrVISIBLE) for an editor instance.  The first parameter,
the pointer to the editor structure, will indicate which instance to
modify.  The third parameter should contain the new state flags for the
instance.  Under normal circumstances, when an editor receives one of
these messages, it will save the new state flags in the instance's
editor structure, and will then redraw the editor using the new 
state information.
.P
A sample of the proper calling sequence is outlined below:
.SP
.DS 1
     INT8       state_flags = (XrSENSITIVE | XrVISIBLE);
     xrEditor * enEditorPtr;

     XrEditorName (enEditorPtr, MSG_SETSTATE, state_flags);
.DE

.H 3 "MSG_MOVE"
.SP
This message provides an application with a means for quickly relocating
a particular editor instance within a window.
The size of the editor rectangle associated with the instance is not
changed.
To relocate an editor instance, a new origin point for the editor instance's
rectangle must be specified; the top left corner of the editor
rectangle will then be translated such that it now coincides with the
new origin.  The origin point is interpreted as an absolute position within
the window; i.e. relative to the window's origin.
.P
When this message is issued, the 'instance' parameter must point to the
editor structure associated with the instance which is to be moved,
while the 'data' parameter must point to a 'POINT' structure
containing the new editor rectangle origin.
.P
When an editor instance is relocated, the field editor will
automatically remove the visual image of the instance from the window,
and will then redraw the instance at it's new location; this occurs
only if the instance is visible.
At the same time, the editor will automatically force the editor group's
rectangle to be recalculated.
If an application plans to relocate several editor instances all at once,
its best bet would be to first make all of them invisible, then
relocate all of them, and then make them all visible again.

.H 3 "MSG_RESIZE"
.SP
This message provides an application with a means for both changing the
size of the editor rectangle associated with a particular editor
instance, and changing the location of the new editor rectangle.
All restrictions regarding the editor rectangle size which applied
when the instance was first created using MSG_NEW, still apply.
If an invalid editor rectangle is specified, then the resize request
will fail.
.P
When this message is issued, the 'instance' parameter must point to
the editor structure associated with the instance which is to be
resized, while the 'data' parameter must point to a 'rectangle' 
structure containing the new size and origin for the editor rectangle.
.P
When an editor instance is resized, the field editor will
automatically remove the visual image of the instance from the window,
and will then redraw the instance using the new size and location information;
this occurs only if the instance is visible.
At the same time, the editor will automatically force the editor group's
rectangle to be recalculated.


.H 3 "MSG_EDIT"
.SP
This message is a signal to the editor that an input event
occurred within one of it's editor instances.
The particular instance is specified by the passed in editor structure
pointer.  A pointer to the input event is also passed in; the pointer
refers to a structure of type 'XEvent'.
The editor should verify that the event occurred within
one of its editor rectangles, an also that the event is one which
is supported by the editor; if it is not, then the editor should
return with a 'failed' status.  
Since most field editors are triggered by an
XrSELECT event, the editor can use the utility
routine XrMapButton() to determine if the event
maps into sn XrSELECT event.
If the event is one which the editor
knows how to handle, then it should perform whatever action is implied
by the event, and should then give some notification to the
application program that the editor instance has been modified;
this is accomplished by generating another input event, and pushing
it back onto the front of the application's input queue.
The information contained within the returned 'xrEvent' structure will
include an indication of which editor did the work, the instance which
was modified, and any other editor specific information.
.P
If the event caused the editor to modify the value
associated with the instance, then it should redraw the instance,
to reflect the new value.
.P
After processing the select event, a field editor should monitor
the input queue, and swallow the corresponding select up event;
if any other X event is received before the select up, then it 
should be pushed back onto
the input queue, followed by the xrEvent event, and the editor
should return.

.H 2 "Returning Field Editor Status"
.P
Each time an action request is received by an editor, it needs to
have a mechanism for returning some status information,
indicating whether or not the message was successfully handled.
This is accomplished by returning a pointer to the
appropriate editor structure.  If the NULL pointer is returned,
then the message request failed; any other value implies
that the message completed successfully.
.SP
.nf
.DS 1
An example would be:

   xrEditor * 
   XrEditorName (editorName, message, data)

      xrEditor * editorName;
      INT32      message;
      INT8     * data;

   {
          .
          .
      if (failed)
          return ((xrEditor *)NULL);
      else
          return (editorName);
   }
.DE
.fi
.P 
An example of how a program would use this value would be:
.SP
.nf
.DS 1
   {
      xrEditor *enEditorPtr;

      if (XrEditorName (enEditorPtr, MSG_REDRAW, XrREDRAW_ALL) == NULL)
           /* Request failed */
      else
           /* Request succeeded */
   }
.DE
.fi

.H 2 "Returning Editor Information"
.P
Many times, after an editor has been modified by some input event,
it would like to notify the application program to which it is attached
that some change has taken place.  Fortunately, a mechanism to accomplish
this has been provided.  The general rule for returning notification
information, is through the application's input queue.  
To do this, you need to take a structure of type 'xrEvent', and 
fill it in with the information you would like returned to the
application program.  
Once the structure has been filled, you make a call to XrInput(),
using the MSG_PUSHEVENT message,
which will add the input event to the front of the application's
input queue.
The next time the application issues a read request,
your information will be passed to it.
.P
The following fields in the 'xrEvent' structure are available
for use by an editor:
.SP
.VL 15
.LI type
Must be set to XrXRAY.
.LI source
Must be set to the window id associated with the field editor.
.LI inputType
When an editor returns an event, this field should be set to 'XrEDITOR'.
.LI inputCode
This field is used to uniquely identify the editor which generated
the event.
For example, the scrollbar editor will set this to 'XrSCROLLBAR'.
.LI valuePtr
This field is used by an editor to uniquely identify the editor
instance which generated the event.
The pointer to the instance's editor structure must be returned here.
.LI valuePt
This field is used by an editor to return the cursor position
at which an event had occurred.
.LI value1
This is an optional field, which allows an editor to return some
sort of indication as to what action the editor last took.
For example, when the 'up arrow' portion of a scrollbar is selected,
the scrollbar editor will set this field to 'XrSCROLL_UP'.
.LI value2
Sometimes an editor will want to return a value as part of the
event information.
That is the purpose of this field.
If an editor has no value to return, then this field does
not need to be set.
Each editor is free to use this field in whatever fashion is
reasonable, to return the editor's value.
.LE
.P
Some field editors, such as the scrollbar and
raster edit editors, have an interactive mode of operation.
This mode is entered when a user issues a select within
a certain region of the editor, and normally continues
until the user releases the select button.
When the select up occurs, the editor will push the xrEvent
event onto the front of the application's input queue, and return.
However, there is a second way in which this interactive mode
may be terminated.
Upon receipt of any X event other than the select up, the
field editor should terminate and push two events.
The first event pushed should be the X event which cause the
operation to terminate, while the second event should be the
xrEvent generated by the editor.
These must be pushed in this order!

.IN "Layout and template, field editor"
.H 2 "Field Editor Layout and Template 1"
.P
Now that most of the conventions for building an editor have been
discussed, lets take a look at how an editor should be organized.
Even though a set of editors may perform radically different functions,
their underlying structure should all be the same.  
The example given below is very general, however, it should be
very easy to understand what it is trying to do, and to then use
it as a template when developing a new editor.
.P
Since the underlying structure of most field editors is identical,
it only makes sense to extract as much of the common code as possible,
into utility routines, which can then be shared by all field editors.
This not only reduces code size, but also makes it easier to fix problems
when they occur.
The template presented below does not take advantage of the common
utility routines.
The reason being that we wanted to demonstrate what
is involved in handling each of the messages understood by our sample
field editor.
The section which follows, will discuss these
common utilities, and will also present a second template, showing
what our sample field editor would look like if we
had used the common utilities.
.SP
.nf
xrEditor *
XrEditorName (editorName, message, data)

   xrEditor * editorName;
   INT32      message;
   INT8     * data;

{
   /* Determine the action being requested */
   switch (message)
   {
      case MSG_NEW:
      {
           /* Create a new instance of this editor */
           xrEditorNameInfo * enInfoPtr = (xrEditorNameInfo *)data;
           xrEditorNameData * enDataPtr;

           /*
            * Get a pointer to the information describing the editor
            * instance and make sure the pointer is valid.
            */
           if (enInfoPtr == NULL)
           {
              xrErrno = XrINVALIDPTR;
	      return ((xrEditor *) NULL);
           }
           else if (enInfoPtr->editorWindowId == 0)
           {
              xrErrno = XrINVALIDID;
	      return ((xrEditor *) NULL);
           }

           /* Allocate some data area for this instance */
           if ((enDataPtr = (xrEditorNameData *)
               (*xrMalloc)(sizeof (xrEditorNameData))) == NULL)
           {
              xrErrno = XrOUTOFMEM;
              return ((xrEditor *)NULL);
           }

           /* Call the routine responsible for creating the instance */
           if (createEditorName (enDataPtr, enInfoPtr, MSG_NEW) == FALSE)
           {
              /* Create failed; xrErrno was set by createEditorName() */
              (*xrFree)(enDataPtr);
              return ((xrEditor *)NULL);
           }

           /* Allocate and initialize the editor structure for the instance */
           if (editorName = (xrEditor *)(*xrMalloc)(sizeof(xrEditor)) == NULL)
           {
              (*xrFree)(enDataPtr->extraData);
              (*xrFree)(enDataPtr);
              xrErrno = XrOUTOFMEM;
              return ((xrEditor *)NULL);
           }
           _XrInitEditorStruct (editorName, enInfoPtr, enDataPtr,
                                XrEditorName);

           /* Attempt to attach the editor to the specified window */
           if (XrEditor (enInfoPtr->editorWindowId, MSG_ADD, editorName) == FALSE)
           {
              /* The attach request failed; xrErrno set by XrEditor() */
              (*xrFree)(enDataPtr->extraData);
              (*xrFree) (enDataPtr);
              (*xrFree) (editorName);
              return ((xrEditor *) NULL);
           }

           /* Lastly, draw the editor instance */
           if (editorName->editorState & XrVISIBLE)
              drawEditorName(editorName, NULL);

           /* Create request was successful */
           return (editorName);
      }
.DS 0
      case MSG_FREE:
      {
           /* Destroy the specified editor instance */
           xrEditorNameData * enDataPtr;

           if (editorName == NULL)
           {
              xrErrno = XrINVALIDID;
              return ((xrEditor *)NULL);
           }

           /*
            * Remove the visible instance from the window,
            * free up the memory used to hold the data describing
            * this instance, and disconnect the instance from
            * the window to which it was attached.
            */
           if (editorName->editorState & XrVISIBLE)
           {
              _XrMakeInvisible (editorName->editorWindowId,
                                &editorName->editorRect, TRUE);
           }
           enDataPtr = (xrEditorNameData *) editorName->editorData;
           (*xrFree)(enDataPtr->extraData);
           (*xrFree)(enDataPtr);
	   XrEditor (editorName->editorWindowId, MSG_REMOVE, editorName);
	   (*xrFree)(editorName);
           return (editorName);
      }
.DE
.DS 0
      case MSG_GETSTATE:
      {
           /* Return the current state flags for an editor instance */
           if (editorName == NULL)
           {
              xrErrno = XrINVALIDID;
              return ((xrEditor *)NULL);
           }
           else if (data == NULL)
           {
              xrErrno = XrINVALIDPTR;
              return ((xrEditor *)NULL);
           }

           *data = editorName->editorState;
           return (editorName);
      }
.DE
.DS 0
      case MSG_SIZE:
      {
           /*
            * Return the size of the rectangle needed to enclose 
            * an instance of this editor, using the specifications
            * passed in by the application program.
            */

           xrEditorNameInfo *enInfoPtr;

           enInfoPtr = (xrEditorNameInfo *)data;

           if (enInfoPtr == NULL)
           {
              xrErrno = XrINVALIDPTR;
              return ((xrEditor *) NULL);
           }
           if (sizeEditorName (enInfoPtr, &enInfoPtr->editorRect) == FALSE)
           {
              /* Size request failed; xrErrno set by sizeEditorName() */
              return ((xrEditor *)NULL);
           }

           return (editorName);
      }
.DE
.DS 0
      case MSG_SETSTATE:
      {
           /* Change the state flags for a particular editor instance */
           INT8 oldState;
           INT8 newState;

           if (editorName == NULL)
           {
              xrErrno = XrINVALIDID;
              return ((xrEditor *)NULL);
           }

           oldState = editorName->editorState;
           newState = (INT8) data;
           editorName->editorState = (INT8) data;

           /* Redraw the editor, using the new state flags */
           if (((oldState & XrVISIBLE) != (newState & XrVISIBLE)) ||
              ((newState & XrVISIBLE) &&
              ((oldState & XrSENSITIVE) != (newState & XrSENSITIVE))))
           {
              drawEditorName (editorName, NULL);
           }
           return (editorName);
      }
.DE
.DS 0
      case MSG_MOVE:
      {
         /* Move the origin for the specified editorRect */
         POINT     * ptPtr (POINT *)data;
         RECTANGLE   oldRect;

         if (editorName == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (ptPtr == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         /* Reset the origin of the editor rectangle */
         XrCopyRect (&editorName->editorRect, &oldRect);
         editorName->editorRect.x = ptPtr->x;
         editorName->editorRect.y = ptPtr->y;

         if (editorName->editorState & XrVISIBLE)
         {
            /* Make the instance invisible, if necessary */
            _XrMakeInvisible (editorName->editorWindowId, &oldRect, TRUE);

            /* Redraw the relocated instance */
            drawEditorName (editorName, NULL);
         }

         /* Force recalculation of editor group rectangle */
         XrEditorGroup (NULL, MSG_ADJUSTGROUPRECT, editorName);
         return (editorName);
      }
.DE
.DS 0
      case MSG_REDRAW:
      {
         /* Redraw the specified editor instance */
         INT32 redrawMode = (INT32)data;

         if (editorName == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL)
         }
         else if (redrawMode != XrREDRAW_ALL)
         {
            xrErrno = XrINVALIDOPTION;
            return ((xrEditor *) NULL)
         }
 
         if (editorName->editorState & XrVISIBLE)
            drawEditorName (editorName, NULL);
         return (editorName);
      }
.DE
.DS 0
      case MSG_EDIT:
      {
         /*
          * Process the incoming event, and generate a return
          * event, to indicate to the application program how
          * the editor instance was modified.
          */
         
         XButtonEvent *eventPtr = (XButtonEvent *) data;
         xrEvent       returnEvent;
         xrEvent       exitEvent;

         if ((!_XrCatchableKey (editorName, eventPtr) ||
             (XrMapButton (XeSELECT,eventPtr) != XrSELECT))
         {
            /*
             * Don't set xrErrno, since receiving a event we don't
             * know how to handle is not really an error.
             */
            return ((xrEditor *)NULL);
         }

         /* Initially fill out the return event */
         returnEvent.type = XrXRAY;
         returnEvent.source = eventPtr->window;
         returnEvent.inputType = XrEDITOR;
         returnEvent.inputCode = XrEDITORNAME;
         returnEvent.valuePtr =  (INT32) editorName;
         returnEvent.valuePt.x = eventPtr->x;
         returnEvent.valuePt.y = eventPtr->y;

         /* Process the input event */
         processEditorName (editorName, eventPtr, &returnEvent);
         
         /* Swallow the select up event */
         while (XrInput (NULL, MSG_BLKREAD, &exitEvent) == FALSE);
         if (XrMapButton (XrSELECTUP, &exitEvent) != XrSELECTUP)
         {
            /* Not a select up, so push back onto the input queue */
            XrInput (NULL, MSG_PUSHEVENT, &exitEvent);
         }

         /* Push the editor event onto the input queue */
         XrInput (NULL,MSG_PUSHEVENT, &returnEvent);

         return (editorName);
      }
.DE
.DS 0
      default:
         /* All other messages are invalid */
         xrErrno = XrINVALIDMSG;
         return ((xrEditor *)NULL);

   }  /* end of switch */
.DE
}  /* end of XrEditorName() */
.fi

.IN "Utility functions, field editor"
.H 2 "Field Editor Utility Functions"
.P
Included as part of the Xr library toolbox library is a set of 
utility functions which provide some actions commonly used by field editors.
Included among these utilities are functions which allow a combination
of solid filled, tile filled and/or bordered shapes (rectangles, ovals,
ellipses and polygons) to be easily drawn, routines for initializing
a graphics context structure (this will be discussed further, in the
sections which follow), routines for displaying 
8 bit text strings and for obtaining further information about a 
particular font, and a routine which can be used to initialize an
editor structure.
.P
A field editor writer is not required to use the facilities discussed
in the following sections; in fact, there is no guarantee that these
functions will always be available.
However, they are provided to hopefully make it easier to port field
editor source code to future releases of "X".
They are only provided as programming aids, so that editor writers
need not waste their time duplicating functionality which already
exists.
Also, since these are included in the library, they will be included
only once within a given application, thus preventing duplication of
code, and thus leading to smaller application programs.

.IN "Common message handlers, field editor"
.H 3 "Common Field Editor Message Handlers"
.P
As was stated in the previous section, the underlying
structure of all field editors is basically the same;
only the routines for creating, drawing and processing the field
editor tend to differ.
With this in mind, it is only logical to extract as much
of this common code as possible, into some shared utility
routines, accessible by all field editors.
This not only helps to reduce the size of a field editor,
but it also helps to reduce the chance of a stray error
finding its way into one of the field editors.
.P
Field editor writers are not required to use these utilities; if the
field editor needs some functionality which cannot be handled
by a particular utility routine, then it should use its own
handler routine.
.P
The following is a list of each of the common message handling
utility routines, along with a brief explanation of how they get
their work done.
.SP
.nf
xrEditor *
_MsgNew (instance, infoPtr, dataStructSize, createFunct, drawFunct,
              freeFunct, editorHandler, drawOption)

   xrEditor         * instance;
   xrEditorTemplate * infoPtr;
   INT32              dataStructSize;
   INT32              (*createFunct)();
   INT32              (*drawFunct)();
   INT32              (*freeFunct)();
   INT32              (*editorHandler)();
   INT32              drawOption;
.fi
.SP
.in 4
This routine, if it does not encounter any errors, will take care
of everything needed to create a new editor instance.
If it succeeds, then it will return the instance pointer for the new
editor instance; if it fails, then it will return NULL.
After having validated the pointer contained in 'infoPtr', and the
window id, this routine will first allocate a block of memory to
hold the data which will describe this particular instance; this
structure is different for each field editor, however,
the 'dataStructSize' parameter tells us how large the structure needs to be.
Then it will invoke the specified 'createFunct()', to allow the
field editor to initialize the data structure.
Next, it will allocate some memory for the editor structure, it will
initialize it, and then attach it to the specified window.
The last step is to tell the field editor to draw the instance,
using the specified 'drawOption'.
The 'editorHandler' parameter should be a pointer to the handler
function for the field editor [XrScrollBar() for the scrollbar editor].
If this create process fails for some reason, then it will invoke the function
specified by the 'freeFunct' parameter, before returning; this is done
so that if a field editor has allocated any additional memory in its
create function, it would have the opportunity to free it up.
A field editor can disable this, by setting the 'freeFunct'
parameter to NULL.
The 'freeFunct' routine will be passed a single parameter: a pointer to
the editor data structure.
.in
.SP
.nf
xrEditor *
_MsgFree (instance, freeFunct)

   xrEditor * instance;
   INT32      (*freeFunct)();
.fi
.SP
.in 4
This routine will free up all memory used by a particular field editor
instance, and will remove its image from the window.
It will fail, and return NULL, only if the 'instance' parameter
is set to NULL; otherwise, it will return the instance pointer.
Deleting an editor instance involves removing its image from the
window, freeing up all memory used by the instance, and
disconnecting the instance from the window to which it was attached.
In addition, if the field editor had allocated some memory itself, then
it can specify a function to release this memory; this is done by means
of the 'freeFunct' parameter.
If 'freeFunct' is set to NULL, then this feature is disabled.
The 'freeFunct' routine will be invoked with a single parameter: a pointer
to the editor data structure.
.in
.SP
.nf
xrEditor *
_MsgGetState (instance, stateFlags)

   xrEditor * instance;
   INT8     * stateFlags;
.fi
.SP
.in 4
This routine will take a copy of the current state flags associated with
the specified editor instance, and return them in the variable
pointed to by the 'stateFlags' parameter.
Upon successful completion, the
editor instance pointer will be returned.
This routine will fail, and return NULL, if either of the parameters has
been set to NULL.
.in
.SP
.nf
xrEditor *
_MsgSetState (instance, stateFlags, drawFunct, drawOption)

   xrEditor * instance;
   INT8       stateFlags;
   INT32      (*drawFunct)();
   INT32      drawOption;
.fi
.SP
.in 4
This routine will assign the new state flag values, specified by
the 'stateFlags' parameter, to the specified editor instance, and
will then redraw the instance.
the editor's drawing routine, specified by the 'drawFunct'
parameter, will be invoked and passed the editor instance pointer
and the 'drawOption' value.
Upon successful completion, the instance pointer will be returned.
This routine will fail, and return a NULL value, if 
the 'instance' parameter is set to NULL.
.in
.SP
.nf
xrEditor *
_MsgRedraw (instance, redrawMode, drawFunct, drawOption)

   xrEditor * instance;
   INT32      redrawMode;
   INT32      (*drawFunct)();
   INT32      drawOption;
.fi
.SP
.in 4
This routine will handle a redraw request, only if the XrREDRAW_ALL
option is specified; if your field editor needs to handle other
redraw options, then you will need to write your own MSG_REDRAW handler.
After verifying the 'instance' and 'redrawMode' parameter,
the editor's drawing function, specified by the 'drawFunct'
parameter, will be invoked and passed the editor instance pointer
and the 'drawOption' value.
This routine will fail, and return NULL, if the 'instance' parameter is
set to NULL, or the 'redrawMode' is set to something other than
XrREDRAW_ALL.
.in
.SP
.nf
xrEditor *
_MsgEdit (instance, event, processFunct, inputCode)

   xrEditor      * instance;
   XButtonEvent  * event;
   INT32           (*processFunct)();
   INT16           inputCode;
.fi
.SP
.in 4
This routine serves as a MSG_EDIT handler for those editors
which only accept 
XrSELECT events.
This routine will check to see if the instance is capable of handling
an event (by checking the state flags, to see if the instance
is both visible and sensitive), it will verify that the select fell
within the bounds of the editor instance, and it will verify that
the event is a select request.
If all of these conditions are met, then the event processing
routine for the field editor, specified by the 'processFunct'
parameter, will be invoked, and passed as parameters the
editor instance pointer, a pointer to the XButtonEvent event structure,
and a pointer to an xrEvent structure (this is the event which
will be pushed onto the input queue later).
Before calling the 'processFunct', this routine will first fill out
the type, source, inputType, inputCode, valuePtr and valuePt fields
in the xrEvent structure; the processing routine must fill in the 'value1'
field itself, however, it should feel free to place values
into any of the other fields it wants so.
Upon completion, this routine will strip out the select up event
matching the select event which cause the editor to
be invoked; this means that the processing routine should not attempt
to strip out the select up event itself!
If a select up event is received, then it is removed from the input
queue; if any other event is received, then it will be pushed
back onto the front of the input queue, and _MsgEdit() will no longer
wait for the select up event.
The return event will then be placed on the front of the
application's input queue, to indicate what action was taken by the editor.
The event will be filled out as follows:
.in
.in 9
.SP
.nf
type = XrXRAY
source = window id
inputType = XrEDITOR
inputCode = 'inputCode' parameter
value1 = Editor specific value
value2 = Editor specific value
value3 = Editor specific value
valuePtr = 'instance' parameter
valuePt = location of the select event
.fi
.in
.SP
.in 4
The processing routine can set any other fields it might need, since a
pointer to the return event is passed to it.
.in

.IN "Graphics context structure"
.H 3 "The Graphics Context Structure"
.P
Each of the drawing routines which are provided as part of these utilities,
use a structure called the "graphics context" to obtain all of the
drawing characterists needed when drawing a particular shape.
A graphics context can be thought of as a drawing environment; a field
editor can have up to 8 distinct drawing environments.
Included as part of this structure are:
.SP
.BL
.LI
The foreground color. (default = black)
.LI
The background color. (default = white)
.LI
The text font id.     (default = none)
.LI
The line width.       (default = 1)
.LI
The tile id.          (default = none)
.LI
The fill style.       (default = Solid)
.LI
The replacement rule used during drawing.  (default = GXcopy)
.LE
.P
The Xrlib toolbox maintains an array of 8 graphics context structures, which
a field editor is free to use in any fashion it chooses; in addition,
it maintains a single graphics context, which contains all of the default
settings.
To use a particular graphics context, the default values should first be
copied into one of the 8 available graphics context structures, and then
the editor should change any fields which need to be set to non-default
values.
The steps for doing this will be covered in the section which deals with
the functions which manipulate a graphics context structure.
.P
A discussion of each of the fields in this structure are discussed below:
.P
The foreground color
.br
.in 5
This field describes the color which will be used whenever one of drawing
utilities uses the foreground color.
.in
.P
The background color
.br
.in 5
This field describes the color which will be used whenever one of drawing
utilities uses the background color.
.in
.P
The text font id
.br
.in 5
This field contains the id for the font which will be used whenever text
is displayed.
.in
.P
The line width
.br
.in 5
This field contains a value describing the height and width to be
used whenever a line segment is drawn.
.in
.P
The tile id
.br
.in 5
This field contains the pixmap id for a tile which will be used anytime
one of the shapes is filled with a tile.
.in
.P
The fill style
.br
.in 5
This field specifies whether an object should be filled with a solid (Solid)
color, or a tile (Tiled).
.in
.P
The replacement rule used during drawing
.br
.in 5
This describes the replacement rule to be used anytime drawing is performed.
It can assume any of the standard X replacement rule values, as outlined
in the include file <X/X.h>.
.in
.P
Since frequently changing the values associated with a particular graphics
context may be expensive (performance wise), if a field editor need to
draw using several different drawing environments, then it should set up
several graphics contexts.

.IN "Graphics context functions"
.H 3 "Graphics Context Functions"
.P
Included in this section is a discussion of three utility routines, which
deal with the manipulation of a graphics context.
This includes a function for copying between two graphics 
contexts ( _XrCopyGC() ),
a general routine for modifying the contents of a graphics 
context ( _XrChangeGC() )
and a routine for setting up two commonly used graphics 
contexts ( _XrInitEditorGCs() ).
Toolbox defines a set of constants, which are used by each of the routines
to access a particular graphics context.
All of these defines take the form: xrEditorGC1 - xrEditorGC8.
In addition, there is one special graphics context lying around, which
contains the default settings; this MUST never be modified!
It is referred to by using the define: xrDefaultGC.
Any of the functions below, which expect a graphics context identifier
to be passed in, will expect the value to correspond to one of these defines.
.SP
.nf
_XrCopyGC (srcGC, dstGC)

   INT32 srcGC;
   INT32 dstGC;
.fi
.SP
.in 4
This function will copy the contents of the graphics context indicated
by srcGC to the graphics context indicated by dstGC.
Most frequently, this is used to initialize a graphics context to the
default state; this is done by setting srcGC to 'xrDefaultGC' and
dstGC to the graphics context which needs to be initialized.
.in
.SP
.nf
_XrChangeGC (GC, changeMask, changeList)

   INT32   GC;
   UINT32  changeMask;
   INT32 * changeList;
.fi
.SP
.in 4
To modify the values currently defined within a graphics context, a
program must first construct an array of 21 INT32 values (INT32 array[21]),
and then modify the desired fields within this array.
As was specified earlier, only the foreground color, background color,
pen height, the font, the tile id, the fill type and the replacement rule
may be modified.
A series of defines are provided, which aid in setting up this array.
The defines are:
.SP
.in
.BL 10
.LI
XrFOREGROUNDVAL
.LI
XrBACKGROUNDVAL
.LI
XrLINEWIDTHVAL
.LI
XrFONTVAL
.LI
XrTILEVAL
.LI
XrFILLSTYLEVAL
.LI
XrALUVAL (the replacement rule)
.LE
.in 4
.P
By using these defines to offset into the array, the desired values may
be modified.
In addition, before the graphics context can be changed, a mask must
be constructed, which indicates which fields are to be modified.
This is accomplished by declaring a 32 bit integer value, and then or'ing
in the appropriate bit flags.
The list of valid bit flags is shown below:
.SP
.in
.BL 10
.LI
XrFOREGROUND
.LI
XrBACKGROUND
.LI
XrLINEWIDTH
.LI
XrFONT
.LI
XrTILE
.LI
XrFILLSTYLE
.LI
XrALU
.LE
.in 4
.P
At this point you are ready to invoke this function, and thus set up
a new graphics context.
.in
.SP
.nf
_XrInitEditorGCs (foreColor, backColor, fontId)

   INT32   foreColor;
   INT32   backColor;
   Font    fontId;
.fi
.SP
.in 4
Almost every field editor needs to perform some drawing function using its
foreground and background colors.
Since most of the drawing functions use the foreground color field within
the graphics context, it is necessary to set up two graphics contexts, one
which has the desired foreground color set as the foreground color in the
graphics context, and one which has the desired background color set as the
foreground color in the graphics context; this will then allow us to draw
using both our foreground and background colors.
In addition, the fontId field within both of these graphics contexts will
be initialized to the specified font; if you do not plan on using a
particular font, then you may set the fontId to -1.
Upon completion of this call, the graphics context referred to by
xrEditorGC1 will have the foreground color field set to the value
specified by the foreColor parameter, the background color field set to
the value specified by the backColor parameter, and the fontId set to
the specified font.
Also, the graphics context referred to by xrEditorGC2 will have the
foreground color field set to the value specified by the backColor
parameter, the background color field set to the value specified by
the foreColor parameter, and the fontId set to the specified font.
.in

.IN "Shape-drawing functions"
.H 3 "Shape-dawing Functions"
.P
The utility routines include a series of functions which allow a
field editor to draw solid/tiled filled shapes, with an optional
border around the object.
The types of objects provided here are rectangles, ovals, ellipses
and polygons.
Each of the drawing functions will use one of the graphics context
structure, which were explained earlier.
Each of the utility functions is explained below:
.SP
.nf
_XrLine (windowId, gc, x1, y1, x2, y2)

   Window windowId;
   INT32  gc;
   INT16  x1;
   INT16  y1;
   INT16  x2;
   INT16  y2;
.fi
.SP
.in 4
This procedure will draw a line between the two points designated
as (x1,y1) and (x2,y2), using the drawing environment specified within
the graphics context 'gc'.
The foreground color will be used when drawing the line, the line width
will be used to determine the height and width of the line, and the
specified replacement rule will be used.
.in
.SP
.nf
_XrRectangle (windowId, gc, drawRect)

   Window      windowId;
   INT32       gc;
   RECTANGLE * drawRect;
.fi
.SP
.in 4
Using the foreground color within the graphics context, this routine
will draw a border around the specified rectangle.
The height and width of the border line is determined by the line width
field within the graphics context; the replacement rule is also obtained
from the graphics context.
The interior of the rectangle is not affected.
.in
.SP
.nf
_XrFillRectangle (windowId, gc, drawRect)

   Window      windowId;
   INT32       gc;
   RECTANGLE * drawRect;
.fi
.SP
.in 4
If the current fill style is set to 'Solid", then this routine will
fill the region defined by the passed-in rectangle definition using the
foreground color specified within the graphics context.
If the current fill style is set to 'Tiled", then this routine will
fill the region defined by the passed-in rectangle definition using the
tile specified within the graphics context.
.in
.SP
.nf
_XrBorderFillRectangle (windowId, borderGC, fillGC, drawRect)

   Window      windowId;
   INT32       borderGC;
   INT32       fillGC;
   RECTANGLE * drawRect;
.fi
.SP
.in 4
This routine will first draw a border around the specified rectangle,
using the foreground color, line width, and replacement rule specified
within the graphics context designated by the 'borderGC' parameter.
Then, if the current fill style is set to 'Solid", then this routine will
fill the interior of the region defined by the passed-in rectangle 
definition using the foreground color specified within the 'fillGC'
graphics context.
If the current fill style is set to 'Tiled", then this routine will
fill the interior of the region defined by the passed-in rectangle 
definition using the tile specified within the 'fillGC' graphics context.
.SP
.nf
_XrPoly (windowId, gc, count, pointList)

   Window   windowId;
   INT32    gc;
   INT32    count;
   Vertex * pointList;
.fi
.SP
.in 4
Using the foreground color, the line width, and the replacement
rule specified within the graphics context, this routine will draw
the series of lines which define the polygon specified by the
count and pointList parameters.
Each element within the pointList is composed of a draw mode flag,
and and (x,y) pair; the standard "X" draw mode flags, which are
listed below, are supported:
.SP
.in
.in 8
.nf
VertexRelative      (else absolute)
VertexDontDraw      (else draw)
VertexCurved        (else straight)
VertexStartClosed   (else not)
VertexEndClosed     (else not)
VertexDrawLastPoint (else don't)
.fi
.in
.in 4
.SP
The draw mode flags are constructed by or'ing together the desired set
of these flags, for each point in the polygon.
The interior of the polygon is not affected.
.in
.SP
.nf
_XrFillPoly (windowId, gc, count, pointList)

   Window   windowId;
   INT32    gc;
   INT32    count;
   Vertex * pointList;
.fi
.SP
.in 4
This function behaves similarly to _XrPoly(), except that instead of
drawing the lines defined by the polygon definition, it instead fills
the polygon with the foreground color (if the fill mode is 'Solid') or
with the specified tile (if the fill mode is 'Tiled').
.in

.IN "Text facilities, additional"
.H 3 "Additional Text Facilities"
.SP
The procedures which will be discussed here allow a field editor
writer to easily place text anywhere within the bounds of a window,
and to also obtain further information about a particular font.
Each of these routines will be discussed below:
.SP
.nf
_XrImageText8 (windowId, gc, len, x, y, string)

   Window   windowId;
   INT32    gc;
   INT32    len;
   INT16    x;
   INT16    y;
   STRING8  string;
.fi
.SP
.in 4
This routine will display the specified text string , using the
foreground color, background color, replacement rule and font 
specified within the graphics context.
The area to be occupied by the text will be first painted with
the background color, and then the text will be drawn using the
foreground color.
The length of the string must be specified; if the string is NULL
terminated, then the 'len' parameter should be set to the 
define 'XrNULLTERMINATED'.
.in
.SP
.nf
_XrTextInfo (fontInfo, textInfo)

   FontInfo   * fontInfo;
   xrTextInfo * textInfo;
.fi
.SP
.in 4
This function allows a program to obtain some additional information
describing a particular font.
This additional information includes the ascent, descent, maximum
character width, average character width, and the leading for the font.
The information is returned in the structure pointed to by
the 'textInfo' parameter.
.in

.IN "Editor functions, common"
.H 3 "Common Editor Functions"
.SP
This section will outline some general purpose field editor
routines, which are useful, but do not fall under any of the
previously discussed sections.
.SP
.nf
_XrInitEditorStruct (editorPtr, infoPtr, dataPtr, function)

   xrEditor         * editorPtr;
   xrEditorTemplate * infoPtr;
   char             * dataPtr;
   xrEditor         * (*function)();
.fi
.SP
.in 4
Whenever a field editor creates a new instance, it must allocate an
editor structure, which will then be attached to the specified window.
The editor structure must be initialized to contain the following
information:
.in
.SP
.BL 4 
.LI
The window Id.
.LI
The editor function.
.LI
The editor state.
.LI
The editor rectangle.
.LI
A pointer to the instance specific data.
.LE
.in 4
.SP
Using the passed-in parameters, this routine will fill in each of
these five field within the editor structure pointed to by the 'editorPtr'
parameter.
.in
.SP
.nf
_XrCatchableKey (editorPtr, eventPtr)

   xrEditor         * editorPtr;
   xrEvent          * eventPtr;
.fi
.SP
.in 4
Whenever a field editor receives a MSG_EDIT request, it must first check
to see if it is an event which it should handle.  This involves making
the following set of checks:
.in
.SP
.BL 4
.LI
Verify that the instance pointer is not NULL.
.LI
Verify that the event pointer is not NULL.
.LI
Verify that the instance is both SENSITIVE and VISIBLE.
.LI
Verify that the event occurred within the instance's editor rectangle.
.LE
.SP
This routine will take care of making all of these checks.
If all of the above conditions are met, then a value of 'TRUE'
will be returned; otherwise, a value of 'FALSE' is returned.
.SP
.nf
_XrMakeInvisible (windowId, rectPtr, makeGCFlag)

   Window      windowId;
   RECTANGLE * rectPtr;
   INT8        makeGCFlag;
.fi
.SP
.in 4
All field editors need to be able to make a particular editor instance
become invisible; such as when an application clears the VISIBLE state
flag.
Normally, this involves filling the region defined by the instance's
editor rectangle with the background tile for the window to which the
instance is attached.
This is exactly what this routine will do.
If the 'makeGCFlag' is set to TRUE, then this routine will inquire
the toolbox intrinsics,
so as to get the pixmap id for the background tile, and will then
use the graphics context specified by 'xrEditorGC8' to do the filling.
If the 'makeGCFlag' is set to FALSE, then this routine will assume
that the graphics context specified by 'xrEditorGC8' is already
set to the desired state.
.in

.IN "Template, field editor"
.H 2 "Field Editor Template 2"
.P
Now that we have discussed each of the utility
routines available to field editor writers, we will take
our original field editor template, and rewrite it using
these utilities.
If you are still unclear as to the exact functions performed by
these common utilites, look back at the first editor template
presented earlier; these do not use the common routines, and
instead have all their functionally inline.
.SP
.nf
xrEditor *
XrEditorName (editorName, message, data)

   xrEditor * editorName;
   INT32      message;
   INT8     * data;

{
   /* Determine the action being requested */
   switch (message)
   {
.DS 0
      case MSG_NEW:
      {
         /* Create a new instance of this editor */
         return ((xrEditor *)_MsgNew(editorName, data, sizeof(xrEditorNameData),
                                       createEditorName, drawEditorName,
                                       enFreeMemory, XrEditorName, NULL));
      }
.DE
.DS 0
      case MSG_FREE:
      {
         /* Destroy the specified editor instance */
         return ((xrEditor *) _MsgFree (editorName, enFreeMemory));
      }
.DE
.DS 0
      case MSG_GETSTATE:
      {
         /* Return the current state flags for an editor instance */
         return ((xrEditor *) _MsgGetState (editorName, data));
      }
.DE
.DS 0
      case MSG_SETSTATE:
      {
         /* Change the state flags for a particular editor instance */
         return ((xrEditor *) _MsgSetState (editorName, data,
                                            drawEditorName, NULL));
      }
.DE
.DS 0
      case MSG_REDRAW:
      {
         /* Redraw the specified editor instance */
         return ((xrEditor *) _MsgRedraw (editorName, data,
                                          drawEditorName, NULL));
      }
.DE
.DS 0
      case MSG_SIZE:
      {
           /*
            * Return the size of the rectangle needed to enclose 
            * an instance of this editor, using the specifications
            * passed in by the application program.
            */
           xrEditorNameInfo *enInfoPtr;

           enInfoPtr = (xrEditorNameInfo *)data;

           if (enInfoPtr == NULL)
           {
              xrErrno = XrINVALIDPTR;
              return ((xrEditor *) NULL);
           }
           if (sizeEditorName (enInfoPtr, &enInfoPtr->editorRect) == FALSE)
              /* Size request failed; xrErrno set by sizeEditorName() */
              return ((xrEditor *)NULL);

           return (editorName);
      }
.DE
.DS 0
      case MSG_MOVE:
      {
         /* Move the origin for the specified editorRect */
         POINT     * ptPtr (POINT *)data;
         RECTANGLE   oldRect;

         if (editorName == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (ptPtr == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         /* Reset the origin of the editor rectangle */
         XrCopyRect (&editorName->editorRect, &oldRect);
         editorName->editorRect.x = ptPtr->x;
         editorName->editorRect.y = ptPtr->y;

         if (editorName->editorState & XrVISIBLE)
         {
            /* Make the instance invisible, if necessary */
            _XrMakeInvisible (editorName->editorWindowId, &oldRect, TRUE);

            /* Redraw the relocated instance */
            drawEditorName (editorName, NULL);
         }

         /* Force recalculation of editor group rectangle */
         XrEditorGroup (NULL, MSG_ADJUSTGROUPRECT, editorName);
         return (editorName);
      }
.DE
.DS 0
      case MSG_EDIT:
      {
         /*
          * Process the incoming event, and generate a return
          * event, to indicate to the application program how
          * the editor instance was modified.
          */
         return ((xrEditor *) _MsgEdit (editorName, data,
                                        processEditorName, XrEDITORNAME));
      }
.DE
      default:
      {
         /* All other messages are invalid */
         xrErrno = XrINVALIDMSG;
         return ((xrEditor *) NULL);
      }
   }   /* end of switch */
}   /* end of XrEditorName() */


.DS 0
/*
 * Free up the block of memory which our create routine
 * allocated at the time we first created the instance.
 */

INT32
enFreeMemory (enDataPtr)

   xrEditorNameData * enDataPtr;

{
   (*xrFree) (enDataPtr->extraData);
}
.DE
.fi

.H 2 "Sample Field Editors"
.P
This section will attempt to provide some insight into the organization of
an Xrlib field editor.  You should refer to the actual source code for the indicated field editors to see how they perform certain tasks.
Each of the field editors presented here follows the template which was
presented in the previous section.
.P
In particular, there are three concepts which we will attempt to cover
in this section:
.SP
.BL
.LI
Provide an example of a simple field editor (XrStaticRaster), which
demonstrates the general organization of a typical field editor.
.LI
Provide an example of a multiple entity field editor (XrCheckBox),
which demonstrates a sample algorithm for laying out one type of
multiple entity field editor.
It also demonstrates how input handling is performed for this type of editor.
.LI
Provide an example of how an editor has been constructed by combining
an existing field editor together with some new software, to form a
new editor.
This concept is useful, for example, when an editor needs to be
created which contains a scrollbar, along with some raster
data (XrRasterEdit).
.LE
.P
Each of the sub-sections which follow will explain one of the above concepts.

.IN "Simple field editor"
.H 3 "Simple Field Editor
.P
The static raster editor is a good example of a short and simple field
editor.
When a new instance is created, there is little work which needs to be done,
other than displaying the raster data itself.
It also demonstrates some of the steps taken by a field editor, in order
to process incoming input events.  (Refer to the source listing for the
Static Raster field editor.) 

.IN "Multiple-entity field editors"
.H 3 "Multiple Entity Field Editors
.P
Some field editors, such as the checkbox and pushbutton editors,
find it convenient to allow an application to create multiple entities
of the editor with a single request.
More often than not, these types of editors are created as a group
of related items anyways.
It's a rare occurrence to create a single checkbox or a single pushbutton.
.P
Designing an editor to handle this can be a real challenge.
In particular, the algorithm used to determine the placement of the
individual items is not always easy to derive.
The example which follows will demonstrate one algorithm which may be used
to do this placement.
However, it should be noted that this is not the only algorithm
available.
In fact, depending upon how the items are to be laid out,
this algorithm may not fit your needs.
But, it is at least an example, which may be enough to help you
customize your own layout routines.
.P
The algorithm used by the checkbox field editor to calculate the location 
of each of the individual checkboxes makes the following assumptions:
.SP
.BL
.LI
Each checkbox is composed of a square box, with an optional
label located to the right, and separated by some white space.
.LI
The checkboxes will be laid out in rows and columns, and must
fit within the specified editorRect.
.LI
Each checkbox in a given column will line up.
.LI
If the editorRect is larger than is needed to contain the checkboxes,
then the extra space will be equally distributed as white space
between the rows and columns.
.LE
.P
Before covering the algorithm, it would first be helpful to picture how
a sample checkbox editor instance might look.
The outline for the editorRect is provided only to show the
relationship between the checkbox location and the borders of the
editorRect.
.SP 2
.DS
\f(CW
           ----....................----............
           |  |   button 1         |  |   button 2.
           ----                    ----           .
           .                                      .
           .                                      .
           ----                    ----           .
           |  |   button 3         |  |   button 4.
           ----                    ----           .
           .                                      .
           .                                      .
           ----                                   .
           |  |   button 5                        .
           ----....................................
\fR
.DE
.P
The algorithm used by the checkbox editor works in the following manner:
.SP
.BL
.LI
Determine the number of rows in the instance, and the width (in pixels)
of each column of checkboxes.
Use these two pieces of information to determine if the editorRect is
larger than is needed.
If it is, then this extra space can be used as padding between the
rows and columns; the padding values will then be calculated and
saved.
.LI
Set the initial checkbox location (the upper left hand corner of
the editorRect); this is where the first checkbox will be placed.
Afterwards, this location will be updated to indicate the location
of the next checkbox.
.LI
At this point, we are ready to start the layout calculations.
This is done one column at a time, starting with column 1 (the
leftmost one).
As each element in this column is laid out, a check is made to
see if it is the widest element in the column; this information
is used later, when we need to offset to the next column.
After calculating the checkbox location, the location for the optional
label will be calculated.
.LI
Once a column has been laid out, the algorithm will calculate the
position for the first checkbox in the next column, and will then
repeat the process for the next column.
.LI
At this point, all of the entities have been laid out in equally
spaced rows and columns.
.LE

.IN "Creating from existing editors"
.H 3 "Creating From Existing Editors"
.P
Sometimes a field editor may find it advantageous to incorporate
an instance of another editor, rather than attempting to write new code
to re-create the desired piece.
As an example of this, the raster editor has the capability of displaying a scrollbar as part of one of its instances.  It doesn't attempt to duplicate the
scrollbar code.  Instead, it simply creates a scrollbar instance, and attaches it to its editor instance.
.P
The major advantages of allowing an editor to do this are:
.SP
.BL
.LI
The field editor writer need not waste time trying to re-create
portions of an existing field editor.
.LI
The software which controls the incorporated editor is already debugged
and running.
.LI
The encorporated component looks and behaves the same as any other
instance of that field editor.
.LE
.P
.IN "Main editor"
.IN "Sub editor"
Before discussing the means for doing this, there are two terms we need to
define.
The term 'main editor' refers to the new field editor being designed, while
the term 'sub-editor' refers to those existing editors being incorporated
into the main editor.
.P
The mechanism for incorporating other field editors is very simple to
use.  However, there are several rules which must be followed at all
times, if you expect your new field editor to function:
.SP
.BL
.LI
Any sub-editors which are to be included within an instance of the
main editor, must reside COMPLETELY within the editorRect associated with
the main editor instance.
Since a field editor knows what components it plans to include when it is
created, it can make sure the application supplied editorRect is large
enough to hold all components; its MSG_SIZE routine must also take
these sub-editors into consideration.
This normally involves having the main editor do a MSG_SIZE for each
sub-editor it will be using, and then using this information to
help determine its size.
.P
The reason such strong emphasis is placed upon the fact that the
main editor must completely contain the sub-editors is the following:
.P
.in 4
Sub-editors do not reside in the editor list maintained by the window;
therefore, they are unable to receive input events by means of the normal
XrInput() dispatching.
This is resolved by having the main editor check its input events to see
if they were really meant for a sub-editor; remember - the main editor
created the sub-editor instances, so it knows exactly where their
editorRects are located.
The catch is that the main editor only receives events which occur
within its editorRect.
If a sub-editor does not lie completely within the main editor's
rectangle, then that portion of the sub-editor outside the rectangle
will never receive input events.
.in
.LI
When a main editor instance is created, it will normally create and display
its components, before calling the sub-editors to create their
components; this helps to guarantee that all the components
overlap in the proper fashion.
Once the sub-editors have been created, they need to be removed from the
window's editor list.
If this is not done, then the main editor instance may behave in an 
unfriendly fashion, since it will not have absolute control over its
sub-editors.
The means for removing the sub-editors from the editor list is by using
the XrEditor() intrinsic, with the MSG_REMOVE message.
You simply pass in the instance pointer for the editor instance to be
removed, along with a pointer to the window to which it is currently attached.
.LI
Under normal circumstances, when an application creates an instance of
a field editor, it is returned a pointer to the editor structure
associated with that new instance; this then serves to act as a
unique identifier for all future references to that instance.
This also holds true for when a main editor creates sub-editors; each
sub-editor will return its editor instance pointer.
The main editor must define space within its own data structures to
hold these values; without these, the sub-editors cannot be communicated
with.
The following should be added to the main editor's data structure (one
of these for each sub-editor it plans to include in one of its instances):
.SP
.in 5
.CO
xrEditor * subEditor1;
.CE
.in
.LI
Since the sub editors no longer reside in the editor list, it is the
responsibility of the main editor to make sure they are kept up-to-date.
Since these editors have been grouped together into a single entity, they
must behave as a single entity.
This implies that anytime the main editor receives a MSG_REDRAW, MSG_FREE,
MSG_MOVE or MSG_SETSTATE message, these commands should also be performed
for the sub editors.
.IN "Route input events, sub editors"
.LI
As was mentioned a little earlier, it is the responsibility of the main
editor to route input events to the sub-editors, as needed.
The means for doing this are straightforward.
Each time the main editor receives an input event, it first checks to see
if it belonged to one of the sub-editors; this is done by
seeing if the event location fell within the bounds of one of the
sub-editor's editorRect.
If it does not belong to a sub-editor, then the main editor can process the
event.
However, if it did belong to a sub-editor, then the main editor must invoke
the sub-editor's input handling routine; this is done by issuing a MSG_EDIT
message to the sub-editor, and then passing the event information with it.
When the sub-editor is done processing the event, it will in turn push some
event information onto the front of the input queue, and then return.
The main editor can then remove the event from the front of the
queue, and perform the desired action. 
Before the main editor can return, however, it needs to first push
a fake 'select up' event on the front of the input queue, followed by
a second event, containing its own result information; the reason for
doing this is discussed in a paragraph which follows.
This mechanism allows the information generated by the sub-editors to be
kept and processed within the main editor.
.LI
The last guideline which needs to be mentioned concerns what happens when
the main editor instance is freed up.
The main editor MUST issue a MSG_FREE to each of its sub-editors,
otherwise the space occupied by their data structures will become 'lost'
and irrecoverable until the application terminates.
.LE
.P
.IN "Select-up event"
One rule which is to be followed by all field editors, is that they
should swallow the select up event which occurs after the select event
which caused their processing routine to be called.
This is done to prevent an application from being inundated with select
up events.
If an editor uses the _MsgEdit() edit routine to invoke its processing
routine, then this will be automatically taken care of; _MsgEdit()
automatically wait for the select up event, and swallows it.
Unfortunately, this can create a problem, when one editor's processing
routine issues a MSG_EDIT to another editor.
Take for instance the example of the raster editor calling the scrollbar
editor.
When the raster editor receives a select event, the following sequence
of events occurs:
.sp
.BL
.LI
XrInput() will pass the event to XrRasterEdit() for processing.
.LI
XrRasterEdit() invokes _MsgEdit(), passing the event, and a pointer
to the raster edit event processing routine [processREdit()].
.LI
_MsgEdit() verifies the event, and then passes it to processREdit().
.LI
processREdit() determines that the event was in one of the scrollbars,
and issues a MSG_EDIT to XrScrollBar(), passing it the event.
.LI
XrScrollBar() invokes _MsgEdit(), passing the event, and a pointer
to the scrollbar event processing routine [processScrollBar()].
.LI
_MsgEdit() verifies the event, and then passes it to processScrollBar().
.LI
processScrollBar() processes the event, and returns to _MsgEdit().
.LI
_MsgEdit() waits for the select up to occur, swallows it,
pushes the return event generated by processScrollBar(), and
then returns to XrScrollBar().
.LI
XrScrollBar() returns to processREdit().
.LI
processREdit() pops the scrollbar event off the input queue,
processes it, and then returns to _MsgEdit().
.LI
_MsgEdit() waits for the select up to occur, so that it can swallow it.
Unfortunately, the first call to _MsgEdit, by the scrollbar editor,
already swallowed the select up event. 
.LE
.P
.IN "Select-up event"
Thus, _MsgEdit() proceeds to 'hang', until some X event occurs. 
This is undesirable!
The solution is for processREdit() to push a fake select up
event onto the front of the input queue, after processing
the event returned by XrScrollBar().
If this is done, then everything works fine.
The following code sample shows how this can be done, using
a utility routine provided by the Xrlib intrinsics:
.SP
.in 5
.nf
XButtonEvent   selectUp;
xrWindowEvent  windowEvent;

XrGetWindowEvent (XrSELECTUP, &windowEvent);
selectUp.type = windowEvent.inputType;
selectUp.detail = windowEvent.inputCode;
XrInput (NULL, MSG_PUSHEVENT, &selectUp);
.fi
.in