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

    MODUL
	block.c

    DESCRIPTION
	In this module are all functions that work with blocks.

    NOTES
	- A block has these attributes:
	    1- the editor it is in
	    2- the type (BT_NONE, BT_LINE, BT_NORMAL or BT_VERTICAL)
	       if it is BT_NONE, there is no block.
	    3- a start-position (line/column). If the type is not BT_NONE,
	       this field is guranteed to contain a valid position.
	    4- an end-position. If the line is -1, the end-position is not
	       yet defined.

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

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


/**************************************
	    Globale Variable
**************************************/
Prototype USHORT is_inblock	 (Line, Column);
Prototype void	 displayblock	 (BOOL);
Prototype void	 redraw_block	 (BOOL, Line, Column, Line, Column);
Prototype void	 do_blocktype	 (void);
Prototype void	 do_block	 (void);
Prototype BOOL	 block_ok	 (void);
Prototype void	 do_copy	 (void);
Prototype char * block_to_string (void);
Prototype void	 do_bdelete	 (void);
Prototype void	 do_bcopy	 (void);
Prototype void	 do_bmove	 (void);
Prototype void	 do_bsource	 (void);


/**************************************
      Interne Defines & Strukturen
**************************************/
#define SWAP(a,b)   (void)((a)^=(b),(b)^=(a),(a)^=(b))
#define MIN(a,b)    ((a) <= (b) ? (a) : (b))
#define MAX(a,b)    ((a) >= (b) ? (a) : (b))

#define BF_LINESWAP	    1L
#define BF_COLUMNSWAP	    2L


/**************************************
	    Interne Variable
**************************************/
static USHORT block_type = BT_LINE;	/* default-type of block */


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


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

    NAME
	is_inblock

    PARAMETER
	Line   line;	    The line we want to check
	Column column;	    the column we are in. If column == -1,
			    we just check the line.

    RETURN
	USHORT blockpos;    Flags:

		    BP_OUTSIDE	    we are not inside any block.
		    BP_START	    we are in the line where the block starts.
		    BP_END	    we are in the line where the block ends.
		    BP_INSIDE	    we are really inside the block.

    DESCRIPTION
	This function checks whether a position is inside a block.
	If it isn't, we get BP_OUTSIDE (= 0). In the other case, we
	have several flags. If line is the start-line of the block,
	BP_START is set. The same goes for the end-line.
	    Normal and vertical blocks also check for the column.
	If we don't care for this, we may simply specify -1L for the
	column. Else, we give the column we want to check and get an
	additional BP_INSIDE if we are really inside the block.

    EXAMPLE

	BP_OUTSIDE	there is no block in that line

	BP_START	the line is the start-line of the block. Also the
			block has more than one line since BP_END is not
			set. The block is not a line-block (BP_INSIDE is
			always true for line-blocks !).

	BP_START|BP_END The block has only one line and we are in it. This is
			also no line-block.

	BP_START|BP_INSIDE We are inside the block and on it's start-line.

	BP_INSIDE	this position is inside the block, but not in the
			start- nor in the end-line.

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

USHORT is_inblock (Line line, Column col)
{
    USHORT blockpos = BP_OUTSIDE;

    /* Look if there is a block, the actual editor is the one that
       has it, we are not in a commandline and the line is inside it.
       NOTE: if there is no end-line yet, the last condition can never
       be TRUE ! */

    if (    ActualBlock.type != BT_NONE     &&
	    Ep == ActualBlock.ep	    &&
	    !globalflags.Comlinemode	    &&
	    line >= ActualBlock.start_line  &&
	    line <= ActualBlock.end_line     )
	{

	/* Check for start- and end-line */

	if (line == ActualBlock.start_line)
	    blockpos |= BP_START;

	if (line == ActualBlock.end_line)
	    blockpos |= BP_END;

	/* now check, if we are inside the block */

	switch (ActualBlock.type)
	{
	case BT_LINE:
	    /* in a line-block, we are always inside */
	    blockpos |= BP_INSIDE;
	break;

	case BT_NORMAL:
	{
	    BOOL after_start, before_end;

	    /* in a normal block, we must also check the column.
		In the startline, we are inside the block if we are to the
		left of the startcolumn. In the endline, we must be to
		the right. */
	    /* NOTE that we check the lines above ! */

	    if (line > ActualBlock.start_line ||
		    (col == -1 || col >= ActualBlock.start_column)
	       )
		after_start = TRUE;
	    else
		after_start = FALSE;

	    if (line < ActualBlock.end_line ||
		    (col == -1 || col <= ActualBlock.end_column ||
		     ActualBlock.end_column == (LINELEN(Ep,line)) )
	       )
		before_end = TRUE;
	    else
		before_end = FALSE;

	    if (after_start && before_end)
		blockpos |= BP_INSIDE;
	}
	break;

	case BT_VERTICAL:
	    /* in a vertical block, we must be between the start- and the
		end-column */

	    if ((     col  == -1                          ||
		    ( col  >= ActualBlock.start_column &&
		      col  <= ActualBlock.end_column ) ) &&
		      line >= ActualBlock.start_line  &&
		      line <= ActualBlock.end_line	  )
		blockpos |= BP_INSIDE;
	break;
	} /* switch (ActualBlock.type) */
    } /* if validblock */

    /* return the flags */

    return (blockpos);
} /* is_inblock */


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

    NAME
	displayblock

    PARAMETER
	BOOL on;	Shall we turn the block on ?

    RETURN
	void

    DESCRIPTION
	This function displays the block. If on is TRUE, the block
	is drawn (again), else we turn the block off. In this case, the
	block-selection is lost !

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

