/* render.c -- System-independant rendering
   Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>

   This file is part of Jade.

   Jade is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   Jade is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Jade; see the file COPYING.	If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "jade.h"
#include "jade_protos.h"

#include <assert.h>

#ifdef HAVE_AMIGA
# ifdef _DCC
#  define GfxBase_DECLARED
# endif
# include <clib/graphics_protos.h>
# include <graphics/gfxbase.h>
# include <graphics/gfxmacros.h>
extern struct GfxBase *GfxBase;
#endif

_PR void cursor(VW *, bool);
_PR void pen_to_line(VW *, long);
_PR void pen_to_pos(VW *, long, long);
_PR void pen_to_glyph_pos(VW *, long, long);
_PR void draw_bit(VW *, int, u_char *, int, long, long);
_PR void draw_line(VW *, LINE *, long);
_PR void redraw_all(VW *);
_PR void redraw_region(VW *, POS *, POS *);
_PR void redraw_lines(VW *, long, long);
_PR void redraw_lines_clr(VW *, long, long);
_PR void redraw_line_from(VW *, long, long);
_PR void redraw_rect(VW *, POS *, POS *, bool);
_PR void cut_paste_lines(VW *, long, long);
_PR void draw_message_line(VW *);
_PR void redraw_message(VW *, u_char *, int);
_PR void redraw_cmd_line(VW *, u_char *, u_char *, long, long);
_PR void redraw_cmd_line_from(VW *, u_char *, u_char *, long, long, long);
_PR void cmd_line_cursor(VW *, bool);

/* I hope this is enough!? :-| */
static u_char glyph_buf[512];

static void
pen_to_cursor(VW *vw)
{
    register int x, y;
    calc_cursor_offset(vw);
    y = vw->vw_FontStart +
	((vw->vw_CursorPos.pos_Line - vw->vw_StartLine) * vw->vw_FontY);
    x = vw->vw_XStartPix +
	((vw->vw_LastCursorOffset - vw->vw_StartCol) * vw->vw_FontX);
    if(x < vw->vw_XStartPix)
	x = vw->vw_XStartPix;
    MOVE(vw, x, y);
}

void
pen_to_line(VW *vw, long line)
{
    MOVE(vw, vw->vw_XStartPix,
	 vw->vw_FontStart + ((line - vw->vw_StartLine) * vw->vw_FontY));
}

void
pen_to_pos(VW *vw, long col, long line)
{
    TX *tx = vw->vw_Tx;
    int x, y;
    y = vw->vw_FontStart + ((line - vw->vw_StartLine) * vw->vw_FontY);
    assert(line < tx->tx_NumLines);
    x = vw->vw_XStartPix + ((glyph_col(tx, col, line) - vw->vw_StartCol)
			    * vw->vw_FontX);
    if(x < vw->vw_XStartPix)
	x = vw->vw_XStartPix;
    MOVE(vw, x, y);
}

void
pen_to_glyph_pos(VW *vw, long gcol, long line)
{
    int x, y;
    y = vw->vw_FontStart + ((line - vw->vw_StartLine) * vw->vw_FontY);
    x = vw->vw_XStartPix + ((gcol - vw->vw_StartCol) * vw->vw_FontX);
    if(x < vw->vw_XStartPix)
	x = vw->vw_XStartPix;
    MOVE(vw, x, y);
}

