/*
 *	$Source: /u1/Xr/src/Xrlib/Editor/RCS/TextEdit.c,v $
 *	$Header: TextEdit.c,v 1.1 86/12/17 09:05:45 swick Exp $
 */

#ifndef lint
static char *rcsid_TextEdit_c = "$Header: TextEdit.c,v 1.1 86/12/17 09:05:45 swick Exp $";
#endif	lint


#include <Xr/xr-copyright.h>

/* $Header: TextEdit.c,v 1.1 86/12/17 09:05:45 swick Exp $ */
/* Copyright 1986, Hewlett-Packard Company */
/* Copyright 1986, Massachussetts Institute of Technology */

static char rcsid[] = "$Header: TextEdit.c,v 1.1 86/12/17 09:05:45 swick Exp $";
/*************************************<+>*************************************
 *****************************************************************************
 **
 **   File:        TextEdit.c
 **
 **   Project:     X-ray Toolbox
 **
 **   Description: 
 **         Source code for the X-ray text edit field editor.
 **
 **
 **   ------------------------ MODIFICATION RECORD   ------------------------
 *
 * $Log:	TextEdit.c,v $
 * Revision 1.1  86/12/17  09:05:45  swick
 * Initial revision
 * 
 * Revision 7.0  86/11/13  08:28:31  08:28:31  fred ()
 * Final QA Release
 * 
 * Revision 6.0  86/11/10  15:37:01  15:37:01  fred ()
 * QA #2 release
 * 
 * Revision 5.8  86/11/10  09:18:03  09:18:03  fred ()
 * Added check for '\n', in addition to '\r' and RETURN_KEY in
 * teTraversalEvent().
 * 
 * Revision 5.7  86/11/07  14:24:15  14:24:15  fred ()
 * Added new copyright message.
 * 
 * Revision 5.6  86/11/06  07:00:59  07:00:59  fred ()
 * Added text string redraw to teActivate(), to handle when the
 * application changes the string value during temporary break event.
 * 
 * Revision 5.5  86/11/05  14:51:42  14:51:42  fred ()
 * Added check for cursor past the end of the string.
 * 
 * Revision 5.4  86/11/05  09:34:09  09:34:09  fred ()
 * Fixed bug which prevented handling of 8 bit characters.
 * 
 * Revision 5.3  86/11/05  08:49:40  08:49:40  fred ()
 * Now only handles input events if they occurred in the window in which
 * the editor instance was created.
 * 
 * Revision 5.2  86/11/03  11:46:19  11:46:19  fred ()
 * Added fix to treat DEL the same as BACKSPACE.
 * 
 * Revision 5.1  86/10/30  13:30:51  13:30:51  fred ()
 * Fixed bug in handling of RETURN and TAB keys for non-HP keyboards.
 * 
 * Revision 5.0  86/10/28  08:37:49  08:37:49  fred ()
 * QA #1.1 release
 * 
 * Revision 4.3  86/10/27  15:50:25  15:50:25  fred ()
 * Fixed bug introduced by previous update.
 * 
 * Revision 4.2  86/10/27  13:49:53  13:49:53  fred ()
 * Updated to match new XrInputMap() syntax.
 * 
 * Revision 4.1  86/10/23  09:10:43  09:10:43  fred ()
 * Removed unused variables.
 * 
 * Revision 4.0  86/10/20  12:14:33  12:14:33  fred ()
 * QA #1 release
 * 
 * Revision 3.6  86/10/16  09:19:16  09:19:16  fred ()
 * Performance enhanced: added use of register variables.
 * 
 * Revision 3.5  86/10/15  09:15:40  09:15:40  fred ()
 * Now treat control characters as unknown events, and return them to
 * the application; this allows keyboard equivalents in menus.
 * 
 * Revision 3.4  86/10/13  10:07:16  10:07:16  fred ()
 * Added use of the default tile, if needed.
 * 
 * Revision 3.3  86/10/09  07:56:02  07:56:02  fred ()
 * Added default color check to create routine.
 * 
 * Revision 3.2  86/10/07  16:50:16  16:50:16  fred ()
 * Upgraded to use new XrMapButton() calling sequence.
 * 
 * Revision 3.1  86/10/07  16:06:33  16:06:33  fred ()
 * Added code to free up the background tile at MSG_FREE time,
 * and now set 'value3' field when traversal event occurs.
 * 
 * Revision 3.0  86/10/02  16:02:22  16:02:22  fred ()
 * Alpha release set to 3.0
 * 
 * Revision 2.7  86/10/01  15:37:27  15:37:27  fred ()
 * Updated several procedure headers.
 * 
 * Revision 2.6  86/10/01  08:41:44  08:41:44  fred ()
 * Changed to use the input translation routines; application must
 * now call XrInputInit(display type) before using a text editor.
 * 
 * Revision 2.5  86/09/29  12:52:37  12:52:37  fred ()
 * Changed processTextEdit() so that an unknown event received while
 * a field is inactive causes NULL to be returned, instead of having
 * the event treated as an unknown event.
 * 
 * Revision 2.4  86/09/29  12:01:59  12:01:59  fred ()
 * Filled in the procedure headers.
 * 
 * Revision 2.3  86/09/29  07:07:31  07:07:31  fred ()
 * Added the variable width cell size parameter.
 * 
 * Revision 2.2  86/09/25  09:49:16  09:49:16  fred ()
 * First full functionality version.
 * 
 * Revision 2.1  86/09/19  07:13:33  07:13:33  fred ()
 * Added a check for XrVISIBLE before doing any drawing.
 * 
 * Revision 2.0  86/09/16  08:12:26  08:12:26  fred ()
 * No change; updated to 2.0 to match revision of other sources.
 * 
 * Revision 1.1  86/09/03  13:58:45  13:58:45  fred ()
 * Initial revision
 * 
 *
 *****************************************************************************
 *************************************<+>*************************************/



#include <X/Xlib.h>
#include <Xr/defs.h>
#include <Xr/types.h>
#include <Xr/in_types.h>
#include <ctype.h>
#include <Xr/keycode.h>

extern INT32 teFreeMemory();
extern INT32 createTextEdit();
extern INT32 drawTextEdit();
extern xrEditor * processTextEdit();
extern xrEditor * teMOVE_handler();
extern xrEditor * teRESIZE_handler();
extern xrEditor * teREDRAW_handler();
extern xrEditor * teINSERTMODE_handler();
extern xrEditor * teActivate();

#define XrINITANDDRAWALL   0x00
#define XrSTRINGONLY       0x01
#define XrALLOTHERPARTS    0x02
#define XrALLCOMPONENTS    (XrSTRINGONLY | XrALLOTHERPARTS)
#define BORDER             3
#define PADDING            2


/*************************************<->*************************************
 *
 *  XrTextEdit (textEdit, message, data)
 *
 *     xrEditor * textEdit;
 *     INT32      message;
 *     INT8     * data;
 *
 *   Description:
 *   -----------
 *     This is the main handler routine for the text edit field editor.
 *     It takes each action request issued by an application, verifies
 *     that the editor can handle it, and then calls the appropriate
 *     handling routine.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = For all messages by MSG_NEW and MSG_SIZE, this must
 *                contain the editor instance pointer for the instance
 *                which is to be operated upon.
 *    
 *     message = This specifies the action to be taken by the editor.
 *
 *     data = This is the message specific data.  It may be a scalar,
 *            a pointer, or not used, depending upon the action requested.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, all messages will return a non-NULL
 *          value (normally the instance pointer).  Additional data may
 *          be returned by certain messages.
 *
 *     Upon failure, NULL will be returned, and xrErrno set.
 *
 *   Procedures Called
 *   -----------------
 *   _MsgNew()       [MsgCommon.c]
 *   _MsgFree()      [MsgCommon.c]
 *   _MsgGetState()  [MsgCommon.c]
 *   _MsgSetState()  [MsgCommon.c]
 *   XrCopyRect()    [calc.c]
 *   sizeTextEdit()
 *   drawTextEdit()
 *   processTextEdit()
 *   teActivate()
 *   teMOVE_handler()
 *   teRESIZE_handler()
 *   teREDRAW_handler()
 *   teINSERTMODE_handler()
 *
 *************************************<->***********************************/

xrEditor *
XrTextEdit (textEdit, message, data)

   register xrEditor * textEdit;
            INT32      message;
            INT8     * data;

