!+
!	RECCUTPAS.TPU - Eve version of  rectangular cut and paste
!-
!
! TPU emulation of  rectangular CUT/PASTE including following routines:
!	EVE_DRAW_BOX
!	EVE_RECTANGULAR_REMOVE
!	EVE_RECTANGULAR_INSERT_HERE
!	EVE_RECTANGULAR_SELECT
!	EVEPLUS_PAD_BLANK
!	EVE_SET_RECTANGULAR
!	EVE_SET_NORECTANGULAR
!	EVEPLUS_SET_MODE
!	EVEPLUS_BLANK_CHARS
!	EVEPLUS_ADVANCE_HORIZONTAL
!
! Rectangular CUT/PASTE provides a way to select a corner of a rectangular
! region on the screen that is to be CUT.  This select point is highlighted
! in reverse video.  The cursor can then be positioned to the opposite
! corner of the box at which point the CUT can be done to place the rectangular
! region in paste_buffer.  PASTE can then be done to overstrike the
! rectangular region in paste_buffer onto the current_buffer using the
! current position as the upper left corner for the pasted region.  Note
! that no provision is made if there are TAB chars in the current buffer.
! Also, no provision is made if the cut or paste is done with part of the
! region to be cut or pasted over not being visible on the screen.
!
! These procedures can be run with the current buffer set to overstrike
! or insert mode - CUT/PASTE need to switch to insert mode temporarily
! to get the chars replaced properly, but the previous mode setting for
! the current buffer is restored when either the cut or paste routine completes.
!
! GLOBAL VARIABLES created/used
!	eveplus_v_begin_select -	position where selected region begins
!	eve$x_vt200_keypad
!
! GLOBAL VARIABLES used
!	current_buffer
!	paste_buffer
!
! This TPU file rebinds the SELECT/REMOVE/INSERT HERE keys to the included
! routines and initializes the eveplus_v_begin_select variable when the
! eve_set_rectangular procedure is executed.  The standard Eve key bindings
! are restored when the eve_set_norectangular procedure is executed.
!

!+
!   Procedure to calculate the current column from the current offset, treating
!   TAB characters as up to 8 blanks.
!-
PROCEDURE edd_current_column
LOCAL
    i,
    line,
    col;

line := current_line;
IF INDEX(line,ASCII(9)) = 0
THEN
    edd_current_column := current_offset
ELSE
    i := 1;
    col := 0;
    LOOP
	EXITIF i > current_offset;
	IF SUBSTR(line,i,1) = ASCII(9)
	THEN
	    col := ((col + 8)/8)*8
	ELSE
	    col := col + 1
	ENDIF;
	i := i + 1
    ENDLOOP;
    edd_current_column := col
ENDIF
ENDPROCEDURE

!+
!   Procedure to replace TAB characters by the appropriate number of
!   blanks on the current line, then pad the line out to a given length, if it
!   is shorter.  The routine assumes overstrike mode is in
!   effect.  It leave the current position at the beginning of the line.
!-
PROCEDURE edd_replace_tabs_with_blanks_and_pad(target_length)
LOCAL
    i,
    col,
    cur_length,
    new_line,
    eight_blanks;

!+
!   Make sure we're not on the EOB marker.
!-
IF MARK(NONE) <> END_OF(CURRENT_BUFFER)
THEN
    IF INDEX(CURRENT_LINE, ASCII(9)) <> 0
    THEN
	new_line := '';
	eight_blanks := "        ";
	i := 1;
	col := 0;
	LOOP
	    EXITIF i > LENGTH(CURRENT_LINE);
	    IF SUBSTR(CURRENT_LINE,i,1) = ASCII(9)
	    THEN
		col := ((col + 8)/8)*8;
		new_line := new_line + SUBSTR(eight_blanks,1,col-LENGTH(new_line))
	    ELSE
		new_line := new_line + SUBSTR(CURRENT_LINE,i,1);
		col := col + 1
	    ENDIF;
	    i := i + 1
	ENDLOOP;

	MOVE_HORIZONTAL(-CURRENT_OFFSET);
	COPY_TEXT(new_line)
    ENDIF
ENDIF;