void
cursor(VW *vw, bool status)
{
    if((vw->vw_Flags & VWFF_SLEEPING) == 0)
    {
	if(vw->vw_Flags & VWFF_STATUS_CURS)
	{
	    PEN_X(vw) = vw->vw_XStartPix
			+ ((vw->vw_MessageLen < vw->vw_MaxX)
			   ? vw->vw_MessageLen
			   : (vw->vw_MaxX - 1)) * vw->vw_FontX;
	    PEN_Y(vw) = vw->vw_MessageFontY;
	    TEXT(vw, P_BLOCK, " ", 1);
	}
	else
	{
	    if(vw->vw_CursorPos.pos_Line < vw->vw_StartLine + vw->vw_MaxY)
	    {
		int pencol;
		LINE *line = vw->vw_Tx->tx_Lines + vw->vw_CursorPos.pos_Line;
		long cursoff = vw->vw_CursorPos.pos_Col;
		int inblk = cursor_in_block(vw);
		if((status && inblk) || (!status && !inblk))
		    pencol = P_TEXT;
		else
		    pencol = P_BLOCK;
		pen_to_cursor(vw);
		if((cursoff + 1) >= line->ln_Strlen)
		    TEXT(vw, pencol, " ", 1);
		else
		    TEXT(vw, pencol, char_glyphs(vw->vw_Tx,
						 line->ln_Line[cursoff]), 1);
	    }
	}
    }
}

/*
 * draws a section of a line of bytes, beg and end are x ordinates
 */
void
draw_bit(VW *vw, int colour, u_char *str, int slen, long beg, long end)
{
    long startx = vw->vw_StartCol;
    long endx = vw->vw_MaxX + startx + 1;
    long length;
    if(end < startx)
	return;
    if(beg < startx)
	beg = startx;
    if(end > endx)
	end = endx;
    if((length = end - beg) <= 0)
	return;
    slen -= beg;
    if(slen <= 0)
	return;
    if(slen < length)
	length = slen;
    TEXT(vw, colour, str + beg, length);
}

static void
draw_block_line(VW *vw, long blockStartCol, long blockEndCol,
		long drawEndCol, bool useBEC)
{
    int xend = ((drawEndCol - vw->vw_StartCol) * vw->vw_FontX)
	       + vw->vw_XStartPix;
    if(PEN_X(vw) < vw->vw_XEndPix)
    {
	int rectblocks = vw->vw_Flags & VWFF_RECTBLOCKS;
	if(useBEC || rectblocks)
	{
	    int xblkend = ((blockEndCol - vw->vw_StartCol)
			   * vw->vw_FontX) + vw->vw_XStartPix;
	    if(rectblocks)
	    {
		if((((PEN_X(vw) - vw->vw_XStartPix) / vw->vw_FontX)
		    + vw->vw_StartCol) < blockStartCol)
		{
		    PEN_X(vw) = ((blockStartCol - vw->vw_StartCol)
				 * vw->vw_FontX) + vw->vw_XStartPix;
		}
	    }
	    if(xblkend >= xend)
		xblkend = xend;
	    if(xblkend > PEN_X(vw))
	    {
		SET_AREA(vw, PEN_X(vw), PEN_Y(vw) - FONT_ASCENT(vw),
			 xblkend - PEN_X(vw), vw->vw_FontY);
	    }
	}
	else
	{
	    SET_AREA(vw, PEN_X(vw), PEN_Y(vw) - FONT_ASCENT(vw),
			   xend - PEN_X(vw), vw->vw_FontY);
	}
    }
}

/*
 * pen should be at start of line to draw (line should be cleared first)
 */
void
draw_line(VW *vw, LINE *line, long lineNum)
{
    long glyphs = make_glyph_array(vw->vw_Tx, line->ln_Line, 0,
				   line->ln_Strlen-1, glyph_buf,
				   vw->vw_StartCol, vw->vw_MaxX);
    if(vw->vw_BlockStatus != 0)
	TEXT(vw, P_TEXT, glyph_buf, glyphs);
    else
    {
	long block0col = glyph_col(vw->vw_Tx, vw->vw_BlockS.pos_Col,
				   vw->vw_BlockS.pos_Line);
	long block1col = glyph_col(vw->vw_Tx, vw->vw_BlockE.pos_Col,
				   vw->vw_BlockE.pos_Line);
	u_char *glyph_line = glyph_buf - vw->vw_StartCol;
	long abs_glyphs = glyphs + vw->vw_StartCol;
	if(vw->vw_Flags & VWFF_RECTBLOCKS)
	{
	    if(block0col > block1col)
	    {
		long tmp;
		tmp = block0col;
		block0col = block1col;
		block1col = tmp;
	    }
	}
	switch(line_in_block(vw, lineNum))
	{
	case 0: /* none of line in block */
	    TEXT(vw, P_TEXT, glyph_buf, glyphs);
	    break;
	case 1: /* whole of line in block */
	    TEXT(vw, P_BLOCK, glyph_buf, glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_StartCol + vw->vw_MaxX, FALSE);
	    break;
	case 2: /* start of line in block */
	    draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, 0, block1col);
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, block1col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_StartCol + vw->vw_MaxX, TRUE);
	    break;
	case 3: /* end of line in block */
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, 0, block0col);
	    draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, block0col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_StartCol + vw->vw_MaxX, FALSE);
	    break;
	case 4: /* middle of line in block */
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, 0, block0col);
	    draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, block0col,
		     block1col);
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, block1col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_StartCol + vw->vw_MaxX, TRUE);
	    break;
	}
    }
}

