home *** CD-ROM | disk | FTP | other *** search
/ synchro.net / synchro.net.tar / synchro.net / main / BBS / ODOORS62.ZIP / ODBlock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-08-07  |  28.5 KB  |  831 lines

  1. /* OpenDoors Online Software Programming Toolkit
  2.  * (C) Copyright 1991 - 1999 by Brian Pirie.
  3.  *
  4.  * This library is free software; you can redistribute it and/or
  5.  * modify it under the terms of the GNU Lesser General Public
  6.  * License as published by the Free Software Foundation; either
  7.  * version 2 of the License, or (at your option) any later version.
  8.  *
  9.  * This library is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12.  * Lesser General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU Lesser General Public
  15.  * License along with this library; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17.  *
  18.  *
  19.  *        File: ODBlock.c
  20.  *
  21.  * Description: Implements the text block manipulation functions.
  22.  *
  23.  *   Revisions: Date          Ver   Who  Change
  24.  *              ---------------------------------------------------------------
  25.  *              Oct 13, 1994  6.00  BP   New file header format.
  26.  *              Dec 09, 1994  6.00  BP   Standardized coding style.
  27.  *              Aug 19, 1995  6.00  BP   32-bit portability.
  28.  *              Nov 11, 1995  6.00  BP   Removed register keyword.
  29.  *              Nov 14, 1995  6.00  BP   Added include of odscrn.h.
  30.  *              Nov 16, 1995  6.00  BP   Removed oddoor.h, added odcore.h.
  31.  *              Dec 12, 1995  6.00  BP   Added entry, exit and kernel macros.
  32.  *              Dec 30, 1995  6.00  BP   Added ODCALL for calling convention.
  33.  *              Feb 19, 1996  6.00  BP   Changed version number to 6.00.
  34.  *              Mar 03, 1996  6.10  BP   Begin version 6.10.
  35.  */
  36.  
  37. #define BUILDING_OPENDOORS
  38.  
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <stdio.h>
  42.  
  43. #include "OpenDoor.h"
  44. #include "ODCore.h"
  45. #include "ODGen.h"
  46. #include "ODScrn.h"
  47. #include "ODKrnl.h"
  48.  
  49.  
  50. /* Set to TRUE when od_puttext() should leave the cursor in its original */
  51. /* position */
  52. static BOOL bScrollAction = TRUE;
  53.  
  54.  
  55.  
  56. /* ----------------------------------------------------------------------------
  57.  * od_puttext()
  58.  *
  59.  * Displays the contents of the buffer passed in block. Leaves cursor in
  60.  * original position, unless bScrollAction is FALSE. Leaves colour at
  61.  * original value.
  62.  *
  63.  * Parameters: nLeft   - Column number of left edge of block of text to
  64.  *                       transfer, where 1 is the leftmost column of the
  65.  *                       screen.
  66.  *
  67.  *             nTop    - Row number of the top edge of block of text to
  68.  *                       to transfer, where 1 is the top row of the screen.
  69.  *
  70.  *             nRight  - Column number of the right edge of block.
  71.  *
  72.  *             nBottom - Row number of bottom edge of block.
  73.  *
  74.  *             pBlock  - Pointer to buffer that has been filled in the format
  75.  *                       used by od_gettext().
  76.  *
  77.  *     Return: TRUE on success, FALSE on failure.
  78.  */
  79. ODAPIDEF BOOL ODCALL od_puttext(INT nLeft, INT nTop, INT nRight, INT nBottom,
  80.    void *pBlock)
  81. {
  82.    INT nRowLength = nRight - nLeft +1;
  83.    INT nRowBytes = nRowLength * 2;
  84.    char *pchTest;
  85.    char *pchMemory;
  86.    char *pBuffer;
  87.    char *pchScreenBlock;
  88.    INT nBlockRow = 0;
  89.    INT nOutRow;
  90.    INT nOutColour = 999;
  91.    INT nOutColumn, nCheckColumn;
  92.    char *pchMemBlock;
  93.    INT nMoveCost = od_control.user_avatar ? 4 : 7;
  94.    BYTE btMaxRight, btMaxBottom;
  95.  
  96.    /* Log function entry if running in trace mode. */
  97.    TRACE(TRACE_API, "od_puttext()");
  98.  
  99.    /* Ensure that OpenDoors is initialized before proceeding. */
  100.    if(!bODInitialized) od_init();
  101.  
  102.    OD_API_ENTRY();
  103.  
  104.    /* Get current display setting profile. */
  105.    ODScrnGetTextInfo(&ODTextInfo);
  106.  
  107.    /* Calculate the maximum values for bottom and right of block. */
  108.    btMaxRight=ODTextInfo.winright-ODTextInfo.winleft+1;
  109.    btMaxBottom=ODTextInfo.winbottom-ODTextInfo.wintop+1;
  110.  
  111.    /* Check that parameters seem reasonable. */
  112.    if(nLeft<1 || nTop<1 || nRight>btMaxRight || nBottom>btMaxBottom
  113.       || nTop > nBottom || nLeft > nRight || pBlock==NULL)
  114.    {
  115.       od_control.od_error = ERR_PARAMETER;
  116.       OD_API_EXIT();
  117.       return(FALSE);
  118.    }
  119.  
  120.    /* Ensure that ANSI and/or AVATAR mode is available. */
  121.    if(!od_control.user_ansi && !od_control.user_avatar)
  122.    {
  123.       od_control.od_error = ERR_NOGRAPHICS;
  124.       OD_API_EXIT();
  125.       return(FALSE);
  126.    }
  127.  
  128.    /* If OpenDoors is operating in remote mode. */
  129.    if(od_control.baud != 0)
  130.    {
  131.       /* Allocate temporary buffer to store original screen contents while */
  132.       /* buffer paste is being performed.                                  */
  133.       if((pBuffer=malloc(nRowBytes*(nBottom-nTop+1)))==NULL)
  134.       {
  135.          od_control.od_error = ERR_MEMORY;
  136.          OD_API_EXIT();
  137.          return(FALSE);
  138.       }
  139.  
  140.       /* Get current screen contents of area to be pasted into, storing */
  141.       /* into the temporary buffer.                                     */
  142.       if(!ODScrnGetText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight, (BYTE)nBottom,
  143.          pBuffer))
  144.       {
  145.          od_control.od_error = ERR_PARAMETER;
  146.          free(pBuffer);
  147.          OD_API_EXIT();
  148.          return(FALSE);
  149.       }
  150.    }
  151.  
  152.    /* Display the block to be pasted on the local screen. */
  153.    if(!ODScrnPutText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight, (BYTE)nBottom,
  154.       pBlock))
  155.    {
  156.       od_control.od_error = ERR_PARAMETER;
  157.       free(pBuffer);
  158.       OD_API_EXIT();
  159.       return(FALSE);
  160.    }
  161.  
  162.    /* If operating in remote mode. */
  163.    if(od_control.baud != 0)
  164.    {
  165.       /* Loop for each line in the buffer to be pasted */
  166.       for(nOutRow=nTop;nOutRow<=nBottom;++nOutRow,++nBlockRow)
  167.       {
  168.          /* Setup pointer to beginning of line of original screen contents. */
  169.          pchScreenBlock=(char *)pBuffer+(nRowBytes*nBlockRow);
  170.  
  171.          /* Setup pointer to beginning of line of block to be displayed. */
  172.          pchMemBlock=(char *)pBlock+(nRowBytes*nBlockRow);
  173.  
  174.          /* Loop for each column on this line. */
  175.          for(nOutColumn=0;nOutColumn<nRowLength;)
  176.          {
  177.             /* Loop from this character onwards, counting number of */
  178.             /* characters that don't need to be changed. */
  179.             nCheckColumn=nOutColumn;
  180.             pchMemory=((char *)pchMemBlock)+(nCheckColumn<<1);
  181.             pchTest=((char *)pchScreenBlock)+(nCheckColumn<<1);
  182.             for(;nCheckColumn<nRowLength;++nCheckColumn)
  183.             {
  184.                if(od_control.od_full_put) break;
  185.  
  186.                /* If both buffers have space characters. */
  187.                if(*pchMemory==*pchTest && (*pchTest==' ' || *pchTest=='\0'))
  188.                {
  189.                   /* If colours differ, then stop comparison loop. */
  190.                   if((pchTest[1]&0x70) != (pchMemory[1]&0x70))
  191.                   {
  192.                      break;
  193.                   }
  194.                }
  195.  
  196.                /* If both have different character and colour attributes. */
  197.                else if(*((int *)pchTest) != *((int *)pchMemory))
  198.                {
  199.                   /* Then stop comparison loop now. */
  200.                   break;
  201.                }
  202.  
  203.                /* Increment source and background pointers by two bytes. */
  204.                pchTest+=2;
  205.                pchMemory+=2;
  206.             }
  207.  
  208.             /* If no futher text to change on this line. */
  209.             if(nCheckColumn==nRowLength)
  210.             {
  211.                /* Move to the next line. */
  212.                goto next_line;
  213.             }
  214.  
  215.             /* If this is the first text to be displayed on this line. */
  216.             if(nOutColumn == 0)
  217.             {
  218.                /* Move the cursor to the first text to be changed on line. */
  219.                nOutColumn = nCheckColumn;
  220.  
  221.                /* If AVATAR mode is available. */
  222.                if(od_control.user_avatar)
  223.                {
  224.                   /* Send the avatar cursor positioning command. */
  225.                   szODWorkString[0]=22;
  226.                   szODWorkString[1]=8;
  227.                   szODWorkString[2]=nOutRow;
  228.                   szODWorkString[3]=nLeft+nOutColumn;
  229.                   od_disp(szODWorkString,4,FALSE);
  230.                }
  231.                else
  232.                {
  233.                   /* Otherwise, send the ANSI cursor positioning command. */
  234.                   sprintf(szODWorkString,"x[%d;%dH",nOutRow,nLeft + nOutColumn);
  235.                   szODWorkString[0]=27;
  236.                   od_disp(szODWorkString, strlen(szODWorkString), FALSE);
  237.                }
  238.             }
  239.  
  240.             /* If the number of characters after current cursor position  */
  241.             /* which must be changed, is greater than the number of       */
  242.             /* characters required to reposition the cursor on this line, */
  243.             /* then move the cursor to skip unchanged characters.         */
  244.             else if((nCheckColumn-nOutColumn)>nMoveCost)
  245.             {
  246.                nOutColumn=nCheckColumn;
  247.                /* If AVATAR mode is available. */
  248.                if(od_control.user_avatar)
  249.                {
  250.                   /* Advance cursor appropriate number of characters */
  251.                   /* using the AVATAR cursor position command.       */
  252.                   szODWorkString[0]=22;
  253.                   szODWorkString[1]=8;
  254.                   szODWorkString[2]=nOutRow;
  255.                   szODWorkString[3]=nLeft+nOutColumn;
  256.                   od_disp(szODWorkString,4,FALSE);
  257.                }
  258.                else
  259.                {
  260.                   /* Otherwise, advance cursor appropriate number of      */
  261.                   /* characters using the ANSI cursor position command.   */
  262.                   sprintf(szODWorkString,"x[%d;%dH",nOutRow,nLeft + nOutColumn);
  263.                   szODWorkString[0]=27;
  264.                   od_disp(szODWorkString,strlen(szODWorkString),FALSE);
  265.                }
  266.             }
  267.  
  268.             /* Output text for the number of characters found to be */
  269.             /* different.                                           */
  270.             pchTest=(char *)&pchMemBlock[nOutColumn*2];
  271.             for(;nOutColumn<=nCheckColumn;++nOutColumn)
  272.             {
  273.                if(pchTest[1] != nOutColour)
  274.                {
  275.                   od_set_attrib(nOutColour=pchTest[1]);
  276.                }
  277.                od_disp(pchTest,1,FALSE);
  278.                pchTest++;
  279.                pchTest++;
  280.             }
  281.          }
  282. next_line:
  283.          ;
  284.       }
  285.  
  286.       /* If not disabled, update cursor position. */
  287.       if(bScrollAction)
  288.       {
  289.          od_set_cursor(ODTextInfo.cury,ODTextInfo.curx);
  290.       }
  291.  
  292.       /* Deallocate temporary buffer. */
  293.       free(pBuffer);
  294.    }
  295.  
  296.    /* Restore the original display attribute. */
  297.    od_set_attrib(ODTextInfo.attribute);
  298.  
  299.    /* Return with success. */
  300.    OD_API_EXIT();
  301.    return(TRUE);
  302. }
  303.  
  304.  
  305. /* ----------------------------------------------------------------------------
  306.  * od_gettext()
  307.  *
  308.  * Retrieves text from the screen (based on what is current displayed on the
  309.  * local display), storing it in the buffer provided by the caller.
  310.  *
  311.  * Parameters: nLeft   - Column number of left edge of block of text to
  312.  *                       transfer, where 1 is the leftmost column of the
  313.  *                       screen.
  314.  *
  315.  *             nTop    - Row number of the top edge of block of text to
  316.  *                       to transfer, where 1 is the top row of the screen.
  317.  *
  318.  *             nRight  - Column number of the right edge of block.
  319.  *
  320.  *             nBottom - Row number of bottom edge of block.
  321.  *
  322.  *             pBlock  - Pointer to buffer large enough to hold two bytes
  323.  *                       for each character in the block.
  324.  *
  325.  *     Return: TRUE on success, FALSE on failure.
  326.  */
  327. ODAPIDEF BOOL ODCALL od_gettext(INT nLeft, INT nTop, INT nRight, INT nBottom,
  328.    void *pBlock)
  329. {
  330.    BYTE btMaxRight, btMaxBottom;
  331.  
  332.    /* Log function entry if running in trace mode. */
  333.    TRACE(TRACE_API, "od_gettext()");
  334.  
  335.    /* Initialize OpenDoors if not already done. */
  336.    if(!bODInitialized) od_init();
  337.  
  338.    OD_API_ENTRY();
  339.  
  340.    ODScrnGetTextInfo(&ODTextInfo);
  341.  
  342.    btMaxRight=ODTextInfo.winright-ODTextInfo.winleft+1;
  343.    btMaxBottom=ODTextInfo.winbottom-ODTextInfo.wintop+1;
  344.    if(nLeft<1 || nTop<1 || nRight>btMaxRight || nBottom>btMaxBottom || !pBlock)
  345.    {
  346.       od_control.od_error = ERR_PARAMETER;
  347.       OD_API_EXIT();
  348.       return(FALSE);
  349.    }
  350.  
  351.    if(!od_control.user_ansi && !od_control.user_avatar)
  352.    {
  353.       od_control.od_error = ERR_NOGRAPHICS;
  354.       OD_API_EXIT();
  355.       return(FALSE);
  356.    }
  357.  
  358.    OD_API_EXIT();
  359.    return(ODScrnGetText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight, (BYTE)nBottom,
  360.       pBlock));
  361. }
  362.  
  363.  
  364. /* ----------------------------------------------------------------------------
  365.  * od_scroll()
  366.  *
  367.  * Scrolls the specified area of the screen by the specified number of
  368.  * lines, in either the up or down directions. The cursor is left at its
  369.  * original locaiton, and the display attribute is left at its original
  370.  * setting. New lines are created in the current display colour.
  371.  *
  372.  * Parameters: nLeft     - Column number of left edge of area to scroll, where
  373.  *                         1 is the leftmost column of the screen.
  374.  *
  375.  *             nTop      - Row number of the top edge of the area to scroll,
  376.  *                         where 1 is the top row of the screen.
  377.  *
  378.  *             nRight    - Column number of the right edge of area to scroll.
  379.  *
  380.  *             nBottom   - Row number of bottom edge of area to scroll.
  381.  *
  382.  *             nDistance - Number of lines to scroll the text. A value of 0
  383.  *                         has no effect on the specified area, a positive
  384.  *                         value moves the text upwards (leaving blank lines
  385.  *                         at the bottom of the specified area), while a
  386.  *                         negative value moves the text upwards. If the
  387.  *                         distance is equal to the number of lines in the
  388.  *                         area, then the entire area is cleared.
  389.  *
  390.  *             nFlags    - Bitwise-or (|) of SCROLL_* flags. SCROLL_NORMAL
  391.  *                         is the default. SCROLL_NO_CLEAR does not clear
  392.  *                         the new area at the top/bottom of the scroll
  393.  *                         region if doing so would be slower.
  394.  *
  395.  *     Return: TRUE on success, FALSE on failure.
  396.  */
  397. ODAPIDEF BOOL ODCALL od_scroll(INT nLeft, INT nTop, INT nRight, INT nBottom,
  398.    INT nDistance, WORD nFlags)
  399. {
  400.    BYTE btWidth, btHeight;
  401.    BYTE btCount;
  402.    BYTE btFirst, btLast;
  403.    char szAVTSeq[7];
  404.    void *pBlock;
  405.    char szBlank[81];
  406.    BYTE btKeepHeight;
  407.    BYTE btMaxRight;
  408.    BYTE btMaxBottom;
  409.    tODScrnTextInfo TextState;
  410.  
  411.    /* Log function entry if running in trace mode. */
  412.    TRACE(TRACE_API, "od_scroll()");
  413.  
  414.    /* Ensure that OpenDoors has been initialized before proceeding. */
  415.    if(!bODInitialized) od_init();
  416.  
  417.    OD_API_ENTRY();
  418.  
  419.    /* Get current display setting information. */
  420.    ODScrnGetTextInfo(&TextState);
  421.  
  422.    /* Determine the height and width of the area to be scrolled. */
  423.    btWidth=nRight-nLeft+1;
  424.    btHeight=nBottom-nTop+1;
  425.  
  426.    /* Determine the number of lines currently in the area that will still */
  427.    /* be visible after scrolling.                                         */
  428.    btKeepHeight=btHeight-((nDistance>=0) ? nDistance : -nDistance);
  429.  
  430.    /* Determine the maximum bottom and left coordinates of an area to be */
  431.    /* scrolled.                                                          */
  432.    btMaxRight=TextState.winright-TextState.winleft+1;
  433.    btMaxBottom=TextState.winbottom-TextState.wintop+1;
  434.  
  435.    /* Check that parameters are valid. */
  436.    if(nLeft<1 || nTop<1 || nRight>btMaxRight || nBottom>btMaxBottom ||
  437.       nLeft > nRight || nTop > nBottom)
  438.    {
  439.       od_control.od_error = ERR_PARAMETER;
  440.       OD_API_EXIT();
  441.       return(FALSE);
  442.    }
  443.  
  444.    /* Check that ANSI or AVATAR graphics mode is available. */
  445.    if(!od_control.user_ansi && !od_control.user_avatar)
  446.    {
  447.       od_control.od_error = ERR_NOGRAPHICS;
  448.       OD_API_EXIT();
  449.       return(FALSE);
  450.    }
  451.  
  452.    /* If distance to be scrolled is 0, then we are finished already and */
  453.    /* can return immediately.                                           */
  454.    if(nDistance == 0)
  455.    {
  456.       OD_API_EXIT();
  457.       return(TRUE);
  458.    }
  459.  
  460.    /* If distance is positive, then we are moving text upwards. */
  461.    if(nDistance>0)
  462.    {
  463.       /* Ensure that distance is not greater than size of scrolled area. */
  464.       if(nDistance>btHeight)
  465.       {
  466.          nDistance=btHeight;
  467.       }
  468.  
  469.       /* Calculate first and last line to be moved. */
  470.       btFirst=nBottom-(nDistance-1);
  471.       btLast=nBottom;
  472.    }
  473.  
  474.    /* If distance is negative, then we are moving text downwards. */
  475.    else /* if(nDistance<0) */
  476.    {
  477.       /* Ensure that distance is not greater than size of scrolled area. */
  478.       if(nDistance<-btHeight)
  479.       {
  480.          nDistance=-btHeight;
  481.       }
  482.  
  483.       /* Calculate first and last line to be moved. */
  484.       btFirst=nTop;
  485.       btLast=nTop-nDistance-1;
  486.    }
  487.  
  488.    /* If AVATAR mode is available */
  489.    if(od_control.user_avatar)
  490.    {
  491.       /* Generate AVATAR sequence which causes the remote terminal to */
  492.       /* scroll an area of its screen.                                */
  493.       szAVTSeq[0]=22;
  494.  
  495.       /* If scrolling text upwards. */
  496.       if(nDistance>0)
  497.       {
  498.          /* Specify control sequence for scrolling upwards. */
  499.          szAVTSeq[1]=10;
  500.          szAVTSeq[2]=nDistance;
  501.  
  502.          /* Move text appropriate direction on local screen. */
  503.          ODScrnCopyText((BYTE)nLeft, (BYTE)(nTop + nDistance), (BYTE)nRight,
  504.             (BYTE)nBottom, (BYTE)nLeft, (BYTE)nTop);
  505.       }
  506.       /* If scrolling text downwards. */
  507.       else /* if(disatnce<0) */
  508.       {
  509.          /* Specify control sequence for scrolling downwards. */
  510.          szAVTSeq[1]=11;
  511.          szAVTSeq[2]=-nDistance;
  512.  
  513.          /* Move text appropriate direction on local screen. */
  514.          ODScrnCopyText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight,
  515.             (BYTE)(nBottom + nDistance), (BYTE)nLeft, (BYTE)(nTop - nDistance));
  516.       }
  517.  
  518.       /* Specify area to be scrolled to the AVATAR terminal. */
  519.       szAVTSeq[3]=nTop;
  520.       szAVTSeq[4]=nLeft;
  521.       szAVTSeq[5]=nBottom;
  522.       szAVTSeq[6]=nRight;
  523.  
  524.       /* Send the control sequence to the AVATAR terminal. */
  525.       od_disp(szAVTSeq,7,FALSE);
  526.  
  527.       /* Generate string containing a blank line of text. */
  528.       for(btCount=0;btCount<btWidth;++btCount) szBlank[btCount]=' ';
  529.       szBlank[btCount]='\0';
  530.  
  531.       /* Blank-out lines that will no longer be visiable. */
  532.       for(;btFirst<=btLast;++btFirst)
  533.       {
  534.          ODScrnSetCursorPos((BYTE)nLeft, btFirst);
  535.          ODScrnDisplayString(szBlank);
  536.       }
  537.  
  538.       /* Reset cursor position on local display. */
  539.       ODScrnSetCursorPos(TextState.curx,TextState.cury);
  540.    }
  541.  
  542.    /* Otherwise, we are using ANSI mode. */
  543.    else /* if(od_control.user_ansi) */
  544.    {
  545.       /* If any of the original text will still be available after */
  546.       /* scrolling.                                                */
  547.       if(btKeepHeight>0)
  548.       {
  549.          /* Allocate some temporary memory to hold text to be "got". */
  550.          if((pBlock=malloc(btKeepHeight*btWidth*2))==NULL)
  551.          {
  552.             /* If memory allocation failed, then scrolling fails. */
  553.             od_control.od_error = ERR_MEMORY;
  554.             OD_API_EXIT();
  555.             return(FALSE);
  556.          }
  557.  
  558.          /* If we are scrolling text upwards. */
  559.          if(nDistance > 0)
  560.          {
  561.             /* Move text that will still be visible, using od_gettext() */
  562.             /* and od_puttext().                                        */
  563.             od_gettext(nLeft,nTop+nDistance,nRight,nBottom,pBlock);
  564.             bScrollAction=FALSE;
  565.             od_puttext(nLeft,nTop,nRight,nBottom-nDistance,pBlock);
  566.             bScrollAction=TRUE;
  567.          }
  568.  
  569.          /* If we are scrolling text downwards. */
  570.          else /* if(nDistance < 0) */
  571.          {
  572.             /* Move text that will still be visible, using od_gettext() */
  573.             /* and od_puttext().                                        */
  574.             od_gettext(nLeft,nTop,nRight,nBottom+nDistance,pBlock);
  575.             bScrollAction=FALSE;
  576.             od_puttext(nLeft,nTop-nDistance,nRight,nBottom,pBlock);
  577.             bScrollAction=TRUE;
  578.          }
  579.  
  580.          /* Deallocate temporary memory block. */
  581.          free(pBlock);
  582.       }
  583.  
  584.       /* If new area clearing has not been disabled. */
  585.       if(!(nFlags&SCROLL_NO_CLEAR))
  586.       {
  587.          /* Loop for lines that should be blank. */
  588.          for(;btFirst<=btLast;++btFirst)
  589.          {
  590.             /* Move cursor to the beginning of this line. */
  591.             od_set_cursor(btFirst,nLeft);
  592.  
  593.             /* If right boarder of area to be scrolled is the edge of the */
  594.             /* screen, then we can use a quick control sequence to clear  */
  595.             /* the rest of the line. Call od_clr_line() to do this.       */
  596.             if(nRight == 80)
  597.             {
  598.                od_clr_line();
  599.             }
  600.  
  601.             /* If right boarder of area to be scrolled is not at the edge */
  602.             /* of the screen, then each line must be manually erased, by  */
  603.             /* sending the appropriate number of blanks (spaces).         */
  604.             else /* if(right != 80) */
  605.             {
  606.                od_repeat(' ',btWidth);
  607.             }
  608.          }
  609.       }
  610.  
  611.       /* Reset the cursor to its original position. */
  612.       od_set_cursor(TextState.cury,TextState.curx);
  613.    }
  614.  
  615.    /* Return with success */
  616.    OD_API_EXIT();
  617.    return(TRUE);
  618. }
  619.  
  620.  
  621. /* ----------------------------------------------------------------------------
  622.  * od_save_screen()
  623.  *
  624.  * Stores the contents of the entire screen into a buffer, along with
  625.  * the current cursor location and display colour. Supports all display
  626.  * modes.
  627.  *
  628.  * Parameters: pBuffer - Pointer to a buffer of at least 4004 bytes in size,
  629.  *                       where the information on the current screen state
  630.  *                       will be stored.
  631.  *
  632.  *     Return: TRUE on success, FALSE on failure.
  633.  */
  634. ODAPIDEF BOOL ODCALL od_save_screen(void *pBuffer)
  635. {
  636.    char btHeight;
  637.    tODScrnTextInfo TextState;
  638.  
  639.    /* Log function entry if running in trace mode. */
  640.    TRACE(TRACE_API, "od_save_screen()");
  641.  
  642.    /* Ensure that OpenDoors is initialized before proceeding. */
  643.    if(!bODInitialized) od_init();
  644.  
  645.    OD_API_ENTRY();
  646.  
  647.    /* Check parameters and current output window size. */
  648.    ODScrnGetTextInfo(&TextState);
  649.    if(TextState.winleft!=1 || TextState.winright!=80 || !pBuffer)
  650.    {
  651.       od_control.od_error = ERR_PARAMETER;
  652.       OD_API_EXIT();
  653.       return(FALSE);
  654.    }
  655.  
  656.    /* Store current cursor location in buffer. */
  657.    ((char *)pBuffer)[0]=TextState.curx;
  658.    ((char *)pBuffer)[1]=TextState.cury;
  659.  
  660.    /* Store current display colour in buffer. */
  661.    ((char *)pBuffer)[2]=TextState.attribute;
  662.  
  663.    /* Store height of buffer stored. */
  664.    ((char *)pBuffer)[3]=btHeight=TextState.winbottom-TextState.wintop+1;
  665.  
  666.    /* Store screen contents using local screen gettext() function. */
  667.    OD_API_EXIT();
  668.    return(ODScrnGetText(1,1,80,btHeight,(char *)pBuffer+4));
  669. }
  670.  
  671.  
  672. /* ----------------------------------------------------------------------------
  673.  * od_restore_screen()
  674.  *
  675.  * Restores the screen contents, along with the current text colour and cursor
  676.  * location, as previously stored by od_save_screen().
  677.  *
  678.  * Parameters: pBuffer - Pointer to buffer which was filled by a previous call
  679.  *                       to od_save_screen().
  680.  *
  681.  *     Return: TRUE on success, FALSE on failure.
  682.  */
  683. ODAPIDEF BOOL ODCALL od_restore_screen(void *pBuffer)
  684. {
  685.    char *pch;
  686.    char btPos;
  687.    char chLast;
  688.    char *pchTextBuffer;
  689.    char btHeight;
  690.    int nToReturn=TRUE;
  691.    tODScrnTextInfo TextState;
  692.    char btLine;
  693.    char btDistance=0;
  694.    char btCursorRow;
  695.  
  696.    /* Log function entry if running in trace mode. */
  697.    TRACE(TRACE_API, "od_restore_screen()");
  698.  
  699.    /* Ensure that OpenDoors is initialized before proceeding. */
  700.    if(!bODInitialized) od_init();
  701.  
  702.    OD_API_ENTRY();
  703.  
  704.    /* Check parameters and current output window size. */
  705.    ODScrnGetTextInfo(&TextState);
  706.    if(TextState.winleft!=1 || TextState.winright!=80 || !pBuffer)
  707.    {
  708.       od_control.od_error = ERR_PARAMETER;
  709.       OD_API_EXIT();
  710.       return(FALSE);
  711.    }
  712.  
  713.    /* Determine current window height were text will be restored. */
  714.    btHeight=TextState.winbottom-TextState.wintop+1;
  715.  
  716.    /* If current display window height is too small for entire buffer */
  717.    /* to be re-displayed.                                             */
  718.    if(btHeight<((char *)pBuffer)[3])
  719.    {
  720.       /* Then we will always display as much of the END of the buffer */
  721.       /* as possible.                                                 */
  722.       btDistance = btHeight - ((char *)pBuffer)[3];
  723.    }
  724.    else if(btHeight > ((char *)pBuffer)[3])
  725.    {
  726.       /* Otherwise, ensure that we don't try to display more lines that */
  727.       /* are in the buffer.                                             */
  728.       btHeight=((char *)pBuffer)[3];
  729.    }
  730.  
  731.    /* Clear the remote and local screens. */
  732.    od_clr_scr();
  733.  
  734.    /* If ANSI or AVATAR modes are available. */
  735.    if(od_control.user_avatar || od_control.user_ansi)
  736.    {
  737.       /* Then we can efficiently display the buffer using od_puttext(). */
  738.       bScrollAction=FALSE;
  739.       nToReturn=od_puttext(1,1,80,btHeight,(char *)pBuffer+(4+((int)btDistance*160)));
  740.       bScrollAction=TRUE;
  741.  
  742.       /* Restore original cursor location. */
  743.       od_set_cursor(((char *)pBuffer)[1],((char *)pBuffer)[0]);
  744.  
  745.       /* Restore original display attribute. */
  746.       od_set_attrib(((char *)pBuffer)[2]);
  747.    }
  748.  
  749.    /* If we are operating in ASCII mode. */
  750.    else /* if (!od_control.od_avatar && !od_control.caller_ansi) */
  751.    {
  752.       /* Then the buffer is displayed one line at a time, beginning  */
  753.       /* at the top of the screen, up to the saved cusrsor location. */
  754.  
  755.       /* Set pointer to beginning of buffer to be displayed. */
  756.       pchTextBuffer=(char *)pBuffer+4;
  757.  
  758.       /* Get final cursor row number. */
  759.       btCursorRow=((char *)pBuffer)[1];
  760.  
  761.       /* Loop for each line in the buffer. */
  762.       for(btLine=1;btLine<=btHeight;++btLine)
  763.       {
  764.          /* Set pointer to last character of line. */
  765.          pch=(char *)pchTextBuffer+158;
  766.  
  767.          /* Loop backwards until a non-blank character is found, or we */
  768.          /* reach the beginning of the line.                           */
  769.          for(chLast=80;chLast>1;)
  770.          {
  771.             /* If this is a blank character. */
  772.             if(*pch==32 || *pch==0)
  773.             {
  774.                /* Move to previous character. */
  775.                --chLast;
  776.                pch-=2;
  777.             }
  778.  
  779.             /* If this is not a blank character, then stop looping. */
  780.             else
  781.             {
  782.                break;
  783.             }
  784.          }
  785.  
  786.          /* If this is the line on which the cursor resides. */
  787.          if(btLine==btCursorRow)
  788.          {
  789.             /* If last non-blank character of line is at or past the final */
  790.             /* cursor location, then we backup the last character to be    */
  791.             /* displayed to the cursor before the final cursor position.   */
  792.             /* This code could be improved to be able to display text on   */
  793.             /* the entire cursor line by displaying the entire line,       */
  794.             /* sending a C/R, and redisplaying first portion of line to    */
  795.             /* end up with the cursor in the desired position.             */
  796.             if(chLast>=((char *)pBuffer)[0])
  797.             {
  798.                chLast=((char *)pBuffer)[0]-1;
  799.             }
  800.          }
  801.  
  802.          /* Display all characters on this line */
  803.          pch = (char *)pchTextBuffer;
  804.          for(btPos=1;btPos<=chLast;++btPos)
  805.          {
  806.             od_putch(*pch);
  807.             pch+=2;
  808.          }
  809.  
  810.          /* If this is the row where the cursor should be left, then we */
  811.          /* stop displaying now.                                        */
  812.          if(btLine==btCursorRow)
  813.          {
  814.             break;
  815.          }
  816.  
  817.          /* If cursor hasn't been wrapped, then we should send a C/R - */
  818.          /* L/F sequence.                                              */
  819.          if(chLast != 80)
  820.          {
  821.             od_disp_str("\n\r");
  822.             pchTextBuffer+=160;
  823.          }
  824.       }
  825.    }
  826.  
  827.    /* Return with the appropriate success/failure status. */
  828.    OD_API_EXIT();
  829.    return(nToReturn);
  830. }
  831.