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


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
               This module contains VE's terminal output routines. All
 
               output requests pass through the procedures here defined.
 
                                                     
 
               THIS MODULE MAKES EXTENSIVE USE OF NCURSES! IT, KBDIF.C

               AND MISCCMD.C ARE THE ONLY MODULES OF VE THAT ARE NOT FULLY 
               
               PORTABLE.


*/

/* .page */

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

#include <string.h>
#include <curses.h>

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

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

struct str_aperture_state {
   int ap_width;    /* The width of the physical screen                 */
   int ap_l;        /* The x co-ord of the left edge of the aperture    */
   int ap_r;        /* The x co-ord of the right edge of the aperture   */
};

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

#include "../ext/strprim.ext"
#include "../ext/termif.ext"

/* .page */

/*************************
 * PUBLIC VARIABLES      *
 *************************/

/* Define the logical and physical terminal size - will be                  */
/* overwritten in set_char_mode with the values from ncurses                */

int lines          = 24;   /* Logical terminal size                         */
int columns        = 80;

int physical_lines = 24;   /* Physical terminal size                        */
int physical_cols  = 80;

char wtab_char     = '?';  /* Output for TAB                                */

/* .page */

/***********************
 * PRIVATE VARIABLES   *
 ***********************/

/* Define the number of colors that are available                           */

#define NUM_COLORS 9

/* Human readable palette of available colors, for use in presenting color  */
/* selections as part of a menu.                                            */

static char color_palette [] = "Default  Black  Red  Green  Yellow  Blue  Magenta  Cyan  White";

/* Look up table for translating upper cased versions of the color palette  */
/* to an index into the below ncurses_colors table. This table must match   */
/* the order of the ncurses_colors table in order for this to work.         */

static char *avail_colors [NUM_COLORS] = {
    "DEFAULT",
    "BLACK",
    "RED",
    "GREEN",
    "YELLOW",
    "BLUE",
    "MAGENTA",
    "CYAN",
    "WHITE"
};

/* Ncurses color definitions which may be directly used in ncurses calls to */
/* achieve the indicated color on screen. The ordering of colors in this    */
/* table must match the ordering of the color names in the above            */
/* avail_colors table.                                                      */

static short ncurses_colors [NUM_COLORS] = {
     DEFAULT_COLOR,
     COLOR_BLACK,
     COLOR_RED,
     COLOR_GREEN,
     COLOR_YELLOW,
     COLOR_BLUE,
     COLOR_MAGENTA,
     COLOR_CYAN,
     COLOR_WHITE
};

/* Scroll Left and Right Variables                                          */

static BOOL horizontal_scrolling_enabled = TRUE;

/* Screen aperture variables - the screen window that VE displays is a      */
/* sliding aperture that can be moved up, down, left and right. All         */
/* implementation related to sliding the aperture left and right is         */
/* contained within this module. The remainder of VE assumes it is working  */
/* with an infinitely wide physical screen - well, more or less... since    */
/* the Qunix and DOS originals couldn't scroll left and right, VE's code    */
/* does know about and deal with the physical terminal size. This is        */
/* essentially defeated by setting the VE global "columns" to an extremely  */
/* large value (512 is used arbitarily).                                    */

#define VIRTUAL_SCREEN_WIDTH  512

static char ap_str [VIRTUAL_SCREEN_WIDTH];

static int ap_width;    /* The width of the physical screen                 */
static int ap_l;        /* The x co-ord of the left edge of the aperture    */
static int ap_r;        /* The x co-ord of the right edge of the aperture   */

struct str_aperture_state aperture_state; /* Saves state when scrolling has */
                                          /* been disabled                  */

/**********************
 * PRIVATE PROCEDURES *
 **********************/

static void move_aperture ( new_left_col )

/************************************************************************
 *                                                                      *
 * This function slides the screen aperture so that it's left edge is   *
 * the indicated column. If the new left column is the current left     *
 * left edge of the aperture, no action is taken.                       *
 *                                                                      *
 * move_aperature does relative insertion and deletion on the screen to *
 * update the onscreen display as efficiently as possible. This makes   *
 * the implicit assumption that the logical view of the screen held in  *
 * curr_screen matches the actual lines showing on the display. If this *
 * is not the case, all sorts of weirdness will occur.                  *
 *                                                                      *
 * Sooo... before calling this function, or calling move_cursor, which  *
 * is the main gateway into this function, make sure that the physical  *
 * screen more or less matches curr_screen. Caveat emptor!              *
 *                                                                      *
 ************************************************************************/

int new_left_col;