{
   /* Determine the action being requested */
   switch (message)
   {
      case MSG_NEW:
      {
           /*
            * Create a new instance of this editor.
            * The 'textEdit' parameter is unused, but the 'data'
            * parameter must point to a filled out instance of
            * the 'xrTextEditInfo' structure, which describes the
            * instance which is to be created.
            */
           return ((xrEditor *)_MsgNew (textEdit, data, 
                                        sizeof(xrTextEditData),
                                        createTextEdit, drawTextEdit,
                                        teFreeMemory, XrTextEdit,
                                        XrINITANDDRAWALL));
      }

      case MSG_FREE:
      {
           /*
            * Destroy the specified editor instance.
            * The only parameter of instance is 'textEdit', which
            * contains the editor instance pointer for the instance
            * which is to be destroyed.
            */
           return ((xrEditor *) _MsgFree (textEdit, teFreeMemory));
      }

      case MSG_GETSTATE:
      {
           /*
            * Return the current state flags associated with the
            * specified text edit instance.  The 'textEdit' parameter
            * contains the editor instance pointer, and the 'data'
            * parameter points to an INT8 value, into which the
            * state flag values will be placed.
            */
           return ((xrEditor *) _MsgGetState (textEdit, data));
      }

      case MSG_SETSTATE:
      {
           /*
            * Change the state flags associated with a particular text
            * edit instance.  The 'textEdit' parameter contains the
            * editor instance pointer for the instance to be modified,
            * while the 'data' parameter is interpreted as an INT8
            * value, containing the new state flag values.
            */
           return ((xrEditor *) _MsgSetState (textEdit, data, 
                                              drawTextEdit, XrINITANDDRAWALL));
      }

      case MSG_GETITEMCOUNT:
      {
         /*
          * Return the number of text edit fields in the specified 
          * instance.  The 'textEdit' parameter contains the editor
          * instance pointer, and the 'data' parameter must point to
          * an INT32 value, into which the field count will be placed.
          */
         INT32 * countPtr = (INT32 *) data;

         if (textEdit == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (countPtr == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         *countPtr = 1;
         return (textEdit);
      }

      case MSG_GETITEMRECTS:
      {
         /*
          * Fill the array passed in by the application, with the
          * rectangle information describing the text edit region
          * for the specified instance.  The 'textEdit' parameter
          * must contain the editor instance pointer, while the
          * 'data' parameter must point to an instance of the
          * 'RECTANGLE' structure, into which the instance's editor
          * rectangle information will be placed.
          */
         xrTextEditData * teDataPtr;

         if (textEdit == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (data == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         teDataPtr = (xrTextEditData *) textEdit->editorData;
         XrCopyRect (&teDataPtr->teFrameRect, data);
         return (textEdit);
      }

      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.  The 'textEdit'
          * parameter is unused, while the 'data' parameter must
          * point to a partially filled out instance of the 
          * 'xrTextEditInfo' structure.
          */
         xrTextEditInfo * teInfoPtr = (xrTextEditInfo *) data;

         if (teInfoPtr == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         return((xrEditor *) sizeTextEdit (teInfoPtr, &teInfoPtr->editorRect));
      }

      case MSG_MOVE:
      {
         /*
          * Relocate an instance of the text editor to a new location.
          * The 'textEdit' parameter must contain the editor instance
          * pointer, while the 'data' parameter must point to an
          * instance of the 'POINT' structure, which contains the new
          * editor rectangle origin.
          */
         return ((xrEditor *) teMOVE_handler (textEdit, data));
      }

      case MSG_RESIZE:
      {
         /*
          * Resize an existing instance of the text editor.
          * The 'textEdit' parameter must contain the editor instance
          * pointer, while the 'data' parameter must point to an
          * instance of the 'RECTANGLE' structure, which contains the new
          * editor rectangle size and origin information.
          */
         return ((xrEditor *) teRESIZE_handler (textEdit, data));
      }

      case MSG_REDRAW:
      {
         /*
          * Redraw the editor, to match new value.
          * The 'textEdit' parameter must contain the editor instance
          * pointer, while the 'data' parameter will be interpreted
          * as an INT32 value, specifying the type of redraw to perform.
          */
         return ((xrEditor *) teREDRAW_handler (textEdit, (INT32) data));
      }

      case MSG_INSERTMODE:
      {
         /*
          * Change the character insertion mode.
          * The 'textEdit' parameter must contain the editor instance
          * pointer, while the 'data' parameter will be interpreted
          * as an INT8 value, specifying the type of insert mode
          * under which the instance will operate.
          */
         return ((xrEditor *) teINSERTMODE_handler (textEdit, (INT8) data));
      }

      case MSG_DEACTIVATE:
      {
         /*
          * Force the text edit field inactive.
          * The 'textEdit' parameter must contain the editor instance
          * pointer; the 'data' parameter is unused.
          */
         xrTextEditData * teDataPtr;

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

         teDataPtr = (xrTextEditData *) textEdit->editorData;

         if (teDataPtr->teCursorOn)
         {
            teDataPtr->teCursorOn = FALSE;
            drawTextEdit (textEdit, XrINITANDDRAWALL);
         }

         return (textEdit);
      }

      case MSG_ACTIVATE:
      {
         /*
          * Activate the instance, and start waiting for an
          * input event to be received.  The 'textEdit' parameter 
          * must contain the editor instance pointer, while the 
          * 'data' parameter is unused.
          */
         return (teActivate (textEdit));
      }

      case MSG_EDIT:
      {
	 /*
          * Process the incoming keystroke, and generate a return
          * keystroke, to indicate to the application program how the
          * editor instance was modified.  The 'textEdit' parameter 
          * must contain the editor instance pointer; the 'data' parameter 
          * must point to an XEvent structure, containing some X event.
	  */
         if ((textEdit == NULL) || (data == NULL) ||
            ((textEdit->editorState & (XrSENSITIVE | XrVISIBLE)) !=
            (XrSENSITIVE | XrVISIBLE)))
            return ((xrEditor *) NULL);
         
         return (processTextEdit (textEdit, data));
      }

      default:
         /* All other commands are invalid */
         xrErrno = XrINVALIDMSG;
         return ((xrEditor *)NULL);

   }  /* end of switch */
}  /* end of XrTextEdit() */


/*************************************<->*************************************
 *
 *  teFreeMemory (teDataPtr)
 *
 *   Description:
 *   -----------
 *     xxxxxxxxxxxxxxxxxxxxxxx
 *
 *
 *   Inputs:
 *   ------
 *     xxxxxxxxxxxx = xxxxxxxxxxxxx
 * 
 *   Outputs:
 *   -------
 *     xxxxxxxxxxxx = xxxxxxxxxxxxx
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
teFreeMemory (teDataPtr)

   xrTextEditData * teDataPtr;

{
   if (teDataPtr->teTileId != xrDefaultTile)
      XFreePixmap (teDataPtr->teTileId);
}


/*************************************<->*************************************
 *
 *  INT32
 *  sizeTextEdit (teInfoPtr, rectPtr)
 *
 *  xrTextEditInfo * teInfoPtr;
 *  RECTANGLE      * rectPtr;
 *
 *   Description:
 *   -----------
 *     This routine takes the text edit specifications, contained in the
 *     'info' structure pointed to by the 'teInfoPtr' parameter, and
 *     calculates the size of the smallest rectangle needed to contain
 *     the instance.
 *
 *
 *   Inputs:
 *   ------
 *     teInfoPtr = This points to an 'info' structure, containing the
 *                 specifics describing the hypothetical instance.  Only
 *                 the 'cellWidth', 'editorFont', 'maxChars' and 'label'
 *                 fields need to be initialized.
 *
 *     rectPtr = The minimally sized 0-based rectangle needed to contain
 *               the instance will be returned in the structure pointed to
 *               by this parameter.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, TRUE is returned, along with the
 *          rectangle definition.
 *
 *     Upon failure, FALSE is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   _XrTextInfo()    [textUtil.c]
 *   XrStringWidth()  [utilities.c]
 *   XrSetRect()      [calc.c]
 *
 *************************************<->***********************************/

static
INT32
sizeTextEdit (teInfoPtr, rectPtr)

   xrTextEditInfo * teInfoPtr;
   RECTANGLE      * rectPtr;

{
   INT16        height;
   INT16        width;
   INT16        stringWidth;
   INT16        labelWidth;
   FontInfo   * fontPtr;
   xrTextInfo   fontData;
   INT8         cellWidth;

   /* Request extra information for the font we will be using. */
   fontPtr = (teInfoPtr->editorFont) ? teInfoPtr->editorFont : xrBaseFontInfo;
   _XrTextInfo (fontPtr, &fontData);

   /* Determine what character cell width to use */
   if (teInfoPtr->cellWidth == XrMAXWIDTH)
      cellWidth = fontData.maxWidth;
   else if (teInfoPtr->cellWidth == XrAVGWIDTH)
      cellWidth = fontData.avgWidth;
   else if (teInfoPtr->cellWidth < 0)
   {
      xrErrno = XrPARMOUTOFRANGE;
      return (FALSE);
   }
   else
      cellWidth = teInfoPtr->cellWidth;

   /*
    * Calculate the width of the label and the largest 
    * text string, including space for the cursor.
    */
   stringWidth = (teInfoPtr->maxChars * cellWidth) +
                 XrStringWidth (fontPtr, " ", 1, 0, 0) + 1;
   if (teInfoPtr->label)
      labelWidth = XrStringWidth (fontPtr, teInfoPtr->label, 
                                  XrNULLTERMINATED, 0, 0);
   else
      labelWidth = 0;

   /* 
    * We now have enough information to calculate the editor rectangle.
    *
    *   width = label width + labelPadding + border width + padding +
    *           string width + padding + border width.
    *           [ For the width, padding = 2 pixels ]
    *
    *   height = border height + padding + font height + padding +
    *            border height.
    *           [ For the height, padding = font Leading value ]
    */
   width = labelWidth + fontData.avgWidth + BORDER + PADDING + stringWidth + 
           PADDING + BORDER;
   height = BORDER + fontData.leading + fontData.ascent + fontData.descent + 
           fontData.leading + BORDER;

   XrSetRect (rectPtr, 0, 0, width, height);
   return (TRUE);
}

/*************************************<->*************************************
 *
 *  createTextEdit (teDataPtr, teInfoPtr, message)
 *
 *     xrTextEditData * teDataPtr;
 *     xrTextEditInfo * teInfoPtr;
 *     INT32            message;
 *
 *   Description:
 *   -----------
 *   This routine is used both during the creation of a new text edit
 *   instance, and during the resizing of an existing instance.  In
 *   either case, it expects the 'teInfoPtr' parameter to point to
 *   a filled out instance of the the 'info' structure.  Using this
 *   information, it will calculate the size and location of each
 *   component of the instance being created/resized; all of this
 *   information will be stored in the 'data' structure pointed to
 *   by the 'teDataPtr' parameter.
 *
 *
 *   Inputs:
 *   ------
 *     teDataPtr = This points to the 'data' structure, into which the
 *                 component information is to be saved.
 *
 *     teInfoPtr = This must point to an instance of the 'info' structure.
 *                 In the case of MSG_NEW, it must be completely filled out;
 *                 for MSG_RESIZE, only the 'editorRect', 'editorFont',
 *                 'label', 'maxChars', 'labelPosition' and 'cellWidth'
 *                 fields must be filled.
 *
 *     message = This must be set to either MSG_NEW or MSG_RESIZE, depending
 *               upon whether the instance is being created or resized.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, TRUE is returned, and the 'data' 
 *          structure is filled out.
 *
 *     Upon failure, FALSE is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   _XrTextInfo()    [textUtil.c]
 *   XrSTringWidth()  [utilities.c]
 *   XrSetPt()        [calc.c]
 *   XrSetRect()      [calc.c]
 *   XrResource()     [resource.c]
 *   XMakePixmap()    [libX.a]
 *   sizeTextEdit()
 *
 *************************************<->***********************************/

static
INT32
createTextEdit (teDataPtr, teInfoPtr, message)

   register xrTextEditData * teDataPtr;
   register xrTextEditInfo * teInfoPtr;
            INT32            message;

{
   register RECTANGLE    * editorRect;
            INT16          textBaseLine;
            INT16          maxTextWidth;
            FontInfo     * fontPtr;
            xrResourceInfo bitmapInfo;
            RECTANGLE      workRect;
            INT16          x;
            INT16          width;

   if (message == MSG_NEW)
   {
      /* Validate incoming parameters */
      if (teInfoPtr->string == NULL)
      {
         xrErrno = XrINVALIDPTR;
         return (FALSE);
      }
      else if (teInfoPtr->maxChars <= 0)
      {
         xrErrno = XrINVALIDPARM;
         return (FALSE);
      }
      else if ((teInfoPtr->label) && strlen(teInfoPtr->label) &&
              (teInfoPtr->labelPosition != XrLEFT_ALIGNED) &&
              (teInfoPtr->labelPosition != XrRIGHT_ALIGNED))
      {
         xrErrno = XrINVALIDOPTION;
         return (FALSE);
      }
   }

   /* Make sure the specified rectangle is not too small */
   if (sizeTextEdit (teInfoPtr, &workRect) == FALSE)
      return (FALSE);

   editorRect = &teInfoPtr->editorRect;
   if ((editorRect->width < workRect.width) ||
       (editorRect->height != workRect.height))
   {
      xrErrno = XrINVALIDRECT;
      return (FALSE);
   }

   /* Get the font description */
   fontPtr = (teInfoPtr->editorFont) ? teInfoPtr->editorFont : xrBaseFontInfo;
   _XrTextInfo (fontPtr, &teDataPtr->teFont);

   /* Get the size of the block cursor */
   teDataPtr->teSpaceWidth = XrStringWidth (fontPtr, " ", 1, 0, 0);

   /* Determine the character cell width to use */
   if (teInfoPtr->cellWidth == XrMAXWIDTH)
      teDataPtr->teCellWidth = teDataPtr->teFont.maxWidth;
   else if (teInfoPtr->cellWidth == XrAVGWIDTH)
      teDataPtr->teCellWidth = teDataPtr->teFont.avgWidth;
   else
      teDataPtr->teCellWidth = teInfoPtr->cellWidth;

   /*
    * Determine the size of the text editing region, including space
    * for the block cursor at the end.
    */
   maxTextWidth = (teInfoPtr->maxChars * teDataPtr->teCellWidth) +
                  teDataPtr->teSpaceWidth + 1;

   /*
    * Determine the base line to be used when displaying both the label
    * and the text string.  It is calculated as follows:
    *
    *   baseLine = top of editor rectangle + border height + padding 
    */
   textBaseLine = editorRect->y + BORDER + teDataPtr->teFont.leading;

   /* 
    * Set the starting point for the label string. 
    * It is calculated as follows:
    *
    *    if (left aligned)
    *       x = left edge of editor rectangle
    *    else
    *       x = right edge of editor rectangle - label width
    *    y = textBaseLine.
    */
   if (teInfoPtr->labelPosition == XrLEFT_ALIGNED)
      x = editorRect->x;
   else
   {
      x = (editorRect->x + editorRect->width - 1) -
          XrStringWidth (fontPtr, teInfoPtr->label, XrNULLTERMINATED, 0, 0);
   }
   XrSetPt (&teDataPtr->teLabelPt, x, textBaseLine);

   /*
    * Determine the size and location of the text region frame:
    * It is calculate as follows:
    *
    *    if (left aligned)
    *       x   = right edge of editor rectangle - border width - 
    *             padding - width of the text region - padding - border
    *    else
    *       x   = left edge of the editor rectangle
    *    y      = top of the editor rectangle
    *    width  = border width + padding + width of the text region +
    *             padding + border width
    *    height = height of the editor rectangle
    */
   if (teInfoPtr->labelPosition == XrLEFT_ALIGNED)
      x = (editorRect->x+editorRect->width-1) - (BORDER << 1) - 
          (PADDING << 1) - maxTextWidth;
   else
      x = editorRect->x;
   width = maxTextWidth + (BORDER << 1) + (PADDING << 1);
   XrSetRect (&teDataPtr->teFrameRect, x, editorRect->y,
              width, editorRect->height);

   /*
    * Set the starting point for the editable text string.
    * It is calculated as follows:
    *
    *    x = left edge of frame rectangle + border width + padding
    *    y = textBaseLine
    */
   XrSetPt (&teDataPtr->teTextPt,
            teDataPtr->teFrameRect.x + BORDER + PADDING, textBaseLine);

   if (message == MSG_NEW)
   {
      teDataPtr->teFGColor = (teInfoPtr->editorFGColor == -1) ?
                 xrForegroundColor : teInfoPtr->editorFGColor;
      teDataPtr->teBGColor = (teInfoPtr->editorBGColor == -1) ?
                 xrBackgroundColor : teInfoPtr->editorBGColor;

      /* Create the 50% tile used when drawing insensitive text editor */
      if ((teInfoPtr->editorFGColor == -1) &&
          (teInfoPtr->editorBGColor == -1))
      {
         /* Use the default 50% tile */
         teDataPtr->teTileId = xrDefaultTile;
      }
      else
      {
         bitmapInfo.resourceType = XrTYPE_BITMAPID;
         bitmapInfo.resourceId = XrPERCENT50;
         if ((XrResource (MSG_FIND, &bitmapInfo) == FALSE) ||
            ((teDataPtr->teTileId = 
            XMakePixmap (((xrBitmapId *)bitmapInfo.resourceObject)->bitmapId,
                          teDataPtr->teFGColor, teDataPtr->teBGColor)) == 0))
         {
            /* Unable to create the tile */
            xrErrno = XrXCALLFAILED;
            return (FALSE);
         }
      }

      /* Fill in the rest of the fields in the text edit structures */
      teDataPtr->teLabel = teInfoPtr->label;
      teDataPtr->teString = teInfoPtr->string;
      teDataPtr->teMaxChars = teInfoPtr->maxChars;
      teDataPtr->teInsertPos = teInfoPtr->insertPos;
      teDataPtr->teCursorOn = FALSE;
      teDataPtr->teLabelPosition = teInfoPtr->labelPosition;
      teDataPtr->teInsertMode = teInfoPtr->insertMode;
      teDataPtr->teInsertState = FALSE;
   }

   return (TRUE);
}


/*************************************<->*************************************
 *
 *  INT32
 *  drawTextEdit (textEdit, components)
 *
 *     xrEditor * textEdit;
 *     INT32      components;
 *
 *   Description:
 *   -----------
 *     This routine will draw either all or just a portion of an existing
 *     text edit instance.  It is capable of making the complete instance
 *     invisible, or drawing the complete instance, or drawing just the
 *     editing string.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the instance which
 *                is to be drawn.
 *
 *     components = This specifies which pieces of the instance are to
 *                  be drawn.  If set to XrINITANDDRAWALL, then the
 *                  required graphic context structure will first be
 *                  initialized, and then the complete instance will be
 *                  drawn.  If set to XrALLCOMPONENTS, then it will be
 *                  assumed that the graphic contexts have already been
 *                  set, and the complete instance will be drawn.  If
 *                  set to XrSTRINGONLY, then it will be assumed that the
 *                  graphic contexts have already been set, and only the
 *                  editing string will be drawn.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrMakeInvisible()       [editorUtil.c]
 *   _XrImageText8()          [textUtil.c]
 *   XrBorderFillRectangle()  [rectUtil.c]
 *   XrFillRectangle()        [rectUtil.c]
 *   XrInsetRect()            [calc.c]
 *   XrCopyRect()             [calc.c]
 *   drawCursor()
 *   teInitGCs()
 *
 *************************************<->***********************************/

static
INT32
drawTextEdit (textEdit, components)

   xrEditor * textEdit;
   INT32      components;

{
   register xrTextEditData   * teDataPtr;
            Window             windowId;
            INT32              sensitive;
            RECTANGLE          frameRect;
            RECTANGLE          workRect;
            INT32              borderGC;
            INT16              delta;
            INT16              oldFrameX;
            INT32              stringLen;
   register INT32              visibleChars;
            INT16              rightEdge;
            FontInfo         * fontPtr;

   /* Initialize variables */
   teDataPtr = (xrTextEditData *) textEdit->editorData;
   windowId = textEdit->editorWindowId;

   /*
    * If the instance is not visible, then fill its area with the
    * background tile for the window, thus making the instance invisible.
    */
   if (!(textEdit->editorState & XrVISIBLE))
   {
      _XrMakeInvisible (windowId, &textEdit->editorRect, TRUE);
      return;
   }

   if (components == XrINITANDDRAWALL)
   {
      /* Initialize the graphic contexts */
      teInitGCs (teDataPtr);
      components = XrALLCOMPONENTS;
   }

   sensitive = (textEdit->editorState & XrSENSITIVE) ? TRUE : FALSE;
   XrCopyRect (&teDataPtr->teFrameRect, &frameRect);

   /* Determine which components of the instance are to be drawn */
   if (components == XrALLCOMPONENTS)
   {
      /* Display the label */
      if (teDataPtr->teLabel)
         _XrImageText8 (windowId, xrEditorGC1, XrNULLTERMINATED,
                        teDataPtr->teLabelPt.x, teDataPtr->teLabelPt.y,
                        teDataPtr->teLabel);

      /*
       * Adjust the definition of the frame rectangle, to take into
       * account the border width.
       */
      if (teDataPtr->teCursorOn)
      {
         frameRect.width -= BORDER;
         frameRect.height -= BORDER;
         borderGC = xrEditorGC3;
      }
      else
      {
         _XrMakeInvisible (windowId, &frameRect, TRUE);
         XrInsetRect (&frameRect, BORDER, BORDER);
         borderGC = xrEditorGC1;
      }

      /* Draw the text edit region and frame */
      if (textEdit->editorState & XrSENSITIVE)
         _XrBorderFillRectangle (windowId, borderGC, xrEditorGC2,
                                 &frameRect);
      else
         _XrBorderFillRectangle (windowId, borderGC, xrEditorGC3,
                                 &frameRect);
   }

   if (components & XrSTRINGONLY)
   {
      /* If the text string is too long, then truncate it */
      stringLen = strlen (teDataPtr->teString);
      if (stringLen > teDataPtr->teMaxChars)
      {
         teDataPtr->teString[teDataPtr->teMaxChars] = NULL;
         stringLen = teDataPtr->teMaxChars;
      }
      if (teDataPtr->teInsertPos > stringLen)
         teDataPtr->teInsertPos = stringLen;

      /* Determine how many character can be displayed */
      visibleChars = stringLen;
      rightEdge = teDataPtr->teFrameRect.x + teDataPtr->teFrameRect.width -
                  (BORDER + PADDING);
      fontPtr = teDataPtr->teFont.fontInfo;
      while ((teDataPtr->teTextPt.x + XrStringWidth (fontPtr,
              teDataPtr->teString, visibleChars, 0, 0)) > rightEdge)
      {
         visibleChars--;
      }

      /* Display the text string */
      if (sensitive)
         _XrImageText8 (windowId, xrEditorGC1, visibleChars,
                     teDataPtr->teTextPt.x, teDataPtr->teTextPt.y,
                     teDataPtr->teString);
      else
      {
         /* Display as patterned out text string */
         _XrImageText8 (windowId, xrEditorGC4, visibleChars,
                     teDataPtr->teTextPt.x, teDataPtr->teTextPt.y,
                     teDataPtr->teString);
         _XrImageText8 (windowId, xrEditorGC5, visibleChars,
                     teDataPtr->teTextPt.x, teDataPtr->teTextPt.y,
                     teDataPtr->teString);
      }

      /* Blank out past the end of the text string */
      if (components == XrSTRINGONLY)
      {
         XrInsetRect (&frameRect, (BORDER -1) << 1, (BORDER -1) << 1);
         delta = XrStringWidth (fontPtr,teDataPtr->teString,visibleChars,0,0);
         oldFrameX = frameRect.x;
         frameRect.x = teDataPtr->teTextPt.x + delta;
         frameRect.width -= (frameRect.x - oldFrameX);

         /* Don't overwrite the cursor, if at end of the field */
         if ((teDataPtr->teInsertPos == stringLen) &&
             (teDataPtr->teInsertPos < visibleChars))
         {
            if ((teDataPtr->teInsertMode == XrALWAYS_ON) ||
               ((teDataPtr->teInsertMode == XrINTERACTIVE) &&
               (teDataPtr->teInsertState)))
            {
               /* Currently using the insert mode cursor */
            }
            else
            {
               frameRect.x += teDataPtr->teSpaceWidth;
               frameRect.width -= teDataPtr->teSpaceWidth;
            }
         }

         if (sensitive)
            _XrFillRectangle (windowId, xrEditorGC2, &frameRect);
         else
            _XrFillRectangle (windowId, xrEditorGC3, &frameRect);
      }

      if (teDataPtr->teCursorOn)
      {
         /* Display the text cursor */
         drawCursor (windowId, textEdit, TRUE);
      }
   }
}


/*************************************<->*************************************
 *
 *  xrEditor *
 *  processTextEdit (textEdit, eventPtr)
 *
 *   Description:
 *   -----------
 *     This is the main event processing routine the the text editor.
 *     When it first receives an event, it will call the various
 *     sub-event handlers (ascii, traversal, editing and button)
 *     and will give them the opportunity to handle the event.  If the
 *     event is handled, then it will read the input queue, and wait
 *     for the next event.  This continues until either a permanent
 *     exit condition occurs (a SELECT outside the instance, or a
 *     traversal event), or a temporary break condition occurs (one
 *     of the status events occurs, or an unknown event is received).
 *     When the sub-event handler is called, it is passed some information
 *     describing the new event, along with a pointer to a partially filled 
 *     out X-ray event structure; if the sub-event handler plans to exit,
 *     it fills out the rest of the X-ray event, pushes it onto the
 *     input queue, and returns a value of TRUE.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the instance to which
 *                the input event is to be applied.
 *
 *     eventPtr = This is a pointer to an XEvent structure, containing the
 *                event to be processed.
 * 
 *   Outputs:
 *   -------
 *     Upon exiting, if any events were handled, then TRUE is returned,
 *          along with anywhere from 1 to 2 events pushed onto the front
 *          of the input queue.
 *
 *     If the initial event received (pointed to by 'eventPtr') is not
 *        handled, then NULL is returned.
 *
 *   Procedures Called
 *   -----------------
 *   XrInput()          [input.c]
 *   teInitGCs()
 *   teButtonEvent()
 *   teTraversalEvent()
 *   teAsciiEvent()
 *   teEditEvent()
 *
 *************************************<->***********************************/

static
xrEditor *
processTextEdit (textEdit, eventPtr)

   register xrEditor * textEdit;
   register XEvent   * eventPtr;

{
   register xrTextEditData  * teDataPtr;
            xrEvent           teEvent;
            INT8              exitFlag;
            UINT16            code;
            INT8            * string;
            INT32             byteCount;

   teDataPtr = (xrTextEditData *) textEdit->editorData;

   /* Initialize the return event structure */
   teEvent.type = XrXRAY;
   teEvent.source = eventPtr->window;
   teEvent.inputType = XrEDITOR;
   teEvent.inputCode = XrTEXTEDIT;
   teEvent.valuePtr = (INT32) textEdit;

   teInitGCs (teDataPtr);

   /* Keep processing until a terminating event occurs */
   while (1)
   {
      exitFlag = FALSE;

      if ((eventPtr->type == ButtonPressed) || 
          (eventPtr->type == ButtonReleased))
      {
         teButtonEvent (textEdit, teDataPtr, eventPtr, &teEvent, &exitFlag);
         if (exitFlag)
            return (textEdit);
      }
      else if (eventPtr->type == KeyPressed)
      {
         string = XrInputMap (eventPtr, &byteCount);
         if (byteCount == 1)
            code = *string & 0xFF;
         else if (byteCount == 2)
            code = (*string << 8) + *(string + 1);

         if (byteCount == 1 || byteCount == 2)
         {
            if (teTraversalEvent (textEdit, teDataPtr, code, &teEvent))
            {
               return (textEdit);
            }
            else if (teAsciiEvent (textEdit, teDataPtr, code, eventPtr, 
                                   &teEvent, &exitFlag))
            {
               if (exitFlag)
                  return (textEdit);
            }
            else if (teEditEvent (textEdit,teDataPtr,code,&teEvent,&exitFlag))
            {
               if (exitFlag)
                  return (textEdit);
            }
         }
      }
      else if (eventPtr->type == KeyReleased)
      {
         /* Swallow all of these events */
      }
      else
      {
         if (teDataPtr->teCursorOn)
         {
            /*
             * Unknown Event:
             * Push the unknown event onto the input queue, along with
             * a text edit event, and return to the application.
             */
            XrInput (NULL, MSG_PUSHEVENT, eventPtr);
            teEvent.value1 = XrUNKNOWN_EVENT;
            XrInput (NULL, MSG_PUSHEVENT, &teEvent);
            return (textEdit);
         }
         else
            return ((xrEditor *) NULL);
      }

      /* Grab the next input event */
      while (1)
      {
         if ((XrInput (NULL, MSG_BLKREAD, eventPtr) != FALSE) &&
             (eventPtr->window == textEdit->editorWindowId))
            break;
      }

   }   /* End of while loop */
}


/*************************************<->*************************************
 *
 *  INT32
 *  teButtonEvent (textEdit, teDataPtr, event, returnEvent, exitFlag)
 *
 *     xrEditor       * textEdit;
 *     xrTextEditData * teDataPtr;
 *     XButonEvent    * event;
 *     xrEvent        * returnEvent;
 *     INT8             exitFlag;
 *
 *   Description:
 *   -----------
 *     This routine takes the 'event', and first determines if it is a
 *     button event; if not, it returns and does nothing.  If it is, then
 *     it checks to see if it is a SELECT or SELECTUP event; if not, then
 *     it is treated as an unknown event, and both the event and an
 *     X-ray 'unknown' event will be pushed onto the input queue, and the
 *     application will be given the change to handle it.  The SELECTUP
 *     will be swallowed.  If this is a SELECT event, then a check will be
 *     made to see if it occurred within the instance.  If it did, then the
 *     cursor will be moved to the character cell nearest to where the
 *     select occurred.  If the select is outside the instance, then the
 *     select event will be pushed back onto the input queue, along with
 *     another event indicating that the instance is going inactive, and
 *     the instance will then be drawn as inactive; the application is
 *     then given the opportunity to handle the select event.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the text edit instance
 *                to which the event is to be applied.
 *
 *     teDataPtr = This is a pointer to the 'data' structure associated
 *                 with the instance.
 *
 *     event = This is a pointer to the X event to be processed.
 *
 *     returnEvent = This is a pointer to a partially filled out X-ray
 *                   event structure.  If we need to push an X-ray event
 *                   onto the front of the input queue, we simply fill
 *                   out any additional fields necessary, and then use
 *                   this.
 *
 *     exitFlag = This allows us to signal to the main event handler that
 *                the event just handled is causing us to exit and return
 *                to the application; this is set when either an unknown
 *                event is received, or a select outside the instance
 *                occurs.
 * 
 *   Outputs:
 *   -------
 *     Upon completion, 'exitFlag' will be set as described above, and
 *          it is possible that 1 or more events have been pushed onto
 *          the front of the input queue.
 *
 *   Procedures Called
 *   -----------------
 *   XrMapButton()    [utilities.c]
 *   XrSetPt()        [calc.c]
 *   XrInput()        [input.c]
 *   XrStringWidth()  [utilities.c]
 *   XFlush()         [libX.a]
 *   drawCursor()
 *   drawTextEdit()
 *
 *************************************<->***********************************/

static
INT32
teButtonEvent (textEdit, teDataPtr, event, returnEvent, exitFlag)

   xrEditor       * textEdit;
   xrTextEditData * teDataPtr;
   XButtonEvent   * event;
   xrEvent        * returnEvent;
   INT8           * exitFlag;

{
   register INT32 i;
            POINT cursorPos;
            INT16 stringWidth;

      if (XrMapButton (XrSELECT, event))
      {
         /* If the select is outside the editor's rectangle, then break */
         XrSetPt (&cursorPos, event->x, event->y);
         if (!XrPtInRect (&cursorPos, &textEdit->editorRect))
         {
            /* 
             * Return two events: the select which is causing us to
             * exit, followed by the break notification.
             */
            XrInput (NULL, MSG_PUSHEVENT, event);
      
            returnEvent->value1 = XrTEDIT_BREAK;
            returnEvent->value2 = XrSELECT;
            XrInput (NULL, MSG_PUSHEVENT, returnEvent);
            
            /* Redraw the field as inactive */
            teDataPtr->teCursorOn = FALSE;
            drawTextEdit (textEdit, XrALLCOMPONENTS);
            XFlush();
            *exitFlag = TRUE;
            return;
         }
      
         /* Determine where to place the insertion bar */
         for (i = 1; i <= strlen(teDataPtr->teString); i++)
         {
            stringWidth = XrStringWidth (teDataPtr->teFont.fontInfo,
                                         teDataPtr->teString, i, 0, 0);
      
            if (cursorPos.x < teDataPtr->teTextPt.x + stringWidth)
               break;
         }
      
         /*
          * Update the insertion bar location, force the field
          * active, and redraw the instance.
          */
         if (!teDataPtr->teCursorOn)
         {
            teDataPtr->teInsertPos = i - 1;
            teDataPtr->teCursorOn = TRUE;
            drawTextEdit (textEdit, XrALLCOMPONENTS);
         }
         else
         {
            /* Just move the cursor */
            drawCursor (textEdit->editorWindowId, textEdit, FALSE);
            teDataPtr->teInsertPos = i - 1;
            drawCursor (textEdit->editorWindowId, textEdit, TRUE);
         }
      
         return;
      }
      else if (XrMapButton (XrSELECTUP, event))
      {
         /* Swallow a selectup event */
         return;
      }
      else
      {
         /*
          * Treat all other events as unknown events.
          * Push it back on the input queue, and add
          * a text edit break event afterwards.
          */
         XrInput (NULL, MSG_PUSHEVENT, event);
         returnEvent->value1 = XrUNKNOWN_EVENT;
         XrInput (NULL, MSG_PUSHEVENT, returnEvent);
            
         *exitFlag = TRUE;
         return;
      }
}


/*************************************<->*************************************
 *
 *  INT32
 *  teAsciiEvent (textEdit, teDataPtr, keyCode, eventPtr, returnEvent, exitFlag)
 *
 *     xrEditor       * textEdit;
 *     xrTextEditData * teDataPtr;
 *     UINT16           keyCode;
 *     XButtonEvent   * eventPtr;
 *     xrEvent        * returnEvent;
 *     INT8             exitFlag;
 *
 *   Description:
 *   -----------
 *     This routine takes the keycode, and first determines if it maps to
 *     an ascii key; if not, it returns without doing anything.  If it
 *     is, and if it is printable then the character is added to the
 *     editing string, and the cursor is updated; if the field is full,
 *     or if the character is unprintable, then it is swallowed.  If this
 *     is the first character in the field, then a 'first character'
 *     status event will be pushed onto the input queue, and we will
 *     signal to the main handler that we need to return to the application.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the text edit instance
 *                to which the event is to be applied.
 *
 *     teDataPtr = This is a pointer to the 'data' structure associated
 *                 with the instance.
 *
 *     keyCode = This contains the keyCode into which the event mapped.
 *
 *     eventPtr = Pointer to the event containing the keyCode value.
 *
 *     returnEvent = This is a pointer to a partially filled out X-ray
 *                   event structure.  If we need to push an X-ray event
 *                   onto the front of the input queue, we simply fill
 *                   out any additional fields necessary, and then use
 *                   this.
 *
 *     exitFlag = This allows us to signal to the main event handler that
 *                the event just handled is causing us to exit and return
 *                to the application; this is set only upon receipt of
 *                the first character in the editing field.
 * 
 *   Outputs:
 *   -------
 *     If the keyCode is handled by this routine, then TRUE will be returned,
 *        and 'exitFlag' will be set as described above.  In addition, a
 *        single status event may be pushed onto the input queue.
 *
 *     If the keyCode is not handled here, then FALSE is returned.
 *
 *   Procedures Called
 *   -----------------
 *   XLookupMapping()  [libX.a]
 *   XrInput()         [input.c]
 *   drawTextEdit()
 *
 *************************************<->***********************************/

static
INT32
teAsciiEvent (textEdit, teDataPtr, keyCode, eventPtr, returnEvent, exitFlag)

            xrEditor         * textEdit;
   register xrTextEditData   * teDataPtr;
            UINT16             keyCode;
            XButtonEvent     * eventPtr;
            xrEvent          * returnEvent;
            INT8             * exitFlag;

{
            Window  windowId;
            UINT8   key;
   register INT32   cursorPos;
            INT32   stringLen;
            INT8    cursorOn;
   register INT32   i;

   /* Test to see if this is a regular ascii event */
   cursorPos = teDataPtr->teInsertPos;
   stringLen = strlen (teDataPtr->teString);
   cursorOn = teDataPtr->teCursorOn;
   windowId = textEdit->editorWindowId;

   /* Exit, if event is not an ascii key event */
   if (((keyCode & 0xFF00) == K_s) || (keyCode == '\010') ||
       (keyCode == '\177') || (keyCode == '\377'))
      return (FALSE);

   /* See if the key falls within the printable range */
   key = keyCode & 0xFF;
   if (key >= '\040')
   {
      if ((teDataPtr->teInsertMode == XrALWAYS_ON) ||
         ((teDataPtr->teInsertMode == XrINTERACTIVE) &&
         (teDataPtr->teInsertState)))
      {
         /*
          * Currently operating in insert mode.
          * See if the field is already full.
          */
         if ((cursorPos < teDataPtr->teMaxChars) && 
            (stringLen < teDataPtr->teMaxChars))
         {
            /* Shift characters to the right */
            for (i = stringLen + 1; i > cursorPos; i--)
               teDataPtr->teString[i] = teDataPtr->teString[i-1];

            teDataPtr->teString[cursorPos] = key;
            if (strlen(teDataPtr->teString) == 1)
            {
               returnEvent->value1 = XrTEDIT_FIRST;
               XrInput (NULL, MSG_PUSHEVENT, returnEvent);
               *exitFlag = TRUE;
            }

            /* Remove the old cursor */
            if (cursorOn)
               drawCursor (windowId, textEdit, FALSE);
            teDataPtr->teInsertPos++;
         }
         /* Field is already full */
         else if (cursorOn)
            return (TRUE);
      }
      else
      {
         /*
          * Currently operating in normal character insertion mode.
          * Add the character only if the field is not full.
          */
         if (cursorPos < teDataPtr->teMaxChars)
         {
            if (cursorPos == stringLen)
               teDataPtr->teString[cursorPos+1] = '\0';
            teDataPtr->teString[cursorPos] = key;
            if (strlen(teDataPtr->teString) == 1)
            {
               returnEvent->value1 = XrTEDIT_FIRST;
               XrInput (NULL, MSG_PUSHEVENT, returnEvent);
               *exitFlag = TRUE;
            }

            /* Remove the old cursor */
            if (cursorOn)
               drawCursor (windowId, textEdit, FALSE);
            teDataPtr->teInsertPos++;
         }
         /* Field is already full */
         else if (cursorOn)
            return (TRUE);
      }
   }
   else
   {
      /* Treat control characters as Unknown events */
      XrInput (NULL, MSG_PUSHEVENT, eventPtr);
      returnEvent->value1 = XrUNKNOWN_EVENT;
      XrInput (NULL, MSG_PUSHEVENT, returnEvent);
      *exitFlag = TRUE;

      if (cursorOn)
         return (TRUE);
   }

   /* Activate the field, if necessary, and redraw it */
   if (teDataPtr->teCursorOn == FALSE)
   {
      teDataPtr->teCursorOn = TRUE;
      drawTextEdit (textEdit, XrALLCOMPONENTS);
   }
   else
      drawTextEdit (textEdit, XrSTRINGONLY);

   return (TRUE);
}


/*************************************<->*************************************
 *
 *  INT32
 *  teEditEvent (textEdit, teDataPtr, keyCode, returnEvent, exitFlag)
 *
 *     xrEditor       * textEdit;
 *     xrTextEditData * teDataPtr;
 *     UINT16           keyCode;
 *     xrEvent        * returnEvent;
 *     INT8             exitFlag;
 *
 *   Description:
 *   -----------
 *     This routine is responsible for handling all of the editing keys.
 *     These are composed of the 'cursor right', 'cursor left', 'backspace',
 *     'delete char', 'delete line', 'clear line' and 'insert char' keys.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the text edit instance
 *                to which the event is to be applied.
 *
 *     teDataPtr = This is a pointer to the 'data' structure associated
 *                 with the instance.
 *
 *     keyCode = This is the keycode into which the event mapped.
 *
 *     returnEvent = This is a pointer to a partially filled out X-ray
 *                   event structure.  If we need to push an X-ray event
 *                   onto the front of the input queue, we simply fill
 *                   out any additional fields necessary, and then use
 *                   this.
 *
 *     exitFlag = This allows us to signal to the main event handler that
 *                the event just handled is causing us to exit and return
 *                to the application; this is set only when the event
 *                causes the editing field to become empty.
 * 
 *   Outputs:
 *   -------
 *     If the event is handled, then TRUE is returned, else FALSE.
 *
 *   Procedures Called
 *   -----------------
 *   XrInput()      [input.c]
 *   drawTextEdit()
 *   drawCursor()
 *
 *************************************<->***********************************/

static
INT32
teEditEvent (textEdit, teDataPtr, keyCode, returnEvent, exitFlag)

            xrEditor         * textEdit;
   register xrTextEditData   * teDataPtr;
            UINT16             keyCode;
            xrEvent          * returnEvent;
            INT8             * exitFlag;

{
   register INT32 cursorPos;
            INT32 stringLen;
            INT8  cursorOn;
            Window windowId;

   stringLen = strlen (teDataPtr->teString);
   cursorPos = teDataPtr->teInsertPos;
   cursorOn = teDataPtr->teCursorOn;
   windowId = textEdit->editorWindowId;

   if (keyCode == (CURS_RT_KEY | K_s))
   {
      /*
       * If not at the end of the string, then move the
       * cursor right one character position.
       */
      if (cursorPos < stringLen)
      {
         if (cursorOn)
         {
            drawCursor (windowId, textEdit, FALSE);
            teDataPtr->teInsertPos++;
            drawCursor (windowId, textEdit, TRUE);
            return (TRUE);
         }
         else
            teDataPtr->teInsertPos++;
      }
      else if (cursorOn)
         return (TRUE);
   }
   else if (keyCode == (CURS_LF_KEY | K_s))
   {
      /*
       * If not at the start of the string, then move
       * the cursor left one character position.
       */
      if (cursorPos > 0)
      {
         if (cursorOn)
         {
            drawCursor (windowId, textEdit, FALSE);
            teDataPtr->teInsertPos--;
            drawCursor (windowId, textEdit, TRUE);
            return (TRUE);
         }
         else
            teDataPtr->teInsertPos--;
      }
      else if (cursorOn)
         return (TRUE);
   }
   else if ((keyCode == (BS_KEY | K_s)) || (keyCode == '\010') ||
            (keyCode == '\177') || (keyCode == '\377'))
   {
      /*
       * If not at the start of the string, then delete the
       * character to the left of the cursor.
       */
      if (cursorPos > 0)
      {
         strcpy (teDataPtr->teString+cursorPos-1,teDataPtr->teString+cursorPos);
         teDataPtr->teInsertPos--;
         if (strlen (teDataPtr->teString) == 0)
         {
            returnEvent->value1 = XrTEDIT_EMPTY;
            XrInput (NULL, MSG_PUSHEVENT, returnEvent);
            *exitFlag = TRUE;
         }
      }
      else if (cursorOn)
         return (TRUE);
   }
   else if (keyCode == (DEL_CHAR_KEY | K_s))
   {
      /*
       * If not at the end of the string, then delete
       * the character under the cursor.
       */
      if (cursorPos < stringLen)
      {
         strcpy (teDataPtr->teString+cursorPos,teDataPtr->teString+cursorPos+1);
         if (strlen (teDataPtr->teString) == 0)
         {
            returnEvent->value1 = XrTEDIT_EMPTY;
            XrInput (NULL, MSG_PUSHEVENT, returnEvent);
            *exitFlag = TRUE;
         }
      }
      else if (cursorOn)
         return (TRUE);
   }
   else if (keyCode == (DEL_LINE_KEY | K_s))
   {
      /* Clear out the whole line */
      teDataPtr->teInsertPos = 0;
      teDataPtr->teString[0] = '\0';
      returnEvent->value1 = XrTEDIT_EMPTY;
      XrInput (NULL, MSG_PUSHEVENT, returnEvent);
      *exitFlag = TRUE;
   }
   else if (keyCode == (CLR_LINE_KEY | K_s))
   {
      /* Clear from the cursor to the end */
      teDataPtr->teString[cursorPos] = '\0';
      if (cursorPos == 0)
      {
         returnEvent->value1 = XrTEDIT_EMPTY;
         XrInput (NULL, MSG_PUSHEVENT, returnEvent);
         *exitFlag = TRUE;
      }
   }
   else if (keyCode == (INSERT_CHAR_KEY | K_s))
   {
      /*
       * Toggle into and out of insert mode, redrawing
       * the cursor to match the mode.
       */
      if (teDataPtr->teInsertMode == XrINTERACTIVE)
      {
         drawCursor (windowId, textEdit, FALSE);
         teDataPtr->teInsertState = teDataPtr->teInsertState ? FALSE : TRUE;
         drawCursor (windowId, textEdit, TRUE);
      }
      return (TRUE);
   }
   else
      /* Not an editing key */
      return (FALSE);

   /* Force the field active, and redraw it */
   if (!teDataPtr->teCursorOn)
   {
      teDataPtr->teCursorOn = TRUE;
      drawTextEdit (textEdit, XrALLCOMPONENTS);
   }
   else
      drawTextEdit (textEdit, XrSTRINGONLY);
 
   return (TRUE);
}


/*************************************<->*************************************
 *
 *  teTraversalEvent (textEdit, teDataPtr, keyCode, returnEvent)
 *
 *     xrEditor         * textEdit;
 *     xrTextEditData   * teDataPtr;
 *     UINT16             keyCode;
 *     xrEvent          * returnEvent;
 *
 *   Description:
 *   -----------
 *     This routine handles the 3 traversal keys: tab, backtab and return.
 *     When a traversal event occurs, the field will be drawn as inactive,
 *     and a traversal event will be pushed onto the front of the input
 *     queue; this is a permanent break condition.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the text edit instance
 *                to which the event is to be applied.
 *
 *     teDataPtr = This is a pointer to the 'data' structure associated
 *                 with the instance.
 *
 *     keyCode = This is the keyCode into which the event mapped.
 *
 *     returnEvent = This is a pointer to a partially filled out X-ray
 *                   event structure.  If we need to push an X-ray event
 *                   onto the front of the input queue, we simply fill
 *                   out any additional fields necessary, and then use
 *                   this.
 * 
 *   Outputs:
 *   -------
 *     If the event was a traversal key, then TRUE is returned, and a
 *        break event is added to the front of the input queue.
 *
 *     If this is not a traversal key, then FALSE is returned.
 *
 *   Procedures Called
 *   -----------------
 *   XFlush()        [libX.a]
 *   XrInput()       [input.c]
 *   drawTextEdit()
 *
 *************************************<->***********************************/

static
INT32
teTraversalEvent (textEdit, teDataPtr, keyCode, returnEvent)

            xrEditor         * textEdit;
            xrTextEditData   * teDataPtr;
   register UINT16             keyCode;
            xrEvent          * returnEvent;

{
   /*
    * Check for a forward or backward traversal request.
    */
   if ((keyCode == '\r') || (keyCode == '\n'))
      keyCode = RETURN_KEY | K_s;
   else if (keyCode == '\t')
      keyCode = TAB_KEY | K_s;

   if ((keyCode == (RETURN_KEY | K_s)) || (keyCode == (TAB_KEY | K_s)))
   {
      /* Traverse forward; turn off this field */
      teDataPtr->teCursorOn = FALSE;
      drawTextEdit (textEdit, XrALLCOMPONENTS);
      XFlush();
      returnEvent->value1 = XrTEDIT_BREAK;
      returnEvent->value2 = XrNEXT;
      returnEvent->value3 = keyCode;
      XrInput (NULL, MSG_PUSHEVENT, returnEvent);
      return (TRUE);
   }
   else if (keyCode == (BACKTAB_KEY | K_s))
   {
      /* Traverse backward; turn off this field */
      teDataPtr->teCursorOn = FALSE;
      drawTextEdit (textEdit, XrALLCOMPONENTS);
      XFlush();
      returnEvent->value1 = XrTEDIT_BREAK;
      returnEvent->value2 = XrPREVIOUS;
      returnEvent->value3 = keyCode;
      XrInput (NULL, MSG_PUSHEVENT, returnEvent);
      return (TRUE);
   }

   /* Else this is not a traversal key */
   return (FALSE);
}


/*************************************<->*************************************
 *
 *  drawCursor (windowId, textEdit, showing)
 *
 *     Window     windowId;
 *     xrEditor * textEdit;
 *     INT8       showing;
 *
 *   Description:
 *   -----------
 *     This routine turns the text cursor on and off, depending upon
 *     whether the 'showing' parameter is set to TRUE or FALSE.  The
 *     insert mode cursor is drawn as a 1 pixel wide vertical bar,
 *     while the normal mode cursor is drawn as a block.  If the
 *     cursor is currently outside the editing field, then no drawing
 *     occurs.
 *
 *
 *   Inputs:
 *   ------
 *     windowId = This is the window Id for the window in which the text
 *                edit instance resides.
 *
 *     textEdit = This is the editor instance pointer. 
 *
 *     showing = If the cursor is to be turned on, then this should be
 *               set to TRUE; the cursor is turned off when this is set
 *               to FALSE.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XrStringWidth()  [utilities.c]
 *   _XrLine()        [rectUtil.c]
 *   _XrImageText8()  [textUtil.c]
 *
 *************************************<->***********************************/

static
drawCursor (windowId, textEdit, showing)

   Window           windowId;
   xrEditor       * textEdit;
   INT8             showing;

{
   register xrTextEditData * teDataPtr;
   register xrTextInfo     * fontPtr;
            INT8             useBlankCursor = FALSE;
            INT8             cursorChar;
   register INT16            cursorX;
   register INT16            cursorY;

   teDataPtr = (xrTextEditData *) textEdit->editorData;
   fontPtr = &teDataPtr->teFont;

   /* Don't bother drawing if we're insensitive */
   if (! (textEdit->editorState & XrSENSITIVE))
      return;

   /*
    * Check for invalid cursor position, and also see if
    * the cursor is at the end of the text field; this lets
    * us know if we need to use a space character as the block
    * cursor.
    */
   if (teDataPtr->teInsertPos >= teDataPtr->teMaxChars)
   {
      teDataPtr->teInsertPos = teDataPtr->teMaxChars;
      useBlankCursor = TRUE;
   }
   else if (teDataPtr->teInsertPos >= strlen(teDataPtr->teString))
      useBlankCursor = TRUE;

   /* Determine the x position for the cursor */
   cursorX = teDataPtr->teTextPt.x + XrStringWidth (fontPtr->fontInfo,
          teDataPtr->teString, teDataPtr->teInsertPos, 0, 0);

   if ((teDataPtr->teInsertMode == XrALWAYS_ON) ||
     ((teDataPtr->teInsertMode == XrINTERACTIVE) && teDataPtr->teInsertState))
   {
      /* Currently operating in insert mode */
      cursorX -= 1;
      cursorY = teDataPtr->teFrameRect.y + BORDER + fontPtr->leading;

      /* Don't draw the cursor if it's outside the editing region */
      if (cursorX + 1 > (teDataPtr->teFrameRect.x + 
          teDataPtr->teFrameRect.width - 5))
         return;

      if (showing)
         _XrLine (windowId, xrEditorGC1, cursorX, cursorY, cursorX,
            cursorY + (fontPtr->ascent + fontPtr->descent - 1));
      else
         _XrLine (windowId, xrEditorGC2, cursorX, cursorY, cursorX,
            cursorY + (fontPtr->ascent + fontPtr->descent - 1));
   }
   else
   {
      /* Currently operating in normal mode */
      if (useBlankCursor)
         cursorChar = ' ';
      else
         cursorChar = teDataPtr->teString[teDataPtr->teInsertPos];

      /* Don't draw the cursor if it's outside the editing region */
      if ((cursorX + XrStringWidth (fontPtr->fontInfo, &cursorChar, 1, 0, 0)) > 
         (teDataPtr->teFrameRect.x + teDataPtr->teFrameRect.width-5))
        return;

      if (showing)
      {
         _XrImageText8 (windowId, xrEditorGC2, 1, cursorX, 
                     teDataPtr->teTextPt.y, &cursorChar);
         _XrLine (windowId, xrEditorGC1, cursorX - 1, 
                  teDataPtr->teTextPt.y, cursorX - 1, 
                  teDataPtr->teTextPt.y + fontPtr->ascent + 
                  fontPtr->descent - 1);
      }
      else
      {
         _XrImageText8 (windowId, xrEditorGC1, 1, cursorX, 
                     teDataPtr->teTextPt.y, &cursorChar);
         _XrLine (windowId, xrEditorGC2, cursorX - 1, 
                  teDataPtr->teTextPt.y, cursorX - 1, 
                  teDataPtr->teTextPt.y + fontPtr->ascent + 
                  fontPtr->descent - 1);
      }
   }
}


/*************************************<->*************************************
 *
 *  xrEditor *
 *  teActivate (textEdit)
 *
 *     xrEditor * textEdit;
 *
 *   Description:
 *   -----------
 *     This is the handler for the MSG_ACTIVATE message.  It will allow
 *     an existing text edit field to be activated, only if the instance
 *     is both visible and sensitive.  After verify the parameters and
 *     the instance's state, the instance will be redrawn as active, and
 *     then this routine will wait for an input event to be received.
 *     When an event is received, the main event handler will be called,
 *     and passed the event; it will then maintain control until an
 *     exit condition occurs.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the text edit instance
 *                which is to be activated.
 * 
 *   Outputs:
 *   -------
 *     If the field is successfully activated, then the instance pointer
 *        will be returned; otherwise, NULL will be returned.
 *
 *   Procedures Called
 *   -----------------
 *   XrInput()         [input.c]
 *   drawTextEdit()
 *   processTextEdit()
 *
 *************************************<->***********************************/

static
xrEditor *
teActivate (textEdit)

   register xrEditor * textEdit;

{
   xrTextEditData * teDataPtr;
   XEvent           event;

   /* Validate the instance pointer */
   if (textEdit == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrEditor *) NULL);
   }

   /* Fail, if the instance is not visible and sensitive */
   if ((textEdit->editorState & (XrVISIBLE | XrSENSITIVE)) !=
       (XrVISIBLE | XrSENSITIVE))
   {
      xrErrno = XrINVALIDPARM;
      return ((xrEditor *) NULL);
   }

   teDataPtr = (xrTextEditData *) textEdit->editorData;

   /* Activate the field, if not already */
   if (! (teDataPtr->teCursorOn))
   {
      teDataPtr->teCursorOn = TRUE;
      drawTextEdit (textEdit, XrINITANDDRAWALL);
   }
   else
   {
      teInitGCs (teDataPtr);
      drawTextEdit (textEdit, XrSTRINGONLY);
   }

   /* Wait for the initial input event */
   while (1)
   {
      if ((XrInput (NULL, MSG_BLKREAD, &event) != FALSE) &&
          (event.window == textEdit->editorWindowId))
         break;
   }

   /* Let the processing routine take it from here */
   return (processTextEdit (textEdit, &event));
}


/*************************************<->*************************************
 *
 *  xrEditor *
 *  teMOVE_handler (textEdit, ptPtr)
 *
 *     xrEditor * textEdit;
 *     POINT    * ptPtr;
 *
 *   Description:
 *   -----------
 *     This is the handler for the MSG_MOVE message.  It takes a new
 *     origin point, and offsets each component within the instance,
 *     such that the instance is now positioned with the upper left
 *     corner of the editor rectangle at the specified point.  In 
 *     addition, the editor group manager is invoked, to recalculate
 *     the editor group rectangle for the group in which this instance
 *     is a member.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the instance to be moved.
 *
 *     ptPtr = This points to a POINT structure, containing the new origin.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the instance pointer is returned,
 *          and the instance will be moved; it is redrawn only if it
 *          is visible.
 *
 *     Upon failure, NULL is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyRect()        [calc.c]
 *   XrOffsetRect()      [calc.c]
 *   XrOffsetPt()        [calc.c]
 *   _XrMakeInvisible()  [editorUtil.c]
 *   XrEditorGroup()     [group.c]
 *   drawTextEdit()
 *
 *************************************<->***********************************/

static
xrEditor *
teMOVE_handler (textEdit, ptPtr)

   register xrEditor * textEdit;
            POINT    * ptPtr;

{
   register xrTextEditData * teDataPtr;
            RECTANGLE        workRect;
   register INT16            xDelta;
   register INT16            yDelta;

   /* Check for invalid pointers */
   if (textEdit == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrEditor *) NULL);
   }
   else if (ptPtr == NULL)
   {
      xrErrno = XrINVALIDPTR;
      return ((xrEditor *) NULL);
   }

   /* Determine how much to relocate the instance by */
   teDataPtr = (xrTextEditData *) textEdit->editorData;
   XrCopyRect (&textEdit->editorRect, &workRect);
   xDelta = ptPtr->x - workRect.x;
   yDelta = ptPtr->y - workRect.y;

   /*
    * Relocate the following components:
    *        - the editor rectangle
    *        - the text frame rectangle
    *        - the label origin
    *        - the text string origin
    */
   XrOffsetRect (&textEdit->editorRect, xDelta, yDelta);
   XrOffsetRect (&teDataPtr->teFrameRect, xDelta, yDelta);
   XrOffsetPt (&teDataPtr->teLabelPt, xDelta, yDelta);
   XrOffsetPt (&teDataPtr->teTextPt, xDelta, yDelta);

   /* Redraw the instance, if visible */
   if (textEdit->editorState & XrVISIBLE)
   {
      /* Remove the instance from the window */
      _XrMakeInvisible (textEdit->editorWindowId, &workRect, TRUE);

      /* Redisplay the instance at its new location */
      drawTextEdit (textEdit, XrINITANDDRAWALL);
   }

   /* Force an editor group rectangle resize */
   XrEditorGroup (NULL, MSG_ADJUSTGROUPRECT, textEdit);
   return (textEdit);
}


