/******************************************************************************

    MODUL
	$Id: edit.c 1.4 1994/09/09 12:31:30 digulla Exp digulla $

    DESCRIPTION
	Everything for editing like DEL, BS, INDENT, DELINE, INSLINE, ...

    HISTORY
	14. Nov 1992	ada created
	$Log: edit.c $
 * Revision 1.4  1994/09/09  12:31:30  digulla
 * added new style Prototypes, DEFCMD and DEFHELP
 * fixed a bug in do_join()
 *
 * Revision 1.3  1994/08/30  11:09:32  digulla
 * use BLOCK_MASK over ALL_MASK
 *
 * Revision 1.2  1994/08/19  14:09:10  digulla
 * fixed SetWrMsk()
 * removed dead assignments
 *
 * Revision 1.1  1994/08/13  17:14:04  digulla
 * Initial revision
 *

******************************************************************************/

/**************************************
		Includes
**************************************/
#include "defs.h"
#include "clipboard.h"
#define MYDEBUG     0
#include <debug.h>


/**************************************
	    Globale Variable
**************************************/


/**************************************
      Interne Defines & Strukturen
**************************************/


/**************************************
	    Interne Variable
**************************************/


/**************************************
	   Interne Prototypes
**************************************/


/*DEFHELP #cmd textedit BACK - backspace, (delete char to left of cursor) */
/*DEFHELP #cmd textedit BS - backspace, (delete char to left of cursor) */
/*DEFHELP #cmd textedit DEL - delete, (deletes char under cursor) */

DEFUSERCMD("bs",   0, CF_COK, void, do_bs, (void),;)
DEFUSERCMD("del",  0, CF_COK, void, do_bs, (void),;)
DEFUSERCMD("back", 0, CF_COK, void, do_bs, (void),)
{
    ED	 * ep  = Ep;
    RP	 * rp  = ep->win->RPort;
    WORD   i,
	   col = ep->column;
    Column right;
    BOOL   is_bs = (av[0][0] == 'b');

    if ((Current[col] && !is_bs) || (col && is_bs))
    {
	if (is_bs)
	{
	    movmem (Current + col, Current + col - 1, Clen - col + 1);

	    col --;
	    ep->column --;

	} else
	    movmem (Current + col + 1, Current + col, Clen - col);

	Clen --;

	i = ep->line;
	right = ep->topcolumn + Columns;

	if (!Nsu) {                         /* PATCH_NULL [09-Dec-92] : line added */
	    if (col < ep->topcolumn)
	    {
		text_adjust (FALSE);
	    } else
	    {
		if (is_inblock (i, right) & BP_INSIDE)
		{
		    SetAPen (rp, BLOCK_FPEN(ep));
		    SetBPen (rp, BLOCK_BPEN(ep));
		} else
		{
		    SetAPen (rp, TEXT_FPEN(ep));
		    SetBPen (rp, TEXT_BPEN(ep));
		}

		SetWrMsk (rp, BLOCK_MASK(ep));

		i -= ep->topline;

		ScrollRaster(rp, Xsize, 0,
		    COL(col - ep->topcolumn), ROW(i),
		    Xpixs, ROW(i + 1) - 1
		);

		if (ep->win->WLayer->Flags & LAYERREFRESH)
		    OptimizedRefresh (ep);
		else if (Clen >= right)
		{
		    right --;

		    Move (rp, COLT(right - ep->topcolumn), ROWT(i));
		    Text (rp, Current + right, 1);
		}
	    }
	} /* if !Nsu */ 		    /* PATCH_NULL [09-Dec-92] : line added */

	if (!GETF_COMLINEMODE(Ep) && GETF_WORDWRAP(Ep))
	    do_reformat(0);
    } else
    {
	SETF_ABORTCOMMAND(Ep,1);
    }
} /* do_bs */


/*DEFHELP #cmd textedit REMEOL - Remove text under and beyond the cursor. */

DEFUSERCMD("remeol", 0, CF_VWM|CF_COK, void, do_remeol, (void),)
{
    Current[Clen = Ep->column] = 0;

    text_sync ();
    text_redisplaycurrline ();
} /* do_remeol */


/*DEFHELP #cmd textedit SPLIT - Split line at cursor */

