| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
timehelp.cpp
/*-------------------------------------------------------------------------*
TIMEHELP.CPP
on-line help module
usage: timehelp filename topic
filename - name of help data file
topic - string corresponding to topic to lookup
by: Stephen R. Schmitt
*-------------------------------------------------------------------------*/
#include <assert.h>
#include <conio.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "input.h"
input Console;
#define HELP_WINDOW 2000
#define HELP_TITLE 80
#define HELP_TOPIC 8192
#define HELP_LINES 256
#define MIN_WINDOW 0
#define MIN_X 20
#define MIN_Y 5
#define MIN_DX 40
#define MIN_DY 10
#define MAX_WINDOW 1
#define MAX_X 1
#define MAX_Y 2
#define MAX_DX 78
#define MAX_DY 22
#define MAX_NAME 24
#define HELP_TEXT CYAN*16 + BLACK
#define HELP_JUMP CYAN*16 + YELLOW
#define HELP_BORDER CYAN*16 + WHITE
#define LF_TOP1 218
#define RT_TOP1 191
#define HORZ1 196
#define VERT1 179
#define LF_BOT1 192
#define RT_BOT1 217
typedef struct
{
char name[MAX_NAME]; // ptr to topic string
long offset; // file offset of topic
}
JUMP;
class help
{
public:
char *screen; // under the help window
help();
~help();
int get_help( char *, char * );
private:
char *name; // of help window
int change_title; // signal for new topic
int topic_count; // number of topics
JUMP *topic_jump; // offsets of topic text
long topic_start; // starting offset for text
char *topic_title; // current topic
char *topic_window; // window for text
char *topic_text; // text of current topic
char **topic_line; // pointers to lines
FILE *hlp; // handle of help text file
int size; // window size flag
int x; // left column
int y; // top row
int dx; // width of view
int dy; // height of view
int last_line; // last line number
int top_line; // of displayed text
int left_col; // of displayed text
int scrn_x; // cursor screen location
int scrn_y; // cursor screen location
int cur_lin; // cursor file line
int cur_col; // cursor file column
void paint_window();
void paint_border();
void update_cursor( int, int );
void change_size();
void line_up();
void line_down();
void page_up();
void page_down();
void goto_topic_begin();
void goto_topic_end();
void column_left();
void column_right();
void goto_line_begin();
void goto_line_end();
void move_window( int );
void function( int );
void enter();
void tab_select();
void character( int );
void mouse_left( int, int );
void process( int, int, int, int );
void load_topic_text( FILE *, long );
int get_topic_from_file();
};
/*-------------------------------------------------------------------------*
* "help" constructor allocates memory
*
* returns: nothing
*/
help::help()
{
name = "Help";
screen = (char *) calloc( HELP_WINDOW, 2 * sizeof( char ) );
topic_title = (char *) calloc( HELP_TITLE, sizeof( char ) );
topic_window = (char *) calloc( HELP_WINDOW, 2 * sizeof( char ) );
topic_text = (char *) calloc( HELP_TOPIC, sizeof( char ) );
topic_line = (char **)calloc( HELP_LINES, sizeof( char * ) );
topic_jump = (JUMP *) calloc( 1024, sizeof( JUMP ) );
}
/*-------------------------------------------------------------------------*
* "help" destructor frees memory
*
* returns: nothing
*/
help::~help()
{
free( screen );
free( topic_title );
free( topic_window );
free( topic_text );
free( topic_line );
free( topic_jump );
}
/*-------------------------------------------------------------------------*
* "paint_window" writes text in the help window
*
* returns: nothing
*/
void help::paint_window()
{
char *p;
int row, i, j, jump;
// setup topic buffer
memset( topic_window, 0, 2 * HELP_WINDOW );
for( i = 0; i < HELP_WINDOW; i++ )
topic_window[2*i+1] = HELP_TEXT;
p = topic_line[top_line];
row = 0;
i = 0;
j = 0;
jump = 0;
while( row < dy && *p )
{
switch( *p )
{
case '\n':
p++;
i = 0;
j = 0;
row++;
break;
case '@':
jump = 1;
p++;
break;
case '$':
jump = 0;
p++;
break;
default:
if( i >= left_col && j < dx )
{
topic_window[2*dx*row + 2*j] = *p;
if( jump )
topic_window[2*dx*row + 2*j + 1] = HELP_JUMP;
j++;
}
i++;
p++;
break;
}
}
Console.mouse_cursor_off();
puttext( x + 1, y + 1, x + dx, y + dy, topic_window );
Console.mouse_cursor_on();
}
/*-------------------------------------------------------------------------*
* "paint_border" draws a border around a help window and rewrites
* the title
*
* returns: nothing
*/
void help::paint_border()
{
int i, n, left, top, right, bottom;
Console.mouse_cursor_off();
left = x;
top = y;
right = x + dx + 1;
bottom = y + dy + 1;
for( i = 0; i < 80; i++ )
{
topic_window[ 2*i ] = HORZ1;
topic_window[ 2*i + 1 ] = HELP_BORDER;
}
topic_window[ 0 ] = LF_TOP1;
topic_window[ 2*( right - left ) ] = RT_TOP1;
puttext( left, top, right, top, topic_window );
topic_window[ 0 ] = LF_BOT1;
topic_window[ 2*( right - left ) ] = RT_BOT1;
puttext( left, bottom, right, bottom, topic_window );
for( i = 0; i < 25; i++ )
topic_window[ 2*i ] = VERT1;
puttext( left, top + 1, left, bottom - 1, topic_window );
puttext( right, top + 1, right, bottom - 1, topic_window );
// update file name display
n = strlen( name ) + 2;
topic_window[0] = ' ';
for( i = 0; i < n; i++ )
topic_window[2*i + 2] = name[i];
topic_window[2*i + 2] = ' ';
// display new name
puttext( x + ( dx / 2 ) - ( n / 2 ) + 1, y,
x + ( dx / 2 ) + ( n / 2 ) + n % 2, y, topic_window );
Console.mouse_cursor_on();
}
/*-------------------------------------------------------------------------*
* "update_cursor" repositions the cursor and updates the line and
* column of the cursor.
*
* returns: nothing
*/
void help::update_cursor( int cx, // column
int cy ) // row
{
gotoxy( cx + x + 1, cy + y + 1 );
}
/*-------------------------------------------------------------------------*
* "change_size" changes the size of the help window.
*
* returns: nothing
*/
void help::change_size()
{
static int mx = MIN_X;
static int my = MIN_Y;
Console.mouse_cursor_off();
puttext( 1, 1, 80, 25, screen );
if( size == MIN_WINDOW )
{
size = MAX_WINDOW;
mx = x;
my = y;
x = MAX_X;
y = MAX_Y;
dx = MAX_DX;
dy = MAX_DY;
}
else
{
size = MIN_WINDOW;
x = mx;
y = my;
dx = MIN_DX;
dy = MIN_DY;
}
paint_border();
paint_window();
update_cursor( scrn_x, scrn_y );
Console.mouse_cursor_on();
}
/*-------------------------------------------------------------------------*
* "line_up" moves cursor up by one line; scrolls up if needed
*
* returns: nothing
*/
void help::line_up()
{
scrn_y--;
cur_lin--;
// if at top of window, must scroll up
if( scrn_y < 0 )
{
if( top_line > 0 )
{
scrn_y++;
top_line--;
paint_window();
}
else if( top_line == 0 )
{
scrn_y++;
cur_lin++;
}
}
}
/*-------------------------------------------------------------------------*
* "line_down" moves cursor down one line
*
* returns: nothing
*/
void help::line_down()
{
if( top_line < last_line )
{
scrn_y++;
cur_lin++;
// if below bottom line, scroll up
if( scrn_y == dy )
{
scrn_y = dy - 1;
top_line++;
paint_window();
}
}
}
/*-------------------------------------------------------------------------*
* "page_up" moves cursor up by dy - 2 lines; scrolls up if needed
*
* returns: nothing
*/
void help::page_up()
{
int i;
int paint = 0;
for( i = 0; i < dy - 2; i++ )
{
scrn_y--;
cur_lin--;
// if at top of window, must scroll up
if( scrn_y < 0 )
{
if( top_line > 0 )
{
scrn_y++;
top_line--;
paint = 1;
}
else if( top_line == 0 )
{
scrn_y++;
cur_lin++;
break;
}
}
}
if( paint )
paint_window();
}
/*-------------------------------------------------------------------------*
* "page_down" moves cursor down by dy - 2 lines
*
* returns: nothing
*/
void help::page_down()
{
int i;
int paint = 0;
for( i = 0; i < dy - 2; i++ )
{
if( top_line < last_line )
{
scrn_y++;
cur_lin++;
// if below bottom line, scroll up
if( scrn_y == dy )
{
scrn_y = dy - 1;
top_line++;
paint = 1;
}
}
}
if( paint )
paint_window();
}
/*-------------------------------------------------------------------------*
* "goto_topic_begin" moves cursor to beginning of topic
*
* returns: nothing
*/
void help::goto_topic_begin()
{
int paint = 0;
while( cur_lin > 0 )
{
scrn_y--;
cur_lin--;
// if at top of window, must scroll up
if( scrn_y < 0 )
{
if( top_line > 0 )
{
scrn_y++;
top_line--;
paint = 1;
}
else if( top_line == 0 )
{
scrn_y++;
cur_lin++;
break;
}
}
}
if( paint )
paint_window();
goto_line_begin();
}
/*-------------------------------------------------------------------------*
* "goto_topic_end" moves cursor to the end of the topic.
*
* returns: nothing
*/
void help::goto_topic_end()
{
int paint = 0;
while( cur_lin < last_line )
{
if( top_line < last_line )
{
scrn_y++;
cur_lin++;
// if below bottom line, scroll up
if( scrn_y == dy )
{
scrn_y = dy - 1;
top_line++;
paint = 1;
}
}
}
if( paint )
paint_window();
goto_line_end();
}
/*-------------------------------------------------------------------------*
* "column_left" moves the cursor one space left
*
* returns: nothing
*/
void help::column_left()
{
scrn_x--;
cur_col--;
if( scrn_x < 0 )
{
if( left_col > 0 )
{
left_col--;
scrn_x++;
paint_window();
}
else if( left_col == 0 )
{
scrn_x++;
cur_col++;
}
}
}
/*-------------------------------------------------------------------------*
* "column_right" moves the cursor one space right
*
* returns: nothing
*/
void help::column_right()
{
if( left_col < 80 )
{
scrn_x++;
cur_col++;
if( scrn_x > dx - 1 )
{
left_col++;
scrn_x--;
paint_window();
}
}
}
/*-------------------------------------------------------------------------*
* "goto_line_begin" moves the cursor to the beginning of the line.
*
* returns: nothing
*/
void help::goto_line_begin()
{
int paint;
paint = 0;
while( cur_col > 0 )
{
scrn_x--;
cur_col--;
if( scrn_x < 0 )
{
if( left_col > 0 )
{
left_col--;
scrn_x++;
paint = 1;
}
else if( left_col == 0 )
{
scrn_x++;
cur_col++;
break;
}
}
}
if( paint )
paint_window();
}
/*-------------------------------------------------------------------------*
* "goto_line_end" moves the cursor to the end of the line.
*
* returns: nothing
*/
void help::goto_line_end()
{
int len, paint;
char *p;
len = 0;
paint = 0;
p = topic_line[cur_lin];
while( *p != '\n' && *p != '\0' )
{
if( *p != '@' && *p != '$' )
len++;
p++;
}
while( cur_col < len )
{
if( left_col < 80 )
{
scrn_x++;
cur_col++;
if( scrn_x > dx - 1 )
{
left_col++;
scrn_x--;
paint = 1;
}
}
}
while( cur_col > len )
{
scrn_x--;
cur_col--;
if( scrn_x < 0 )
{
if( left_col > 0 )
{
left_col--;
scrn_x++;
paint = 1;
}
else if( left_col == 0 )
{
scrn_x++;
cur_col++;
break;
}
}
}
if( paint )
paint_window();
}
/*-------------------------------------------------------------------------*
* "move_window" moves the help window on the screen.
*
* returns: nothing
*/
void help::move_window( int code ) // key code
{
int move;
if( size == MAX_WINDOW )
return;
move = 0;
switch( code )
{
case ALT_UP_ARROW:
if( y > 1 )
{
y--;
move = 1;
}
break;
case ALT_LF_ARROW:
if( x > 1 )
{
x--;
move = 1;
}
break;
case ALT_RT_ARROW:
if( x + dx + 1 < 80 )
{
x++;
move = 1;
}
break;
case ALT_DN_ARROW:
if( y + dy + 1 < 25 )
{
y++;
move = 1;
}
break;
}
if( move )
{
Console.mouse_cursor_off();
puttext( 1, 1, 80, 25, screen );
paint_border();
paint_window();
update_cursor( scrn_x, scrn_y );
Console.mouse_cursor_on();
}
}
/*-------------------------------------------------------------------------*
* "function" processes a function key message
*
* returns: nothing
*/
void help::function( int code )
{
switch( code )
{
case F1_KEY:
memset( topic_title, 0, HELP_TITLE );
change_title = 1;
break;
case F5_KEY:
change_size();
break;
case HOME:
goto_line_begin();
break;
case END:
goto_line_end();
break;
case LF_ARROW:
column_left();
break;
case RT_ARROW:
column_right();
break;
case UP_ARROW:
line_up();
break;
case DN_ARROW:
line_down();
break;
case PAGE_UP:
page_up();
break;
case PAGE_DN:
page_down();
break;
case CTRL_PAGE_DN:
goto_topic_end();
break;
case CTRL_PAGE_UP:
goto_topic_begin();
break;
case ALT_UP_ARROW:
case ALT_LF_ARROW:
case ALT_RT_ARROW:
case ALT_DN_ARROW:
move_window( code );
break;
}
}
/*-------------------------------------------------------------------------*
* "enter" checks for a jump to a new topic
*
* returns: nothing
*/
void help::enter()
{
int i, key, jump;
char *p, *beg, *end;
i = 0;
key = 0;
jump = 0;
p = topic_line[cur_lin];
while( *p && *p != '\n' )
{
if( *p == '@' )
{
key = 1;
p++;
beg = p;
}
else if( *p == '$' )
{
if( jump )
{
end = p;
break;
}
key = 0;
p++;
}
if( i == cur_col )
jump = key;
p++;
i++;
}
if( jump )
{
memset( topic_title, 0, HELP_TITLE );
strncpy( topic_title, beg, (int)( end - beg ) );
change_title = 1;
}
}
/*-------------------------------------------------------------------------*
* "tab_select" moves the cursor to the next key word
*
* returns: nothing
*/
void help::tab_select()
{
int i, j, key, paint;
char *p;
i = 0;
j = cur_lin;
key = 0;
p = topic_line[cur_lin];
while( *p && *p != '\n' ) // find cursor location
{
if( *p == '@' || *p == '$' )
p++;
if( i == cur_col )
break;
p++;
i++;
}
while( *p ) // find next keyword
{
switch( *p )
{
case '@':
key = 1;
p++;
break;
case '$':
p++;
break;
case '\n':
i = 0;
j++;
p++;
break;
default:
p++;
i++;
break;
}
if( key )
break;
}
paint = 0;
if( key )
{
while( j > cur_lin )
{
if( top_line < last_line )
{
scrn_y++;
cur_lin++;
// if below bottom line, scroll up
if( scrn_y == dy )
{
scrn_y = dy - 1;
top_line++;
paint = 1;
}
}
}
while( cur_col < i )
{
if( left_col < 80 )
{
scrn_x++;
cur_col++;
if( scrn_x > dx - 1 )
{
left_col++;
scrn_x--;
paint = 1;
}
}
}
while( cur_col > i )
{
scrn_x--;
cur_col--;
if( scrn_x < 0 )
{
if( left_col > 0 )
{
left_col--;
scrn_x++;
paint = 1;
}
else if( left_col == 0 )
{
scrn_x++;
cur_col++;
break;
}
}
}
}
if( paint )
paint_window();
}
/*-------------------------------------------------------------------------*
* "character" processes a character message
*
* returns: nothing
*/
void help::character( int code )
{
switch( code )
{
case ENTER:
enter();
break;
case TAB:
tab_select();
break;
default:
// process characters here
break;
}
}
/*-------------------------------------------------------------------------*
* "mouse_left" processes a mouse left key message
*
* returns: nothing
*/
void help::mouse_left( int mx, // mouse column
int my ) // mouse row
{
if( my >= y && my < ( y + dy ) &&
mx >= x && mx < ( x + dx ) )
{
scrn_x = mx - x;
scrn_y = my - y;
cur_col = left_col + scrn_x;
cur_lin = top_line + scrn_y;
}
enter();
}
/*-------------------------------------------------------------------------*
* "process" is the entry point for editing a file
*
* returns: nothing
*/
void help::process( int msg, // message type
int mod, // keyboard modifier
int code, // message
int ampl ) // message amplifier
{
if( mod );
switch( msg )
{
case MSG_PAINT:
paint_border();
paint_window();
break;
case MSG_FUNCTION:
function( code );
break;
case MSG_MOUSE_LF:
mouse_left( code, ampl );
break;
case MSG_CHARACTER:
character( code );
break;
}
update_cursor( scrn_x, scrn_y );
}
/*-------------------------------------------------------------------------*
* "load_topic_text" loads text for a topic from data file
*
* returns: nothing
*/
void help::load_topic_text( FILE *hlp, // text source file
long offset ) // offset of text start
{
int i, j, n;
char buffer[80];
fseek( hlp, offset, SEEK_SET );
memset( topic_text, 0, HELP_TOPIC );
i = 0;
while( fgets( buffer, 80, hlp ) )
{
if( buffer[0] == '#' )
break;
else
{
n = strlen( topic_text );
topic_line[i] = &topic_text[n];
j = 0;
while( buffer[j] )
{
if( buffer[j] == '\t' )
{
// replace tabs with 8 spaces
strcat( topic_text, " " );
n += 8;
}
else
{
topic_text[n] = buffer[j];
n++;
}
j++;
}
i++;
}
}
last_line = i - 1;
top_line = 0;
left_col = 0;
scrn_x = 0;
scrn_y = 0;
cur_col = 0;
cur_lin = 0;
}
/*-------------------------------------------------------------------------*
* "get_topic_from_file" opens the help file and searches for a topic.
*
* returns: nothing
*/
int help::get_topic_from_file()
{
int j;
int msg, mod, code, ampl;
for( j = 0; j < topic_count; j++ )
{
if( !strcmp( topic_jump[j].name, topic_title ) )
break;
}
load_topic_text( hlp, topic_jump[j].offset + topic_start );
// display a window full of text
paint_border();
paint_window();
update_cursor( 0, 0 );
change_title = 0;
while( 1 )
{
msg = Console.message( &mod, &code, &l );
if( msg == MSG_CHARACTER && code == ESCAPE )
break;
else
process( msg, mod, code, ampl );
if( change_title )
break;
}
return change_title;
}
/*-------------------------------------------------------------------------*
* "get_help" initializes help viewer and calls function to open the
* help file and search for a topic.
*
* returns: 1 if successful in opening help file, else 0
*/
int help::get_help( char *file, // help file name
char *word ) // word to lookup
{
int again;
size = MIN_WINDOW;
x = MIN_X;
y = MIN_Y;
dx = MIN_DX;
dy = MIN_DY;
hlp = fopen( file, "r" );
if( !hlp )
return 0;
// get jump table from top of help file
int i, j, k, n;
char buffer[80];
j = 0;
n = 0;
while( fgets( buffer, 80, hlp ) )
{
if( buffer[0] == '#' )
break;
n += strlen( buffer ) + 1;
if( buffer[0] == '<' )
{
for( i = 0; i < 80; i++ )
{
if( buffer[i+1] == '>' )
{
buffer[i] = 0;
assert( strlen( buffer ) < MAX_NAME );
strcpy( topic_jump[j].name, buffer );
break;
}
else
buffer[i] = buffer[i+1];
}
i += 2;
k = i;
for( ; i < 80; i++ )
{
if( !isdigit( buffer[i] ) )
{
buffer[i] = 0;
topic_jump[j].offset = atol( &buffer[k] );
break;
}
}
j++;
}
}
topic_count = j - 1;
topic_start = n;
strcpy( topic_title, word );
again = 1;
while( again )
again = get_topic_from_file();
fclose( hlp );
return 1;
}
/*-------------------------------------------------------------------------*
* "main" entry point for help program.
*
* returns: nothing
*/
void main( int argc, char *argv[] )
{
help h;
int cx, cy, rv = 1;
if( argc == 3 )
{
cx = wherex();
cy = wherey();
gettext( 1, 1, 80, 25, h.screen );
Console.set_mouse( MIN_X, MIN_Y );
Console.mouse_cursor_on();
_setcursortype( _NORMALCURSOR );
rv = h.get_help( argv[1], argv[2] );
Console.mouse_cursor_off();
_setcursortype( _NOCURSOR );
puttext( 1, 1, 80, 25, h.screen );
gotoxy( cx, cy );
}
if( rv == 0 )
exit( -1 );
}
| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
Copyright © 2004, Stephen R. Schmitt