/*************************************<->*************************************
 *
 *  xrEditor *
 *  teRESIZE_handler (textEdit, rectPtr)
 *
 *     xrEditor  * textEdit;
 *     RECTANGLE * rectPtr;
 *
 *   Description:
 *   -----------
 *     This is the handler routine for the MSG_RESIZE message.  It takes
 *     a rectangle definition, and relays an existing text edit instance,
 *     such that it now fits within the specified rectangle; all size
 *     restrictions which applied when the instance was first created are
 *     still enforced.  The resizing is accomplished by creating a fake
 *     'info' structure, and then calling createTextEdit(); it will fill
 *     in the 'data' structure with the size and position information for
 *     the instance.  Afterwards, the editor group handler will be called
 *     and given the opportunity to recalculate the group rectangle for
 *     the group in which this instance resides.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This instance pointer for the instance being resized.
 *
 *     rectPtr = A pointer to the new editor rectangle definition.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the instance pointer is returned, and
 *          the instance will be resized; it will be redrawn only if it
 *          is visible.
 *
 *     Upon failure, NULL is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyRect()        [calc.c]
 *   _XrMakeInvisible()  [editorUtil.c]
 *   XrEditorGroup()     [group.c]
 *   createTextEdit()
 *   drawTextEdit()
 *
 *************************************<->***********************************/