begin
   register int i, j;                /* Loop counters                   */
   register int num_columns;         /* Number of columns to shift by   */
   register int new_ap_l, new_ap_r;  /* New ap_l and ap_r values        */
   register struct node *this_line;  /* The present line of text        */
   int initial_cx, initial_cy;       /* cx, cy upon entry               */

   initial_cx = ve_win->_curx;       /* Save initial cursor position    */
   initial_cy = ve_win->_cury;

   /* Sanitize the requested left column, in case it is out of bounds   */

   if ( new_left_col < 0 ) 
      new_left_col = 0;

   else if ( new_left_col  > ( VIRTUAL_SCREEN_WIDTH - ap_width ) )
      new_left_col = VIRTUAL_SCREEN_WIDTH - ap_width;

   /* Compute new left and right aperture x co-ords                     */

   new_ap_l = new_left_col;
   new_ap_r = new_ap_l + ap_width - 1;

   this_line = curr_screen.top;

   if ( new_ap_l < ap_l )
   begin
      /* Moving to the left */

      num_columns = ap_l - new_ap_l;
      if ( num_columns >= ap_width )
      begin
         /* Check for the case of an attempt to shift the aperture by       */
         /* a full screen width or more. Adjust if this occurs.             */

         num_columns = ap_width;
      end

      for ( i = 0; ( (i < lines - 2) && ( this_line != NULL) ); i++ )
      begin
         /* For each line of the screen, create a string to insert at col 0 */
         /* that is either characters from the line, or blank spaces if     */
         /* there are no characters at this column position                 */

         for ( j = 0; j < num_columns; j++ )
         begin
            ap_str[j] = ' ';
            if ( this_line->length > new_ap_l+j )
               ap_str[j] = this_line->text[new_ap_l+j];

            if ( ap_str[j] == TAB )
               ap_str[j] = wtab_char;
         end

         /* Insert the resultant string at col 0 - this accomplishes the    */
         /* physical shifting of the screen display                         */

         ap_str[num_columns] = '\0';
         mvwinsstr ( ve_win, 2+i, 0, ap_str );  /* Shifts actual display    */

         this_line = this_line->fwd_link;
      end

   end /* moving to the left */

   else if ( new_ap_r > ap_r )
   begin
      /* Moving to the right */

      num_columns = new_ap_r - ap_r;
      if ( num_columns >= ap_width )
      begin
         /* Check for the case of an attempt to shift the aperture by       */
         /* a full screen width or more. Adjust if this occurs.             */

         num_columns = ap_width;
         ap_r        = new_ap_r - ap_width;
      end

      for ( i = 0; ( (i < lines - 2) && (this_line != NULL) ); i++ )
      begin
         /* For each line of the screen, delete the appropriate number of   */
         /* chars at column 0, creating blanks at the far end of the line.  */
         /* Create a string to write into that set of blanks that consists  */
         /* of either blanks or characters from the text line line, if it   */
         /* is long enough to have text at each column position.            */

         for ( j = 0; j < num_columns; j++ )
         begin
            if ( num_columns < ap_width )    /* No sense shifting if more than full screen */
               mvwdelch ( ve_win, 2+i, 0 );  /* Shifts the actual display                  */

            ap_str[j] = ' ';
            if ( this_line->length > ap_r+j+1 )
               ap_str[j] = this_line->text[ap_r+j+1];

            if ( ap_str[j] == TAB )
               ap_str[j] = wtab_char;
         end

         /* Write the resulting string into the blanks created earlier      */

         ap_str[num_columns] = '\0';
         mvwaddstr ( ve_win, 2+i, ap_width-num_columns, ap_str );

         this_line = this_line->fwd_link;
      end

   end /* moving to the right */

   /* Update the global aperture defintion, restore the cursor to its       */
   /* original position and we are done.                                    */

   ap_l = new_ap_l;
   ap_r = new_ap_r;

   wmove ( ve_win, initial_cy, initial_cx );

end /* move_aperture */

/* .page */

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

short get_default_fg_color ()

/**************************************************************************
 *                                                                        *     
 * This function returns the default foreground color.                    *     
 *                                                                        *     
 **************************************************************************/

begin

   return ( DEFAULT_FG_COLOR );

end /* get_default_fg_color */



short get_default_bg_color ()

/**************************************************************************
 *                                                                        *     
 * This function returns the default background color.                    *     
 *                                                                        *     
 **************************************************************************/

begin

   return ( DEFAULT_BG_COLOR );

end /* get_default_bg_color */

/*.page */

char *get_color_palette ()

/**************************************************************************
 *                                                                        *     
 * This function returns a pointer to a string that contains the list of  *     
 * available colors that can be used for VE's foreground and background   *     
 * color settings. Individual color names from this string may then be    *     
 * translated to color values that can be loaded into the options block   *     
 * by calling the get_color_value function, below. It returns color       *
 * values suitable for use by the wcolors ANSI control sequence handler   *
 * in its ncurses bkgd calls.                                             *
 *                                                                        *
 * This function returns a pointer to a string containing human readable  *
 * color names, such as "black white red yellow" etc.                     *
 *                                                                        *     
 **************************************************************************/

