/*
BEGIN DOCUMENTATION

Name: TCMEDFUNC.C 				Created: 08/02/83  DTS
					    Last Update: 05/15/84
Title: Text collection management editing functions.

Index:

Abstract: Implements keypad functions of TCM system.  All functions have same
	  basic format.

Usage: coutptr = func(cinptr);

Parameters: cinptr  - pointer to current character in fixed line buffer.
    	    coutptr - pointer to new current character after function applied.

	    Note: Many routines use and change global editing parameters
		  defined in TCMPUBLIC.H.


Environment: DECUS C, RSX11M V4.0

See Also: TCM.C, TCMTABLES.C, TCMEDIT.C, TCM.DOC

Description:

Example(s):

Uses:

Internal:

Update History:

END DOCUMENTATION
*/

#include <clang.c>
#include <stdio.h>
#include <cx.h>
#include "tcmerr.h"
#include "tcmdefs.h"
#include "tcmpublic.h"

/* externals not in tcmpublic */

extern padlin();
extern nxtchr();
extern lstchr();

/* forward references */

extern charpointer csdown();
extern charpointer bottom();
extern charpointer inewln();



/***	Null editing function to ignore undefined key press    ***/

charpointer
nulfun(chr)
    charpointer chr;
{
	return(chr);
} /* end nulfun */



/***	Null editing function to ignore user special key press    ***/

charpointer
invfun(chr)
    charpointer chr;
{
	tcmerr(TS_UKY);		/* Undefined Key (for user) */
	return(chr);
} /* end invfun */



/***	Exit editing function forward    ***/

charpointer
xitfwd(chr)
    charpointer chr;
{
	ret.cd = TS_XFR;
	eddone = true;
	return(chr);
} /* end xitfwd */



/***	Exit editing function backwards    ***/

charpointer
xitbkd(chr)
    charpointer chr;
{
	ret.cd = TS_XBK;
	eddone = true;
	return(chr);
} /* end xitbkd */



/***	Cancel editing function to ignore entry    ***/

charpointer
canfun(chr)
    charpointer chr;
{
	ret.cd = TS_ABO;
	eddone = true;
	return(chr);
} /* end canfun */



/***	Invalid key press (ENTER)   ***/

charpointer
entfun(chr)
    charpointer chr;
{
	tcmerr(TS_ENT);
	return(chr);
} /* end entfun */



/***	Toggle insert/replace mode    ***/

charpointer
instgl(chr)
    register charpointer chr;
{
	iff rdonly then
	{
	    tcmerr(TS_IKY);
	    return(chr);
	}
	insert = !insert;
	iff lin[vtlin].fircol == nochg then
	    lin[vtlin].fircol = changed;	/* Mark status line as changed */
	return(chr);
} /* end instgl */



/***	Carriage return function    ***/

charpointer
retfun(chr)
    register charpointer chr;
{
	iff rdonly then
	{
	    bufcol = 1;
	    return(csdown(line));
	}
	iff (bufline == curwin->botline) or insert then
	{
	    iff !insert then
		chr = &line[lin[0].len - 1];	/* Point to end of text */
	    chr = inewln(chr);
	    iff ret.cd != TS_SUC then
		return(chr);			/* Exit if error */
	}
	savlin(curwin);
	bufline++;
	bufcol = 1;
	iff curline < bot then
	    curwin->edline = lin[++curline].start;
	else
	{
	    scrlup();
	    curwin->topline++;
	    curwin->edline = lin[curline].start + lin[curline].len;	/* Next line in buffer */
	}
	return(getlin(curwin->edline));
} /* end retfun */



/***	Move cursor up    ***/

charpointer
csup(chr)
    register charpointer chr;
{
	iff bufline > 1 then
	{
	    savlin(curwin);
	    iff curline > top then
		chr = lin[--curline].start;
	    else
	    {
		curwin->topline--;
		inslin();
		chr = fndlast(curwin->edline,curwin->bufstart);
	    }
	    bufline--;
	    chr = getlin(curwin->edline = chr);
	}
	else
	    tcmerr(TS_BPS);
	return(chr);
} /* end csup */



