
/*
 * RENDER.C
 * (c) 1992 J.Harper
 */

#include "jed.h"
#include "jed_protos.h"

#define TEXTPEN  1
#define BLOCKPEN 2
#define CURSPEN  3

Prototype   VOID        pentocursor     (VOID);
Prototype   VOID        pentoline       (WORD);
Prototype   VOID        pentopos        (WORD, WORD);
Prototype   VOID        setpospen       (WORD, LONG);
Prototype   VOID        cursor          (BOOL);
Local       VOID        drawbit         (STRPTR, WORD, WORD);
Prototype   VOID        drawline        (LINE *, LONG);
Prototype   VOID        drawlinepart    (LINE *, LONG, WORD);
Prototype   VOID        redrawall       (VOID);
Prototype   VOID        redrawallfrom   (WORD, LONG);
Prototype   VOID        redrawline      (LONG);
Prototype   VOID        redrawlinefrom  (WORD, LONG);
Prototype   VOID        redrawcmdline   (STRPTR, STRPTR, LONG, LONG);
Prototype   VOID        redrawcmdlinefrom(STRPTR, STRPTR, LONG, LONG, WORD);
Prototype   VOID        restorecmdline  (VOID);
Prototype   VOID        cmdlncursor     (BOOL);
Prototype   VOID        refresh         (VOID);
Prototype   VOID        setrefresh      (WORD, WORD, LONG);
Prototype   VOID        refreshallviews (TX *);

VOID
pentocursor(VOID)
{
    VW *vw = CurrVW;
    Move(vw->vw_Rp, vw->vw_XStartPix + ((vw->vw_CursorPos.pos_Col - vw->vw_StartCol) * vw->vw_FontX), vw->vw_FontStart + ((vw->vw_CursorPos.pos_Line - vw->vw_StartLine) * vw->vw_FontY));
}

/*
 * note actual y coordinate not line number
 */
VOID
pentoline(WORD yPos)
{
    VW *vw = CurrVW;
    Move(vw->vw_Rp, vw->vw_XStartPix, vw->vw_FontStart + (yPos * vw->vw_FontY));
}

/*
 * this also takes coordinates...
 */
VOID
pentopos(WORD xPos, WORD yPos)
{
    VW *vw = CurrVW;
    Move(vw->vw_Rp, vw->vw_XStartPix + (xPos * vw->vw_FontX), vw->vw_FontStart + (yPos * vw->vw_FontY));
}

/*
 * ...but this wants column and line numbers (confusing, eh?)
 */
VOID
setpospen(WORD col, LONG line)
{
    if(posinblock(col, line))
	SetAPen(CurrVW->vw_Rp, BLOCKPEN);
    else
	SetAPen(CurrVW->vw_Rp, TEXTPEN);
    SetDrMd(CurrVW->vw_Rp, JAM2);
}

VOID
cursor(BOOL status)
{
    VW *vw = CurrVW;
    if((!vw->vw_Sleeping) && (!vw->vw_DisplayLock))
    {
	LINE *line = vw->vw_Tx->tx_Lines + vw->vw_CursorPos.pos_Line;
	WORD cursoff = vw->vw_CursorPos.pos_Col;
	struct RastPort *rp = vw->vw_Rp;

	if(status)
	{
	    SetAPen(rp, CURSPEN);
	    SetDrMd(rp, JAM1 | INVERSVID);
	}
	else if(cursinblock())
	{
	    SetAPen(rp, BLOCKPEN);
	    SetDrMd(rp, JAM2);
	}
	else
	{
	    SetAPen(rp, TEXTPEN);
	    SetDrMd(rp, JAM2);
	}
	pentocursor();
	if((cursoff + 1) >= line->ln_Strlen)
	    Text(rp, " ", 1);
	else
	    Text(rp, line->ln_Line + cursoff, 1);
    }
}

/*
 * draws a section of a line of text, beg and end are x ordinates
 */
Local VOID
drawbit(STRPTR str, WORD beg, WORD end)
{
    VW *vw = CurrVW;
    WORD startx = vw->vw_StartCol;
    WORD endx = vw->vw_MaxX + startx + 1;

    if(end > startx)
    {
	WORD length;
	if(beg < startx)
	    beg = startx;
	if(end > endx)
	    end = endx;
	if((length = end - beg - 1) > 0)
	    Text(vw->vw_Rp, str + beg, length);
    }
}