DEFUSERCMD("split", 0, 0, void, do_split, (void),)
{
    UBYTE buf[MAXLINELEN];
    ED	* ep		   = Ep;
    RP	* rp		   = ep->win->RPort;
    char  onLastLine;

    strcpy ((char *)buf, (char *)Current+ep->column);
    Current[Clen = ep->column] = '\0';

    text_sync ();

    SetAPen (rp, TEXT_BPEN(ep));
    SetWrMsk (rp, BLOCK_MASK(ep));

    if (!Nsu)
	RectFill (rp, COL(0), ROW(ep->line-ep->topline),
	       Xpixs, ROW(ep->line-ep->topline+1)-1);

    SetAPen (rp, TEXT_FPEN(ep));

    text_displayseg (ep->line - ep->topline, 1);

    onLastLine = (ep->line == ep->lines-1);
    do_downadd ();

    if (!onLastLine)
	do_insline ();

    strcpy ((char *)Current, (char *)buf);
    Clen = strlen ((char *)Current);

    text_sync ();
    text_displayseg (ep->line - ep->topline, 1);

    do_up ();
} /* do_split */


/*DEFHELP #cmd textedit JOIN - join next line to line at cursor */

DEFUSERCMD("join", 0, 0, int, do_join, (void),)
{
    int   i = Clen;
    ED * ep = Ep;

    /* Check if there is a line below and the length of both lines */
    if (ep->line + 1 < ep->lines &&
		LINELEN(ep,ep->line+1)+i <= MAXLINELEN-2)
    {
	char * ptr;
	int    column = ep->column;

	/* Find first nonspace in next line */
	for (ptr=GETTEXT(ep,ep->line+1); *ptr==' ' && *ptr; ptr ++);

	if (i && !Current[i]) /* ignore last char if it's \0 */
	    i --;

	/* Find last char in this line */
	for (; i >= 0 && Current[i] == ' '; i--);

	/* Add a space if neccessary */
	if (i >= 0)
	{
	    if (Current[i])
		i++;

	    Current[i ++] = ' ';
	}

	/* Copy the line */
	strcpy((char *)Current+i, ptr);

	/* new length */
	Clen = strlen ((char *)Current);

	/* redisplay */
	text_sync ();
	text_displayseg (ep->line - ep->topline, 1);

	i = text_lineno ();

	do_down ();
	do_deline ();

	if (i != text_lineno ())
	    do_up ();

	/* 29-11-94 - fix for shortlines */
	if (ep->column != column)
	{
	    ep->column = column;
	    text_adjust (FALSE);
	}
	return(1);
    }

    return(0);
} /* do_join */


/*
 * n == -1  :	force reformat entire paragraph
 * n ==  0  :	only until line equalizes (from text_write())
 *
 * What is a paragraph?   A paragraph ends whenever the left justification
 * gets larger, or on a blank line.
 */

/*DEFHELP #cmd textedit,format REFORMAT - reformat paragraph using the margin. */