void displayblock (BOOL on)
{
    if (on)
    {
	/* draw block and write the text over it */
	redraw_block (TRUE,
		    Ep->topline, Ep->topcolumn,
		    Ep->topline + Lines - 1, Ep->topcolumn + Columns - 1);

    } else if (block_ok ()) /* turn block off AND there was a block */
    {
	ED   * ep = Ep;
	RP   * rp = ep->win->RPort;
	USHORT start;
	USHORT end;

	/* no block anymore */

	ActualBlock.type = BT_NONE;
	ActualBlock.ep	 = NULL;

	/* block is not visible */
	if (ActualBlock.end_line < ep->topline)
	    return;

	/* start-line: If block starts above the 1st visible line,
	   we start in line 0 else in the line where the block starts. */

	if (ActualBlock.start_line < ep->topline)
	    start = ep->topline;
	else
	    start = ActualBlock.start_line;

	/* is end-line visible ? */
	if (ep->topline + Lines > ActualBlock.end_line)
	    end = ActualBlock.end_line;
	else
	    /* only upto the end of the display */
	    end = ep->topline + Lines -1;

	if (start <= end)
	{
	    /* color and mask */

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

	    /* clear area */
	    RectFill (rp, Xbase, ROW(start - Ep->topline), Xpixs,
		    ROW(end-Ep->topline + 1)-1);

	    /* redraw text */
	    redraw_block (TRUE, start, ep->topcolumn,
		end, ep->topcolumn + Columns -1);
	}
    } else /* just say "there is no block" */
	ActualBlock.type = BT_NONE;

} /* displayblock */


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

    NAME
	do_blocktype

    PARAMETER
	av[0] : blocktype
	av[1] : LINE/charACTER/NORMAL/VERTICAL

    RETURN
	void

    DESCRIPTION
	This sets the type of block you want to use. Allowed are 3 types
	of blocks:

	    LINE:	The line-block begins in a line and ends in a line.
			The columns are ignored and both lines are included
			in the block.
	    CHARCTER or NORMAL: The character-block begins at a specifiy line
			and columns and ends at another character-position.
	    VERTICAL:	The vertical block is a rectangular region of text
			like a column of a table.

	If you have already marked a block, it is converted to the new type.

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

void do_blocktype (void)
{
    switch (av[1][0])
    {
    case 'l':
    case 'L':
	block_type = BT_LINE;
    break;

    case 'c':
    case 'C':
    case 'n':
    case 'N':
	block_type = BT_NORMAL;
    break;

    case 'v':
    case 'V':
	block_type = BT_VERTICAL;
    break;
    }

    /* check if there is a block and convert the type */
    if (block_ok() && ActualBlock.type != block_type)
    {
	/* set new type */
	ActualBlock.type = block_type;

	if (block_type == BT_NORMAL)
	{
	    int linelen;

	    /* check start and end-columns */
	    linelen = LINELEN(Ep,ActualBlock.start_line);

	    if (linelen < ActualBlock.start_column)
		ActualBlock.start_column = linelen;

	    linelen = LINELEN(Ep,ActualBlock.end_line);

	    if (linelen < ActualBlock.end_column)
		ActualBlock.end_column = linelen;
	}

	/* clean screen and force redraw */
	text_redisplay ();
    }
} /* do_blocktype */


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

    NAME
	do_block

    PARAMETER
	av[0] : block unblock bstart bend lineblock

    RETURN
	void

    DESCRIPTION
	This routine allows to specify the limits of a block.

	The two commands BSTART and BEND can be used anywhere in the
	text (ie. you can user BEND above a BSTART). In this case,
	BEND and BSTART swap their meanings, ie. BEND now sets the start
	of the block and BSTART the end. This is for specifying a block
	with the mouse. If you drag the mouse above the BSTART, the BEND
	will begin to set the beginning of the block and if you drag it
	down over the end of the block, BEND will switch back.

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