/*
 * pen should be at start of line to draw (line should be cleared first)
 */
VOID
drawline(LINE *line, LONG lineNum)
{
    VW *vw = CurrVW;
    struct RastPort *rp = vw->vw_Rp;

    if(vw->vw_BlockStatus)
    {
	SetAPen(rp, TEXTPEN);
	SetDrMd(rp, JAM2);
	drawbit(line->ln_Line, 0, line->ln_Strlen);
    }
    else
    {
	WORD flag = lineinblock(lineNum);

	switch(flag)
	{
	    case 0:     /* none of line in block */
		SetAPen(rp, TEXTPEN);
		SetDrMd(rp, JAM2);
		drawbit(line->ln_Line, 0, line->ln_Strlen);
		break;
	    case 1:     /* whole of line in block */
		SetAPen(rp, BLOCKPEN);
		SetDrMd(rp, JAM2);
		drawbit(line->ln_Line, 0, line->ln_Strlen);
		break;
	    case 2:     /* start of line in block */
		SetAPen(rp, BLOCKPEN);
		SetDrMd(rp, JAM2);
		drawbit(line->ln_Line, 0, vw->vw_Block[1].pos_Col + 1);
		SetAPen(rp, TEXTPEN);
		drawbit(line->ln_Line, vw->vw_Block[1].pos_Col, line->ln_Strlen);
		break;
	    case 3:     /* end of line in block */
		SetAPen(rp, TEXTPEN);
		SetDrMd(rp, JAM2);
		drawbit(line->ln_Line, 0, vw->vw_Block[0].pos_Col + 1);
		SetAPen(rp, BLOCKPEN);
		drawbit(line->ln_Line, vw->vw_Block[0].pos_Col, line->ln_Strlen);
		break;
	    case 4:     /* middle of line in block */
		SetAPen(rp, TEXTPEN);
		SetDrMd(rp, JAM2);
		drawbit(line->ln_Line, 0, vw->vw_Block[0].pos_Col + 1);
		SetAPen(rp, BLOCKPEN);
		drawbit(line->ln_Line, vw->vw_Block[0].pos_Col, vw->vw_Block[1].pos_Col + 1);
		SetAPen(rp, TEXTPEN);
		drawbit(line->ln_Line, vw->vw_Block[1].pos_Col, line->ln_Strlen);
		break;
	}
    }
}

/*
 * pen should be at first draw position
 */
VOID
drawlinepart(LINE *line, LONG lineNum, WORD xStart)
{
    VW *vw = CurrVW;
    struct RastPort *rp = vw->vw_Rp;

    if(vw->vw_BlockStatus)
    {
	SetAPen(rp, TEXTPEN);
	SetDrMd(rp, JAM2);
	drawbit(line->ln_Line, xStart, line->ln_Strlen);
    }
    else
    {
	WORD flag = lineinblock(lineNum);

	switch(flag)
	{
	    case 0:     /* none of line in block */
		SetAPen(rp, TEXTPEN);
		SetDrMd(rp, JAM2);
		drawbit(line->ln_Line, xStart, line->ln_Strlen);
		break;
	    case 1:     /* whole of line in block */
		SetAPen(rp, BLOCKPEN);
		SetDrMd(rp, JAM2);
		drawbit(line->ln_Line, xStart, line->ln_Strlen);
		break;
	    case 2:     /* start of line in block */
		WORD block1col = vw->vw_Block[1].pos_Col;
		SetAPen(rp, BLOCKPEN);
		SetDrMd(rp, JAM2);
		if(xStart < block1col)
		    drawbit(line->ln_Line, xStart, block1col + 1);
		else
		    block1col = xStart;
		SetAPen(rp, TEXTPEN);
		drawbit(line->ln_Line, block1col, line->ln_Strlen);
		break;
	    case 3:     /* end of line in block */
		WORD block0col = vw->vw_Block[0].pos_Col;
		SetAPen(rp, TEXTPEN);
		SetDrMd(rp, JAM2);
		if(xStart < block0col)
		    drawbit(line->ln_Line, xStart, block0col + 1);
		else
		    block0col = xStart;
		SetAPen(rp, BLOCKPEN);
		drawbit(line->ln_Line, block0col, line->ln_Strlen);
		break;
	    case 4:     /* middle of line in block */
		WORD block0col = vw->vw_Block[0].pos_Col;
		WORD block1col = vw->vw_Block[1].pos_Col;
		SetAPen(rp, TEXTPEN);
		SetDrMd(rp, JAM2);
		if(xStart < block0col)
		    drawbit(line->ln_Line, xStart, block0col + 1);
		else
		    block0col = xStart;
		SetAPen(rp, BLOCKPEN);
		if(block0col < block1col)
		    drawbit(line->ln_Line, block0col, block1col + 1);
		else
		    block1col = block0col;
		SetAPen(rp, TEXTPEN);
		drawbit(line->ln_Line, block1col, line->ln_Strlen);
		break;
	}
    }
}