DEFUSERCMD("reformat", 0, 0, void, do_reformat, (int n),)
{
    char * str;
    ED	 * ep = Ep;
    RP	 * rp = ep->win->RPort;
    int    nlok;   /* Next Line ok ? */
    int    lnsc;   /* LastNS of Current */
    int    fnst;   /* FirstNS of next Line */
    int    fnsc;   /* FirstNS of Current */
    int    column  = ep->column;
    int    srow    = ep->line;
    int    crow    = srow;
    int    erow    = srow;
    WORD   dins    = 0; 	/* relative insert lines/delete lines	*/
    char   moded   = 0; 	/* any modifications done at all?	*/
    char   checked = 0; 	/* for cursor positioning.		*/
    Line   old_top;
    char   slin_bak;
    slin_bak = GETF_SLINE( ep );
    SETF_SLINE( ep, 0 );

    if (ep->config.margin == 0)
	ep->config.margin = 75;

    Nsu ++;

    old_top = ep->topline;

    for (;;)
    {
	str = GETTEXT(ep,ep->line+1);
	fnst = 0;
	fnsc = firstns((char *)Current);
	nlok = (ep->line + 1 < ep->lines && fnsc >= (fnst=firstns(str)));
	if (ep->config.wwcol >= 0)
	    fnst = fnsc = ep->config.wwcol;
	if (nlok && str[0] == 0)
	    nlok = 0;
	lnsc = lastns((char *)Current);
	if (lnsc < ep->config.margin)
	{    /* space at end of line for marg-lnsc-2 letter word   */
	    if (nlok == 0)        /* but no more data to joinup   */
		break;		  /* done */
	    if (ep->config.margin - lnsc - 2 >= wordlen(str+fnst))
	    {
		ep->column = 0;
		Clen = lastns((char *)Current);
		if (Current[Clen])
		    ++Clen;
		moded = 1;
		--dins;
		if (do_join())
		    continue;
		++dins;

		error ("reformat:\nMargin too big");
		break;
	    }
	    if (n == 0)        /* if couldn't mod line, and text_write, don't update any more */
		break;
	    do_down ();
	    erow = ep->line;
	    continue;
	}
				/* no space, need to split	*/
				/* find start of prev word	*/
	for (;;)
	{
	    int i = lnsc;
	    while (i && Current[i] != ' ')
		--i;
	    lnsc = i;
	    if (i >= ep->config.margin)
	    {
		while (i && Current[i] == ' ')
		    --i;
		if (i < ep->config.margin)
		    break;
		lnsc = i;
	    } else
		break;
	}

	/* Word too long */
	if (wordlen((char *)Current+lnsc) >= ep->config.margin)
	{
	    error ("reformat:\nWord too long");
	    break;
	}

	if (lnsc)
	{	      /* ok to split at word	      */
	    ++lnsc;
	    ++dins;
	    ep->column = lnsc;
	    do_split (); /* Split at point LNSC          */
	    do_down ();          /* must insert proper amount?   */
	    {
		int indent = (nlok == 0) ? fnsc : fnst;
		if (ep->config.wwcol >= 0)
		    indent = ep->config.wwcol;
		if (!checked)
		{
		    checked = 1;
		    if (lnsc <= column)
		    {	/* if split before cursor   */
			column = column - ep->column + indent;
			++crow;
		    }
		}
		if (Clen + indent < MAXLINELEN-3)
		{
		    movmem(Current, Current + indent,
			    strlen((char *)Current)+1);
		    setmem(Current, indent, ' ');
		    Clen += indent;
		}
	    }
	    erow = ep->line;
	    continue;
	}

	if (n == 0)
	    break;

	do_down ();
    }

    if (column < 0 || column > 200)
	column = 0;

    if (srow >= ep->lines)
	goto ra;

    Nsu --;

    if (old_top != ep->topline)
    {
	scroll_display (0, ep->topline-old_top,
		ep->topcolumn, ep->topline,
		ep->topcolumn + Columns, ep->topline + Lines);
    }

    if (dins || srow < ep->topline || srow >= ep->topline + Lines)
    {
ra:
	text_sync ();

	ep->line = crow;
	ep->column = column;

	text_load ();

	text_adjust (TRUE);
    } else
    {
	text_sync ();

	ep->line = crow;
	ep->column = column;

	text_load ();

	if (erow != srow)
	{
	    if (!text_adjust (FALSE))
	    {
		if (erow - ep->topline > Lines)
		    erow = ep->topline + Lines;

		SetAPen(rp, TEXT_BPEN(ep));
		SetWrMsk (rp, BLOCK_MASK(ep));

		RectFill(rp, COL(0), ROW(srow - ep->topline),
		    Xpixs, ROW(erow - ep->topline + 1)-1);

		SetAPen(rp, TEXT_FPEN(ep));

		text_displayseg(srow - ep->topline, erow - srow + 1);
	    }
	} else
	{
	    if (moded)
		text_redisplaycurrline ();
	}
    }
    if (column > Clen)
    {
	setmem(Current+Clen, column - Clen, ' ');
	Current[column] = 0;
    }
    ep->column = column;

    SETF_SLINE( ep, slin_bak);
} /* do_reformat */


/*DEFHELP #cmd textedit INSLINE - insert line */

DEFUSERCMD("insline", 0, 0, void, do_insline, (void),)
{
    LINE ptr;
    ED *ep = Ep;

    SETF_MODIFIED(ep,1);

    text_sync ();

    if (makeroom (32))
    {
	ptr = allocline (1);

	bmovl(ep->list+ep->line, ep->list+ep->line+1,ep->lines-ep->line);
	SETLINE(ep,ep->line,ptr);

	ep->lines ++;

	if (ActualBlock.ep == ep)
	{
#if 1 /* PATCH_NULL 27-09-94  */
	    if (ep->line <= ActualBlock.end_line)
		set_block_end (ActualBlock.end_line+1, NO_CHANGE);

	    if (ep->line <= ActualBlock.start_line)
		set_block_start (ActualBlock.start_line+1, NO_CHANGE);
#else
	    if (ep->line <= ActualBlock.start_line)
		MOVE_BLOCKMARK ( +1, 0 );
	    else
	    if (ep->line <= ActualBlock.end_line)
		set_block_end (ActualBlock.end_line+1, NO_CHANGE);
#endif
	}
    } else
    {
	nomemory ();
    }

    text_load ();

    if (Nsu == 0)
    {
	scroll_display (0, -1, 0, ep->line, MAXLINELEN, ep->topline+Lines);
	text_displayseg (ep->line - ep->topline, 1);
    }
} /* do_insline */


/*DEFHELP #cmd textedit DELINE - delete line */