MOVE_HORIZONTAL(-CURRENT_OFFSET);

!+
!   Now pad out the line if we have to
!-
IF MARK(NONE) = END_OF(CURRENT_BUFFER)
THEN
    cur_length := 0
ELSE
    cur_length := LENGTH(CURRENT_LINE)
ENDIF;

IF cur_length < target_length
THEN
    MOVE_HORIZONTAL(cur_length);
    COPY_TEXT(eveplus_blank_chars(target_length - cur_length));
ENDIF;

MOVE_HORIZONTAL(-CURRENT_OFFSET)
ENDPROCEDURE


PROCEDURE eve_draw_box
    LOCAL 
	saved_mode,
	end_column,
	start_column,
	temp,
	end_select,
	top_bottom_text;

    !+
    !   Check for no select active
    !-
    IF eveplus_v_begin_select = 0
    THEN
	MESSAGE("Select not active");
	RETURN
    ENDIF;

    !+
    !  Set INSERT mode
    !-
    saved_mode := eveplus_set_mode(INSERT);

    !+
    !   Make sure there is a character at the corner of the box opposite
    !   the begin_select mark.  If the end_select mark is before the
    !   begin_select mark, juggle the markers so that begin_select precedes
    !   end_select.
    !-
    eveplus_pad_blank;
    IF MARK(NONE) >= eveplus_v_begin_select
    THEN
	end_select := MARK(NONE)
    ELSE
	end_select := eveplus_v_begin_select;
	eveplus_v_begin_select := MARK(NONE);
	POSITION(end_select)
    ENDIF;

    !+
    !   Figure out what column the box ends in and set END_COLUMN there.
    !   Then, clear out the video on EVEPLUS_V_BEGIN_SELECT.  Figure out
    !   the start column.
    !-
    end_column := edd_current_column;
    POSITION(eveplus_v_begin_select);
    eveplus_v_begin_select := MARK(NONE);
    start_column := edd_current_column;

    !+
    !   We may have the upper right and lower left corners of the box
    !   selected.  If so, START_COLUMN and END_COLUMN need to be reversed.
    !-
    IF start_column > end_column
    THEN
	temp := end_column;
	end_column := start_column;
	start_column := temp
    ENDIF;

    !+
    !   We may be building the box on the first line of the buffer.  In
    !   that case, we must put a new top line in the buffer.
    !-
    MOVE_HORIZONTAL(-CURRENT_OFFSET);
    IF MARK(NONE) = BEGINNING_OF(CURRENT_BUFFER)
    THEN
	SPLIT_LINE;
	POSITION(BEGINNING_OF(CURRENT_BUFFER));                    
	COPY_TEXT(eveplus_blank_chars(start_column));
	MOVE_VERTICAL(1);
	MOVE_HORIZONTAL(-CURRENT_OFFSET)
    ENDIF;

    !+
    !   Move back one line and put in the top line of the box
    !-
    top_bottom_text := '+' + eveplus_blank_chars(end_column-start_column+1) +
	'+';
    TRANSLATE(top_bottom_text, "-", " ");
    SET(OVERSTRIKE, current_buffer);
    MOVE_VERTICAL(-1);

    !+
    !   Replace all TABs with blanks on this line and pad it, if we need to.
    !-
    edd_replace_tabs_with_blanks_and_pad(end_column + 1);

    IF start_column <> 0
    THEN
	MOVE_HORIZONTAL(start_column - 1)
    ENDIF;

    COPY_TEXT(top_bottom_text);
    MOVE_VERTICAL(1);
    MOVE_HORIZONTAL(-CURRENT_OFFSET);

    !+
    !   Step through the selected lines, putting vertical bars on either side
    !   of the selected text.
    !-
    LOOP
	EXITIF MARK(NONE) > end_select;

	!+
	!   Replace all TABs with blanks on this line, if we need to.
	!-
	edd_replace_tabs_with_blanks_and_pad(end_column + 1);

	!+
	!   If START_COLUMN is zero, we must insert a vertical bar to do the
	!   left column, then put the right vertical bar one column farther out
	!   than normal.
	!-
	IF start_column = 0
	THEN
	    SET(INSERT, CURRENT_BUFFER);
	    COPY_TEXT("|");
	    SET(OVERSTRIKE, CURRENT_BUFFER);
	    MOVE_HORIZONTAL(end_column + 1);
	ELSE
	    MOVE_HORIZONTAL(start_column-1);
	    COPY_TEXT("|");
	    MOVE_HORIZONTAL(end_column - CURRENT_OFFSET + 1)
	ENDIF;

	COPY_TEXT("|");
	MOVE_HORIZONTAL(-CURRENT_OFFSET);
	MOVE_VERTICAL(1)
    ENDLOOP;

    !+
    !   Now put in the bottom line of the box.
    !-

    !+
    !   Replace all TABs with blanks on this line, if we need to.
    !-
    edd_replace_tabs_with_blanks_and_pad(end_column + 1);
    IF start_column <> 0
    THEN
	MOVE_HORIZONTAL(start_column - 1)
    ENDIF;

    COPY_TEXT(top_bottom_text);

    !+
    !   Position to the beginning of the cut area, reset BEGIN_SELECT,
    !   restore old insert/overstrike setting
    !-
    POSITION(eveplus_v_begin_select);
    eveplus_v_begin_select := 0;
    MOVE_HORIZONTAL(-CURRENT_OFFSET);
    IF start_column = 0
    THEN
	MOVE_HORIZONTAL(1)
    ELSE
	MOVE_HORIZONTAL(start_column)
    ENDIF;

    SET(saved_mode, CURRENT_BUFFER)
