#include<stdio.h> 
 
/* 




                 VV                   VV    EEEEEEEEEEEEEE   
                  VV                 VV     EE  
                   VV               VV      EE
                    VV             VV       EE
                     VV           VV        EE
                      VV         VV         EEEEEEEEEEEEEE
                       VV       VV          EE
                        VV     VV           EE
                         VV   VV            EE
                          VV VV             EE
                           VVV              EE
                            V               EEEEEEEEEEEEEE
                                                       
 
 
 
 
                       MODULE  : delete.c
 
                       VERSION : v3.5
 
                       REVISED : 12 Jul 2005
 
                       AUTHOR  : Michael A. Campbell
 
 
 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
               This module contains all of the delete commands used by
 
               VE.  These includes DELETE, RUBOUT, DELETE RIGHT, and
 
               DELETE LINE.  As an implementation consequence, LINE JOIN
 
               also occurs in this file.










*/

/* .page */

/*****************
 * INCLUDE FILES *
 *****************/

#include <curses.h>

#include "../def/common.def"
#include "../def/kbdif.def"
#include "../def/cmdix.def"

#include "../str/vestruct.str"

#include "../inc/vedbase.inc"
#include "../inc/termif.inc"
#include "../inc/kbdif.inc"

#include "../ext/strprim.ext"
#include "../ext/termif.ext"
#include "../ext/cursor.ext"
#include "../ext/veutils.ext"
#include "../ext/lnklst.ext"
#include "../ext/delete.ext"
#include "../ext/undo.ext"

/* .page */

/*************************
 *  LOCAL PROCEDURES     *
 *************************/

void line_join ()

/****************************************************************************
 *                                                                          *
 *  This function is called when the delete_char command is executed with   *
 *  the cursor is at the end of the current line or when the rubout command *
 *  is executed with the cursor at col 0 of a line. In either case, the     *
 *  intent is to delete the implicit line end and join the current line and *
 *  the line below it.                                                      *
 *                                                                          *
 ****************************************************************************/

begin
    struct node *next_line;      /* The line after the current line         */
    BOOL   last_line_of_file;    /* Is it the last line of the file?        */
    int    offset;               /* The curr_line offset where join starts  */

    /*  Update the RAM version of the file by creating a new line which is  */
    /*  the total of the current line and the next line.                    */

    update_curr_line ();

    if ( curr_line->fwd_link == EOL ) then    /* no next line !              */
         return;

    next_line = curr_line->fwd_link;

    line_buf.length = curr_line->length + next_line->length;

    if ( line_buf.length >= MAX_LINE_SIZE - 2 )
    begin
       write_ve_msg ( "Can't join lines - lines too long" );
       line_buf.length = 0;
       return;
    end
    
    movb ( &curr_line->text, &line_buf.text, curr_line->length );
    movb ( &next_line->text, &line_buf.text[curr_line->length],
            next_line->length );

    /*  Have now constructed the new line.  Replace the current line with    */
    /*  this newly constructed line.                                         */

    update_curr_line ();

    /*  Now update the screen: do different things based on where cursor was */

    if ( curr_screen.cy == lines - 1 ) begin

       /*  The cursor is on the last line of the screen. Just delete the     */
       /*  now joined line  - curr line will be updated later on.            */

       next_line = (struct node *) delete ( &curr_file, next_line, &status );
       free ( next_line );
    end

    else begin
         /*  Cursor is on some line other than the last line of the screen,  */
         /*  so delete the next line and update this one.                    */

         cursor_down_cmd ();
         last_line_of_file = (curr_line->fwd_link == EOL) ? TRUE : FALSE;

         delete_line_cmd ();

         /* If the line being deleted was the last line of the file, the    */
         /* delete_line_cmd has already moved the cursor up to the previous */
         /* line. If not, do it ourselves.                                  */

         if ( !last_line_of_file ) then
            cursor_up_cmd   ();
    end

    if ( curr_screen.cx <= columns - 1 )
       writeln ( curr_line->text, curr_line->length, 0 );

    restore_cursor ();

end /* line_join */

/* .page */

/*************************
 *  PUBLIC PROCEDURES    *
 *************************/

void delete_chars ( line, cx1, cx2, cy )

/****************************************************************************
 *                                                                          *
 * This function deletes the chars between cx1 and cx2 (inclusive) on the   *
 * supplied line. It assumes that the line is displayed at the given cy,    *
 * and updates the line on screen as well. This function handles those      *
 * cases where cx1 or cx2 are beyond the end of the line, and also the case *
 * where cy is beyond the bottom of the screen (no screen update done)      *
 *                                                                          *
 ****************************************************************************/