/***	Move cursor down    ***/

charpointer
csdown(chr)
    register charpointer chr;
{
	iff bufline < curwin->botline then
	{
	    savlin(curwin);
	    curwin->edline = lin[curline].start + lin[curline].len;	/* Assumes contiguous buffer */
	    iff curline < bot then
		curline++;
	    else
		{ scrlup(); curwin->topline++; }
	    bufline++;
	    chr = getlin(curwin->edline);
	}
	else tcmerr(TS_APE);
	return(chr);
} /* end csdown */



/***	Move cursor left    ***/

charpointer
csleft(chr)
    register charpointer chr;
{
	iff bufcol > 1 then
	    { bufcol--; chr = lstchr(chr); }	/* Backup on this line (chr--) */
	else iff bufline == 1 then
	    tcmerr(TS_BPS);		/* Error: backup past start */
	else
	{
	    savlin(curwin);
	    iff curline > top then	/* Move cursor up line */
	    {
		curline--;
		chr = lin[curline].start;
	    }
	    else
	    {
		chr = fndlast(curwin->edline,curwin->bufstart);	/* backup line */
		inslin();		/* Insert line on screen */
		curwin->topline--;
	    }
	    bufcol = virlen(chr)+1;	/* Cursor at end of previous line */
	    bufline--;
	    curwin->edline = chr;
	    chr = getlin(chr);
	}
	return(chr);
} /* end csleft */

/***	Move cursor right    ***/

charpointer
csright(chr)
    register charpointer chr;
{
	iff bufcol < curwin->width then
	{
	    bufcol++;
	    chr = nxtchr(chr);		/* Advance to next character */
	}
	else
	{
	iff bufline < curwin->botline then	/* Error if at end */
	    bufcol = 1;
	chr = csdown(chr);	/* Advance to start of next line */
	}
	return(chr);
} /* end csright */



/***	Cursor to top of window    ***/

charpointer
topscr(chr)
    register charpointer chr;
{
	savlin(curwin);
	curwin->edline = lin[top].start;
	bufline -= (curline - top);
	curline = top;
	return(getlin(curwin->edline));
} /* end topscr */



/***	Cursor to bottom of window    ***/

charpointer
botscr(chr)
    register charpointer chr;
{
    register int oldcol;

	oldcol = bufcol;
	iff curwin->topline + curwin->height - 1 > curwin->botline then
	    chr = bottom(chr);
	while ((curwin->topline + curwin->height - 1 > curwin->botline) and (ret.cd == TS_SUC))
	    chr = inewln(chr);			/* Extend buffer */
	bufline = curwin->topline + curwin->height - 1;
	savlin(curwin);
	iff bufline > curwin->botline then
	{
	    curline = bot - (bufline - curwin->botline);
	    bufline = curwin->botline;
	}
	else curline = bot;
	bufcol = oldcol;
	curwin->edline = lin[curline].start;
	return(getlin(curwin->edline));
} /* end botscr */

/***	Cursor to start of line    ***/

charpointer
beglin(chr)
    register charpointer chr;
{
	bufcol = 1;
	return(line);
} /* end beglin */



/***	Cursor to right side of window    ***/

charpointer
endlin(chr)
    register charpointer chr;
{
	bufcol = curwin->width;
	line[lin[0].len] = eos;
	return(virchr(line,bufcol));
} /* end endlin */



/***	Backup Cursor one line    ***/

charpointer
backln(chr)
    register charpointer chr;
{
	iff bufcol > 1 then
	{
	    bufcol = 1;
	    return(line);
	}
	else
	    return(csup(chr));
} /* end backln */



/***	Scroll screen down page (backup cursor)    ***/