ENDPROCEDURE

PROCEDURE eve_rectangular_remove
    LOCAL 
	saved_mode,
	end_select,
	end_column,
	start_column,
	temp,
	pad_chars,
	save_position,
	blank_chars,
	cut_text;

    !+
    !   Check for no select active
    !-
    IF eveplus_v_begin_select = 0
    THEN
	MESSAGE("Select not active");
	RETURN
    ENDIF;

    !+
    !   Set INSERT mode and erase PASTE_BUFFER
    !-
    saved_mode := eveplus_set_mode(INSERT);
    ERASE(paste_buffer);

    !+
    !   Make sure there is a character at the corner of the box opposite
    !   the begin_select mark.  If the end_select mark is before the
    !   begin_select mark, juggle the markers so that begin_select precedes
    !   end_select.
    !-
    eveplus_pad_blank;
    IF MARK(NONE) >= eveplus_v_begin_select
    THEN
	end_select := MARK(NONE)
    ELSE
	end_select := eveplus_v_begin_select;
	eveplus_v_begin_select := MARK(NONE);
	POSITION(end_select)
    ENDIF;

    !+
    !   Figure out what column the box ends in and set END_COLUMN there.
    !   Then, clear out the video on EVEPLUS_V_BEGIN_SELECT.  Figure out
    !   the start column.
    !-
    end_column := edd_current_column;
    POSITION(eveplus_v_begin_select);
    eveplus_v_begin_select := MARK(NONE);
    start_column := edd_current_column;

    !+
    !   We may have the upper right and lower left corners of the box
    !   selected.  If so, START_COLUMN and END_COLUMN need to be reversed.
    !-
    IF start_column > end_column
    THEN
	temp := end_column;
	end_column := start_column;
	start_column := temp
    ENDIF;

    !+
    !   Get a string of the appropriate number of blanks to paste back in
    !-
    pad_chars := eveplus_blank_chars(end_column - start_column + 1);

    !+
    !   Step through the selected lines, copying the text to the paste buffer
    !   and replacing it with blanks as we go.  Replace all TABs with blanks
    !   before we look at it so we get the columns straight.
    !-
    MOVE_HORIZONTAL(-current_offset);
    SET(OVERSTRIKE, current_buffer);
    LOOP
	EXITIF MARK(NONE) > end_select;

	!+
	!   Replace all TABs with blanks on this line, if we need to.
	!-
	edd_replace_tabs_with_blanks_and_pad(end_column + 1);

	!+
	!   Obtain the text we're cutting
	!-
	cut_text := SUBSTR(CURRENT_LINE, start_column + 1,
	    end_column - start_column + 1);

	!+
	!   Replace the text with blanks
	!-
	MOVE_HORIZONTAL(start_column);
	COPY_TEXT(pad_chars);

	!+
	!   Copy the text to the paste buffer
	!-
	save_position := MARK(NONE);
	POSITION(paste_buffer);
	COPY_TEXT(cut_text);
	MOVE_HORIZONTAL(1);

	!+
	!   Reposition to the other buffer and move to the next line
	!-
	POSITION(save_position);
	MOVE_HORIZONTAL(-CURRENT_OFFSET);
	MOVE_VERTICAL(1)
    ENDLOOP;

    !+
    !   Position to the beginning of the cut area, reset BEGIN_SELECT,
    !   restore old insert/overstrike setting
    !-
    POSITION(eveplus_v_begin_select);
    eveplus_v_begin_select := 0;
    MOVE_HORIZONTAL(-CURRENT_OFFSET);
    MOVE_HORIZONTAL(start_column);
    SET(saved_mode, CURRENT_BUFFER)
