/*==========================================================+
|   ===========================                             |
|    TORCH XXX : 1ST WORD PLUS                              |
|   ===========================                             |
|                                                           |
|    E D I T   W I N D O W   M A N A G E R                  |
|                                                           |
|    8888.1 GST 016 rev 0.01                                |
|                                                           |
+===========================================================+
|                                                           |
|    Revision History:                                      |
|                                                           |
|    0.01 Created on 15/4/86, by MCD, from WINDO101.C       |
|                                                           |
+==========================================================*/

#include "opentop.h"
#include "winhead.h"

#page
 
/* ----- library routines ----- */
 
extern strlen();
extern sign();
extern abs();
extern calloc();
 
extern at();
extern cursen();
extern scrchar();
extern scrstring();
extern setscreen();
extern cls();
extern pan();
extern scroll();


/* more display file routines */

extern dfofftop();
extern dfoffbot();
extern wrouttop();
extern wroutbot();
extern hookfill();


#page

/* ----- whole-window routines ----- */
 
wminit ( wcb, width, height )
     WORD * wcb, width, height;
{
     BYTE *p; 
     LONG *win = calloc ( WINDSZ, 4 );
     if ( win == 0 ) return 0;

     if ( pginit(win) == 0 ) return 0;

     p =  calloc(BUFBYTES,1);
     if ( p == 0 ) return 0; 

     win[WCB] = wcb;
     win[TEXTBUFF] = p;
     win[WINWIDTH]=width;
     win[WINDEPTH]=height;
 
     wmreset( win );
     redisplay ( win );
     return win;
}
 
#page

 
wmreset( win )
     LONG win[];
{
     LONG i, *p;
     WORD *wcb = win[WCB];
     BYTE *cursline, *cursattr;
     LONG botcount = win[BOTCOUNT];
     LONG wpmode=win[WPMODE], wpever=win[WPEVER];

     for ( i=WMRESET; i<PAGEDATA; i++ ) 
          win[i]=0; 

     win[WPMODE]=wpmode;
     win[WPEVER]=wpever;

     win[BOTLINE]= -1;

     win[CHARWID] = wcb[CH_WIDTH];
     win[LINEHEIGHT] = wcb[CH_HEIGHT];
     win[CURSLINE] = cursline = lines(win, 0);
     win[CURSATTR] = cursattr = attrs(win, 0);
     cursline[0] = NL;
     win[WINTOPATR] = cursattr[0] = 0;
     win[WINDUNDER] = -win[WINDEPTH];
     win[WINSPARE] = BUFLINES;
     win[CURSENABLED] = YES;
     win[OLDXPOS] = -1;
 
     /* initialise the main 8 markers */
     i=MAXMARKS; p = &win[MARKS];
     while ( i-- )
     {    *p=p+1;p++;
          *p++=0;
          *p++=0;
     }
 
     if(win[WINHOOK])
     {
          win[BOTCOUNT] = botcount;
          hookfill(win);
     }

     if ( win[BOTLINE] < 0 )
     {
          ++win[BOTLINE]; --win[WINSPARE]; ++win[WINDUNDER];
          pgaddlin(win);
     }

     pgfixall(win);
     fixcursor(win);
     return 0;
}


wmclose ( win )
     LONG win[];
{
     free ( win[TEXTBUFF] );
     if ( win[PAGELIST] ) free ( win[PAGELIST] );
     free_pic ( win );
     free ( win );
}


wmsize ( win, width, height )
     LONG win[], width, height;
{
     LONG diff = height-win[WINDEPTH];

     /* do nothing if size has not changed */
     if ( diff==0 && width==win[WINWIDTH] ) return;
 
     win[WINWIDTH] = width;

     if ( diff > 0 )
          while ( win[WINDUNDER] < diff )
               if ( dfoffbot(win) != 0 )
                    break;

     win[WINDUNDER] -= diff;
     win[WINDEPTH] = height;

}




wmenable ( win, flag )
     LONG win[];
     FLAG flag;
{
     if(flag)
     {    if(win[WINDISABLED]) --win[WINDISABLED];
          if(win[WINDISABLED]==0) wmupdate(win);
     }
     else
          ++win[WINDISABLED];
}

 
redisplay(win)
     LONG win[];
{
     win[CHANGEALL] = YES;
     wmupdate(win);
     return 0;
}



/* clear a partial line at the bottom of the window */
clearstrip(win)
     LONG win[];
{ 
     wmcursor ( win, win[WINDXPOS], win[WINDLNO]+win[WINDEPTH]-1, OFF );
     wcls ( win, PART_BOT );
}



#page 
/* PAINTCHARS
**
** This routine writes some chars from a line to the screen.
** It collects groups of chars with similar attributes in a buffer
** when is then sent to the screen in routine FLUSHBUF
*/
 