void do_block (void)
{
    ED * ep = Ep;

    /* make sure we work on the most recent version of the text */

    text_sync ();

    /* look for the 1st character */

    switch(av[0][0])
    {
	case 'b': /* Block Bstart Bend */
	    switch (av[0][1]) /* b[lse] */
	    {
		case 'l': /* BLock */
		    /* if there is no block yet, we set the start-line */

		    if (ActualBlock.type == BT_NONE)
		    {
bstart:
			/* set the editor, the start-line and the type. To
			   make clear the end-line is not valid yet, we set
			   it to -1 */

			ActualBlock.ep		 = ep;
			ActualBlock.type	 = block_type;
			ActualBlock.start_line	 = ep->line;
			ActualBlock.start_column = ep->column;
			ActualBlock.end_line	 = -1;
			ActualBlock.flags	 = 0;

			title ("Block begin");
		    } else
		    {
			/* if there is a block */

			if (block_ok ())
			{
			    /* shall we automagically unblock ? */

			    if (ep->config.autounblock)
			    {
				/* trun block off */

				text_redrawblock (0);

				/* set start */

				goto bstart;
			    } else
			    {
				/* no auto-unblock ? ERROR ! */

				error ("block:\nBlock already marked");

				break;
			    } /* autounblock */
			} /* if there is already a block */

			/* The last BLOCK began in another editor: Restart ! */

			if (ActualBlock.ep != ep)
			    goto bstart;

			ActualBlock.end_line   = ep->line;
			ActualBlock.end_column = ep->column;

			/* now, the block is ready ! */

			title ("Block end");

			text_redrawblock (1);       /* Now display */
		    }
		break;

		case 's': /* BStart */
		    /* no block yet or the old block was in another editor ? */
		    if (ActualBlock.type == BT_NONE || ActualBlock.ep != ep)
		    {
			/* is there an old block and the editor that contains
			   the block is not iconified */
			if (ActualBlock.type != BT_NONE &&
				!ActualBlock.ep->iconmode)
			{
			    /* change editor */
			    switch_ed (ActualBlock.ep);

			    /* switch block off */
			    text_redrawblock (0);

			    /* come back */
			    switch_ed (ep);
			}

			goto bstart;
		    }

		    if (ActualBlock.end_line != -1)
		    {
			redraw_block (FALSE,
				    ep->line,
				    ep->column,
				    -1, -1);
		    } else
		    {
			ActualBlock.start_line	 = ep->line;
			ActualBlock.start_column = ep->column;

			title ("Block begin");
		    }
		break;

		case 'e': /* bend */
		    /* It is not possible to use BEND if there is no
		       start yet or the block is in another editor */

		    if (ActualBlock.type == BT_NONE)
		    {
			error ("bend:\n You must use BLOCK or\nBSTART first");
			break;
		    } else if (ActualBlock.ep != ep)
		    {
			error ("bend:\nYou cannot set the end for\n"
			       "a block in another editor !");
			break;
		    }

		    /* say what we do */
		    title ("Block end");

		    /* if there was no end yet, just set it and force
			a redisplay */

		    if (ActualBlock.end_line == -1)
		    {
			ActualBlock.end_line   = ep->line;
			ActualBlock.end_column = ep->column;

			text_redrawblock (1);   /* Now display */
		    } else
		    {
			redraw_block (FALSE,
				    -1, -1,
				    ep->line, ep->column);
		    }
		break;
	    }
	break;

	case 'u':
	    /* force UNDRAW and say what we did */

	    text_redrawblock (0);

	    title ("Block unmarked");
	break;

	case 'l':
	    /* mark the whole line as a block */

	    ActualBlock.ep	     = ep;
	    ActualBlock.type	     = BT_NORMAL;
	    ActualBlock.start_line   =
		ActualBlock.end_line = ep->line;
	    ActualBlock.start_column = 0;
	    ActualBlock.end_column   = Clen ();
	    ActualBlock.flags	     = 0;

	    title ("Line marked");

	    /* force redraw */

	    text_redrawblock (1);
	break;
    }
} /* do_block */


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

    NAME
	block_ok

    PARAMETER
	void

    RETURN
	BOOL

    DESCRIPTION
	Returns TRUE, if there is a valid block.

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

