#include<stdio.h> 
#include<string.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  : misccmd.c
 
                       VERSION : v3.5
 
                       REVISED : 12 Sep 2005
 
                       AUTHOR  : Michael A. Campbell
 
 
 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
               This module contains a number of short commands that
 
               do various useful, but usually unrelated things, such as
 
               changing the mode, refreshing the screen, etc.
 


               This module contains the mouse_cmd, and as such uses 

               ncurses directly. Like TERMIF.C and KBDIF.C it is thus

               not fully portable.




*/

/* .page */

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

#include <curses.h>

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

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

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

#include "../ext/veutils.ext"
#include "../ext/fileutil.ext"
#include "../ext/strprim.ext"
#include "../ext/termif.ext"
#include "../ext/lnklst.ext"
#include "../ext/stacks.ext"
#include "../ext/columncmd.ext"
#include "../ext/undo.ext"

/* .page */

/*******************************
 * HELP, ABOUT SCREEN CONTENTS *
 *******************************/

#define HELP_PANEL_LINES 32
#define ABOUT_VE_LINES   17

/* Note that the first line of ve_help must be the longest line of the panel */
/* (either by virtue of actually being the longest line, or by padding with  */
/* spaces) since help_cmd uses its length to figure out centering of the     */
/* panel.                                                                    */

/* THE HELP PANEL IS FULL! On VE's minimum 80x20 screen size, the current    */
/* help panel occupies EXACTLY two screen fulls, which is all the help       */
/* command is set up to do. If more help lines are needed, the help command  */
/* will need additional work to make it use up to three screens.             */

static char *ve_help [HELP_PANEL_LINES] = {
"F1=Again           F2=Block  F3=Column   F4=Jump         F5=Find     F6=Replace",
"F7=Paste           F8=Edit   F9=DelLine  F10=DeleteEol   F11=Update  F12=Quit  ",
" ",
"SHFT-CurUp=Scroll-Up     :   also ESC--  SHFT-CurDn=ScrollDown     : also ESC-+",
"SHFT-CurLeft=Scroll-Left :   also ESC-(  SHFT-CurRight=ScrollRight : also ESC-)",
" ",
"Keypad/=PickLine   Keypad+=NextFile      Keypad*=Info    Keypad-=SelectFile",
"Keypad5=ExtMotion  Backspace=Rubout      ESC-#=Rubout",
"CTL-c=CopyLine     CTL-x=CutLine         CTL-v=PasteLine CTL-z=Undo",
" ",
"Insert=Ins/Ovr     ESC-Insert=unused     Delete=DelChar  ESC-Delete=DeleteWord",
"Home=StartOfLine   ESC-Home=unused       End=EndOfLine   ESC-End=unused",
"PageUp=PageUp      ESC-PageUp=TopOfFile  PageDn=PageDn   ESC-PageDn=EndOfFile",
"CurLeft=left       ESC-CurLeft=WordLeft  CurRight=right  ESC-CurRight=WordRight",
"CurUp=up           ESC-CurUp=ParaUp      CurDown=down    ESC-CurDown=ParaDown",
" ",
"ESC-a=Again        ESC-b=Block           ESC-c=Column    ESC-d=DelLeft",
"ESC-e=Edit         ESC-f=Find            ESC-g=GoToFile  ESC-h=Help",
"ESC-i=Info         ESC-j=Jump            ESC-k=DelRight  ESC-l=DelLine",
"ESC-m=Macro        ESC-n=NextFile        ESC-o=Option    ESC-p=Paste",
"ESC-q=Quit         ESC-r=Replace         ESC-s=Status    ESC-t=Tags",
"ESC-u=Undo         ESC-v=ViewScreen      ESC-w=WrapPara  ESC-x=eXecuteMacro",
"ESC-y=unused       ESC-z=SelectLine",
" ",
"ESC-]=WordRight    ESC-[=WordLeft        ESC-{=ParaUp    ESC-}=ParaDown",
"ESC-\\=TopOfFile    ESC-~=EndOfFile       ESC->=EndofLine ESC-<=StartOfLine",
" ",
"ESC-I=Insert       ESC-O=Overwrite       ESC-D=DelChar   ESC-W=DeleteWord",
"ESC-B=PageUp       ESC-F=PageDown        ESC-A=AboutVE   ESC-;=ExtMotion",
"ESC-H=Home         ESC-M=Middle          ESC-L=Last      ESC-U=Update",
" ",
"Visit www.campbellware.com for full user manual and latest VE release"  };