/*
 * pen should be at first draw position, draws from character XSTART to the
 * end of the line.
 */
static void
draw_line_part(VW *vw, LINE *line, long lineNum, long xStart,
	       long glyph_xStart)
{
    u_char *src = line->ln_Line + xStart;
    u_long srclen = (line->ln_Strlen - 1) - xStart;
    long glyphs = make_glyph_array(vw->vw_Tx, src, glyph_xStart, srclen,
				   glyph_buf, 0,
				   (vw->vw_StartCol+vw->vw_MaxX)-glyph_xStart);
    if(vw->vw_BlockStatus != 0)
	TEXT(vw, P_TEXT, glyph_buf, glyphs);
    else
    {
	long block0col = glyph_col(vw->vw_Tx, vw->vw_BlockS.pos_Col,
				   vw->vw_BlockS.pos_Line);
	long block1col = glyph_col(vw->vw_Tx, vw->vw_BlockE.pos_Col,
				   vw->vw_BlockE.pos_Line);
	u_char *glyph_line = glyph_buf - glyph_xStart;
	long abs_glyphs = glyphs + glyph_xStart;
	if(vw->vw_Flags & VWFF_RECTBLOCKS)
	{
	    if(block0col > block1col)
	    {
		long tmp;
		tmp = block0col;
		block0col = block1col;
		block1col = tmp;
	    }
	}
	switch(line_in_block(vw, lineNum))
	{
	case 0: /* none of line in block */
	    TEXT(vw, P_TEXT, glyph_buf, glyphs);
	    break;
	case 1: /* whole of line in block */
	    TEXT(vw, P_BLOCK, glyph_buf, glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_MaxX + vw->vw_StartCol, FALSE);
	    break;
	case 2: /* start of line in block */
	    if(glyph_xStart < block1col)
		draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, glyph_xStart,
			 block1col);
	    else
		block1col = glyph_xStart;
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, block1col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_MaxX + vw->vw_StartCol, TRUE);
	    break;
	case 3: /* end of line in block */
	    if(glyph_xStart < block0col)
		draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, glyph_xStart,
			 block0col);
	    else
		block0col = glyph_xStart;
	    draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, block0col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_MaxX + vw->vw_StartCol, FALSE);
	    break;
	case 4: /* middle of line in block */
	    if(glyph_xStart < block0col)
		draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, glyph_xStart,
			 block0col);
	    else
		block0col = glyph_xStart;
	    if(block0col < block1col)
		draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, block0col,
			 block1col);
	    else
		block1col = block0col;
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, block1col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col,
			    vw->vw_MaxX + vw->vw_StartCol, TRUE);
	    break;
	}
    }
}

#ifdef HAVE_X11
/*
 * pen should be at first draw position
 * The XSTART and XEND are *glyph* indexes -- the physical position on the
 * screen.
 */