ENDPROCEDURE

PROCEDURE eve_rectangular_insert_here
!+
!   This procedure pastes the rectangular region in the paste buffer
!   using the current position in the current buffer as the upper left corner.
!-
    LOCAL
	save_position,
	start_column,
	paste_line,
	save_buffer,
	save_mode;

    save_buffer := CURRENT_BUFFER;
    save_position := MARK(NONE);
    start_column := edd_current_column;
    save_mode := eveplus_set_mode(OVERSTRIKE);
    POSITION(BEGINNING_OF(paste_buffer));
    IF MARK(NONE) = END_OF(paste_buffer)
    THEN
	MESSAGE("Paste buffer is empty");
	RETURN
    ENDIF;

    !+
    !   Loop through lines in the paste buffer, putting them at the
    !   appropriate offset in the current buffer.
    !-
    LOOP
	EXITIF MARK(NONE) = END_OF(paste_buffer);

	!+
	!   Get the current line of the paste buffer.
	!-
	paste_line := CURRENT_LINE;
	MOVE_VERTICAL(1);

	!+
	!   Convert tabs to blanks on the line in the current buffer.
	!-
	POSITION(save_buffer);
	edd_replace_tabs_with_blanks_and_pad(start_column+1);

	!+
	!   Position at the correct offset and overwrite the text there.
	!-
	MOVE_HORIZONTAL(start_column);
	COPY_TEXT(paste_line);
	MOVE_VERTICAL(1);
	POSITION(paste_buffer)
    ENDLOOP;

    !+
    !   Position to start of pasted text and restore old mode setting.
    !-
    POSITION(save_position);
    MOVE_HORIZONTAL(-CURRENT_OFFSET);
    MOVE_HORIZONTAL(start_column);
    SET(save_mode, CURRENT_BUFFER)
ENDPROCEDURE

PROCEDURE EVE_RECTANGULAR_SELECT
    if eveplus_v_begin_select = 0
    then
	eveplus_pad_blank;
	eveplus_v_begin_select := mark(REVERSE);
	message("Selection started.  Press Remove when finished.");
    else
	eveplus_v_begin_select := 0;
	message("Selection cancelled");
    endif;
endprocedure	! eve_rectangular_select

PROCEDURE EVEPLUS_PAD_BLANK

!+
! This procedure drops a space at the current position if the current
! character is null so that any mark will be for an existing character.
! In EDD, we really want a mark in a particular screen column.  In TPU,
! an EOL mark would move if the line were extended.  Also in EDD, we
! want to highlight the select point so we need a character there.
! The cursor is returned to its original position after the space is
! copied to the current position in the current buffer.
!-
    IF MARK(NONE) = END_OF(CURRENT_BUFFER)
    THEN
	copy_text(" ");
	move_horizontal(-1)
    ELSE
	if current_character = ""
	then
	    copy_text(" ");
	    move_horizontal(-1);
	endif
    ENDIF
endprocedure	! eveplus_pad_blank

Procedure EVE_SET_RECTANGULAR
eveplus_v_begin_select := 0;
if eve$x_vt200_keypad then
    define_key("eve_rectangular_remove", e3, "edd_remove");
    define_key("eve_rectangular_insert_here", e2, "edd_insert_here");
    define_key("eve_rectangular_select", e4, "edd_select");
