#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  : editcmd.c
 
                       VERSION : v3.5
 
                       REVISED : 12 Jul 2005
 
                       AUTHOR  : Michael A. Campbell
 
 
 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
              This module contains the edit commands for the VE editor.
 
              These commands initiate the editing of a new file, switch
              
              between files, and so on. 
 




*/

/* .page */

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

#include <string.h>
#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/termif.ext"
#include "../ext/veutils.ext"
#include "../ext/fileutil.ext"
#include "../ext/strprim.ext"
#include "../ext/lnklst.ext"
#include "../ext/stacks.ext"

/* .page */

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

void save_context ( file_ix )

/****************************************************************************
 *                                                                          *
 *   Save the current context of VE in the file descriptor of the file      *
 *   whose index is given.                                                  *
 *                                                                          *
 ****************************************************************************/

 int file_ix;   /* Index of descriptor to save current context in           */

 begin
    int actual;                     /*  Actual number of nodes moved over   */
    struct file_des_str *save_des;  /*  Descriptor being saved into         */

    if ( file_ix == -1 ) then
        return;

    else begin

        update_curr_line ();

        /* Move to the descriptor, and then copy current data to it.        */

        save_des = (struct file_des_str *) traverse_list ( &open_file_list, 
                    open_file_list.fwd_link, '+', &actual, file_ix, &status );

        curr_screen.ap_l = ti_get_left_cx ();
        curr_screen.ap_r = ti_get_right_cx ();

        movb ( &curr_file,     &save_des->file_list,   sizeof(curr_file) );
        movb ( &curr_screen,   &save_des->screen_data, sizeof(curr_screen) );
        movb ( &curr_filename, &save_des->filename,    sizeof(curr_filename) );

        save_des->curr_line     = curr_line;
        save_des->curr_line_num = curr_line_num;
        save_des->conn          = curr_conn;

    end /* good file index */

end /* save_context */

/* .page */

void restore_context ( file_ix )

/****************************************************************************
 *                                                                          *
 *   Restore the context of VE from the file descriptor of the file         *
 *   whose index is given.                                                  *
 *                                                                          *
 ****************************************************************************/

 int file_ix;       /* Index of descriptor to restore context from          */

 begin

    int actual;                     /* Actual number of nodes moved over    */
    struct file_des_str *rest_des;  /* Descriptor being restored from       */


    if ( file_ix == -1 ) then
        return;

    else begin

        /* Move to the descriptor, and then copy current data from it.       */

        rest_des = (struct file_des_str *) traverse_list ( &open_file_list, 
                    open_file_list.fwd_link, '+', &actual, file_ix, &status );

        movb ( &rest_des->file_list,   &curr_file,      sizeof(curr_file) );
        movb ( &rest_des->screen_data, &curr_screen,    sizeof(curr_screen) );
        movb ( &rest_des->filename,    &curr_filename,  sizeof(curr_filename) );

        ti_set_left_cx  ( curr_screen.ap_l );
        ti_set_right_cx ( curr_screen.ap_r );

        curr_line     = rest_des->curr_line;
        curr_line_num = rest_des->curr_line_num;
        curr_conn     = rest_des->conn;

        curr_file_des = rest_des;

    end /* good file index */

end /* restore_context */

/* .page */

void context_switch ( old_ix, new_ix )

/****************************************************************************
 *                                                                          *
 *  Switch between the current file, whose index is old_ix, and the new     *
 *  file, whose index is new_ix.  This involves saving the old context      *
 *  and restoring a new one.                                                *
 *                                                                          *
 ****************************************************************************/

 int old_ix;        /* Indices of old and new file descriptors              */
 int new_ix;

 begin

    save_context    ( old_ix );
    restore_context ( new_ix );

    curr_file_ix = new_ix;

    curr_screen.cy = update_curr_screen ( curr_screen.cy );
    draw_curr_screen ();

end /* context_switch */

/* .page */

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

int get_file ( file_name )