BOOL block_ok (void)
{
    /* A block is valid, if it has a type and a valid end-line */

    return (ActualBlock.type != BT_NONE && ActualBlock.end_line >= 0);
} /* block_ok */


void test (void)
{
} /* test */


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

    NAME
	block_to_string

    PARAMETER
	void

    RETURN
	char * string;

    DESCRIPTION
	Creates a string from the currently marked block. The lines
	are separated with '\n' and the string is terminated with
	'\0'.

    NOTES
	- The string will only contain '\n's, if the end of a line is
	  included in the block.
	- The block has to be freed with free() if you don't need it anymore,
	  but this may change.

    HISTORY
	13. Feb 1993	ada created

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

void do_copy (void)
{
    struct IOClipReq * ior;
    char * str = block_to_string ();

    if (str)
    {
	D(bug("block_to_string:\n`%s'\n", str));

	if (ior = CBOpen (0))
	{
	    CBWriteFTXT (ior, str);

	    CBClose (ior);
	}

	free (str);
    }
    else
	D(bug("block_to_string: (NUL)\n"));
} /* do_copy */


char * block_to_string (void)
{
    ED	 * ep;
    Line   line;
    int    length;
    int    linelen;
    char * string,
	 * ptr,
	 * text;

    if (block_ok ())
    {
	ep = ActualBlock.ep;

	/* to prevent us from write the same code twice (once for
	   figuring out how long the string will be and once for actually
	   filling the string), we simply check if "string" is NULL.
	   If it is, we have to get the length else we copy the contents. */

	string = NULL;

	/*
	   LINE: The length of the block is the sum of the lengths of all
		lines + CR + \0.

	   NORMAL: The length of this block is the sum of the lengths of all
		body lines plus the length of the start- and the end-line
		plus \0. The length of the start-line is

		    o 1, if length < start_column
		    o length-start_column+1, if length > start_column

		and the length of the end-line is

		    o end_column+1, if length > end_column
		    o length+1, if length < end_column

		If the block has only one line, the length is

		    o end_column-start_column if length > end_col
		    o length-start_column+1 if length < end_col

	   VERTICAL: The length of the block is the sum of the lengths of
		all lines + CR + \0. The length of a line is

		    o 0 if the line is < start-column
		    o length-start_column if the line is < end-column
		    o start_column-end_column if line is >= end-column

	   When we have the length, we allocate memory for it and copy
	   the block into that memory.
	 */

	length = 1;	/* ending '\0' */

	text_sync ();   /* use latest text */

	do {
	    for (line=ActualBlock.start_line; line <= ActualBlock.end_line;
								       line ++)
	    {
		text = GETTEXT (ep,line);
		linelen = strlen (text);

		if (linelen > 256)
		{
		    D(bug("block_to_string: Aborting..."
			    "len (%ld) in line %ld too large !\n",
			    linelen, line));
		    break;
		}

		if (string && ptr-string > length)
		    break;

		switch (ActualBlock.type)
		{
		case BT_LINE:
		    {
			if (string)
			{
			    strcpy (ptr, text);
			    ptr += linelen;

			    *ptr ++ = '\n';     /* always add LF */
			}
			else
			    length += linelen + 1;
		    }
		break;

		case BT_NORMAL:
		    if (line != ActualBlock.start_line)
		    {
			if (line != ActualBlock.end_line)
			{
			    /* body line */

			    if (string)
			    {
				strcpy (ptr, text);
				ptr += linelen;

				*ptr ++ = '\n';     /* always add LF */
			    }
			    else
				length += linelen + 1;
			}
			else
			{
			    /* end-line */
			    if (string)
			    {
				strncpy (ptr, text, ActualBlock.end_column);
				ptr += ActualBlock.end_column;
			    }
			    else
				length += ActualBlock.end_column;

			    if (linelen == ActualBlock.end_column)
			    {
				if (string)
				    *ptr ++ = '\n';
				else
				    length ++;
			    }
			}
		    }
		    else if (line != ActualBlock.end_line)
		    {
			/* start-line */
			if (linelen > ActualBlock.start_column)
			{
			    if (string)
			    {
				strcpy (ptr, text + ActualBlock.start_column);
				ptr += strlen (ptr);
			    }
			    else
				length += linelen - ActualBlock.start_column;
			}

			if (string)
			    *ptr ++ = '\n';
			else
			    length ++;
		    }
		    else /* block has only one line */
		    {
			if (linelen > ActualBlock.end_column)
			{
			    /* copy part of the line */

			    linelen = ActualBlock.end_column -
					    ActualBlock.start_column + 1;

			    if (string)
			    {
				strncpy (ptr, text + ActualBlock.start_column,
					linelen);
				ptr += linelen;
			    }
			    else
				length += linelen;
			}
			else if (linelen > ActualBlock.start_column)
			{
			    /* end of line + CR */
			    if (string)
			    {
				strcpy (ptr, text + ActualBlock.start_column);
				ptr += strlen (ptr);

				*ptr ++ = '\n';
			    }
			    else
			    {
				linelen -= ActualBlock.start_column;

				length += linelen + 1;
			    }
			}
			else
			{
			    /* only CR */

			    if (string)
				*ptr ++ = '\n';
			    else
				length ++;
			}
		    }
		break;

		case BT_VERTICAL:
		{
		    if (linelen <= ActualBlock.start_column)
		    {
			/* only '\n' */
			if (string)
			    *ptr ++ = '\n';
			else
			    length ++;
		    }
		    else if (linelen <= ActualBlock.end_column)
		    {
			if (string)
			{
			    strcpy (ptr, text + ActualBlock.start_column);
			    ptr += strlen (ptr);

			    *ptr ++ = '\n';
			}
			else
			    length += linelen - ActualBlock.start_column + 2;
		    }
		    else /* line is longer than block wide */
		    {
			linelen = ActualBlock.end_column -
						ActualBlock.start_column + 1;

			if (string)
			{
			    strncpy (ptr, text + ActualBlock.start_column,
				    linelen);
			    ptr += linelen;

			    *ptr ++ = '\n';
			}
			else
			    length += linelen;
		    }
		}
		break;
		} /* switch */
	    }

	    if (string && ptr-string > length)
	    {
		D(bug("Aborting ... String exceeds "
		      "(%ld:%ld) )buffer in line %ld !\n",
			ptr-string, length, line));
	    }

	    if (string)
		break;	/* done ! */
	    else
	    {
		string = malloc (length+256);   /* allocate memory */
		ptr = string;		    /* points to EOS */
	    }

	/* after the loop, the string must be non-NULL, else we didn't get the
	   memory ! */
	} while (string != NULL);

	if (string) /* add '\0' */
	{
	    *ptr = 0;

	    if (length != strlen (string)+1)
	    {
		D(bug("Fehler: length: %ld  strlen: %ld\n",
			length, strlen(string)+1));
	    }
	}
    }
    else
	string = NULL;

    return (string);
} /* block_to_string */


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

    NAME
	do_bdelete

    PARAMETER
	void

    RETURN
	void

    DESCRIPTION
	Deletes the marked block from the text.

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