VOID
redrawall(VOID)
{
    VW *vw = CurrVW;
    LONG linenum = vw->vw_StartLine;
    LINE *line = vw->vw_Tx->tx_Lines + linenum;
    WORD y = 0;

    SetAPen(vw->vw_Rp, 0);
    SetDrMd(vw->vw_Rp, JAM2);
    RectFill(vw->vw_Rp, vw->vw_XStartPix, vw->vw_YStartPix, vw->vw_XEndPix - 1, vw->vw_YEndPix - 1);

    while((y < vw->vw_MaxY) && (linenum < vw->vw_Tx->tx_NumLines))
    {
	pentoline(y);
	drawline(line, linenum);
	y++;
	linenum++;
	line++;
    }
}

VOID
redrawallfrom(WORD col, LONG lineNum)
{
    VW *vw = CurrVW;
    LINE *line = vw->vw_Tx->tx_Lines + lineNum;
    WORD y = lineNum - vw->vw_StartLine;
    UWORD yord = (y * vw->vw_FontY) + vw->vw_YStartPix;

    if(col < vw->vw_StartCol)
	col = vw->vw_StartCol;
    if((y >= 0) && (y < vw->vw_MaxY))
    {
	SetAPen(vw->vw_Rp, 0);
	SetDrMd(vw->vw_Rp, JAM2);
	RectFill(vw->vw_Rp, ((col - vw->vw_StartCol) * vw->vw_FontX) + vw->vw_XStartPix, yord, vw->vw_XEndPix - 1, yord + vw->vw_FontY - 1);
	pentopos(col - vw->vw_StartCol, y);
	drawlinepart(line, lineNum, col);
	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 < vw->vw_MaxY)
    {
	SetAPen(vw->vw_Rp, 0);
	SetDrMd(vw->vw_Rp, JAM2);
	RectFill(vw->vw_Rp, vw->vw_XStartPix, yord, vw->vw_XEndPix - 1, vw->vw_YEndPix - 1);

	while((y < vw->vw_MaxY) && (lineNum < vw->vw_Tx->tx_NumLines))
	{
	    pentoline(y);
	    drawline(line, lineNum);
	    y++;
	    lineNum++;
	    line++;
	}
    }
}

VOID
redrawline(LONG lineNum)
{
    VW *vw = CurrVW;
    UWORD yord = ((lineNum - vw->vw_StartLine) * vw->vw_FontY) + vw->vw_YStartPix;

    SetAPen(vw->vw_Rp, 0);
    SetDrMd(vw->vw_Rp, JAM2);
    RectFill(vw->vw_Rp, vw->vw_XStartPix, yord, vw->vw_XEndPix - 1, yord + vw->vw_FontY - 1);

    pentoline(lineNum - vw->vw_StartLine);
    drawline(vw->vw_Tx->tx_Lines + lineNum, lineNum);
}