static char *about_ve [ABOUT_VE_LINES] = {
"                      CampbellWare, 1985, 1994, 2005                     ",
"                                                                         ",
"VE  (Visual  Editor) is  a lean,  fast,  feature-rich ncurses-based text ",
"editor designed for speed and strong multi file support. VE started life ",
"in  1983  in  Toronto  Canada,  as a text editor for the NABU 1600 small ",
"business  computer.  The  NABU 1600 was an Intel 8086-based machine with ",
"512Kb  of  RAM and a 10Mb hard drive, delivered with the QUNIX operating ",
"system.  The  name  'VE'  was a good natured jab at the vi editor, which ",
"was  delivered  with  the NABU 1600, but which could never be induced to ",
"run  properly,  necessitating  the  creation of VE. For several years in ",
"the  mid  1980's,  VE  was the sole text editor for a small community of ",
"users  on  the  NABU  1600.  In 1994, the author acquired a PC (wow! VGA ",
"graphics!)  and  ported  VE  to MSDOS. Through the mid 1990's, VE was in ",
"use  by  a small community of MSDOS users. In 2005, the author ported VE ",
"to  Linux  (various distros)  and  ncurses, and redistributed it to the  ",
"Linux  user community. VE is provided free of charge, and is distributed ",
"under the GNU Public License. Use and enjoy.                             "  };

/* .page */

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

int ram_in_use ()

/****************************************************************************
 *                                                                          *
 *   Return the total number of bytes of RAM which are currently in use     *
 *   in files which are open.                                               *
 *                                                                          *
 ****************************************************************************/

 begin
    int ram_used    = 0;
    int bytes_in_use, lines_in_use, checksum;

    struct file_des_str *des;
    struct file_ctl     *file_des;

    struct node         *cnode;

    /* Pick up the head of the list of file descriptors. */

    save_context ( curr_file_ix );  /* Update the current files descriptor  */

    des = (struct file_des_str *) open_file_list.head;

    /* Iterate through each descriptor, summing up all of the bytes used    */
    /* by lines in that descriptor.                                         */

    while ( des != EOL ) begin

        /* Get head of this file, and iterate through the file, summing up  */
        /* the lines of the file.                                           */

        file_des = &des->file_list;
        file_info ( file_des, TRUE, &bytes_in_use, &lines_in_use, &checksum );

        ram_used += bytes_in_use;

        /* Advance to the next descriptor   */

        des = (struct file_des_str *) des->fwd_link;

    end

    /* Have now done all of the nodes of the open files.  Do the block      */
    /* buffer, the line delete buffer and the undo queue as well.           */

    file_info ( &line_block, TRUE, &bytes_in_use, &lines_in_use, &checksum );
    ram_used += bytes_in_use;

    file_info ( &line_del_block, TRUE, &bytes_in_use, &lines_in_use, &checksum );
    ram_used += bytes_in_use;

    ram_used += get_undo_queue_size ();

    return ( ram_used );

end /* ram_in_use */

/* .page */

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

void view_cmd ()

/****************************************************************************
 *                                                                          *
 *   Repaint the current screen.                                            *
 *                                                                          *
 ****************************************************************************/