struct node *line;              /* The line to work on                      */
int    cx1, cx2;                /* The start and end cols to delete between */
int    cy;                      /* The y co-ord at which line is displayed  */

begin
   register int start_cx;       /* First column for deletion - always cx1   */
   register int end_cx;         /* Last column for deletion - computed      */
   register int i;              /* Loop counter                             */
   register int orig_length;    /* Pre-deletion length of line              */
   register int right_cx;       /* X co-ord of right edge of screen         */

   if ( line->length == 0 || line->length < cx1 )
      return;

   orig_length = line->length;

   /* Adjust for case where cx2 is beyond the end of the line               */

   start_cx = cx1;
   end_cx   = (line->length <= cx2) ? line->length - 1: cx2;

   /* Create a copy of the line with the section to be deleted not present  */

   movb ( line->text,            temp.text,            start_cx                  );
   movb ( &line->text[end_cx+1], &temp.text[start_cx], line->length - end_cx - 1 );

   /* Move the result back into the line, adjusting its length first        */

   line->length = line->length - (end_cx - start_cx) - 1;
   movb ( temp.text, line->text, line->length );

   if ( cy < lines )
   begin
      /* Now update the screen, assuming the line is displayed at given cy  */
      /* Start by deleting the range to be deleted                          */

      ti_move_cursor ( start_cx, cy );
      for ( i = start_cx; i <= end_cx; i++ )
         ti_delete_char ();

      /* If the line's length was greater than the screen width, then we    */
      /* have some characters to repaint at the right edge, as these need   */
      /* to "shift left" onto the display                                   */

      right_cx = ti_get_right_cx();

      if ( orig_length > right_cx )
          writeln ( line->text, line->length, (right_cx - (end_cx - start_cx)) );
   end

end /* delete_chars */

/* .page */

void delete_char_cmd ()

/****************************************************************************
 *                                                                          *
 *   Delete the character under the cursor.                                 *
 *                                                                          *
 ****************************************************************************/

begin
    register int i;

    load_line_buf ();

    /* If the cursor is beyond the end of the line, cannot delete: Return.   */
    /* If the cursor is at the end of the line, join the next one to it.     */

    if ( curr_screen.cx > line_buf.length )
       return;

    if ( curr_screen.cx == line_buf.length ) 
    begin
         if ( curr_cmd == commands[del_char_ix] )
            push_undo_del_char ( ENTER, get_curr_line_num(), curr_screen.cx, 0 );

         line_join ();

         return;
    end

    /* If delete_char_cmd is not being run by some other command (which may */
    /* have its own undo action) push an undo action for it.                */

    if ( curr_cmd == commands[del_char_ix] )
       push_undo_del_char ( line_buf.text[curr_screen.cx], get_curr_line_num(), 
                            curr_screen.cx, 0 );

    /* Delete the character in the line. */

    for ( i = curr_screen.cx ; i < line_buf.length ; i++ )
          line_buf.text[i] = line_buf.text[i + 1];

    /* Note that current line must be done as well, to cover for the case   */
    /* when a line is deleted to zero (update_curr_line .. won't!).         */

    line_buf.length -= 1;
    if ( curr_line->length > 0 ) then
         curr_line->length -= 1;

    ti_delete_char ();

    /* If this delete has recalled a character that was previously off      */
    /* screen, redraw that character                                        */

    ti_draw_last_column ( &line_buf );
    restore_cursor ();

    /* Finally, if text wrapping is active, attempt to wrap as need be      */

    if ( options.wrap_text )
    begin
       flush_undo_queue ();
       wrap_line ( TRUE );
    end

end /* delete_char_cmd */

/* .page */

void delete_right_cmd ()

/****************************************************************************
 *                                                                          *
 *   Delete all text to the right of the cursor on the current line.        *
 *                                                                          *
 ****************************************************************************/

begin
    load_line_buf ();

    /* If we at or beyond the end of the line, there is nothing to delete   */
    /* and so simply return.                                                */

    if ( (line_buf.length == 0) || (curr_screen.cx >= line_buf.length ) )
       return;

    push_undo_del_right ( &line_buf,      curr_screen.cx, get_curr_line_num(),
                          curr_screen.cx, curr_screen.cy);

    /* Note that current line must be done as well, to cover for the case   */
    /* when cx is at left edge of screen (update_curr_line .. won't!).      */

    line_buf.length = curr_screen.cx;
    if ( curr_screen.cx == 0 )
        curr_line->length = 0;

    setb (' ', &line_buf.text[curr_screen.cx], (MAX_LINE_SIZE - curr_screen.cx));

    if ( curr_screen.cx < columns ) then
         ti_clear_to_eol ( curr_screen.cy );