paintchars ( win, line )
        LONG win[], line;
{
        BYTE *ap = attrs(win, line);
        BYTE *cp = lines(win, line);
        BYTE linebuff[BUFLEN];
        WORD bpos = 0;
        WORD posonline = win[WINDXPOS];
        BYTE currentatr = 0x80;
        WORD w = win[WINWIDTH];
        WORD ch;

        if ( NOT enabled(win,line) ) return;

        if ( posonline >= (strlen(cp)-1) || line > win[BOTLINE] )
        {       blankline ( win, line );
                show_pic ( win, line );
                return;
        }

        cp += posonline; ap += posonline;

        wmcursor ( win, posonline, line, OFF );


        while ( w-- )
        {
                if ( currentatr != *ap )
                {       
                        flushbuf ( win, linebuff, &bpos );

                        /* avoid garbage on screen when switching to italic */
                        if ( *ap & ~currentatr & (1<<STYL_ITALIC) )
                        {
                                setscreen ( win[WCB], *ap & ~(1<<STYL_ITALIC) );
                                scrchar ( win[WCB], SPACE );
                                wmcursor ( win, posonline, line, OFF );
                        }

                        setscreen ( win[WCB], *ap );
                        currentatr = *ap;
                }

                ch = *cp++;

                if ( ch == NL )    /* remove trailing spaces */
                {       while ( bpos && linebuff[bpos-1] == SPACE )
                             --bpos;
                        break;
                }

                if ( ch < SPACE && ch > ESC ) ch = SPACE;

                ++posonline;
                linebuff[bpos++] = ch;
                ++ap;
        }

        flushbuf ( win, linebuff, &bpos );

        /* if the line ends in italic, don't chop the last char */
        if ( currentatr & (1<<STYL_ITALIC) )
                scrchar ( win[WCB], SPACE );

        cls ( win[WCB], PART_EOL );

        show_pic ( win, line );
}


static flushbuf ( win, buff, bufpos )
     LONG win[];
     BYTE buff[]; WORD *bufpos;
{
        if ( *bufpos )
        {       buff[*bufpos] = 0;
                scrstring ( win[WCB], buff );
        }

        *bufpos = 0;
}


#page
/* ----- line-based routines ----- */
 
wmdellin(win)
     LONG win[];
{
     LONG i, status, *p=win[MARKS],
          y=win[CURSLNO],
          attr=0;
     BYTE *cp, *ap;

     for ( cp=win[CURSLINE], ap=win[CURSATTR]; *cp != 0; ++cp, ++ap )
          attr |= *ap;
 
     status = scrollup(win, win[CURSLNO]);
     if(status) return(status);

     if ( win[CURSLNO] == y )
     {
          ap = win[CURSATTR];
          ap[0] |= ( attr & CHANGEMASK );
     }

     while ( p )
     {
          if ( p[MARKYPOS] > win[CURSLNO] )
               -- p[MARKYPOS];
          else if ( p[MARKYPOS] == win[CURSLNO] )
               p[MARKXPOS] = 0;

          p = p[MARKNEXT];
     }

     return 0;
}
 
 
 
 
wmjoin(win)
     LONG win[];
{    
     LONG i, status, attr, *p=win[MARKS];
     BYTE *p0, *p1, *p2;
     BYTE *cursattr;
     LONG lineno, len1, len2;
 
     /* check that the next line is in memory */
 
     if ( win[CURSLNO]==win[BOTLINE] && win[WINDUNDER] <= 0 )
     {    status = dfoffbot(win);
          if(status) return(status);
     }
 
     /* set up some useful pointers */
 
     lineno = win[CURSLNO]+1;
     cursattr = win[CURSATTR];
     p1 = p0 = win[CURSLINE];
     p2 = lines(win, lineno);
 
     /* check that the line will not be too long */
 
     len1 = strlen(p1);
     len2 = strlen(p2);
 
     if ( (len1 + len2) > MAXLEN )
          return ( ER_LL );
 
     /* copy the next line into the cursor line */

     attr = cursattr[len1-1]; /* attributes of newline char */

     p1 += len1-1;
     fastcopy ( p2, p1, len2+1 );    /* copy text & terminating null  */
     fastcopy ( p2+BUFLEN, p1+BUFLEN, len2 ); /* copy attributes */
 
     /* display line and cursor */
 
     wmputcur ( win, len1-1, win[CURSLNO] );
     cursattr[win[CURSPOS]] |= ( attr & CHANGEMASK );
     /* KEEP ANY CHANGE BITS FROM END OF LINE */

     paintline ( win, win[CURSLNO] );
     fixcursor( win );

     /* scroll up rest of screen */
 
     status = scrollup(win, win[CURSLNO]+1);
     if(status) return(status);
 
     /* update markers */
 
     while ( p )
     {    if ( p[MARKYPOS] >= lineno )
          {    if ( p[MARKYPOS] == lineno )
                    p[MARKXPOS] += win[CURSPOS];
               -- p[MARKYPOS];
          }
          p = p[MARKNEXT];
     }
 
     return 0;
}
 
 
wminslin ( win, text )
     LONG win[];
     BYTE *text;
{
     BYTE *cursattr = win[CURSATTR];
     WORD colour = cursattr[0];
     BYTE stylbuf[BUFLEN];

     fastfill ( colour&ATTRMASK, stylbuf, BUFLEN );

     wmlinestyl ( win, text, stylbuf );
} 
 


