home *** CD-ROM | disk | FTP | other *** search
/ synchro.net / synchro.net.tar / synchro.net / main / BBS / ODOORS62.ZIP / ODEdit.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-19  |  100.0 KB  |  2,645 lines

  1. /* OpenDoors Online Software Programming Toolkit
  2.  * (C) Copyright 1991 - 1999 by Brian Pirie.
  3.  *
  4.  * Oct-2001 door32.sys/socket modifications by Rob Swindell (www.synchro.net)
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  *
  20.  *
  21.  *        File: ODEdit.c
  22.  *
  23.  * Description: Implementation of the OpenDoors multi-line editor, which
  24.  *              allows the user to edit strings which may span many lines.
  25.  *              Provides standard text editor features, such as word wrap.
  26.  *
  27.  *   Revisions: Date          Ver   Who  Change
  28.  *              ---------------------------------------------------------------
  29.  *              Dec 07, 1995  6.00  BP   Created.
  30.  *              Dec 12, 1995  6.00  BP   Added entry, exit and kernel macros.
  31.  *              Dec 30, 1995  6.00  BP   Added ODCALL for calling convention.
  32.  *              Jan 04, 1996  6.00  BP   Use od_get_input().
  33.  *              Jan 12, 1996  6.00  BP   Claim exclusive use of arrow keys.
  34.  *              Jan 31, 1996  6.00  BP   Added timeout for od_get_input().
  35.  *              Feb 08, 1996  6.00  BP   Finished implementation details.
  36.  *              Feb 13, 1996  6.00  BP   Added od_get_input() flags parameter.
  37.  *              Feb 16, 1996  6.00  BP   New trans. size estimation heuristics.
  38.  *              Feb 19, 1996  6.00  BP   Changed version number to 6.00.
  39.  *              Feb 24, 1996  6.00  BP   Fixed garbage on [Enter] after w-wrap.
  40.  *              Mar 03, 1996  6.10  BP   Begin version 6.10.
  41.  *              Mar 13, 1996  6.10  BP   Restore cursor position after menu.
  42.  *              Mar 19, 1996  6.10  BP   MSVC15 source-level compatibility.
  43.  *              Jun 08, 1996  6.10  BP   Added cast in call to alloc function.
  44.  *              Oct 19, 2001  6.20  RS   Eliminated MSVC 6.0 warning.
  45.  */
  46.  
  47. #define BUILDING_OPENDOORS
  48.  
  49. #include <string.h>
  50. #include <stdlib.h>
  51. #include <stdio.h>
  52.  
  53. #include "OpenDoor.h"
  54. #include "ODTypes.h"
  55. #include "ODGen.h"
  56. #include "ODCore.h"
  57. #include "ODKrnl.h"
  58. #include "ODStat.h"
  59. #include "ODCom.h"
  60. #include "ODScrn.h"
  61.  
  62.  
  63. /* ========================================================================= */
  64. /* Misc. definitions.                                                        */
  65. /* ========================================================================= */
  66.  
  67. /* Macros used by this module. */
  68. #define IS_EOL_CHAR(ch)       ((ch) == '\n' || (ch) == '\r' || (ch) == '\0')
  69.  
  70. /* Configurable constants. */
  71. #define LINE_ARRAY_GROW_SIZE     20
  72. #define BUFFER_GROW_SIZE         4096
  73. #define ANSI_SCROLL_DISTANCE     7
  74. #define PRE_DRAIN_TIME           10000
  75. #define MAX_TAB_STOP_SIZE        8
  76. #define DEFAULT_TAB_STOP_SIZE    8
  77. #define DEFAULT_LINE_BREAK       "\n"
  78.  
  79. /* Other manifest constants. */
  80. #define REDRAW_NO_BOUNDARY       0xffff
  81.  
  82. /* Default editor options. */
  83. static tODEditOptions ODEditOptionsDefault =
  84. {
  85.    1, 1, 80, 23,
  86.    FORMAT_PARAGRAPH_BREAKS,
  87.    NULL,
  88.    NULL,
  89.    EFLAG_NORMAL,
  90. };
  91.  
  92.  
  93.  
  94. /* ========================================================================= */
  95. /* Multiline editor instance structure.                                      */
  96. /* ========================================================================= */
  97.  
  98. typedef struct
  99. {
  100.    char *pszEditBuffer;
  101.    UINT unBufferSize;
  102.    tODEditOptions *pUserOptions;
  103.    UINT unCurrentLine;
  104.    UINT unCurrentColumn;
  105.    UINT unLineScrolledToTop;
  106.    UINT unAreaWidth;
  107.    UINT unAreaHeight;
  108.    char **papchStartOfLine;
  109.    UINT unLineArraySize;
  110.    UINT unLinesInBuffer;
  111.    BOOL bInsertMode;
  112.    UINT unTabStopSize;
  113.    UINT unScrollDistance;
  114.    char *pszLineBreak;
  115.    char *pszParagraphBreak;
  116.    BOOL bWordWrapLongLines;
  117.    void *pRememberBuffer;
  118. } tEditInstance;
  119.  
  120.  
  121.  
  122. /* ========================================================================= */
  123. /* Editor function prototypes.                                               */
  124. /* ========================================================================= */
  125.  
  126. /* High level implementation. */
  127. static BOOL ODEditSetupInstance(tEditInstance *pEditInstance,
  128.    char *pszBufferToEdit, UINT unBufferSize, tODEditOptions *pUserOptions);
  129. static void ODEditRedrawArea(tEditInstance *pEditInstance);
  130. static void ODEditDrawAreaLine(tEditInstance *pEditInstance,
  131.    UINT unAreaLineToDraw);
  132. static INT ODEditMainLoop(tEditInstance *pEditInstance);
  133. static void ODEditGotoPreviousLine(tEditInstance *pEditInstance);
  134. static void ODEditGotoNextLine(tEditInstance *pEditInstance);
  135. static BOOL ODEditScrollArea(tEditInstance *pEditInstance, INT nDistance);
  136. static BOOL ODEditRecommendFullRedraw(tEditInstance *pEditInstance,
  137.    UINT unEstPartialRedrawBytes, BOOL bDefault);
  138. static UINT ODEditEstDrawBytes(tEditInstance *pEditInstance,
  139.    UINT unStartRedrawLine, UINT unStartRedrawColumn, UINT unFinishRedrawLine,
  140.    UINT unFinishRedrawColumn);
  141. static UINT ODEditGetCurrentLineInArea(tEditInstance *pEditInstance);
  142. static void ODEditUpdateCursorPos(tEditInstance *pEditInstance);
  143. static void ODEditUpdateCursorIfMoved(tEditInstance *pEditInstance);
  144. static tODResult ODEditEnterText(tEditInstance *pEditInstance,
  145.    char *pszEntered, BOOL bInsertMode);
  146. static void ODEditSetBreakSequence(tEditInstance *pEditInstance,
  147.    char chFirstEOLChar, char chSecondEOLChar);
  148. static BOOL ODEditCursorLeft(tEditInstance *pEditInstance);
  149. static void ODEditDeleteCurrentChar(tEditInstance *pEditInstance);
  150. static void ODEditDeleteCurrentLine(tEditInstance *pEditInstance);
  151. static BOOL ODEditPastEndOfCurLine(tEditInstance *pEditInstance);
  152. static size_t ODEditRememberBufferSize(tEditInstance *pEditInstance);
  153. static void ODEditRememberArea(tEditInstance *pEditInstance,
  154.    void *pRememberedArea);
  155. static void ODEditRedrawChanged(tEditInstance *pEditInstance,
  156.    void *pRememberedArea, UINT unUpperBoundary, UINT unLowerBoundary);
  157. static BOOL ODEditDetermineChanged(tEditInstance *pEditInstance,
  158.    void *pRememberedArea, UINT unUpperBoundary, UINT unLowerBoundary, 
  159.    UINT *punStartRedrawLine, UINT *punStartRedrawColumn,
  160.    UINT *punFinishRedrawLine, UINT *punFinishRedrawColumn);
  161. static void ODEditRedrawSubArea(tEditInstance *pEditInstance,
  162.    UINT unStartRedrawLine, UINT unStartRedrawColumn, UINT unFinishRedrawLine,
  163.    UINT unFinishRedrawColumn);
  164. static void ODEditGetActualCurPos(tEditInstance *pEditInstance,
  165.    UINT *punRow, UINT *punColumn);
  166. static BOOL ODEditIsEOLForMode(tEditInstance *pEditInstance, char chToTest);
  167.  
  168. /* Low level buffer manipulation functions. */
  169. static BOOL ODEditBufferFormatAndIndex(tEditInstance *pEditInstance);
  170. static UINT ODEditBufferGetLineLength(tEditInstance *pEditInstance,
  171.    UINT unBufferLine);
  172. static UINT ODEditBufferGetTotalLines(tEditInstance *pEditInstance);
  173. static char *ODEditBufferGetCharacter(tEditInstance *pEditInstance,
  174.    UINT unBufferLine, UINT unBufferColumn);
  175. static tODResult ODEditBufferMakeSpace(tEditInstance *pEditInstance,
  176.    UINT unLine, UINT unColumn, UINT unNumChars);
  177. static tODResult ODEditTryToGrow(tEditInstance *pEditInstance,
  178.    UINT unSizeNeeded);
  179.  
  180.  
  181.  
  182. /* ========================================================================= */
  183. /* High level editor implementation.                                         */
  184. /* ========================================================================= */
  185.  
  186. /* ----------------------------------------------------------------------------
  187.  * od_multiline_edit()
  188.  *
  189.  * Multiline editor function, allows the user to enter or change text that
  190.  * spans multiple lines.
  191.  *
  192.  * Parameters: pszBufferToEdit  - Pointer to '\0'-terminated buffer of text
  193.  *                                to edit.
  194.  *
  195.  *             unBufferSize     - Size of the buffer, in characters.
  196.  *
  197.  *             pEditOptions     - Pointer to a tODEditOptions structure, or
  198.  *                                NULL to use default settings.
  199.  *
  200.  *     Return: An od_multiline_edit()-specific result code.
  201.  */
  202. ODAPIDEF INT ODCALL od_multiline_edit(char *pszBufferToEdit, UINT unBufferSize,
  203.    tODEditOptions *pEditOptions)
  204. {
  205.    tEditInstance EditInstance;
  206.    INT nToReturn;
  207.  
  208.    /* Log function entry if running in trace mode */
  209.    TRACE(TRACE_API, "od_node_open()");
  210.  
  211.    /* Initialize OpenDoors if not already done. */
  212.    if(!bODInitialized) od_init();
  213.  
  214.    OD_API_ENTRY();
  215.  
  216.    /* Validate parameters. */
  217.    if(pszBufferToEdit == NULL || unBufferSize == 0)
  218.    {
  219.       od_control.od_error = ERR_PARAMETER;
  220.       OD_API_EXIT();
  221.       return(OD_MULTIEDIT_ERROR);
  222.    }
  223.  
  224.    /* Check the user's terminal supports the required capabilities. */
  225.    if(!(od_control.user_ansi || od_control.user_avatar))
  226.    {
  227.       od_control.od_error = ERR_NOGRAPHICS;
  228.       OD_API_EXIT();
  229.       return(OD_MULTIEDIT_ERROR);
  230.    }
  231.  
  232.    /* Initialize editor instance information structure. */
  233.    if(!ODEditSetupInstance(&EditInstance, pszBufferToEdit, unBufferSize,
  234.       pEditOptions))
  235.    {
  236.       OD_API_EXIT();
  237.       return(OD_MULTIEDIT_ERROR);
  238.    }
  239.  
  240.    /* Attempt to build the buffer line index and ensure that the buffer */
  241.    /* conforms to the format specified by the client application.       */
  242.    if(!ODEditBufferFormatAndIndex(&EditInstance))
  243.    {
  244.       od_control.od_error = ERR_MEMORY;
  245.       OD_API_EXIT();
  246.       return(OD_MULTIEDIT_ERROR);
  247.    }
  248.  
  249.    /* Claim exclusive use of arrow keys. */
  250.    ODStatStartArrowUse();
  251.  
  252.    /* Ensure that all information in the outbound communications buffer  */
  253.    /* has been sent before starting. This way, we can safely purge the   */
  254.    /* outbound buffer at any time without loosing anything that was sent */
  255.    /* before od_multiline_edit() was called.                             */
  256.    ODWaitDrain(PRE_DRAIN_TIME);
  257.  
  258.    /* Draw the initial edit area. */
  259.    ODEditRedrawArea(&EditInstance);
  260.  
  261.    /* Run the main editor loop. */
  262.    nToReturn = ODEditMainLoop(&EditInstance);
  263.  
  264.    /* Release exclusive use of arrow keys. */
  265.    ODStatEndArrowUse();
  266.  
  267.    /* Set final information which will be available in the user options */
  268.    /* structure for the client application to access.                   */
  269.    EditInstance.pUserOptions->pszFinalBuffer = EditInstance.pszEditBuffer;
  270.    EditInstance.pUserOptions->unFinalBufferSize = unBufferSize;
  271.  
  272.    OD_API_EXIT();
  273.    return(nToReturn);
  274. }
  275.  
  276.  
  277. /* ----------------------------------------------------------------------------
  278.  * ODEditSetupInstance()                               *** PRIVATE FUNCTION ***
  279.  *
  280.  * Initializes editor instance information structure.
  281.  *
  282.  * Parameters: pEditInstance   - Editor instance information structure.
  283.  *
  284.  *             pszBufferToEdit - Buffer pointer provided by client.
  285.  *
  286.  *             unBufferSize    - Initial buffer size, as specified by the
  287.  *                               client.
  288.  *
  289.  *             pUserOptions    - Editor options specified by the client.
  290.  *
  291.  *     Return: TRUE on success, FALSE on failure. In the case of failure,
  292.  *             od_control.od_error is set appropriately.
  293.  */
  294. static BOOL ODEditSetupInstance(tEditInstance *pEditInstance,
  295.    char *pszBufferToEdit, UINT unBufferSize, tODEditOptions *pUserOptions)
  296. {
  297.    ASSERT(pEditInstance != NULL);
  298.    ASSERT(pszBufferToEdit != NULL);
  299.  
  300.    /* Setup editor instance structure. */
  301.    pEditInstance->pszEditBuffer = pszBufferToEdit;
  302.    pEditInstance->unBufferSize = unBufferSize;
  303.    if(pUserOptions == NULL)
  304.    {
  305.       /* Edit options is just the defaults. */
  306.       pEditInstance->pUserOptions = &ODEditOptionsDefault;
  307.    }
  308.    else
  309.    {
  310.       /* Edit options are supplied by the user. */
  311.       pEditInstance->pUserOptions = pUserOptions;
  312.  
  313.       /* Initialize any edit options that the user did not setup. */
  314.       /* Check that edit area has been initialized. */
  315.       if(pUserOptions->nAreaLeft == 0)
  316.       {
  317.          pUserOptions->nAreaLeft = ODEditOptionsDefault.nAreaLeft;
  318.       }
  319.       if(pUserOptions->nAreaRight == 0)
  320.       {
  321.          pUserOptions->nAreaRight = ODEditOptionsDefault.nAreaRight;
  322.       }
  323.         if(pUserOptions->nAreaTop == 0)
  324.       {
  325.          pUserOptions->nAreaTop = ODEditOptionsDefault.nAreaTop;
  326.       }
  327.       if(pUserOptions->nAreaBottom == 0)
  328.       {
  329.          pUserOptions->nAreaBottom = ODEditOptionsDefault.nAreaBottom;
  330.       }
  331.    }
  332.    pEditInstance->unCurrentLine = 0;
  333.    pEditInstance->unCurrentColumn = 0;
  334.    pEditInstance->unLineScrolledToTop = 0;
  335.    pEditInstance->papchStartOfLine = NULL;
  336.    pEditInstance->unLineArraySize = 0;
  337.    pEditInstance->unLinesInBuffer = 0;
  338.    pEditInstance->unAreaWidth = (UINT)pEditInstance->pUserOptions->
  339.       nAreaRight - (UINT)pEditInstance->pUserOptions->nAreaLeft + 1;
  340.    pEditInstance->unAreaHeight = (UINT)pEditInstance->pUserOptions->
  341.       nAreaBottom - (UINT)pEditInstance->pUserOptions->nAreaTop + 1;
  342.    pEditInstance->bInsertMode = TRUE;
  343.    pEditInstance->unTabStopSize = DEFAULT_TAB_STOP_SIZE;
  344.  
  345.    /* Setup line break and paragraph break sequences, if they can be  */
  346.    /* determined at this point. If they can't be determined, set them */
  347.    /* to NULL.                                                        */
  348.    switch(pEditInstance->pUserOptions->TextFormat)
  349.    {
  350.       case FORMAT_FTSC_MESSAGE:
  351.          /* FTSC compliant messages use \r as a paragraph break, and do */
  352.          /* not have any line break characters.                         */
  353.          pEditInstance->pszLineBreak = "";
  354.          pEditInstance->pszParagraphBreak = "\r";
  355.          break;
  356.  
  357.       case FORMAT_PARAGRAPH_BREAKS:
  358.          /* Paragraph break mode only inserts CR/LF sequences at the end  */
  359.          /* of a paragrah, and word-wraps the text that forms a paragrah. */
  360.          pEditInstance->pszLineBreak = "";
  361.          pEditInstance->pszParagraphBreak = NULL;
  362.          break;
  363.  
  364.       case FORMAT_LINE_BREAKS:
  365.       case FORMAT_NO_WORDWRAP:
  366.          /* Line break mode and no word wrap mode both terminate every    */
  367.          /* line of the file with a CR/LF sequence, and have no paragrah  */
  368.          /* terminator. In line break mode, word wrap is enabled, whereas */
  369.          /* it is not in FORMAT_NO_WORDWRAP mode.                         */
  370.          pEditInstance->pszLineBreak = NULL;
  371.          pEditInstance->pszParagraphBreak = "";
  372.          break;
  373.  
  374.       default:
  375.          /* An invalid text format was specified. */
  376.          od_control.od_error = ERR_PARAMETER;
  377.          return(FALSE);
  378.    }
  379.  
  380.    /* Determine whether long lines sould be word wrapped or character */
  381.    /* wrapped.                                                        */
  382.    pEditInstance->bWordWrapLongLines = (pEditInstance->pUserOptions->TextFormat
  383.       != FORMAT_NO_WORDWRAP);
  384.  
  385.    /* Attempt to allocate abuffer for remembered data. */
  386.    pEditInstance->pRememberBuffer =
  387.       malloc(ODEditRememberBufferSize(pEditInstance));
  388.    
  389.    if(pEditInstance->pRememberBuffer == NULL)
  390.    {
  391.       od_control.od_error = ERR_MEMORY;
  392.       return(FALSE);
  393.    }
  394.  
  395.    /* If AVATAR mode or local mode is active, then scroll up or down one */
  396.    /* line at a time.                                                    */
  397.    if(od_control.user_avatar || od_control.baud == 0)
  398.    {
  399.       pEditInstance->unScrollDistance = 1;
  400.    }
  401.    /* In ANSI mode with a remote connection, scroll multiple lines at a */
  402.    /* time. This is the minimum of the default scroll distance, and the */
  403.    /* current height of the edit area - 1.                              */
  404.    else
  405.    {
  406.       pEditInstance->unScrollDistance = MIN(ANSI_SCROLL_DISTANCE,
  407.          pEditInstance->pUserOptions->nAreaBottom
  408.          - pEditInstance->pUserOptions->nAreaTop);
  409.    }
  410.  
  411.    /* Return with success. */
  412.    return(TRUE);
  413. }
  414.  
  415.  
  416. /* ----------------------------------------------------------------------------
  417.  * ODEditRedrawArea()                                  *** PRIVATE FUNCTION ***
  418.  *
  419.  * Redraws the area of the screen used by the editor.
  420.  *
  421.  * Parameters: pEditInstance - Editor instance information structure.
  422.  *
  423.  *     Return: void
  424.  */
  425. static void ODEditRedrawArea(tEditInstance *pEditInstance)
  426. {
  427.    UINT unAreaLine;
  428.  
  429.    ASSERT(pEditInstance != NULL);
  430.  
  431.    ODScrnEnableCaret(FALSE);
  432.  
  433.    /* First, remove anything that is still in the outbound communications */
  434.    /* buffer, since whatever it was, it will no longer be visible after   */
  435.    /* the screen redraw anyhow.                                           */
  436.    if(od_control.baud != 0) ODComClearOutbound(hSerialPort);
  437.  
  438.    /* Loop, drawing every line in the edit area. */
  439.    for(unAreaLine = 0; unAreaLine < pEditInstance->unAreaHeight; ++unAreaLine)
  440.    {
  441.       ODEditDrawAreaLine(pEditInstance, unAreaLine);
  442.    }
  443.  
  444.    ODScrnEnableCaret(TRUE);
  445. }
  446.  
  447.  
  448. /* ----------------------------------------------------------------------------
  449.  * ODEditDrawAreaLine()                                *** PRIVATE FUNCTION ***
  450.  *
  451.  * Redraws the specified line in the area of the screen used by the editor.
  452.  *
  453.  * Parameters: pEditInstance    - Editor instance information structure.
  454.  *
  455.  *             unAreaLineToDraw - 0-based line number in the edit area to be
  456.  *                                redrawn.
  457.  *
  458.  *     Return: void
  459.  */
  460. static void ODEditDrawAreaLine(tEditInstance *pEditInstance,
  461.    UINT unAreaLineToDraw)
  462. {
  463.    UINT unBufferLine;
  464.    UINT unLineLength;
  465.  
  466.    ASSERT(pEditInstance != NULL);
  467.    ASSERT(unAreaLineToDraw >= 0);
  468.    ASSERT(unAreaLineToDraw < pEditInstance->unAreaHeight);
  469.  
  470.    /* Determine the buffer line that is displayed on this screen line. */   
  471.    unBufferLine = unAreaLineToDraw + pEditInstance->unLineScrolledToTop;
  472.  
  473.    /* Position the cursor to the beginning of this line. */
  474.    od_set_cursor((UINT)pEditInstance->pUserOptions->nAreaTop
  475.       + unAreaLineToDraw, (UINT)pEditInstance->pUserOptions->nAreaLeft);
  476.  
  477.    /* If this line is not beyond the end of the buffer. */
  478.    if(unBufferLine < pEditInstance->unLinesInBuffer)
  479.    {
  480.       /* Determine the length of this buffer line. */
  481.       unLineLength = ODEditBufferGetLineLength(pEditInstance, unBufferLine);
  482.  
  483.       od_disp(ODEditBufferGetCharacter(pEditInstance, unBufferLine, 0),
  484.          unLineLength, TRUE);
  485.    }
  486.  
  487.    /* If right edge of edit area aligns with the right edge of the screen. */
  488.    if(pEditInstance->pUserOptions->nAreaRight == OD_SCREEN_WIDTH)
  489.    {
  490.       /* Clear the remainder of this line on the screen. */
  491.       od_clr_line();
  492.    }
  493.    else
  494.    {
  495.       /* Place spaces after the end of the current line, up to right edge of */
  496.       /* the edit area.                                                      */
  497.       od_repeat(' ', (BYTE)(pEditInstance->unAreaWidth - unLineLength));
  498.    }
  499. }
  500.  
  501.  
  502. /* ----------------------------------------------------------------------------
  503.  * ODEditMainLoop()                                    *** PRIVATE FUNCTION ***
  504.  *
  505.  * Implements the main editor loop, which repeatedly waits for input from the
  506.  * user, until the user chooses to exit.
  507.  *
  508.  * Parameters: pEditInstance - Editor instance information structure.
  509.  *
  510.  *     Return: Value to be returned by od_multiline_edit.
  511.  */
  512. static INT ODEditMainLoop(tEditInstance *pEditInstance)
  513. {
  514.    tODInputEvent InputEvent;
  515.  
  516.    ASSERT(pEditInstance != NULL);
  517.  
  518.    /* Set initial cursor position. */
  519.    ODEditUpdateCursorPos(pEditInstance);
  520.  
  521.    /* Loop, obtaining keystrokes until the user chooses to exit. */
  522.    for(;;)
  523.    {
  524.       od_get_input(&InputEvent, OD_NO_TIMEOUT, GETIN_NORMAL);
  525.       if(InputEvent.EventType == EVENT_EXTENDED_KEY)
  526.       {
  527.          switch(InputEvent.chKeyPress)
  528.          {
  529.             case OD_KEY_UP:
  530.                /* If we aren't at the start of the file, then move to the */
  531.                /* previous line.                                          */
  532.                if(pEditInstance->unCurrentLine > 0)
  533.                {
  534.                   ODEditGotoPreviousLine(pEditInstance);
  535.                   ODEditUpdateCursorPos(pEditInstance);
  536.                }
  537.                break;
  538.  
  539.             case OD_KEY_DOWN:
  540.                /* If we aren't at the end of the file, then move to the */
  541.                /* next line.                                            */
  542.                if(pEditInstance->unCurrentLine
  543.                   < ODEditBufferGetTotalLines(pEditInstance) - 1)
  544.                {
  545.                   ODEditGotoNextLine(pEditInstance);
  546.                   ODEditUpdateCursorPos(pEditInstance);
  547.                }
  548.                break;
  549.  
  550.             case OD_KEY_LEFT:
  551.                /* Attempt to move the cursor left. */
  552.                if(ODEditCursorLeft(pEditInstance))
  553.                {
  554.                   /* If it was possible to move the cursor, then update the */
  555.                   /* cursor position on the screen.                         */
  556.                   ODEditUpdateCursorPos(pEditInstance);
  557.                }
  558.                break;
  559.  
  560.             case OD_KEY_RIGHT:
  561.                /* In word wrap mode, we allow the cursor to move up to the */
  562.                /* end of this line, and then wrap around to the next line. */
  563.                if(pEditInstance->bWordWrapLongLines)
  564.                {
  565.                   if(pEditInstance->unCurrentColumn < ODEditBufferGetLineLength
  566.                      (pEditInstance, pEditInstance->unCurrentLine))
  567.                   {
  568.                      ++pEditInstance->unCurrentColumn;
  569.                      ODEditUpdateCursorPos(pEditInstance);
  570.                   }
  571.                   else if(pEditInstance->unCurrentLine
  572.                      < ODEditBufferGetTotalLines(pEditInstance) - 1)
  573.                   {
  574.                      ODEditGotoNextLine(pEditInstance);
  575.                      pEditInstance->unCurrentColumn = 0;
  576.                      ODEditUpdateCursorPos(pEditInstance);
  577.                   }
  578.                }
  579.  
  580.                /* In character wrap mode, we allow the cursor to move up to */
  581.                /* the right edge of the edit area.                          */
  582.                else
  583.                {
  584.                   if(pEditInstance->unCurrentColumn
  585.                      < pEditInstance->unAreaWidth - 1)
  586.                   {
  587.                      ++pEditInstance->unCurrentColumn;
  588.                      ODEditUpdateCursorPos(pEditInstance);
  589.                   }
  590.                }
  591.                break;
  592.  
  593.             case OD_KEY_HOME:
  594.                pEditInstance->unCurrentColumn = 0;
  595.                ODEditUpdateCursorPos(pEditInstance);
  596.                break;
  597.  
  598.             case OD_KEY_END:
  599.                pEditInstance->unCurrentColumn = ODEditBufferGetLineLength(
  600.                   pEditInstance, pEditInstance->unCurrentLine);
  601.                ODEditUpdateCursorPos(pEditInstance);
  602.                break;
  603.  
  604.             case OD_KEY_PGUP:
  605.                if(pEditInstance->unLineScrolledToTop > 0)
  606.                {
  607.                   UINT unDistance = MIN(pEditInstance->unAreaHeight - 1,
  608.                      pEditInstance->unLineScrolledToTop);
  609.                   ODEditScrollArea(pEditInstance, -((INT)unDistance));
  610.                   pEditInstance->unCurrentLine -= unDistance;
  611.                   ODEditUpdateCursorPos(pEditInstance);
  612.                }
  613.                else if(pEditInstance->unCurrentLine != 0)
  614.                {
  615.                   pEditInstance->unCurrentLine = 0;
  616.                   ODEditUpdateCursorPos(pEditInstance);
  617.                }
  618.                break;
  619.  
  620.             case OD_KEY_PGDN:
  621.                if(pEditInstance->unLineScrolledToTop <
  622.                   pEditInstance->unLinesInBuffer - 1)
  623.                {
  624.                   UINT unDistance = MIN(pEditInstance->unAreaHeight - 1,
  625.                      pEditInstance->unLinesInBuffer
  626.                      - pEditInstance->unLineScrolledToTop - 1);
  627.                   ODEditScrollArea(pEditInstance, (INT)unDistance);
  628.                   pEditInstance->unCurrentLine = MIN(
  629.                      pEditInstance->unCurrentLine + unDistance,
  630.                      pEditInstance->unLinesInBuffer - 1);
  631.                   ODEditUpdateCursorPos(pEditInstance);
  632.                }
  633.             break;
  634.  
  635.             case OD_KEY_INSERT:
  636.                /* If the insert key is pressed, then toggle insert mode. */
  637.                pEditInstance->bInsertMode = !pEditInstance->bInsertMode;
  638.                break;
  639.  
  640.             case OD_KEY_DELETE:
  641.                /* Delete the character at the current position. */
  642.                
  643.                /* If we are currently past the end of this line. */
  644.                if(ODEditPastEndOfCurLine(pEditInstance))
  645.                {
  646.                   /* Add spaces to this line to fill it up to the current */
  647.                   /* cursor position.                                     */
  648.                   switch(ODEditBufferMakeSpace(pEditInstance,
  649.                      pEditInstance->unCurrentLine,
  650.                      pEditInstance->unCurrentColumn, 0))
  651.                   {
  652.                      case kODRCUnrecoverableFailure:
  653.                         /* If we encountered an unrecoverable failure, then */
  654.                         /* exit from the editor with a memory allocation    */
  655.                         /* error.                                           */
  656.                         od_control.od_error = ERR_MEMORY;
  657.                         return(OD_MULTIEDIT_ERROR);
  658.  
  659.                      case kODRCSuccess:
  660.                         /* On success, delete the current character. */
  661.                         ODEditDeleteCurrentChar(pEditInstance);
  662.                         break;
  663.  
  664.                      default:
  665.                         /* On any other failure, just beep and continue. */
  666.                         od_putch('\a');
  667.                   }
  668.                }
  669.                else
  670.                {
  671.                   /* If we aren't pas the end of the line, then just do a */
  672.                   /* simple delete character operation.                   */
  673.                   ODEditDeleteCurrentChar(pEditInstance);
  674.                }
  675.                break;
  676.          }
  677.       }
  678.       else if(InputEvent.EventType == EVENT_CHARACTER)
  679.       {
  680.          if(InputEvent.chKeyPress == 25)
  681.          {
  682.             /* Control-Y (delete line) has been pressed. */
  683.             ODEditDeleteCurrentLine(pEditInstance);
  684.          }
  685.          else if(InputEvent.chKeyPress == 26
  686.             || InputEvent.chKeyPress == 27)
  687.          {
  688.             /* Escape or control-Z has been pressed. */
  689.  
  690.             /* If a menu callback function has been provided by */
  691.             /* the client, then call it.                        */
  692.             if(pEditInstance->pUserOptions->pfMenuCallback != NULL)
  693.             {
  694.                /* Call the menu callback function. */
  695.                switch((*pEditInstance->pUserOptions->pfMenuCallback)(NULL))
  696.                {
  697.                   case EDIT_MENU_EXIT_EDITOR:
  698.                      return(OD_MULTIEDIT_SUCCESS);
  699.  
  700.                   case EDIT_MENU_DO_NOTHING:
  701.                      /* Continue in the editor without doing anything. */
  702.                      break;
  703.  
  704.                   default:
  705.                      ASSERT(FALSE);
  706.                }
  707.  
  708.                /* If we are continuing, then restore initial cursor pos. */
  709.                ODEditUpdateCursorPos(pEditInstance);
  710.             }
  711.             else
  712.             {
  713.                /* If a menu key callback function has not been provided, */
  714.                /* then we exit the editor unconditionally.               */
  715.                return(OD_MULTIEDIT_SUCCESS);
  716.             }
  717.          }
  718.          else if(InputEvent.chKeyPress == '\b')
  719.          {
  720.             /* Backspace key has been pressed. */
  721.  
  722.             /* If the cursor is past the end of the line, then we just move */
  723.             /* the cursor left, without deleting any characters. */
  724.             BOOL bDelete = !ODEditPastEndOfCurLine(pEditInstance);
  725.  
  726.             /* Backup the cursor one space. */
  727.             if(ODEditCursorLeft(pEditInstance))
  728.             {
  729.                /* If there was space to move the cursor back to, then       */
  730.                /* proceed and remove the character at the current position. */
  731.                if(bDelete)
  732.                {
  733.                   ODEditDeleteCurrentChar(pEditInstance);
  734.                }
  735.                else
  736.                {
  737.                   /* In this case, we must still show the new cursor */
  738.                   /* position.                                       */
  739.                   ODEditUpdateCursorPos(pEditInstance);
  740.                }
  741.             }
  742.          }
  743.          else if(InputEvent.chKeyPress == '\t')
  744.          {
  745.             char szTextToAdd[MAX_TAB_STOP_SIZE + 1];
  746.             UINT unTargetColumn;
  747.             UINT unTargetDistance;
  748.  
  749.             /* A tab key has been entered. */
  750.  
  751.             /* Determine the column that this will move the cursor to. */
  752.             ASSERT(pEditInstance->unTabStopSize <= MAX_TAB_STOP_SIZE);
  753.             unTargetColumn = ((pEditInstance->unCurrentColumn / pEditInstance->
  754.                unTabStopSize) + 1) * pEditInstance->unTabStopSize;
  755.  
  756.             /* In insert mode, then insert spaces into the buffer. */
  757.             if(pEditInstance->bInsertMode)
  758.             {
  759.                /* Determine the number of columns that we need to advance in */
  760.                /* order to reach this target column.                         */
  761.                unTargetDistance = unTargetColumn -
  762.                   pEditInstance->unCurrentColumn;
  763.                ASSERT(unTargetDistance <= MAX_TAB_STOP_SIZE);
  764.  
  765.                /* Translate this to a string with the appropriate number of */
  766.                /* spaces.                                                   */
  767.                memset(szTextToAdd, ' ', unTargetDistance);
  768.                szTextToAdd[unTargetDistance] = '\0';
  769.  
  770.                /* Add this to the buffer. */
  771.                if(ODEditEnterText(pEditInstance, szTextToAdd, TRUE) ==
  772.                   kODRCUnrecoverableFailure)
  773.                {
  774.                   od_control.od_error = ERR_MEMORY;
  775.                   return(OD_MULTIEDIT_ERROR);
  776.                }
  777.             }
  778.  
  779.             /* In overwrite mode, then just advance the cursor position. */
  780.             else
  781.             {
  782.                /* Determine the column where the cursor should be wrapped. */
  783.                UINT unWrapColumn = pEditInstance->bWordWrapLongLines ?
  784.                   ODEditBufferGetLineLength(pEditInstance,
  785.                   pEditInstance->unCurrentLine)
  786.                   : pEditInstance->unAreaWidth;
  787.  
  788.                if(unTargetColumn < unWrapColumn)
  789.                {
  790.                   pEditInstance->unCurrentColumn = unTargetColumn;
  791.                   ODEditUpdateCursorPos(pEditInstance);
  792.                }
  793.                else if(pEditInstance->unCurrentLine
  794.                   < ODEditBufferGetTotalLines(pEditInstance) - 1)
  795.                {
  796.                   ODEditGotoNextLine(pEditInstance);
  797.                   pEditInstance->unCurrentColumn = 0;
  798.                   ODEditUpdateCursorPos(pEditInstance);
  799.                }
  800.             }
  801.          }
  802.          else if(InputEvent.chKeyPress == '\r')
  803.          {
  804.             char *pszTextToAdd;
  805.  
  806.             /* The enter key has been pressed. In insert mode, or at the end */
  807.             /* of the buffer in overwrite mode, this adds a new line.        */
  808.             if(pEditInstance->bInsertMode || pEditInstance->unCurrentLine
  809.                   >= ODEditBufferGetTotalLines(pEditInstance) - 1)
  810.             {
  811.                if(!pEditInstance->bInsertMode)
  812.                {
  813.                   /* If we are not in insert mode, begin by positioning the */
  814.                   /* cursor at the end of this line.                        */
  815.                   pEditInstance->unCurrentColumn = ODEditBufferGetLineLength(
  816.                      pEditInstance, pEditInstance->unCurrentLine);
  817.                }
  818.  
  819.                /* Determine the line/paragraph break sequence to use. */
  820.                if(pEditInstance->pszLineBreak != NULL
  821.                   && strlen(pEditInstance->pszLineBreak) > 0)
  822.                {
  823.                   pszTextToAdd = pEditInstance->pszLineBreak;
  824.                }
  825.                else if(pEditInstance->pszParagraphBreak != NULL
  826.                   && strlen(pEditInstance->pszParagraphBreak) > 0)
  827.                {
  828.                   pszTextToAdd = pEditInstance->pszParagraphBreak;
  829.                }
  830.                else
  831.                {
  832.                   pszTextToAdd = DEFAULT_LINE_BREAK;
  833.                }
  834.  
  835.                /* Insert the sequence into the buffer. */
  836.                if(ODEditEnterText(pEditInstance, pszTextToAdd, TRUE) ==
  837.                   kODRCUnrecoverableFailure)
  838.                {
  839.                   od_control.od_error = ERR_MEMORY;
  840.                   return(OD_MULTIEDIT_ERROR);
  841.                }
  842.             }
  843.             else
  844.             {
  845.                /* Pressing the enter key in overwrite mode just moves the   */
  846.                /* cursor to the beginning of the next line. In other words, */
  847.                /* it is equivalent to pressing Down arrow followed by home. */
  848.                ODEditGotoNextLine(pEditInstance);
  849.                pEditInstance->unCurrentColumn = 0;
  850.                ODEditUpdateCursorPos(pEditInstance);
  851.             }
  852.          }
  853.          else if(InputEvent.chKeyPress >= 32)
  854.          {
  855.             char szTextToAdd[2];
  856.             szTextToAdd[0] = InputEvent.chKeyPress;
  857.             szTextToAdd[1] = '\0';
  858.  
  859.             /* A valid buffer character has been entered. */
  860.             if(ODEditEnterText(pEditInstance, szTextToAdd,
  861.                (BOOL)(pEditInstance->bInsertMode
  862.                || ODEditPastEndOfCurLine(pEditInstance)))
  863.                == kODRCUnrecoverableFailure)
  864.             {
  865.                od_control.od_error = ERR_MEMORY;
  866.                return(OD_MULTIEDIT_ERROR);
  867.             }
  868.          }
  869.       }
  870.    }
  871. }
  872.  
  873.  
  874. /* ----------------------------------------------------------------------------
  875.  * ODEditGotoPreviousLine()                            *** PRIVATE FUNCTION ***
  876.  *
  877.  * Moves the current cursor position to the previous line, scrolling the screen
  878.  * if necessary to keep the cursor visible.
  879.  *
  880.  * Parameters: pEditInstance - Editor instance information structure.
  881.  *
  882.  *     Return: void
  883.  */
  884. static void ODEditGotoPreviousLine(tEditInstance *pEditInstance)
  885. {
  886.    ASSERT(pEditInstance != NULL);
  887.  
  888.    /* If we are already at the first line, then return without doing */
  889.    /* anything.                                                      */
  890.    if(pEditInstance->unCurrentLine == 0) return;
  891.  
  892.    /* If cursor is at top of edit area, then scroll area */
  893.    /* first.                                             */
  894.    if(ODEditGetCurrentLineInArea(pEditInstance) == 0)
  895.    {
  896.       ODEditScrollArea(pEditInstance,
  897.          -(INT)(MIN(pEditInstance->unScrollDistance,
  898.          pEditInstance->unCurrentLine)));
  899.    }
  900.  
  901.    /* Move cursor to previous line. */
  902.    --pEditInstance->unCurrentLine;
  903. }
  904.  
  905.  
  906. /* ----------------------------------------------------------------------------
  907.  * ODEditGotoNextLine()                                *** PRIVATE FUNCTION ***
  908.  *
  909.  * Advances the current cursor position to the next line, scrolling the screen
  910.  * if necessary to keep the cursor visible.
  911.  *
  912.  * Parameters: pEditInstance - Editor instance information structure.
  913.  *
  914.  *     Return: void
  915.  */
  916. static void ODEditGotoNextLine(tEditInstance *pEditInstance)
  917. {
  918.    ASSERT(pEditInstance != NULL);
  919.  
  920.    /* If we are already at the end of the file, then return without */
  921.    /* doing anything.                                               */
  922.    if(pEditInstance->unCurrentLine
  923.       >= ODEditBufferGetTotalLines(pEditInstance) - 1)
  924.    {
  925.       return;
  926.    }
  927.  
  928.    /* If cursor is at the bottom of the edit area, then scroll area first. */
  929.    if(ODEditGetCurrentLineInArea(pEditInstance)
  930.       == pEditInstance->unAreaHeight - 1)
  931.    {
  932.       ODEditScrollArea(pEditInstance,
  933.          (INT)MIN(pEditInstance->unScrollDistance,
  934.          ODEditBufferGetTotalLines(pEditInstance)
  935.          - pEditInstance->unCurrentLine));
  936.    }
  937.  
  938.    /* Move cursor to next line. */
  939.    ++pEditInstance->unCurrentLine;
  940. }
  941.  
  942.  
  943. /* ----------------------------------------------------------------------------
  944.  * ODEditScrollArea()                                  *** PRIVATE FUNCTION ***
  945.  *
  946.  * Scrolls the edit area up or down the specified distance, redrawing newly
  947.  * "exposed" lines.
  948.  *
  949.  * Parameters: pEditInstance - Editor instance information structure.
  950.  *
  951.  *             nDistance     - Number of lines to scroll, where a positive
  952.  *                             value moves the text upwards, and a negative
  953.  *                             value moves the text down.
  954.  *
  955.  *     Return: FALSE if a full redraw has been performed, TRUE if an efficient
  956.  *             scroll command has been used.
  957.  */
  958. static BOOL ODEditScrollArea(tEditInstance *pEditInstance, INT nDistance)
  959. {
  960.    BOOL bUseScrollCommand = FALSE;
  961.    UINT unAreaLine;
  962.    UINT unBufferLine;
  963.    UINT unFirstAreaLineToDraw;
  964.    UINT unLastAreaLineToDraw;
  965.    UINT unPositiveDistance;
  966.  
  967.    ASSERT(pEditInstance);
  968.  
  969.    /* If scroll distance is zero, then we don't need to do anything at all. */
  970.    if(nDistance == 0)
  971.    {
  972.       return(TRUE);
  973.    }
  974.    /* Otherwise, obtain the absolute value of the distance as an unsigned */
  975.    /* integer.                                                            */
  976.    else if(nDistance < 0)
  977.    {
  978.       unPositiveDistance = (UINT)-nDistance;
  979.    }
  980.    else
  981.    {
  982.       unPositiveDistance = (UINT)nDistance;
  983.    }
  984.  
  985.    /* In AVATAR mode, if more than one of the currently visible lines will */
  986.    /* still be visible after scrolling, then we will consider using the    */
  987.    /* scroll operation.                                                    */
  988.    if(od_control.user_avatar
  989.       && ((INT)pEditInstance->unAreaHeight) - ((INT)unPositiveDistance) > 1)
  990.    {
  991.       /* Even under this situation, we only want to use the scroll operation */
  992.       /* if the amount of data still in the outbound buffer + our estimate   */
  993.       /* of the amount of data that will be sent to perform the scroll       */
  994.       /* operation is less than our estimate of the amount of data that      */
  995.       /* would be sent by a complete screen redraw.                          */
  996.       UINT unEstimatedScrollData = ((pEditInstance->unAreaWidth + 4) *
  997.          unPositiveDistance) + 7;
  998.  
  999.       if(!ODEditRecommendFullRedraw(pEditInstance, unEstimatedScrollData,
  1000.          TRUE))
  1001.       {
  1002.          bUseScrollCommand = TRUE;
  1003.       }
  1004.    }
  1005.  
  1006.    /* In local mode, we can also use the scroll command for efficiency. */
  1007.    if(od_control.baud == 0)
  1008.    {
  1009.       bUseScrollCommand = TRUE;
  1010.    }
  1011.  
  1012.    /* Area scroll is achieved by one of two means. We either use the scroll */
  1013.    /* command, and then draw just the newly visible lines, or we redraw the */
  1014.    /* entire edit area, after removing any data from the outbound           */
  1015.    /* communications buffer.                                                */
  1016.  
  1017.    if(bUseScrollCommand)
  1018.    {
  1019.       /* Use the od_scroll() function to scroll the screen contents. */
  1020.       od_scroll(pEditInstance->pUserOptions->nAreaLeft,
  1021.          pEditInstance->pUserOptions->nAreaTop,
  1022.          pEditInstance->pUserOptions->nAreaRight,
  1023.          pEditInstance->pUserOptions->nAreaBottom,
  1024.          nDistance, SCROLL_NO_CLEAR);
  1025.  
  1026.       /* Fill the newly visible lines. First, the portion of the area that   */
  1027.       /* requires redrawing is determined, and then a loop redraws the lines */
  1028.       /* that must be drawn.                                                 */
  1029.  
  1030.       /* If we are moving text upwards, exposing new lines at the bottom of */
  1031.       /* the area:                                                          */
  1032.       if(nDistance > 0)
  1033.       {
  1034.          ASSERT(pEditInstance->unLineScrolledToTop + unPositiveDistance
  1035.             < pEditInstance->unLinesInBuffer);
  1036.          pEditInstance->unLineScrolledToTop += unPositiveDistance;
  1037.          unFirstAreaLineToDraw = pEditInstance->unAreaHeight
  1038.             - (UINT)unPositiveDistance;
  1039.          unLastAreaLineToDraw = pEditInstance->unAreaHeight - 1;
  1040.       }
  1041.       /* Otherwise, we have moved text downwards, exposing new lines at the */
  1042.       /* top of the edit area.                                              */
  1043.       else
  1044.       {
  1045.          ASSERT(pEditInstance->unLineScrolledToTop >= unPositiveDistance);
  1046.          pEditInstance->unLineScrolledToTop -= unPositiveDistance;
  1047.          unFirstAreaLineToDraw = 0;
  1048.          unLastAreaLineToDraw = unPositiveDistance - 1;
  1049.       }
  1050.  
  1051.       ODScrnEnableCaret(FALSE);
  1052.  
  1053.       /* Now, redraw the new lines. */
  1054.       unBufferLine = unFirstAreaLineToDraw
  1055.          + pEditInstance->unLineScrolledToTop;
  1056.       for(unAreaLine = unFirstAreaLineToDraw; unAreaLine <=
  1057.          unLastAreaLineToDraw; ++unAreaLine, ++unBufferLine)
  1058.       {
  1059.          /* Draw the entire line. */
  1060.          ODEditDrawAreaLine(pEditInstance, unAreaLine);
  1061.       }
  1062.  
  1063.       ODScrnEnableCaret(TRUE);
  1064.    }
  1065.  
  1066.    /* Just redraw the entire edit area. */
  1067.    else
  1068.    {
  1069.       /* Adjust the line number that is scrolled to the top of the screen. */
  1070.       if(nDistance > 0)
  1071.       {
  1072.          pEditInstance->unLineScrolledToTop += unPositiveDistance;
  1073.       }
  1074.       else
  1075.       {
  1076.          pEditInstance->unLineScrolledToTop -= unPositiveDistance;
  1077.       }
  1078.  
  1079.       /* Perform redraw, first purging outbound buffer. */
  1080.       ODEditRedrawArea(pEditInstance);
  1081.    }
  1082.  
  1083.    return(bUseScrollCommand);
  1084. }
  1085.  
  1086.  
  1087. /* ----------------------------------------------------------------------------
  1088.  * ODEditRecommendFullRedraw()                         *** PRIVATE FUNCTION ***
  1089.  *
  1090.  * Determines whether it would be more efficient to add the specified number
  1091.  * of bytes to the outbound buffer as part of an incremental redraw, or if
  1092.  * it would be more efficient to just purge the outbound buffer and do a
  1093.  * complete redraw of the edit area.
  1094.  *
  1095.  * Parameters: pEditInstance           - Editor instance information structure.
  1096.  *
  1097.  *             unEstPartialRedrawBytes - Estimate of the number of bytes that
  1098.  *                                       would be transmitted if an incremental
  1099.  *                                       redraw is performed.
  1100.  *
  1101.  *             bDefault                - The default action (TRUE for full
  1102.  *                                       redraw, FALSE for incremental) if the
  1103.  *                                       number of bytes in the outbound buffer
  1104.  *                                       cannot be determined.
  1105.  *
  1106.  *     Return: TRUE if a full redraw is recommended, FALSE if an the
  1107.  *             incremental redraw is recommended.
  1108.  */
  1109. static BOOL ODEditRecommendFullRedraw(tEditInstance *pEditInstance,
  1110.    UINT unEstPartialRedrawBytes, BOOL bDefault)
  1111. {
  1112.    int nOutboundBufferBytes;
  1113.    UINT unEstFullRedrawBytes;
  1114.  
  1115.    /* In local mode, just return the default action. */
  1116.    if(od_control.baud == 0)
  1117.    {
  1118.       return(bDefault);
  1119.    }
  1120.  
  1121.    /* Attempt to obtain the number of bytes in the communications outbound */
  1122.    /* buffer. Unfortunately, this information may not be available. For    */
  1123.    /* example, FOSSIL drivers will only report whether or not there is     */
  1124.    /* still data in the outbound buffer, but not a count of the number of  */
  1125.    /* bytes in the buffer. Under such a situation, ODComOutbound() returns */
  1126.    /* SIZE_NON_ZERO if there is data in the buffer, and 0 if there is no   */
  1127.    /* data in the buffer. This is not a problem under OpenDoor's internal  */
  1128.    /* serial I/O code, nor is it a problem under Win32's communications    */
  1129.    /* facilities.                                                          */
  1130.    ODComOutbound(hSerialPort, &nOutboundBufferBytes);
  1131.  
  1132.    if(nOutboundBufferBytes == SIZE_NON_ZERO)
  1133.    {
  1134.       /* We know that there is data in the outbound buffer, but we don't */
  1135.       /* know how much, and so we cannot make a recommendation. Instead, */
  1136.       /* the default course of action will be taken.                     */
  1137.       return(bDefault);
  1138.    }
  1139.  
  1140.    /* Estimate the # of bytes required for a full redraw of the edit area. */
  1141.    unEstFullRedrawBytes = ODEditEstDrawBytes(pEditInstance, 0,
  1142.       0, pEditInstance->unAreaHeight - 1, pEditInstance->unAreaWidth);
  1143.  
  1144.    /* Recommend a full redraw if the number of bytes for an incremental */
  1145.    /* redraw plus the number of bytes already in the outbound buffer    */
  1146.    /* exceed the number of bytes required for a full redraw.            */
  1147.    if(unEstPartialRedrawBytes + (UINT)nOutboundBufferBytes
  1148.       > unEstFullRedrawBytes)
  1149.    {
  1150.       return(TRUE);
  1151.    }
  1152.    else
  1153.    {
  1154.       return(FALSE);
  1155.    }
  1156. }
  1157.  
  1158.  
  1159. /* ----------------------------------------------------------------------------
  1160.  * ODEditEstDrawBytes()                                *** PRIVATE FUNCTION ***
  1161.  *
  1162.  * Estimates the number of bytes which will be transmitted in order to redraw
  1163.  * the specified portion of the edit area.
  1164.  *
  1165.  * Parameters: pEditInstance - Editor instance information structure.
  1166.  *
  1167.  *             unStartRedrawLine    - Line of first character to draw.
  1168.  *
  1169.  *             unStartRedrawColumn  - Column of first character to draw.
  1170.  *
  1171.  *             unFinishRedrawLine   - Line of last character to draw.
  1172.  *
  1173.  *             unFinishRedrawColumn - Column after last character to draw.
  1174.  *
  1175.  *     Return: A rough estimate of the number of bytes required for the redraw.
  1176.  */
  1177. static UINT ODEditEstDrawBytes(tEditInstance *pEditInstance,
  1178.    UINT unStartRedrawLine, UINT unStartRedrawColumn, UINT unFinishRedrawLine,
  1179.    UINT unFinishRedrawColumn)
  1180. {
  1181.    UINT unAreaLine;
  1182.    UINT unBufferLine;
  1183.    UINT unLineLength;
  1184.    UINT unByteTally = 0;
  1185.  
  1186.    /* If we are only drawing text on a single line, then estimate is just  */
  1187.    /* the distance between the start and finish redraw column. This number */
  1188.    /* is precise only if the cursor is already at the location where       */
  1189.    /* output is to begin, and the final cursor position is the location    */
  1190.    /* where output finishes. This is in fact the situation when the user   */
  1191.    /* is entering new text in the middle of the line - the most common     */
  1192.    /* situation that will be encountered.                                  */
  1193.    if(unStartRedrawLine == unFinishRedrawLine)
  1194.    {
  1195.       return(unFinishRedrawColumn - unStartRedrawColumn);
  1196.    }
  1197.  
  1198.    /* If we are drawing text on multiple lines, then inspect the contents */
  1199.    /* of those lines to estimate the number of bytes to be transmitted.   */
  1200.    for(unAreaLine = unStartRedrawLine,
  1201.       unBufferLine = pEditInstance->unLineScrolledToTop + unStartRedrawLine;
  1202.       unAreaLine <= unFinishRedrawLine;
  1203.       ++unAreaLine, ++unBufferLine)
  1204.    {
  1205.       /* Determine the length of this line. */
  1206.       if(unBufferLine < pEditInstance->unLinesInBuffer)
  1207.       {
  1208.          unLineLength = ODEditBufferGetLineLength(pEditInstance, unBufferLine);
  1209.          if(unAreaLine == unStartRedrawLine)
  1210.          {
  1211.             unLineLength -= unStartRedrawColumn;
  1212.          }
  1213.       }
  1214.       else
  1215.       {
  1216.          unLineLength = 0;
  1217.       }
  1218.  
  1219.       /* Add the number of characters on this line, along with the number of */
  1220.       /* bytes required to reposition the cursor and to clear the unused     */
  1221.       /* portion of this line to the tally. This assumes that the edit area  */
  1222.       /* spans the entire screen.                                            */
  1223.       unByteTally += unLineLength + 7;
  1224.    }
  1225.  
  1226.    return(unByteTally);
  1227. }
  1228.  
  1229.  
  1230. /* ----------------------------------------------------------------------------
  1231.  * ODEditGetCurrentLineInArea()                        *** PRIVATE FUNCTION ***
  1232.  *
  1233.  * Determines which line of the edit area the cursor is currently located in.
  1234.  *
  1235.  * Parameters: pEditInstance - Editor instance information structure.
  1236.  *
  1237.  *     Return: 0-based index of the distance from the top of the edit area
  1238.  *             (as specified in the user options structure) where the
  1239.  *             cursor is currently located.
  1240.  */
  1241. static UINT ODEditGetCurrentLineInArea(tEditInstance *pEditInstance)
  1242. {
  1243.    ASSERT(pEditInstance != NULL);
  1244.  
  1245.    return(pEditInstance->unCurrentLine - pEditInstance->unLineScrolledToTop);
  1246. }
  1247.  
  1248.  
  1249. /* ----------------------------------------------------------------------------
  1250.  * ODEditUpdateCursorPos()                             *** PRIVATE FUNCTION ***
  1251.  *
  1252.  * Unconditionally updates the position of the cursor on the screen to 
  1253.  * reflect its actual position. Compare with ODEditUpdateCursorIfMoved().
  1254.  *
  1255.  * Parameters: pEditInstance - Editor instance information structure.
  1256.  *
  1257.  *     Return: void
  1258.  */
  1259. static void ODEditUpdateCursorPos(tEditInstance *pEditInstance)
  1260. {
  1261.    ASSERT(pEditInstance != NULL);
  1262.  
  1263.    /* Reposition the cursor on the screen. */
  1264.    od_set_cursor(ODEditGetCurrentLineInArea(pEditInstance)
  1265.       + pEditInstance->pUserOptions->nAreaTop,
  1266.       pEditInstance->unCurrentColumn + pEditInstance->pUserOptions->nAreaLeft);
  1267. }
  1268.  
  1269.  
  1270. /* ----------------------------------------------------------------------------
  1271.  * ODEditUpdateCursorIfMoved()                         *** PRIVATE FUNCTION ***
  1272.  *
  1273.  * Updates the position of the cursor on the screen to reflec its actual
  1274.  * position only if we belive it isn't already there. Compare with
  1275.  * ODEditUpdateCursorPos().
  1276.  *
  1277.  * Parameters: pEditInstance - Editor instance information structure.
  1278.  *
  1279.  *     Return: void
  1280.  */
  1281. static void ODEditUpdateCursorIfMoved(tEditInstance *pEditInstance)
  1282. {
  1283.    UINT unActualRow;
  1284.    UINT unActualColumn;
  1285.    ODEditGetActualCurPos(pEditInstance, &unActualRow, &unActualColumn);
  1286.  
  1287.    if(!(unActualRow == ODEditGetCurrentLineInArea(pEditInstance)
  1288.          + pEditInstance->pUserOptions->nAreaTop
  1289.       && unActualColumn == pEditInstance->unCurrentColumn
  1290.          + pEditInstance->pUserOptions->nAreaLeft))
  1291.    {
  1292.       ODEditUpdateCursorPos(pEditInstance);
  1293.    }
  1294. }
  1295.  
  1296.  
  1297. /* ----------------------------------------------------------------------------
  1298.  * ODEditEnterText()                                   *** PRIVATE FUNCTION ***
  1299.  *
  1300.  * Inserts new text at the current cursor position, updating the cursor
  1301.  * position accordingly.
  1302.  *
  1303.  * Parameters: pEditInstance - Editor instance information structure.
  1304.  *
  1305.  *             pszEntered    - The character(s) to be inserted.
  1306.  *
  1307.  *     Return: kODRCSuccess on success, kODRCSafeFailure if we were unable to
  1308.  *             obtain enough buffer space before making any changes, or
  1309.  *             kODRCUnrecoverableFailure if the buffer has been changed, but
  1310.  *             there was insufficient memory to re-index the buffer.
  1311.  */
  1312. static tODResult ODEditEnterText(tEditInstance *pEditInstance,
  1313.    char *pszEntered, BOOL bInsertMode)
  1314. {
  1315.    UINT unNumCharsToAdd;
  1316.    char *pch;
  1317.    tODResult Result;
  1318.  
  1319.    ASSERT(pEditInstance != NULL);
  1320.    ASSERT(pszEntered != NULL);
  1321.  
  1322.    /* Remember initial edit area contents, to permit selective redraw. */
  1323.    ODEditRememberArea(pEditInstance, pEditInstance->pRememberBuffer);
  1324.  
  1325.    /* Determine the number of characters that are to be added to the buffer. */
  1326.    unNumCharsToAdd = strlen(pszEntered);
  1327.  
  1328.    /* Make room in the buffer for the new characters, if needed. */
  1329.    if(bInsertMode)
  1330.    {
  1331.       Result = ODEditBufferMakeSpace(pEditInstance,
  1332.          pEditInstance->unCurrentLine, pEditInstance->unCurrentColumn,
  1333.          unNumCharsToAdd);
  1334.  
  1335.       if(Result != kODRCSuccess)
  1336.       {
  1337.          /* Beep on failure. */
  1338.          od_putch('\a');
  1339.          return(Result);
  1340.       }
  1341.    }
  1342.  
  1343.    /* Copy the new characters to the buffer. */
  1344.    pch = ODEditBufferGetCharacter(pEditInstance,
  1345.       pEditInstance->unCurrentLine, pEditInstance->unCurrentColumn);
  1346.    memcpy(pch, pszEntered, unNumCharsToAdd);
  1347.  
  1348.    /* Move the cursor position to the end of the newly added text. The  */
  1349.    /* cursor position may be temporarily assigned to a position that is */
  1350.    /* past the end of the edit area or past the end of the buffer.      */
  1351.    for(pch = pszEntered; *pch != '\0'; ++pch)
  1352.    {
  1353.       if(IS_EOL_CHAR(*pch))
  1354.       {
  1355.          /* A carriage return character advances the cursor to the */
  1356.          /* leftmost column on the next line.                      */
  1357.          pEditInstance->unCurrentColumn = 0;
  1358.          pEditInstance->unCurrentLine++;
  1359.  
  1360.          /* If the next character is a different EOL character, and is */
  1361.          /* not the end of the string, then skip that character.       */
  1362.          if(IS_EOL_CHAR(pch[1]) && pch[1] != '\0' && pch[1] != *pch)
  1363.          {
  1364.             ++pch;
  1365.          }
  1366.       }
  1367.       else
  1368.       {
  1369.          /* All other characters move the cursor ahead one column. */
  1370.          pEditInstance->unCurrentColumn++;
  1371.       }
  1372.    }
  1373.  
  1374.    /* Reindex and reformat the buffer based on its new contents. */
  1375.    if(!ODEditBufferFormatAndIndex(pEditInstance))
  1376.    {
  1377.       return(kODRCUnrecoverableFailure);
  1378.    }
  1379.  
  1380.    /* Check whether the cursor is now positioned past the end of the edit */
  1381.    /* area, requiring the edit area to be scrolled up.                    */
  1382.    if(ODEditGetCurrentLineInArea(pEditInstance)
  1383.       >= pEditInstance->unAreaHeight)
  1384.    {
  1385.       /* We need to scroll the area accordingly. */
  1386.  
  1387.       /* Distance to scroll is maximum of the current single-step scroll */
  1388.       /* distance, and the distance that the cursor is positioned below  */
  1389.       /* the bottom of the current edit area.                            */
  1390.       UINT unScrollDistance = MAX(pEditInstance->unScrollDistance,
  1391.          ODEditGetCurrentLineInArea(pEditInstance) -
  1392.          pEditInstance->unAreaHeight + 1);
  1393.  
  1394.       /* Perform actual scroll operation. */
  1395.       if(ODEditScrollArea(pEditInstance, (INT)unScrollDistance))
  1396.       {
  1397.          /* Entire area wasn't redrawn by operation, so some redrawing */
  1398.          /* may still be required, within the area of text that was    */
  1399.          /* visible before the scroll operation.                       */
  1400.          ODEditRedrawChanged(pEditInstance, pEditInstance->pRememberBuffer,
  1401.             0, pEditInstance->unAreaHeight - unScrollDistance);
  1402.       }
  1403.    }
  1404.  
  1405.    /* Perform necessary redrawing and reposition the cursor if needed. */
  1406.    ODEditRedrawChanged(pEditInstance, pEditInstance->pRememberBuffer,
  1407.       REDRAW_NO_BOUNDARY, REDRAW_NO_BOUNDARY);
  1408.  
  1409.    /* Return with success. */
  1410.    return(kODRCSuccess);
  1411. }
  1412.  
  1413.  
  1414. /* ----------------------------------------------------------------------------
  1415.  * ODEditSetBreakSequence()                            *** PRIVATE FUNCTION ***
  1416.  *
  1417.  * If the default line or paragraph break sequence has not yet been set, then
  1418.  * this function sets it based on the break sequence that was encountered in
  1419.  * the buffer supplied by the client application.
  1420.  *
  1421.  * Parameters: pEditInstance   - Editor instance information structure.
  1422.  *
  1423.  *             chFirstEOLChar  - First character in the encountered sequence.
  1424.  *
  1425.  *             chSecondEOLChar - Second character in the encountered sequence.
  1426.  *
  1427.  *     Return: void
  1428.  */
  1429. static void ODEditSetBreakSequence(tEditInstance *pEditInstance,
  1430.    char chFirstEOLChar, char chSecondEOLChar)
  1431. {
  1432.    char *pszSequence;
  1433.  
  1434.    ASSERT(pEditInstance != NULL);
  1435.  
  1436.    if(pEditInstance->pszParagraphBreak != NULL
  1437.       && pEditInstance->pszLineBreak != NULL)
  1438.    {
  1439.       /* In this situation, both the default line break sequence and default */
  1440.       /* paragraph break sequence have been set, so there is nothing for us  */
  1441.       /* to do.                                                              */
  1442.       return;
  1443.    }
  1444.  
  1445.    /* Obtain a pointer to the encountered sequence. We want to use a static */
  1446.    /* string constant for this, so that the string will continue to exist,  */
  1447.    /* in unchanged form.                                                    */
  1448.    if(chFirstEOLChar == '\r' && chSecondEOLChar == '\0')
  1449.    {
  1450.       pszSequence = "\r";
  1451.    }
  1452.    else if(chFirstEOLChar == '\n' && chSecondEOLChar == '\0')
  1453.    {
  1454.       pszSequence = "\n";
  1455.    }
  1456.    else if(chFirstEOLChar == '\n' && chSecondEOLChar == '\r')
  1457.    {
  1458.       pszSequence = "\n\r";
  1459.    }
  1460.    else if(chFirstEOLChar == '\r' && chSecondEOLChar == '\n')
  1461.    {
  1462.       pszSequence = "\r\n";
  1463.    }
  1464.    else
  1465.    {
  1466.       /* This should never happen: an invalid end of line sequence was */
  1467.       /* passed in.                                                    */
  1468.       ASSERT(FALSE);
  1469.    }
  1470.  
  1471.    /* Set the as yet undetermined line/paragraph terminators. */
  1472.    if(pEditInstance->pszParagraphBreak == NULL)
  1473.    {
  1474.       pEditInstance->pszParagraphBreak = pszSequence;
  1475.    }
  1476.  
  1477.    if(pEditInstance->pszLineBreak == NULL)
  1478.    {
  1479.       pEditInstance->pszLineBreak = pszSequence;
  1480.    }
  1481. }
  1482.  
  1483.  
  1484. /* ----------------------------------------------------------------------------
  1485.  * ODEditCursorLeft()                                  *** PRIVATE FUNCTION ***
  1486.  *
  1487.  * Attempts to move the cursor left. Called when the user presses the left
  1488.  * arrow key, and is also called as part of the process of handling the
  1489.  * backspace key.
  1490.  *
  1491.  * Parameters: pEditInstance   - Editor instance information structure.
  1492.  *
  1493.  *     Return: TRUE on success, or FALSE if the cursor cannot be moved left any
  1494.  *             further.
  1495.  */
  1496. static BOOL ODEditCursorLeft(tEditInstance *pEditInstance)
  1497. {
  1498.    ASSERT(pEditInstance != NULL);
  1499.  
  1500.    /* In word wrap mode, pressing the left key when the cursor */
  1501.    /* is past the end of the line jumps the cursor to the end  */
  1502.    /* of the line.                                             */
  1503.    if(pEditInstance->bWordWrapLongLines &&
  1504.       pEditInstance->unCurrentColumn > ODEditBufferGetLineLength
  1505.       (pEditInstance, pEditInstance->unCurrentLine))
  1506.    {
  1507.       pEditInstance->unCurrentColumn = ODEditBufferGetLineLength
  1508.          (pEditInstance, pEditInstance->unCurrentLine);
  1509.       return(TRUE);
  1510.    }
  1511.  
  1512.    /* If we are not already at the leftmost column. */
  1513.    else if(pEditInstance->unCurrentColumn > 0)
  1514.    {
  1515.       /* Move left one column. */
  1516.       --pEditInstance->unCurrentColumn;
  1517.       return(TRUE);
  1518.    }
  1519.    else if(pEditInstance->bWordWrapLongLines)
  1520.    {
  1521.       /* In word wrap mode, this will move us up to the end of */
  1522.       /* the previous line.                                    */
  1523.       if(pEditInstance->unCurrentLine > 0)
  1524.       {
  1525.          ODEditGotoPreviousLine(pEditInstance);
  1526.          pEditInstance->unCurrentColumn = ODEditBufferGetLineLength(
  1527.             pEditInstance, pEditInstance->unCurrentLine);
  1528.          return(TRUE);
  1529.       }
  1530.    }
  1531.  
  1532.    /* It wasn't possible to move the cursor. */
  1533.    return(FALSE);
  1534. }
  1535.  
  1536.  
  1537. /* ----------------------------------------------------------------------------
  1538.  * ODEditDeleteCurrentChar()                           *** PRIVATE FUNCTION ***
  1539.  *
  1540.  * Deletes the character at the current cursor position, performing necessary
  1541.  * redraw. Pressing the delete key causes just this function to be called.
  1542.  * Pressing the backspace key causes ODEditCursorLeft() to be called to first
  1543.  * move the cursor left before calling ODEditDeleteCurrentChar().
  1544.  *
  1545.  * Parameters: pEditInstance   - Editor instance information structure.
  1546.  *
  1547.  *     Return: void
  1548.  */
  1549. static void ODEditDeleteCurrentChar(tEditInstance *pEditInstance)
  1550. {
  1551.    char *pch;
  1552.  
  1553.    ASSERT(pEditInstance != NULL);
  1554.  
  1555.    /* Remember initial edit area contents, to permit selective redraw. */
  1556.    ODEditRememberArea(pEditInstance, pEditInstance->pRememberBuffer);
  1557.  
  1558.    /* Backup the entire buffer contents by one character. */
  1559.    pch = ODEditBufferGetCharacter(pEditInstance,
  1560.       pEditInstance->unCurrentLine, pEditInstance->unCurrentColumn);
  1561.    memmove(pch, pch + 1, strlen(pch + 1) + 1);
  1562.  
  1563.    /* Reindex and reformat the buffer based on its new contents. */
  1564.    ODEditBufferFormatAndIndex(pEditInstance);
  1565.  
  1566.    /* Perform necessary redrawing and reposition the cursor if needed. */
  1567.    ODEditRedrawChanged(pEditInstance, pEditInstance->pRememberBuffer,
  1568.       REDRAW_NO_BOUNDARY, REDRAW_NO_BOUNDARY);
  1569. }
  1570.  
  1571.  
  1572. /* ----------------------------------------------------------------------------
  1573.  * ODEditDeleteCurrentLine()                           *** PRIVATE FUNCTION ***
  1574.  *
  1575.  * Removes the entire current line from the buffer.
  1576.  *
  1577.  * Parameters: pEditInstance - Editor instance information structure.
  1578.  *
  1579.  *     Return: void
  1580.  */
  1581. static void ODEditDeleteCurrentLine(tEditInstance *pEditInstance)
  1582. {
  1583.    char *pszStartOfThisLine;
  1584.    char *pszStartOfNextLine;
  1585.  
  1586.    ASSERT(pEditInstance != NULL);
  1587.  
  1588.    /* Remember initial edit area contents, to permit selective redraw. */
  1589.    ODEditRememberArea(pEditInstance, pEditInstance->pRememberBuffer);
  1590.  
  1591.    /* Determine start of this line. */
  1592.    pszStartOfThisLine = ODEditBufferGetCharacter(pEditInstance,
  1593.       pEditInstance->unCurrentLine, 0);
  1594.  
  1595.    if(pEditInstance->unLinesInBuffer == pEditInstance->unCurrentLine + 1)
  1596.    {
  1597.       /* If this is the last line of the buffer, then we just remove    */
  1598.       /* everything from this line, without actually removing the line. */
  1599.       *pszStartOfThisLine = '\0';
  1600.    }
  1601.    else
  1602.    {
  1603.       /* If this is not the last line of the buffer, then remove this */
  1604.       /* entire line, so that the next line will become the current   */
  1605.       /* line.                                                        */
  1606.       pszStartOfNextLine = ODEditBufferGetCharacter(pEditInstance,
  1607.          pEditInstance->unCurrentLine + 1, 0);
  1608.       memmove(pszStartOfThisLine, pszStartOfNextLine,
  1609.          strlen(pszStartOfNextLine) + 1);
  1610.    }
  1611.  
  1612.    /* Reset the cursor position to the beginning of the current line. */
  1613.    pEditInstance->unCurrentColumn = 0;
  1614.  
  1615.    /* Reindex and reformat the buffer based on its new contents. */
  1616.    ODEditBufferFormatAndIndex(pEditInstance);
  1617.  
  1618.    /* Perform necessary redrawing and reposition the cursor if needed. */
  1619.    ODEditRedrawChanged(pEditInstance, pEditInstance->pRememberBuffer,
  1620.       REDRAW_NO_BOUNDARY, REDRAW_NO_BOUNDARY);
  1621. }
  1622.  
  1623.  
  1624. /* ----------------------------------------------------------------------------
  1625.  * ODEditPastEndOfCurLine()                            *** PRIVATE FUNCTION ***
  1626.  *
  1627.  * Determines whether the cursor is currently past the end of the current line.
  1628.  * The end of the line is considered to be the first column after the last
  1629.  * character on the line. So, on a blank line, a cursor is considered to be
  1630.  * past the end if it is in or past the second column (column 0).
  1631.  *
  1632.  * Parameters: pEditInstance   - Editor instance information structure.
  1633.  *
  1634.  *     Return: TRUE if the cursor is past the end of the current line,
  1635.  *             FALSE if it is not.
  1636.  */
  1637. static BOOL ODEditPastEndOfCurLine(tEditInstance *pEditInstance)
  1638. {
  1639.    ASSERT(pEditInstance != NULL);
  1640.  
  1641.    return(pEditInstance->unCurrentColumn >
  1642.       ODEditBufferGetLineLength(pEditInstance, pEditInstance->unCurrentLine));
  1643. }
  1644.  
  1645.  
  1646. /* ----------------------------------------------------------------------------
  1647.  * ODEditRememberBufferSize()                          *** PRIVATE FUNCTION ***
  1648.  *
  1649.  * Determines the buffer size required by ODEditRememberArea().
  1650.  *
  1651.  * Parameters: pEditInstance   - Editor instance information structure.
  1652.  *
  1653.  *     Return: the required buffer size, in size_t units (bytes).
  1654.  */
  1655. static size_t ODEditRememberBufferSize(tEditInstance *pEditInstance)
  1656. {
  1657.    ASSERT(pEditInstance != NULL);
  1658.  
  1659.    return((pEditInstance->unAreaWidth + 1)
  1660.          * pEditInstance->unAreaHeight);
  1661. }
  1662.  
  1663.  
  1664. /* ----------------------------------------------------------------------------
  1665.  * ODEditRememberArea()                                *** PRIVATE FUNCTION ***
  1666.  *
  1667.  * Stores a copy of the text currently displayed in the edit area in the
  1668.  * provided buffer, so that it can later be reused to redraw only the portion
  1669.  * of the edit area which has been changed by an operation.
  1670.  *
  1671.  * Parameters: pEditInstance   - Editor instance information structure.
  1672.  *
  1673.  *             pRememberedArea - Pointer to a buffer, which is at least
  1674.  *                               the number of bytes specified by
  1675.  *                               ODEditRememberBufferSize() in size.
  1676.  *
  1677.  *     Return: void
  1678.  */
  1679. static void ODEditRememberArea(tEditInstance *pEditInstance,
  1680.    void *pRememberedArea)
  1681. {
  1682.    UINT unDataLineOffset = 0;
  1683.    UINT unDataLineSize;
  1684.    UINT unAreaLine;
  1685.    UINT unBufferLine;
  1686.    UINT unLineLength;
  1687.    char *pchStartOfLine;
  1688.    char *pchDataLocation;
  1689.  
  1690.    ASSERT(pEditInstance != NULL);
  1691.    ASSERT(pRememberedArea != NULL);
  1692.  
  1693.    /* Determine the length of a single line in the remember buffer. */
  1694.    unDataLineSize = pEditInstance->unAreaWidth + 1;
  1695.  
  1696.    pchDataLocation = (char *)pRememberedArea + unDataLineOffset;
  1697.    for(unBufferLine = pEditInstance->unLineScrolledToTop, unAreaLine = 0;
  1698.       unAreaLine < pEditInstance->unAreaHeight;
  1699.       ++unAreaLine, ++unBufferLine)
  1700.    {
  1701.       /* If this line is not beyond the end of the buffer. */
  1702.       if(unBufferLine < pEditInstance->unLinesInBuffer)
  1703.       {
  1704.          /* Determine the length of this buffer line. */
  1705.          unLineLength = ODEditBufferGetLineLength(pEditInstance, unBufferLine);
  1706.  
  1707.          /* Determine the start location of this buffer line. */
  1708.          pchStartOfLine = ODEditBufferGetCharacter(pEditInstance, unBufferLine,
  1709.             0);
  1710.       }
  1711.       else
  1712.       {
  1713.          /* If this line is beyond the end of the buffer, then it is empty. */
  1714.          unLineLength = 0;
  1715.          pchStartOfLine = "";
  1716.       }
  1717.  
  1718.       /* Copy the contents of this line to the data buffer. */
  1719.       memcpy(pchDataLocation, pchStartOfLine, unLineLength);
  1720.       pchDataLocation[unLineLength] = '\0';
  1721.  
  1722.       /* Update the location where data is being stored in the buffer. */
  1723.       pchDataLocation += unDataLineSize;
  1724.    }
  1725. }
  1726.  
  1727.  
  1728. /* ----------------------------------------------------------------------------
  1729.  * ODEditRedrawChanged()                               *** PRIVATE FUNCTION ***
  1730.  *
  1731.  * Redraws the portion of the edit area which has been changed by an operation,
  1732.  * based on the original edit area contents as stored in a buffer by the
  1733.  * ODEditRememberArea() function.
  1734.  *
  1735.  * Parameters: pEditInstance   - Editor instance information structure.
  1736.  *
  1737.  *             pRememberedArea - Pointer to a buffer that was filled by a
  1738.  *                               previous call to ODEditRememberArea().
  1739.  *
  1740.  *             unUpperBoundary - The first line in the edit area to consider
  1741.  *                               redrawing, or REDRAW_NO_BOUNDARY to specify
  1742.  *                               the top of the edit area.
  1743.  *
  1744.  *             unLowerBoundary - The last line in the edit area to consider
  1745.  *                               redrawing, or REDRAW_NO_BOUNDARY to specify
  1746.  *                               the bottom of the edit area.
  1747.  *
  1748.  *     Return: void
  1749.  */
  1750. static void ODEditRedrawChanged(tEditInstance *pEditInstance,
  1751.    void *pRememberedArea, UINT unUpperBoundary, UINT unLowerBoundary)
  1752. {
  1753.    UINT unStartRedrawLine;
  1754.    UINT unStartRedrawColumn;
  1755.    UINT unFinishRedrawLine;
  1756.    UINT unFinishRedrawColumn;
  1757.    UINT unEstPartialRedrawBytes;
  1758.  
  1759.    ASSERT(pEditInstance != NULL);
  1760.    ASSERT(pRememberedArea != NULL);
  1761.  
  1762.    /* Determine what portion of the edit area, within the specified upper */
  1763.    /* and lower boundaries, has been changed.                             */
  1764.    if(!ODEditDetermineChanged(pEditInstance, pRememberedArea, unUpperBoundary,
  1765.       unLowerBoundary, &unStartRedrawLine, &unStartRedrawColumn,
  1766.       &unFinishRedrawLine, &unFinishRedrawColumn))
  1767.    {
  1768.       /* Nothing has changed in the edit area. */
  1769.       ODEditUpdateCursorIfMoved(pEditInstance);
  1770.       return;
  1771.    }
  1772.  
  1773.    /* Now that we have determined the portion of the edit area that would */
  1774.    /* be redraw by a partial (incremental) redraw, we compare the amount  */
  1775.    /* of data involved + the amount of data still in the outbound buffer  */
  1776.    /* with the amount of data involved in a full redraw. If the amount of */
  1777.    /* data in the outbound buffer cannot be determined, we default to     */
  1778.    /* using an incremental redraw.                                        */
  1779.    unEstPartialRedrawBytes = ODEditEstDrawBytes(pEditInstance,
  1780.       unStartRedrawLine, unStartRedrawColumn, unFinishRedrawLine,
  1781.       unFinishRedrawColumn);
  1782.    if(ODEditRecommendFullRedraw(pEditInstance, unEstPartialRedrawBytes,
  1783.       FALSE))
  1784.    {
  1785.       /* Purge the outbound buffer and do a full redraw. */
  1786.       ODEditRedrawArea(pEditInstance);
  1787.  
  1788.       /* Move the cursor back to its appropriate position. */
  1789.       ODEditUpdateCursorPos(pEditInstance);
  1790.    }
  1791.    else
  1792.    {
  1793.       /* Perform a partial (incremental) redraw. */
  1794.  
  1795.       /* Now, redraw the portion of the edit area that has been determined to */
  1796.       /* require redrawing.                                                   */
  1797.       ODEditRedrawSubArea(pEditInstance, unStartRedrawLine, unStartRedrawColumn,
  1798.          unFinishRedrawLine, unFinishRedrawColumn);
  1799.  
  1800.       /* Now, move the cursor back to its appropriate position, if it isn't */
  1801.       /* already there.                                                     */
  1802.       ODEditUpdateCursorIfMoved(pEditInstance);
  1803.    }
  1804. }
  1805.  
  1806.  
  1807. /* ----------------------------------------------------------------------------
  1808.  * ODEditDetermineChanged()                            *** PRIVATE FUNCTION ***
  1809.  *
  1810.  * Determines what portion of the edit area, within the specifiede upper and
  1811.  * lower boundary, has been changed. Area is specified as row and column
  1812.  * position of the first changed characer between boundaries, and the row
  1813.  * and column position past the last changed character between the boundaries.
  1814.  *
  1815.  * Parameters: pEditInstance         - Editor instance information structure.
  1816.  *
  1817.  *             pRememberedArea       - Pointer to a buffer that was filled by a
  1818.  *                                     previous call to ODEditRememberArea().
  1819.  *
  1820.  *             unUpperBoundary       - The first line in the edit area to
  1821.  *                                     consider redrawing, or
  1822.  *                                     REDRAW_NO_BOUNDARY to specify the top of 
  1823.  *                                     the edit area.
  1824.  *
  1825.  *             unLowerBoundary       - The last line in the edit area to
  1826.  *                                     consider redrawing, or
  1827.  *                                     REDRAW_NO_BOUNDARY to specify the bottom
  1828.  *                                     of the edit area.
  1829.  *
  1830.  *             punStartRedrawLine    - Output: Line of first changed character.
  1831.  *
  1832.  *             punStartRedrawColumn  - Output: Column of first changed char.
  1833.  *
  1834.  *             punFinishRedrawLine   - Output: Line of last changed character.
  1835.  *
  1836.  *             punFinishRedrawColumn - Output: Column after last changed char.
  1837.  *
  1838.  *     Return: TRUE if some text in the edit area has been changed, FALSE
  1839.  *             if there is no change.
  1840.  */
  1841. static BOOL ODEditDetermineChanged(tEditInstance *pEditInstance,
  1842.    void *pRememberedArea, UINT unUpperBoundary, UINT unLowerBoundary, 
  1843.    UINT *punStartRedrawLine, UINT *punStartRedrawColumn,
  1844.    UINT *punFinishRedrawLine, UINT *punFinishRedrawColumn)
  1845. {
  1846.    BOOL bFoundStart = FALSE;
  1847.    BOOL bFoundFinish = FALSE;
  1848.    UINT unDataLineOffset = 0;
  1849.    UINT unDataLineSize;
  1850.    UINT unAreaLine;
  1851.    UINT unLineLength;
  1852.    UINT unColumn;
  1853.    UINT unBufferLine;
  1854.    char *pchCurrent;
  1855.    char *pchRemembered;
  1856.  
  1857.    /* Determine the length of a single line in the remember buffer. */
  1858.    unDataLineSize = pEditInstance->unAreaWidth + 1;
  1859.  
  1860.    /* If caller specified no upper boundary, then reset upper boundary */
  1861.    /* to 0.                                                            */
  1862.    if(unUpperBoundary == REDRAW_NO_BOUNDARY) unUpperBoundary = 0;
  1863.  
  1864.    /* Likewise, iff caller specified no lower boundary, then reset the */
  1865.    /* lower boundary to the bottom of the edit area.                   */
  1866.    if(unLowerBoundary == REDRAW_NO_BOUNDARY)
  1867.    {
  1868.       unLowerBoundary = pEditInstance->unAreaHeight;
  1869.    }
  1870.  
  1871.    /* Loop through the area within boundaries, determining which */
  1872.    /* portion of the edit area has changed.                      */
  1873.    for(unBufferLine = pEditInstance->unLineScrolledToTop + unUpperBoundary,
  1874.       unAreaLine = unUpperBoundary; unAreaLine < unLowerBoundary;
  1875.       ++unAreaLine, ++unBufferLine)
  1876.    {
  1877.       /* Determine location of corresponding line in remembered data. */
  1878.       pchRemembered = (char *)pRememberedArea + unDataLineOffset
  1879.          + unDataLineSize * unAreaLine;
  1880.  
  1881.       /* If this line is not beyond the end of the buffer. */
  1882.       if(unBufferLine < pEditInstance->unLinesInBuffer)
  1883.       {
  1884.          /* Determine the start location of this buffer line. */
  1885.          pchCurrent = ODEditBufferGetCharacter(pEditInstance, unBufferLine, 0);
  1886.  
  1887.          /* Determine the length of this buffer line. */
  1888.          unLineLength = ODEditBufferGetLineLength(pEditInstance, unBufferLine);
  1889.       }
  1890.       else
  1891.       {
  1892.          pchCurrent = "";
  1893.          unLineLength = 0;
  1894.       }
  1895.  
  1896.       /* Start at the first column on this line. */
  1897.       unColumn = 0;
  1898.  
  1899.       /* Look for any characters that differ. */
  1900.       for(;; ++unColumn, ++pchCurrent, ++pchRemembered)
  1901.       {
  1902.          /* Determine if we are at the end of the remembered line. */
  1903.          BOOL bEndOfRemembered = (*pchRemembered == '\0');
  1904.  
  1905.          /* Determine if we are at the end of the current buffer line. */
  1906.          BOOL bEndOfCurrent = (unColumn == unLineLength);
  1907.  
  1908.          /* If we are at the end of either of the buffers (but not both), */
  1909.          /* or if these two characters differ, then we have found the     */
  1910.          /* start of the area that must be redrawn.                       */
  1911.          if(!(bEndOfRemembered && bEndOfCurrent))
  1912.          {
  1913.             if(bEndOfRemembered || bEndOfCurrent
  1914.                || *pchCurrent != *pchRemembered)
  1915.             {
  1916.                if(bFoundStart)
  1917.                {
  1918.                   bFoundFinish = FALSE;
  1919.                }
  1920.                else
  1921.                {
  1922.                   /* We have found a character that differs. */
  1923.                   bFoundStart = TRUE;
  1924.                   *punStartRedrawLine = unAreaLine;
  1925.                   *punStartRedrawColumn = unColumn;
  1926.                }
  1927.             }
  1928.          }
  1929.  
  1930.          /* If we have found the first changed text in the buffer, then we */
  1931.          /* are now looking for the last changed text in the buffer.       */
  1932.          if(bFoundStart && !bFoundFinish)
  1933.          {
  1934.             if(*pchCurrent == *pchRemembered)
  1935.             {
  1936.                bFoundFinish = TRUE;
  1937.                *punFinishRedrawLine = unAreaLine;
  1938.                *punFinishRedrawColumn = unColumn;
  1939.             }
  1940.             else if(bEndOfRemembered)
  1941.             {
  1942.                bFoundFinish = TRUE;
  1943.                *punFinishRedrawLine = unAreaLine;
  1944.                *punFinishRedrawColumn = unLineLength;
  1945.             }
  1946.             else if(bEndOfCurrent)
  1947.             {
  1948.                bFoundFinish = TRUE;
  1949.                *punFinishRedrawLine = unAreaLine;
  1950.                *punFinishRedrawColumn = unColumn + strlen(pchRemembered);
  1951.             }
  1952.          }
  1953.  
  1954.          /* If we are at the end of either buffers. */
  1955.          if(bEndOfRemembered || bEndOfCurrent)
  1956.          {
  1957.             /* Now, proceed to processing the next line in the edit area. */
  1958.             break;
  1959.          }
  1960.       }
  1961.    }
  1962.  
  1963.    /* If we haven't found any text in the edit area that has changed. */
  1964.    if(!bFoundStart)
  1965.    {
  1966.       /* Then return indicating no change. */
  1967.       return(FALSE);
  1968.    }
  1969.  
  1970.    /* If we haven't found an end to the portion of the area that has */
  1971.    /* changed, then we must redraw up to the end of the edit area.   */
  1972.    if(!bFoundFinish)
  1973.    {
  1974.       *punFinishRedrawLine = unLowerBoundary;
  1975.       *punFinishRedrawColumn = unColumn;
  1976.    }
  1977.  
  1978.    /* Return indicating that ther has been some change. */
  1979.    return(TRUE);
  1980. }
  1981.  
  1982.  
  1983. /* ----------------------------------------------------------------------------
  1984.  * ODEditRedrawSubArea()                               *** PRIVATE FUNCTION ***
  1985.  *
  1986.  * Redraws the portion of the edit area within the specified range. Redrawing
  1987.  * is performed from the location of the start redraw row and column, up to
  1988.  * but not includin gthe finish redraw row and column.
  1989.  *
  1990.  * Parameters: pEditInstance        - Editor instance information structure.
  1991.  *
  1992.  *             unStartRedrawLine    - Line of first character to draw.
  1993.  *
  1994.  *             unStartRedrawColumn  - Column of first character to draw.
  1995.  *
  1996.  *             unFinishRedrawLine   - Line of last character to draw.
  1997.  *
  1998.  *             unFinishRedrawColumn - Column after last character to draw.
  1999.  *
  2000.  *     Return: void
  2001.  */
  2002. static void ODEditRedrawSubArea(tEditInstance *pEditInstance,
  2003.    UINT unStartRedrawLine, UINT unStartRedrawColumn, UINT unFinishRedrawLine,
  2004.    UINT unFinishRedrawColumn)
  2005. {
  2006.    UINT unAreaLine;
  2007.    UINT unLineLength;
  2008.    UINT unBufferLine;
  2009.    char *pchCurrent;
  2010.    UINT unStartColumn;
  2011.    UINT unFinishColumn;
  2012.    UINT unScrnStartColumn;
  2013.    UINT unTextLength;
  2014.  
  2015.    /* Now, perform actual redraw in area that requires redrawing. */
  2016.    for(unBufferLine = pEditInstance->unLineScrolledToTop + unStartRedrawLine,
  2017.       unAreaLine = unStartRedrawLine; unAreaLine <= unFinishRedrawLine;
  2018.       ++unBufferLine, ++unAreaLine)
  2019.    {
  2020.       BOOL bFirstLine = (unAreaLine == unStartRedrawLine);
  2021.       BOOL bLastLine = (unAreaLine == unFinishRedrawLine);
  2022.       UINT unScrnRow = (UINT)pEditInstance->pUserOptions->nAreaTop
  2023.          + unAreaLine;
  2024.  
  2025.       /* If this line is not beyond the end of the buffer. */
  2026.       if(unBufferLine < pEditInstance->unLinesInBuffer)
  2027.       {
  2028.          pchCurrent = ODEditBufferGetCharacter(pEditInstance, unBufferLine, 0);
  2029.          unTextLength = unLineLength =
  2030.             ODEditBufferGetLineLength(pEditInstance, unBufferLine);
  2031.       }
  2032.       else
  2033.       {
  2034.          pchCurrent = "";
  2035.          unTextLength = unLineLength = 0;
  2036.       }
  2037.  
  2038.       /* Move to the position on the first line to begin redraw. */
  2039.       if(bFirstLine)
  2040.       {
  2041.          UINT unActualRow;
  2042.          UINT unActualColumn;
  2043.          ODEditGetActualCurPos(pEditInstance, &unActualRow, &unActualColumn);
  2044.  
  2045.          unStartColumn = unStartRedrawColumn;
  2046.          unScrnStartColumn = (UINT)pEditInstance->pUserOptions->nAreaLeft
  2047.             + unStartColumn;
  2048.  
  2049.          if(unScrnRow != unActualRow || unScrnStartColumn != unActualColumn)
  2050.          {
  2051.             od_set_cursor(unScrnRow, unScrnStartColumn);
  2052.          }
  2053.  
  2054.          pchCurrent += unStartRedrawColumn;
  2055.          unTextLength -= unStartRedrawColumn;
  2056.       }
  2057.       else
  2058.       {
  2059.          unStartColumn = 0;
  2060.          unScrnStartColumn = (UINT)pEditInstance->pUserOptions->nAreaLeft;
  2061.          od_set_cursor(unScrnRow, unScrnStartColumn);
  2062.       }
  2063.  
  2064.       /* If this is the last line to redraw, then adjust accordingly. */
  2065.       if(bLastLine)
  2066.       {
  2067.          if(unFinishRedrawColumn < unLineLength)
  2068.          {
  2069.             unTextLength -= unLineLength - unFinishRedrawColumn;
  2070.          }
  2071.          unFinishColumn = unFinishRedrawColumn;
  2072.       }
  2073.       else
  2074.       {
  2075.          unFinishColumn = pEditInstance->unAreaWidth;
  2076.       }
  2077.  
  2078.       /* Output changed text. */
  2079.       if(unStartColumn < unLineLength)
  2080.       {
  2081.          od_disp(pchCurrent, unTextLength, TRUE);
  2082.          unStartColumn += unTextLength;
  2083.       }
  2084.  
  2085.       /* If we need to clear the rest of the line. */
  2086.       if(unFinishColumn == pEditInstance->unAreaWidth)
  2087.       {
  2088.          /* If right edge of edit area aligns with the right edge of the */
  2089.          /* screen.                                                      */
  2090.          if(pEditInstance->pUserOptions->nAreaRight == OD_SCREEN_WIDTH)
  2091.          {
  2092.             /* Clear the remainder of this line on the screen. */
  2093.             od_clr_line();
  2094.          }
  2095.          else
  2096.          {
  2097.             /* Place spaces after the end of the current line, up to right */
  2098.             /* edge of the edit area.                                      */
  2099.             od_repeat(' ', (BYTE)(pEditInstance->unAreaWidth - unLineLength));
  2100.          }
  2101.       }
  2102.       else if(unStartColumn < unFinishColumn)
  2103.       {
  2104.          od_repeat(' ', (BYTE)(unFinishColumn - unStartColumn));
  2105.       }
  2106.    }
  2107. }
  2108.  
  2109.  
  2110. /* ----------------------------------------------------------------------------
  2111.  * ODEditGetActualCurPos()                             *** PRIVATE FUNCTION ***
  2112.  *
  2113.  * Estimates the actual position of the cursor on the screen.
  2114.  *
  2115.  * Parameters: pEditInstance   - Editor instance information structure.
  2116.  *
  2117.  *             punRow          - Pointer to location where cursor row number
  2118.  *                               should be stored.
  2119.  *
  2120.  *             punColumn       - Pointer to location where cursor column number
  2121.  *                               should be stored.
  2122.  *
  2123.  *     Return: void
  2124.  */
  2125. static void ODEditGetActualCurPos(tEditInstance *pEditInstance,
  2126.    UINT *punRow, UINT *punColumn)
  2127. {
  2128.    tODScrnTextInfo TextInfo;
  2129.  
  2130.    ASSERT(pEditInstance != NULL);
  2131.    ASSERT(punRow != NULL);
  2132.    ASSERT(punColumn != NULL);
  2133.  
  2134.    UNUSED(pEditInstance);
  2135.  
  2136.    /* Obtain current cursor position information from the ODScrn module. */
  2137.    ODScrnGetTextInfo(&TextInfo);
  2138.    *punRow = (UINT)TextInfo.cury;
  2139.    *punColumn = (UINT)TextInfo.curx;
  2140. }
  2141.  
  2142.  
  2143. /* ----------------------------------------------------------------------------
  2144.  * ODEditIsEOLForMode()                                *** PRIVATE FUNCTION ***
  2145.  *
  2146.  * Determines whether the specified character should be treated as an EOL
  2147.  * character for the current mode.
  2148.  *
  2149.  * Parameters: pEditInstance   - Editor instance information structure.
  2150.  *
  2151.  *     Return: TRUE if this is an EOL character, FALSE otherwise.
  2152.  */
  2153. static BOOL ODEditIsEOLForMode(tEditInstance *pEditInstance, char chToTest)
  2154. {
  2155.    switch(pEditInstance->pUserOptions->TextFormat)
  2156.    {
  2157.       case FORMAT_FTSC_MESSAGE:
  2158.          return(chToTest == '\r' || chToTest == '\0');
  2159.  
  2160.       default:
  2161.          return(IS_EOL_CHAR(chToTest));
  2162.    }
  2163. }
  2164.  
  2165.  
  2166.  
  2167. /* ========================================================================= */
  2168. /* Low level buffer manipulation functions.                                  */
  2169. /* ========================================================================= */
  2170.  
  2171. /* ----------------------------------------------------------------------------
  2172.  * ODEditBufferFormatAndIndex()                        *** PRIVATE FUNCTION ***
  2173.  *
  2174.  * Regenerates the count of lines in the buffer, and the array of pointers to
  2175.  * the start of each line.
  2176.  *
  2177.  * Parameters: pEditInstance - Editor instance information structure.
  2178.  *
  2179.  *     Return: TRUE on success, or FALSE if there is not enough memory
  2180.  *             available to complete this operation.
  2181.  */
  2182. static BOOL ODEditBufferFormatAndIndex(tEditInstance *pEditInstance)
  2183. {
  2184.    char *pch;
  2185.    char *pchLastSpace;
  2186.    UINT unProcessingColumn;
  2187.    UINT unProcessingLine;
  2188.    BOOL bAtEndOfBuffer = FALSE;
  2189.    BOOL bLineEndedByBreak;
  2190.    BOOL bFTSCMode =
  2191.       (pEditInstance->pUserOptions->TextFormat == FORMAT_FTSC_MESSAGE);
  2192.  
  2193.    ASSERT(pEditInstance != NULL);
  2194.  
  2195.    /* Reset current line count. */
  2196.    unProcessingLine = 0;
  2197.  
  2198.    /* Begin at the beginning of the buffer to edit. */
  2199.    pch = pEditInstance->pszEditBuffer;
  2200.    ASSERT(pch != NULL);
  2201.  
  2202.    /* Loop for each line in the buffer. */
  2203.    while(!bAtEndOfBuffer)   
  2204.    {
  2205.       /* In FTSC mode, skip a line if it begins with a ^A ("kludge lines"). */
  2206.       if(bFTSCMode)
  2207.       {
  2208.          /* Loop while the current line begins with a ^A. */
  2209.          while(*pch == 0x01)
  2210.          {
  2211.             /* Loop until the end of the line is found. */
  2212.             while(!ODEditIsEOLForMode(pEditInstance, *pch)) ++pch;
  2213.  
  2214.             if(*pch == '\0')
  2215.             {
  2216.                /* If the line was ended by a null character, then note that */
  2217.                /* the end of the buffer has been reached.                   */
  2218.                bAtEndOfBuffer = TRUE;
  2219.             }
  2220.             else
  2221.             {
  2222.                /* If the line was not ended by a null character, then skip */
  2223.                /* the end of line character.                               */
  2224.                ++pch;
  2225.             }
  2226.          }
  2227.  
  2228.          continue;
  2229.       }
  2230.  
  2231.       /* Add the address of the start of this line to the line array. */
  2232.       
  2233.       /* If the line array is full, then attempt to grow it. */
  2234.       ASSERT(unProcessingLine <= pEditInstance->unLineArraySize);
  2235.       if(unProcessingLine == pEditInstance->unLineArraySize)
  2236.       {
  2237.          /* Determine the size to grow the array to. */
  2238.          UINT unNewArraySize = pEditInstance->unLineArraySize
  2239.             + LINE_ARRAY_GROW_SIZE;
  2240.  
  2241.          /* Attempt to reallocate this memory block. */
  2242.          char **papchNewLineArray = (char **)realloc(
  2243.             pEditInstance->papchStartOfLine, unNewArraySize * sizeof(char *));
  2244.  
  2245.          /* If reallocation failed, then return with failure. */
  2246.          if(papchNewLineArray == NULL)
  2247.          {
  2248.             return(FALSE);
  2249.          }
  2250.  
  2251.          /* Otherwise, update the editor instance information with the new */
  2252.          /* array address and array size information.                      */
  2253.          pEditInstance->papchStartOfLine = papchNewLineArray;
  2254.          pEditInstance->unLineArraySize = unNewArraySize;
  2255.       }
  2256.  
  2257.       /* Add the address of the start of this line to the array. */
  2258.       pEditInstance->papchStartOfLine[unProcessingLine] = pch;
  2259.  
  2260.       /* Reset word wrap information. */
  2261.       pchLastSpace = NULL;
  2262.  
  2263.       /* Now, find the end of this line. */
  2264.       bLineEndedByBreak = TRUE;
  2265.       unProcessingColumn = 0;
  2266.       while(!ODEditIsEOLForMode(pEditInstance, *pch))
  2267.       {
  2268.          /* If this character is a space, then record the location of the */
  2269.          /* last space characters.                                        */
  2270.          if(*pch == ' ') pchLastSpace = pch;
  2271.  
  2272.          /* Check for characters which must be filtered from the buffer */
  2273.          /* in FTSC message mode.                                       */
  2274.          if(bFTSCMode)
  2275.          {
  2276.             if(*pch == 0x0a || ((unsigned char)*pch) == 0x8d)
  2277.             {
  2278.                /* If this character must be removed, then move rest of */
  2279.                /* buffer up by one character, and proceed to next loop */
  2280.                /* iteration.                                           */
  2281.                memmove(pch, pch + 1, strlen(pch + 1) + 1);
  2282.                continue;
  2283.             }
  2284.          }
  2285.  
  2286.          /* Increment count of characters on this line. */
  2287.          ++unProcessingColumn;
  2288.  
  2289.          /* Check whether we have reached the maximum number of characters */
  2290.          /* that will fit on this line.                                    */
  2291.          if(unProcessingColumn >= pEditInstance->unAreaWidth - 1)
  2292.          {
  2293.             if(pEditInstance->bWordWrapLongLines)
  2294.             {
  2295.                /* If we are to word wrap long lines, then back up to the */
  2296.                /* beginning of the last word, if we have encountered any */
  2297.                /* space characters.                                      */
  2298.                if(pchLastSpace != NULL && pchLastSpace < pch)
  2299.                {
  2300.                   /* Update current column number accordingly. */
  2301.                   unProcessingColumn -= (UINT)(pch - pchLastSpace);
  2302.  
  2303.                   /* Back up to position to perform word wrap at. */
  2304.                   pch = pchLastSpace;
  2305.                }
  2306.             }
  2307.  
  2308.             /* If we are wrapping text where the cursor is located, then we */
  2309.             /* will have to reposition the cursor accordingly.              */
  2310.             if(unProcessingLine == pEditInstance->unCurrentLine
  2311.                && unProcessingColumn < pEditInstance->unCurrentColumn)
  2312.             {
  2313.                /* Move the cursor to the next line. */
  2314.                pEditInstance->unCurrentLine++;
  2315.  
  2316.                /* Adjust the cursor column number to the position where the */
  2317.                /* corresponding wrapped text will appear.                   */
  2318.                pEditInstance->unCurrentColumn -= unProcessingColumn;
  2319.             }
  2320.  
  2321.             /* Note that line was not ended by en explicit line break. */
  2322.             bLineEndedByBreak = FALSE;
  2323.  
  2324.             break;
  2325.          }
  2326.  
  2327.          /* Move to the next character in the buffer. */
  2328.          ++pch;
  2329.       }
  2330.  
  2331.       /* If we the line was terminated by a '\0', then note that the end of */
  2332.       /* the buffer has been reached.                                       */
  2333.       if(*pch == '\0')
  2334.       {
  2335.          bAtEndOfBuffer = TRUE;
  2336.       }
  2337.  
  2338.       /* If the line was not terminated by a '\0', then find the first */
  2339.       /* character of the next line.                                   */
  2340.       else
  2341.       {
  2342.          char chFirstEOLChar = *pch;
  2343.          char chSecondEOLChar = '\0';
  2344.  
  2345.          /* Move to the next character in the buffer. */
  2346.          ++pch;
  2347.          
  2348.          /* If this character is a different EOL sequence character from the */
  2349.          /* already encountered EOL character, then skip past it too.        */
  2350.          if(ODEditIsEOLForMode(pEditInstance, chFirstEOLChar) && *pch != '\0'
  2351.             && ODEditIsEOLForMode(pEditInstance, *pch)
  2352.             && *pch != chFirstEOLChar)
  2353.          {
  2354.             chSecondEOLChar = *pch;
  2355.             ++pch;
  2356.          }
  2357.          
  2358.          /* If we don't already know what form of line/paragraph break to */
  2359.          /* use (just a CR, just a LF, a CR/LF sequence or a LF/CR        */
  2360.          /* sequence), then use this line termination as an example of    */
  2361.          /* what should be used.                                          */
  2362.          if(bLineEndedByBreak)
  2363.          {
  2364.             ODEditSetBreakSequence(pEditInstance, chFirstEOLChar,
  2365.                chSecondEOLChar);
  2366.          }
  2367.       }
  2368.  
  2369.       /* Increment the count of the current line number. */
  2370.       unProcessingLine++;
  2371.    }
  2372.  
  2373.    /* Update count of number of lines in the buffer, based on the number */
  2374.    /* that we have found.                                                */
  2375.    pEditInstance->unLinesInBuffer = unProcessingLine;
  2376.  
  2377.    /* Return with success. */
  2378.    return(TRUE);
  2379. }
  2380.  
  2381.  
  2382. /* ----------------------------------------------------------------------------
  2383.  * ODEditBufferGetLineLength()                         *** PRIVATE FUNCTION ***
  2384.  *
  2385.  * Determines the length of the specified line in the buffer, not including
  2386.  * any line terminator characters.
  2387.  *
  2388.  * Parameters: pEditInstance - Editor instance information structure.
  2389.  *
  2390.  *             unBufferLine  - 0-based index of the line in question.
  2391.  *
  2392.  *     Return: The number of characters on this line.
  2393.  */
  2394. static UINT ODEditBufferGetLineLength(tEditInstance *pEditInstance,
  2395.    UINT unBufferLine)
  2396. {
  2397.    char *pch;
  2398.    char *pchStartOfLine;
  2399.    UINT unCharsBeforeEOL;
  2400.  
  2401.    ASSERT(pEditInstance != NULL);
  2402.    ASSERT(unBufferLine < pEditInstance->unLinesInBuffer);
  2403.    ASSERT(pEditInstance->unLinesInBuffer <= pEditInstance->unLineArraySize);
  2404.  
  2405.    /* Get the address of the start of this line in the buffer. */
  2406.    pchStartOfLine
  2407.       = ODEditBufferGetCharacter(pEditInstance, unBufferLine, 0);
  2408.  
  2409.    /* Count the number of characters before the next end of line character. */
  2410.    for(pch = pchStartOfLine, unCharsBeforeEOL = 0;
  2411.       !ODEditIsEOLForMode(pEditInstance, *pch);
  2412.       ++unCharsBeforeEOL, ++pch);
  2413.  
  2414.    /* If this is the last line in the buffer, then the number of characers */
  2415.    /* before the next end of line character is the length of this line.    */
  2416.    if(unBufferLine >= pEditInstance->unLinesInBuffer - 1)
  2417.    {
  2418.       return(unCharsBeforeEOL);
  2419.    }
  2420.  
  2421.    /* If this is not the last line in the buffer, then the length of this  */
  2422.    /* line is the minimum of the number of characters before the next end  */
  2423.    /* of line character and the number of characters before the next line, */
  2424.    /* according to the line index information. This is because all lines   */
  2425.    /* do not necessarily end with an end-of-line character.                */
  2426.    else
  2427.    {
  2428.        return(MIN(unCharsBeforeEOL, (UINT)(ODEditBufferGetCharacter(
  2429.           pEditInstance, unBufferLine + 1, 0) - pchStartOfLine)));
  2430.    }
  2431. }
  2432.  
  2433.  
  2434. /* ----------------------------------------------------------------------------
  2435.  * ODEditBufferGetTotalLines()                         *** PRIVATE FUNCTION ***
  2436.  *
  2437.  * Determines the number of lines in the current edit buffer.
  2438.  *
  2439.  * Parameters: pEditInstance - Editor instance information structure.
  2440.  *
  2441.  *     Return: The total number of lines in the buffer.
  2442.  */
  2443. static UINT ODEditBufferGetTotalLines(tEditInstance *pEditInstance)
  2444. {
  2445.    ASSERT(pEditInstance != NULL);
  2446.    ASSERT(pEditInstance->unLinesInBuffer <= pEditInstance->unLineArraySize);
  2447.  
  2448.    /* Return the total number of lines in the buffer. */
  2449.    return(pEditInstance->unLinesInBuffer);
  2450. }
  2451.  
  2452.  
  2453. /* ----------------------------------------------------------------------------
  2454.  * ODEditBufferGetCharacter()                          *** PRIVATE FUNCTION ***
  2455.  *
  2456.  * Obtains the character at the specified position in the buffer.
  2457.  *
  2458.  * Parameters: pEditInstance  - Editor instance information structure.
  2459.  *
  2460.  *             unBufferLine   - 0-based index of the line in question.
  2461.  *
  2462.  *             unBufferColumn - The position in the line of the required
  2463.  *                              character.
  2464.  *
  2465.  *     Return: A pointer to the character at the specified position in the
  2466.  *             specified line. The caller can assume that any remaining
  2467.  *             character(s) in the line follow this character, but should
  2468.  *             not assume that the pointer can be used to access following
  2469.  *             lines in the buffer.
  2470.  */
  2471. static char *ODEditBufferGetCharacter(tEditInstance *pEditInstance,
  2472.    UINT unBufferLine, UINT unBufferColumn)
  2473. {
  2474.    ASSERT(pEditInstance != NULL);
  2475.    ASSERT(unBufferLine < pEditInstance->unLinesInBuffer);
  2476.    ASSERT(pEditInstance->unLinesInBuffer <= pEditInstance->unLineArraySize);
  2477.    ASSERT(unBufferColumn <= ODEditBufferGetLineLength(pEditInstance, unBufferLine));
  2478.  
  2479.    /* The position of this character is the position of this line, plus */
  2480.    /* the number of characters into the line.                           */
  2481.    return(pEditInstance->papchStartOfLine[unBufferLine] + unBufferColumn);
  2482. }
  2483.  
  2484.  
  2485. /* ----------------------------------------------------------------------------
  2486.  * ODEditBufferMakeSpace()                             *** PRIVATE FUNCTION ***
  2487.  *
  2488.  * Moves the remaining buffer contents by the specified distance to make room
  2489.  * for new text. The new space is filled by space (' ') characters. Does not
  2490.  * necessarily reindex the buffer before returning, and so this should be done
  2491.  * by the caller after the new buffer space has been filled.
  2492.  *
  2493.  * Parameters: pEditInstance       - Editor instance information structure.
  2494.  *
  2495.  *             unLine              - Line number to make more room on.
  2496.  *
  2497.  *             unColumn            - Column number to insert the space.
  2498.  *
  2499.  *             unNumChars          - Number of characters to make room for.
  2500.  *
  2501.  *     Return: kODRCSuccess on success, kODRCSafeFailure if we were unable to
  2502.  *             obtain enough buffer space before making any changes, or
  2503.  *             kODRCUnrecoverableFailure if the buffer has been changed, but
  2504.  *             there was insufficient memory to re-index the buffer.
  2505.  */
  2506. static tODResult ODEditBufferMakeSpace(tEditInstance *pEditInstance,
  2507.    UINT unLine, UINT unColumn, UINT unNumChars)
  2508. {
  2509.    UINT unLineLength;
  2510.    UINT unBufferUsed;
  2511.    UINT unBufferUnused;
  2512.    UINT unRemainingBufferBytes;
  2513.    UINT unCount;
  2514.    char *pchBufferPos;
  2515.    tODResult Result;
  2516.  
  2517.    ASSERT(pEditInstance != NULL);
  2518.    ASSERT(unLine < pEditInstance->unLinesInBuffer);
  2519.  
  2520.    /* Determine the current length of the specified line. */
  2521.    unLineLength = ODEditBufferGetLineLength(pEditInstance, unLine);
  2522.  
  2523.    /* If a column past the current end of this line was specified, then    */
  2524.    /* adjust column and number of characters in order to extend the line   */
  2525.    /* up to the specified column as well as adding space beginning at that */
  2526.    /* column.                                                              */
  2527.    if(unColumn > unLineLength)
  2528.    {
  2529.       UINT unExtendLineBy = unColumn - unLineLength;
  2530.       unColumn -= unExtendLineBy;
  2531.       unNumChars += unExtendLineBy;
  2532.    }
  2533.  
  2534.    /* Now, determine whether the buffer is large enough for the additional */
  2535.    /* space that will be added.                                            */
  2536.    unBufferUsed = strlen(pEditInstance->pszEditBuffer) + 1;
  2537.    unBufferUnused = pEditInstance->unBufferSize - unBufferUsed;
  2538.    if(unBufferUnused < unNumChars)
  2539.    {
  2540.       /* There is not currently sufficient room in the buffer for the new */
  2541.       /* characters, then attempt to grow the buffer to make more room.   */
  2542.       Result = ODEditTryToGrow(pEditInstance, unBufferUsed + unNumChars);
  2543.       if(Result != kODRCSuccess)
  2544.       {
  2545.          /* On failure, return the result code that indicates the nature */
  2546.          /* of the failure.                                              */
  2547.          return(Result);
  2548.       }
  2549.    }
  2550.  
  2551.    /* Now, shift the buffer contents from this location forward by */
  2552.    /* unNumChars characters.                                       */
  2553.    pchBufferPos = ODEditBufferGetCharacter(pEditInstance, unLine, unColumn);
  2554.    unRemainingBufferBytes = strlen(pchBufferPos) + 1;
  2555.    memmove(pchBufferPos + unNumChars, pchBufferPos, unRemainingBufferBytes);
  2556.  
  2557.    /* Next, we fill the new buffer area with space characters. */
  2558.    for(unCount = 0; unCount < unNumChars; ++unCount)
  2559.    {
  2560.       *pchBufferPos++ = ' ';
  2561.    }
  2562.  
  2563.    /* Return with success. */
  2564.    return(kODRCSuccess);
  2565. }
  2566.  
  2567.  
  2568. /* ----------------------------------------------------------------------------
  2569.  * ODEditTryToGrow()                                   *** PRIVATE FUNCTION ***
  2570.  *
  2571.  * Attempts to reallocate the buffer to a larger size. This function is called
  2572.  * if it is found that the current buffer isn't large enough to complete some
  2573.  * operation. If the client application has setup the editor to permit buffer
  2574.  * reallocation, then this function will call the realloc callback function
  2575.  * supplied by the client application. If buffer growing is not possible, then
  2576.  * this function automatically fails.
  2577.  *
  2578.  * Parameters: pEditInstance       - Editor instance information structure.
  2579.  *
  2580.  *             unSizeNeeded        - The minimum buffer size that is needed.
  2581.  *
  2582.  *     Return: kODRCSuccess on success, kODRCSafeFailure if we were unable to
  2583.  *             obtain enough buffer space before making any changes, or
  2584.  *             kODRCUnrecoverableFailure if the buffer has been changed, but
  2585.  *             there was insufficient memory to re-index the buffer.
  2586.  */
  2587. static tODResult ODEditTryToGrow(tEditInstance *pEditInstance,
  2588.    UINT unSizeNeeded)
  2589. {
  2590.    BOOL bFullReIndexRequired = FALSE;
  2591.  
  2592.    ASSERT(pEditInstance != NULL);
  2593.    ASSERT(unSizeNeeded > pEditInstance->unBufferSize);
  2594.  
  2595.    if(pEditInstance->pUserOptions->pfBufferRealloc != NULL)
  2596.    {
  2597.       /* If the buffer is growable, then attempt to grow it using the */
  2598.       /* realloc function provided by the client application.         */
  2599.       UINT unNewBufferSize = MAX(pEditInstance->unBufferSize
  2600.          + BUFFER_GROW_SIZE, unSizeNeeded);
  2601.       char *pszNewBuffer = (char *)((*pEditInstance->pUserOptions->
  2602.          pfBufferRealloc)(pEditInstance->pszEditBuffer, unNewBufferSize));
  2603.  
  2604.       /* If we were unable to grow the buffer, then fail now. At this */
  2605.       /* point, nothing has changed, and so the buffer information    */
  2606.       /* is still intact and valid.                                   */
  2607.       if(pszNewBuffer == NULL)
  2608.       {
  2609.          return(kODRCSafeFailure);
  2610.       }
  2611.  
  2612.       /* Otherwise, determine whether the entire buffer will now have to */
  2613.       /* be reindexed. This is necessary if the reallocated buffer is at */
  2614.       /* a new location than the original was.                           */
  2615.       if(pszNewBuffer != pEditInstance->pszEditBuffer)
  2616.       {
  2617.          bFullReIndexRequired = TRUE;
  2618.       }
  2619.  
  2620.       /* Now, store the new buffer pointer and buffer size information. */
  2621.       pEditInstance->pszEditBuffer = pszNewBuffer;
  2622.       pEditInstance->unBufferSize = unNewBufferSize;
  2623.    }
  2624.    else
  2625.    {
  2626.       /* If the buffer is not growable, then fail right away. */
  2627.       return(kODRCSafeFailure);
  2628.    }
  2629.  
  2630.    /* If a full reindex is required due to buffer reallocation, then do so. */
  2631.    if(bFullReIndexRequired)
  2632.    {
  2633.       if(!ODEditBufferFormatAndIndex(pEditInstance))
  2634.       {
  2635.          /* If this fails, then return with failure. */
  2636.          return(kODRCUnrecoverableFailure);
  2637.       }
  2638.       bFullReIndexRequired = FALSE;
  2639.    }
  2640.  
  2641.    /* If we get to this point, we suceeded in growing the buffer to the */
  2642.    /* required size, so return with success.                            */
  2643.    return(kODRCSuccess);
  2644. }
  2645.