/*****************************************************************************
 *                                                                           *
 *  Search the current file list for a file  whose name is the same as the   *
 *  file name supplied.                                                      *
 *                                                                           *
 *****************************************************************************/

 char file_name [];

 begin

    struct file_des_str *des;
    int len, name_len, file_ix;

    des = (struct file_des_str *) open_file_list.fwd_link;
    name_len = strlen ( file_name );

    file_ix = 0;

    while ( des != EOL ) begin

        len = strlen ( des->filename );

        if ( len == name_len ) begin

            if ( strcmp ( file_name, des->filename ) == 0 ) then
                 return ( file_ix );
        end

        des = (struct file_des_str *) des->fwd_link;
        file_ix += 1;

    end

    return ( -1 );

end /* get_file */

/* .page */

void edit_file ( file_ix, prev_file_ix )

/****************************************************************************
 *                                                                          *
 *   Begin editing of the file whose index is supplied.  This file becomes  *
 *   the current file, and so this procedure sets up all of the relevant    *
 *   "current" data structures.                                             *
 *                                                                          *
 ****************************************************************************/

int file_ix;        /* Index of file descriptor for the file to edit        */
int prev_file_ix;   /* Index of curr file before this add_file attempted    */

begin
    int actual;                       /* Actual nodes moved in file list    */
    struct file_des_str *file_des;    /* Pointer to current file des        */
    char fmt_str[] = "[Unix]";

    /*  This file will become the current file, and so set up the new       */
    /*  current attributes. Assume the file is unix formatted (uses LF      */
    /*  only for its line end character. DOS/Win files use CR,LF).          */

    file_des = (struct file_des_str *) traverse_list ( &open_file_list, 
                open_file_list.fwd_link, '+', &actual, file_ix, &status );
    curr_conn = file_des->conn;

    setb ( 0, &curr_file, sizeof(curr_file) );
    setb ( 0, &curr_screen, sizeof(curr_screen) );
    strcpy ( curr_filename, file_des->filename );

    ve_exception = read_file ( curr_filename, &curr_file, &curr_file.num_chars,
                               &curr_file.num_lines, &curr_file.checksum,
                               &curr_file.file_format                       );

    if ( ve_exception != E_OK ) begin

        set_status_line (TRUE, ve_exception, last_ve_mode);

        discard_curr_file ( curr_file.head, TRUE );
        wait_for_enter ();

        if ( files_open > 0 ) begin
           context_switch ( -1, prev_file_ix );
           return;
        end
        else
           quit_ve ( ve_exception );
    end

    /* Finally, draw the current screen. This step is skipped if VE is in   */
    /* initialization process, since multiple files may be being opened and */
    /* this would cause distracting screen flashing. ve_init, in main.c,    */
    /* draws the screen for the final file when initializing is done.       */

    curr_screen.top    = curr_line = curr_file.head;
    curr_screen.bottom = traverse_list (&curr_file, curr_screen.top, (int)'+',
                                        &actual, lines - 3, &status);
    if ( !ve_initializing ) then
       draw_curr_screen ();

    /* Same idea for file stats: set up ve_msg but don't actually write it  */
    /* out. It will be written out by a later set_status_line call. This    */
    /* stops distracting flashing of status line during multi file opens.   */

    if ( curr_file.file_format == 'd' )
       strcpy ( fmt_str, "[DOS/Win]" );

    sprintf ( ve_msg_buf, "%u %s %d %s %s", curr_file.num_chars, "Chars",
              curr_file.num_lines, "Lines", fmt_str );
    ve_msg = ve_msg_buf;

    curr_screen.cx = 0;
    curr_screen.cy = 2;

    curr_screen.direction = rcd;

end /* edit_file */

/* .page */

void edit_new_file ()