DEFUSERCMD("deline", 0, 0, void, do_deline, (void),)
{
    int delline, eline;
    ED *ep = Ep;

    /* store line for later UNDO */
    strcpy ((char *)Deline, (char *)Current);

    if (ep->lines > 1)
    {
	SETF_MODIFIED(ep,1);

	text_sync ();

	/* remove line */
	freeline (GETLINE(ep,ep->line));
	bmovl (ep->list+ep->line+1, ep->list+ep->line, ep->lines-ep->line-1);

	/* adjust block */
	if (ActualBlock.ep == ep)
	{
#if 0 /* PATCH_NULL 27-09-94  */
	    if (ep->line < ActualBlock.start_line)
		set_block_start (ActualBlock.start_line-1, NO_CHANGE);

	    if (ep->line <= ActualBlock.end_line)
		set_block_end (ActualBlock.end_line-1, NO_CHANGE);

	    /* if we just deleted the last line of the block, clear it */
	    if (ActualBlock.end_line < ActualBlock.start_line)
		unblock ();
#else
// it would really be great, if we could say something like
// #define uplast(which) block. ## which ## _column = INFRONT_OF_LINE;
// where that IN_FRONT_OF was e.g. a '-3'
// or alternatively, a PAST_END (instead of the 500) would be
// a little bit more convenient since Aaron is working on longer lines
#define uplast(which) do{ -- block. ## which ## _line; block. ## which ## _column = 500; }while(0)
	    Block block;
	    get_block(&block);

	    if (ep->line == block.end_line)
		 uplast( end );
	    else if (ep->line < block.end_line)
		-- block.end_line;

	    if (ep->line == block.start_line)
		uplast( start );
	    else if (ep->line < block.start_line)
		-- block.start_line;

	    if ((block.start_line > block.end_line) ||
	       ((block.start_line == block.end_line) &&
		(block.start_column > block.end_column)))
		unblock();
	    else if (ep->line <= ActualBlock.end_line)
	      {
		set_block(&block);
		//displayblock (1);
	      }
//printf ("block: %d (%2d,%3d)-(%2d,%3d)\n", block.type, block.start_line, block.start_column, block.end_line, block.end_column);

#if 0
	    if ((ep->line < ActualBlock.end_line)) {
		    if (ep->line == ActualBlock.start_line)
			set_block_start (NO_CHANGE, 0);
		    set_block_end (ActualBlock.end_line-1, NO_CHANGE);
	    } else
	    if ((ep->line == ActualBlock.end_line))
		if ((ActualBlock.end_line == ActualBlock.start_line))
		    unblock ();
		else
		    set_block_end (NO_CHANGE, 0);
#endif
#endif
	}

	/* remember line we deleted */
	delline = ep->line;

	/* line was last one */
	if (ep->line >= --ep->lines)
	{
	    /* move up one line */
	    --ep->line;

	    /* get the line into current */
	    text_load ();

	    /* line now below topline ? */
	    if (ep->line < ep->topline)
	    {
		/* update screen if allowed */
		if (!Nsu)
		{
		    /* find new position */
		    ep->topline = ep->line - (Lines>>1);

		    if (ep->topline < 0)
			ep->topline = 0;

		    text_redisplay ();
		}

		return;
	    }
	}

	/* get line */
	text_load ();

	/* update display */
	if (!Nsu)
	{
	    eline = ep->topline + Lines - 1;

	    if (ep->lines < eline)
		eline = ep->lines +1;

	    scroll_display (0, 1, ep->topcolumn, delline,
				  ep->topcolumn+Columns-1, eline);
	}
    } else
    {
	/* no more lines in the text */
	do_firstcolumn ();
	do_remeol ();
	SETF_MODIFIED(ep,0);
    }
} /* do_deline */


/*DEFHELP #cmd textedit TLATE how - Modify character under cursor. */