begin

   return ( color_palette );

end /* get_color_palette */

/* .page */

short get_color_value ( color_name )

/**************************************************************************
 *                                                                        *     
 * This function accepts a color name string, and returns a color code    *     
 * which can be loaded in the options block's color settings (fg_color    *
 * and bg_color) and subsequently used by the handling for the wcolors    *
 * ANSI control string to set colors using the ncurses bkgd call.         *
 *                                                                        *     
 * This function encapsulates all the ncurses specific elements of this,  *     
 * allowing VE operations that need to manipulate color to work only with *
 * color names and abstract color_value settings.                         *
 *                                                                        *     
 * This function returns -2 if the supplied color name cannot be mapped   *     
 * to a valid color, or (a) -1 for the default colors that were in effect *
 * when VE was start or (b) a color number >= 0 associated with the       *
 * supplied color name.                                                   *
 *                                                                        *     
 **************************************************************************/

char color_name [];

begin
   register int i;         /* loop counter                                  */
   register int len;       /* length of color name string                   */
   short color_value = -2; /* ncurses color value for the input color_name  */

   /* Upper case the provided name to remove case sensitivity.              */

   len = strlen ( color_name );
   for ( i = 0; i < len; i++ )
      color_name[i] = toupper(color_name[i]);

   /* Scan the avail_colors table to see if any color there matches the     */
   /* provided color name. If it does, use its index in that table to pull  */
   /* out and return the appropriate ncurses_colors value.                  */

   for ( i = 0; i < NUM_COLORS; i++)
      if ( strcmp(avail_colors[i], color_name) == 0 ) begin
         color_value = ncurses_colors[i];
         break;
      end

   return ( color_value );

end /* get_color_value */

/* .page */

void get_color_name ( color_value, color_name ) 

/**************************************************************************
 *                                                                        *     
 * This function accepts an ncurses color value, and returns a string     *     
 * which is the human readable name associated with that color.           *
 *                                                                        *     
 * This function returns the string's name if the color value is known,   *     
 * or "Unknown" if the color value is not known.                          *     
 *                                                                        *     
 **************************************************************************/

short color_value;         /* the ncurses color value                       */
char  color_name [];       /* the name string to load with the name         */

begin
   register int i;         /* loop counter                                  */
   register int len;       /* length of color name string                   */
   short color_ix = -1;    /* ncurses color value for the input color_name  */

   strcpy ( color_name, "Unknown" );

   /* Look up the supplied color number in the table of ncurses colors   */

   for ( i = 0; i < NUM_COLORS; i++ ) 
      if ( ncurses_colors[i] == color_value )
         color_ix = i;

   /* If the color was found, map it to its name from the avail_colors   */
   /* table. Convert the name to lower case before returning - more      */
   /* friendly looking                                                   */

   if ( color_ix >= 0 ) begin
      strcpy ( color_name, avail_colors[color_ix] );

      len = strlen ( color_name );
      for ( i = 0; i < len; i++ )
         color_name[i] = tolower(color_name[i]);
      color_name[0] = toupper(color_name[0]);
   end

end /* get_color_name */

/* .page */

void writechar (character)

/****************************************************************************
 *                                                                          *
 *   Write a char onto the terminal. This routine filters the char for      *
 *   and embedded TAB character and outputs the TAB char replacement if     *
 *   one is found.                                                          *
 *                                                                          *
 ****************************************************************************/

char character;  /* character to be output                                  */

begin
    register int column, line;
    register char out_char;

    /* Handle BEL and TAB                                                   */

    out_char = character;

    if ( out_char == '\t' ) then
         out_char = wtab_char;

    if ( out_char == BEL ) then
         beep ();

    else
    begin
        /* Normal write of a character to the present cursor position       */

        line   = ve_win->_cury;
        column = ve_win->_curx;

        if ( column == physical_cols - 1 )
        begin
           move_aperture ( ap_l + 1 );
           column -= 1;
        end

        mvwaddch (ve_win, line, column, out_char);
    end

end /* writechar */

/* .page */

void writechar_emphasis ( character )

/****************************************************************************
 *                                                                          *
 *   Write a char onto the terminal with emphasis that will make the char   *
 *   stand out. The form of the emphasis is left to this implementation. In *
 *   this case, color is used.                                              *
 *                                                                          *
 ****************************************************************************/

char character;  /* character to be output                                  */