void do_bdelete (void)
{
    long  lines;
    ED	* bep	  = ActualBlock.ep;
    ED	* saveed  = Ep;
    ARRAY old;

    /* TODO */
    if (ActualBlock.type != BT_LINE)
    {
	if (block_ok ())
	    error ("bdelete:\nCannot use for this\ntype of block.");

	return;
    }

    /* don't allow it in VIEWMODE */

    if (bep->viewmode)
    {
	error ("%s:\nCannot delete from a\nwrite-protected text", av[0]);
	return;
    } /* if */

    /* change editor */

    switch_ed (bep);

    /* length of block */

    lines = ActualBlock.end_line - ActualBlock.start_line + 1;

    /* if line is inside the block, move to start of block. If it is
	beyond the end-line, move up */

    if (bep->line >= ActualBlock.start_line && bep->line <= ActualBlock.end_line)
	bep->line = ActualBlock.start_line;
    else if (bep->line > ActualBlock.end_line)
	bep->line -= lines;

    /* same for the 1st line */

    if (bep->topline >= ActualBlock.start_line && bep->topline <= ActualBlock.end_line)
	bep->topline = ActualBlock.start_line;
    else if (bep->topline > ActualBlock.end_line)
	bep->topline -= lines;

    /* free memory and copy lines from beyond the block back */

    if (old = CutArray (bep->linearray, ActualBlock.start_line, lines))
    {
	/* UNDO here ! */
	FreeArray (old);
    }

    /* adjust number of lines. Text has been modified */

    bep->modified = 1;

    /* move cursor if it's now outside the text */

    if (bep->line >= NUMLINES(bep))
	bep->line = NUMLINES(bep) - 1;

    /* move topline if it's now outside the text */

    if (bep->topline >= NUMLINES(bep))
	bep->topline = NUMLINES(bep) - 1;

    /* no lines in text ? */

    if (bep->line < 0)
	bep->topline = bep->line = 0;

    /* create at least one line */

    if (NUMLINES(bep) == 0)
	SETLINE(bep, 0, allocline(1));

    /* get current line */
    text_load ();

    /* no block anymore */
    ActualBlock.type = BT_NONE;

    /* always redisplay */
    text_adjust (TRUE);

    /* adjust scroller */
    prop_adj ();

    /* old editor */
    switch_ed (saveed);

} /* do_bdelete */


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

    NAME
	do_bcopy

    PARAMETER
	void

    RETURN
	void

    DESCRIPTION
	Copies a block to the line above the cursor. It is possible to
	copy the block into itself thus making it bigger.

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