begin
    int cy;

    update_curr_line ();

    ti_clear_display ( 0 );

    /* Set up the status line to either the current menu, or the normal      */
    /* display.                                                              */

    if (stack_empty(&status_stack)) then
        set_status_line (TRUE, E_OK, last_ve_mode);

    else
        write_status_line (tos(&status_stack));

    /* Repaint the devider line on second line (line 1), reconstruct the     */
    /* current screen, and repaint it.                                       */

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

end /* view_cmd */

/* .page */

void help_cmd ()

/*****************************************************************************
 *                                                                           *
 *  Display the VE help panel                                                *
 *                                                                           *
 *****************************************************************************/

 begin
    int cx, cy, i, lines_to_show;

    update_curr_line ();

    create_full_screen_info_page ( " - VE Help Panel" );

    /* Print out the help panel centered on the page */

    cx = ( physical_cols - strlen(ve_help[0]) ) / 2;
    lines_to_show = ( lines > HELP_PANEL_LINES + 3 ) ? HELP_PANEL_LINES : lines - 4;

    for ( i = 0; i < lines_to_show; i++ ) then
       mv_writestr ( cx, i+2, FALSE, ve_help[i] );

    /* Put out the bottom of screen divider line, and then either prompt    */
    /* the user for ENTER, or tell them that there is more                  */

    write_divider_line ( lines - 2 );

    if ( lines_to_show == HELP_PANEL_LINES ) then 
       mv_writestr ( 0, lines-1, FALSE, enter_to_edit_msg);

    else 
    begin
        /* Couldn't write whole help panel on first screen full. Write out  */
        /* navigation line, wait for enter, and then do the second page.    */

        mv_writestr ( 0, lines-1, FALSE, "more... press ENTER to view" );
        ti_move_cursor ( 0, 0 );
        wait_for_enter ();

        ti_clear_display ( 2 );              /* clear the last screen full  */
        cy = 2;                              /* resume display at line 2    */

        for ( i = lines-4; i < HELP_PANEL_LINES; i++ )
           mv_writestr ( cx, cy++, FALSE, ve_help[i] );

        write_divider_line ( lines - 2 );
        mv_writestr ( 0, lines-1, FALSE, enter_to_edit_msg );
    end

    remove_full_screen_info_page ();

end /* help_cmd */

/* .page */

void about_ve_cmd ()

/*****************************************************************************
 *                                                                           *
 *  Display the "About VE" panel to the user                                 *
 *                                                                           *
 *****************************************************************************/

begin
    int i, x, cx, len;
    char msg_buf [82];       /* General purpose message construction buffer */

    update_curr_line ();

    create_full_screen_info_page ( " - About VE (2005)" );

    /* Print out the "About VE" panel centered on the page. Unlike the help */
    /* command, there is no need to check if more than one screen is needed.*/
    /* VE requires a minimum 20 lines, and this will unconditionally fit.   */

    cx = ( physical_cols - strlen(about_ve[0]) ) / 2;

    for ( i=0; i < ABOUT_VE_LINES; i++ )
       mv_writestr ( cx, i+2, FALSE, about_ve[i] );

    remove_full_screen_info_page ();

end /* about_ve_cmd */

/* .page */

void insert_cmd ()

/****************************************************************************
 *                                                                          *
 *   Toggle VE's mode between M_INSERT and M_OVERWRITE.                     *
 *                                                                          *
 ****************************************************************************/

begin

    if ( last_ve_mode == M_INSERT )
    begin
        /* Change from INSERT into OVERWRITE mode. In OVERWRITE mode, the   */
        /* RUBOUT command "erases" typed characters back to their original  */
        /* values by restoring them from curr_line. This only works if      */
        /* curr_line and line_buf are in alignment. They could be out of    */
        /* alignment if the user has been inserting text or deleting text   */
        /* prior to changing to OVERWRITE. To ensure all is well with the   */
        /* OVERWRITE mode RUBOUT, check to see if the line_buf and the      */
        /* curr_line are the same size. If not, resync them. This is not    */
        /* perfect, but it is pretty good.                                  */

        load_line_buf ();
        if ( line_buf.length != curr_line->length ) 
        begin
           update_curr_line ();
           load_line_buf ();
        end

        ve_mode = last_ve_mode = M_OVERWRITE;
    end

    else
        ve_mode = last_ve_mode = M_INSERT;

    set_status_line (TRUE, E_OK, last_ve_mode);