begin
   short emphasis_fg;

   emphasis_fg = (options.bg_color == COLOR_RED) ? COLOR_BLUE : COLOR_RED;
   init_pair ( 3, emphasis_fg, options.bg_color );

   attron  ( COLOR_PAIR(3) );

   writechar ( character );

   attroff ( COLOR_PAIR(3) );

end /* writechar_emphasis */

/* .page */

void writestr (str)

/****************************************************************************
 *                                                                          *
 *   Write a string to the terminal.                                        *
 *                                                                          *
 ****************************************************************************/

char *str;     /* pointer to string to be printed                           */

begin

    waddstr ( ve_win, str );

end /* writestr */

/* .page */

void writestr_emphasis ( str )

/****************************************************************************
 *                                                                          *
 *   Write a string onto the terminal with emphasis that will make it       *
 *   stand out. The form of the emphasis is left to this implementation. In *
 *   this case, color and underlineing is used.                             *
 *                                                                          *
 ****************************************************************************/

char str[];               /* The string to output                           */

begin
   short emphasis_fg;

   emphasis_fg = (options.bg_color == COLOR_BLUE) ? COLOR_RED : COLOR_BLUE;
   init_pair ( 4, emphasis_fg, options.bg_color );

   attron   ( COLOR_PAIR(4) | A_UNDERLINE );

   writestr ( str );

   attroff  ( COLOR_PAIR(4) | A_UNDERLINE );

end /* writestr_emphasis */

/* .page */

void mv_writestr ( x, y, restore_when_done, str )

/****************************************************************************
 *                                                                          *
 * This function moves the cursor to the supplied x and y co-ordinates,     *
 * and then outputs the supplied string. If the restore_when_done flag is   *
 * TRUE, a restore_cursor operation is done as well.                        *
 *                                                                          *
 ****************************************************************************/

int x, y;                 /* The X and Y co-ordinates to move to            */
BOOL restore_when_done;   /* Do restore cursor when done?                   */
char str[];               /* The string to output                           */

begin

   ti_move_cursor ( x, y );
   waddstr ( ve_win, str );

   if ( restore_when_done ) begin

      x = (curr_screen.cx > columns - 1 ? columns - 1 : curr_screen.cx);
      y = curr_screen.cy;

      ti_move_cursor ( x, y );
   end

end /* mv_writestr */

/* .page */

void mv_writestr_emphasis ( x, y, restore_when_done, str )

/****************************************************************************
 *                                                                          *
 *   Write a string onto the terminal with emphasis that will make it       *
 *   stand out. The form of the emphasis is left to this implementation. In *
 *   this case, color and underlineing is used.                             *
 *                                                                          *
 ****************************************************************************/

int x, y;                 /* The X and Y co-ordinates to move to            */
BOOL restore_when_done;   /* Do restore cursor when done?                   */
char str[];               /* The string to output                           */

begin
   short emphasis_fg;

   emphasis_fg = (options.bg_color == COLOR_BLUE) ? COLOR_RED : COLOR_BLUE;
   init_pair ( 5, emphasis_fg, options.bg_color );

   attron  ( COLOR_PAIR(5) | A_UNDERLINE );

   mv_writestr ( x, y, restore_when_done, str );

   attroff ( COLOR_PAIR(5) | A_UNDERLINE );

end /* mv_writestr_emphasis */

/* .page */

void writeln (wtext, length, offset)

/****************************************************************************
 *                                                                          *
 *   Write a line of text onto the console. writeln and its equivalent      *
 *   mv_writeln are used exlusively to output text lines from the file,     *
 *   in that they perform filtering for embedded tabs, and DO NOT           *
 *   perform any filtering for ANSI terminal control sequences.             *
 *                                                                          *
 *   writeln generally displays the full portion of the line that is        *
 *   in the current screen aperture, irrespective of the cx position of the *
 *   cursor at the time writeln is called.                                  *
 *                                                                          *
 ****************************************************************************/

char wtext [];   /*  Text to write to terminal                              */
int  length;     /*  Number of chars in text                                */
int  offset;     /*  First text char to write                               */