DEFUSERCMD("tlate", 1, CF_COK, void, do_tlate, (void),)
{
    UBYTE * ptr = av[1];
    ED	  * ep	= Ep;
    UBYTE   c	= Current[ep->column];

    if (c == 0)
	c = ' ';

    switch (*ptr)
    {
	case '+':
	    c += strtol ((char *)ptr+1, NULL, 0);
	    break;

	case '-':
	    c -= strtol ((char *)ptr+1, NULL, 0);
	    break;

	case '"':
	    c = ptr[1];
	    break;

	case 'u':
	case 'U':
	    c = toupper (c);
	    break;

	case 'l':
	case 'L':
	    c = tolower (c);
	    break;

	default:
	    c = strtol ((char *)ptr, NULL, 0);
    }

    if (c)
    {
	if (Current[ep->column] == 0)
	{
	    Clen = ep->column + 1;
	    Current[Clen] = 0;
	}

	if (!Nsu && Current[ep->column] != c)
	{
	    LINE text_line;

	    SETF_MODIFIED(ep,TRUE);

	    Current[ep->column] = c;	/* Change char */

	    /* get the actual line */
	    text_line = GETLINE(ep,ep->line);

	    /* if the char fits into the line, just copy it (ie. avoid
	       text_sync(). Else call text_sync() */

	    if (ep->column < LENGTH(text_line))
	    {
		char * ptr;

		/* change it */
		ptr = CONTENTS(text_line);
		ptr[ep->column] = c;
	    }
	    else
		text_sync ();

	    /* Delete the character on screen. First, find the color
	       we need for this operation, then draw over it */
	    if (is_inblock (ep->line, ep->column) & BP_INSIDE)
		SetAPen (ep->win->RPort, BLOCK_BPEN(ep));
	    else
		SetAPen (ep->win->RPort, TEXT_BPEN(ep));

	    /* redraw that character */
	    {
		UWORD lin, col;

		col = ep->column - ep->topcolumn;
		lin = ep->line - ep->topline;

		SetWrMsk (ep->win->RPort, BLOCK_MASK(Ep));
		RectFill (ep->win->RPort, COL(col), ROW(lin),
				    COL(col+1)-1, ROW(lin+1)-1);
		SetWrMsk (ep->win->RPort, ALL_MASK);
	    }

	    redraw_textlineseg (ep->line, ep->column, ep->column+1);
	}
    }
} /* do_tlate */


/*DEFHELP #cmd textedit,format JUSTIFY how - simple text justification. */

DEFUSERCMD("justify", 1, 0, void, do_justify, (void),)
{
    ED *ep = Ep;
    WORD  firstnb, lastnb, i, n, fill, c, sp;
    WORD  changed = FALSE;

    firstnb = firstns((char *)Current);
    lastnb = lastns((char *)Current);

    if (ep->config.margin >= MAXLINELEN)
    {
	error ("%s:\nMargin (%ld) too large\n(max %ld)\n",
		av[0], ep->config.margin, MAXLINELEN-1);
	return;
    }

    if (firstnb >= lastnb) /* line empty ? */
	return;

    switch (tolower (av[1][0]))
    {
	case 'l':
	    /* remove all firstnb spaces */
	    if (firstnb)
	    {
		Clen -= firstnb;
		memmove (Current, Current+firstnb, Clen+1);
		changed = TRUE;
	    }
	    break;

	case 'r':
	    /* insert/remove spaces, so the last character is at margin */
	    if (n = (ep->config.margin - lastnb))
		goto insrem;

	    break;

	case 'c':
	    /* width of line */
	    n = (lastnb - firstnb + 1);

	    if (n < ep->config.margin)
	    {
		n = (ep->config.margin/2) - (n+1)/2 - firstnb;

		if (n) /* insert/remove n spaces */
		{
insrem:
		    changed = TRUE;

		    if (n > 0) /* insert */
		    {
			memmove (Current+n, Current, Clen+1);
			memset (Current, ' ', n);
		    }
		    else /* remove */
		    {
			/* n is bigger than the number of spaces at this
			   point, because otherwise the length of the
			   line would have been bigger than the margin.
			   And remember: n is NEGATIVE */

			memmove (Current, Current-n, Clen+n+1);
		    }

		    Clen += n;
		}
	    }

	    break;

	case 'f':
	    n = 0;
	    i = firstnb;

	    while (i <= lastnb)
	    {
		while ((c = Current[i]) && c != ' ')
		    i++;
		if (i <= lastnb)
		{
		    n++;
		    while (Current[i] == ' ')
			i++;
		}
	    }

	    fill = ep->config.margin - lastnb - 1;
	    i = firstnb;
	    Current[lastnb + 1] = 0;

	    if (n > 0 && fill > 0)
		changed = TRUE;

	    while (n > 0 && fill > 0 && Current[i])
	    {
		while ((c = Current[i]) && c != ' ')
		    i++;
		sp = fill / n;
		movmem (&Current[i], &Current[i + sp],
			strlen((char *)&Current[i]) + 1);
		memset ((char *)&Current[i], ' ', sp);
		while (Current[i] == ' ')
		    i++;
		fill -= sp;
		n--;
	    }

	    break;

	default:
	    error ("%s:\nunknown justification\n%s", av[0], av[1]);
	    break;
    }

    if (changed)
    {
	text_sync ();
	text_redisplaycurrline ();
    }
} /* do_justify */


/*DEFHELP #cmd textedit UNJUSTIFY - removes extra spaces in a line */

DEFUSERCMD("unjustify", 0, 0, void, do_unjustify, (void),)
{
    WORD  i, j, waswhite = FALSE;
    UBYTE c;

    for (i = 0; Current[i] == ' '; i++);

    for (j = i; Current[i]; i++)
    {
	c = Current[j] = Current[i];

	if (c != ' ' || !waswhite)
	    j++;

	waswhite = (c == ' ');
    }

    Current[j] = 0;

    if (i != j)
    {
	text_sync ();
	text_redisplaycurrline ();
    }
} /* do_unjustify */