else
    define_key("eve_rectangular_remove", kp8, "edd_remove");
    define_key("eve_rectangular_insert_here", kp9, "edd_insert_here");
    define_key("eve_rectangular_select", kp7, "edd_select");
endif;
endprocedure

Procedure EVE_SET_NORECTANGULAR
eveplus_v_begin_select := 0;
if eve$x_vt200_keypad then
    define_key("eve_remove", e3, "remove");
    define_key("eve_insert_here", e2, "insert_here");
    define_key("eve_select", e4, "select");
else
    define_key("eve_remove", kp8, "remove");
    define_key("eve_insert_here", kp9, "insert_here");
    define_key("eve_select", kp7, "select");
endif;
endprocedure

PROCEDURE EVEPLUS_SET_MODE(new_mode)

!+
! This procedure returns the current mode for the current buffer
! and sets it to the value in NEW_MODE.
!-

    eveplus_set_mode := get_info(current_buffer,"MODE");
    set(new_mode, current_buffer);
endprocedure	! eveplus_set_mode

PROCEDURE EVEPLUS_BLANK_CHARS(eveplus_v_blank_count)

!+
! This procedure returns a string of eveplus_v_blank_count blank chars.
!-
  local
    eveplus_v_blank_chars,
    eveplus_v_oldlen,
    eveplus_v_blanks_so_far;	! Length of blank char string so far

    IF eveplus_v_blank_count = 0
    THEN
	RETURN ""
    ENDIF;

    eveplus_v_blank_chars := " ";
    eveplus_v_blanks_so_far := 1;
    loop
	exitif eveplus_v_blanks_so_far >= eveplus_v_blank_count;
	eveplus_v_oldlen := LENGTH(eveplus_v_blank_chars);
	eveplus_v_blank_chars := eveplus_v_blank_chars + eveplus_v_blank_chars;
	eveplus_v_blanks_so_far := eveplus_v_blanks_so_far + eveplus_v_oldlen;
    endloop;
    
    IF eveplus_v_blanks_so_far > eveplus_v_blank_count
    THEN
	eveplus_v_blank_chars :=
	    SUBSTR(eveplus_v_blank_chars,1,eveplus_v_blank_count)
    ENDIF;
    RETURN eveplus_v_blank_chars
endprocedure	! eveplus_blank_chars

PROCEDURE EVEPLUS_ADVANCE_HORIZONTAL(eveplus_v_columns,eveplus_v_blank_chars)

!+
! This procedure advances current_offset to be eveplus_v_columns from
! current_offset.  eveplus_v_blanks_chars must be
! a string of blank chars of at least length eveplus_v_columns.
!-
  local
    eveplus_v_save_offset,		! current_offset on entry to this procedure
    eveplus_v_eol_columns;		! Number of columns to [EOL]

    eveplus_v_save_offset := current_offset;
    if eveplus_v_columns <= 0
    then
	move_horizontal(eveplus_v_columns);
    else
	!+
	! Find out how far to [EOL].
	!-
	eveplus_v_eol_columns := length(current_line)-current_offset;
	if eveplus_v_eol_columns >= eveplus_v_columns
	then
	    move_horizontal(eveplus_v_columns);
	else
	    move_horizontal(eveplus_v_eol_columns);
	    copy_text(substr(eveplus_v_blank_chars,1,
			     eveplus_v_columns-eveplus_v_save_offset));
	endif;
    endif;
endprocedure	! eveplus_advance_horizontal

PROCEDURE tpu$define_keys_edd
DEFINE_KEY("EVE_RECTANGULAR_SELECT", F17, "EDD SELECT");
DEFINE_KEY("EVE_RECTANGULAR_REMOVE", F18, "EDD CUT");
DEFINE_KEY("EVE_RECTANGULAR_INSERT_HERE", F19, "EDD PASTE");
DEFINE_KEY("EVE_DRAW_BOX", F20, "EDD DRAW BOX")
ENDPROCEDURE

PROCEDURE tpu$init_proc_edd
eveplus_v_begin_select := 0
ENDPROCEDURE