begin
    register int i;            /* Loop counter                              */
    register int start_ix;     /* Starting column to output                 */
    register int end_ix;       /* Last column to output                     */
    char text [MAX_LINE_SIZE]; /* Output buffer                             */

    if ( length < ap_l || length == 0 )
    begin
       /* Nothing to display - the screen is scrolled right beyond the end   */
       /* of the line, so set up a zero length string                        */

       text[0] = '\0';
    end

    else
    begin
       /* There is at least some text on the line that extends beyond the    */
       /* current left edge of the aperture. Copy it to the output string,   */
       /* filtering for TABs and text wrapper markers as we go.              */

       start_ix = ( ap_l   > offset ) ? ap_l : offset;
       end_ix   = ( length > ap_r   ) ? ap_r : length - 1;

       if ( end_ix < start_ix )  /* Dumb ass safety check                    */
          end_ix = start_ix;

       text[end_ix - start_ix + 1] = '\0';

       /* Filter out tabs and cursor position markers used by the text       */
       /* wrapper                                                            */

       for (i = start_ix; i <= end_ix; i++)
       begin
          text[i - start_ix] = wtext[i];       /* Copy to output string      */

          if ( wtext[i] == TAB )               /* Filter out TABs            */
             text[i - start_ix] = wtab_char;
          if ( wtext[i] == WRAP_MARKER )       /* Filter out wrapper marks   */
             text[i - start_ix] = ' ';
       end
    end

    /* Print the line and we are done                                        */

    mvwaddstr ( ve_win, ve_win->_cury, start_ix-ap_l, text );

end /* writeln */

/* .page */

void mv_writeln ( x, y, restore_when_done, text, len, offset )

/****************************************************************************
 *                                                                          *
 * This function moves the cursor to the supplied x and y co-ordinates,     *
 * and then outputs the supplied line. If the restore_when_done flag is     *
 * TRUE, a restore_cursor operation is done as well.                        *
 *                                                                          *
 ****************************************************************************/

int x, y;                 /* The X and Y co-ordinates to move to            */
BOOL restore_when_done;   /* Do restore cursor when done?                   */
char text[];              /* The text to output - not a string (no '\0')    */
int len;                  /* The length of the text to output               */ 
int offset;               /* Offset to start writing at                     */

begin

   ti_move_cursor ( x, y );

   writeln ( text, len, offset );

   if ( restore_when_done ) begin

      x = (curr_screen.cx > columns - 1 ? columns - 1 : curr_screen.cx);
      y = curr_screen.cy;

      ti_move_cursor ( x, y );
   end

end /* mv_writeln */

/* .page */

int ti_set_char_mode ()

/*****************************************************************************
 *                                                                           *
 *   Save the current terminal attributes, and then set the console to       *
 *   single character, non echoing mode.                                     *
 *                                                                           *
 *   This function returns one of the following values:                      *
 *   0 - char mode set OK                                                    *
 *   1 - physical terminal too small                                         *
 *   2 - physical terminal too large                                         *
 *                                                                           *
 *****************************************************************************/

begin
   int ret_val = 0;         /* Assume success to start                       */

   /* In the QUNIX original, this procedure set the terminal into            */
   /* unbreakable mode, and disabled line editing and echoing. In the Linux  */
   /* port, this procedure initializes the ncurses environment.              */

   ve_win = initscr ();     /* initializes ncurses                           */
   start_color ();          /* enable use of colors on the terminal          */
   use_default_colors ();   /* Define color "-1" as defaults when VE called  */
   keypad ( stdscr, TRUE ); /* enable PgUp, PgDn, Home, etc. on keypad       */
   raw ();                  /* turns off buffering                           */
   noecho ();               /* disables echoing                              */

   /* Enable receipt of mouse events if the relevant option is set.          */

   if ( options.mouse_enabled)
      mousemask ( ALL_MOUSE_EVENTS, NULL);

   /* Pick up the xterm size from ncurses and use it to define the logical   */
   /* and physical screen sizes and the initial aperture values. Note that   */
   /* the physical screen size (particularly its width) is needed for use    */
   /* with the status line, since this isn't subject to left and right       */
   /* scrolling                                                              */

   ap_width = COLS;                 /* Define the screen aperture            */
   ap_l     = 0;                    /* First column inside aperture          */
   ap_r     = ap_l + ap_width - 1;  /* Last column inside aperture           */

   aperture_state.ap_width = ap_width;  /* Initialize aperture state info    */
   aperture_state.ap_l     = ap_l;
   aperture_state.ap_r     = ap_r;

   lines    = LINES;                /* Advertize logical screen size         */
   columns  = VIRTUAL_SCREEN_WIDTH;

   physical_lines = LINES;          /* Advertize physical screen size        */
   physical_cols  = COLS;

   /* Check that the screen geometry is OK - if not return an error code     */

   if ( physical_cols < 80 || physical_lines < 20 )
      ret_val = 1;

   if ( physical_cols >= VIRTUAL_SCREEN_WIDTH )
      ret_val = 2;

   return ( ret_val );

end /* ti_set_char_mode */

/* .page */

void ti_reset_char_mode ()

/**************************************************************************
 *                                                                        *     
 *   Reset the terminal to its previous line editing mode, by restoring   *
 *   the previously saved set of terminal attributes.                     *
 *                                                                        *     
 **************************************************************************/