static
xrEditor *
teRESIZE_handler (textEdit, rectPtr)

   register xrEditor  * textEdit;
            RECTANGLE * rectPtr;

{
            RECTANGLE        workRect;
            xrTextEditInfo   teInfo;
   register xrTextEditData * teDataPtr;
   register xrTextEditInfo * teInfoPtr;

   if (textEdit == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrEditor *) NULL);
   }
   else if (rectPtr == NULL)
   {
      xrErrno = XrINVALIDPTR;
      return ((xrEditor *) NULL);
   }

   /* Create a pseudo Info structure */
   teDataPtr = (xrTextEditData *) textEdit->editorData;
   XrCopyRect (&textEdit->editorRect, &workRect);
   teInfoPtr = &teInfo;
   XrCopyRect (rectPtr, &teInfoPtr->editorRect);
   teInfoPtr->editorFont = teDataPtr->teFont.fontInfo;
   teInfoPtr->label = teDataPtr->teLabel;
   teInfoPtr->maxChars = teDataPtr->teMaxChars;
   teInfoPtr->labelPosition = teDataPtr->teLabelPosition;
   teInfoPtr->cellWidth = teDataPtr->teCellWidth;

   /* Allow createTextEdit() to do the calculations */
   if (createTextEdit (teDataPtr, teInfoPtr, MSG_RESIZE) == FALSE)
      /* Must have supplied an invalid rectangle definition */
      return ((xrEditor *) NULL);

   /* Save the new editor rectangle */
   XrCopyRect (&teInfoPtr->editorRect, &textEdit->editorRect);

   if (textEdit->editorState & XrVISIBLE)
   {
      /* Remove the instance from the window */
      _XrMakeInvisible (textEdit->editorWindowId, &workRect, TRUE);

      /* Redisplay the instance */
      drawTextEdit (textEdit, XrINITANDDRAWALL);
   }

   /* Force an editor group rectangle resize */
   XrEditorGroup (NULL, MSG_ADJUSTGROUPRECT, textEdit);
   return (textEdit);
}