wmlinestyl ( win, text, style )
     LONG win[];
     BYTE *text, *style;
{
     BYTE *cursattr,
          *cursline,
          *lineattr;

     LONG status, i, *p=win[MARKS],
          lineno = win[CURSLNO],
          len = strlen ( text );
 
     status = scrolldown(win, win[CURSLNO]);         /* make room for a new line */
     if(status) return status;

     cursline = win[CURSLINE];
     cursattr = win[CURSATTR];

     fastcopy ( text, cursline, len+1 ); /* +1 to get terminatng null */
     fastcopy ( style, cursattr, len );
     lineattr = attrs( win, (win[CURSLNO]+1) );


     wmputcur ( win, 0, win[CURSLNO] );

     paintline ( win, win[CURSLNO] );
     fixcursor( win );
     

     /* update markers */
 
     while ( p )
     {    if ( p[MARKYPOS] >= lineno )
               ++ p[MARKYPOS];

          p = p[MARKNEXT];
     }
 
     return 0;
}
 
#page
/*   SCROLLUP
** This routine is called when deleting a line
** to scroll up the rest of the window.
*/
 
scrollup( win, scrolineno )
     LONG win[], scrolineno;   
{
     LONG lineno, status=0;
     BYTE *linebuff, *nextbuff;
     BYTE *cursline;

     if ( win[WINDUNDER] <= 0 )
          status = dfoffbot(win);

 
     for ( lineno = scrolineno; lineno < win[BOTLINE]; ++lineno )
     {
          linebuff = lines(win, lineno);
          nextbuff = lines(win, (lineno+1));
          fastcopy ( nextbuff, linebuff, BUFLEN*2 );
     }



     if ( scrolineno == win[BOTLINE]         /* on bottom line in buffer */ 
          && win[WINDUNDER] <= 0             /* if no more lines ( so bottom of file) */
          && scrolineno == win[CURSLNO] )    /* delete line (not join) */
     {    
          /* bottom line in the buffer */

          cursline = win[CURSLINE];
          cursline[0] = NL; cursline[1] = 0;
          blankline ( win, win[CURSLNO] );
          win[CURSPOS] = 0;
          fixcursor(win);
          return 0;
     }
 
     ++win[WINSPARE];
     --win[WINDUNDER];
     --win[BOTLINE];
 
     pgdellin ( win, scrolineno );
     scrollpart ( win, scrolineno, -win[LINEHEIGHT] );

     lineno = win[WINDLNO]+win[WINDEPTH]-1;
     wmcursor ( win, win[WINDXPOS], lineno, OFF );
     if ( offscreen ( win[WCB] ) )
          redisplay(win);
     else if ( status == 0 )
          paintline ( win, lineno );

     if ( scrolineno == win[CURSLNO] )
     {
          if ( win[CURSLNO] > win[BOTLINE] ) --win[CURSLNO];
          wmputcur ( win, 0, win[CURSLNO] );
     }

     fixcursor(win);

     if(status==EOF) return 0;
     return status;
}
 
#page
/*   SCRODOWN
** This routine is called when inserting a line
** to scroll down the rest of the window.
*/
 
scrolldown(win, scrolineno)
     LONG win[], scrolineno;    /* no of first line to scroll down */ 
{
     LONG lineno,status=0;
     BYTE *linebuff, *nextbuff;
 
     if ( win[WINSPARE] == 0 )
     {
          if(win[WINDOVER])
               status = wrouttop(win);
          else
               status = wroutbot(win);
          if(status) return status;
     }
 
     for (lineno = win[BOTLINE]; lineno >= scrolineno; --lineno )
     {
          linebuff = lines(win, lineno);
          nextbuff = lines(win, (lineno+1));
          fastcopy ( linebuff, nextbuff, BUFLEN*2 );
     }
 
     --win[WINSPARE];
     ++win[WINDUNDER];
     ++win[BOTLINE];

     linebuff = lines(win, scrolineno);
     *linebuff=NL; *++linebuff=0;
 
     pginslin ( win, win[CURSLNO] );
     /* note that we do not use scrolineno in the above call.
     ** this means a split on the last line of a page puts the
     ** new line on the same page, rather than the next page!
     */

     scrollpart ( win, scrolineno, win[LINEHEIGHT] );
 
     return status;
}
 
#page
 