static void
draw_line_glyph_length(VW *vw, LINE *line, long lineNum, long xStart, long xEnd)
{
    long glyphs;
    if(xStart < vw->vw_StartCol)
	xStart = vw->vw_StartCol;
    if(xEnd >= (vw->vw_StartCol + vw->vw_MaxX))
	xEnd = (vw->vw_StartCol + vw->vw_MaxX) - 1;
    glyphs = make_glyph_array(vw->vw_Tx, line->ln_Line, 0, line->ln_Strlen-1,
			      glyph_buf, xStart, xEnd - xStart);
    if(vw->vw_BlockStatus != 0)
	TEXT(vw, P_TEXT, glyph_buf, glyphs);
    else
    {
	long block0col = glyph_col(vw->vw_Tx, vw->vw_BlockS.pos_Col,
				   vw->vw_BlockS.pos_Line);
	long block1col = glyph_col(vw->vw_Tx, vw->vw_BlockE.pos_Col,
				   vw->vw_BlockE.pos_Line);
	u_char *glyph_line = glyph_buf - xStart;
	long abs_glyphs = glyphs + xStart;
	if(vw->vw_Flags & VWFF_RECTBLOCKS)
	{
	    if(block0col > block1col)
	    {
		long tmp;
		tmp = block0col;
		block0col = block1col;
		block1col = tmp;
	    }
	}
	switch(line_in_block(vw, lineNum))
	{
	case 0: /* none of line in block */
	    TEXT(vw, P_TEXT, glyph_buf, glyphs);
	    break;
	case 1: /* whole of line in block */
	    TEXT(vw, P_BLOCK, glyph_buf, glyphs);
	    draw_block_line(vw, block0col, block1col, xEnd, FALSE);
	    break;
	case 2: /* start of line in block */
	    if(xStart < block1col)
		draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, xStart,
			 block1col);
	    else
		block1col = xStart;
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, block1col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col, xEnd, TRUE);
	    break;
	case 3: /* end of line in block */
	    if(xStart < block0col)
		draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, xStart,
			 block0col);
	    else
		block0col = xStart;
	    draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, block0col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col, xEnd, FALSE);
	    break;
	case 4: /* middle of line in block */
	    if(xStart < block0col)
		draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, xStart,
			 block0col);
	    else
		block0col = xStart;
	    if(block0col < block1col)
		draw_bit(vw, P_BLOCK, glyph_line, abs_glyphs, block0col,
			 block1col);
	    else
		block1col = block0col;
	    draw_bit(vw, P_TEXT, glyph_line, abs_glyphs, block1col,
		     abs_glyphs);
	    draw_block_line(vw, block0col, block1col, xEnd, TRUE);
	    break;
	}
    }
}
#endif /* HAVE_X11 */

void
redraw_all(VW *vw)
{
    long linenum = vw->vw_StartLine;
    LINE *line = vw->vw_Tx->tx_Lines + linenum;
    short y = 0;
    CLR_RECT(vw, vw->vw_XStartPix, vw->vw_YStartPix,
	     vw->vw_XEndPix, vw->vw_YEndPix);
    while((y < vw->vw_MaxY) && (linenum < vw->vw_Tx->tx_NumLines))
    {
	pen_to_line(vw, linenum);
	draw_line(vw, line, linenum);
	y++;
	linenum++;
	line++;
    }
}

