/*
 *	$Source: /u1/Xr/src/Xrlib/Editor/RCS/PageEdit.c,v $
 *	$Header: PageEdit.c,v 1.1 86/12/17 09:00:34 swick Exp $
 */

#ifndef lint
static char *rcsid_PageEdit_c = "$Header: PageEdit.c,v 1.1 86/12/17 09:00:34 swick Exp $";
#endif	lint


#include <Xr/xr-copyright.h>

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

static char rcsid[] = "$Header: PageEdit.c,v 1.1 86/12/17 09:00:34 swick Exp $";
/*************************************<+>*************************************
 *****************************************************************************
 **
 **   File:        PageEdit.c
 **
 **   Project:     X-ray Toolbox
 **
 **   Description: 
 **         Source code for the X-ray multi-line text edit field editor.
 **
 **
 **   ------------------------ MODIFICATION RECORD   ------------------------
 *
 * $Log:	PageEdit.c,v $
 * Revision 1.1  86/12/17  09:00:34  swick
 * Initial revision
 * 
 * Revision 7.0  86/11/13  08:22:17  08:22:17  fred ()
 * Final QA Release
 * 
 * Revision 6.0  86/11/10  15:27:37  15:27:37  fred ()
 * QA #2 release
 * 
 * Revision 5.4  86/11/07  14:19:18  14:19:18  fred ()
 * Added new copyright message.
 * 
 * Revision 5.3  86/11/05  08:46:03  08:46:03  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:47:34  11:47:34  fred ()
 * Added fix to treat DEL the same as BACKSPACE.
 * 
 * Revision 5.1  86/10/30  13:24:21  13:24:21  fred ()
 * Fixed cursor movement bug (using mouse), and also fixed the
 * handling of the RETURN key.
 * 
 * Revision 5.0  86/10/28  08:27:29  08:27:29  fred ()
 * QA #1.1 release
 * 
 * Revision 4.4  86/10/27  16:51:36  16:51:36  fred ()
 * Fixed but in NEXT PAGE code.
 * 
 * Revision 4.3  86/10/27  15:48:55  15:48:55  fred ()
 * Fixed bug introduced by previous update.
 * 
 * Revision 4.2  86/10/27  13:48:24  13:48:24  fred ()
 * Updated to match new XrInputMap() syntax.
 * 
 * Revision 4.1  86/10/23  09:05:05  09:05:05  fred ()
 * Removed unused variables.
 * 
 * Revision 4.0  86/10/20  12:08:48  12:08:48  fred ()
 * QA #1 release
 * 
 * Revision 1.9  86/10/20  09:11:34  09:11:34  fred ()
 * Fixed character insertion mode bug.
 * 
 * Revision 1.8  86/10/17  15:26:26  15:26:26  fred ()
 * Enhanced redraw during character insertion, and scrolling.
 * 
 * Revision 1.7  86/10/17  12:12:20  12:12:20  fred ()
 * Enhanced the redraw algorithm used during placement of ascii
 * characters which do not wrap.
 * 
 * Revision 1.6  86/10/17  08:46:41  08:46:41  fred ()
 * Tab expansion and control characters now completely in.  Control
 * character in last column still not handled correctly; Ok for now.
 * (version received from Phil).
 * 
 * Revision 1.3  86/10/10  13:14:13  13:14:13  fred ()
 * Filled procedure headers and fixed several small bugs.
 * 
 * Revision 1.2  86/10/10  08:42:32  08:42:32  fred ()
 * First real working version.  Includes all editing features but
 * the scroll keys, paging, and control character input.
 * 
 * Revision 1.1  86/10/10  07:33:36  07:33:36  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 createPageEdit();
extern INT32 drawPageEdit();
extern INT32 peFreeMemory();
extern INT8 * getStartOfPreviousLine();
extern xrEditor * processPageEdit();
extern xrEditor * peMOVE_handler();
extern xrEditor * peRESIZE_handler();
extern xrEditor * peREDRAW_handler();
extern xrEditor * peINSERTMODE_handler();
extern xrEditor * peActivate();

#define XrFRAME_ONLY       0x01
#define XrALLCOMPONENTS    0x02

#define SCREEN             0x01
#define BUFFER             0x02

#define BORDER             3
#define PADDING            2

#define UP                 0
#define DOWN               1



/*************************************<->*************************************
 *
 *  XrPageEdit (pageEdit, message, data)
 *
 *     xrEditor * pageEdit;
 *     INT32      message;
 *     INT8     * data;
 *
 *   Description:
 *   -----------
 *     This is the main handler routine for the multi-line 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:
 *   ------
 *     pageEdit = 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]
 *   sizePageEdit()
 *   createPageEdit()
 *   drawPageEdit()
 *   drawTextRegion()
 *   displayBuffer()
 *   drawCursor()
 *   processPageEdit()
 *   peInitGCs()
 *   peActivate()
 *   peMOVE_handler()
 *   peREDRAW_handler()
 *
 *************************************<->***********************************/

xrEditor *
XrPageEdit (pageEdit, message, data)

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