wmsplit(win)
     LONG win[];
{
     LONG pos = win[CURSPOS],
         len,
         status=0,
         xpos = win[WINDXPOS],
          *p = win[MARKS],
         i;
     BYTE 
          *cursline = win[CURSLINE],
          *cursattr = win[CURSATTR],
          *oldline=cursline,
          *oldattr=cursattr;

     len = strlen ( cursline );
     status = scrolldown(win, win[CURSLNO]+1);       /* make room for a line */
     if(status) return status;

     wcls ( win, PART_EOL );             /* clear end of line */
     status = wmputcur ( win, 0, win[CURSLNO]+1 ); /* move cursor down to next line */
     if ( status ) return status;

     /* copy right hand end down to next line */

     cursline = win[CURSLINE];
     cursattr = win[CURSATTR];
     fastcopy ( oldline+pos, cursline, len-pos+1 );
     fastcopy ( oldattr+pos, cursattr, len-pos+1 );

     oldline[pos]=NL; oldline[pos+1]=0;    /* terminate the line */
     oldattr[pos] = win[NEWSTYLE];

     cursattr[0] &= ATTRMASK; /* remove change bits (now on NL) */
     if ( xpos ) paintline ( win, win[CURSLNO]-1 );
     if ( *cursline != NL ) paintline ( win, win[CURSLNO] );   /* display new cursor line */
     fixcursor(win);

     while ( p )
     {
          if ( p[MARKYPOS] > win[CURSLNO]-1 )
               ++p[MARKYPOS];
          else if ( p[MARKYPOS] == win[CURSLNO]-1 && p[MARKXPOS] > pos )
               ++p[MARKYPOS], p[MARKXPOS] -= pos;

          p = p[MARKNEXT];
     }

     return status;
     
}
 
 
 
 
#page
/* ----- char-based routines ----- */
 
 
wmrdbyt ( win, dx )
     LONG win[], dx;
{
     BYTE *cursline;

     if(dx) 
     {    wcursen ( win, OFF );
          moveit(win, dx);
     }

     if ( win[CURSPOS] < win[WINDXPOS] 
          || win[CURSPOS] >= win[WINDXPOS]+win[WINWIDTH] )
          wmputcur ( win, win[CURSPOS], win[CURSLNO] );
     else if(dx) 
          fixcursor(win);

     cursline = win[CURSLINE];
     return cursline[win[CURSPOS]];
}
 
 
wmwrbyt( win, ch )
     LONG win[];
     BYTE ch;
{
     BYTE *cursline = win[CURSLINE],
          *cursattr = win[CURSATTR];

     if ( ch == NL ) return(ERR);
 
     if ( cursline[win[CURSPOS]] == NL )
     {
          if ( win[CURSPOS] >= MAXLEN )
               return(ER_LL);
 
          return wminschr(win,ch);
     }
     cursline[win[CURSPOS]] = ch;

     setscreen ( win[WCB], cursattr[win[CURSPOS]] );
     wscrchar ( win, ch );
     fixcursor( win );
 
     return 0;
}
 
 
 
 
wminschr ( win, ch ) 
     LONG win[];
     BYTE ch;
{
     BYTE *cursline = win[CURSLINE],
          *cursattr = win[CURSATTR];
     LONG 
          len = strlen ( cursline ),
          pos = win[CURSPOS],
          stublen = len-pos+1,
          *p = win[MARKS],
          i;

     if ( len+1 >= MAXLEN ) return ER_LL;
 
     backshuffle ( cursline+len, 1, stublen );
     backshuffle ( cursattr+len, 1, stublen );
 
     wpan ( win, win[CHARWID], PART_EOL );
 
     cursline[pos] = ch;
     cursattr[pos] = win[NEWSTYLE];
     showchar ( win, pos );

     if ( cursattr[pos-1] & (1<<STYL_ITALIC) ) showchar ( win, pos-1 );


     cursattr[++win[CURSPOS]] &= ATTRMASK;   /* remove change bits */

     if ( win[CURSPOS] >= win[WINDXPOS]+win[WINWIDTH] )   /* check for h-scroll */
          wmputcur ( win, win[CURSPOS], win[CURSLNO] );

     fixcursor(win);


     /* update markers */

     while ( p )
     {
          if ( p[MARKYPOS] == win[CURSLNO] )
               if ( p[MARKXPOS] > pos )
                    ++p[MARKXPOS];

          p = p[MARKNEXT];
     }
     return 0;
}     