charpointer
pgbac(chr)
    register charpointer chr;
{
    register int i;

	iff curline > top then
	    chr = topscr(chr);
	else iff bufline <= 1 then
	    tcmerr(TS_BPS);
	for (i = 0; (i < curwin->scroll) and (bufline > 1); i++)
	    chr = csup(chr);
	return(chr);
} /* end pgbac */




/***	Scroll screen up page (advance cursor)    ***/

charpointer
pgfor(chr)
    register charpointer chr;
{
    register int i;

	iff curline < bot then
	    chr = botscr(chr);
	else iff bufline >= curwin->botline then
	    tcmerr(TS_APE);
	for (i = 0; (i < curwin->scroll) and (bufline < curwin->botline); i++)
	    chr = csdown(chr);
	return(chr);
} /* end pgfor */



/***	Go to top of text    ***/

charpointer
toptxt(chr)
    register charpointer chr;
{
    register int oldtop;

	oldtop = curwin->topline;
	savlin(curwin);
	curwin->edline = curwin->bufstart;
	curwin->topline = curwin->column = bufline = 1;
	curline = top;
	bufcol = 1;
	iff oldtop != 1 then
	    setout(curwin);
	return(getlin(curwin->edline));
} /* end toptxt */



/***	Go to bottom (end) of text    ***/

charpointer
bottom(chr)
    register charpointer chr;
{
    register int i,oldtop;

	oldtop = curwin->topline;
	savlin(curwin);
/*	curwin->edline = fndlin(curwin->edline,curwin->bufend,(curwin->botline - curline) + 1);
*/
	curwin->edline = fndlin(curwin->bufstart,curwin->bufend,curwin->botline);
	curwin->topline = ifx (i = curwin->botline - curwin->height) > 0 thenx (i+1) elsex 1;
	bufline = curwin->botline;
	curline = ifx i < 0 thenx bot + i elsex bot;	/* Bottom of window unless .height > .botline */
	bufcol = curwin->column = virlen(curwin->edline) + 1;
	iff oldtop != curwin->topline then
	    setout(curwin);
	return(getlin(curwin->edline));
} /* end bottom */

/***	Delete character to left of cursor    ***/

charpointer
deleft(chr)
    register charpointer chr;
{
    register charpointer str,linend;

	iff rdonly then
	{
	    tcmerr(TS_IKY);
	    return(chr);
	}
	iff bufcol > 1 then
	{
	    str = &line[lin[0].len];
	    *str = eos;
	    bufcol--;			/* Move cursor left */
	    iff chr-- < str then	/* Within line */
	    	/* iff insert or (chr+2 == str) then	* Close this line */
	    {
		movedn(chr+1,chr,(str-chr)+1);
		subchr();
		rfline(bufcol,chr-line,lin[0].len+2);
	    }
	}
	else iff bufline == 1 then
	    tcmerr(TS_BPS);		/* Error: backup past start */
	else
	{   /* Delete end of line character & join lines */
	    savlin(curwin);
	    iff ((curline == top) and (bot > top)) /* or * scroll down to close *
	        ((curwin->botline <= curwin->topline + curwin->height) and
		(curwin->topline > 1)) */ then
	    {
		inslin(); 		/* scroll top of screen down */
		curwin->topline--;
		curline++;
	    }
	    chr = fndlast(curwin->edline,curwin->bufstart);
	    bufcol = virlen(chr)+2;	/* Cursor at end of previous line */
					/* Assumes contiguous buffer */
	    *(curwin->edline-1) = ' ';	/* Join current line with previous */
	    iff virlen(chr) > curwin->width then
	    {
		for(str = linend = virchr(chr,curwin->width+1); str >= curwin->edline; )
		    iff *(--str) == ' ' then break;
		iff str < curwin->edline then
		{
		    movedn(str+1,str,linend-str);	/* Close over added space */
		    str = linend;
		    bufcol--;
		}
		*str = eos;		/* Break line at last space or break word */
		lin[curline].start = ++str;
		lin[curline].len = strlen(str) + 1;
		lin[curline].fircol = newlin;
	    }
	    else
	    {
		iff (bufcol <= 2) and (curline > top) then	/* Test added for hard scroll */
		    dellin(curline-1);
		else dellin(curline);	/* Delete line on screen (scroll others up) */
		sublin();		/* Adjust max counters */
	    }
	    bufline--;
	    iff curline > top then
		curline--;
	    curwin->edline = chr;
	    chr = getlin(chr);
	}
	return(chr);
} /* end deleft */