void
redraw_region(VW *vw, POS *start, POS *end)
{
    long linenum = start->pos_Line;
    LINE *line = vw->vw_Tx->tx_Lines + linenum;
    short y = linenum - vw->vw_StartLine;
    short yend = end->pos_Line - vw->vw_StartLine + 1;
    int yord = (y * vw->vw_FontY) + vw->vw_YStartPix;
    if(POS_EQUAL_P(start, end))
	return;
    if(yend > vw->vw_MaxY)
	yend = vw->vw_MaxY;
    if((y >= 0) && (y < yend))
    {
	long gcol = glyph_col(vw->vw_Tx, start->pos_Col, start->pos_Line);
	long tmp = gcol - vw->vw_StartCol;
	if(tmp < 0)
	{
	    gcol = vw->vw_StartCol;
	    start->pos_Col = char_col(vw->vw_Tx, gcol, start->pos_Line);
	    tmp = 0;
	}
	CLR_RECT(vw, (tmp * vw->vw_FontX) + vw->vw_XStartPix,
		 yord, vw->vw_XEndPix, yord + vw->vw_FontY);
	if(linenum < vw->vw_Tx->tx_NumLines)
	{
	    pen_to_glyph_pos(vw, gcol, linenum);
	    draw_line_part(vw, line, linenum, start->pos_Col, gcol);
	}
	line++;
	linenum++;
	y++;
	yord += vw->vw_FontY;
    }
    else
    {
	y = 0;
	yord = vw->vw_YStartPix;
	linenum = vw->vw_StartLine;
	line = vw->vw_Tx->tx_Lines + linenum;
    }
    if(y < yend)
    {
	CLR_RECT(vw, vw->vw_XStartPix, yord, vw->vw_XEndPix,
		  vw->vw_YStartPix + (yend * vw->vw_FontY));
	while((y < yend) && (linenum < vw->vw_Tx->tx_NumLines))
	{
	    pen_to_line(vw, linenum);
	    draw_line(vw, line, linenum);
	    y++;
	    linenum++;
	    line++;
	}
    }
}

/*
 * DOES NOT clear the drawing area
 */
void
redraw_lines(VW *vw, long startLine, long endLine)
{
    LINE *line = vw->vw_Tx->tx_Lines;
    short y;
    if(startLine < vw->vw_StartLine)
	startLine = vw->vw_StartLine;
    y = startLine - vw->vw_StartLine;
    line += startLine;
    if(endLine > vw->vw_Tx->tx_NumLines)
	endLine = vw->vw_Tx->tx_NumLines;
    endLine -= vw->vw_StartLine;
    if(endLine > vw->vw_MaxY)
	endLine = vw->vw_MaxY;
    while(y < endLine)
    {
	pen_to_line(vw, startLine);
	draw_line(vw, line, startLine);
	startLine++;
	y++;
	line++;
    }
}

void
redraw_lines_clr(VW *vw, long startLine, long endLine)
{
    LINE *line = vw->vw_Tx->tx_Lines;
    short y;
    endLine++;
    if((endLine <= vw->vw_StartLine)
       || (startLine > (vw->vw_StartLine + vw->vw_MaxY)))
    {
	return;
    }
    if(startLine < vw->vw_StartLine)
	startLine = vw->vw_StartLine;
    y = startLine - vw->vw_StartLine;
    line += startLine;
    if(endLine > vw->vw_Tx->tx_NumLines)
	endLine = vw->vw_Tx->tx_NumLines;
    endLine -= vw->vw_StartLine;
    if(endLine > vw->vw_MaxY)
	endLine = vw->vw_MaxY;
    CLR_RECT(vw, vw->vw_XStartPix, (y * vw->vw_FontY) + vw->vw_YStartPix,
	      vw->vw_XEndPix, (endLine * vw->vw_FontY) + vw->vw_YStartPix);
    while(y < endLine)
    {
	pen_to_line(vw, startLine);
	draw_line(vw, line, startLine);
	startLine++;
	y++;
	line++;
    }
}

void
redraw_line_from(VW *vw, long col, long lineNum)
{
    if((lineNum >= vw->vw_StartLine)
       && (lineNum < vw->vw_StartLine + vw->vw_MaxY))
    {
	long gcol = glyph_col(vw->vw_Tx, col, lineNum);
	int yord = ((lineNum - vw->vw_StartLine) * vw->vw_FontY)
		   + vw->vw_YStartPix;
	long tmp = gcol - vw->vw_StartCol;
	if(tmp < 0)
	{
	    gcol = vw->vw_StartCol;
	    col = char_col(vw->vw_Tx, gcol, lineNum);
	    tmp = 0;
	}
	CLR_RECT(vw, vw->vw_XStartPix + (tmp * vw->vw_FontX),
		 yord, vw->vw_XEndPix, yord + vw->vw_FontY);
	pen_to_glyph_pos(vw, gcol, lineNum);
	draw_line_part(vw, vw->vw_Tx->tx_Lines + lineNum, lineNum, col, gcol);
    }
}