/*DEFHELP #cmd textedit INDENT what how - indent text. @{B}what@{UB} specifies what to indent and @{B}how@{UB} how to indent it. */

DEFUSERCMD("indent", 2, CF_BLK|0, void, do_indent, (void),)
{
    Line   start,
	   end;
    Column column;
    int    indent,
	   dir,
	   align,
	   len,
	   curr_indent,
	   leading,
	   trailing,
	   t;
    char * ptr;
    RP	 * rp;
    LINE * linelist;

    /* decode range */
    ptr = av[1];

    start = end = -1;

    while (*ptr)
    {
	if (*ptr == '.')
	{
	    start = end = Ep->line;
	    break;
	} else if (isdigit(*ptr))
	{
	    if (start == -1)
		start = atoi (ptr);
	    else end = atoi (ptr);

	    while (isdigit(*ptr)) ptr ++;

	    ptr --;
	} else if (*ptr == '$')
	{
	    if (start == -1)
		start = get_pong (*ptr - '0');
	    else
		end = get_pong (*ptr - '0');
	} else if (*ptr == 'b')
	{
	    if (!block_ok())
	    {
		error ("indent block:\nNo block specified");
		return ;
	    }

	    if (!ptr[1])
	    {
		start = ActualBlock.start_line;
		end = ActualBlock.end_line;
	    } else
	    {
		ptr ++;

		if (*ptr == 's')
		    indent = ActualBlock.start_line;
		else
		    indent = ActualBlock.end_line;

		if (start == -1)
		    start = indent;
		else
		    end = indent;
	    }
	} else if (*ptr == 't')
	{
	    start = 1;
	    end = Ep->lines;
	} else if (*ptr == '_')
	{
	    if (start == -1)
		start = Ep->lines;
	    else
		end = Ep->lines;
	}

	ptr ++;
    } /* while (*ptr) */

    if (start == -1)
    {
	error ("indent:\ncannot evaluate start-line");
	return ;
    }

    if (end == -1) end = start;

    if (start > end)
    {
	start ^= end;	    /* Swap ! 8-O */
	end   ^= start;
	start ^= end;
    }

    /* Now find out how to indent */

    ptr = av[2];

    dir = 1;		/* indent, i.e. insert spaces */
    align = 0;		/* just insert `indent' spaces */
    indent = -1;
    column = 0;

    while (*ptr)
    {
	if (*ptr == '-')
	    dir = -1;		/* outdent line, i.e. delete spaces */
	else if (*ptr == '.')
	    align = 1;		/* align line to a multiple of `indent' */
	else if (*ptr == 't')
	    indent = Ep->config.tabstop;
	else if (isdigit(*ptr))
	{
	    indent = atoi (ptr);

	    while (isdigit(*ptr)) ptr++;
	    ptr --;
	} else if (*ptr == 'c')
	    column = Ep->column;

	ptr ++;
    } /* while (*ptr) */

    if (indent == -1)
    {
	error ("indent:\nCannot evaluate indent-amount");
	return ;
    }

    text_sync ();

    /* Note : If the line contains less spaces than indent specifies and
       the user wants to outdent, the line is left-aligned, i.e. no chars
       except spaces are deleted ! This is also true for indent, i.e. no
       chars are moved over the right border (MAXLINELEN chars). */

    linelist = Ep->list + start;

    for (t=start; t <= end; t ++, linelist ++)
    {
	strcpy (Current, linelist[0]);
	Clen = strlen (Current);

	if (Clen <= column) continue;

	ptr = Current + column;

	while (*ptr == ' ') ptr ++;

	if (!*ptr) continue;          /* Empty line */

	leading  = (int)ptr - (int)Current;
	trailing = MAXLINELEN-1 - Clen;

	/* Should I align the line to a multiple of indent or just
	    insert/delete indent spaces ? */
	if (align)
	{
	    /* use appropriate difference */
	    if (dir > 0)
		curr_indent = indent - (leading % indent);
	    else
	    {
		curr_indent = leading % indent;

		if (!curr_indent)
		    curr_indent = indent;
	    }
	} else curr_indent = indent;

	len = strlen (ptr) + 1;

	if (dir > 0)
	{
	    if (trailing < curr_indent)
		curr_indent = trailing;

	    if (!curr_indent) continue;

	    movmem (ptr, ptr+curr_indent, len);
	    setmem (ptr, curr_indent, ' ');
	} else
	{
	    if (leading < curr_indent)
		curr_indent = leading;

	    if (!curr_indent) continue;

	    movmem (ptr, ptr-curr_indent, len);
	}

	Clen = strlen (Current);

#ifdef DEBUG
printf ("Old len %d  New Len %d     BlenOld %d  BlenNew %d\n",
	len, Clen, ((len + 8) & ~7), ((Clen + 8) & ~7));
#endif

	/* Find out if we have to allocate new memory */
	/* Clen is total length in bytes while len is only strlen() */
	/* TODO: Ain't work yet */
	/*if ((((Clen + 8) & ~7) != ((len + 8) & ~7)))
	{*/
	    ptr = allocline (Clen + 1);

	    if (!ptr)
	    {
		nomemory ();
		break;
	    }

	    freeline (linelist[0]);
	    linelist[0] = ptr;
	/*}*/

	strcpy (ptr, Current);
    } /* for (t = start; t <= end; t ++, linelist ++) */

    SETF_MODIFIED(Ep,1);

    text_load ();

    /* Don't display if there is nothing to display */
    if (end < Ep->topline)
	return;

    if (start < Ep->topline)
    {
	start = 0;
	t = end - Ep->topline +1;
    }
    else
    {
	t = end - start +1;
	start -= Ep->topline;

	if (start >= Lines)
	    return;
    }

    if (start+t > Lines)
	t = Lines-start;

    if (!Nsu)
    {
	rp = Ep->win->RPort;

	end = start + t-1;

	/* only clear area, if there is a line outside the block */
	if (!is_inblock (start + Ep->topline, -1) ||
		!is_inblock (end + Ep->topline, -1))
	{
	    SetAPen (rp, TEXT_BPEN(Ep));
	    SetWrMsk (rp, BLOCK_MASK(Ep));
	    RectFill (rp, Xbase, ROW(start), Xpixs, ROW(start+t)-1);
	}

	if (!text_sync ())
	    text_displayseg (start, t);
    }
} /* do_indent */