begin
   /* In the QUNIX original, this procedure returned the terminal to the     */
   /* state it was in prior to invocation of VE. Specifically, it reallowed  */
   /* the break char, and re-enabled line editing and echoing. In the Linux  */
   /* port, this procedure simply terminates the ncurses environment.        */

   endwin ();



end /* ti_reset_char_mode */

/* .page */

void ti_move_cursor ( col, row )

/*****************************************************************************
 * Move the cursor to the indicated column and row positions.                *
 *                                                                           *
 * This function will cause the screen to scroll left or right in order to   *
 * show the requested column. It does this using the move_aperture function. *
 *                                                                           *
 * move_aperature does relative insertion and deletion on the screen to      *
 * update the onscreen display as efficiently as possible. This makes        *
 * the implicit assumption that the logical view of the screen held in       *
 * curr_screen matches the actual lines showing on the display. If this      *
 * is not the case, all sorts of weirdness will occur.                       *
 *                                                                           *
 * Sooo... before calling move_cursor, make sure that the physical           *
 * screen more or less matches curr_screen. Caveat emptor!                   *
 *                                                                           *
 *****************************************************************************/

int col, row;



begin
   if ( ( row >= 2 ) && ( horizontal_scrolling_enabled ) )
   begin
      /* We are addressing the file display area. This area scrolls left    */
      /* and right, so adjust the column relative to the current aperture,  */
      /* moving that aperture if needed, and then move the cursor. Note     */
      /* that since we are moving the aperture anyway, we move it 10 spaces */
      /* more than needed, so that the cursor doesn't end up at the left or */
      /* right edge of the screen. The value '10' is completely arbitrary.  */

      if ( col < ap_l )
         move_aperture ( ( ((col - 10) < 0) ? 0 : col - 10 ) );

      else if ( col > ap_r )
         move_aperture ( ap_l + (col - ap_r) + 10 );

      wmove ( ve_win, row, (col - ap_l) );
   end

   else
   begin
      /* If the row we are moving to is row 0 or row 1, then we are         */
      /* addressing the status or divider lines. Neither of these scrolls   */
      /* left and right so just use the provided row and column. The same   */
      /* is true if horizontal scrolling is disabled, which most VE full    */
      /* screen dialogs do (disable it, present their dialog, re-enable it  */
      /* when done).                                                        */

      wmove ( ve_win, row, col );
   end


end /* ti_move_cursor */

/* .page */

void ti_set_screen_colors ()

/**************************************************************************
 *                                                                        *     
 * This function changes the screen colors based on the current options   *     
 *                                                                        *     
 **************************************************************************/

begin

   init_pair ( 1, options.fg_color, options.bg_color );

   wbkgd ( ve_win, COLOR_PAIR(1) );

end /* set_screen_colors */

/* .page */

void ti_force_screen_update ()

/*****************************************************************************
 * This function forces an update of the physical screen via a call to the   *
 * ncurses wrefresh routine.                                                 *
 *****************************************************************************/

begin

   wrefresh ( ve_win );

end /* ti_force_screen_update */

/* .page */

int ti_get_left_cx ()

/*****************************************************************************
 * This function returns the logical cx value of the left edge of the        *
 * physical screen.                                                          *
 *****************************************************************************/

begin

   return ( ap_l );

end /* ti_get_left_cx */


void ti_set_left_cx ( new_ap_l )

/*****************************************************************************
 * This function sets the logical cx value of the left edge of the           *
 * physical screen. This function is used when shifting between open files,  *
 * to set the aperture to the aperture that is in use for the file being     *
 * switched to.                                                              *
 *****************************************************************************/

int new_ap_l;



begin

   ap_l = new_ap_l;

end /* ti_set_left_cx */

/* .page */

int ti_get_right_cx ()

/*****************************************************************************
 * This function returns the logical cx value of the right edge of the       *
 * physical screen.                                                          *
 *****************************************************************************/

begin

   return ( ap_r );

end /* ti_get_right_cx */


void ti_set_right_cx ( new_ap_r )

/*****************************************************************************
 * This function sets the logical cx value of the right edge of the          *
 * physical screen. This function is used when shifting between open files,  *
 * to set the aperture to the aperture that is in use for the file being     *
 * switched to.                                                              *
 *****************************************************************************/

int new_ap_r;



begin

   ap_r = new_ap_r;

end /* ti_set_right_cx */

/* .page */

void ti_enable_horizontal_scrolling ()

/*****************************************************************************
 * This function enables horizontal scrolling by setting the associated      *
 * BOOL to true.                                                             *
 *****************************************************************************/

begin

   ap_width = aperture_state.ap_width;
   ap_l     = aperture_state.ap_l;
   ap_r     = aperture_state.ap_r;

   horizontal_scrolling_enabled = TRUE;