wminsblk ( win, ptr, count )
     LONG win[];
     BYTE *ptr; int count;
{
     LONG i,status=0;
     WORD savestyle = win[NEWSTYLE];


     while(status==0)
     {
          i=0;

          while ( ptr[i] != NL && ptr[i] != ESC && i < count )
               ++i;

          if ( i ) status = wminsert ( win, ptr, i );

          if ( status || i == count ) break;

          if ( ptr[i] == NL )
               status = wmsplit(win);
          else if ( ptr[i] == ESC )
               win[NEWSTYLE] = ptr[++i] & 0x7f;

          ptr += ++i; count -= i;
     }

     win[NEWSTYLE] = savestyle;
     return status;
}

 
wminsert ( win, str, slen )
     BYTE *str; LONG win[], slen;
{
     BYTE *cursline = win[CURSLINE],
          *cursattr = win[CURSATTR];
     LONG 
          llen = strlen(cursline),
          pos = win[CURSPOS],
          stublen = llen-pos+1,
          *p = win[MARKS],
          i;
 

     if ( llen+slen >= MAXLEN ) return ER_LL;
 
     backshuffle ( cursline+llen, slen, stublen );
     backshuffle ( cursattr+llen, slen, stublen );
 
     fastcopy ( str, cursline+win[CURSPOS], slen );
     fastfill ( win[NEWSTYLE], cursattr+win[CURSPOS], slen );

     /* note inserted string gets new attributes */

     win[CURSPOS]+=slen;
     if ( win[CURSPOS] < win[WINDXPOS]+win[WINWIDTH] )
          paintline ( win, win[CURSLNO] ); /* else repainted by h-scrol */
     wmputcur ( win, win[CURSPOS], win[CURSLNO] );

     /* update markers */

     while ( p )
     {
          if ( p[MARKYPOS] == win[CURSLNO] )
               if ( p[MARKXPOS] > pos )
                    p[MARKXPOS] += slen;

          p = p[MARKNEXT];
     }
     return 0;
}     
 
 
wmdelchr(win)
     LONG win[]; 
{
     BYTE 
          *cursline = win[CURSLINE],
          *cursattr = win[CURSATTR],
          *temp;

     LONG
          len = strlen ( cursline ),
          pos = win[CURSPOS],
          stublen = len-pos,
          endposn = win[WINDXPOS]+win[WINWIDTH]-1,
          chbits,
          *p = win[MARKS],
          i;
     
 
     if ( cursline[pos] == NL ) return ERR;
 
     temp = cursline+pos;
     fastcopy ( temp+1, temp, stublen );
 
     temp = cursattr+pos;
     chbits = *temp & CHANGEMASK;
     fastcopy ( temp+1, temp, stublen );
     *temp |= chbits;    /* keep any change bits */

     wpan ( win, -win[CHARWID], PART_EOL );

 
     /* if deleting near italic, repaint char */

     if ( cursattr[pos-1] & (1<<STYL_ITALIC) )
          showchar ( win, pos-1 );


     /* now display char at end of line */
 
     wmcursor ( win, endposn, win[CURSLNO], OFF );
     if ( offscreen ( win[WCB] ) )
          paintline ( win, win[CURSLNO] );
     else if ( len > endposn+1 )
     {
          showchar( win, endposn-1 );
          showchar( win, endposn );
     }

     fixcursor( win );

     /* update markers */

     while ( p )
     {
          if ( p[MARKYPOS] == win[CURSLNO] )
               if ( p[MARKXPOS] > win[CURSPOS] )
                    --p[MARKXPOS];

          p = p[MARKNEXT];
     }

     return 0;
}     
#page
/* ----- cursor and marker routines ----- */
 
/*   WMGETBUF
** This routine is used by the editor to get a pointer
** to the buffer for the cursor line in order to do
** searches quickly
*/
 
wmgetbuf(win)
     LONG win[];
{
     return win[CURSLINE];
}
 
 
 
wmcurena ( win, onoff )
     LONG win[];
     FLAG onoff;
{
     win[CURSENABLED] = onoff;
     if ( onoff ) wmputcur ( win, win[CURSPOS], win[CURSLNO] );
     wcursen ( win, onoff );
     return 0;
}
 
 
 
wmgetcur( win, xp, yp )
     LONG win[], *xp, *yp;
{
     *xp = win[CURSPOS];
     *yp = win[CURSLNO];
 
     return 0;
}
 
 
wmputcur ( win, x, y )
     LONG win[], x, y;
{
     int target = y-win[WINDEPTH]/2;

     /* If the line we want is not in the window, scroll until
     ** the required line is in the middle of the window
     */

     /* but if we're only moving 1 line out of the window,
     ** only scroll the window by 1 line
     */

     if ( enabled(win,-2) )
     {
          if ( y == win[WINDLNO]-1 ) target=y;
          if ( y == win[WINDLNO]+win[WINDEPTH] ) target = win[WINDLNO]+1;
     }

     if ( y >= win[WINDLNO] && y < (win[WINDLNO]+win[WINDEPTH]) )
          target = win[WINDLNO];

     wmscrto ( win, x, y, target );
}