/*************************************<->*************************************
 *
 *  xrEditor *
 *  teREDRAW_handler (textEdit, redrawMode)
 *
 *     xrEditor * textEdit;
 *     INT32      redrawMode;
 *
 *   Description:
 *   -----------
 *     This is the handler routine for the MSG_REDRAW message; it is
 *     capable of handling either an XrREDRAW_ALL or XrREDRAW_ACTIVE
 *     request.  These redraw the entire field, or just the editing
 *     string.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer for the instance to be
 *                redrawn.
 *
 *     redrawMode = This specifies the type of redraw to perform.  This
 *                  must be set to either of the values mentioned in the
 *                  description above.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the instance pointer is returned, and
 *          the instance is redrawn (if it was visible).
 *
 *     Upon failure, NULL is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   drawTextEdit()
 *   teInitGCs()
 *
 *************************************<->***********************************/

static
xrEditor *
teREDRAW_handler (textEdit, redrawMode)

   register xrEditor * textEdit;
            INT32      redrawMode;

{
   register xrTextEditData * teDataPtr;

   /* Validate the instance pointer and the redraw mode */
   if (textEdit == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrEditor *) NULL);
   }
   else if (redrawMode == XrREDRAW_ALL) 
   {
      if (textEdit->editorState & XrVISIBLE)
         drawTextEdit (textEdit, XrINITANDDRAWALL);
      return (textEdit);
   }
   else if (redrawMode == XrREDRAW_ACTIVE)
   {
      teDataPtr = (xrTextEditData *) textEdit->editorData;

      if (textEdit->editorState & XrVISIBLE)
      {
         teInitGCs (teDataPtr);
         drawTextEdit (textEdit, XrSTRINGONLY);
      }
      return (textEdit);
   }
   else
   {
      xrErrno = XrINVALIDOPTION;
      return ((xrEditor *) NULL);
   }
}