end /* ti_enable_horizontal_scrolling */

/* .page */

void ti_disable_horizontal_scrolling ()

/*****************************************************************************
 * This function disables horizontal scrolling by setting the associated     *
 * BOOL to false.                                                            *
 *****************************************************************************/

begin

   aperture_state.ap_width = ap_width;
   aperture_state.ap_l     = ap_l;
   aperture_state.ap_r     = ap_r;

   ap_l = 0;
   ap_r = ap_l + ap_width - 1;

   horizontal_scrolling_enabled = FALSE;

end /* ti_disable_horizontal_scrolling */

/* .page */

void ti_scroll_left ( screen_width_percentage )

/*****************************************************************************
 * This function provides an externally visible interface to scroll the the  *
 * screen window left by the indicated percentage of the total screen width  *
 *****************************************************************************/

int screen_width_percentage;

begin
   register int  ap_delta;  /* Number of columns to scroll                   */
   register int  new_ap_l;  /* New x co-ord for left edge of aperture        */
   char msg_buf [82];       /* Message buffer for result message             */

   ap_delta = ( physical_cols * screen_width_percentage ) / 100;

   if ( ap_l > ap_delta )
      new_ap_l = ap_l - ap_delta;
   else
      new_ap_l = 0;

   move_aperture ( new_ap_l );

end /* ti_scroll_left */

/* .page */

void ti_scroll_right ( screen_width_percentage )

/*****************************************************************************
 * This function provides an externally visible interface to scroll the the  *
 * screen window left by the indicated percentage of the total screen width  *
 *****************************************************************************/

int screen_width_percentage;

begin
   register int  ap_delta;  /* Number of columns to scroll                   */
   register int  new_ap_l;  /* New x co-ord for left edge of aperture        */
   char msg_buf [82];       /* Message buffer for result message             */

   ap_delta = ( physical_cols * screen_width_percentage ) / 100;

   if ( ap_r < (VIRTUAL_SCREEN_WIDTH - ap_delta) )
      new_ap_l = ap_l + ap_delta;
   else
      new_ap_l = VIRTUAL_SCREEN_WIDTH - ap_delta;

   move_aperture ( new_ap_l );

end /* ti_scroll_right */

/* .page */

void ti_draw_last_column ( this_line )

/*****************************************************************************
 * This function checks to see if the supplied line has a character at the   *
 * last position of the physical display. If it does, this routine outputs   *
 * that character.                                                           *
 *****************************************************************************/

struct node *this_line;

begin

   if ( this_line->length > ap_r )
      mvwaddch ( ve_win, ve_win->_cury, ap_width - 1, this_line->text[ap_r] );

end /* ti_draw_last_column */

/* .page */

void ti_clear_display ( starting_line )

/*****************************************************************************
 * This function clears the physical display, starting at the provided line  *
 * down through the remainder of the screen.                                 *
 *****************************************************************************/

int starting_line;

begin

   wmove ( ve_win, starting_line, 0 );
   wclrtobot (ve_win);

end /* ti_clear_display */

/* .page */

void ti_cursor_up ()

/*****************************************************************************
 * This function moves the cursor up one line.                               *
 *****************************************************************************/

begin
    register int line, column;

    column = ve_win->_curx;
    line   = ( ve_win->_cury > 0 ) ? ve_win->_cury - 1 : ve_win->_cury;

    wmove ( ve_win, line, column );

end /* ti_cursor_up */

/* .page */

void ti_cursor_down ()

/*****************************************************************************
 * This function moves the cursor down one line.                             *
 *****************************************************************************/

begin
    register int line, column;

    column = ve_win->_curx;
    line   = ( ve_win->_cury < physical_lines ) ? ve_win->_cury + 1 : ve_win->_cury;

    wmove ( ve_win, line, column );

end /* ti_cursor_down */

/* .page */

void ti_cursor_left ()

/*****************************************************************************
 * This function moves the cursor left one line.                             *
 *****************************************************************************/

begin
    register int line, column;

    /* If we are at the left edge of the screen, simply scroll the     */
    /* screen left by one column                                       */

    if ( ve_win->_curx == 0 )
       move_aperture ( ap_l - 1 );

    else
    begin
       column = ve_win->_curx - 1;
       line = ve_win->_cury;
       wmove ( ve_win, line, column );
    end

end /* ti_cursor_left */

/* .page */

void ti_cursor_right ()

/*****************************************************************************
 * This function moves the cursor right one line.                            *
 *****************************************************************************/