/****************************************************************************
 *                                                                          *
 *   Set up for the editing of a new, unnamed file.                         *
 *                                                                          *
 ****************************************************************************/

 begin
    /*  This file will become the current file, and so, set up the new  */
    /*  current attributes.                                             */

    setb ( 0, &curr_file, sizeof(curr_file) );
    setb ( 0, &curr_screen, sizeof(curr_screen) );
    strcpy ( curr_filename, curr_file_des->filename);

    curr_conn = NULL;

    curr_file.file_format = 'u';    /* Set file to be unix formatted    */
    curr_file.num_chars   = 1;      /* The linux end of line            */
    curr_file.num_lines   = 1;      /* Reflect the ve_alloc'd line below  */
    curr_file.checksum    = 0;

    /* Set up a default first line so that space to write is allocated. */

    curr_line = (struct node *) ve_alloc (82 + DES_SIZE);
    curr_line->length = 0;

    send_node (&curr_file, curr_line);

    curr_screen.top       = curr_line;
    curr_screen.bottom    = curr_line;
    curr_screen.direction = rcl;

    curr_screen.cx = 0;
    curr_screen.cy = 2;

    ti_clear_display ( 2 );

end /* edit_new_file */

/* .page */

void add_file ( pathname )

/****************************************************************************
 *                                                                          *
 *   Add the file whose name is given to the set of active files currently  *
 *   being serviced by VE.  This file becomes the current file.             *
 *                                                                          *
 *   Adding the file involves allocating and initializing a new file        *
 *   descriptor, and then intiating the editing of the file, by calling an  *
 *   appropriate procedure.  If the supplied name is null (0), then a new   *
 *   empty file is added.  If the filename is non null, a new file is read  *
 *   in and set up;                                                         *
 *                                                                          *
 ****************************************************************************/

 char *pathname;

 begin
    int i;                              /* General purpose index            */
    int prev_file_ix;                   /* Current file index               */
    struct file_des_str dummy_file_des; /* Dummy, for getting sizeof        */
    FILE *temp_conn;

    ve_exception = E_OK;
    temp_conn    = NULL;
    prev_file_ix = curr_file_ix;

    /*  If a new empty file is not being added, attempt to open the given   */
    /*  file to be sure that it exists.                                     */

    if ( pathname != NULL ) 
    begin
        temp_conn = fopen ( pathname, "r" );
        if ( temp_conn == NULL ) then
            ve_exception = E_OPEN_ERROR;
    end

    if ( ve_exception == E_OK ) begin

        /*  File does exist so close it back up for now. Allocate and       */
        /*  initialize a file descriptor block for it.                      */

        if ( temp_conn != NULL) then
           fclose ( temp_conn ); 

        file_des = (struct file_des_str *) ve_alloc ( sizeof(struct file_des_str) );

        setb ( 0, file_des, sizeof(struct file_des_str) );
        file_des->conn = temp_conn;

        if ( pathname != NULL ) then
            strcpy ( file_des->filename, pathname );
        else
            strcpy ( file_des->filename, "Unnamed File");

        send_node ( &open_file_list, file_des );

        /* Save the current file context, and then add a new file.          */

        save_context ( curr_file_ix );

        files_open    += 1;
        curr_file_ix  = files_open - 1;
        curr_file_des = file_des;

        if ( pathname != NULL ) then
            edit_file ( curr_file_ix, prev_file_ix );
        else
            edit_new_file ();

    end /* ve_exception == e_ok */

end /* add_file */

/* .page */

void goto_file_cmd ()

/****************************************************************************
 *                                                                          *
 *   Present the user with a menu of the available open files, and allow    *
 *   them to select which file they wish to be editing. Go to that file.    *
 *                                                                          *
 ****************************************************************************/

begin
   int selection;       /* The index of the new file to edit                */

   update_curr_line ();

   if ( files_open == 0 )
       write_ve_msg ( "No open files" );

   else 
   begin
       /* Since there is definitely at least one file open, the            */
       /* goto_file_dialog will always return a valid selection, even if   */
       /* the user types rubbish, or quits out. So, just switch to the new */
       /* selection. If the user simply quit out of the dialog, we will be */
       /* switching to the current file, but this nil operation is OK.     */

       selection = goto_file_dialog ( " - Select File to Switch To", 
                                      temp.text, MAX_LINE_SIZE, FALSE );

       context_switch ( curr_file_ix, selection );

       set_status_line ( TRUE, E_OK, last_ve_mode );
   end

end /* goto_file_cmd */