void do_bcopy (void)
{
    long    lines;
    ED	  * ep = Ep;
    ARRAY   copy;

    /* update line */

    text_sync ();

    /* TODO */
    if (ActualBlock.type != BT_LINE)
    {
	if (block_ok ())
	    error ("bcopy:\nCannot use for this\ntype of block.");

	return;
    }

    /* not allowed in viewmode */

    if (ep->viewmode)
    {
	error ("%s:\nCannot move from a\nwrite-protected text", av[0]);
	return;
    } /* if */

    /* get length of block */

    lines = ActualBlock.end_line - ActualBlock.start_line + 1;

    /* make sure we have enough space */

    if (copy = CopyArray (ActualBlock.ep->linearray, ActualBlock.start_line,
									lines))
    {
	ARRAY new;

	if (new = InsertArray (ep->linearray, copy, ep->line))
	{
	    ep->linearray = new;

	    /* Free empty array */
	    SetArrayTags (copy, AR_NumElements, 0, TAG_DONE);
	    FreeArray (copy);

	    /* if we inserted the block before the start of the block,
	       we must adjust it */

	    if (ep == ActualBlock.ep)
	    {
		if (ep->line <= ActualBlock.start_line)
		{
		    ActualBlock.start_line += lines;
		    ActualBlock.end_line   += lines;
		} else if (ep->line <= ActualBlock.end_line)
		    ActualBlock.end_line   += lines;
	    }

	    /* Text has been modified. Also the number of lines has changed. */

	    ep->modified = 1;

	    /* Move cursor to the bottom */

	    ep->line += lines;

	    /* get new contents */

	    text_load ();

	    if (!text_adjust (FALSE)) /* display is adjusted, if needed */
	    {
		scroll_display (0, -lines, ep->topcolumn, ep->line-lines,
			ep->topcolumn+Columns, ep->topline+Lines-1);
	    }

	    /* adjust scroller */
	    prop_adj ();
	}
	else
	    nomemory ();
    } else
	nomemory ();

} /* do_bcopy */


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

    NAME
	do_bmove

    PARAMETER
	void

    RETURN
	void

    DESCRIPTION
	Moves the marked block between the current line and the line
	above it. It's not possible to move the block into itself.

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

