/* * $Id$ */ /* * File......: DISPC.C * Author....: Mike Taylor * CIS ID....: ? * * This is an original work by Mike Taylor and is placed in the * public domain. * * Modification history: * --------------------- * * Rev 1.10 22 Apr 2004 15:32:00 David G. Holm * Corrected all hb_fsSeek calls to use FS_ defines instead of using * redefined SEEK_ ones that conflict with the C-level SEEK_ defines. * Rev 1.9 ? ? * An unknown number of changes were made between Rev 1.8 and Rev 1.10. * * Rev 1.8 24 May 2002 19:25:00 David G. Holm * Fixed some problems that caused C++ compiles to fail. * * Rev 1.7 29 Mar 2002 17:00:00 Walter Negro * Ported to Harbour * * Rev 1.6 01 Jan 1995 03:01:00 TED * Changed some prototypes to eliminate compiler warnings. * * Rev 1.5 14 Feb 1994 16:58:42 GLENN * Steve Tyrakowski and Kevin Maher modified to be CPMI-compliant. * * Rev 1.4 18 Nov 1991 02:20:20 GLENN * Mike fixed a bug in _ft_dfinit() related to allocating memory. Some * users had been reporting problems, but everyone who tested this patch * reported success. * * Rev 1.3 17 Aug 1991 15:25:46 GLENN * Don Caton fixed some spelling errors in the doc * * Rev 1.2 15 Aug 1991 23:08:14 GLENN * Forest Belt proofread/edited/cleaned up doc * * Rev 1.1 14 Jun 1991 19:53:42 GLENN * Minor edit to file header * * Rev 1.0 01 Apr 1991 01:02:46 GLENN * Nanforum Toolkit * * */ /* NOTE: we need this to prevent base types redefinition */ #define _CLIPDEFS_H #include "hbdefs.h" #include "hbapi.h" #include "hbapifs.h" #include "hbapigt.h" #include "fm.api" #include "inkey.ch" #define OFF 0 #define ON (!OFF) #define NO 0 #define YES (!NO) #define OK 0 #define K_STRING 0 #define K_LIST (!K_STRING) #define CR ((char) 13) #define LF ((char) 10) #define FEOF ((char) 26) #define READONLY 0 /* open file modes */ #define WRITEONLY 1 #define READWRITE 2 #define BUFFERSIZE 4096 /* maximum size of the file buffer */ #define MAXLINE 255 /* default maximum size of a line */ #define TABSET 8 long buffoffset; /* offset into buffer of current line */ long fsize; /* file size in bytes */ int bufftop, buffbot; /* first and last character in buffer */ int wintop, winbot; /* first and last character in window */ int winrow, wincol; /* row and column of window highlight */ int sline, eline; /* start and end line of window */ int scol, ecol; /* start and end col of window */ int height, width; /* height and width of window */ int infile; /* input file handle */ int maxlin; /* line size */ int buffsize; /* buffer size */ int hlight; /* highlight attribute */ int norm; /* normal attribute */ int kcount; /* number of keys in terminate key list*/ int colinc; /* col increment amount */ int brows; /* browse flag */ char refresh; /* YES means refresh screen */ char kstr[25]; /* terminate key string */ int keylist[24]; /* terminate key list */ int keytype; /* K_STRING or K_LIST */ int isallocated; /* if buffers were allocated */ char *buffer; /* file buffer pointer */ char *lbuff; /* line buffer pointer */ char *vseg; /* video segment variable */ /* prototypes */ static int keyin(void); static void chattr(int x, int y, int len, int attr); static long getblock(long offset); static void buff_align(void); static void win_align(void); static void disp_update(int offset); static void windown(void); static void winup(void); static void linedown(void); static void lineup(void); static void filetop(void); static void filebot(void); static void strcpyn(char *dest, const char *source, int len); /* * chattr() replace the color attribute with a new one starting at * location x, y and going for length len. * */ static void chattr(int x, int y, int len, int attr) { int i; char *vmem; vmem = vseg + (y * (width + 1) * 2) + (x * 2) + 1; /* calc the screen memory coord */ for (i = 0; i <= len; i++, vmem += 2) /* write the new attribute value */ *vmem = (char) attr; } /* * function getblock() reads the text file and returns the a block. * the variables offset and buffsize tell it where to start reading and * how many bytes to try to read. if the block read in would not fill * the buffer then the offset is adjusted so that the start or end of * of the file is positioned at the head or tail of the buffer. * * it returns the offset into the file of the first byte of the buffer. * */ static long getblock(long offset) { /* set the file pointer to the proper offset and if an error occured then check to see if a positive offset was requested, if so then set the pointer to the offset from the end of the file, otherwise set it from the beginning of the file. */ hb_fsSeek( infile, offset, FS_SET ); /* read in the file and set the buffer bottom variable equal */ /* to the number of bytes actually read in. */ buffbot = hb_fsReadLarge( infile, ( BYTE * ) buffer, buffsize ); /* if a full buffer's worth was not read in, make it full. */ if (( buffbot != buffsize ) && ( fsize > buffsize )) { if ( offset > 0 ) hb_fsSeek( infile, (long) -buffsize, FS_END ); else hb_fsSeek( infile, (long) buffsize, FS_SET ); buffbot = hb_fsReadLarge( infile, ( BYTE * ) buffer, buffsize ); } /* return the actual file position */ return( hb_fsSeek( infile, 0L, FS_RELATIVE ) - buffbot); } /* * buff_align makes sure the buffer top and bottom variables point * to actual complete lines of text. * */ static void buff_align() { int i; bufftop = 0; buffbot = buffsize; if ( buffoffset != 0L ) /* if the buffoffset is otherthan 0 */ { i = bufftop; /* start at the top of the file and scan */ /* forward until a CR is reached. */ while (( buffer[i] != CR ) && ( i < buffbot )) i++; bufftop = i + 2; } /* if the buffer offset is not a complete */ /* buffer's length away from the file end */ if ( buffoffset + ((long) buffbot) != fsize ) { /* if the file position of the last byte of the buffer would end up past the end of the file, then the buffer does contain a complete buffer full and the buffer end pointer needs to be set to the last character of the file. */ if ( buffoffset + ((long) buffbot) > fsize ) buffbot = (int) (fsize - buffoffset); i = buffbot; /* point the end of the buffer to a valid */ /* complete text line. */ while (( buffer[i] != CR ) && ( i > bufftop )) i--; buffbot = i + 2; } } /* * win_align takes the value for wintop and then figures out where * winbot would be. if winbot would extend past the end of the * buffer, then the top of the window is adjusted to ensure that a full * screen of text will appear. This simplifies the cursor routines. * */ static void win_align() { int i; winbot = wintop; /* find out if there is enough text for */ i = 0; /* full window. */ while (( winbot < buffbot ) && ( i < height )) { if ( buffer[winbot] == CR ) i++; winbot++; } if ( i < height ) /* if there is not a full window, */ { /* then retrofit winbot to the end of a line */ while ( buffer[winbot] != LF && winbot > bufftop) winbot--; wintop = winbot; i = 0; /* and setup wintop */ while (( wintop > bufftop ) && ( i <= height )) { if ( buffer[wintop] == LF ) i++; wintop--; } if ( wintop != bufftop ) wintop += 2; } } /* * this routine displays the actual text in the window. This is done * by taking each line and placing it in a string. the screen line * is then taken from the appropriate group of characters in the string. * this allows a window to page left-right across the buffer without * having to use any complex algorithm to calc the needed chars. * */ static void disp_update(int offset) { int line, col, pos, i; char *vmem; refresh = NO; line = 0; while ( line < height ) { /* calculate the initial position, this save execution time because each column is considered as a offset from the line start */ pos = (line * (width + 1) * 2); /* copy string to temp buffer */ for (i = 0; buffer[offset] != CR && offset <= winbot; offset++) { if ( i <= maxlin ) { if (buffer[offset] == '\t') /* check for a tab */ { lbuff[i++] = ' '; /* pad with spaces */ while (i % TABSET && i <= maxlin) /* until tab stop */ lbuff[i++] = ' '; /* is reached or EOL */ } else lbuff[i++] = buffer[offset]; } } for (; i <= maxlin; i++) /* fill out with spaces */ lbuff[i] = ' '; /* place the proper characters onto the screen */ for (i = wincol, col = 0; col <= width; col++) { vmem = vseg + pos + (col * 2); *vmem = lbuff[i++]; } line += 1; offset += 2; } hb_gtRest( sline, scol, eline, ecol, vseg ); } /* * move the window pointers so that a new window's worth of information * is visible. it adjusts the pointers within the buffer and if necessary * it calls the getblock function to load in a new buffer * */ static void winup() { int k; long i, j; refresh = YES; k = wintop - 3; while (( buffer[k] != CR ) && ( k > bufftop )) k--; if ( k >= bufftop ) { if (buffer[k] == CR) k += 2; wintop = k; k = winbot - 3; while ( buffer[k] != CR ) k--; winbot = k + 2; } else if ( ((long) bufftop) + buffoffset > 0 && fsize > buffsize ) { i = buffoffset + wintop; j = buffoffset - ((long) (buffsize / 2)); if ( j < 0 ) j = 0; buffoffset = getblock(j); wintop = ((int) (i - buffoffset)); buff_align(); win_align(); } } /* * move the window pointers so that a new window's worth of information * is visible. it adjusts the pointers within the buffer and if necessary * it calls the getblock function to load in a new buffer * */ static void windown() { int k; long i, j; refresh = YES; k = winbot; while (( buffer[k] != CR ) && ( k <= buffbot )) k++; k += 2; if ( k <= buffbot ) { winbot = k; k = wintop; while ( buffer[k] != CR ) k++; wintop = k + 2; } else if ( (((long) buffbot) + buffoffset) < fsize && fsize > buffsize) { i = buffoffset + wintop; j = i; if ( j > fsize ) j = fsize - ((long) buffsize); buffoffset = getblock(j); if ( i < buffoffset ) wintop = 0; else wintop = ((int) (i - buffoffset)); buff_align(); win_align(); } } /* move the cursor one line down */ static void linedown() { if ( winrow < eline ) /* if cursor not at last line */ winrow += 1; else /* otherwise adjust the window top variable */ windown(); } /* move the cursor one line up */ static void lineup() { if ( winrow > sline ) winrow -= 1; else winup(); } /* go to the top of the file */ static void filetop() { if ( buffoffset != 0 ) { buffoffset = getblock(0L); buff_align(); } refresh = YES; wintop = (int) buffoffset; winrow = sline; wincol = 0; win_align(); } /* goto the bottom of the file */ static void filebot() { if ( (((long) buffbot) + buffoffset) < fsize && fsize > buffsize ) { buffoffset = getblock(fsize + 1); buff_align(); } refresh = YES; wintop = buffbot - 3; winrow = eline; wincol = 0; win_align(); } HB_FUNC( _FT_DFINIT ) { int rval, i, j; ULONG ulSize; rval = 0; sline = hb_parni(2); /* top row of window */ scol = hb_parni(3); /* left col */ eline = hb_parni(4); /* bottom row */ ecol = hb_parni(5); /* right col */ width = ecol - scol; /* calc width of window */ height = eline - sline + 1; /* calc height of window */ hb_gtRectSize( sline, scol, eline, ecol, &ulSize ); vseg = (char * ) hb_xalloc( ulSize ); if (vseg != NULL) hb_gtSave( sline, scol, eline, ecol, vseg ); maxlin = hb_parni(12); buffsize = hb_parni(13); /* yes - load value */ buffer = (char *) hb_xalloc(buffsize); /* allocate memory */ lbuff = (char *) hb_xalloc(maxlin + 1); /* for buffers */ isallocated = !(buffer == NULL || lbuff == NULL || vseg == NULL); /* memory allocated? */ if (isallocated == FALSE) { rval = 8; /* return error code 8 (memory) */ if (buffer != NULL) hb_xfree(buffer); if (lbuff != NULL) hb_xfree(lbuff); if (vseg != NULL) hb_xfree(vseg); } else /* get parameters */ { infile = hb_parni(1); /* file handle */ j = hb_parni(6); /* starting line value */ norm = hb_parni(7); /* normal color attribute */ hlight = hb_parni(8); /* highlight color attribute */ if ((hb_parinfo(9) && 512) == 512) /* if array */ { keytype = K_LIST; kcount = hb_parinfa( 9, 0 ); if (kcount > 24) kcount = 24; for (i = 1; i <= kcount; i++) keylist[i - 1] = hb_parni( 9, i ); /* get exit key list */ } else { keytype = K_STRING; kcount = hb_parclen( 9 ); if (kcount > 24) kcount = 24; strcpyn(kstr, hb_parc(9), kcount); /* get exit key string */ } brows = hb_parl(10); /* get browse flag */ colinc = hb_parni(11); /* column skip value */ bufftop = 0; /* init buffer top pointer */ buffbot = buffsize; /* init buffer bottom pointer */ buffoffset = 0; /* curr line offset into buffer */ winrow = sline; /* init window row */ wincol = 0; /* init window col */ wintop = 0; /* init window top pointer */ winbot = 0; /* init window bottom pointer */ /* get file size */ fsize = hb_fsSeek( infile, 0L, FS_END ) - 1; /* get the first block */ hb_fsSeek( infile, 0L, FS_SET ); /* if block less than buffsize */ if ( fsize < ((long) buffbot) ) buffbot = (int) fsize; /* then set buffer bottom */ /* set the current lines buffer offset pointer */ buffoffset = getblock((long) bufftop); /* align buffer and window pointer to valid values */ buff_align(); win_align(); /* point line pointer to line passed by caller */ for (i = 1; i < j; i++) linedown(); hb_gtRest( sline, scol, eline, ecol, vseg ); } hb_retni(rval); } HB_FUNC ( _FT_DFCLOS ) { if (isallocated == TRUE) { if (buffer != NULL) hb_xfree(buffer); /* free up allocated buffer memory */ if (lbuff != NULL) hb_xfree(lbuff); if (vseg != NULL) hb_xfree(vseg); } } /* $DOC$ * $FUNCNAME$ * FT_DISPFILE() * $CATEGORY$ * File I/O * $ONELINER$ * Browse a text file * $SYNTAX$ * FT_DISPFILE() -> cExitkey * $ARGUMENTS$ * None * $RETURNS$ * The ASCII keystroke that terminated FT_DISPFILE() * $DESCRIPTION$ * This routine displays a text file within a defined window using as * little memory as possible. The text file to display has to be * present or an error value of 0 is returned (as a character.) * * Assumptions: The routine assumes that all lines are terminated * with a CR/LF sequence (0x0d and 0x0a). * * Note: Make sure you allocate a buffer large enough to hold * enough data for the number of lines that you have * in the window. Use the following formula as a * guideline - buffer size = (# of line) + 1 * RMargin * this is the smallest you should make the buffer and * for normal use I recommend 4096 bytes. * * Cursor Keys: Up, Down - moves the highlight line * Left, Right - moves the window over nColSkip col's * Home - moves the window to the far left * End - moves the window to the nRMargin column * PgUp, PgDn - moves the highlight one page * Ctrl-PgUp - moves the highlight to the file top * Ctrl-PgDn - moves the highlight to the file bottom * Ctrl-Right - moves the window 16 col's to the right * Ctrl-Left - moves the window 16 col's to the left * * Esc, Return - terminates the function * * All other keys are ignored unless they are specified * within cExitKeys parameter. This list will tell the * routine what keys terminate the function. Special * keys must be passed by a unique value and that value * can be found by looking in the keys.h file. * $EXAMPLES$ * @ 4,9 TO 11,71 * * FT_DFSETUP("test.txt", 5, 10, 10, 70, 1, 7, 15,; * "AaBb" + Chr(143), .T., 5, 132, 4096) * * cKey = FT_DISPFILE() * * FT_DFCLOSE() * * @ 20,0 SAY "Key that terminated FT_DISPFILE() was: " + '[' + cKey + ']' * $SEEALSO$ * FT_DFSETUP() FT_DFCLOSE() * $END$ */ HB_FUNC( FT_DISPFILE ) { int i, done; char rval[2]; int ch; /* make sure buffers were allocated and file was opened */ if (isallocated == TRUE && infile > 0) { done = NO; refresh = YES; /* draw inside of window with normal color attribute */ for (i = 0; i < height; i++) chattr(0, i, width, norm); hb_gtRest( sline, scol, eline, ecol, vseg ); /* main processing loop -- terminated by user key press */ do { if ( refresh == YES ) /* redraw window contents? */ disp_update(wintop); hb_gtRest( sline, scol, eline, ecol, vseg ); /* if not browse, highlight the current line */ if ( brows == NO ) chattr(0, winrow - sline, width, hlight); hb_gtRest( sline, scol, eline, ecol, vseg ); hb_gtSetPos( winrow, scol ); ch = keyin(); /* get user key press */ /* if not browse, then un-highlight current line */ if ( brows == NO ) chattr(0, winrow - sline, width, norm); hb_gtRest( sline, scol, eline, ecol, vseg ); /* figure out what the user wants to do */ switch (ch) { case K_DOWN : if ( brows == YES ) /* if browse flag */ winrow = eline; /* is set, force */ /* active line to */ linedown(); /* be last line */ break; case K_UP : if ( brows == YES ) /* if browse flag */ winrow = sline; /* is set, force */ /* active line to */ lineup(); /* be first line */ break; case K_LEFT : wincol -= colinc; /* move cursor */ refresh = YES; /* to the left */ if ( wincol < 0 ) wincol = 0; break; case K_RIGHT : wincol += colinc; /* move cursor */ refresh = YES; /* to the right */ if ( wincol > (maxlin - width) ) wincol = maxlin - width; break; case K_HOME : wincol = 0; /* move cursor */ refresh = YES; /* to first col */ break; /* move cursor to last col */ case K_END : wincol = maxlin - width; refresh = YES; break; case K_CTRL_LEFT : wincol -= 16; /* move cursor */ refresh = YES; /* 16 col to left */ if ( wincol < 0 ) wincol = 0; break; case K_CTRL_RIGHT : wincol += 16; /* move cursor */ refresh = YES; /* 16 col to right */ if ( wincol > (maxlin - width) ) wincol = maxlin - width; break; case K_PGUP : for (i = 0; i < height; i++) /* move window */ winup(); /* up one page */ break; case K_PGDN : for (i = 0; i < height; i++) /* move window */ windown(); /* down 1 page */ break; case K_CTRL_PGUP : filetop(); /* move cursor to */ break; /* to top of file */ case K_CTRL_PGDN : filebot(); /* move cursor to */ break; /* to bot of file */ case K_ENTER : done = YES; /* carriage return */ break; /* terminates */ case K_ESC : done = YES; /* escape key */ break; /* terminates */ /* scan key list and see if key pressed is there */ default : if (keytype == K_STRING) { for (i = 0; i <= kcount; i++) if ((ch > 0) && (ch < 256)) if ( (int) kstr[i] == ch ) done = YES; break; /* if so terminate */ } else { for (i = 0; i < kcount; i++) if ( keylist[i] == ch ) done = YES; break; } } } while ( done == NO ); } else ch = 0; /* store the key pressed as a character to be returned */ /* return key value to caller */ if (keytype == K_STRING) { rval[0] = (char) ch; rval[1] = '\0'; hb_retc( rval ); } else hb_retni( ch ); } /* * keyin() gets the next key typed and does any translation needed. * Some keys are converted to a common name - like the up arrow is * converted to the UP value which also is the Ctrl-E value. This * allows the Wordstar-like control keys to be used. Only extended * keys are translated - the values of the defines were chosen to * match up with the non-extended key codes. * */ static int keyin() { int ch; ch = 0; while ( ch == 0x00 ) /* check to see if it's extended */ { ch = hb_inkeyTranslate( hb_gtReadKey( 0 ), 0 ); if (ch == 257) /* error compiling with bcc55 */ ch = 27; /* ESC with CapsLock ON = 257, with CapsLock OFF = 27 */ hb_idleState(); } return ( ch ); } static void strcpyn( char *dest, const char *source, int len ) { int i; for (i = 0; i < len; i++) dest[i] = source[i]; dest[len+1] = 0x00; }