/* .page */

void next_file_cmd ()

/****************************************************************************
 *                                                                          *
 *   Move to the next open file, and resume editing there.                  *
 *                                                                          *
 ****************************************************************************/

begin
   int new_ix;        /* Index of the next file                             */

   update_curr_line ();

   if ( files_open <= 1 ) 
   begin
       write_ve_msg ( "Only 1 file open" );
       writechar ( BEL);
   end

   else 
   begin
       new_ix = ( curr_file_ix + 1 ) % files_open;
       context_switch ( curr_file_ix, new_ix );
       set_status_line ( TRUE, E_OK, last_ve_mode );
   end

end /* next_file_cmd */

/* .page */

void edit_cmd ()

/****************************************************************************
 *                                                                          *
 *   Present the user with a menu of the available edit options, and then   *
 *   act on the selection.                                                  *
 *                                                                          *
 ****************************************************************************/

begin

    int new_ix;             /* Index of new file's descriptor               */
    int selection;          /* User's selection from file menu              */
    int fn_len;             /* Length of truncated filename                 */
    char key = ' ';         /* Input key                                    */
    int i;                  /* General purpose index                        */
    char display_fn[82];    /* Displayable version of current filename      */

    char edit_menu [] = "File  New  Menu  + : ";

    update_curr_line ();

    truncate_str_left ( curr_filename, display_fn, FN_FIELD_WIDTH-1 );
    fn_len = strlen ( display_fn );

    push_status_line ( edit_menu );

    while ( key != ENTER ) begin

        mv_writestr ( physical_cols-fn_len-1, 0, FALSE, display_fn );
        ti_move_cursor ( strlen(edit_menu), 0 );

        key = toupper (read_char());

        if ( key == 'F' ) begin

            /*  Selection is File:  Add a new file to the current set, and  */
            /*  initiate editing of the new file.                           */

            if ( files_open >= MAX_OPEN_FILES )
                write_ve_msg ( "Cannot open any more files" );

            else begin

                open_file_dialog ( select_file_open, temp.text, MAX_LINE_SIZE, FALSE );

                if ( strlen(temp.text) == 0 )
                   write_ve_msg ( no_file_select_open );

                else if ( (new_ix = get_file (temp.text)) != -1 )

                    /* If we already have the selected file, then get it    */
                    /* back as the current.                                 */

                    context_switch ( curr_file_ix, new_ix );

                else
                begin
                   /* Try to open the indicated file. If it doesn't exist,  */
                   /* this will fail. Since we told open_file_dialog not to */
                   /* redraw the screen, if the open fails, we must do it   */
                   /* ourselves.                                            */

                    add_file ( temp.text );

                    if ( ve_exception != E_OK )
                       view_cmd ();
                end

            end

            break;

        end /* Key == F */


        else if ( key == 'N' ) begin

            /*  Selection is New:  Set up for the editing of a new, unnamed */
            /*  file, and add this to the list of active files.             */

            if ( files_open >= MAX_OPEN_FILES )
                write_ve_msg ( "Cannot open any more files" );
            else
                add_file ( NULL );

            break;  /* after opening the file, exit the edit menu */

        end /* Key == N */


        else if ( key == 'M' ) begin

            /* Selection is Menu:  Show the current menu, and then prompt   */
            /* for a selection from that menu. Go to that file.             */

            goto_file_cmd ();
            break;

        end /* Key == M */


        else if ( key == '+' || key == '=' ) begin

            /*  Selection is + : Move to the next file, no matter what      */
            /*  file that is. Allow "=" as well, as this is the lower case  */
            /*  of the + key.                                               */

            next_file_cmd ();

        end /* Key == + */


        else begin

            /*  Unless the user entered ENTER, an invalid selection  was    */
            /*  chosen.                                                     */

            if ( key != ENTER ) then
               writechar ( BEL );

        end /* Invalid selection */

        /* Restore the edit command menu */

        write_status_line ( tos(&status_stack) );

    end /* while not ENTER */

    pop_status_line ();    /* Restore master menu */

end /* edit_cmd */