#ifdef HAVE_X11
/*
 * Assumes (start, end) is valid AND totally viewable (ie, no checks, no
 * clipping). Should be ok since this is meant for Expose type events
 * start is probably trashed
 * `gapBlank' says whether or not I need to clear the space I'm drawing into.
 * These coordinates are *GLYPH* coordinates.
 */
void
redraw_rect(VW *vw, POS *start, POS *end, bool gapBlank)
{
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines + start->pos_Line;
    int yord = ((start->pos_Line - vw->vw_StartLine) * vw->vw_FontY)
	       + vw->vw_YStartPix;
    end->pos_Col++;
    end->pos_Line++;
    if(!gapBlank)
    {
	CLR_RECT(vw,
		  vw->vw_XStartPix + ((start->pos_Col - vw->vw_StartCol)
				      * vw->vw_FontX),
		  yord,
		  vw->vw_XStartPix + ((end->pos_Col - vw->vw_StartCol)
				      * vw->vw_FontX),
		  ((end->pos_Line - vw->vw_StartLine)
		   * vw->vw_FontY) + vw->vw_YStartPix);
    }
    if(end->pos_Line > tx->tx_NumLines)
	end->pos_Line = tx->tx_NumLines;
    while(end->pos_Line > start->pos_Line)
    {
	pen_to_glyph_pos(vw, start->pos_Col, start->pos_Line);
	draw_line_glyph_length(vw, line, start->pos_Line, start->pos_Col,
			       end->pos_Col + 1);
	line++;
	start->pos_Line++;
    }
}
#endif /* HAVE_X11 */

/*
 * Copies from srcLine to bottom of screen to dstLine
 */
void
cut_paste_lines(VW *vw, long srcLine, long dstLine)
{
    int xsrc, ysrc, xwth, yht, xdst, ydst;
    int ybottom = vw->vw_YStartPix + (vw->vw_MaxY * vw->vw_FontY);
    long lastline = vw->vw_StartLine + vw->vw_MaxY;
    /* number of lines which are not blank. */
    int lastdisp = (vw->vw_Tx->tx_NumLines - vw->vw_Tx->tx_ModDelta)
		   - vw->vw_LastDisplayOrigin.pos_Line;
    if(srcLine < vw->vw_StartLine)
	srcLine = vw->vw_StartLine;
    if(dstLine < vw->vw_StartLine)
	dstLine = vw->vw_StartLine;
#if 0
    if(srcLine >= lastline)
	srcLine = lastline - 1;
#endif
    if(dstLine >= lastline)
	return;
    if(srcLine == dstLine)
	return;
    xsrc = vw->vw_XStartPix;
    xwth = vw->vw_XWidthPix;
    xdst = vw->vw_XStartPix;
    ysrc = vw->vw_YStartPix + ((srcLine - vw->vw_StartLine) * vw->vw_FontY);
    ydst = vw->vw_YStartPix + ((dstLine - vw->vw_StartLine) * vw->vw_FontY);
    if(ysrc > ydst)
    {
	if(lastdisp >= vw->vw_MaxY)
	    yht = ybottom - ysrc;
	else
	{
	    yht = (vw->vw_YStartPix + (lastdisp * vw->vw_FontY)) - ydst;
	    /* I'm not sure about this? should `ysrc' be replaced by `ydst'? */
	    if((yht + ysrc) > ybottom)
		yht = ybottom - ysrc;
	}
    }
    else
    {
	if(lastdisp >= vw->vw_MaxY)
	    yht = ybottom - ydst;
	else
	{
	    yht = (vw->vw_YStartPix + (lastdisp * vw->vw_FontY)) - ysrc;
	    if((yht + ydst) > ybottom)
		yht = ybottom - ydst;
	}
    }
    if((xwth > 0) && (yht > 0))
	COPY_AREA(vw, xsrc, ysrc, xwth, yht, xdst, ydst);
    if(ysrc > ydst)
    {
	/* stuff we weren't able to blit.  */
	long firstline = lastline - (srcLine - dstLine);
	if(firstline < vw->vw_Tx->tx_NumLines)
	{
	    redraw_lines_clr(vw, firstline, lastline);
	    if(lastline > vw->vw_Tx->tx_NumLines)
	    {
		CLR_RECT(vw, vw->vw_XStartPix,
			 (vw->vw_YStartPix + (vw->vw_FontY
					      * (vw->vw_Tx->tx_NumLines
						 - vw->vw_StartLine))),
			 vw->vw_XEndPix, ybottom);
	    }
	}
	else
	{
	    CLR_RECT(vw, vw->vw_XStartPix,
		     (vw->vw_YStartPix + (vw->vw_FontY
					  * (firstline
					     - vw->vw_StartLine))),
		     vw->vw_XEndPix, ybottom);
	}
    }
}