end /* delete_right_cmd */

/* .page */

void delete_left_cmd ()

/****************************************************************************
 *                                                                          *
 *   Delete all text from the current cursor position to the start of the   *
 *   line.                                                                  *
 *                                                                          *
 ****************************************************************************/

begin
    register int i, cx;         /* Loop counters                            */
    register int new_length;    /* New length of line_buf after delete      */

    load_line_buf ();

    /* If the current line is of zero length, there is nothing to delete    */
    /* and so simply return.                                                */

    if ( (line_buf.length == 0) || (curr_screen.cx > line_buf.length ) )
       return;

    /* The undo action for Delete Word works just fine here, and so we      */
    /* re-use it. It re-inserts a set of deleted characters. We tell it to  */
    /* do this starting at column 0. Fits like a glove!                     */

    push_undo_del_word  ( &line_buf, curr_screen.cx, get_curr_line_num(),
                          0, curr_screen.cy);

    cx = curr_screen.cx;
    new_length = line_buf.length - curr_screen.cx;

    ti_move_cursor ( (curr_screen.cx = 0), curr_screen.cy );

    /* Delete all the chars in the line from current cx to zero             */

    for ( i = 0; i < new_length; i++ )
       line_buf.text[i] = line_buf.text[cx + i];

    /* Update the current line - note that we have to manually adjust the   */
    /* current line's length since if it is zero, update_curr_line ...wont! */

    line_buf.length = curr_line->length = new_length;
    update_curr_line ();

    if ( cx >= physical_cols )
    begin
       /* If we have deleted an entire screen width, just redraw the line   */

       ti_clear_to_eol ( curr_screen.cy );
       writeln ( curr_line->text, curr_line->length, 0 );
    end

    else
    begin
       /* If we have deleted only a portion of the screen, update the       */
       /* display by deleting chars at column zero                          */

       for ( i = 0; i < cx; i++ )
          ti_delete_char ();

       /* If the line's length was greater than the width of the screen,    */
       /* then we have some characters to repaint at the right edge, as     */
       /* these need to "shift left" onto the display                       */

       if ( new_length + cx > physical_cols )
          writeln ( curr_line->text, curr_line->length, physical_cols - cx );
    end

    restore_cursor ();

end /* delete_left_cmd */

/* .page */

void rubout_cmd ()

/****************************************************************************
 *                                                                          *
 *   Implement the RUBOUT function with cursor_left, delete_char.           *
 *                                                                          *
 ****************************************************************************/

begin
    if ( curr_screen.cx == 0 ) 
    begin
       /* User has entered Backspace/Rubout at the left margin. Attempt to  */
       /* wrap up to the end of the previous line, if it exists.            */

       if ( curr_line->bwd_link != (struct node *) &curr_file ) 
       begin
          cursor_up_cmd ();
          ext_end_cmd ();

          line_join ();

          /* The undo action, of course, is to type ENTER at the current    */
          /* spot                                                           */

          push_undo_del_char ( ENTER, get_curr_line_num(), 
                               curr_screen.cx, curr_screen.cy );
       end

       return;

    end /* curr_screen.cx == 0 */

    /* Else, load up the current line buffer, cursor left by one             */

    cursor_left_cmd ();
    load_line_buf ();

    /* If we are beyond the end of the line, simply return                   */

    if (curr_screen.cx >= line_buf.length) then
        return;

    /* If we are in INSERT mode, delete the character we are on top of       */

    if (last_ve_mode == M_INSERT) then
    begin
       /* If rubout_cmd is not being run by some other command (which may    */
       /* have its own undo action) push an undo action for it.              */

       if ( curr_cmd == commands[rubout_ix] )
          push_undo_del_char ( line_buf.text[curr_screen.cx], get_curr_line_num(), 
                               curr_screen.cx, curr_screen.cy );
       delete_char_cmd ();
    end

    /* If VE's mode is OVERWRITE, restore any overwritten character or       */
    /* replace the character with a space                                    */

    else
    begin
        /* If the user is not beyond the end of the line, Backspace simply   */
        /* restores any characters that they may have overwritten. Else it   */
        /* loads spaces into the line.                                       */

        if ( curr_line->length > curr_screen.cx ) then
            line_buf.text[curr_screen.cx] = curr_line->text[curr_screen.cx];
        else
            line_buf.text[curr_screen.cx] = ' ';

        if (curr_screen.cx < columns - 1) then
            writechar ( line_buf.text[curr_screen.cx] );

        ti_cursor_left ();
    end