VOID
redrawlinefrom(WORD col, LONG lineNum)
{
    VW *vw = CurrVW;
    if(col < vw->vw_StartCol)
	col = vw->vw_StartCol;
    if((lineNum >= vw->vw_StartLine) && (lineNum < vw->vw_StartLine + vw->vw_MaxY))
    {
	UWORD yord = ((lineNum - vw->vw_StartLine) * vw->vw_FontY) + vw->vw_YStartPix;

	SetAPen(vw->vw_Rp, 0);
	SetDrMd(vw->vw_Rp, JAM2);
	RectFill(vw->vw_Rp, vw->vw_XStartPix + ((col - vw->vw_StartCol) * vw->vw_FontX), yord, vw->vw_XEndPix - 1, yord + vw->vw_FontY - 1);

	pentopos(col - vw->vw_StartCol, lineNum - vw->vw_StartLine);
	drawlinepart(vw->vw_Tx->tx_Lines + lineNum, lineNum, col);
    }
}

/*
 */
VOID
redrawcmdline(STRPTR prompt, STRPTR cmdLine, LONG promptLen, LONG cmdLen)
{
    VW *vw = CurrVW;
    struct RastPort *rp = vw->vw_Rp;
    WORD yclr = ((vw->vw_MaxY - 1) * vw->vw_FontY) + vw->vw_YStartPix;

    SetAPen(rp, 0);
    SetDrMd(rp, JAM2);
    RectFill(rp, vw->vw_XStartPix, yclr, vw->vw_XEndPix - 1, yclr + vw->vw_FontY - 1);

    SetAPen(rp, TEXTPEN);
    Move(rp, vw->vw_XStartPix, ((vw->vw_MaxY - 1) * vw->vw_FontY) + vw->vw_FontStart);
    drawbit(prompt, 0, promptLen + 1);
    drawbit(cmdLine - promptLen, promptLen, cmdLen + promptLen + 1);
}

VOID
redrawcmdlinefrom(STRPTR prompt, STRPTR cmdLine, LONG promptLen, LONG cmdLen, WORD startPos)
{
    VW *vw = CurrVW;
    struct RastPort *rp = vw->vw_Rp;
    WORD yclr = ((vw->vw_MaxY - 1) * vw->vw_FontY) + vw->vw_YStartPix;
    WORD xord;

    if(startPos < vw->vw_StartCol)
	startPos = vw->vw_StartCol;
    if(startPos < (vw->vw_StartCol + vw->vw_MaxX))
    {
	xord = vw->vw_XStartPix + (startPos * vw->vw_FontX);
	SetAPen(rp, 0);
	SetDrMd(rp, JAM2);
	RectFill(rp, xord, yclr, vw->vw_XEndPix - 1, yclr + vw->vw_FontY - 1);
	SetAPen(rp, TEXTPEN);
	Move(rp, xord, ((vw->vw_MaxY - 1) * vw->vw_FontY) + vw->vw_FontStart);
	if(startPos < promptLen)
	{
	    drawbit(prompt, startPos, promptLen + 1);
	    drawbit(cmdLine - promptLen, promptLen, cmdLen + promptLen + 1);
	}
	else
	    drawbit(cmdLine - promptLen, startPos, cmdLen + promptLen + 1);
    }
}

/*
 * redraws the line which the command line overwrote
 */
VOID
restorecmdline(VOID)
{
    VW *vw = CurrVW;
    UWORD yord = ((vw->vw_MaxY - 1) * vw->vw_FontY) + vw->vw_YStartPix;
    LONG linenum = vw->vw_StartLine + vw->vw_MaxY - 1;

    SetAPen(vw->vw_Rp, 0);
    SetDrMd(vw->vw_Rp, JAM2);
    RectFill(vw->vw_Rp, vw->vw_XStartPix, yord, vw->vw_XEndPix - 1, yord + vw->vw_FontY - 1);
    if(vw->vw_Tx->tx_NumLines > linenum)
    {
	pentoline(vw->vw_MaxY - 1);
	drawline(vw->vw_Tx->tx_Lines + linenum, linenum);
    }
}

/*
 * draws the cursor for the command line
 */
VOID
cmdlncursor(BOOL status)
{
    VW *vw = CurrVW;
    struct RastPort *rp = vw->vw_Rp;
    if(status)
    {
	SetAPen(rp, CURSPEN);
	SetDrMd(rp, JAM2 | INVERSVID);
    }
    else
    {
	SetAPen(rp, TEXTPEN);
	SetDrMd(rp, JAM2);
    }
    Move(rp, vw->vw_XStartPix + ((vw->vw_CursorPos.pos_Col - vw->vw_StartCol) * vw->vw_FontX), ((vw->vw_MaxY - 1) * vw->vw_FontY) + vw->vw_FontStart);
    Text(rp, " ", 1);
}