void
draw_message_line(VW *vw)
{
    DRAW_LINE(vw, vw->vw_XStartPix, vw->vw_MessageLineY,
	      vw->vw_XEndPix, vw->vw_MessageLineY);
}

void
redraw_message(VW *vw, u_char *str, int len)
{
    if(len >= vw->vw_MaxX - 1)
	len = vw->vw_MaxX - 1;
    MOVE(vw, vw->vw_XStartPix, vw->vw_MessageFontY);
    TEXT(vw, P_TEXT, str, len);
    if(len != vw->vw_MaxX - 1)
    {
	CLR_AREA(vw, PEN_X(vw), vw->vw_MessageFontY - FONT_ASCENT(vw),
		 vw->vw_XEndPix - PEN_X(vw), vw->vw_FontY);
    }
}

void
redraw_cmd_line(VW *vw, u_char *prompt, u_char *cmdLine, long promptLen,
		long cmdLen)
{
    CLR_AREA(vw, vw->vw_XStartPix, vw->vw_MessageFontY - FONT_ASCENT(vw),
	     vw->vw_XWidthPix, vw->vw_FontY);
    MOVE(vw, vw->vw_XStartPix, vw->vw_MessageFontY);
    draw_bit(vw, P_TEXT, prompt, 100000, 0, promptLen);
    draw_bit(vw, P_TEXT, cmdLine - promptLen, 100000, promptLen,
	    cmdLen + promptLen);
}

void
redraw_cmd_line_from(VW *vw, u_char *prompt, u_char *cmdLine, long promptLen,
		     long cmdLen, long startPos)
{
    if(startPos < vw->vw_StartCol)
	startPos = vw->vw_StartCol;
    if(startPos < (vw->vw_StartCol + vw->vw_MaxX))
    {
	int xord = vw->vw_XStartPix + (startPos * vw->vw_FontX);
	CLR_AREA(vw, xord, vw->vw_MessageFontY - FONT_ASCENT(vw),
		 vw->vw_XEndPix - xord, vw->vw_FontY);
	MOVE(vw, xord, vw->vw_MessageFontY);
	if(startPos < promptLen)
	{
	    draw_bit(vw, P_TEXT, prompt, 100000, startPos, promptLen);
	    draw_bit(vw, P_TEXT, cmdLine - promptLen, 100000, promptLen,
		    cmdLen + promptLen);
	}
	else
	    draw_bit(vw, P_TEXT, cmdLine - promptLen, 100000, startPos,
		    cmdLen + promptLen);
    }
}

void
cmd_line_cursor(VW *vw, bool status)
{
    PEN_X(vw) = vw->vw_XStartPix
	+ ((vw->vw_CursorPos.pos_Col - vw->vw_StartCol) * vw->vw_FontX);
    PEN_Y(vw) = vw->vw_MessageFontY;
    TEXT(vw, (status == CURS_ON) ? P_BLOCK : P_TEXT, " ", 1);
}