wmscrto ( win, x, y, target )
     LONG win[], x, y, target;
{
     LONG status=0, 
          oldwlno = win[WINDLNO],
          saveenable=win[CURSENABLED],
          i,
          len;

     BYTE *cursline, *cursattr;

     wcursen(win,OFF);
     win[CURSENABLED]=OFF;

     while ( win[WINDLNO] > target )
     {
          if ( (status = windup(win)) != 0 ) 
               break;
     }
 

     while ( win[WINDLNO] < target && win[WINDUNDER] >= 0 )
     {
          if ( (status = winddown(win)) != 0 ) 
               break;
     }
 
 
 
     /* Now look for the required line amongst those lines in the window */
 
     for ( i = win[WINDLNO]; i < y; ++i )
          if ( i == win[BOTLINE] ) break;
 
     win[CURSLNO] = i;
     win[CURSLINE] = cursline = lines(win, win[CURSLNO]);
     win[CURSATTR] = cursattr = attrs(win, win[CURSLNO]);
 
     len = strlen(cursline);
     if(x>(len-1))x=len-1;
     if(x<0) x=0;

     win[CURSPOS]=x;
     
 
     /* horizontal scrolling */

     if ( ( win[CURSPOS] < win[WINDXPOS] || win[CURSPOS] >= win[WINDXPOS]+win[WINWIDTH] )
        && saveenable )
     {
          if ( win[CURSPOS] < win[WINWIDTH] )
               win[WINDXPOS]=0;
          else
               win[WINDXPOS]=win[CURSPOS]-(3*win[WINWIDTH]/4);
          redisplay(win);
     }
     else
          vsscroll(win, oldwlno-win[WINDLNO]);

     win[CURSENABLED]=saveenable;
     fixcursor(win);
 
     if ( win[CURSLNO] == y ) status=0;
     else if ( status == 0 ) status = EOF;
     return status;
}


 
#page
 
/*   WINDUP and WINDDOWN
** These routines are called when scrolling the window to move
** the window up or down with respect to the display file.
*/
windup(win)
     LONG win[];
{
     int status=0;
 
     if ( win[WINDOVER] == 0 )
          status = dfofftop(win);
 
     if ( status == 0 )
     {
          --win[WINDLNO]; 
          --win[WINDOVER]; ++win[WINDUNDER];
     }
 
     return status;
}
 
 
 
winddown(win)
     LONG win[];
{
     int status=0;
 
     if ( win[WINDUNDER] <= 0 )
          status = dfoffbot(win);
 
     if ( status == 0 || status == EOF )
     {
          ++win[WINDLNO]; 
          ++win[WINDOVER]; --win[WINDUNDER];
     }
 
     return status;
}
 
 
 
#page
 
/*   MARKER ROUTINES
**
** These routines are for manipulating markers.
**
** WMMARK      sets a numbered marker at the current cursor position
**
** WMMKPOS     returns the position of a marker
**
** WMMOVEMK    moves the cursor to a numbered marker
*/
 
wmmark ( win, marker )
     LONG win[], marker;
{
     LONG *p = win[MARKS];

     while ( marker-- )
     {    p = p[MARKNEXT];
          if ( p == 0 ) return ERR;
     } 
 
     p[MARKYPOS] = win[CURSLNO];
     p[MARKXPOS] = win[CURSPOS];
 
     return 0;
}
 
 
 
wmmkpos ( win, marker, xp, yp )
     LONG win[], marker,
          *xp, *yp;
{
     LONG *p = win[MARKS];

     while ( marker-- )
     {    p = p[MARKNEXT];
          if ( p == 0 ) return ERR;
     } 
 
     *xp = p[MARKXPOS];
     *yp = p[MARKYPOS];
 
     return 0;
}
 
 
wmmovemk ( win, marker )
     LONG win[], marker;
{
     LONG *p = win[MARKS];

     while ( marker-- )
     {    p = p[MARKNEXT];
          if ( p == 0 ) return ERR;
     } 
 
     return wmputcur ( win, p[MARKXPOS], p[MARKYPOS] );
}
 

marklink ( win, markrec )
     LONG win[], markrec[];
{
     LONG i=1, *p=win[MARKS];

     /* add the marker to the end of the list, 
     ** return position in chain */
 

     while ( p[MARKNEXT] != 0 )
          p = p[MARKNEXT], i++;

     p[MARKNEXT] = markrec;
     markrec[MARKNEXT]=0;

     return i;
}
     


markunlink ( win, markrec )
     LONG win[], markrec[];
{
     LONG *p=win[MARKS];

     /* remove the marker from the list */ 
 

     while ( p[MARKNEXT] != markrec )
     {
          p = p[MARKNEXT];
          if ( p == 0 ) return ERR;
     }

     p[MARKNEXT] = markrec[MARKNEXT];

     return 0;
}

#page
 
/*   SIMPLE CURSOR MOVEMENT ROUTINES
**
** These routines can be called to move the cursor up, down,
** left or right.
*/
 