/***	FMS Stype Delete character left    ***/

charpointer
dechlf(chr)
    register charpointer chr;
{

	iff insert or ((chr-line)+1 >= lin[0].len) then	/* or end of line */
	    chr = deleft(chr);
	else
	    chr = csleft(chr);
/*				* Replace mode: change char to space *
	{
	    *chr = ' ';
	    rfline(bufcol,chr-line,chr-line);
	}
*/
	return(chr);
} /* end dechlf */



/***	Delete character right    ***/

charpointer
dechrt(chr)
    register charpointer chr;
{
    register int oldcol;

	iff rdonly then
	{
	    tcmerr(TS_IKY);
	    return(chr);
	}
	oldcol = bufcol;
	iff (chr-line)+1 >= lin[0].len then	/* IF End of line */
	{
	    padlin(chr);			/* Pad with spaces (see tcmedit) */
	    iff (*(chr-1) == ' ') and (bufline < curwin->botline) then
		*(chr-1) = '\r';		/* Mark end of line */
	    bufcol = 1;
	    chr = csdown(line);
	    iff ret.cd == TS_SUC then
	    {
		chr = deleft(chr);		/* THEN Join with next line */
		iff (ret.cd == TS_SUC) and (lin[0].len < curwin->width+1) then
		    chr = deleft(chr);		/* Delete added space */
		iff *(chr-1) == '\r' then
		    *(chr-1) = ' ';
		while (oldcol < bufcol)
		    chr = csleft(chr);
	    }
	    else { chr = virchr(line,oldcol); bufcol = oldcol; }
	}
	else 
	{
	    chr = csright(chr);			/* ELSE Cursor Right */
	    iff ret.cd == TS_SUC then
		chr = deleft(chr);		/* Delete character left */
	    else bufcol = oldcol;
	}
	return(chr);
} /* end dechrt */



/***	Insert line break at current cursor position	     ***/
/***	Note: Assumes contiguous text buffer (see TCMBUF)    ***/

charpointer
inewln(chr)
    register charpointer chr;
{
    register	int	newlen,nxtlin;
    register	char	*lasthalf;

	iff rdonly then
	{
	    tcmerr(TS_IKY);
	    return(chr);
	}
	iff ! addchr() then			/* Add one to current line length */
	    return(chr);
	iff ! addlin() then			/* Add line to buffer length */
	{
	    subchr();
	    return(chr);			/* Buffer full */
	}
	newlen = chr - line;
	line[lin[0].len - 2] = 'x'; 		/* Overwrite end of old line */
	iff newlen < lin[0].len - 1 then
	    moveup(chr,chr+1,lin[0].len - newlen);	/* Make room for line break */
	else
	    newlen = lin[0].len - 2;
	savlin(curwin);
	*chr = eos;				/* Mark end of new line (redundant?) */
	lasthalf = curwin->edline + newlen;
	*lasthalf++ = eos;			/* Mark end of new current line */
	inslin();				/* Roll lines down on screen */
	iff curline < bot then			/* Update next line to current */
	{
	    nxtlin = curline + 1;
	    lin[nxtlin].start = lasthalf;
	    lin[nxtlin].len = strlen(lasthalf) + 1;
	    lin[nxtlin].fircol = newlin;
	}
	return(getlin(curwin->edline));
} /* end inewln */