/*************************************<->*************************************
 *
 *  xrEditor *
 *  teINSERTMODE_handler (textEdit, insertMode)
 *
 *     xrEditor * textEdit;
 *     INT8       insertMode;
 *
 *   Description:
 *   -----------
 *     This is the handler for the MSG_INSERTMODE message.  It allows
 *     an application to change the way in which an existing text edit
 *     instance will respond to the insert mode key.  Insert mode may
 *     be forced on always, forced off always, or allowed to be 
 *     interactively modified.  After the mode has been changed, the
 *     cursor will be redrawn, if the instance was visible.
 *
 *
 *   Inputs:
 *   ------
 *     textEdit = This is the instance pointer.
 *
 *     insertMode = This specifies the type of insert mode the instance
 *                  should operated under.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the editor instance pointer is returned,
 *          and the new insert mode is set.
 *
 *     Upon failure, NULL is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   drawCursor()
 *
 *************************************<->***********************************/

static
xrEditor *
teINSERTMODE_handler (textEdit, insertMode)

   xrEditor * textEdit;
   INT8       insertMode;

{
   xrTextEditData * teDataPtr;

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

   if ((insertMode != XrALWAYS_ON) && 
       (insertMode != XrALWAYS_OFF) &&
       (insertMode != XrINTERACTIVE))
   {
      xrErrno = XrINVALIDOPTION;
      return ((xrEditor *) NULL);
   }

   teDataPtr = (xrTextEditData *) textEdit->editorData;

   /* Turn off the old cursor */
   if ((textEdit->editorState & XrVISIBLE) && (teDataPtr->teCursorOn))
      drawCursor (textEdit->editorWindowId, textEdit, FALSE);

   teDataPtr->teInsertMode = insertMode;
   teDataPtr->teInsertState = FALSE;

   /* Turn on the new cursor */
   if ((textEdit->editorState & XrVISIBLE) && (teDataPtr->teCursorOn))
      drawCursor (textEdit->editorWindowId, textEdit, TRUE);

   return (textEdit);
}