wmup(win)
     LONG win[];
{
     return wmputcur ( win, win[CURSPOS], win[CURSLNO]-1 );
}
 
 
wmdown(win)
     LONG win[];
{
     return wmputcur ( win, win[CURSPOS], win[CURSLNO]+1 );
}
 
 
wmleft(win)
     LONG win[];
{
     return wmputcur ( win, win[CURSPOS]-1, win[CURSLNO] );
}
 
 
wmright(win)
     LONG win[];
{
     return wmputcur ( win, win[CURSPOS]+1, win[CURSLNO] );
}
 
 
#page
 
/*   MOVEIT
** This routine roughly corresponds to DFMOVEAP
** in the original DFM but is of course much simpler.
**
** DX specifies the offset on the cursor position. (There is
** now no separate AP).
*/
moveit(win,dx)
     LONG win[], dx;
{
     BYTE *cursline = win[CURSLINE];

     if ( dx > 0 )
          while ( dx-- && cursline[win[CURSPOS]] != NL )
               ++win[CURSPOS];
     else
          while ( dx++ && win[CURSPOS] != 0 )
               --win[CURSPOS];

}
 
 
#page
/* ----- COLOUR CHANGE ROUTINES ----- */
 

wmgetstyle(win)
     LONG win[];
{ 
     BYTE *attrptr = win[CURSATTR];
     return attrptr[win[CURSPOS]];
}
 
 
/*   WMSCAN
**
** This routine is called to change the attributes 
** of a large area of text: eg a marked block.
** The change stops at a marker point.
** Some of the attributes can be changed without
** affecting the others, by appropriate use of the mask. 
*/ 
wmscan ( win, newattr, mask, marker )
     LONG win[];    /* which window */
     WORD newattr;  /* the new attributes bits */
     WORD mask;     /* which bits to change */
     WORD marker;   /* where to stop */
{
     BYTE *cp, *ap,
          *cursattr = win[CURSATTR];

     LONG
          num,
          endx, endy,
          lineno,
          ypos = win[CURSLNO],
          xpos = win[CURSPOS];
 
     /* do nothing if at or past end point */ 
     lineno = ypos;
     wmmkpos ( win, marker, &endx, &endy );
     if ( lineno > endy || 
          ( lineno == endy && xpos >= endx )
        ) return;

     newattr &= mask;
     mask = ~mask;

     /* outer loop for lines */
     while (1)
     {    
          cp = lines ( win, lineno ) + xpos;
          ap = attrs ( win, lineno ) + xpos;

          /* calculate no of bytes to fill in this line */
          num = BUFLEN;
          if ( lineno == endy ) num = endx;
          if ( lineno == ypos ) num -= xpos;

          /* update bytes in this line */
          while ( num-- > 0 )
          {    if ( *cp++ == 0 ) break;      /* quit at end of line */

               *ap = (*ap & mask) + newattr;
               ap++;
          }

          /* repaint & set up for next line */
          paintline ( win, lineno );
          if ( lineno == endy ) break;
          xpos = 0;
          if ( wmputcur ( win, 0, ++lineno ) != 0 ) break;
     }
}

#page
/* ---- sort of screen driver routines ---- */


showchar ( win, pos )
     LONG win[], pos;
{
     BYTE *cursattr = win[CURSATTR],
          *cursline = win[CURSLINE];

     wmcursor ( win, pos, win[CURSLNO], OFF );
     setscreen ( win[WCB], cursattr[pos] );
     wscrchar ( win, cursline[pos] );
}


wcls ( win, part )
     LONG win[], part;
{
     if ( enabled(win, -2) )
          cls ( win[WCB], part );
}


wcursen ( win, onoff )
     LONG win[], onoff;
{
     if ( enabled(win,-2) )
          cursen(win[WCB],onoff);
}



wpan ( win, distance, part )
     LONG win[], distance, part;
{
     if ( enabled(win, win[CURSLNO]) )
          pan ( win[WCB], distance, part );
}



wscrchar ( win, ch )
     LONG win[], ch;
{
     if ( ch < SPACE && ch > ESC ) ch = SPACE;

     if ( enabled(win, win[CURSLNO]) )
          scrchar( win[WCB], ch);
}

 

paintline ( win, lineno )
     LONG win[], lineno;
{
     if ( lineno >= win[WINDLNO] && lineno < win[WINDLNO]+win[WINDEPTH] )
          paintchars ( win, lineno );
}



blankline( win, line )
     LONG win[], line;
{
     wmcursor ( win, win[WINDXPOS], line, OFF );
     cls ( win[WCB], PART_LINE );
}
 
 
fixcursor(win)
     LONG win[];
{
     wmcursor ( win, win[CURSPOS], win[CURSLNO], win[CURSENABLED] );
}
 
 
wmcursor ( win, x, y, onoff )
     LONG win[], x, y;
     FLAG onoff;
{
     if ( NOT enabled(win,-2) ) return;

     if ( onoff == OFF )
          cursen ( win[WCB], OFF );
 
     at ( win[WCB], y-win[WINDLNO], x-win[WINDXPOS] );
 
     if ( onoff )
          cursen ( win[WCB], ON );
}