void do_bmove (void)
{
    long   lines;
    ED	 * ep = Ep;
    ARRAY  block;
    ARRAY  new;

    /* update text */

    text_sync ();

    /* TODO */
    if (ActualBlock.type != BT_LINE)
    {
	if (block_ok ())
	    error ("bmove:\nCannot use for this\ntype of block.");

	return;
    }

    /* not allowed in viewmode */

    if (ActualBlock.ep->viewmode)
    {
	error ("%s:\nCannot move from a\nwrite-protected text", av[0]);
	return;
    } /* if */

    /* it is not very usefull to move a block into itself */

    if (    ActualBlock.ep == ep                &&
	    ep->line >= ActualBlock.start_line	&&
	    ep->line <= ActualBlock.end_line	)
    {
	error ("bmove:\nCannot move a\nblock into itself");
	return;
    }

    /* length of block */

    lines = ActualBlock.end_line - ActualBlock.start_line + 1;

    /* get a copy of the block */

    if (!(block = CutArray (ActualBlock.ep->linearray, ActualBlock.start_line,
								      lines)) )
    {
	nomemory ();
	return;
    }

    /* text has been modified */
    ActualBlock.ep->modified = ep->modified = 1;

    /* are we in the editor that has the block or do we have to move the
       block to some other editor ? */
    if (new = InsertArray (ep->linearray, block, ep->line))
    {
	ep->linearray = new;

	/* load (new) current line */
	text_load ();
    }
    else
    {
	nomemory ();
	return;
    }

    if (ep != ActualBlock.ep)
    {	/* move block between editors */

	/* the current line in the editor that had the block was inside
	   the block */
	if (    ActualBlock.ep->line >= ActualBlock.start_line  &&
		ActualBlock.ep->line <= ActualBlock.end_line	)
	{
	    /* move cursor to the place where the block started */
	    ActualBlock.ep->line = ActualBlock.start_line;

	    /* if the block was the last thing in this text, move up
		one more line */
	    if (ActualBlock.ep->line == NUMLINES(ActualBlock.ep))
		ActualBlock.ep->line --;
	}

	/* if the line was behind the last line of the block, move up
	    the length of the block in lines. */
	if (ActualBlock.ep->line > ActualBlock.end_line)
	    ActualBlock.ep->line -= lines;

	/* ... but not too far */
	if (ActualBlock.ep->line < 0)
	    ActualBlock.ep->line = 0;

	/* no block anymore */
	ActualBlock.type = BT_NONE;

	/* if we moved the whole text, make sure the old editor
	    al least contains one single line */
	if (NUMLINES(ActualBlock.ep) == 0)
	{
	    /* this does always work ! */
	    SETLINE(ActualBlock.ep, 0, allocline (1));
	}

	ep->line += lines;  /* Move cursor down */

	text_load ();       /* text_switch() calls text_sync() ! */
	switch_ed (ActualBlock.ep); /* make source to the active editor */

	if (!Ep->iconmode)
	{	 /* Not iconified */
	    text_adjust (TRUE);

	    /* adjust scroller */
	    prop_adj ();
	}

	switch_ed (ep);             /* old Ed */
    } /* block was in another editor */

    ActualBlock.type = BT_NONE;    /* We don't have any block anymore */

    /* force redisplay */
    text_adjust (TRUE);
} /* do_bmove */


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

    NAME
	do_bsource

    PARAMETER
	void

    RETURN
	void

    DESCRIPTION
	Executes all lines inside a block as if they were typed as commands.
	The block is removed before the execution starts. The text
	is updated before every line. Thus self-modifying code is possible.

    NOTE
	- since the start and end lines are loaded immediately and the
	  block unblock'd before execution starts, you can theoretically have
	  another BSOURCE as part of this BSOURCE (but be carefull!).
	- BSOURCE allows self-modifying code

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

void do_bsource (void)
{
    ED	 * ep;
    char * buf;
    int    i, start, end, len;

    /* TODO */
    if (ActualBlock.type != BT_LINE)
    {
	if (block_ok ())
	    error ("bsource:\nCannot use for this\ntype of block.");

	return;
    }

    /* find start and end */

    ep	  = ActualBlock.ep;
    start = ActualBlock.start_line;
    end   = ActualBlock.end_line + 1;

    /* unblock */

    text_redrawblock (0);

    /* execute every line */

    buf = NULL;

    for (i = start; i < end; i ++)
    {
	text_sync (); /* make sure we are using the latest text */

	if (*(GETTEXT(ep,i)) != '#')
	{
	    if (buf)
		free (buf);

	    if (buf = malloc (len=LINELEN(ep,i)))
	    {
		strncpy (buf, GETTEXT(ep,i), len);
		buf[len] = 0;

		if (!do_command (buf))
		    break;
	    } /* if enough memory for a copy */
	} /* if no comment */
    } /* for all lines */

    if (buf)
	free (buf);

} /* do_bsource */


/******************************************************************************
*****  ENDE block.c
******************************************************************************/