{
   /* Determine the action being requested */
   switch (message)
   {
      case MSG_NEW:
      {
           /*
            * Create a new instance of this editor.
            * The 'pageEdit' parameter is unused, but the 'data'
            * parameter must point to a filled out instance of
            * the 'xrPageEditInfo' structure, which describes the
            * instance which is to be created.
            */
           return ((xrEditor *)_MsgNew (pageEdit, data, 
                                        sizeof(xrPageEditData),
                                        createPageEdit, drawPageEdit,
                                        peFreeMemory, XrPageEdit,
                                        NULL));
      }

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

      case MSG_GETSTATE:
      {
           /*
            * Return the current state flags associated with the
            * specified page edit instance.  The 'pageEdit' 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 (pageEdit, data));
      }

      case MSG_SETSTATE:
      {
           /*
            * Change the state flags associated with a particular page
            * edit instance.  The 'pageEdit' 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 (pageEdit, data, 
                                              drawPageEdit, NULL));
      }

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

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

         return((xrEditor *) sizePageEdit (peInfoPtr, &peInfoPtr->editorRect));
      }

      case MSG_REDRAW:
      {
         /*
          * Redraw the editor, to match new value.
          * The 'pageEdit' 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 *) peREDRAW_handler (pageEdit, (INT32) data));
      }

      case MSG_DEACTIVATE:
      {
         /*
          * Force the page edit field inactive.
          * The 'pageEdit' parameter must contain the editor instance
          * pointer; the 'data' parameter is unused.
          */
         xrPageEditData * peDataPtr;

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

         peDataPtr = (xrPageEditData *) pageEdit->editorData;

         if (peDataPtr->peCursorOn)
         {
            peDataPtr->peCursorOn = FALSE;
            peInitGCs (peDataPtr);
            drawTextRegion (pageEdit, XrFRAME_ONLY);
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
         }

         return (pageEdit);
      }

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

      case MSG_GETBUFINFO:
      {
         /*
          * Return a block of information describing the contents
          * and limits associated with the editing buffer.  The
          * 'pageEdit' parameter specifies the instance to be
          * queried, while the 'data' parameter must point to an
          * instance of the 'xrPageEditBufInfo' structure, into
          * which the buffer information will be placed.
          */
         xrPageEditBufInfo * bufInfo = (xrPageEditBufInfo *) data;
         xrPageEditData    * peDataPtr;

         if (pageEdit == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (bufInfo == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         peDataPtr = (xrPageEditData *) pageEdit->editorData;
         bufInfo->buffer = peDataPtr->peBuffer;
         bufInfo->bufferCount = peDataPtr->peBufferCount;
         bufInfo->bufferSize = peDataPtr->peBufferSize;
         bufInfo->maxCharCount = peDataPtr->peMaxSize;

         return (pageEdit);
      }

      case MSG_SETBUFINFO:
      {
         /*
          * Take the modified buffer information, contained in the
          * structure pointed to by the 'data' parameter, and attempt 
          * to determine where the top of frame, the cursor pointer, 
          * and the cursor row/col should be located.
          */
         xrPageEditBufInfo * bufInfo  = (xrPageEditBufInfo *) data;
         xrPageEditData    * peDataPtr;

         /* Validate all of the parameter values */
         if (pageEdit == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (bufInfo == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }
         else if (bufInfo->buffer == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return (FALSE);
         }
         else if ((bufInfo->maxCharCount == 0) ||
                  (bufInfo->bufferSize <= 0))
         {
            xrErrno = XrINVALIDPARM;
            return (FALSE);
         }
         else if (((bufInfo->maxCharCount > 0) &&
                 (bufInfo->bufferCount > bufInfo->maxCharCount)) ||
                 (bufInfo->bufferCount > bufInfo->bufferSize))
         {
            xrErrno = XrPARMOUTOFRANGE;
            return (FALSE);
         }
         else if (bufInfo->bufferCount < 0)
         {
            xrErrno = XrINVALIDPARM;
            return (FALSE);
         }

         /***********************************/
         /* Determine the top of frame, the */
         /* cursor ptr, and the cursor row  */
         /* height here.                    */
         /***********************************/
        
         /*
          * For first pass, whenever the application changes the
          * buffer contents, we will force the top of frame back
          * to the start of the buffer, and the cursor to (0,0).
          * This could be enhanced at a later time, with a much
          * more complicated algorithm.
          */
         peDataPtr = (xrPageEditData *) pageEdit->editorData;
         peDataPtr->peBuffer = bufInfo->buffer;
         peDataPtr->peBufferCount = bufInfo->bufferCount;
         peDataPtr->peBufferSize = bufInfo->bufferSize;
         peDataPtr->peMaxSize = bufInfo->maxCharCount;
         peDataPtr->peTopOfFrame = peDataPtr->peBuffer;
         peDataPtr->peCursorPtr = peDataPtr->peBuffer;
         peDataPtr->peCursorRow = 0;
         peDataPtr->peCursorCol = 0;
         peDataPtr->peVCursorCol = 0;

         /* Redraw, if the instance is visible */
         if (pageEdit->editorState & XrVISIBLE)
         {
            peInitGCs (peDataPtr);
            displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
            if (peDataPtr->peCursorOn)
               drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
         }

         return (pageEdit);
      }

      case MSG_MOVE:
      {
         /*
          * Relocate an instance of the page editor to a new location.
          * The 'pageEdit' 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 *) peMOVE_handler (pageEdit, data));
      }

      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 'pageEdit' parameter 
          * must contain the editor instance pointer; the 'data' parameter 
          * must point to an XEvent structure, containing some X event.
	  */
         if ((pageEdit == NULL) || (data == NULL) ||
            ((pageEdit->editorState & (XrSENSITIVE | XrVISIBLE)) !=
            (XrSENSITIVE | XrVISIBLE)))
            return ((xrEditor *) NULL);
         
         return (processPageEdit (pageEdit, data));
      }

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

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


/*************************************<->*************************************
 *
 *  INT32
 *  peFreeMemory (peDataPtr)
 *
 *     xrPageEditData * peDataPtr
 *
 *   Description:
 *   -----------
 *     This routine is invoked whenever an instance is destroyed [MSG_FREE],
 *     or anytime a MSG_NEW fails after createPageEdit() has been called.
 *     It will delete the 'peBlanks' buffer space, along with freeing
 *     up the pixmap which was created for displaying an insensitive
 *     instance.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This points to the internal 'data' structure associated
 *                 with the instance being destroyed.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XFreePixmap()  [libX.a]
 *   (*xrFree)()
 *
 *************************************<->***********************************/

static
INT32
peFreeMemory (peDataPtr)

   xrPageEditData * peDataPtr;

{
   (*xrFree) (peDataPtr->peBlanks);
   if (peDataPtr->peTileId != xrDefaultTile)
      XFreePixmap (peDataPtr->peTileId);
}

/*************************************<->*************************************
 *
 *  INT32
 *  sizePageEdit (peInfoPtr, rectPtr)
 *
 *  xrPageEditInfo * peInfoPtr;
 *  RECTANGLE      * rectPtr;
 *
 *   Description:
 *   -----------
 *     This routine takes the page edit specifications, contained in the
 *     'info' structure pointed to by the 'peInfoPtr' parameter, and
 *     calculates the size of the rectangle needed to contain the instance.
 *
 *
 *   Inputs:
 *   ------
 *     peInfoPtr = This points to an 'info' structure, containing the
 *                 specifics describing the hypothetical instance.  Only
 *                 the 'editorFont', 'colCount' and 'rowCount'
 *                 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]
 *   XrSetRect()      [calc.c]
 *
 *************************************<->***********************************/

static
INT32
sizePageEdit (peInfoPtr, rectPtr)

   xrPageEditInfo * peInfoPtr;
   RECTANGLE      * rectPtr;

{
   INT16        height;
   INT16        width;
   INT16        lineWidth;
   INT16        lineHeight;
   FontInfo   * fontPtr;
   xrTextInfo   fontData;

   /* Check the row and column specifications */
   if ((peInfoPtr->rowCount <= 0) || (peInfoPtr->colCount <= 0))
   {
      xrErrno = XrPARMOUTOFRANGE;
      return (FALSE);
   }

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

   /* Make sure the font is fixed width */
   if (fontPtr->fixedwidth == 0)
   {
      xrErrno = XrINVALIDPARM;
      return (FALSE);
   }

   /* Calculate the width of the text region */
   lineWidth = (peInfoPtr->colCount * fontData.maxWidth);
   lineHeight = (fontData.ascent + fontData.descent + fontData.leading);

   /* 
    * We now have enough information to calculate the editor rectangle.
    *
    *   width = border width + padding + line width + padding + 
    *           border width.
    *           [ For the width, padding = 2 pixels ]
    *
    *   height = border height + padding + (font height * line count) + 
    *            padding + border height.
    *           [ For the height, padding = 2 pixels ]
    */
   width = BORDER + PADDING + lineWidth + PADDING + BORDER;
   height = BORDER + PADDING + (lineHeight * peInfoPtr->rowCount) + 
            PADDING + BORDER;

   XrSetRect (rectPtr, 0, 0, width, height);
   return (TRUE);
}

/*************************************<->*************************************
 *
 *  createPageEdit (peDataPtr, peInfoPtr, message)
 *
 *     xrPageEditData * peDataPtr;
 *     xrPageEditInfo * peInfoPtr;
 *     INT32            message;
 *
 *   Description:
 *   -----------
 *   This routine is used during the creation of a new page edit instance.
 *   It expects the 'peInfoPtr' 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; all of this information will be stored in 
 *   the 'data' structure pointed to by the 'peDataPtr' parameter.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This points to the 'data' structure, into which the
 *                 component information is to be saved.
 *
 *     peInfoPtr = This must point to an instance of the 'info' structure.
 *                 It must be completely filled out.
 *
 *     message = This parameter is unused for this editor.
 * 
 *   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]
 *   XrResource()     [resource.c]
 *   XMakePixmap()    [libX.a]
 *   XFreePixmap()    [libX.a]
 *   sizePageEdit()
 *
 *************************************<->***********************************/

static
INT32
createPageEdit (peDataPtr, peInfoPtr, message)

   register xrPageEditData * peDataPtr;
   register xrPageEditInfo * peInfoPtr;
            INT32            message;

{
   register INT32          i;
   register RECTANGLE    * editorRect;
            FontInfo     * fontPtr;
            xrResourceInfo bitmapInfo;
            RECTANGLE      workRect;

   /* Validate incoming parameters */
   if (peInfoPtr->buffer == NULL)
   {
      xrErrno = XrINVALIDPTR;
      return (FALSE);
   }
   else if ((peInfoPtr->maxCharCount == 0) ||
           (peInfoPtr->bufferSize <= 0))
   {
      xrErrno = XrINVALIDPARM;
      return (FALSE);
   }
   else if (((peInfoPtr->maxCharCount > 0) &&
           (peInfoPtr->bufferCount > peInfoPtr->maxCharCount)) ||
           (peInfoPtr->bufferCount > peInfoPtr->bufferSize))
   {
      xrErrno = XrPARMOUTOFRANGE;
      return (FALSE);
   }
   else if (peInfoPtr->bufferCount < 0)
   {
      xrErrno = XrINVALIDPARM;
      return (FALSE);
   }
   else
   {
      /* Parameters are valid, so save them */
      peDataPtr->peBuffer = peInfoPtr->buffer;
      peDataPtr->peBufferCount = peInfoPtr->bufferCount;
      peDataPtr->peBufferSize = peInfoPtr->bufferSize;
      peDataPtr->peMaxSize = peInfoPtr->maxCharCount;
   }

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

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

   /* Get the font description */
   fontPtr = (peInfoPtr->editorFont) ? peInfoPtr->editorFont : xrBaseFontInfo;
   _XrTextInfo (fontPtr, &peDataPtr->peFont);

   /* Create the 50% tile used when drawing insensitive page editor */
   peDataPtr->peFGColor = (peInfoPtr->editorFGColor == -1) ?
              xrForegroundColor : peInfoPtr->editorFGColor;
   peDataPtr->peBGColor = (peInfoPtr->editorBGColor == -1) ?
              xrBackgroundColor : peInfoPtr->editorBGColor;
   bitmapInfo.resourceType = XrTYPE_BITMAPID;
   bitmapInfo.resourceId = XrPERCENT50;
   if ((peInfoPtr->editorFGColor == -1) &&
       (peInfoPtr->editorBGColor == -1))
   {
      peDataPtr->peTileId = xrDefaultTile;
   }
   else
   {
      if ((XrResource (MSG_FIND, &bitmapInfo) == FALSE) ||
         ((peDataPtr->peTileId = 
         XMakePixmap (((xrBitmapId *)bitmapInfo.resourceObject)->bitmapId,
                        peDataPtr->peFGColor, peDataPtr->peBGColor)) == 0))
      {
         /* Unable to create the tile */
         xrErrno = XrXCALLFAILED;
         return (FALSE);
      }
   }

   /* Create an array to use when blanking out a line */
   if ((peDataPtr->peBlanks = (*xrMalloc)(peInfoPtr->colCount)) == NULL)
   {
      if (peDataPtr->peTileId != xrDefaultTile)
         XFreePixmap (peDataPtr->peTileId);
      xrErrno = XrOUTOFMEM;
      return (FALSE);
   }
   for (i = 0; i < peInfoPtr->colCount; i++)
      peDataPtr->peBlanks[i] = ' ';

   /* Fill in the rest of the fields in the text edit structures */
   peDataPtr->peCursorOn = FALSE;
   peDataPtr->peRowNum = peInfoPtr->rowCount;
   peDataPtr->peColNum = peInfoPtr->colCount;
   peDataPtr->peTopOfFrame = peDataPtr->peBuffer;
   peDataPtr->peCursorPtr = peDataPtr->peBuffer;
   peDataPtr->peCursorRow = 0;
   peDataPtr->peCursorCol = 0;
   peDataPtr->peVCursorCol = 0;
   if (peInfoPtr->tabWidth <= 0)
      peDataPtr->peTabWidth = 1;
   else 
      peDataPtr->peTabWidth = peInfoPtr->tabWidth;
   return (TRUE);
}

/*************************************<->*************************************
 *
 *  INT32
 *  drawPageEdit (pageEdit, components)
 *
 *     xrEditor * pageEdit;
 *     INT32      components;
 *
 *   Description:
 *   -----------
 *     This routine will draw all of an existing text edit instance.  
 *     This includes the border, interior, text, and cursor (if on).
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = This is the instance pointer for the instance which
 *                is to be drawn.
 *
 *     components = This parameter is not used by this module.  It needs
 *                  to be present so that this routine may be invoked
 *                  by _MsgNew().
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrMakeInvisible()       [editorUtil.c]
 *   drawTextRegion()
 *   displayBuffer()
 *   drawCursor()
 *   peInitGCs()
 *
 *************************************<->***********************************/

static
INT32
drawPageEdit (pageEdit, components)

   xrEditor * pageEdit;
   INT32      components;

{
   register xrPageEditData   * peDataPtr;

   /* Initialize variables */
   peDataPtr = (xrPageEditData *) pageEdit->editorData;

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

   /* Initialize the graphic contexts */
   peInitGCs (peDataPtr);

   /* Draw the instance */
   drawTextRegion (pageEdit, XrALLCOMPONENTS);
   displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);

   if (peDataPtr->peCursorOn)
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
}

/*************************************<->*************************************
 *
 *  xrEditor *
 *  processPageEdit (pageEdit, eventPtr)
 *
 *     xrEditor * pageEdit;
 *     XEvent   * eventPtr;
 *
 *   Description:
 *   -----------
 *     This is the main event processing routine the the page editor.
 *     When it first receives an event, it will call the various
 *     sub-event handlers (ascii, 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 temporary break condition occurs (buffer expansion occurred
 *     or failed, 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, sets 'exitFlag' to TRUE, and returns a value of TRUE.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = 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 0 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]
 *   peInitGCs()
 *   peButtonEvent()
 *   peAsciiEvent()
 *   peEditEvent()
 *
 *************************************<->***********************************/

static
xrEditor *
processPageEdit (pageEdit, eventPtr)

   xrEditor * pageEdit;
   XEvent   * eventPtr;

{
   register xrPageEditData  * peDataPtr;
            xrEvent           peEvent;
            INT8              exitFlag;
            UINT16            code;
            INT8            * string;
            INT32             byteCount;

   peDataPtr = (xrPageEditData *) pageEdit->editorData;

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

   peInitGCs (peDataPtr);

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

      if ((eventPtr->type == ButtonPressed) || 
          (eventPtr->type == ButtonReleased))
      {
         peButtonEvent (pageEdit, peDataPtr, eventPtr, &peEvent, &exitFlag);
         if (exitFlag)
            return (pageEdit);
      }
      else if (eventPtr->type == KeyPressed)
      {
         string = XrInputMap (eventPtr, &byteCount);
         if (byteCount == 1)
            code = *string;
         else if (byteCount == 2)
            code = (*string << 8) + *(string + 1);

         if (byteCount == 1 || byteCount == 2)
         {
            if (peAsciiEvent (pageEdit, peDataPtr, code, &peEvent, &exitFlag))
            {
               if (exitFlag)
                  return (pageEdit);
            }
            else if (peEditEvent (pageEdit,peDataPtr,code,&peEvent,&exitFlag))
            {
               if (exitFlag)
                  return (pageEdit);
            }
         }
      }
      else if (eventPtr->type == KeyReleased)
      {
         /* Swallow all of these events */
      }
      else
      {
         if (peDataPtr->peCursorOn)
         {
            /*
             * Unknown Event:
             * Push the unknown event onto the input queue, along with
             * a page edit event, and return to the application.
             */
            XrInput (NULL, MSG_PUSHEVENT, eventPtr);
            peEvent.value1 = XrUNKNOWN_EVENT;
            XrInput (NULL, MSG_PUSHEVENT, &peEvent);
            return (pageEdit);
         }
         else
            return ((xrEditor *) NULL);
      }

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

   }   /* End of while loop */
}

/*************************************<->*************************************
 *
 *  INT32
 *  teButtonEvent (pageEdit, peDataPtr, event, returnEvent, exitFlag)
 *
 *     xrEditor       * pageEdit;
 *     xrPageEditData * peDataPtr;
 *     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:
 *   ------
 *     pageEdit = This is the instance pointer for the page edit instance
 *                to which the event is to be applied.
 *
 *     peDataPtr = 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]
 *   XrPtInRect()     [calc.c]
 *   XrInput()        [input.c]
 *   XFlush()         [libX.a]
 *   drawCursor()
 *   drawTextRegion()
 *   getScreenCol()
 *   getBufCol()
 *
 *************************************<->***********************************/

static
INT32
peButtonEvent (pageEdit, peDataPtr, event, returnEvent, exitFlag)

   xrEditor       * pageEdit;
   xrPageEditData * peDataPtr;
   XButtonEvent   * event;
   xrEvent        * returnEvent;
   INT8           * exitFlag;

{
            POINT   cursorPos;
            INT32   selectRow, selectCol;
            INT32   rowCount, lineLen;
            INT32   newLineFound;
   register INT8  * startOfLine;
   register INT8  * ptr;

      if (XrMapButton (XrSELECT, event))
      {
         /* If the select is outside the editor's rectangle, then break */
         XrSetPt (&cursorPos, event->x, event->y);
         if (!XrPtInRect (&cursorPos, &pageEdit->editorRect))
         {
            /* 
             * Return two events: the select which is causing us to
             * exit, followed by the break notification.
             */
            XrInput (NULL, MSG_PUSHEVENT, event);
      
            returnEvent->value1 = XrPEDIT_BREAK;
            returnEvent->value2 = XrSELECT;
            XrInput (NULL, MSG_PUSHEVENT, returnEvent);
            
            /* Redraw the field as inactive */
            peDataPtr->peCursorOn = FALSE;
            drawTextRegion (pageEdit, XrFRAME_ONLY);
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
            XFlush();
            *exitFlag = TRUE;
            return;
         }
         else if (peDataPtr->peCursorOn)
         {
            /*
             * Place the cursor as close as possible to the location
             * where the select occurred.
             */

            /* Convert the point to a row and column */
            selectRow = ((event->y - pageEdit->editorRect.y-BORDER-PADDING) / 
                   (peDataPtr->peFont.ascent + peDataPtr->peFont.descent + 
                    peDataPtr->peFont.leading));
            selectCol = ((event->x - pageEdit->editorRect.x-BORDER-PADDING) / 
                    peDataPtr->peFont.maxWidth);

            /* Check an edge condition */
            if (selectRow >= peDataPtr->peRowNum)
               selectRow = peDataPtr->peRowNum - 1;
            if (selectCol >= peDataPtr->peColNum)
               selectCol = peDataPtr->peColNum - 1;

            /* Turn off the cursor */
            if (peDataPtr->peCursorOn)
               drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr,FALSE);

            /* Get a pointer to the start of that line */
            startOfLine = ptr = peDataPtr->peTopOfFrame;
            rowCount = 0;
            lineLen = 0;
            newLineFound = FALSE;

            while (selectRow + 1 != rowCount)
            {
               /* Check for the end of the buffer */
               if (ptr >= peDataPtr->peBuffer + peDataPtr->peBufferCount)
                  break;

               lineLen = readALine (peDataPtr, ptr, &newLineFound);
               startOfLine = ptr;
               ptr += lineLen;
               rowCount++;
            }

            /* Check where the select occurred */
            if (peDataPtr->peBufferCount == 0)
            {
               peDataPtr->peCursorPtr = peDataPtr->peBuffer;
               selectRow = 0;
               selectCol = 0;
            }
            else if (rowCount != selectRow + 1)
            {
               /*
                * The select occurred outside the range of the buffer,
                * so place the cursor at the end of the line.
                */
               peDataPtr->peCursorPtr = peDataPtr->peBuffer +
                                        peDataPtr->peBufferCount;
               if (newLineFound)
               {
                  /*
                   * The last character in the buffer is a newline,
                   * so place cursor at start of next line.
                   */
                  selectCol = 0;
                  selectRow = rowCount;
               }
               else
               {
                  /*
                   * The last character in the buffer is not a newline,
                   * so place cursor at the end of this line.
                   */
                  selectCol = lineLen;
                  selectRow = rowCount - 1;
               }
            }
            else
            {
               /* The line exists */
               if (lineLen <= selectCol)
               {
                  /* 
                   * The line exists, but it is too short.  Therefore,
                   * if the last character in the line is a newline,
                   * then place the cursor on top of the newline; else
                   * place the cursor after the last character.
                   */
                  if (newLineFound)
                     selectCol = lineLen - 1;
                  else
                     selectCol = lineLen;
               }

               peDataPtr->peCursorPtr =  startOfLine + 
                               getBufCol(peDataPtr, startOfLine, selectCol);
            }

            peDataPtr->peCursorRow = selectRow;
            peDataPtr->peCursorCol = selectCol;
            peDataPtr->peVCursorCol =  
                getScreenCol (peDataPtr, startOfLine, 
                              (INT32)(peDataPtr->peCursorPtr - startOfLine));

            /* Turn the cursor back on */
            if (peDataPtr->peCursorOn)
               drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr,TRUE);
         }
      
         /*
          * Force the field active, and redraw the instance.
          */
         if (!peDataPtr->peCursorOn)
         {
            peDataPtr->peCursorOn = TRUE;
            drawTextRegion (pageEdit, XrFRAME_ONLY);
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, 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 page edit break event afterwards.
          */
         XrInput (NULL, MSG_PUSHEVENT, event);
         returnEvent->value1 = XrUNKNOWN_EVENT;
         XrInput (NULL, MSG_PUSHEVENT, returnEvent);
            
         *exitFlag = TRUE;
         return;
      }
}


/*************************************<->*************************************
 *
 *  INT32
 *  peAsciiEvent (pageEdit, peDataPtr, keyCode, returnEvent, exitFlag)
 *
 *     xrEditor       * pageEdit;
 *     xrPageEditData * peDataPtr;
 *     UINT16           keyCode;
 *     xrEvent        * returnEvent;
 *     INT8             exitFlag;
 *
 *   Description:
 *   -----------
 *     If the buffer is not already full, then this routine takes the
 *     keycode and adds it to the buffer at the current cursor position.
 *     Afterwards, the cursor position is updated, and a check is made
 *     to see if the buffer is full.  If the buffer is full, than an
 *     attempt will be made to expand the buffer, if this feature is
 *     enabled.  If the buffer is full, or gets expanded, or the
 *     expansion fails, then a status event will be pushed onto the
 *     input queue, and control will return to the application.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = This is the instance pointer for the page edit instance
 *                to which the event is to be applied.
 *
 *     peDataPtr = This is a pointer to the 'data' structure associated
 *                 with the instance.
 *
 *     keyCode = This contains 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 if the buffer becomes
 *                full, is expanded, or the expansion fails.
 * 
 *   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]
 *   displayBuffer()
 *   drawCursor()
 *   getCharWidth()
 *   expandBuffer()
 *
 *************************************<->***********************************/

static
INT32
peAsciiEvent (pageEdit, peDataPtr, keyCode, returnEvent, exitFlag)

            xrEditor         * pageEdit;
   register xrPageEditData   * peDataPtr;
            UINT16             keyCode;
            xrEvent          * returnEvent;
            INT8             * exitFlag;

{
            INT8    key;
            INT32   junk;
            INT32   newLineFound;
            INT32   redrawLine;
            INT32   lineHeight;
            INT32   oldLineCount, newLineCount;
            INT8  * endOfBuffer;
   register INT8  * bufr;

   if ((keyCode == (RETURN_KEY | K_s)) || (keyCode == '\r'))
      keyCode = '\n';
   else if ((keyCode == (TAB_KEY | K_s)) ||
            (keyCode == (BACKTAB_KEY | K_s)) ||
            (keyCode == '\011'))
      keyCode = '\t';
   else if (((keyCode & 0xFF00) == K_s) || (keyCode == '\010') ||
            (keyCode == '\177'))
      return (FALSE);

   /* Check for an already full buffer */
   if (expandBuffer (peDataPtr, exitFlag, returnEvent))
      return (TRUE);

   key = keyCode & 0xFF;
   lineHeight = peDataPtr->peFont.ascent + peDataPtr->peFont.descent +
                peDataPtr->peFont.leading;
   endOfBuffer = peDataPtr->peBuffer + peDataPtr->peBufferCount;
   drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);

   /* Shift all character down in the buffer, and add the new character */
   oldLineCount = getLineCount (peDataPtr, getStartOfPreviousLine (
                  peDataPtr, peDataPtr->peCursorPtr, &junk, SCREEN));
   for (bufr = endOfBuffer; bufr > peDataPtr->peCursorPtr; bufr--)
      *bufr = *(bufr - 1);
   *(peDataPtr->peCursorPtr) = key;
   peDataPtr->peBufferCount++;

   /* Update the cursor position */
   redrawLine = peDataPtr->peCursorRow;
   peDataPtr->peCursorCol += getCharWidth (peDataPtr, peDataPtr->peCursorPtr,
                                           peDataPtr->peCursorCol);
   peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
   peDataPtr->peCursorPtr++;
   newLineCount = getLineCount (peDataPtr, getStartOfPreviousLine (
                  peDataPtr, peDataPtr->peCursorPtr, &junk, SCREEN));
   /* Check for line wrap, or a newline character */
   if ((peDataPtr->peCursorCol >= peDataPtr->peColNum) || (key == '\012'))
   {
      /* Move to the start of the next line */
      peDataPtr->peCursorCol = 0;
      peDataPtr->peVCursorCol = 0;

      /* See if we need to scroll */
      if (peDataPtr->peCursorRow + 1 == peDataPtr->peRowNum)
      {
         peDataPtr->peTopOfFrame += readALine (peDataPtr,
                                               peDataPtr->peTopOfFrame,
                                               &newLineFound);
         blockCopy (pageEdit, peDataPtr, UP, 1);
         displayBuffer (pageEdit, peDataPtr, peDataPtr->peRowNum - 2,
                        peDataPtr->peRowNum);
      }
      else
      {
         blockCopy (pageEdit, peDataPtr, DOWN, 
                    peDataPtr->peCursorRow + oldLineCount);
         displayBuffer (pageEdit, peDataPtr, peDataPtr->peCursorRow,
                        peDataPtr->peCursorRow + newLineCount + 1);
         peDataPtr->peCursorRow++;
      }
   }
   else
   {
      if (newLineCount == oldLineCount)
      {
         displayBuffer (pageEdit, peDataPtr, redrawLine, redrawLine + 
                        newLineCount);
      }
      else
      {
         blockCopy (pageEdit, peDataPtr, DOWN, 
                    peDataPtr->peCursorRow + oldLineCount);
         displayBuffer (pageEdit, peDataPtr, peDataPtr->peCursorRow,
                        peDataPtr->peCursorRow + newLineCount);
      }
   }

   /* Redraw the affected area */
   drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

   /* Check to see if the buffer is full, or needs to be expanded */
   expandBuffer (peDataPtr, exitFlag, returnEvent);

   return (TRUE);
}


/*************************************<->*************************************
 *
 *  INT32
 *  peEditEvent (pageEdit, peDataPtr, keyCode, returnEvent, exitFlag)
 *
 *     xrEditor       * pageEdit;
 *     xrPageEditData * peDataPtr;
 *     UINT16           keyCode;
 *     xrEvent        * returnEvent;
 *     INT8             exitFlag;
 *
 *   Description:
 *   -----------
 *     This routine is responsible for handling all of the editing keys.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = This is the instance pointer for the page edit instance
 *                to which the event is to be applied.
 *
 *     peDataPtr = 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 normally only occurs if the
 *                buffer is full, is expanded, or the expansion fails.
 * 
 *   Outputs:
 *   -------
 *     If the event is handled, then TRUE is returned, else FALSE.
 *
 *   Procedures Called
 *   -----------------
 *   XrInput()      [input.c]
 *   readALine()
 *   getStartOfPreviousLine()
 *   getLineCount()
 *   displayBuffer()
 *   drawCursor()
 *   expandBuffer()
 *   getScreenCol()
 *   getBufCol()
 *
 *************************************<->***********************************/

static
INT32
peEditEvent (pageEdit, peDataPtr, keyCode, returnEvent, exitFlag)

            xrEditor         * pageEdit;
   register xrPageEditData   * peDataPtr;
            UINT16             keyCode;
            xrEvent          * returnEvent;
            INT8             * exitFlag;

{
   register INT32   i;
            INT32   lineDelta;
            INT32   count;
            INT32   count2;
            INT32   newLineFound;
            INT8  * endOfBuffer;
   register INT8  * bufr;
            INT8  * previousLinePtr;
            INT8  * nextLinePtr;
   register INT8  * ptr;

   if ((keyCode == (BS_KEY | K_s)) || (keyCode == '\010') ||
      (keyCode == '\177'))
   {
      /* Make sure we're not already at the start of the buffer */
      if (peDataPtr->peCursorPtr == peDataPtr->peBuffer)
         return (TRUE);

      /* See if we need to scroll or wrap */
      if (peDataPtr->peCursorCol !=  0)
      {
         /* Move the cursor */
         peDataPtr->peCursorPtr--;
         previousLinePtr = getStartOfPreviousLine (peDataPtr,
                           peDataPtr->peCursorPtr, &count, SCREEN);

         peDataPtr->peCursorCol =  getScreenCol (peDataPtr, previousLinePtr,
                                   (INT32)(peDataPtr->peCursorPtr -
                                           previousLinePtr));
         peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      else if (peDataPtr->peCursorRow == 0)
      {
         /* Scroll 1 line */
         peDataPtr->peTopOfFrame = getStartOfPreviousLine (peDataPtr,
                                   peDataPtr->peTopOfFrame - 1, 
                                   &count, SCREEN);
         peDataPtr->peCursorPtr--;
         peDataPtr->peCursorCol = getScreenCol (peDataPtr, 
                                  peDataPtr->peTopOfFrame, 
                                  (INT32)(peDataPtr->peCursorPtr - 
                                          peDataPtr->peTopOfFrame));
         peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
      }
      else
      {
         /* Just wrap 1 line */
         peDataPtr->peCursorRow--;
         peDataPtr->peCursorPtr--;
         previousLinePtr = getStartOfPreviousLine (peDataPtr, 
                                                   peDataPtr->peCursorPtr, 
                                                    &count, SCREEN);
         peDataPtr->peCursorCol =  getScreenCol (peDataPtr, previousLinePtr,
                                   (INT32)(peDataPtr->peCursorPtr -
                                           previousLinePtr));
         peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
      }

      /* Delete the character under the cursor */
      ptr = peDataPtr->peCursorPtr;
      while (ptr < (peDataPtr->peBuffer + peDataPtr->peBufferCount))
      {
         *ptr = *(ptr+1);
         ptr++;
      }
      *ptr = '\0';
      peDataPtr->peBufferCount --;


      /* Redraw */
      displayBuffer (pageEdit, peDataPtr, peDataPtr->peCursorRow, 
                     peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

      return (TRUE);
   }
   else if (keyCode == (DEL_CHAR_KEY | K_s))
   {
      /* Ignore, if at end of the buffer */
      if (peDataPtr->peCursorPtr != 
         (peDataPtr->peBuffer + peDataPtr->peBufferCount))
      {
         /* Delete the character under the cursor */
         ptr = peDataPtr->peCursorPtr;
         while (ptr < (peDataPtr->peBuffer + peDataPtr->peBufferCount))
         {
            *ptr = *(ptr+1);
            ptr++;
         }
         *ptr = '\0';

         peDataPtr->peBufferCount --;

         /* Redisplay */
         displayBuffer (pageEdit, peDataPtr, peDataPtr->peCursorRow, 
                        peDataPtr->peRowNum);
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      return (TRUE);
   }
   else if (keyCode == (CURS_RT_KEY | K_s))
   {
      /* Make sure we're not already at the end of the buffer */
      if (peDataPtr->peCursorPtr >= 
          (peDataPtr->peBuffer + peDataPtr->peBufferCount))
         return(TRUE);


      /* See if we need to wrap */
      count = getCharWidth (peDataPtr, peDataPtr->peCursorPtr, 
                            peDataPtr->peCursorCol);
      if ((*peDataPtr->peCursorPtr == '\n') ||
          (peDataPtr->peCursorCol + count  >= peDataPtr->peColNum))
      {
         /* See if we need to scroll */
         if (peDataPtr->peCursorRow + 1 == peDataPtr->peRowNum)
         {
            /* Scroll 1 line */
            peDataPtr->peCursorCol = 0;
            peDataPtr->peVCursorCol = 0;
            peDataPtr->peCursorPtr ++;
            peDataPtr->peTopOfFrame += readALine (peDataPtr, 
                                       peDataPtr->peTopOfFrame, 
                                       &newLineFound);
            displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
         }
         else
         {
            /* No scroll; just move to next line */
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
            peDataPtr->peCursorRow++;
            peDataPtr->peCursorCol = 0;
            peDataPtr->peVCursorCol = 0;
            peDataPtr->peCursorPtr++;
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
         }
      }
      else
      {
         /* Move the cursor */
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
         peDataPtr->peCursorCol += count;
         peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
         peDataPtr->peCursorPtr++;
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      return (TRUE);
   }
   else if (keyCode == (CURS_LF_KEY | K_s))
   {
      /* Make sure we're not already at the start of the buffer */
      if (peDataPtr->peCursorPtr == peDataPtr->peBuffer)
         return(TRUE);

      /* See if we need to scroll or wrap */
      if (peDataPtr->peCursorCol !=  0)
      {
         /* Move the cursor */
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
         peDataPtr->peCursorPtr--;
         previousLinePtr = getStartOfPreviousLine (peDataPtr,
                           peDataPtr->peCursorPtr, &count, SCREEN);

         peDataPtr->peCursorCol = getScreenCol (peDataPtr, previousLinePtr,
                                  (INT32)(peDataPtr->peCursorPtr -
                                          previousLinePtr));
         peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      else if (peDataPtr->peCursorRow == 0)
      {
         /* Scroll 1 line */
         peDataPtr->peTopOfFrame = getStartOfPreviousLine (peDataPtr,
                                   peDataPtr->peTopOfFrame - 1, 
                                   &count, SCREEN);
         peDataPtr->peCursorPtr--;
         peDataPtr->peCursorCol = getScreenCol (peDataPtr, 
                                  peDataPtr->peTopOfFrame, 
                                  (INT32)(peDataPtr->peCursorPtr - 
                                          peDataPtr->peTopOfFrame));
         peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
         displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      else
      {
         /* Just wrap 1 line */
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
         peDataPtr->peCursorRow--;
         peDataPtr->peCursorPtr--;
         previousLinePtr = getStartOfPreviousLine (peDataPtr, 
                           peDataPtr->peCursorPtr,  &count, SCREEN);
         peDataPtr->peCursorCol = getScreenCol (peDataPtr, previousLinePtr,
                                  (INT32)(peDataPtr->peCursorPtr -
                                          previousLinePtr));
         peDataPtr->peVCursorCol = peDataPtr->peCursorCol;
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      return (TRUE);
   }
   else if (keyCode == (CURS_UP_KEY | K_s))
   {
      /* Determine if we can just move the cursor, or if we must scroll */
      if (peDataPtr->peCursorRow != 0)
      {
         /* Just move the cursor */
         previousLinePtr = getStartOfPreviousLine (peDataPtr,
                           peDataPtr->peCursorPtr, &count, SCREEN);

         if (previousLinePtr != peDataPtr->peBuffer)
         {
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
            peDataPtr->peCursorRow--;

            /* Find position of end of new line */
            previousLinePtr = getStartOfPreviousLine (peDataPtr,
                              previousLinePtr - 1, &count, SCREEN);

            peDataPtr->peCursorCol = getScreenCol (peDataPtr,
                                     previousLinePtr, count - 1);

            /* Position at shorter of line end or traveling column */
            if (peDataPtr->peCursorCol > peDataPtr->peVCursorCol)
               peDataPtr->peCursorCol = peDataPtr->peVCursorCol;

            /* Compute position of character in buffer */
            peDataPtr->peCursorPtr = previousLinePtr + 
                                     getBufCol (peDataPtr, previousLinePtr, 
                                                peDataPtr->peCursorCol);

            /* Adjust cursor to beginning position of character on screen */
            peDataPtr->peCursorCol = getScreenCol(peDataPtr, previousLinePtr,
                                     (INT32)(peDataPtr->peCursorPtr - 
                                     previousLinePtr));

            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
         }
      }
      else
      {
         /* Need to scroll */
         if (peDataPtr->peTopOfFrame != peDataPtr->peBuffer)
         {
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
            peDataPtr->peTopOfFrame = getStartOfPreviousLine (peDataPtr,
                                      peDataPtr->peTopOfFrame - 1, 
                                      &count, SCREEN);

            /* Find position of end of new line */
            peDataPtr->peCursorCol = getScreenCol (peDataPtr,
                                     peDataPtr->peTopOfFrame, count - 1);

            /* Position at shorter of line end or traveling column */
            if (peDataPtr->peCursorCol > peDataPtr->peVCursorCol)
               peDataPtr->peCursorCol = peDataPtr->peVCursorCol;

            /* Compute position of character in buffer */
            peDataPtr->peCursorPtr = peDataPtr->peTopOfFrame +
                                     getBufCol (peDataPtr,
                                                peDataPtr->peTopOfFrame,
                                                peDataPtr->peCursorCol);

            /* Adjust cursor to beginning position of character on screen */
            peDataPtr->peCursorCol = getScreenCol(peDataPtr,
                                     peDataPtr->peTopOfFrame,
                                     (INT32)(peDataPtr->peCursorPtr -
                                             peDataPtr->peTopOfFrame));

            blockCopy (pageEdit, peDataPtr, DOWN, 0);
            displayBuffer (pageEdit, peDataPtr, 0, 1);
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
         }
      }

      return (TRUE);
   }

   else if (keyCode == (CURS_DN_KEY | K_s))
   {
      if (peDataPtr->peBufferCount == 0)
         return (TRUE);

      /* Get to beginning of previous line */
      previousLinePtr = getStartOfPreviousLine (peDataPtr, 
                        peDataPtr->peCursorPtr, &count, SCREEN);

      /* Find beginning of next line */
      nextLinePtr = previousLinePtr + 
            readALine (peDataPtr, previousLinePtr, &newLineFound);

      /* Can't move if buffer end is on same screen line as cursor */
      if (getStartOfPreviousLine (peDataPtr, nextLinePtr, &count, SCREEN) ==
          previousLinePtr)
         return (TRUE);          

      /* Determine if we need to scroll, or can just move the cursor */
      if (peDataPtr->peCursorRow + 1 == peDataPtr->peRowNum)
      {
         /* Scroll up 1 line */
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
         peDataPtr->peTopOfFrame += readALine (peDataPtr,
                                    peDataPtr->peTopOfFrame, &newLineFound);

         /* Find position of the end of new line */
         count = readALine (peDataPtr, nextLinePtr, &newLineFound);
         peDataPtr->peCursorCol = getScreenCol(peDataPtr, 
                                  nextLinePtr, count);

         /* Position at shorter of line end or traveling column */
         if (peDataPtr->peCursorCol <= peDataPtr->peVCursorCol)
         {
            /* Try to position cursor on the newline character */
            if (*(nextLinePtr + count - 1) == '\n')
               peDataPtr->peCursorCol--;
         }
         else
            peDataPtr->peCursorCol = peDataPtr->peVCursorCol;

         /* Compute position of character in buffer */
         peDataPtr->peCursorPtr = nextLinePtr + 
                                  getBufCol (peDataPtr, nextLinePtr,
                                             peDataPtr->peCursorCol);

         /* Adjust cursor to beginning position of character on screen */
         peDataPtr->peCursorCol = getScreenCol(peDataPtr, nextLinePtr, 
                                  (INT32)(peDataPtr->peCursorPtr - 
                                  nextLinePtr));

         blockCopy (pageEdit, peDataPtr, UP, 1);
         displayBuffer (pageEdit, peDataPtr, peDataPtr->peRowNum - 1, 
                        peDataPtr->peRowNum);
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      else
      {
         /* Just move the cursor down */
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
         peDataPtr->peCursorRow++;

         /* Find position of end of new line */
         count = readALine (peDataPtr, nextLinePtr, &newLineFound);
         peDataPtr->peCursorCol = getScreenCol (peDataPtr, 
                                  nextLinePtr, count);

         /* Position at shorter of line end or traveling column */
         if (peDataPtr->peCursorCol <= peDataPtr->peVCursorCol)
         {
            /* Position at shorter of line end or traveling column */
            if (*(nextLinePtr + count - 1) == '\n')
               peDataPtr->peCursorCol--;
         }
         else
            peDataPtr->peCursorCol = peDataPtr->peVCursorCol;

         /* Compute position of character in buffer */
         peDataPtr->peCursorPtr = nextLinePtr + 
                                  getBufCol (peDataPtr, nextLinePtr,
                                             peDataPtr->peCursorCol);

         /* Adjust cursor to beginning position of character on screen */
         peDataPtr->peCursorCol = getScreenCol(peDataPtr, nextLinePtr, 
                                  (INT32)(peDataPtr->peCursorPtr - 
                                          nextLinePtr));
         
         drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }
      return (TRUE);
   }

   else if (keyCode == (ROLL_DN_KEY | K_s))
   {
      /* Determine if we can scroll down */
      if (peDataPtr->peTopOfFrame == peDataPtr->peBuffer)
         return (TRUE);      

      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
      peDataPtr->peTopOfFrame = getStartOfPreviousLine (peDataPtr,
                                peDataPtr->peTopOfFrame - 1, &count, SCREEN);

      /* Get info for the line the cursor was on */
      previousLinePtr = getStartOfPreviousLine (peDataPtr,
                        peDataPtr->peCursorPtr, &count, SCREEN);

      /* Find position of end of new line */
      previousLinePtr = getStartOfPreviousLine (peDataPtr,
                        previousLinePtr - 1, &count, SCREEN);

      peDataPtr->peCursorCol = getScreenCol (peDataPtr, 
                               previousLinePtr, count - 1);

      /* position at shorter of line end or traveling column */
      if (peDataPtr->peCursorCol > peDataPtr->peVCursorCol)
         peDataPtr->peCursorCol = peDataPtr->peVCursorCol;

      /* Compute position of character in buffer */
      peDataPtr->peCursorPtr = previousLinePtr + 
                               getBufCol (peDataPtr, previousLinePtr,
                                          peDataPtr->peCursorCol);
  

      /* Adjust cursor to beginning position of character on screen */
      peDataPtr->peCursorCol = getScreenCol(peDataPtr, previousLinePtr, 
                               (INT32)(peDataPtr->peCursorPtr - 
                                       previousLinePtr));

      blockCopy (pageEdit, peDataPtr, DOWN, 0);
      displayBuffer (pageEdit, peDataPtr, 0, 1);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

      return (TRUE);
   }
   else if (keyCode == (ROLL_UP_KEY | K_s))
   {
      if (peDataPtr->peBufferCount == 0)
         return (TRUE);

      /* get to the beginning of current line */
      previousLinePtr = getStartOfPreviousLine (peDataPtr,
                        peDataPtr->peCursorPtr, &count, SCREEN);

      /* Find beginning of next line */
      nextLinePtr = previousLinePtr +  
                    readALine (peDataPtr, previousLinePtr, &newLineFound);

      /* Can't move if buffer end is on same screen line as cursor */
      if (getStartOfPreviousLine (peDataPtr, nextLinePtr, &count, SCREEN) ==
          previousLinePtr)
         return (TRUE);          

      /* Scroll up 1 line */
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
      peDataPtr->peTopOfFrame += readALine (peDataPtr,
                                 peDataPtr->peTopOfFrame, &newLineFound);

      /* Find position of end of new line */
      count = readALine (peDataPtr, nextLinePtr, &newLineFound);
      peDataPtr->peCursorCol = getScreenCol (peDataPtr, nextLinePtr, count);

      /* Position at shorter of line end or traveling column */
      if (peDataPtr->peCursorCol <= peDataPtr->peVCursorCol)
      {
         /* Try to position cursor on the newline character */
         if (*(nextLinePtr + count - 1) == '\n')
            peDataPtr->peCursorCol--;
      }
      else
         peDataPtr->peCursorCol = peDataPtr->peVCursorCol;

      /* Compute position of character in buffer */
      peDataPtr->peCursorPtr = nextLinePtr + getBufCol (peDataPtr,
                                             nextLinePtr, 
                                             peDataPtr->peCursorCol);

      /* Adjust cursor to beginning position of character on screen */
      peDataPtr->peCursorCol = getScreenCol(peDataPtr, nextLinePtr, 
                               (INT32)(peDataPtr->peCursorPtr - 
                                       nextLinePtr));
  
      blockCopy (pageEdit, peDataPtr, UP, 1);
      displayBuffer (pageEdit, peDataPtr, peDataPtr->peRowNum - 1, 
                     peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      return (TRUE);
   }

   else if (keyCode == (PREV_KEY | K_s))
   {
      
      /* count back rows to start of previous frame or start of buffer */
      for (i=1; (i < peDataPtr->peRowNum) &&
                (peDataPtr->peTopOfFrame != peDataPtr->peBuffer); i++)

         peDataPtr->peTopOfFrame = getStartOfPreviousLine (peDataPtr,
                                   peDataPtr->peTopOfFrame - 1, 
                                   &count, SCREEN);

      peDataPtr->peVCursorCol = peDataPtr->peCursorCol = 0;
      peDataPtr->peCursorRow = 0;
      peDataPtr->peCursorPtr = peDataPtr->peTopOfFrame;

      displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

      return (TRUE);
   }

   else if (keyCode == (NEXT_KEY | K_s))
   {
      if (peDataPtr->peBufferCount == 0)
         return (TRUE);

      for (i=1; i < peDataPtr->peRowNum; i++)
      {
         /* Find beginning of next line */
         nextLinePtr = peDataPtr->peTopOfFrame + 
                       readALine (peDataPtr, peDataPtr->peTopOfFrame, 
                                  &newLineFound);

         /* Can't move further if buffer end is on same screen line
          * as top of frame 
          */
         if (getStartOfPreviousLine (peDataPtr, nextLinePtr,
                        &count, SCREEN) == peDataPtr->peTopOfFrame)
            break;

         peDataPtr->peTopOfFrame = nextLinePtr;
      }

      peDataPtr->peVCursorCol = peDataPtr->peCursorCol = 0;
      peDataPtr->peCursorRow = 0;
      peDataPtr->peCursorPtr = peDataPtr->peTopOfFrame;

      displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

      return (TRUE);
   }

   else if (keyCode == (HOME_UP_KEY | K_s))
   {
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
      peDataPtr->peVCursorCol = peDataPtr->peCursorCol = 0;
      peDataPtr->peCursorRow = 0;
      peDataPtr->peCursorPtr = peDataPtr->peBuffer;

      if (peDataPtr->peTopOfFrame != peDataPtr->peBuffer)
      {
         peDataPtr->peTopOfFrame = peDataPtr->peBuffer;
         displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
      }
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

      return (TRUE);
   }

   else if (keyCode == (HOME_DN_KEY | K_s))
   {
      if (peDataPtr->peBufferCount == 0)
         return(TRUE);

      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, FALSE);
      previousLinePtr = getStartOfPreviousLine (peDataPtr,
                        peDataPtr->peBuffer + peDataPtr->peBufferCount,
                        &count, SCREEN);
      peDataPtr->peCursorCol = getScreenCol (peDataPtr, previousLinePtr,
                                             count);
      peDataPtr->peCursorPtr = previousLinePtr + count;
      peDataPtr->peVCursorCol = peDataPtr->peCursorCol;

      /* count back rows to top of frame */
      for (i = 1; (i < peDataPtr->peRowNum) &&
                  (previousLinePtr != peDataPtr->peTopOfFrame); i++)

         previousLinePtr = getStartOfPreviousLine (peDataPtr,
                           previousLinePtr - 1, &count, SCREEN);

      peDataPtr->peCursorRow = i - 1;
      peDataPtr->peTopOfFrame = previousLinePtr;      
      displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

      return (TRUE);
   }

   else if (keyCode == (CLR_LINE_KEY | K_s))
   {
      /* Clear from the cursor to the next Newline character */

      /* Ignore, if at end of the buffer */
      if (peDataPtr->peCursorPtr >= 
          peDataPtr->peBuffer + peDataPtr->peBufferCount)
         return (TRUE);

      /* Determine how many characters to delete */
      count = 0;
      newLineFound = FALSE;
      while (!newLineFound)
      {
         count += readALine (peDataPtr, peDataPtr->peCursorPtr + count,
                  &newLineFound);

         if (peDataPtr->peCursorPtr + count >=
             peDataPtr->peBuffer + peDataPtr->peBufferCount)
            break;
      }
  
      /* Don't delete the newline at end of line, if present */
      if (*(peDataPtr->peCursorPtr + count - 1) == '\n')
         count--;

      if (count == 0)
         return (TRUE);

      /* Delete the characters */
      for (ptr = peDataPtr->peCursorPtr + count; 
           ptr < peDataPtr->peBuffer + peDataPtr->peBufferCount; ptr++)
         *(ptr - count) = *(ptr);

      peDataPtr->peBufferCount -= count;

      displayBuffer (pageEdit, peDataPtr, peDataPtr->peCursorRow, 
                     peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      return (TRUE);
   }
   else if (keyCode == (DEL_LINE_KEY | K_s))
   {
      /* Clear from previous Newline to the next Newline character */

      /* Ignore if buffer is empty */
      if (peDataPtr->peBufferCount == 0)
         return (TRUE);

      /* Determine how many characters to delete */
      previousLinePtr = getStartOfPreviousLine (peDataPtr,
                        peDataPtr->peCursorPtr, &count, SCREEN);

      count = 0;
      newLineFound = FALSE;

      /* Find the end of the line */
      while (!newLineFound)
      {
         count += readALine (peDataPtr, previousLinePtr + count,
                            &newLineFound);
         if (previousLinePtr + count >=
             peDataPtr->peBuffer + peDataPtr->peBufferCount)
            break;
      }

      /* Find the start of the line */
      ptr = getStartOfPreviousLine (peDataPtr, previousLinePtr,
                                    &count2, BUFFER);
      count += count2 - 1;
      lineDelta = getLineCount (peDataPtr, ptr) -
                  getLineCount (peDataPtr, previousLinePtr);
  
      if (count == 0)
         return (TRUE);

      /* Delete the characters */
      peDataPtr->peCursorPtr = ptr;
      for (; ptr < peDataPtr->peBuffer + peDataPtr->peBufferCount; ptr++)
         *ptr = *(ptr + count);

      peDataPtr->peBufferCount -= count;

      /* Update the cursor position */
      peDataPtr->peCursorCol = 0;
      peDataPtr->peVCursorCol = 0;
      if (peDataPtr->peCursorPtr < peDataPtr->peTopOfFrame)
      {
         peDataPtr->peTopOfFrame = peDataPtr->peCursorPtr;
         peDataPtr->peCursorRow = 0;
      }
      else
         peDataPtr->peCursorRow -= lineDelta;

      displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      return (TRUE);
   }
   else if (keyCode == (INSERT_LINE_KEY | K_s))
   {
      /*
       * Insert a new line before the line at which the cursor is
       * currently positioned.
       */

      /* See if the buffer is full */
      if (expandBuffer (peDataPtr, exitFlag, returnEvent))
         return (TRUE);
      
      /* Find the start of the line */
      ptr = getStartOfPreviousLine (peDataPtr, 
            peDataPtr->peCursorPtr,  &count, BUFFER);

      /* Determine how many lines the cursor position will change by */
      lineDelta = getLineCount (peDataPtr, ptr) - 
                  getLineCount (peDataPtr, peDataPtr->peCursorPtr - 
                                           peDataPtr->peCursorCol);

      /* Shift buffer, and insert the newline character */
      endOfBuffer = peDataPtr->peBuffer + peDataPtr->peBufferCount;
      for (bufr = endOfBuffer; bufr > ptr; bufr--)
         *bufr = *(bufr - 1);
 
      *ptr = '\n';
      peDataPtr->peBufferCount++;

      /* 
       * If the newline was inserted above the current top of frame,
       * then we need to update the top of frame, in addition to the
       * cursor position.
       */
      if (ptr < peDataPtr->peTopOfFrame)
      {
         peDataPtr->peTopOfFrame = ptr;
         peDataPtr->peCursorRow = 0;
      }
      else
         peDataPtr->peCursorRow -= lineDelta;

      peDataPtr->peCursorPtr = ptr;
      peDataPtr->peCursorCol = 0;
      peDataPtr->peVCursorCol = 0;

      /* Redraw the screen */
      displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);

      /*
       * Check to see if the buffer is now full, or needs to
       * be expanded.
       */
      expandBuffer (peDataPtr, exitFlag, returnEvent);
   }
   else
      return (FALSE);
}


/*************************************<->*************************************
 *
 *  xrEditor *
 *  peActivate (pageEdit)
 *
 *     xrEditor * pageEdit;
 *
 *   Description:
 *   -----------
 *     This is the handler for the MSG_ACTIVATE message.  It will allow
 *     an existing page 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:
 *   ------
 *     pageEdit = This is the instance pointer for the page 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]
 *   drawTextRegion()
 *   drawCursor()
 *   processPageEdit()
 *
 *************************************<->***********************************/

static
xrEditor *
peActivate (pageEdit)

   xrEditor * pageEdit;

{
   xrPageEditData * peDataPtr;
   XEvent           event;

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

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

   peDataPtr = (xrPageEditData *) pageEdit->editorData;

   /* Activate the field, if not already */
   if (! (peDataPtr->peCursorOn))
   {
      peDataPtr->peCursorOn = TRUE;
      drawTextRegion (pageEdit, XrFRAME_ONLY);
      drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
   }

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

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


/*************************************<->*************************************
 *
 *  xrEditor *
 *  peMOVE_handler (pageEdit, ptPtr)
 *
 *     xrEditor * pageEdit;
 *     POINT    * ptPtr;
 *
 *   Description:
 *   -----------
 *     This routine will relocate an existing page edit instance, such
 *     that the origin of the editor rectangle is at the point indicated
 *     by the 'ptPtr' parameter.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = Editor instance pointer, for the instance to be moved.
 *
 *     ptPtr = Pointer to a 'POINT' structure, containing new origin.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, the instance pointer is returned.
 *
 *     Upon failure, NULL is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyRect()        [calc.c]
 *   _XrMakeInvisible()  [editorUtil.c]
 *   XrEditorGroup()     [group.c]
 *   drawPageEdit()
 *
 *************************************<->***********************************/

static
xrEditor *
peMOVE_handler (pageEdit, ptPtr)

   xrEditor * pageEdit;
   POINT    * ptPtr;

{
   RECTANGLE        workRect;
 
   /* Check for invalid parameters */
   if (pageEdit == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrEditor *) NULL);
   }
   else if (ptPtr == NULL)
   {
      xrErrno = XrINVALIDPTR;
      return ((xrEditor *) NULL);
   }

   /* Save a copy of the old editor rectangle */
   XrCopyRect (&pageEdit->editorRect, &workRect);

   /* Set the new editor rectangle origin */
   pageEdit->editorRect.x = ptPtr->x;
   pageEdit->editorRect.y = ptPtr->y;

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

      /* Redisplay the instance at its new location */
      drawPageEdit (pageEdit, NULL);
   }

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


/*************************************<->*************************************
 *
 *  xrEditor *
 *  teREDRAW_handler (pageEdit, redrawMode)
 *
 *     xrEditor * pageEdit;
 *     INT32      redrawMode;
 *
 *   Description:
 *   -----------
 *     This is the handler routine for the MSG_REDRAW message; it is
 *     capable of handling either an XrREDRAW_ALL or XrREDRAW_TEXT
 *     request.  These redraw the entire field, or just the editing
 *     text.  It will assume that the user has not modified the
 *     buffer contents at all!!
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = 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
 *   -----------------
 *   drawPageEdit()
 *   displayBuffer()
 *   drawCursor()
 *   peInitGCs()
 *
 *************************************<->***********************************/

static
xrEditor *
peREDRAW_handler (pageEdit, redrawMode)

   xrEditor * pageEdit;
   INT32      redrawMode;

{
   xrPageEditData * peDataPtr;

   /* Validate the instance pointer and the redraw mode */
   if (pageEdit == NULL)
   {
      xrErrno = XrINVALIDID;
      return ((xrEditor *) NULL);
   }
   else if (redrawMode == XrREDRAW_ALL)
   {
      if (pageEdit->editorState & XrVISIBLE)
         drawPageEdit (pageEdit, NULL);

      return (pageEdit);
   }
   else if (redrawMode == XrREDRAW_TEXT)
   {
      if (pageEdit->editorState & XrVISIBLE)
      {
         peDataPtr = (xrPageEditData *) pageEdit->editorData;

         peInitGCs (peDataPtr);
         displayBuffer (pageEdit, peDataPtr, 0, peDataPtr->peRowNum);
         if (peDataPtr->peCursorOn)
            drawCursor (pageEdit, pageEdit->editorWindowId, peDataPtr, TRUE);
      }

      return (pageEdit);
   }
   else
   {
      xrErrno = XrINVALIDOPTION;
      return ((xrEditor *) NULL);
   }
}


/*************************************<->*************************************
 *
 *  peInitGCs (peDataPtr)
 *
 *     xrPageEditData * peDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine initializes 6 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 page edit instance.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = 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, xrEditorGC5 and xrEditorGC6
 *     will be set.
 *
 *   Procedures Called
 *   -----------------
 *   _XrCopyGC()    [gcUtil.c]
 *   _XrChangeGC()  [gcUtil.c]
 *
 *************************************<->***********************************/

static
peInitGCs (peDataPtr)

   xrPageEditData * peDataPtr;

{
   INT32 changeList[21];
   INT32 changeMask;

   /* Initialize the standard graphic contexts we will be using */
   _XrInitEditorGCs (peDataPtr->peFGColor, peDataPtr->peBGColor,
                     peDataPtr->peFont.fontInfo->id);

   /*
    * Initialize the graphics context used to draw 
    * the background for an insensitive page edit instance,
    * and the wide border.
    */
   _XrCopyGC (xrDefaultGC, xrEditorGC3);
   changeList[XrFOREGROUNDVAL] = peDataPtr->peFGColor;
   changeList[XrBACKGROUNDVAL] = peDataPtr->peBGColor;
   changeList[XrLINEWIDTHVAL] = BORDER;
   changeList[XrTILEVAL] = peDataPtr->peTileId;
   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] = peDataPtr->peFont.fontInfo->id;
   changeList[XrALUVAL] = GXand;
   changeMask = (XrFOREGROUND | XrBACKGROUND | XrFONT | XrALU);
   _XrChangeGC (xrEditorGC4, changeMask, changeList);
   _XrCopyGC (xrDefaultGC, xrEditorGC5);
   changeList[XrFOREGROUNDVAL] = peDataPtr->peFGColor;
   changeList[XrBACKGROUNDVAL] = 0;
   changeList[XrFONTVAL] = peDataPtr->peFont.fontInfo->id;
   changeList[XrALUVAL] = GXor;
   changeMask = (XrFOREGROUND | XrBACKGROUND | XrFONT | XrALU);
   _XrChangeGC (xrEditorGC5, changeMask, changeList);

   /* GC used for drawing over the wide border */
   _XrCopyGC (xrDefaultGC, xrEditorGC6);
   changeList[XrFOREGROUNDVAL] = peDataPtr->peBGColor;
   changeList[XrBACKGROUNDVAL] = peDataPtr->peFGColor;
   changeList[XrLINEWIDTHVAL] = BORDER - 1;
   changeMask = (XrFOREGROUND | XrBACKGROUND | XrLINEWIDTH);
   _XrChangeGC (xrEditorGC6, changeMask, changeList);
}


/*************************************<->*************************************
 *
 *  INT32
 *  drawTextRegion (pageEdit, components)
 *
 *     xrEditor * pageEdit;
 *     INT32      components;
 *
 *   Description:
 *   -----------
 *     This routine draws only the editing region (the border and the
 *     interior of the instance); the actual text data is not displayed
 *     here.  If the 'components' parameter is set to XrALLCOMPONENTS,
 *     then both the frame and the interior of the instance will be
 *     drawn.  If the 'components' parameter is set to XrFRAME_ONLY,
 *     then only the frame is drawn.  XrALLCOMPONENTS is normally used
 *     only when the complete instance needs to be redrawn.  XrFRAME_ONLY
 *     is used when the field is to be activated and deactivated, and
 *     we only want the frame redrawn.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = The instance pointer for the instance to be drawn.
 *
 *     components = The portion of the instance to be redrawn, as explained
 *                  above.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyRect()              [calc.c]
 *   _XrRectangle()            [rectUtil.c]
 *   _XrBorderFillRectangle()  [rectUtil.c]
 *
 *************************************<->***********************************/

static
INT32
drawTextRegion (pageEdit, components)

   xrEditor * pageEdit;
   INT32      components;

{
   RECTANGLE frameRect;
   xrPageEditData * peDataPtr = (xrPageEditData *) pageEdit->editorData;

   /*
    * If the field is active, then draw a wide border, else
    * draw a narrow border.  The frame definition will need
    * to be adjusted to take into account the wide border,
    * because X does wierd things with wide line drawing.
    */
   XrCopyRect (&pageEdit->editorRect, &frameRect);

   if (peDataPtr->peCursorOn)
   {
      /* Draw the field as active */
      frameRect.width -= BORDER - 1;
      frameRect.height -= BORDER - 1;

      /* Draw a wide border, and fill, if requested */
      if (components == XrFRAME_ONLY)
         _XrRectangle (pageEdit->editorWindowId, xrEditorGC3, &frameRect);
      else if (pageEdit->editorState & XrSENSITIVE)
         _XrBorderFillRectangle (pageEdit->editorWindowId, xrEditorGC3, 
                                 xrEditorGC2, &frameRect);
      else
         _XrBorderFillRectangle (pageEdit->editorWindowId, xrEditorGC3, 
                                 xrEditorGC3, &frameRect);
   }
   else
   {
      /* Draw the field as inactive */
      if (components == XrFRAME_ONLY)
      {
         /* Draw the narrow border */
         _XrRectangle (pageEdit->editorWindowId, xrEditorGC1, &frameRect);

         frameRect.x += 1;
         frameRect.y += 1;
         frameRect.width -= BORDER;
         frameRect.height -= BORDER;
         _XrRectangle (pageEdit->editorWindowId, xrEditorGC6, &frameRect);
         return;
      }

      /* Draw a narrow border, and fill the interior */
      if (pageEdit->editorState & XrSENSITIVE)
         _XrBorderFillRectangle (pageEdit->editorWindowId, xrEditorGC1, 
                                 xrEditorGC2, &frameRect);
      else
         _XrBorderFillRectangle (pageEdit->editorWindowId, xrEditorGC1, 
                                 xrEditorGC3, &frameRect);
   }
}

/*************************************<->*************************************
 *
 *  INT32
 *  drawCursor (pageEdit, windowId, peDataPtr, showing)
 *
 *     xrEditor       * pageEdit;
 *     Window           windowId;
 *     xrPageEditData * peDataPtr;
 *     INT32            showing;
 *
 *   Description:
 *   -----------
 *     This routine will draw the cursor at the current cursor position,
 *     if 'showing' is set to TRUE, or it will remove the cursor, if
 *     'showing' is set to FALSE.  When drawing the cursor, if either
 *     no character is defined at the cursor position, or a newline is
 *     at the cursor position, then the SPACE character will be used; if
 *     a printable character is at the cursor position, then it will be
 *     redrawn, to look as if a block cursor were on top of it.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = This is the instance pointer for the instance which is
 *                to have its cursor modified.
 *
 *     windowId = The window Id for the window to which the instance is
 *                attached.
 *
 *     peDataPtr = Pointer to the instance's internal 'data' structure.
 *
 *     showing = 'TRUE' if cursor should be drawn as visible, else FALSE.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrImageText8()  [textUtil.c]
 *
 *************************************<->***********************************/

static
INT32
drawCursor (pageEdit, windowId, peDataPtr, showing)

   xrEditor                * pageEdit;
   Window                    windowId;
   register xrPageEditData * peDataPtr;
   INT32                     showing;

{
   register INT8       * cursorLoc;
            INT8         cursorChar;
   register INT32        penX, penY;
   register xrTextInfo * fontPtr;

   cursorLoc = peDataPtr->peCursorPtr;

   /* Determine what character the cursor will sit on top of */
   if ((*cursorLoc == '\n') ||
       (*cursorLoc == '\t') ||
       (cursorLoc == peDataPtr->peBuffer + peDataPtr->peBufferCount))
   {
      cursorChar = ' ';
   }
   else if ((*cursorLoc & '\340') == 0)      /* control char */
      cursorChar = '^';
   else
      cursorChar = *cursorLoc;

   fontPtr = &peDataPtr->peFont;
   penX = pageEdit->editorRect.x + PADDING + BORDER +
      (fontPtr->maxWidth * peDataPtr->peCursorCol),
   penY = pageEdit->editorRect.y + PADDING + BORDER + (peDataPtr->peCursorRow *
      (fontPtr->ascent + fontPtr->descent + fontPtr->leading));

   if (showing)
      _XrImageText8 (windowId, xrEditorGC2, 1, penX, penY, &cursorChar);
   else
      _XrImageText8 (windowId, xrEditorGC1, 1, penX, penY, &cursorChar);
}

/*************************************<->*************************************
 *
 *  INT32
 *  readALine (peDataPtr, start, newLineFound)
 *
 *     xrPageEditData * peDataPtr;
 *     INT8           * start;
 *     INT32            newLineFound; 
 * 
 *   Description:
 *   -----------
 *     Starting at the specified position within the editing buffer,
 *     this routine will extract a single screen line of data, and
 *     return the number of bytes in the line.  When extracting a line,
 *     it will continue until either the end of the buffer is encountered,
 *     a newline is encountered, or 1 full screen line is obtained.
 *     In addition, upon return, the 'newLineFound' parameter will specify
 *     if it was a newline character which caused the scan to stop.
 *     The starting point must be at the beginning of a screen line, or
 *     else this routine will not work correctly.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This is a pointer to the internal data structure
 *                 associated with the instance being manipulated.
 *
 *     start = This is the point at which the scan will start. IT MUST 
 *             POINT AT THE BEGINNING OF A LINE, OR THIS ROUTINE WILL
 *             NOT WORK!!
 *
 *     newLineFound = If the scan stops because a newline character is
 *                    encountered, then this will be set to TRUE; for
 *                    any other stopping reason, this will be set to
 *                    FALSE.
 * 
 *   Outputs:
 *   -------
 *     Upon completion, the value pointed to by 'newLineFound' will be
 *          set as described above; also, the number of bytes in the
 *          line will be returned.
 *
 *   Procedures Called
 *   -----------------
 *   getCharWidth()
 *
 *************************************<->***********************************/

static
INT32
readALine (peDataPtr, start, newLineFound)

   register xrPageEditData * peDataPtr;
   register INT8           * start;
            INT32          * newLineFound;

{
   register INT8 * end = start;
   register INT32  cursorCol = 0;
   register INT8 * EOF = peDataPtr->peBuffer + peDataPtr->peBufferCount;

   *newLineFound = FALSE;

   /* Extract upto a single display line */
   while ((end < EOF) && (cursorCol < peDataPtr->peColNum) && 
          (*newLineFound == FALSE))
   {
      if (*end == '\n')
         *newLineFound = TRUE;
      cursorCol += getCharWidth (peDataPtr, end, cursorCol);
      end++;
   }

   return (end - start);
}


/*************************************<->*************************************
 *
 *  INT32
 *  printALine (pageEdit, peDataPtr, start, count, line)
 *
 *     xrEditor       * pageEdit;
 *     xrPageEditData * peDataPtr;
 *     INT8           * start;
 *     INT32            count;
 *     INT32            line;
 *
 *   Description:
 *   -----------
 *     This routine will print a single screen line starting at the
 *     screen line specified by the 'line' parameter; It does not
 *     print lines starting at any point other than the left margin.
 *     If a line has a newline character at the end, 'count' should
 *     not include this; this routine does not know how to handle a
 *     newline character.  A routine calling this can use the 'line'
 *     parameter to get the effect of a newline.
 *
 *     If the instance is sensitive, then the text is displayed using
 *     the foreground color.  If the instance is insensitive, then the
 *     text is displayed in a hashed form.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = This is the editor instance pointer.
 *
 *     peDataPtr = This points to the instance's internal data.
 *
 *     start = This points to the start of the line, within the buffer.
 *
 *     count = This specifies the number of characters in the line.
 *
 *     line = This specifies the screen line at which the text is to appear.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrImageText8()  [textUtil.c]
 *   XrStringWidth()  [utilities.c]
 *
 *************************************<->***********************************/

static
INT32
printALine (pageEdit, peDataPtr, start, count, line)

   xrEditor * pageEdit;
   xrPageEditData * peDataPtr;
   INT8 * start;
   INT32 count;
   INT32 line;

{
   INT16 penX, penY, lineHeight;
   INT8 * EOF = start+count;
   INT8 * end;
   INT8 * printBufPtr;
   INT8 printBuf[2];
   INT32 printCount;
   INT32 printCol;


   /* Calculate the pen position */
   lineHeight = peDataPtr->peFont.ascent + peDataPtr->peFont.descent +
                peDataPtr->peFont.leading;
   penX = pageEdit->editorRect.x + BORDER + PADDING;
   penY = pageEdit->editorRect.y + BORDER + PADDING + (line * lineHeight);


   printCol = 0;
   for (end = start; end != EOF; start = end)
   {
      if (*start == '\t')                     /* tab */
      {
         printBufPtr = peDataPtr->peBlanks;
         printCount = getCharWidth (peDataPtr, start, printCol);
         if ((printCol + printCount) > peDataPtr->peColNum)
            printCount = peDataPtr->peColNum - printCol;
         printCol += printCount;
         end = start + 1;
      }
      else if ((*start & '\340') == 0)         /* control char */
      {
         printBufPtr = printBuf;
         printBuf[0] = '^';
         printBuf[1] = *start | '\100';
         printCount = 2;
         if ((printCol + printCount) > peDataPtr->peColNum)
            printCount = peDataPtr->peColNum - printCol;
         printCol += printCount;
         end = start + 1;
      }
      else                                    /* normal char */
      {
         printBufPtr = start;
         printCount = 0;
         while ((end != EOF) && 
                (*end != '\t') &&
                ((*end & '\340') != 0))
         {
            printCount++;
            printCol++;
            end++;
         }
      }

      if (pageEdit->editorState & XrSENSITIVE)
         _XrImageText8 (pageEdit->editorWindowId, xrEditorGC1, printCount,
                        penX, penY, printBufPtr);
      else
      {
         /* Draw the text as insensitive */
         _XrImageText8 (pageEdit->editorWindowId, xrEditorGC4, printCount,
                        penX, penY, printBufPtr);
         _XrImageText8 (pageEdit->editorWindowId, xrEditorGC5, printCount,
                        penX, penY, printBufPtr);
      }

      penX = penX + XrStringWidth (peDataPtr->peFont.fontInfo, 
                    printBufPtr, printCount, 0, 0);
   }

   /* pad out the rest of the line */
   if (pageEdit->editorState & XrSENSITIVE)
      _XrImageText8 (pageEdit->editorWindowId, xrEditorGC1, 
                     peDataPtr->peColNum - printCol, penX, penY,
                     peDataPtr->peBlanks);
   else
   {
      _XrImageText8 (pageEdit->editorWindowId, xrEditorGC4, 
                     peDataPtr->peColNum - printCol, penX, penY,
                     peDataPtr->peBlanks);
      _XrImageText8 (pageEdit->editorWindowId, xrEditorGC5, 
                     peDataPtr->peColNum - count, penX, penY,
                     peDataPtr->peBlanks);
   }
}

/*************************************<->*************************************
 *
 *  INT32
 *  displayBuffer (pageEdit, peDataPtr, firstLine)
 *
 *     xrEditor       * pageEdit;
 *     xrPageEditData * peDataPtr;
 *     INT32            firstLine;
 *
 *   Description:
 *   -----------
 *     This routine displays the contents of the editing buffer, starting
 *     at the screen line specified by 'firstLine', and ending at the end
 *     of the editing region.  It is used to repaint the screen, after
 *     most editing functions.
 *
 *
 *   Inputs:
 *   ------
 *     pageEdit = This is the instance pointer.
 *
 *     peDataPtr = This points to the instance's internal data.
 *
 *     firstLine = This specifies where, in screen lines, the redisplay
 *                 is to start.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   readALine()
 *   printALine()
 *
 *************************************<->***********************************/

static
INT32
displayBuffer (pageEdit, peDataPtr, firstLine, stopAtLine)

   register xrEditor       * pageEdit;
   register xrPageEditData * peDataPtr;
   register INT32            firstLine;
   register INT32            stopAtLine;

{
   register INT8 * start = peDataPtr->peTopOfFrame;
   register INT8 * EOB;
   register INT32  currentLine = 0;
            INT32  count;
            INT32  newLineFound;
            INT32  lineHt;
            RECTANGLE drawRect;

   EOB = peDataPtr->peBuffer + peDataPtr->peBufferCount;
   if (stopAtLine > peDataPtr->peRowNum)
      stopAtLine = peDataPtr->peRowNum;
   if (firstLine < 0)
      firstLine = 0;

   /* Skip to the request line */
   while (currentLine < firstLine)
   {
      count = readALine (peDataPtr, start, &newLineFound);
      currentLine++;
      start += count;
   }

   /* Display the request lines */
   while (firstLine < stopAtLine)
   {
      /* Check for end of buffer */
      if (start >= EOB)
      {
         /* Fill the remainder of the window */
         lineHt = peDataPtr->peFont.ascent + peDataPtr->peFont.descent +
                  peDataPtr->peFont.leading;
         drawRect.x = pageEdit->editorRect.x + BORDER + PADDING;
         drawRect.y = pageEdit->editorRect.y + BORDER + PADDING +
                      (firstLine * lineHt);
         drawRect.height = (peDataPtr->peRowNum - firstLine) * lineHt;
         drawRect.width = pageEdit->editorRect.width - (BORDER << 1) -
                          (PADDING << 1);

         _XrFillRectangle (pageEdit->editorWindowId, xrEditorGC2, &drawRect);
         break;
      }

      count = readALine (peDataPtr, start, &newLineFound);
      printALine (pageEdit, peDataPtr, start, 
                  (newLineFound ? count - 1 : count), firstLine);
      firstLine ++;
      start += count;
   }
}


/*************************************<->*************************************
 *
 *  INT8 *
 *  getStartOfPreviousLine (peDataPtr, bufPtr, count, mode)
 *
 *     xrPageEditData * peDataPtr;
 *     INT8           * bufPtr;
 *     INT32          * count;
 *     INT32            mode;
 *
 *   Description:
 *   -----------
 *     Given a pointer to the end of a screen line, this routine will
 *     return a pointer to the start of that line.  If mode is set to
 *     "SCREEN", then it will be the start of that screen line (not
 *     including any wrapping to a previous screen line).  If mode is
 *     set to "BUFFER", then it will be the start of complete line.
 *     In addition, it also returns the number of characters in the
 *     line, including the NEWLINE character if ther is one.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This points to the instance's internal data.
 *
 *     bufPtr = This is the pointer to the end of a screen line.
 *
 *     count = This points to an INT32 value, into which the number
 *             of bytes in the line minus 1 is placed.
 *
 *     mode = SCREEN or BUFFER, as described above.
 * 
 *   Outputs:
 *   -------
 *     Returns a pointer to the start of the line, along with the
 *             number of bytes (as described above).
 *
 *   Procedures Called
 *   -----------------
 *   readALine()
 *   getScreenCol()
 *
 *************************************<->***********************************/

INT8 *
getStartOfPreviousLine (peDataPtr, bufPtr, count, mode)

   register xrPageEditData * peDataPtr;
   register INT8           * bufPtr;
   register INT32          * count;
            INT32            mode;

{
   register INT8 * nextLinePtr;
   register INT8 * currentLinePtr = bufPtr;
            INT32  newLineFound;
   register INT8 * EOF = peDataPtr->peBuffer + peDataPtr->peBufferCount;

   *count = 1;

   /* Look for the start of the previous line */
   while((currentLinePtr != peDataPtr->peBuffer) &&
         (*(currentLinePtr - 1) != '\n'))
   {
      (*count)++;
      currentLinePtr--;
   }

   /* Determine how much of the line fits */
   if (mode == SCREEN)
   {
      /* Scan forward for screen line that contains bufPtr */
      nextLinePtr = currentLinePtr;
      while ((nextLinePtr <= bufPtr) && (nextLinePtr != EOF))
      {
          currentLinePtr = nextLinePtr;
          nextLinePtr += readALine (peDataPtr, currentLinePtr, &newLineFound);
      }

      /* wrap if bufPtr is at end of last line that is full */
      if ((bufPtr == EOF) && 
          (getScreenCol(peDataPtr, currentLinePtr, 
                        (INT32)(nextLinePtr - currentLinePtr)) >= 
           peDataPtr->peColNum))
         currentLinePtr = nextLinePtr;

      *count = nextLinePtr - currentLinePtr;
   }

   return (currentLinePtr);
}


/*************************************<->*************************************
 *
 *  INT32
 *  getLineCount (peDataPtr, bufPtr)
 *
 *     xrPageEditData * peDataPtr;
 *     INT8           * bufPtr;
 *
 *   Description:
 *   -----------
 *     Starting at the beginning of a screen line, as specified by the
 *     'bufPtr' parameter, this routine will determine how many screen
 *     lines are occupied by that line.  The scan continues until either
 *     the end of the buffer is encountered, or a newline is found.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This points to the instance's internal data.
 *
 *     bufPtr = This points to the start of the screen line.
 * 
 *   Outputs:
 *   -------
 *     The number of screen lines is returned.
 *
 *   Procedures Called
 *   -----------------
 *   readALine()
 *   getScreenCol()
 *
 *************************************<->***********************************/

static
INT32
getLineCount (peDataPtr, bufPtr)

   register xrPageEditData * peDataPtr;
   register INT8           * bufPtr;

{
   register INT32   lineCount = 0;
   register INT32   count;
   register INT8  * EOF = peDataPtr->peBuffer + peDataPtr->peBufferCount;
            INT32   newLineFound = FALSE;

   while (!newLineFound)
   {
      count = readALine (peDataPtr, bufPtr, &newLineFound);
      lineCount++;

      if (bufPtr + count == EOF)
      {
         /* Count extra line when full last line wraps */
         if (getScreenCol(peDataPtr, bufPtr, count) >= peDataPtr->peColNum)
            lineCount++;
         break;
      }
      bufPtr += count;
   }

   return (lineCount);
}

/*************************************<->*************************************
 *
 *  INT32
 *  getCharWidth (peDataPtr, chrPtr, chrCol)
 *
 *     xrPageEditData * peDataPtr;
 *     INT8           * chrPtr;
 *     INT32          * chrCol;
 *
 *   Description:
 *   -----------
 *     Determines the display width of the character pointed to by
 *     'chrPtr', which is to be displayed starting in column 'chrCol'
 *     in the display.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This points to the instance's internal data.
 *
 *     chrPtr = This points to the character whose display width is required
 * 
 *     chrCol = This is the column in which the character is displayed
 *
 *   Outputs:
 *   -------
 *     The display width of the character
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
getCharWidth (peDataPtr, chrPtr, chrCol)

   xrPageEditData * peDataPtr;
   INT8           * chrPtr;
   INT32            chrCol;
{
   register INT32 chrWidth;

   if (*chrPtr == '\t')                 /* tab character */
      chrWidth = peDataPtr->peTabWidth - (chrCol % peDataPtr->peTabWidth);

   else if (*chrPtr == '\n')            /* newline character */
      chrWidth = 1;
   else if ((*chrPtr == '\010') || (*chrPtr == '\177'))  /* backspace */
      chrWidth = -1;
   else if ((*chrPtr & '\340') == 0)    /* control character */
      chrWidth = 2;
   else                                 /* normal character */
      chrWidth = 1;

   return (chrWidth);
}


/*************************************<->*************************************
 *
 *  INT32
 *  getScreenCol (peDataPtr, bufStartPtr, bufCol)
 *
 *     xrPageEditData * peDataPtr;
 *     INT8           * bufStartPtr
 *     INT32            bufCol
 * 
 *   Description:
 *   -----------
 *     Determines the screen column of a character given the buffer column
 *     and a pointer to beginning of the line
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This is a pointer to the internal data structure
 *                 associated with the instance being manipulated.
 *
 *     bufStartPtr = This is a pointer to the character position in the buffer
 *             POINT AT THE BEGINNING OF A LINE, OR THIS ROUTINE WILL
 *             NOT WORK!!
 *
 *     bufCol = This is the byte offset in the buffer from the start of line
 * 
 *   Outputs:
 *   -------
 *     Returns the screen column corresponding to the given buffer column
 *
 *   Procedures Called
 *   -----------------
 *   getCharWidth()
 *
 *************************************<->***********************************/

static
INT32
getScreenCol (peDataPtr, bufStartPtr, bufCol)

   xrPageEditData * peDataPtr;
   INT8           * bufStartPtr;
   INT32          bufCol;

{
   INT32  screenCol = 0;
   register INT32 i;

   for (i=0; i < bufCol; i++)
   {
      screenCol += getCharWidth (peDataPtr, bufStartPtr, screenCol);
      bufStartPtr++;
   }

   return (screenCol);
}


/*************************************<->*************************************
 *
 *  INT32
 *  getBufCol (peDataPtr, bufStartPtr, screenCol)
 *
 *     xrPageEditData * peDataPtr;
 *     INT8           * bufStartPtr
 *     INT32            screenCol
 * 
 *   Description:
 *   -----------
 *     Determines the buffer column of a character given the screen column
 *     and a pointer to beginning of the line
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = This is a pointer to the internal data structure
 *                 associated with the instance being manipulated.
 *
 *     bufStartPtr = This is a pointer to the character position in the buffer
 *             POINT AT THE BEGINNING OF A LINE, OR THIS ROUTINE WILL
 *             NOT WORK!!
 *
 *     screenCol = This is the screen position of the character in the line
 * 
 *   Outputs:
 *   -------
 *     Returns the buffer column corresponding to the given screen column
 *
 *   Procedures Called
 *   -----------------
 *   getCharWidth()
 *
 *************************************<->***********************************/

static
INT32
getBufCol (peDataPtr, bufStartPtr, screenCol)

   xrPageEditData * peDataPtr;
   INT8           * bufStartPtr;
   INT32          screenCol;

{
   INT8           * bufPtr = bufStartPtr;
   register INT32   i  = 0;

   while ((i += getCharWidth (peDataPtr, bufPtr, i)) <= screenCol)
      bufPtr++;

   return (bufPtr - bufStartPtr);
}


/*************************************<->*************************************
 *
 *  INT32
 *  expandBuffer (peDataPtr, exitFlag, returnEvent)
 *
 *     xrPageEditData * peDataPtr;
 *     INT8           * exitFlag;
 *     xrEvent        * returnEvent;
 *
 *   Description:
 *   -----------
 *     This routine checks to see if the buffer has reached its end.
 *     If so, one of several things will happen.  If the buffer is as
 *     large as the application will allow it, then a "BUFFER FULL"
 *     event will be pushed onto the front of the input queue, and
 *     control will return to the application.  Otherwise, an attempt
 *     will be made to expand the buffer.  If this succeeds, then a
 *     "BUFFER EXPANDED" event will be pushed onto the front of the
 *     input queue, and control will return to the application.  If
 *     the buffer expansion fails, then a "BUFFER EXPAND FAILED" event
 *     will be pushed onto the front of the input queue, and control
 *     will return to the application.
 *
 *
 *   Inputs:
 *   ------
 *     peDataPtr = Pointer to the instance's internal data.
 *
 *     exitFlag = Set to TRUE if control must be returned to the
 *                application, because an event was pushed.
 *
 *     returnEvent = Pointer to a partially filled out X-ray event.
 *                   This will be used to push any return events.
 * 
 *   Outputs:
 *   -------
 *     If the buffer cannot be expanded, for one reason or another, then
 *        a value of TRUE is returned; otherwise, FALSE is returned.  A
 *        TRUE value tells the routine which called us that it is NOT
 *        safe to add anymore characters.
 *
 *   Procedures Called
 *   -----------------
 *   XrInput()  [input.c]
 *
 *************************************<->***********************************/

static
INT32
expandBuffer (peDataPtr, exitFlag, returnEvent)

   xrPageEditData * peDataPtr;
   INT8           * exitFlag;
   xrEvent        * returnEvent;

{   
   register INT8  * bufr;
            INT32   delta;
            INT32   cursorOffset;
            INT32   frameOffset;

   if (peDataPtr->peBufferCount == peDataPtr->peMaxSize)
   {
      /* The buffer has reached the application imposed limit */
      returnEvent->value1 = XrBUFFER_FULL;
      *exitFlag = TRUE;
      XrInput (NULL, MSG_PUSHEVENT, returnEvent);
      return (TRUE);
   }
   else if (peDataPtr->peBufferCount == peDataPtr->peBufferSize)
   {
      /* Determine how much to expand the buffer by */
      delta = ((peDataPtr->peMaxSize == -1) ||
             (peDataPtr->peMaxSize - peDataPtr->peBufferSize > 5120)) ?
             5120 : (peDataPtr->peMaxSize - peDataPtr->peBufferSize);
      cursorOffset = peDataPtr->peCursorPtr - peDataPtr->peBuffer;
      frameOffset = peDataPtr->peTopOfFrame - peDataPtr->peBuffer;

      /* Attempt to expand the buffer */
      if ((bufr = (char *) (*xrRealloc)(peDataPtr->peBuffer, delta)) == NULL)
      {
         /* Buffer expansion failed */
         returnEvent->value1 = XrBUFEXPANDFAILED;
         *exitFlag = TRUE;
         XrInput (NULL, MSG_PUSHEVENT, returnEvent);
         return (TRUE);
      }
      else
      {
         /*
          * Tell the application the buffer has expanded, and potentially
          * moved.
          */
         returnEvent->value1 = XrBUFEXPANDED;
         *exitFlag = TRUE;
         XrInput (NULL, MSG_PUSHEVENT, returnEvent);
         peDataPtr->peBuffer = bufr;
         peDataPtr->peBufferSize += delta;
         peDataPtr->peCursorPtr = peDataPtr->peBuffer + cursorOffset;
         peDataPtr->peTopOfFrame = peDataPtr->peBuffer + frameOffset;
         return (FALSE);
      }
   }

   return (FALSE);
}

/*************************************<->*************************************
 *
 *  ProcedureName (parameters)
 *
 *   Description:
 *   -----------
 *     xxxxxxxxxxxxxxxxxxxxxxx
 *
 *
 *   Inputs:
 *   ------
 *     xxxxxxxxxxxx = xxxxxxxxxxxxx
 * 
 *   Outputs:
 *   -------
 *     xxxxxxxxxxxx = xxxxxxxxxxxxx
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
blockCopy (pageEdit, peDataPtr, direction, startLine)

   register xrEditor       * pageEdit;
   register xrPageEditData * peDataPtr;
            INT32            direction;
            INT32            startLine;

{
   INT32 lineHt;
   INT32 X, srcY, dstY;
   INT32 height, width;

   lineHt = peDataPtr->peFont.ascent + peDataPtr->peFont.descent +
            peDataPtr->peFont.leading;

   /* Determine the region to be moved */
   X = pageEdit->editorRect.x + PADDING + BORDER;
   srcY = pageEdit->editorRect.y + PADDING + BORDER +
          (startLine * lineHt);
   width = pageEdit->editorRect.width - (BORDER << 1) - (PADDING << 1);

   if (direction == UP)
   {
      dstY = pageEdit->editorRect.y + PADDING + BORDER + 
             ((startLine - 1) * lineHt);
      height = lineHt * (peDataPtr->peRowNum - startLine);
   }
   else
   {
      dstY = pageEdit->editorRect.y + PADDING + BORDER +
             ((startLine + 1) * lineHt);
      height = lineHt * (peDataPtr->peRowNum - startLine - 1);
   }

   XMoveArea (pageEdit->editorWindowId, X, srcY, X, dstY, width, height);
}