wmupdate(win)
     LONG win[];
{
     LONG line;
     BYTE *changed=&win[CHANGED];

     if ( NOT enabled(win, -2) ) 
          return 0;

     win[CURSENABLED]=OFF;
     wcursen(win, OFF);

     for ( line=0; line<win[WINDEPTH]; ++line )
     {    if ( win[CHANGEALL] || changed[line] )
               paintchars ( win, line+win[WINDLNO] );
          changed[line]=NO;
     }

     clearstrip(win);                   /* clear part-line gap at bottom of window */

     win[CURSENABLED]=ON;
     fixcursor(win);                    /* display cursor in correct place */

     pgdisplay(win, win[CHANGEALL]);    /* redisplay page breaks */
     show_ruler(win);                   /* redisplay ruler line */
     scvertbar(win);                    /* update verical scroll bar */
     schorizbar(win);                   /* update horiz scroll bar */

     win[CHANGEALL]=NO;

     return 0;
}





enabled( win, chline )
     LONG win[], chline;
{
     BYTE *changed = &win[CHANGED];

     if(win[WINDISABLED])
     {    if(chline == -1)
               win[CHANGEALL]=YES;
          else if ( chline >= win[WINDLNO] )
               changed[chline-win[WINDLNO]]=YES;
     }
     else 
          checktop(win[WCB]);

     return (win[WINDISABLED]==NO);
}


 
vsscroll ( win, distance )
     LONG win[],
     distance;            /* + means move text down (window up) */
{
     int firstline = 0;                 /* no of first line to repaint */
     int numlines = abs(distance);      /* work out no of lines to paint */

                       /* beware  side effect */
     if ( distance == 0 || NOT enabled(win,-1) ) return;
 
     if ( distance < 0 && numlines < win[WINDEPTH] ) 
          firstline = win[WINDEPTH] - numlines;
 
     if ( numlines < win[WINDEPTH] )
          scroll ( win[WCB], win[LINEHEIGHT] * distance, PART_ALL);
     else 
          numlines = win[WINDEPTH];
 
     wmcursor ( win, win[WINDXPOS], win[WINDLNO]+firstline, OFF );
     if ( offscreen ( win[WCB] ) )
          redisplay(win);
     else
     {
          wmrefresh ( win, firstline, numlines );
          clearstrip(win);
     }
     pgdisplay(win, NO);
     scvertbar(win);       /* update vertical scroll bar */
}


wmrefresh ( win, y, h )
     LONG win[], y, h;
{
     LONG n;

     for ( n = win[WINDLNO]+y; n < win[WINDLNO]+y+h; ++n )
          paintchars ( win, n );

     fixcursor ( win );

     return 0;
}



schorizbar(win)
     LONG win[];
{
     if ( win[WINDXPOS] != win[OLDXPOS] )
           s_hbar ( win[WCB], win[OLDXPOS]=win[WINDXPOS], win[WINWIDTH], MAXLEN );
}



scvertbar(win)
     LONG win[];
{
     LONG total = win[WINDLNO]+win[WINDEPTH]+win[BOTCOUNT];

     if ( win[WINDUNDER] > 0 ) total += win[WINDUNDER];

     s_vbar ( win[WCB], win[WINDLNO], win[WINDEPTH], total );
}


#page
scrollpart ( win, lineno, distance )
     LONG win[], lineno, distance;
{
     if ( NOT enabled(win, -1) ) return;

     cursen ( win[WCB], OFF );

     if ( lineno == win[WINDLNO] )
          scroll ( win[WCB], distance, PART_ALL );
     else
     {
          wmcursor ( win, win[WINDXPOS], lineno-1, OFF );
          scroll ( win[WCB], distance, PART_BOT );
     }

     fixcursor(win);
     pgdisplay(win, NO);
     scvertbar(win);
}


#page
fastcopy ( sce, dest, count )
     register BYTE  *sce;
     register BYTE  *dest;
     register WORD  count;
{
     if ( dest >= sce )        /* account for possible overlap */
     {
          sce += count;
          dest += count;
          while ( count-- ) *--dest = *--sce;
     }
     else
     {
          while ( count-- ) *dest++ = *sce++;
     }
}
          

fastfill ( value, pointer, count )
     register BYTE  value;
     register BYTE  *pointer;
     register WORD  count;
{
     while ( count-- ) *pointer++ = value;
}


backshuffle ( sce, offset, count )
     register BYTE  *sce; 
     WORD           offset; 
     register WORD  count;
{
     register BYTE  *dst = sce + offset;

     while ( count-- ) *dst-- = *sce--;
}


lines ( win, line )
     LONG win[], line;
{
     return win[TEXTBUFF]+(line%BUFLINES)*(2*BUFLEN);
}


attrs ( win, line )
     LONG win[], line;
{
     return win[TEXTBUFF]+BUFLEN+(line%BUFLINES)*(2*BUFLEN);
}