/*************************************<->*************************************
 *
 *  teInitGCs (teDataPtr)
 *
 *     xrTextEditData * teDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine initializes 5 graphics context structures.
 *     These include ones used for drawing the wide border, ones used
 *     for drawing a sensitive instance, and also ones used for drawing 
 *     the hashed text string displayed in an insensitive text edit instance.
 *
 *
 *   Inputs:
 *   ------
 *     teDataPtr = This points to the internal 'data' structure associated
 *                 with instance for which these graphic contexts are being
 *                 initialized; this structure contains the font and color
 *                 information.
 * 
 *   Outputs:
 *   -------
 *     The graphic contexts references by the defines xrEditorGC1,
 *     xrEditorGC2, xrEditorGC3, xrEditorGC4 and xrEditorGC5 will be set.
 *
 *   Procedures Called
 *   -----------------
 *   _XrCopyGC()    [gcUtil.c]
 *   _XrChangeGC()  [gcUtil.c]
 *
 *************************************<->***********************************/

static
teInitGCs (teDataPtr)

   register xrTextEditData * teDataPtr;

{
   INT32 changeList[21];
   INT32 changeMask;

   /* Initialize the standard graphic contexts we will be using */
   _XrInitEditorGCs (teDataPtr->teFGColor, teDataPtr->teBGColor,
                     teDataPtr->teFont.fontInfo->id);

   /*
    * Initialize the graphics context used to draw 
    * the background for an insensitive text edit instance,
    * and the wide border.
    */
   _XrCopyGC (xrDefaultGC, xrEditorGC3);
   changeList[XrFOREGROUNDVAL] = teDataPtr->teFGColor;
   changeList[XrBACKGROUNDVAL] = teDataPtr->teBGColor;
   changeList[XrLINEWIDTHVAL] = BORDER;
   changeList[XrTILEVAL] = teDataPtr->teTileId;
   changeList[XrFILLSTYLEVAL] = Tiled;
   changeMask = (XrTILE | XrFILLSTYLE | XrFOREGROUND | XrBACKGROUND |
                 XrLINEWIDTH);
   _XrChangeGC (xrEditorGC3, changeMask, changeList);

   /* 
    * Initialize the two graphics contexts used to draw an insensitive
    * editing string.
    */
   _XrCopyGC (xrDefaultGC, xrEditorGC4);
   changeList[XrFOREGROUNDVAL] = 0;
   changeList[XrBACKGROUNDVAL] = AllPlanes;
   changeList[XrFONTVAL] = teDataPtr->teFont.fontInfo->id;
   changeList[XrALUVAL] = GXand;
   changeMask = (XrFOREGROUND | XrBACKGROUND | XrFONT | XrALU);
   _XrChangeGC (xrEditorGC4, changeMask, changeList);
   _XrCopyGC (xrDefaultGC, xrEditorGC5);
   changeList[XrFOREGROUNDVAL] = teDataPtr->teFGColor;
   changeList[XrBACKGROUNDVAL] = 0;
   changeList[XrFONTVAL] = teDataPtr->teFont.fontInfo->id;
   changeList[XrALUVAL] = GXor;
   changeMask = (XrFOREGROUND | XrBACKGROUND | XrFONT | XrALU);
   _XrChangeGC (xrEditorGC5, changeMask, changeList);
}