end /* insert_cmd */

/* .page */

void overwrite_cmd ()

/****************************************************************************
 *                                                                          *
 *   Change VE's mode to M_OVERWRITE.  (left over from QUNIX, not deleted   *
 *   yet, but unused by DOS/Linux versions, and unreachable by any command) *
 *                                                                          *
 ****************************************************************************/

begin

    ve_mode = last_ve_mode = M_OVERWRITE;
    set_status_line (TRUE, E_OK, last_ve_mode);

end /* overwrite_cmd */

/* .page */

void null_cmd ()

/*****************************************************************************
 *                                                                           *
 *   This command is a stub for unimplemented VE commands.                   *
 *                                                                           *
 *****************************************************************************/

begin

    return;

end

/* .page */

void get_cmd ()

/*****************************************************************************
 *                                                                           *
 *  Dummy entry point for the Block_Get (paste) function, to allow it to be  *
 *  used with repitition counts.                                             *
 *                                                                           *
 *****************************************************************************/

begin
    update_curr_line ();

    block_get ();

end

/* .page */

void xecute_cmd ()

/*****************************************************************************
 *                                                                           *
 *  Entry point for the macro_execute command, to allow it to be used        *
 *  with repetition counts.                                                  *
 *                                                                           *
 *****************************************************************************/

begin

    update_curr_line ();

    macro_execute ( NIL_STRING );

    set_status_line (TRUE, ve_exception, last_ve_mode);

end

/* .page */

void status_cmd ()

/*****************************************************************************
 *                                                                           *
 *  Write the current status of VE onto the users terminal.                  *
 *                                                                           *
 *****************************************************************************/

 begin
    int bytes_used, lines_used, checksum;
    int undo_actions, undo_items;
    char status_buf [82];
    char display_fn [82];
    int len, x, cx, cy;

    struct file_ctl   *column_buffer;
    struct screen_ctl temp_screen;

    update_curr_line ();

    create_full_screen_info_page ( " - VE Status Panel " );

    /* Provide filename, size and file format info for current file */

    truncate_str_left ( curr_filename, display_fn, physical_cols-18 );
    sprintf ( status_buf, "%s %s", "Curr Filename:  ", display_fn );
    mv_writestr ( 0, 3, FALSE, status_buf );

    file_info ( &curr_file, FALSE, &bytes_used, &lines_used, &checksum );

    sprintf ( status_buf, "%s %d %s %d %s", "Size:           ",
               bytes_used, "Chars,", lines_used, "Lines"     );
    mv_writestr ( 0, 4, FALSE, status_buf );

    mv_writestr ( 0, 5, FALSE, "File format:     " );
    if ( curr_file.file_format == 'd' ) then
         writestr ( "DOS/Win" );
    else
         writestr ( "Linux" );

    /* Provide size info about the line block buffer and the single line  */
    /* buffer                                                             */

    file_info ( &line_block, FALSE, &bytes_used, &lines_used, &checksum );

    sprintf ( status_buf, "%s %d %s %d %s", "Block Line Buf: ",
               bytes_used, "Chars,", lines_used, "Lines"     );
    mv_writestr ( 0, 7, FALSE, status_buf );

    file_info ( &line_del_block, FALSE, &bytes_used, &lines_used, &checksum );

    sprintf ( status_buf, "%s %d %s %d %s", "Single Line Buf:",
               bytes_used, "Chars,", lines_used, "Lines"     );
    mv_writestr ( 0, 8, FALSE, status_buf );

    /* Provide information about the column buffer                        */

    column_buffer = get_column_block_ptr ();
    file_info ( column_buffer, FALSE, &bytes_used, &lines_used, &checksum );
    sprintf ( status_buf, "%s %d %s %d %s", "Column Buf:     ",
               bytes_used, "Chars,", lines_used, "Lines"     );
    mv_writestr ( 0, 9, FALSE, status_buf );

    /* Provide information on the state of the undo buffers               */

    undo_actions = get_num_undo_queue_actions ();
    undo_items   = get_num_undo_queue_items ();

    sprintf ( status_buf, "%s %d %s %d %s", "Undo Actions:   ", 
              undo_actions, "Actions in", undo_items, "Undo Items" );
    mv_writestr ( 0, 11, FALSE, status_buf );

    /* Say how many bytes of memory are in use for files and block buffer */
    
    sprintf ( status_buf, "%s %d %s", "Memory In Use:  ",
              ram_in_use(), "Bytes" );
    mv_writestr ( 0, 13, FALSE, status_buf );

    /* Provide info on files and macros that are currently active */

    sprintf ( status_buf, "%s %d", "Files Open:     ", files_open );
    mv_writestr ( 0, 15, FALSE, status_buf );

    sprintf ( status_buf, "%s %d", "Macros Defined: ", macro_list.nodes );
    mv_writestr ( 0, 17, FALSE, status_buf );

    remove_full_screen_info_page ();