begin
    register int line, column;

    /* If we are at the right edge of the screen, simply scroll the    */
    /* screen right by one column.                                     */

    if ( ve_win->_curx == physical_cols-1 )
       move_aperture ( ap_l + 1 );

    else
    begin
       column = ve_win->_curx + 1;
       line = ve_win->_cury;
       wmove ( ve_win, line, column );
    end

end /* ti_cursor_right */

/* .page */

void ti_insert_char ( char_to_insert )

/*****************************************************************************
 * This function inserts a blank character at the current cursor position    *
 *****************************************************************************/

char char_to_insert;

begin
    register int line, column;

    /* If we are at the right edge of the screen, start by scrolling   */
    /* the screen right by one column.                                 */

    column = ve_win->_curx;
    line   = ve_win->_cury;

    if ( ve_win->_curx == physical_cols-1 )
    begin
       move_aperture ( ap_l + 1 );
       column--;
    end

    mvwinsch (ve_win, line, column++, char_to_insert);
    wmove    (ve_win, line, column                  );

end /* ti_insert_char */

/* .page */

void ti_insert_str ( str )

/*****************************************************************************
 * This function inserts a string at the current cursor position             *
 *****************************************************************************/

char str [];

begin
    register int line, column, len, amount;

    column = ve_win->_curx;
    line   = ve_win->_cury;

    len = strlen ( str );

    if ( column + len > ap_width )
    begin
       /* If possible, move the aperture such that the whole inserted string */
       /* will be visible. If this is not possible, don't bother             */

       amount = (len <= ap_width) ? (column + len - ap_width) : 0;
       column -= amount;

       if ( amount > 0 )
          move_aperture ( ap_l + amount );
    end

    mvwinsstr ( ve_win, line, column, str );

end /* ti_insert_str */

/* .page */

void ti_delete_line ( line_num )

/*****************************************************************************
 * This function deletes the line at the supplied line number, moving the    *
 * screen up from that line onwards.                                         *
 *****************************************************************************/

int line_num;



begin

   wmove ( ve_win, line_num, ve_win->_curx );
   wdeleteln (ve_win);

end /* ti_delete_line */

/* .page */

void ti_insert_line ( line_num )

/*****************************************************************************
 * This function inserts a line at the supplied line number, moving the      *
 * screen down by one line from that line onwards.                           *
 *****************************************************************************/

int line_num;



begin

   wmove ( ve_win, line_num, ve_win->_curx );
   winsertln (ve_win);

end /* ti_insert_line */

/* .page */

void ti_clear_to_eol ( line_num )

/*****************************************************************************
 * This function clears the indicated line number from the current x co-ord  *
 * to the end of the line                                                    *
 *****************************************************************************/

int line_num;



begin

   wmove ( ve_win, line_num, ve_win->_curx );
   wclrtoeol (ve_win);

end /* ti_clear_to_eol */

/* .page */

void ti_delete_char ()

/*****************************************************************************
 * This function deletes the char under the cursor, moving the line left by  *
 * one character position from there onwards.                                *
 *****************************************************************************/

begin

   wdelch (ve_win);

end /* ti_delete_char */

/* .page */

void ti_rubout ()

/*****************************************************************************
 * This function implements the rubout functionality, deleting the character *
 * to the left of the cursor.                                                *
 *****************************************************************************/

begin
   register int line, column;

   /* There is no single ncurses operation for this one - have to do it the  */
   /* hard way: cursor left, output a space, cursor left                     */

   /* If we are at the left edge of the screen, start by scrolling   */
   /* the screen left by one column                                  */

   if ( ve_win->_curx == 0 )
      move_aperture ( ap_l - 1 );

   else
      column = ve_win->_curx - 1;

   line = ve_win->_cury;

   wmove ( ve_win, line, column );
   waddch ( ve_win, ' ' );
   wmove ( ve_win, line, column );

end /* ti_rubout */

/* .page */

void display_scroll_indicator ()

/*****************************************************************************
 * This function displays or clears the scroll indicator in the status line, *
 * immediately to the left of the mode (INS/OVR) indicator                   *
 *****************************************************************************/

begin
   int SCROLL_X;
   short indicator_fg;
   char display_str [4];

   SCROLL_X = MODE_X;

   if ( ap_l > 0 )
   begin
      strcpy ( display_str, "<-" );
      indicator_fg = (options.bg_color == COLOR_RED) ? COLOR_BLUE : COLOR_RED;
   end

   else
   begin
      strcpy ( display_str, "  " );
      indicator_fg = options.fg_color;
   end

   init_pair ( 2, indicator_fg, options.bg_color );

   ti_move_cursor ( SCROLL_X, 0 );
   attron ( COLOR_PAIR(2) );
   waddstr ( ve_win, display_str );
   attroff ( COLOR_PAIR(2) );

end /* display_scroll_indicator */

