| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
Textview.cpp
/*---------------------------------------------------------------------------------*
* "textview.cpp"
*
* Definitions for the textview class. These provide for user input and
* the display of spreadsheet data.
*
* Stephen R. Schmitt
*
* January 1999
*/
#include <unistd.h> // sleep()
#include "textview.h"
#include "scalc.h"
const int KEY_DELETE = 127;
const int KEY_TAB = 9;
const int OFFSET = 4; // start of cell edit line
char menu[] =
"Tab-edit Q-quit S-save L-load C-clear R-recalc A-auto";
char edit[] =
"Enter-finish Tab-cancel ";
/*---------------------------------------------------------------------------------*
* "textview" constructor initializes curses facility
*
* returns: nothing
*/
textview::textview()
{
// initialize curses
initscr(); // initialize curses
cbreak(); // inhibit response to Ctrl-C
noecho(); // stop echoing keyboard input
nonl(); // no CR after LF
intrflush( stdscr, FALSE ); // make curses see true screen
keypad( stdscr, TRUE ); // enable keypad
// initialize view
CX = 0;
CY = 0;
grid();
}
/*---------------------------------------------------------------------------------*
* "~textview" destructor restores terminal from curses settings.
*
* returns: nothing
*/
textview::~textview()
{
endwin(); // restore terminal
printf( "\n\n" );
}
/*---------------------------------------------------------------------------------*
* "run" operates view of spreadsheet
*
* returns: nothing
*/
void textview::run()
{
bool ok = true;
int key;
// initialize the display
update_cells();
enter_cell();
function_menu( NULL );
while( ok )
{
key = getch();
switch( key )
{
case KEY_UP: move_up(); break;
case KEY_DOWN: move_down(); break;
case KEY_RIGHT: move_right(); break;
case KEY_LEFT: move_left(); break;
case KEY_TAB: edit_cell_contents(); break;
default: ok = function_menu( key ); break;
}
}
}
/*---------------------------------------------------------------------------------*
* "grid" displays the grid indices on left and top of view
*
* returns: nothing
*/
void textview::grid()
{
int i;
char j;
char buffer[16];
for( j = 0; j < CXmax; j++ )
{
sprintf( buffer, "| %c |", j + 'A' );
put_string( Ypos( -1 ), Xpos( j ) - 1, buffer );
}
for( i = 0; i < CYmax; i++ )
{
sprintf( buffer, "%2d", i + 1 );
put_string( Ypos( i ) - 1, 0, "--" );
put_string( Ypos( i ) , 0, buffer );
put_string( Ypos( i ) + 1, 0, "--" );
}
put_string( Ypos( CYmax ), 0, menu );
}
/*---------------------------------------------------------------------------------*
* "update_cells" writes the current value or text of each cell.
*
* returns: nothing
*/
void textview::update_cells()
{
int x, y;
for( y = 0; y < CYmax; y++ )
{
for( x = 0; x < CXmax; x++ )
show_cell( y, x );
}
refresh();
}
/*---------------------------------------------------------------------------------*
* "put_string" puts a string of text on the screen
*
* returns: nothing
*/
void textview::put_string( int y, // row
int x, // col
char *str ) // to put on screen
{
move( y, x );
printw( "%s", str );
move( LINES - 1, COLS - 1 ); // 'hide' cursor
}
/*---------------------------------------------------------------------------------*
* "clear_to_eol" clears a row from col to the right edge of the screen
*
* returns: nothing
*/
void textview::clear_to_eol( int y, // row
int x ) // col
{
move( y, x );
clrtoeol();
move( LINES - 1, COLS - 1 ); // 'hide' cursor
}
/*---------------------------------------------------------------------------------*
* "move_up" moves the selected cell up one line or wrap around.
*
* returns: nothing
*/
void textview::move_up()
{
leave_cell();
CY--;
if( CY < 0 )
CY = CYmax - 1;
enter_cell();
}
/*---------------------------------------------------------------------------------*
* "move_left" moves the selected cell left one column or wrap around.
*
* returns: nothing
*/
void textview::move_left()
{
leave_cell();
CX--;
if( CX < 0 )
CX = CXmax - 1;
enter_cell();
}
/*---------------------------------------------------------------------------------*
* "move_down" moves the selected cell down one line or wrap around.
*
* returns: nothing
*/
void textview::move_down()
{
leave_cell();
CY++;
if( CY >= CYmax )
CY = 0;
enter_cell();
}
/*---------------------------------------------------------------------------------*
* "move_right" moves the selected cell right one column or wrap around.
*
* returns: nothing
*/
void textview::move_right()
{
leave_cell();
CX++;
if( CX >= CXmax )
CX = 0;
enter_cell();
}
/*---------------------------------------------------------------------------------*
* "enter_cell" updates the screen for the just selected cell.
*
* returns: nothing
*/
void textview::enter_cell()
{
// draw box to show selected cell
put_string( Ypos( CY ) - 1, Xpos( CX ) - 1, "+--------------+" );
put_string( Ypos( CY ), Xpos( CX ) - 1, " " );
put_string( Ypos( CY ) + 1, Xpos( CX ) - 1, "+--------------+" );
// rewrite cell value and update cell contents on edit line
show_cell_contents( CY, CX );
show_cell( CY, CX );
}
/*---------------------------------------------------------------------------------*
* "leave_cell" updates the screen for the just de-selected cell.
*
* returns: nothing
*/
void textview::leave_cell()
{
put_string( Ypos( CY ) - 1, Xpos( CX ) - 1, " " );
put_string( Ypos( CY ), Xpos( CX ) - 1, " " );
put_string( Ypos( CY ) + 1, Xpos( CX ) - 1, " " );
//rewrite cell value since it was blotted out
show_cell( CY, CX );
}
/*---------------------------------------------------------------------------------*
* "show_cell" creates a string for a cell to display corresponding to
* the cells status.
*
* returns: nothing
*/
void textview::show_cell( int y, // row
int x ) // col
{
double val; // value of cell expression
char buf[MAX_CONTENTS]; // work space
// blank old contents
memset( buf, ' ', MAX_CONTENTS );
buf[CWmax - 1] = 0;
put_string( Ypos(y), Xpos(x), buf );
switch( ss->get_cell_status( y, x ) )
{
case TEXTCELL:
ss->get_cell_contents( y, x, buf );
break;
case EXPRESSION:
val = ss->get_cell_value( y, x );
sprintf( buf, "%*g", CWmax - 1, val );
break;
case SYNTAX:
strcpy( buf, " * Error * " );
break;
default:
buf[0] = 0;
break;
}
// put new contents
buf[CWmax - 1] = 0;
put_string( Ypos(y), Xpos(x), buf );
}
/*---------------------------------------------------------------------------------*
* "show_cell_contents" displays the contents of the selected cell on
* the edit line.
*
* returns: nothing
*/
void textview::show_cell_contents( int y, // row
int x ) // col
{
char buffer[MAX_CONTENTS]; // work space
// clear existing text
clear_to_eol( 0, 0 );
// update cell name field and data entry field
// columns lettered A ... rows numbered 1 ...
sprintf( buffer, "%c%c :", char( 'A' + CX ), char( '1' + CY ) );
put_string( 0, 0, buffer );
// add cell contents
ss->get_cell_contents( CY, CX, buffer );
put_string( 0, OFFSET, buffer );
// add message line
cell_message( y, x );
}
/*---------------------------------------------------------------------------------*
* "cell_message" creates a message to display under the edit line
* showing details of the cell's status.
*
* returns: nothing
*/
void textview::cell_message( int y, // row
int x ) // column
{
int n; // error location
char buf[MAX_CONTENTS]; // work space
// blank old contents
clear_to_eol( 1, 0 );
switch( ss->get_cell_status( y, x ) )
{
case TEXTCELL:
sprintf( buf, "text" );
break;
case EXPRESSION:
sprintf( buf, "expression" );
break;
case SYNTAX:
n = ss->get_cell_errloc( y, x );
if( 0 < n )
{
// point to location of syntax error
memset( buf, ' ', MAX_CONTENTS );
buf[OFFSET + n - 1] = '^';
buf[OFFSET + n] = 0;
}
break;
default:
sprintf( buf, "empty" );
break;
}
// write message
put_string( 1, 0, buf );
}
/*---------------------------------------------------------------------------------*
* "edit_cell_contents" processes text input which changes the active cell
* contents.
*
* returns: nothing
*/
void textview::edit_cell_contents()
{
int n, ch;
bool done = false;
char buffer[MAX_CONTENTS];
// setup for input
put_string( Ypos( CYmax ), 0, edit );
show_cell_contents( CY, CX );
memset( buffer, 0, MAX_CONTENTS );
ss->get_cell_contents( CY, CX, buffer );
n = strlen( buffer );
move( 0, OFFSET + n );
while( !done )
{
ch = getch();
switch( ch )
{
case KEY_TAB: // restore display to original
ss->get_cell_contents( CY, CX, buffer );
put_string( 0, OFFSET, buffer );
case '\r': // Enter or Return
done = true;
break;
case '\b': // backspace (not on all terminals)
case KEY_DELETE:
if( 0 < n ) n--;
memmove( &buffer[n], &buffer[n+1], MAX_CONTENTS - n - 1 );
break;
case KEY_RIGHT:
if( int( strlen( buffer ) ) > n ) n++;
break;
case KEY_LEFT:
if( 0 < n ) n--;
break;
default:
// add char when buffer not full and char is text
if( n < MAX_CONTENTS - 1 && isprint( ch ) )
{
memmove( &buffer[n+1], &buffer[n], MAX_CONTENTS - n - 1 );
buffer[n] = char( ch );
n++;
buffer[MAX_CONTENTS - 1] = 0; // truncate at limit
}
break;
}
clear_to_eol( 0, OFFSET );
put_string( 0, OFFSET, buffer );
move( 0, OFFSET + n );
}
// update data entry line
ss->put_cell_contents( CY, CX, buffer );
ss->evaluate( CY, CX );
if( auto_calc )
{
ss->recalculate();
update_cells();
}
show_cell_contents( CY, CX );
show_cell( CY, CX );
put_string( Ypos( CYmax ), 0, menu );
}
/*---------------------------------------------------------------------------------*
* "function_menu" displays a list of function on the message line.
*
* returns: false if Quit selected, else true
*/
bool textview::function_menu( int ch ) // user input from keyboard
{
bool run = true; // continue program
switch( tolower( char( ch ) ) )
{
case 'q':
run = fn_quit(); // program terminates on false
break;
case 's':
fn_save();
break;
case 'l':
fn_load();
break;
case 'c':
fn_clear();
break;
case 'r':
fn_recalc();
break;
case 'a':
// toggle mode
auto_calc = auto_calc ? false : true;
break;
}
clear_to_eol( 2, 0 );
if( auto_calc )
put_string( 2, 0, "AutoCalc On " );
else
put_string( 2, 0, "AutoCalc Off" );
return run;
}
/*---------------------------------------------------------------------------------*
* "fn_quit" asks user to confirm quit command.
*
* returns: false if Quit confirmed, else true
*/
bool textview::fn_quit()
{
clear_to_eol( 2, 0 );
put_string( 2, 0, "Quit? [y|N]" );
if( tolower( getch() ) == 'y' )
return false;
else
return true;
}
/*---------------------------------------------------------------------------------*
* "set_filename" sets the name of the file to be saved or loaded.
*
* returns: false to abort operation, else true
*/
bool textview::set_filename()
{
int n, ch; // index, char
bool ok = true; // do operation
bool done = false; // continue editing
char buffer[MAX_CONTENTS]; // work space
// setup filename entry line and turn on cursor
put_string( Ypos( CYmax ), 0, edit );
clear_to_eol( 2, 0 );
put_string( 2, 0, "File? " );
memset( buffer, 0, MAX_CONTENTS );
ss->get_filename( buffer );
put_string( 2, 6, buffer );
n = strlen( buffer );
move( 2, 6 + n );
while( !done )
{
ch = getch();
switch( ch )
{
case KEY_TAB: // abort operation
ok = false;
case '\r': // Enter
done = true;
break;
case '\b': // Backspace
case KEY_DELETE:
if( 0 < n ) n--;
memmove( &buffer[n], &buffer[n+1], MAX_CONTENTS - n - 1 );
break;
case KEY_RIGHT: // -->
if( int( strlen( buffer ) ) > n ) n++;
break;
case KEY_LEFT: // <--
if( 0 < n ) n--;
break;
default:
// add character if buffer not full and is text character
if( n < MAX_CONTENTS - 1 && isprint( ch ) )
{
memmove( &buffer[n+1], &buffer[n], MAX_CONTENTS - n - 1 );
buffer[n] = char( ch );
n++;
buffer[MAX_CONTENTS - 1] = 0; // truncate at limit
}
break;
}
clear_to_eol( 2, 6 );
put_string( 2, 6, buffer );
move( 2, 6 + n );
}
if( ok )
ss->put_filename( buffer );
put_string( Ypos( CYmax ), 0, menu );
return ok;
}
/*---------------------------------------------------------------------------------*
* "fn_save" saves the current spread sheet.
*
* returns: nothing
*/
void textview::fn_save()
{
if( !set_filename() )
return;
clear_to_eol( 2, 0 );
put_string( 2, 0, "Saving spreadsheet . . ." );
// let user see operation
refresh();
sleep( 1 );
if( !ss->save() )
{
clear_to_eol( 2, 0 );
put_string( 2, 0, "Save failed" );
refresh();
sleep( 2 );
}
}
/*---------------------------------------------------------------------------------*
* "fn_load" loads a spread sheet from disk.
*
* returns: nothing
*/
void textview::fn_load()
{
if( !set_filename() )
return;
clear_to_eol( 2, 0 );
put_string( 2, 0, "Loading spreadsheet . . ." );
// let user see operation
refresh();
sleep( 1 );
// perform operation
if( ss->load() )
{
ss->recalculate();
update_cells();
}
else
{
clear_to_eol( 2, 0 );
put_string( 2, 0, "Load failed" );
refresh();
sleep( 2 );
}
// update selected cell
show_cell_contents( CY, CX );
}
/*---------------------------------------------------------------------------------*
* "fn_clear" clears the current spread sheet.
*
* returns: nothing
*/
void textview::fn_clear()
{
clear_to_eol( 2, 0 );
put_string( 2, 0, "Clear this worksheet? [y|N]" );
if( tolower( getch() ) == 'y' )
{
ss->clear_sheet();
update_cells();
}
enter_cell();
}
/*---------------------------------------------------------------------------------*
* "fn_recalc" recalculates all cells in the spread sheet.
*
* returns: nothing
*/
void textview::fn_recalc()
{
clear_to_eol( 2, 0 );
put_string( 2, 0, "Recalculating . . ." );
// let user see operation
refresh();
sleep( 1 );
ss->recalculate();
update_cells();
}
| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
Copyright © 2004, Stephen R. Schmitt