/*DEFHELP #cmd textedit INSLINES n - insert @{B}n@{UB} lines at once */

DEFUSERCMD("inslines", 1, 0, void, do_inslines, (void),)
{
     int lines = atoi (av[1]);

     if (lines <= 0) return;

     while (lines)
     {
	  do_insline ();
	  lines --;
     }

} /* do_inslines */


/*DEFHELP DELETE - delete characters upto the next blank (if on a blank) or to the next non-blank (if on non-blank) */

DEFUSERCMD("delete", 0, CF_COK, void, do_delete, (void),)
{
    char * ptr, * hptr;

    hptr = ptr = Current + Ep->column;

    if (!*hptr)
	return;

    if (*hptr == ' ')
	while (*hptr == ' ') hptr ++;
    else
	while (*hptr != ' ') hptr ++;

    memmove (ptr, hptr, strlen (hptr)+1);

    text_sync ();
    text_redisplaycurrline ();
} /* do_delete */


/*DEFHELP #cmd textedit DELINES n - delete @{B}n@{UB} lines */

DEFUSERCMD("delines", 1, 0, void, do_delines, (void),)
{
     int t;

     t = atoi (av[1]);

     for ( ; t >= 0; t--)
	  do_deline ();
} /* do_delines */


/*
 *  Commands submitted by Markus Wenzel
 */

/*DEFHELP #cmd misc UNDELINE - insert most recently deleted line (only last line saved) */

DEFUSERCMD("undeline", 0, 0, void, do_undeline, (void),)
{
   do_insline ();
   text_load ();

   strcpy((char *)Current, (char *)Deline);

   text_sync ();
   text_displayseg (Ep->line - Ep->topline, 1);
} /* do_undeline */


/*DEFHELP #cmd block,clip CLIPINS - Insert current contents of clipboard in the text */