/*
 * The idea with this routine is that each function used to perform some type
 * of operation OR's the refresh type that it needs into the vw_RefreshType
 * flag. Then at the end of the operation the highest level routine calls
 * refresh() and the smallest type of refresh possible is performed.
 *
 * the only problem is that most of the low level editing functions in
 * edit.c don't set the refresh flag, oh well...
 */
VOID
refresh(VOID)
{
    VW *vw = CurrVW;
    if(vw && (!vw->vw_DeferRefresh))
    {
	if((!vw->vw_Sleeping) && (!vw->vw_DisplayLock))
	{
	    WORD refflags = vw->vw_RefreshType;
	    if(refflags & RFF_ALL)
		redrawall();
	    else if(refflags & RFF_ALLFROM)
	    {
		if(vw->vw_RefreshPos.pos_Line < vw->vw_StartLine)
		{
		    vw->vw_RefreshPos.pos_Line = vw->vw_StartLine;
		    vw->vw_RefreshPos.pos_Col = 0;
		}
		if(vw->vw_RefreshPos.pos_Line <= vw->vw_StartLine + vw->vw_MaxY)
		    redrawallfrom(vw->vw_RefreshPos.pos_Col, vw->vw_RefreshPos.pos_Line);
	    }
	    else if(refflags & RFF_CURSLINE)
		redrawline(vw->vw_CursorPos.pos_Line);
	    else if(refflags & RFF_LINEFROM)
	    {
		if((vw->vw_RefreshPos.pos_Line >= vw->vw_StartLine) && (vw->vw_RefreshPos.pos_Line <= vw->vw_StartLine + vw->vw_MaxY))
		    redrawlinefrom(vw->vw_RefreshPos.pos_Col, vw->vw_RefreshPos.pos_Line);
	    }
	    vw->vw_RefreshType = 0;
	    vw->vw_LastRefresh = vw->vw_Tx->tx_Changes;
	}
    }
    else if(vw)
	vw->vw_DeferRefresh--;
}

/*
 * This must be called if you want to set the refresh position, this routine
 * makes it all work :-)
 */
VOID
setrefresh(WORD refFlag, WORD refX, LONG refY)
{
    VW *vw = CurrVW;
    if(refFlag & (RFF_LINEFROM | RFF_ALLFROM))
    {
	if(vw->vw_RefreshType & (RFF_ALLFROM | RFF_LINEFROM))
	{
	    if(vw->vw_RefreshPos.pos_Line > refY)
	    {
		vw->vw_RefreshType |= RFF_ALLFROM;
		vw->vw_RefreshPos.pos_Col = refX;
		vw->vw_RefreshPos.pos_Line = refY;
	    }
	    else if((vw->vw_RefreshPos.pos_Line == refY) && (vw->vw_RefreshPos.pos_Col > refX))
	    {
		vw->vw_RefreshType |= refFlag;
		vw->vw_RefreshPos.pos_Col = refX;
	    }
	    else
		vw->vw_RefreshType |= RFF_ALLFROM;
	}
	else
	{
	    vw->vw_RefreshType |= refFlag;
	    vw->vw_RefreshPos.pos_Col = refX;
	    vw->vw_RefreshPos.pos_Line = refY;
	}
    }
    else
	vw->vw_RefreshType |= refFlag;
}

/*
 * this function steps through all views attached to this file refresh()'ing
 * each one.
 */
VOID
refreshallviews(TX *tx)
{
    VW *oldvw = CurrVW;
    for(CurrVW = (VW *)tx->tx_Views.mlh_Head; CurrVW->vw_Node.mln_Succ; CurrVW = (VW *)CurrVW->vw_Node.mln_Succ)
    {
	if(!CurrVW->vw_Sleeping)
	{
	    cursor(OFF);
	    refresh();
	    cursor(ON);
	}
    }
    CurrVW = oldvw;
    cursor(OFF);
}