end /* status_cmd */

/* .page */

void pick_cmd ()

/****************************************************************************
 *                                                                          *
 *   Pick the current line, moving it to near top of screen.                *
 *                                                                          *
 ****************************************************************************/

begin

    update_curr_line ();

    /* Reconstruct current screen, and then repaint the screen.              */

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

end /* pick_cmd */

/* .page */

void info_cmd ()

/****************************************************************************
 *                                                                          *
 *   This command provides the user with info on the file they are in and   *
 *   their current position in that file.                                   *
 *                                                                          *
 ****************************************************************************/

begin

    update_curr_line ();

    curr_line_num = get_curr_line_num () + 1;

    display_curr_position ();

end /* info_cmd */

/* .page */

void mouse_cmd ()

/****************************************************************************
 *                                                                          *
 *   This command is called whenever VE receives a mouse event. It          *
 *   retrieves the mouse event and acts upon it.                            *
 *                                                                          *
 ****************************************************************************/

begin
    int    actual;        /* The actual number of lines traversed           */
    int    status;        /* Status of the traversal attempt                */
    MEVENT mouse_event;   /* The received mouse event                       */

    update_curr_line ();

    getmouse ( &mouse_event );

    if ( mouse_event.bstate & BUTTON1_CLICKED ||
         mouse_event.bstate & BUTTON2_CLICKED ||
         mouse_event.bstate & BUTTON3_CLICKED ) 
    begin
       /* Attempt to position the cursor to the cx, cy indicated by the     */
       /* received mouse click. If the user has clicked beyond the last     */
       /* line of the file, the cursor is positioned to the last line.      */
       /* Note that we must subtract 2 from the supplied "y" coordinate     */
       /* before attempting to reach the indicated line, since we must      */
       /* compensate for the status line and the divider line. We put the   */
       /* 2 back afterwards, when computing the ultimate cursor position.   */

       if ( mouse_event.y < 2 ) /* Check for clicks in status, divider line */
          mouse_event.y = 2;

       curr_line = traverse_list (&curr_file,      curr_screen.top, '+', &actual,
                                  mouse_event.y-2, &status);

       curr_screen.cx = mouse_event.x + ti_get_left_cx();
       curr_screen.cy = actual + 2;

       restore_cursor ();
    end

    else
    begin
       write_ve_msg ( "Unsupported mouse event" );
       writechar ( BEL );
    end

end /* mouse_cmd */