DEFUSERCMD("clipins", 0, CF_COK, void, do_clipins, (void),)
{
    struct IOClipReq * ior;
    struct cbbuf * buf;
    LONG    lines;
    BOOL    failed = 0;
    ED	  * ep	   = Ep;

    if (!(ior = CBOpen (0)) )
	return;

    if (CBQueryFTXT (ior))
    {
	buf = CBReadCHRS (ior);

	if (buf)
	{
	    UBYTE line[MAXLINELEN];
	    UBYTE * ptr1, * ptr2;
	    LINE ptr;
	    int len;

	    ptr2 = line;
	    ptr1 = buf->mem;

	    while (*ptr1 && *ptr1 != '\n')
		*ptr2 ++ = *ptr1 ++;

	    *ptr2 = 0;

	    text_write (line);

	    if (*ptr1)
	    {
		do_split ();
		do_down ();
		do_firstcolumn ();

		lines = ep->lines;

		for (ptr1 ++; *ptr1; )
		{
		    ptr2 = line;
		    len = 0;

		    while (*ptr1 && *ptr1 != '\n')
		    {
			*ptr2 ++ = *ptr1 ++;
			len ++;
		    }

		    *ptr2 = 0;
		    len ++;

		    if (*ptr1)
			ptr1 ++;
		    else
		    {
			text_write (line);
			break;
		    }

		    if (makeroom(256) && (ptr = allocline(len)))
		    {
			SETLINE(ep,ep->lines,ptr);
			ep->lines ++;

			movmem(line, ptr, len);
		    }
		    else
		    {
			set_window_params ();
			nomemory ();
			failed = 1;
			break;
		    }
		}

		if (!failed && lines <= ep->lines - 1)
		{
		    Block block;

		    /* INSFILE: move text to correct position */
		    block.ep	     = ep;
		    block.start_line = lines;
		    block.end_line   = ep->lines - 1;
		    block.type	     = BT_LINE;

		    displayblock (0);
		    set_block (&block);
		    do_bmove ();
		}
	    } /* more than one line */

	    window_title ();

	    CBFreeBuf (buf);
	}

	CBReadDone (ior);
    }

    CBClose (ior);
} /* do_clipins */


/*DEFHELP SPACE - inserts a single space no matter what insertmode is set to */

DEFUSERCMD("space", 0, CF_COK, void, do_space, (void),)
{
    ED * ep = Ep;
    int  insmode = GETF_INSERTMODE(ep);

    SETF_INSERTMODE(ep,1);
    text_write (" ");
    SETF_INSERTMODE(ep,insmode);
} /* do_space */


/*DEFHELP #cmd textedit,movement RETURN - if AUTOINDENT is off: (FIRST DOWNADD) else insert line, split current line and indent like last line above. */

DEFUSERCMD("return", 0, CF_COK, void, do_return, (void),)
{
    char   buf[MAXLINELEN];
    char * partial;

    if (GETF_COMLINEMODE(Ep))
    {
	strcpy (buf, (char *)Current);

	partial = esc_partial;	/* PATCH_NULL: P -> esc_p */
	esc_partial = NULL;	/* PATCH_NULL: P -> esc_p */

	escapecomlinemode ();

	if (partial)
	{
	    if (do_command (buf))
		do_command (partial);

	    free(partial);
	} else
	    do_command (buf);
    } else
    {
	if (GETF_AUTOINDENT(Ep))
	{   /* If user want's autoindent */
	    WORD   indent;  /* how much */
	    WORD   line;    /* Line-no. */
	    char * ptr;     /* pointer to Ep->list[line] */

	    text_sync ();   /* store actual line */

	    /* Start with current line */
	    indent = 0;
	    ptr = (char *)Current;

	    /* Find 1. nonspace ... */
	    while (isspace (*ptr))
	    {
		ptr ++;
		indent ++;
	    }

	    /* ... but not '\0'. In this case the line is empty and
	       indent is therefore 0 */
	    if (!*ptr)
		indent = 0;

	    /* Try on, if indent is zero, line is empty and there are more
	       lines on top */
	    if (!indent && !*ptr && Ep->line)
	    {
		line = Ep->line - 1;

		/* Until 1. line */
		while (line)
		{
		    /* Try every line */
		    ptr = GETTEXT(Ep,line);
		    indent = 0;

		    while (isspace (*ptr))
		    {
			ptr ++;
			indent ++;
		    }

		    if (indent || *ptr)
			break;

		    line --;
		}
	    }

	    /* Not at last position in line ? then we have to split and
	       indent the rest */
	    if (Ep->column != Clen)
	    {
		/* Split line, move to next line but shouldn't show this */
		do_split ();
		do_down ();

		/* Indent new line */
		if (indent > Ep->column)
		    indent -= indent - Ep->column;

		if (indent > 0)
		{
		    movmem (Current, Current+indent, Clen+1);
		    setmem (Current, indent, ' ');
		    Clen += indent;
		    Ep->column = indent;
		} else
		    Ep->column = 0;

		Current[Clen] = 0;

		/* and position cursor */
		text_sync ();

		/* Show result */
		text_redisplaycurrline ();
	    } else
	    {
		if (Ep->line != Ep->lines-1)
		{
		    do_down ();
		    do_insline ();

		    Ep->column = indent;
		    text_sync ();
		} else
		{
		    do_split ();
		    Ep->column = indent;
		    text_sync ();
		    do_down ();
		}
	    }
	} else
	{
	    Ep->column = 0;
	    text_sync ();
	    do_downadd ();
	}
    } /* !Comlinemode */
} /* do_return */


/******************************************************************************
*****  ENDE edit.c
******************************************************************************/
