Category : Word Processors
Archive   : HYPE11.ZIP
Filename : HYPE.PAS

 
Output of file : HYPE.PAS contained in archive : HYPE11.ZIP

{$R-} {Range checking off}
{$B+} {Boolean complete evaluation on}
{$S+} {Stack checking on}
{$I+} {I/O checking on}
{$N-} {No numeric coprocessor}
{$M 65500,16384,655360} {Turbo 3 default stack and heap}

{.PW132}
{.HE HYPE.PAS Page # }
{$R+,V-}
PROGRAM HyperText ;

(* Copyright 1987 - Knowledge Garden Inc.
473A Malden Bridge Rd.
R.D. 2
Nassau, NY 12123 *)


(* This program implements the hypertext technique described in the
AI apprentice column in August 1987 issue of AI Expert Magazine.

This program has been tested using Turbo ver 3.01A on an IBM PC/AT and
two PC clones. It has been run under both DOS 3.2 and Concurrent 5.0 .

We would be pleased to hear your comments, good or bad, or any applications
and modifications of the program. Contact us at:

AI Expert
Miller Freeman Publications
500 Howard Street
San Francisco, CA 94105

or on the AI Expert BBS. Our id is BillandBev Thompson ,[76703,4324].
You can also contact us on BIX, our id is bbt.

Bill and Bev Thompson *)



Uses Crt, Dos,Turbo3,Qwik;


CONST
color_base = $B800 ; (* Location of PC color screen memory map *)
mono_base = $B000 ; (* Location of PC mono screen memory map *)
esc = #27 ; (* These rest of these constants could have been defined in *)
F10 = #68 ; (* process_file, but we put them here for convenience *)
up_arrow = #72 ;
down_arrow = #80 ;
PgUp = #73 ;
PgDn = #81 ;
mark_char = '\' ;
enter = #13 ;
def_window_size_x = 65 ;
def_window_size_y = 12 ;
def_fore_color = white ;
def_back_color = red ;
MaxWndw = 100;

TYPE
counter = 0 .. maxint ;
text_file = text;
string255 = string[255] ;
string80 = string[80] ;
LongString = String[80];
char_ptr = ^char ;
col_pos = 1 .. 80 ; (* The PC screen is 80 by 25 *)
row_pos = 1 .. 25 ;
color = 0 .. 31 ;
window_pos = RECORD (* cursor location on screen *)
x : col_pos ;
y : row_pos ;
END ;
window_ptr = ^window_desc ;
window_desc = RECORD (* Basic window description *)
next_window : window_ptr ; (* windows are linked lists of *)
prev_window : window_ptr ; (* these descriptors *)
abs_org : window_pos ; (* origin relative to upper left *)
window_size : window_pos ; (* rows and columns in window *)
cursor_pos : window_pos ; (* saves current cursor location *)
has_frame : boolean ; (* size and org do not include frame *)
fore_color : color ;
back_color : color ;
scrn_area : char_ptr ; (* pointer to actual window data *)
END ;
string_ptr = ^string255 ; (* we don't actually allocate space for 255 chars *)
line_ptr = ^line_desc ;
line_desc = RECORD (* text is stored as a linked list *)
next_line : line_ptr ;
prev_line : line_ptr ;
txt : string_ptr ; (* points to actual text data *)
END ;
mark_ptr = ^mark_desc ;
mark_desc = RECORD (* marked text is also a linked list *)
next_mark : mark_ptr ;
prev_mark : mark_ptr ;
mark_pos : window_pos ; (* location of start of mark in window *)
mark_text : string_ptr ; (* actual marked text *)
END ;
dos_rec = RECORD (* used for low-level functions *)
CASE boolean OF
true : (ax,bx,cx,dx,bp,si,di,ds,es,flags : integer) ;
false : (al,ah,bl,bh,cl,ch,dl,dh : byte) ;
END ;
monitor_type = (color_monitor,mono_monitor,ega_monitor) ;


VAR
window_list,main_window,message_window,last_window : window_ptr ;
screen_base : char_ptr ;
monitor_kind : monitor_type ;
main_file : text_file ;
button_fore,button_back : color ;
i : row_pos;
Buffer : Array[1..51200] of Char;
Ch : Char;
(* Important variables:
window_list - points to a linked list of window descriptors,
the top window is the currently active window.
To write in a window, bring it to the front of the list.
last_window - points to end of window list
main_window - the big window, that text initially appears in
message_window - 2 line area at the bottom of the screen, available keys,
commands etc. appear here
screen_base - points to actual memory location of screen, either
mono_base or color_base
main_file - the original text file, the one we start the program with
button_fore,
button_back - the button is the large cursor which moves from mark to mark
on a color screen it is yellow on black, on a mono screen
the text is underlined. *)


(* Note - In most cases this program uses the Turbo standard string
functions. You can probably get better performance by turning
off range checking and accessing the strings directly, but
we didn't want to make this program even less portable than it
already is. *)

(* \\\\\\\\\\\\\ Basic Utility Routines \\\\\\\\\\\\\\\\\\\\\\ *)

Procedure Menu;
Var
MenuLine : LongString;
begin
MenuLine := ' Esc : Prev. Window; '+#24+','+#25+',Enter : Select; PgUp,PgDn : Page Text; F10 : Quit ';
QWriteC(25,2,80,Black+LightGrayBG,MenuLine);
end;

FUNCTION min(x,y : integer) : integer ;
BEGIN
IF x <= y
THEN min := x
ELSE min := y ;
END ; (* min *)


FUNCTION max(x,y : integer) : integer ;
BEGIN
IF x >= y
THEN max := x
ELSE max := y ;
END ; (* max *)


PROCEDURE makestr(VAR s : string255 ; len : byte) ;
(* Fixes string "s" to length "len" - pads with blanks if necessary. *)
VAR
old_length : byte ;
BEGIN
old_length := length(s) ;
(*$R- *)
s[0] := chr(len) ;
(*$R+ *)
IF old_length < len
THEN fillchar(s[old_length+1],len - old_length,' ') ;
END ; (* makestr *)


FUNCTION toupper(s : string255) : string255 ;
(* converts a string to uppercase *)
VAR
i : byte ;
BEGIN
IF length(s) > 0
THEN
FOR i := 1 TO length(s) DO
s[i] := upcase(s[i]) ;
toupper := s ;
END ; (* toupper *)


PROCEDURE strip_leading_blanks(VAR s : string255) ;
(* Trim the leading blanks from a string *)
BEGIN
IF length(s) > 0
THEN
IF s[1] = ' '
THEN
BEGIN
delete(s,1,1) ;
strip_leading_blanks(s) ;
END ;
END ; (* strip_leading_blanks *)


PROCEDURE strip_trailing_blanks(VAR s : string255) ;
(* Trim the trailing blanks from a string *)
BEGIN
IF length(s) > 0
THEN
IF s[length(s)] = ' '
THEN
BEGIN
delete(s,length(s),1) ;
strip_trailing_blanks(s) ;
END ;
END ; (* strip_trailing_blanks *)


FUNCTION tointeger(s : string255) : integer ;
(* converts a string to an integer. Returns 0 for non-numeric strings *)
VAR
num : real ;
code : integer ;
BEGIN
strip_trailing_blanks(s) ;
strip_leading_blanks(s) ;
val(s,num,code) ;
IF code = 0
THEN
IF (num < -32768.0) OR (num > 32767.0)
THEN tointeger := 0
ELSE tointeger := trunc(num)
ELSE tointeger := 0 ;
END ; (* tointeger *)


FUNCTION open(VAR f : text_file ; f_name : string80) : boolean ;
(* Open a text file and return true if file can be opened *)
BEGIN
assign(f,f_name) ;
(*$I- *)
reset(f) ;
(*$I+ *)
open := (ioresult = 0) ;
END ; (* open *)


(* \\\\\\\\\\\\\\\\\\\ Window Routines \\\\\\\\\\\\\\\\\\\\ *)

PROCEDURE draw_frame(x1,y1,x2,y2 : counter ; title : string80 ;
frame_color : color) ;
(* Draw a frame on the screen at absolute screen positions *)
(* x1,y1 - upper left corner *)
(* x2,y2 - lower right corner *)
CONST
bar = #196 ;
vert_bar = #179 ;
upper_lf = #218 ;
upper_rt = #191 ;
lower_lf = #192 ;
lower_rt = #217 ;
VAR
i : 1 .. 25 ;
border : string80 ;

PROCEDURE get_frame_co_ords ;
BEGIN
x1 := min(max(1,x1),78) ;
y1 := min(max(1,y1),23) ;
x2 := min(max(3,x2),80) ;
y2 := min(max(3,y2),25) ;
END ; (* get_frame_co_ords *)

PROCEDURE write_title ;
BEGIN
IF length(title) > (x2 - x1 - 1)
THEN title := copy(title,1,x2 - x1 - 1) ;
write(title) ;
write(copy(border,1,length(border) - length(title))) ;
END ; (* write_title *)

BEGIN
get_frame_co_ords ;
window(1,1,80,25) ;
border := '' ;
makestr(border,x2 - x1 - 1) ;
fillchar(border[1],x2 - x1 - 1,bar) ;
gotoxy(x1,y1) ;
textcolor(frame_color) ;
textbackground(black) ;
write(upper_lf) ;
write_title ;
write(upper_rt) ;
FOR i := y1 + 1 TO y2 - 1 DO
BEGIN
gotoxy(x1,i) ;
write(vert_bar) ;
gotoxy(x2,i) ;
write(vert_bar) ;
END ;
gotoxy(x1,y2) ;
write(lower_lf) ;
write(border) ;
IF (wherex = 80) AND (wherey = 25)
THEN
BEGIN
mem[seg(screen_base^) : 3998] := ord(lower_rt) ;
mem[seg(screen_base^) : 3999] := (black SHL 4) + frame_color ;
END
ELSE write(lower_rt) ;
END ; (* draw_frame *)


PROCEDURE retrace_wait ;
(* This routine is a delay to prevent snow on a CGA screen *)
(* It is unecessary for mono and EGA. It watches the color status reg *)
(* until the horizontal retrace is finished. On CGA clones it may not *)
(* be needed, so try removing the calls to it and see if you get snow. *)
CONST
color_status_reg = $3DA ;
BEGIN
IF monitor_kind = color_monitor
THEN WHILE (port[color_status_reg] AND $08) = 0 DO ;
END ; (* retrace_wait *)


PROCEDURE get_monitor_type ;
(* find out what kind of display we are using *)
(* A hercules card is a mono card *)
VAR
regs : dos_rec ;
BEGIN
WITH regs DO
BEGIN
ah := $12 ;
bh := $03 ;
bl := $10 ;
END ;
intr($10,Dos.Registers(regs)) ;
IF regs.bh < 2
THEN
BEGIN
monitor_kind := ega_monitor ;
screen_base := ptr(color_base,0) ;
END
ELSE
BEGIN
regs.ax := $0F00 ;
intr($10,Dos.Registers(regs)) ;
IF regs.al < 7
THEN
BEGIN
monitor_kind := color_monitor ;
screen_base := ptr(color_base,0) ;
END
ELSE
BEGIN
monitor_kind := mono_monitor ;
screen_base := ptr(mono_base,0) ;
END
END ;
END ; (* get_monitor_type *)


PROCEDURE move_from_scrn(save_org,save_size : window_pos ;
save_scrn : char_ptr) ;
(* Move data from physical screen memory-map area to save_scrn *)
(* i.e. reads the the screen *)
(* It moves characters and attributes starting at location given by *)
(* save_org. It copies save_size.x cols by save_size.y rows *)
(* Copy is performed on row at a time *)
(* This routine is extremely machine specific *)
VAR
physical_scrn : char_ptr ;
i : row_pos ;
BEGIN
physical_scrn := ptr(seg(screen_base^),ofs(screen_base^) +
((save_org.y - 1) * 80 + (save_org.x - 1)) * 2) ;
FOR i := 1 TO save_size.y DO
BEGIN
retrace_wait ;
move(physical_scrn^,save_scrn^,save_size.x * 2) ;
physical_scrn := ptr(seg(physical_scrn^),ofs(physical_scrn^) + 160) ;
save_scrn := ptr(seg(save_scrn^),ofs(save_scrn^) + save_size.x * 2) ;
END ;
END ; (* move_from_scrn *)


PROCEDURE move_to_scrn(save_org,save_size : window_pos ;
save_scrn : char_ptr) ;
(* Move data from save_scrn to physical screen memory-map area, *)
(* i.e. displays data on the screen *)
(* It moves characters and attributes starting at location given by *)
(* save_org. It copies save_size.x cols by save_size.y rows *)
(* Copy is performed on row at a time *)
(* This routine is extremely machine specific *)
VAR
physical_scrn : char_ptr ;
i : row_pos ;
BEGIN
physical_scrn := ptr(seg(screen_base^),ofs(screen_base^) +
((save_org.y - 1) * 80 + (save_org.x - 1)) * 2) ;
FOR i := 1 TO save_size.y DO
BEGIN
retrace_wait ;
move(save_scrn^,physical_scrn^,save_size.x * 2) ;
physical_scrn := ptr(seg(physical_scrn^),ofs(physical_scrn^) + 160) ;
save_scrn := ptr(seg(save_scrn^),ofs(save_scrn^) + save_size.x * 2) ;
END ;
END ; (* move_to_scrn *)


PROCEDURE window_reverse ;
(* After this routine is called all text written to current window will be *)
(* displayed in reverse video *)
BEGIN
WITH window_list^ DO
BEGIN
textcolor(back_color) ;
textbackground(fore_color) ;
END ;
END ; (* window_reverse *)


PROCEDURE window_normal ;
(* returns to normal colors *)
(* After this routine is called all text written to current window will be *)
(* displayed in the colors declared when the window was opened *)
BEGIN
WITH window_list^ DO
BEGIN
textcolor(fore_color) ;
textbackground(back_color) ;
END ;
END ; (* window_normal *)


PROCEDURE window_write(s : string80) ;
(* Write a string to the window at the current cursor position in the *)
(* window described by the first item on the window list *)
(* Strings too long for the window are truncated at the right edge of *)
(* the window. All of the fooling around in last row is to prevent *)
(* the window from scrollong when you write to the lower left corner. *)
VAR
y_pos : byte ;

PROCEDURE last_row ;
VAR
x_pos,i : byte ;
done : boolean ;

PROCEDURE handle_last ;
(* This routine makes sonme BIOS calls to get the current screen *)
(* attribute and then pokes the character into the lower right hand *)
(* corner. There's probably better ways to do this. *)
VAR
attrib : byte ;
last_pos : counter ;
regs : dos_rec ;
BEGIN
WITH window_list^ DO
BEGIN
regs.ax := $0F00 ;
intr($10,Dos.Registers(regs)) ;
regs.ax := $0200 ;
regs.dh := (abs_org.y - 1) + (y_pos - 1) ;
regs.dl := (abs_org.x - 1) + (x_pos - 2) ;
intr($10,Dos.Registers(regs)) ;
regs.ax := $0800 ;
intr($10,Dos.Registers(regs)) ;
attrib := regs.ah ;
last_pos := (((abs_org.y - 1) + (y_pos - 1)) * 80
+ (abs_org.x - 1) + (x_pos - 1)) * 2 ;
mem[seg(screen_base^) : last_pos] := ord(s[i]) ;
mem[seg(screen_base^) : last_pos + 1] := attrib ;
gotoxy(window_size.x,y_pos) ;
done := true ;
END ;
END ; (* handle_last *)

BEGIN
WITH window_list^ DO
BEGIN
i := 1 ;
done := false ;
WHILE (i <= length(s)) AND (NOT done) DO
BEGIN
x_pos := wherex ;
IF (x_pos = window_size.x) AND (y_pos = window_size.y)
THEN handle_last
ELSE IF x_pos = window_size.x
THEN
BEGIN
write(s[i]) ;
gotoxy(window_size.x,y_pos) ;
done := true ;
END
ELSE write(s[i]) ;
i := i + 1 ;
END ;
END ;
END ; (* last_row *)

BEGIN
y_pos := wherey ;
WITH window_list^ DO
IF y_pos = window_size.y
THEN last_row
ELSE
BEGIN
write(copy(s,1,min(length(s),window_size.x - wherex + 1))) ;
IF wherey <> y_pos
THEN gotoxy(window_size.x,y_pos) ;
END ;
END ; (* window_write *)


PROCEDURE window_writeln(s : string80) ;
(* write a string to the current window and the move cursor to *)
(* start of the next line *)
BEGIN
window_write(s) ;
IF wherey < window_list^.window_size.y
THEN gotoxy(1,wherey + 1) ;
END ; (* window_writeln *)


PROCEDURE get_window_co_ords(s_ptr : window_ptr ;
VAR act_org,act_size : window_pos) ;
(* Get the actual origin and size of the window described by *)
(* s_ptr. The physical size of the window includes the frame. The *)
(* size and origin in the descriptor do not. *)
BEGIN
WITH s_ptr^ DO
IF has_frame
THEN
BEGIN
act_org.x := min(max(abs_org.x - 1,1),80) ;
act_org.y := min(max(abs_org.y - 1,1),25) ;
act_size.x := max(min(window_size.x + 2,81 - act_org.x),1) ;
act_size.y := max(min(window_size.y + 2,26 - act_org.y),1) ;
END
ELSE
BEGIN
act_org := abs_org ;
act_size.x := max(min(window_size.x,81 - act_org.x),1) ;
act_size.y := max(min(window_size.y,26 - act_org.y),1) ;
END ;
END ; (* get_window_co_ords *)


PROCEDURE save_window ;
(* save the date from the current window in the windows save area *)
(* If the window doesn't have a save area yet, allocate one for it *)
(* We don't allocate any storage for data for the window until it *)
(* is switched out *)
(* move_from_screen does the actual move from the screen *)
VAR
save_size,save_org : window_pos ;
BEGIN
IF window_list <> NIL
THEN
WITH window_list^ DO
BEGIN
cursor_pos.x := wherex ;
cursor_pos.y := wherey ;
get_window_co_ords(window_list,save_org,save_size) ;
IF scrn_area = NIL
THEN getmem(scrn_area,2 * save_size.x * save_size.y) ;
move_from_scrn(save_org,save_size,scrn_area) ;
END ;
END ; (* save_window *)


PROCEDURE ins_desc(p : window_ptr) ;
(* Insert a window descriptor at the front of the window list *)
BEGIN
p^.next_window :=window_list ;
IF window_list = NIL
THEN last_window := p
ELSE window_list^.prev_window := p ;
p^.prev_window := NIL ;
window_list := p ;
END ; (* ins_desc *)


PROCEDURE del_desc(del_ptr : window_ptr) ;
(* delete a descriptor from the window list *)
BEGIN
IF del_ptr = window_list
THEN
BEGIN
window_list := del_ptr^.next_window ;
window_list^.prev_window := NIL ;
END
ELSE
BEGIN
del_ptr^.prev_window^.next_window := del_ptr^.next_window ;
IF del_ptr^.next_window <> NIL
THEN del_ptr^.next_window^.prev_window := del_ptr^.prev_window ;
END ;
IF window_list = NIL
THEN last_window := NIL
ELSE IF del_ptr = last_window
THEN last_window := del_ptr^.prev_window ;
END ; (* scrn_del_desc *)


FUNCTION open_window(org_x : col_pos ; org_y : row_pos ; size_x : col_pos ;
size_y : row_pos ; use_frame : boolean ; title : string80 ;
f_color,b_color,frame_color : color) : window_ptr ;
(* Create a new window and place it at front of the window list *)
(* This window becomes the current window and is displayed on the screen *)
(* The old window is saved and can be restored *)
(* Returns a pointer to the descriptor of the new window *)
(* org_x,org_y - the upper left hand corner of the window on the PC *)
(* screen. Co-ordinates are measured from (1,1). The frame *)
(* is not part of the window, it is outside. *)
(* size_x,size_y - the number of columns and rows in the window. The *)
(* frame is not included *)
(* use_frame - true if you want a frame around the window. If use_frame *)
(* is false, title and frame_color are ignored *)
(* title - string printed on top line of frame *)
(* f_color - the text color *)
(* b_color - the background color *)
(* frame_color - color of the frame, if present *)

PROCEDURE create_descriptor ;
(* create a window descriptor and insert it in the window list *)
VAR
p : window_ptr ;
BEGIN
getmem(p,sizeof(window_desc)) ;
WITH p^ DO
BEGIN
abs_org.x := org_x ;
abs_org.y := org_y ;
window_size.x := min(size_x,81 - abs_org.x) ;
window_size.y := min(max(2,size_y),26 - abs_org.y) ;
cursor_pos.x := 1 ;
cursor_pos.y := 1 ;
has_frame := use_frame ;
fore_color := f_color ;
back_color := b_color ;
scrn_area := NIL ;
ins_desc(p) ;
END ;
END ; (* create_descriptor *)

BEGIN
IF window_list <> NIL
THEN save_window ;
create_descriptor ;
WITH window_list^ DO
BEGIN
IF use_frame
THEN draw_frame(abs_org.x - 1,abs_org.y - 1,abs_org.x + window_size.x,
abs_org.y + window_size.y,title,frame_color) ;
window(abs_org.x,abs_org.y,abs_org.x + window_size.x - 1,
abs_org.y + window_size.y - 1) ;
textcolor(fore_color) ;
textbackground(back_color) ;
clrscr ;
END ;
open_window := window_list ;
END ; (* open_window *)


PROCEDURE display_window(win_ptr : window_ptr) ;
(* display the window whose descriptor is win_ptr on the screen *)
(* this routine is called by other routines and shouldn't be called *)
(* directly. Use use_window instead *)
VAR
save_size,save_org : window_pos ;
BEGIN
WITH win_ptr^ DO
BEGIN
get_window_co_ords(win_ptr,save_org,save_size) ;
move_to_scrn(save_org,save_size,scrn_area) ;
END ;
END ; (* display_window *)


PROCEDURE use_window(win_ptr : window_ptr) ;
(* make win_ptr the current window, display it and restore cursor *)
(* to its original position. The old window is saved and becomes the *)
(* second window on the list *)
BEGIN
IF win_ptr <> NIL
THEN
IF win_ptr <> window_list
THEN
BEGIN
save_window ;
del_desc(win_ptr) ;
ins_desc(win_ptr) ;
display_window(win_ptr) ;
WITH window_list^ DO
BEGIN
window(abs_org.x,abs_org.y,abs_org.x + window_size.x - 1,
abs_org.y + window_size.y - 1) ;
gotoxy(cursor_pos.x,cursor_pos.y) ;
textcolor(fore_color) ;
textbackground(back_color) ;
END ;
END ;
END ; (* use_window *)


PROCEDURE scrn_refresh ;
(* Re-draw the entire screen. The screen is assembled in a memory *)
(* buffer before being moved to physical screen. The screen is assembled *)
(* from the last window forward. We assemble the screen in memory *)
(* to prevent the annoying screen blank which occurs when you assemble *)
(* dirctly in the screen area *)
(* screen - 4000 byte memory region to assemeble the screen *)
VAR
physical_scrn,save_scrn,screen : char_ptr ;
save_size,save_org : window_pos ;

PROCEDURE scrn_fill(win_ptr : window_ptr) ;
(* This routine is like move_to_scrn, except it moves the data to *)
(* the buffer rather than the actual screen *)
BEGIN
IF win_ptr <> NIL
THEN
BEGIN
WITH win_ptr^ DO
BEGIN
get_window_co_ords(win_ptr,save_org,save_size) ;
physical_scrn := ptr(seg(screen^),ofs(screen^) +
((save_org.y - 1) * 80 + (save_org.x - 1)) * 2) ;
save_scrn := scrn_area ;
FOR i := 1 TO save_size.y DO
BEGIN
move(save_scrn^,physical_scrn^,save_size.x * 2) ;
physical_scrn := ptr(seg(physical_scrn^),ofs(physical_scrn^) + 160) ;
save_scrn := ptr(seg(save_scrn^),ofs(save_scrn^) + save_size.x * 2 ) ;
END ;
END ;
scrn_fill(win_ptr^.prev_window) ;
END ;
END ; (* scrn_fill *)

BEGIN
getmem(screen,4000) ;
fillchar(screen^,4000,chr(0)) ;
scrn_fill(last_window) ;
save_org.x := 1 ;
save_org.y := 1 ;
save_size.x := 80 ;
save_size.y := 25 ;
move_to_scrn(save_org,save_size,screen) ;
freemem(screen,4000) ;
IF window_list <> NIL
THEN
WITH window_list^ DO
BEGIN
window(abs_org.x,abs_org.y,abs_org.x + window_size.x - 1,
abs_org.y + window_size.y - 1) ;
gotoxy(cursor_pos.x,cursor_pos.y) ;
textcolor(fore_color) ;
textbackground(back_color) ;
END
ELSE window(1,1,80,25) ;
END ; (* scrn_refresh *)



PROCEDURE close_window(win_ptr : window_ptr) ;
(* remove the window from the window_list, and then call scrn_refesh *)
(* update the screen. If win_ptr is the current window, the next window *)
(* becomes the active window *)
VAR
save_org,save_size : window_pos ;

FUNCTION found_window : boolean ;
VAR
p : window_ptr ;
found : boolean ;
BEGIN
found := false ;
p := window_list ;
WHILE (p <> NIL) AND (NOT found) DO
BEGIN
found := (win_ptr = p) ;
p := p^.next_window ;
END ;
found_window := found ;
END ; (* found_window *)

BEGIN
IF found_window
THEN
BEGIN
IF win_ptr <> window_list
THEN save_window ;
get_window_co_ords(win_ptr,save_org,save_size) ;
del_desc(win_ptr) ;
IF win_ptr^.scrn_area <> NIL
THEN freemem(win_ptr^.scrn_area,2 * save_size.x * save_size.y) ;
freemem(win_ptr,sizeof(window_desc)) ;
scrn_refresh ;
menu;
END ;
END ; (* close_window *)

(* ///////////////////// Window routines for this program ////////// *)

PROCEDURE wait ;
(* Display a message at bottom of screen and and wait for user to *)
(* press a key *)
VAR
ch : char ;
old_window : window_ptr ;
BEGIN
old_window := window_list ;
use_window(message_window) ;
clrscr ;
gotoxy(1,2) ;
window_write('Press any key to continue ') ;
Read(kbd,ch);
clrscr ;
use_window(old_window) ;
END ; (* wait *)


PROCEDURE init_windows ;
(* Initialize windows for this program *)
BEGIN
ClrScr;
get_monitor_type ;
IF monitor_kind = mono_monitor
THEN button_fore := blue
ELSE button_fore := white ;
button_back := red ;
window_list := NIL ;
message_window := open_window(2,23,78,2,false,'',white,black,white) ;
main_window := open_window(2,2,78,20,true,'HyperText',white,blue,white) ;
gotoxy(10,5) ;
window_writeln('HYPE - Copyright [c] 1987 Knowledge Garden Inc. ') ;
window_writeln(' 473A Malden Bridge Rd. ') ;
window_writeln(' Nassau, NY 12123') ;
window_writeln(' Enhanced (Slightly !) and Recompiled by');
window_writeln(' Brian Corll - June 1988');
wait ;
clrscr ;
END ; (* init_windows *)


PROCEDURE finish_up ;
(* Clean up screen before leaving *)
BEGIN
window(1,1,80,25) ;
textcolor(white) ;
textbackground(black) ;
clrscr ;
END ; (* finish_up *)


PROCEDURE error(msg : string80) ;
(* Display a message and wait for the user to read it *)
VAR
error_window : window_ptr ;
BEGIN
error_window := open_window(10,10,60,3,true,'Error',white,red,white) ;
window_writeln('') ;
window_write(msg) ;
wait ;
close_window(error_window) ;
END ; (* error *)

(* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ *)

FUNCTION got_file : boolean ;
(* Called from main program block to get the file name typed after *)
(* the program at the DOS prompt *)
(* If the file cannot be found, display an error message and quit *)
VAR
f_name : string80 ;
BEGIN
f_name := paramstr(1) ;
IF f_name = ''
THEN
BEGIN
error('Missing file name -- Try ''hype filename''') ;
got_file := false ;
END
ELSE IF open(main_file,f_name)
THEN got_file := true
ELSE
BEGIN
error(concat('Unable to open ',f_name)) ;
got_file := false ;
END ;
END ; (* got_file *)


PROCEDURE process_file(title : string80 ; VAR f : text_file ;
text_window : window_ptr) ;
(* The actual hypertext routine *)
(* Reads file f starting at current line until eof or ..end(title) *)
(* builds a linked list of line descriptors and displays them one page *)
(* at a time in text_window *)
(* first_line - start of list of lines *)
(* last_line - last line *)
(* mark_win_org,mark_win_size,mark_fore,mark_back - window parameters *)
(* for threaded text display *)
VAR
first_line,last_line : line_ptr ;
mark_win_org,mark_win_size : window_pos ;
mark_fore,mark_back : color ;

PROCEDURE release_list(list : line_ptr) ;
(* free memory used by line descriptors and text *)
VAR
p : line_ptr ;
BEGIN
WHILE list <> NIL DO
BEGIN
p := list ;
list := list^.next_line ;
freemem(p^.txt,length(p^.txt^) + 1) ;
freemem(p,sizeof(line_desc)) ;
END ;
END ; (* release_list *)

PROCEDURE read_file(VAR f : text_file ; f_title : string80 ;
VAR first,last : line_ptr) ;
(* read file f until eof or ..end(f_title) *)
(* build linked list of text lines *)
(* lines beginning with .. are processed separately, only lines *)
(* pertaining to concept f_title are processed *)
(* first,last point to the start and end of the line list *)
(* We only allocate enough storage for the actual characters in the line, *)
(* not all 255 characters *)
VAR
line : string255 ;
p : line_ptr ;
done : boolean ;

PROCEDURE insert_line(lne : line_ptr) ;
(* insert a line at the end of the line list *)
BEGIN
lne^.next_line := NIL ;
lne^.prev_line := last ;
IF last = NIL
THEN first := lne
ELSE last^.next_line := lne ;
last := lne ;
END ; (* insert_line *)

PROCEDURE process_dots ;
(* process lines beginning with dots *)

PROCEDURE process_end ;
(* process ..end *)
(* if ..end(f_title) then we are done with this concept *)
BEGIN
delete(line,1,4) ;
strip_leading_blanks(line) ;
IF copy(line,1,length(f_title)) = f_title
THEN done := true ;
END ; (* process_end *)

PROCEDURE process_window ;
(* process ..window(f_title) - sets window parameteres for this concept *)
(* syntax is ..window(f_title) fore_color,back_color,org_x,org_y, *)
(* size_x,size_y *)

FUNCTION read_num(def : integer) : integer ;
(* read next number from line *)
VAR
comma_pos : byte ;
num : string80 ;

FUNCTION get_num(num_str : string80) : integer ;
VAR
finished : boolean ;
n : string80 ;
BEGIN
n := '' ;
finished := false ;
WHILE NOT finished DO
IF num_str = ''
THEN finished := true
ELSE IF num_str[1] IN ['0' .. '9']
THEN
BEGIN
n := concat(n,num_str[1]) ;
delete(num_str,1,1) ;
END
ELSE finished := true ;
get_num := tointeger(n) ;
END ; (* get_num *)

BEGIN
comma_pos := pos(',',line) ;
IF comma_pos > 0
THEN
BEGIN
num := copy(line,1,comma_pos - 1) ;
delete(line,1,comma_pos) ;
END
ELSE
BEGIN
num := line ;
line := '' ;
END ;
strip_leading_blanks(num) ;
IF num = ''
THEN read_num := def
ELSE read_num := get_num(num) ;
END ; (* read_num *)

BEGIN
delete(line,1,7) ;
strip_leading_blanks(line) ;
IF copy(line,1,length(f_title)) = f_title
THEN
BEGIN
delete(line,1,length(f_title)) ;
strip_leading_blanks(line) ;
delete(line,1,1) ;
mark_fore := abs(read_num(def_fore_color)) MOD 16 ;
mark_back := abs(read_num(def_back_color)) MOD 16 ;
mark_win_org.x := max(min(read_num(mark_win_org.x),80),1) ;
mark_win_org.y := max(min(read_num(mark_win_org.y),25),1) ;
mark_win_size.x := max(min(read_num(mark_win_size.x),80),1) ;
mark_win_size.y := max(min(read_num(mark_win_size.y),25),1) ;
END ;
END ; (* process_window *)

PROCEDURE process_new_file ;
(* process ..file(f_title) file_name *)
(* read a list of lines from file_name and attach them to the end *)
(* of the current list *)
VAR
new_file : text_file ;
new_file_name : string80 ;

PROCEDURE read_new_file ;
VAR
new_start,new_last : line_ptr ;
BEGIN
read_file(new_file,f_title,new_start,new_last) ;
IF new_start <> NIL
THEN
BEGIN
new_start^.prev_line := last ;
IF last = NIL
THEN first := new_start
ELSE last^.next_line := new_start ;
last := new_last ;
END ;
close(new_file) ;
END ; (* read_new_file *)

BEGIN
delete(line,1,5) ;
strip_leading_blanks(line) ;
IF copy(line,1,length(f_title)) = f_title
THEN
BEGIN
delete(line,1,length(f_title)) ;
strip_leading_blanks(line) ;
delete(line,1,1) ;
strip_leading_blanks(line) ;
new_file_name := line ;
IF open(new_file,new_file_name)
THEN read_new_file
ELSE error(concat(new_file_name,' can not be read.')) ;
END ;
END ; (* process_new_file *)

BEGIN
line := toupper(copy(line,3,255)) ;
strip_trailing_blanks(line) ;
IF copy(line,1,4) = 'END('
THEN process_end
ELSE IF copy(line,1,7) = 'WINDOW('
THEN process_window
ELSE IF copy(line,1,5) = 'FILE('
THEN process_new_file ;
END ; (* process_dots *)

BEGIN
f_title := toupper(f_title) ;
first := NIL ;
last := NIL ;
done := false ;
WHILE (NOT eof(f)) AND (NOT done) DO
BEGIN
readln(f,line) ;
IF copy(line,1,2) = '..'
THEN process_dots
ELSE
BEGIN
getmem(p,sizeof(line_desc)) ;
getmem(p^.txt,length(line) + 1) ;
p^.txt^ := line ;
insert_line(p) ;
END ;
END ;
END ; (* read_file *)

PROCEDURE display_list(first,last : line_ptr ; disp_window : window_ptr) ;
(* display the list pointed to by first in disp_window *)
(* read keyboard until F10 or Esc is pressed *)
(* left and right arrows move among marked text, Enter selects text *)
(* for display *)
(* Text is displayed one page at a time - PgUp and PgDn page *)
(* mark_list is a linked list of highlighted text on the current page *)
(* of the disp_window *)
(* mark is the current mark, i.e. the one with the button color *)
(* top_of_page points to first line on the page *)
VAR
done : boolean ;
top_of_page : line_ptr ;
mark,mark_list,last_mark : mark_ptr ;


PROCEDURE move_to_mark(m_ptr : mark_ptr) ;
(* move to the highlighted region of screen pointed to by m_ptr *)
(* redisplay text in button colors so that user can see where we are *)
VAR
p : mark_ptr ;

PROCEDURE remove_old_mark ;
(* return previous marked text to reverse video *)
BEGIN
gotoxy(mark^.mark_pos.x,mark^.mark_pos.y) ;
window_reverse ;
window_write(mark^.mark_text^) ;
window_normal ;
END ; (* remove_old_mark *)

BEGIN
IF m_ptr <> NIL
THEN
BEGIN
IF mark <> NIL
THEN remove_old_mark ;
p := mark_list ;
WHILE (p <> NIL) AND (p <> m_ptr) DO
p := p^.next_mark ;
IF p <> NIL
THEN
BEGIN
mark := p ;
gotoxy(mark^.mark_pos.x,mark^.mark_pos.y) ;
textcolor(button_fore) ;
textbackground(button_back) ;
window_write(mark^.mark_text^) ;
window_normal ;
gotoxy(mark^.mark_pos.x,mark^.mark_pos.y) ;
END ;
END ;
END ; (* move_to_mark *)

PROCEDURE display_page ;
(* display a page of text in disp_window *)
(* marked text is displayed inreverse video *)
(* move mark to first item on mark list *)
VAR
line_cnt : counter ;
p : line_ptr ;

PROCEDURE release_marks ;
(* release the old mark list - the mark list is rebuilt each *)
(* time a page is displayed *)
VAR
m_ptr : mark_ptr ;
BEGIN
WHILE mark_list <> NIL DO
BEGIN
m_ptr := mark_list ;
mark_list := mark_list^.next_mark ;
freemem(m_ptr^.mark_text,length(m_ptr^.mark_text^) + 1) ;
freemem(m_ptr,sizeof(mark_desc)) ;
END ;
mark := NIL ;
last_mark := NIL ;
END ; (* release_marks *)

PROCEDURE write_the_line(s : string255) ;
(* write the line on the screen *)
(* if text is marked add it to list and display inreverse video *)
VAR
mark_loc : byte ;

PROCEDURE add_mark ;
(* add this text to list and save its co-ordinates *)
VAR
m_ptr : mark_ptr ;
ps : integer ;
BEGIN
getmem(m_ptr,sizeof(mark_desc)) ;
m_ptr^.mark_pos.x := wherex ;
m_ptr^.mark_pos.y := wherey ;
delete(s,1,1) ;
ps := pred(pos(mark_char,s)) ;
IF ps < 0
THEN ps := length(s) ;
getmem(m_ptr^.mark_text,ps + 1) ;
m_ptr^.mark_text^ := copy(s,1,ps) ;
window_reverse ;
window_write(m_ptr^.mark_text^) ;
window_normal ;
delete(s,1,succ(ps)) ;
m_ptr^.next_mark := NIL ;
m_ptr^.prev_mark := last_mark ;
IF last_mark = NIL
THEN mark_list := m_ptr
ELSE last_mark^.next_mark := m_ptr ;
last_mark := m_ptr ;
END ; (* add_mark *)

BEGIN
IF s <> ''
THEN
BEGIN
mark_loc := pos(mark_char,s) ;
IF mark_loc > 0
THEN
BEGIN
window_write(copy(s,1,pred(mark_loc))) ;
delete(s,1,pred(mark_loc)) ;
add_mark ;
write_the_line(s) ;
END
ELSE window_write(s) ;
END ;
END ; (* write_the_line *)

BEGIN
release_marks ;
clrscr ;
p := top_of_page ;
line_cnt := 1 ;
WHILE (p <> NIL) AND (line_cnt <= disp_window^.window_size.y) DO
BEGIN
gotoxy(1,line_cnt) ;
IF copy(p^.txt^,1,2) <> '..'
THEN
BEGIN
write_the_line(p^.txt^) ;
line_cnt := succ(line_cnt) ;
END ;
p := p^.next_line ;
END ;
move_to_mark(mark_list)
END ; (* display_page *)

PROCEDURE handle_keys ;
(* read the keyboard - ignore everything but keys displayed on bottom *)
(* of screen *)
VAR
ch : char ;

PROCEDURE exit_prog ;
(* F10 - pressed erase screen and quit *)
BEGIN
finish_up ;
halt(0) ;
END ; (* exit_prog *)

PROCEDURE page_forward ;
(* display previous page *)
(* count backwards until we get to it *)
VAR
p : line_ptr ;
line_cnt : counter ;
BEGIN
p := top_of_page ;
line_cnt := 1 ;
WHILE (p <> NIL) AND (line_cnt < disp_window^.window_size.y) DO
BEGIN
p := p^.next_line ;
line_cnt := succ(line_cnt) ;
END ;
IF p <> NIL
THEN
IF p^.next_line <> NIL
THEN
BEGIN
top_of_page := p^.next_line ;
display_page ;
END ;
END ; (* page_forward *)

PROCEDURE page_back ;
(* display next page *)
(* count forwards until we get to it *)
VAR
p : line_ptr ;
line_cnt : counter ;
BEGIN
p := top_of_page ;
line_cnt := disp_window^.window_size.y ;
WHILE (p <> NIL) AND (line_cnt >= 1) do
BEGIN
p := p^.prev_line ;
line_cnt := pred(line_cnt) ;
END ;
IF p <> NIL
THEN
BEGIN
top_of_page := p ;
display_page ;
END ;
END ; (* page_back *)

PROCEDURE move_to_next_mark ;
(* move to next mark on screen, if at end go back to first *)
BEGIN
IF mark_list <> NIL
THEN
BEGIN
IF mark^.next_mark <> NIL
THEN move_to_mark(mark^.next_mark)
ELSE move_to_mark(mark_list) ;
END ;
END ; (* move_to_next_mark *)

PROCEDURE move_to_prev_mark ;
(* move to prev mark on screen, if at first go to end *)
BEGIN
IF mark_list <> NIL
THEN
BEGIN
IF mark^.prev_mark <> NIL
THEN move_to_mark(mark^.prev_mark)
ELSE move_to_mark(last_mark) ;
END ;
END ; (* move_to_prev_mark *)

PROCEDURE process_mark ;
(* process the text under the button *)
(* find its lable in the file, open a window and display it *)
VAR
mark_start,mark_end : line_ptr ;
mark_window : window_ptr ;

FUNCTION found_mark : boolean ;
VAR
found : boolean ;
mark_str,line : string255 ;
BEGIN
mark_str := toupper(mark^.mark_text^) ;
found := false ;
reset(f) ;
WHILE (NOT eof(f)) AND (NOT found) DO
BEGIN
readln(f,line) ;
found := (toupper(copy(line,3,255)) = mark_str) ;
END ;
found_mark := found ;
END ; (* found_mark *)

PROCEDURE set_window_parameters ;
(* set default window paramters *)
BEGIN
mark_win_org.x := (disp_window^.abs_org.x + 2) MOD 8 ;
mark_win_org.y := (disp_window^.abs_org.y + 2) MOD 8 ;
mark_win_size.x := def_window_size_x ;
mark_win_size.y := def_window_size_y ;
mark_fore := def_fore_color ;
mark_back := def_back_color ;
END ; (* set_window_parameters *)

BEGIN
IF mark_list <> NIL
THEN
IF found_mark
THEN
BEGIN
set_window_parameters ;
read_file(f,mark^.mark_text^,mark_start,mark_end) ;
mark_window := open_window(mark_win_org.x,mark_win_org.y,
mark_win_size.x,mark_win_size.y,
true,mark^.mark_text^,mark_fore,
mark_back,mark_fore) ;
display_list(mark_start,mark_end,mark_window) ;
close_window(mark_window) ;
use_window(disp_window) ;
release_list(mark_start) ;
END
ELSE
BEGIN
error(concat('''',mark^.mark_text^,''' could not be found.')) ;
menu;
END ;
END ; (* process_mark *)

BEGIN
Read(kbd,ch);
IF ch = enter
THEN process_mark
ELSE IF ch = esc
THEN
IF keypressed
THEN
BEGIN
read(kbd,ch) ;
CASE ch OF
down_arrow : move_to_next_mark ;
up_arrow : move_to_prev_mark ;
PgUp : page_back ;
PgDn : page_forward ;
F10 : exit_prog ;
END ;
END
ELSE done := true ;
END ; (* handle_keys *)

BEGIN
done := false ;
menu;
mark := NIL ;
mark_list := NIL ;
last_mark := NIL ;
top_of_page := first ;
display_page ;
WHILE NOT done DO
handle_keys ;
END ; (* display_list *)

BEGIN
SetTextBuf(f,Buffer);
reset(f) ;
read_file(f,title,first_line,last_line) ;
display_list(first_line,last_line,text_window) ;
release_list(first_line) ;
END ; (* process_file *)


BEGIN
init_windows ;
IF got_file
THEN
BEGIN
process_file('MAIN',main_file,main_window) ;
close(main_file) ;
END ;
finish_up ;
END.