end /* rubout_cmd */

/* .page */

void delete_line_cmd ()

/****************************************************************************
 *                                                                          *
 *   Delete the line that the cursor is on.                                 *
 *                                                                          *
 ****************************************************************************/

begin
    int line_num, actual, x, y;
    struct node *new_curr_line, *old_bottom, *copy_line;
    BOOL end_of_file = FALSE;

    update_curr_line ();

    /* Start by making a copy of the line we are about to delete into the   */
    /* Line delete buffer, in the same way as CTL-X does.                   */

    if ( curr_rep == 1 )
       purge_line_del_buffer ();

    copy_line = (struct node *) ve_alloc (curr_line->length + DES_SIZE);
    movb (&curr_line->length, &copy_line->length, curr_line->length + sizeof(int));

    send_node (&line_del_block, copy_line);

    /* Now, set up for our line deletion                                    */

    old_bottom    = curr_screen.bottom;  /* Save old last line of screen    */
    new_curr_line = curr_line->fwd_link;
    x             = curr_screen.cx;      /* Save cursor co-ordinates        */
    y             = curr_screen.cy;

    if ( curr_line->fwd_link == EOL ) 
    begin
       /* Current is also the last line of the file. The end of file flag   */
       /* is used by the undo system to decide whether to insert the newly  */
       /* undeleted line before the current line (normal) or AFTER the      */
       /* current line (only done if the deleted line is the last line of   */
       /* the file. HOWEVER, if we are deleting the ONLY line of the file,  */
       /* VE automatically creates a new line, since it MUST have a current */
       /* line at all times. In this case, we will want the undo system to  */
       /* do its normal thing (insert above). Hence, only set the end of    */
       /* file flag if we are not deleting the only line of the file.       */

        if ( curr_file.nodes > 1 ) 
           end_of_file = TRUE;

        /* Bring previous line onto screen, if we are at the top of the     */
        /* screen.                                                          */

        cursor_up_cmd ();
        cursor_down_cmd ();

        /* If we delete the last line of the file, the new current line is  */
        /* one line up, instead of the normal one line down. If this is the */
        /* only line of the file that we are deleting, however, the cursor  */
        /* has to stay right where it is.                                   */

        y = curr_screen.cy;
        if ( curr_file.nodes > 1 ) then
           curr_screen.cy--;

        new_curr_line = curr_line->bwd_link;
    end

    /* Delete the line, update the display, and then queue the deleted line */
    /* for possible undo action. We only queue the undo action if we see    */
    /* that delete_line_cmd is not being run from somewhere else, as that   */
    /* "somewhere else" will have its own undo action.                      */

    line_num  = get_curr_line_num ();
    curr_line = delete (&curr_file, curr_line, &status);
    ti_delete_line ( y );

    if ( curr_cmd == commands[del_line_ix] )
       push_undo_del_line ( curr_line, end_of_file, line_num, x, y );

    curr_line = new_curr_line;

    /* Check for case of deleting ONLY line of file. VE *always*    */
    /* needs a current line, so if we have deleted the only line,   */
    /* lets replace it.                                             */
    
    if ( curr_file.nodes == 0 ) begin
       curr_line = (struct node *) ve_alloc (82 + DES_SIZE);
       curr_line->length = 0;
       send_node (&curr_file, curr_line);
    end

    restore_cursor ();
    actual = update_curr_screen (curr_screen.cy);

    /* If the end of the file has not been reached, display a new line at   */
    /* the bottom of the screen.                                            */

    if (curr_screen.bottom != old_bottom) begin

        /* Starting at the top of the screen, try to move down the correct  */
        /* number of lines to the bottom of the screen.  The variable       */
        /* "actual" will hold the number of lines actually moved (less than */
        /* lines - 3 if we are at or near the end of the file).  Move the   */
        /* cursor to the correct x,y and write the new bottom line.         */

        cnode = traverse_list ( &curr_file, curr_screen.top, (int)'+', &actual, lines-3, 
                                &status);

        /* Pick up the bottom line into cnode and print it                  */

        cnode = curr_screen.bottom;
        mv_writeln ( ti_get_left_cx(), actual+2, TRUE, &cnode->text, cnode->length, 0);
    end

end /* delete_line_cmd */

