home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / HYPE11.ZIP / HYPE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-06-16  |  43.3 KB  |  1,449 lines

  1. {$R-}    {Range checking off}
  2. {$B+}    {Boolean complete evaluation on}
  3. {$S+}    {Stack checking on}
  4. {$I+}    {I/O checking on}
  5. {$N-}    {No numeric coprocessor}
  6. {$M 65500,16384,655360} {Turbo 3 default stack and heap}
  7.  
  8. {.PW132}
  9. {.HE HYPE.PAS                                        Page # }
  10. {$R+,V-}
  11. PROGRAM HyperText ;
  12.  
  13. (* Copyright 1987 - Knowledge Garden Inc.
  14.                     473A Malden Bridge Rd.
  15.                     R.D. 2
  16.                     Nassau, NY 12123       *)
  17.  
  18.  
  19. (* This program implements the hypertext technique described in the
  20.    AI apprentice column in August 1987 issue of AI Expert Magazine.
  21.  
  22.    This program has been tested using Turbo ver 3.01A on an IBM PC/AT and
  23.    two PC clones. It has  been run under both DOS 3.2 and Concurrent 5.0 .
  24.  
  25.    We would be pleased to hear your comments, good or bad, or any applications
  26.    and modifications of the program. Contact us at:
  27.  
  28.      AI Expert
  29.      Miller Freeman Publications
  30.      500 Howard Street
  31.      San Francisco, CA 94105
  32.  
  33.    or on the AI Expert BBS. Our id is BillandBev Thompson ,[76703,4324].
  34.    You can also contact us on BIX, our id is bbt.
  35.  
  36.    Bill and Bev Thompson    *)
  37.  
  38.  
  39.  
  40. Uses Crt, Dos,Turbo3,Qwik;
  41.  
  42.  
  43. CONST
  44.   color_base = $B800 ;   (* Location of PC color screen memory map *)
  45.   mono_base = $B000 ;    (* Location of PC mono screen memory map *)
  46.   esc = #27 ;      (* These rest of these constants could have been defined in *)
  47.   F10 = #68 ;      (* process_file, but we put them here for convenience *)
  48.   up_arrow = #72 ;
  49.   down_arrow = #80 ;
  50.   PgUp = #73 ;
  51.   PgDn = #81 ;
  52.   mark_char = '\' ;
  53.   enter = #13 ;
  54.   def_window_size_x = 65 ;
  55.   def_window_size_y = 12 ;
  56.   def_fore_color = white ;
  57.   def_back_color = red ;
  58.   MaxWndw = 100;
  59.  
  60.  TYPE
  61.   counter = 0 .. maxint ;
  62.   text_file = text;
  63.   string255 = string[255] ;
  64.   string80 = string[80] ;
  65.   LongString = String[80];
  66.   char_ptr = ^char ;
  67.   col_pos = 1 .. 80 ;      (* The PC screen is 80 by 25 *)
  68.   row_pos = 1 .. 25 ;
  69.   color = 0 .. 31 ;
  70.   window_pos = RECORD           (* cursor location on screen *)
  71.                 x : col_pos ;
  72.                 y : row_pos ;
  73.                END ;
  74.   window_ptr = ^window_desc ;
  75.   window_desc = RECORD                        (* Basic window description *)
  76.                  next_window : window_ptr ;   (* windows are linked lists of *)
  77.                  prev_window : window_ptr ;   (* these descriptors *)
  78.                  abs_org     : window_pos ;   (* origin relative to upper left *)
  79.                  window_size : window_pos ;   (* rows and columns in window *)
  80.                  cursor_pos  : window_pos ;   (* saves current cursor location *)
  81.                  has_frame   : boolean ;      (* size and org do not include frame *)
  82.                  fore_color  : color ;
  83.                  back_color  : color ;
  84.                  scrn_area   : char_ptr ;      (* pointer to actual window data *)
  85.                 END ;
  86.   string_ptr = ^string255 ;   (* we don't actually allocate space for 255 chars *)
  87.   line_ptr = ^line_desc ;
  88.   line_desc = RECORD                 (* text is stored as a linked list *)
  89.                next_line : line_ptr ;
  90.                prev_line : line_ptr ;
  91.                txt       : string_ptr ; (* points to actual text data *)
  92.               END ;
  93.   mark_ptr = ^mark_desc ;
  94.   mark_desc = RECORD                   (* marked text is also a linked list *)
  95.                next_mark : mark_ptr ;
  96.                prev_mark : mark_ptr ;
  97.                mark_pos  : window_pos ;  (* location of start of mark in window *)
  98.                mark_text : string_ptr ;  (* actual marked text *)
  99.               END ;
  100.   dos_rec = RECORD                       (* used for low-level functions *)
  101.              CASE boolean OF
  102.               true  : (ax,bx,cx,dx,bp,si,di,ds,es,flags : integer) ;
  103.               false : (al,ah,bl,bh,cl,ch,dl,dh          : byte) ;
  104.              END ;
  105.   monitor_type = (color_monitor,mono_monitor,ega_monitor) ;
  106.  
  107.  
  108.  VAR
  109.   window_list,main_window,message_window,last_window : window_ptr ;
  110.   screen_base : char_ptr ;
  111.   monitor_kind : monitor_type ;
  112.   main_file : text_file ;
  113.   button_fore,button_back : color ;
  114.   i : row_pos;
  115.   Buffer : Array[1..51200] of Char;
  116.   Ch : Char;
  117. (* Important variables:
  118.    window_list - points to a linked list of window descriptors,
  119.                  the top window is the currently active window.
  120.                  To write in a window, bring it to the front of the list.
  121.    last_window - points to end of window list
  122.    main_window - the big window, that text initially appears in
  123.    message_window - 2 line area at the bottom of the screen, available keys,
  124.                     commands etc. appear here
  125.    screen_base - points to actual memory location of screen, either
  126.                  mono_base or color_base
  127.    main_file - the original text file, the one we start the program with
  128.    button_fore,
  129.    button_back - the button is the large cursor which moves from mark to mark
  130.                  on a color screen it is yellow on black, on a mono screen
  131.                  the text is underlined. *)
  132.  
  133.  
  134.  (* Note - In most cases this program uses the Turbo standard string
  135.            functions. You can probably get better performance by turning
  136.            off range checking and accessing the strings directly, but
  137.            we didn't want to make this program even less portable than it
  138.            already is. *)
  139.  
  140. (* \\\\\\\\\\\\\ Basic Utility Routines  \\\\\\\\\\\\\\\\\\\\\\ *)
  141.  
  142. Procedure Menu;
  143. Var
  144.     MenuLine : LongString;
  145. begin
  146.     MenuLine := ' Esc : Prev. Window; '+#24+','+#25+',Enter : Select; PgUp,PgDn : Page Text; F10 : Quit ';
  147.     QWriteC(25,2,80,Black+LightGrayBG,MenuLine);
  148. end;
  149.  
  150.  FUNCTION min(x,y : integer) : integer ;
  151.   BEGIN
  152.    IF x <= y
  153.     THEN min := x
  154.     ELSE min := y ;
  155.   END ; (* min *)
  156.  
  157.  
  158.  FUNCTION max(x,y : integer) : integer ;
  159.   BEGIN
  160.    IF x >= y
  161.     THEN max := x
  162.     ELSE max := y ;
  163.   END ; (* max *)
  164.  
  165.  
  166.  PROCEDURE makestr(VAR s : string255 ; len : byte) ;
  167.   (* Fixes string "s" to length "len" - pads with blanks if necessary. *)
  168.   VAR
  169.    old_length : byte ;
  170.   BEGIN
  171.    old_length := length(s) ;
  172.    (*$R- *)
  173.    s[0] := chr(len) ;
  174.    (*$R+ *)
  175.    IF old_length < len
  176.     THEN fillchar(s[old_length+1],len - old_length,' ') ;
  177.   END ; (* makestr *)
  178.  
  179.  
  180.  FUNCTION toupper(s : string255) : string255 ;
  181.   (* converts a string to uppercase *)
  182.   VAR
  183.    i : byte ;
  184.   BEGIN
  185.    IF length(s) > 0
  186.     THEN
  187.      FOR i := 1 TO length(s) DO
  188.       s[i] := upcase(s[i]) ;
  189.    toupper := s ;
  190.   END ; (* toupper *)
  191.  
  192.  
  193.  PROCEDURE strip_leading_blanks(VAR s : string255) ;
  194.   (* Trim the leading blanks from a string *)
  195.   BEGIN
  196.    IF length(s) > 0
  197.     THEN
  198.      IF s[1] = ' '
  199.       THEN
  200.        BEGIN
  201.         delete(s,1,1) ;
  202.         strip_leading_blanks(s) ;
  203.        END ;
  204.   END ; (* strip_leading_blanks *)
  205.  
  206.  
  207.  PROCEDURE strip_trailing_blanks(VAR s : string255) ;
  208.   (* Trim the trailing blanks from a string *)
  209.   BEGIN
  210.    IF length(s) > 0
  211.     THEN
  212.      IF s[length(s)] = ' '
  213.       THEN
  214.        BEGIN
  215.         delete(s,length(s),1) ;
  216.         strip_trailing_blanks(s) ;
  217.        END ;
  218.   END ; (* strip_trailing_blanks *)
  219.  
  220.  
  221.  FUNCTION tointeger(s : string255) : integer ;
  222.   (* converts a string to an integer. Returns 0 for non-numeric strings *)
  223.   VAR
  224.    num : real ;
  225.    code : integer ;
  226.   BEGIN
  227.    strip_trailing_blanks(s) ;
  228.    strip_leading_blanks(s) ;
  229.    val(s,num,code) ;
  230.    IF code = 0
  231.     THEN
  232.      IF (num < -32768.0) OR (num > 32767.0)
  233.       THEN tointeger := 0
  234.       ELSE tointeger := trunc(num)
  235.     ELSE tointeger := 0 ;
  236.   END ; (* tointeger *)
  237.  
  238.  
  239.  FUNCTION open(VAR f : text_file ; f_name : string80) : boolean ;
  240.   (* Open a text file and return true if file can be opened *)
  241.   BEGIN
  242.    assign(f,f_name) ;
  243.    (*$I- *)
  244.    reset(f) ;
  245.    (*$I+ *)
  246.    open := (ioresult = 0) ;
  247.   END ; (* open *)
  248.  
  249.  
  250. (* \\\\\\\\\\\\\\\\\\\ Window Routines \\\\\\\\\\\\\\\\\\\\ *)
  251.  
  252.  PROCEDURE draw_frame(x1,y1,x2,y2 : counter ; title : string80 ;
  253.                       frame_color : color) ;
  254.   (* Draw a frame on the screen at absolute screen positions *)
  255.   (* x1,y1 - upper left corner *)
  256.   (* x2,y2 - lower right corner *)
  257.   CONST
  258.    bar = #196 ;
  259.    vert_bar = #179 ;
  260.    upper_lf = #218 ;
  261.    upper_rt = #191 ;
  262.    lower_lf = #192 ;
  263.    lower_rt = #217 ;
  264.   VAR
  265.    i : 1 .. 25 ;
  266.    border : string80 ;
  267.  
  268.   PROCEDURE get_frame_co_ords ;
  269.    BEGIN
  270.     x1 := min(max(1,x1),78) ;
  271.     y1 := min(max(1,y1),23) ;
  272.     x2 := min(max(3,x2),80) ;
  273.     y2 := min(max(3,y2),25) ;
  274.    END ; (* get_frame_co_ords *)
  275.  
  276.   PROCEDURE write_title ;
  277.    BEGIN
  278.     IF length(title) > (x2 - x1 - 1)
  279.      THEN title := copy(title,1,x2 - x1 - 1) ;
  280.     write(title) ;
  281.     write(copy(border,1,length(border) - length(title))) ;
  282.    END ; (* write_title *)
  283.  
  284.   BEGIN
  285.    get_frame_co_ords ;
  286.    window(1,1,80,25) ;
  287.    border := '' ;
  288.    makestr(border,x2 - x1 - 1) ;
  289.    fillchar(border[1],x2 - x1 - 1,bar) ;
  290.    gotoxy(x1,y1) ;
  291.    textcolor(frame_color) ;
  292.    textbackground(black) ;
  293.    write(upper_lf) ;
  294.    write_title ;
  295.    write(upper_rt) ;
  296.    FOR i := y1 + 1 TO y2 - 1  DO
  297.     BEGIN
  298.      gotoxy(x1,i) ;
  299.      write(vert_bar) ;
  300.      gotoxy(x2,i) ;
  301.      write(vert_bar) ;
  302.     END ;
  303.    gotoxy(x1,y2) ;
  304.    write(lower_lf) ;
  305.    write(border) ;
  306.    IF (wherex = 80) AND (wherey = 25)
  307.     THEN
  308.      BEGIN
  309.       mem[seg(screen_base^) : 3998] := ord(lower_rt) ;
  310.       mem[seg(screen_base^) : 3999] := (black SHL 4) + frame_color ;
  311.      END
  312.     ELSE write(lower_rt) ;
  313.   END ; (* draw_frame *)
  314.  
  315.  
  316.  PROCEDURE retrace_wait ;
  317.   (* This routine is a delay to prevent snow on a CGA screen *)
  318.   (* It is unecessary for mono and EGA. It watches the color status reg *)
  319.   (* until the horizontal retrace is finished. On CGA clones it may not *)
  320.   (* be needed, so try removing the calls to it and see if you get snow. *)
  321.   CONST
  322.    color_status_reg = $3DA ;
  323.   BEGIN
  324.    IF monitor_kind = color_monitor
  325.     THEN WHILE (port[color_status_reg] AND $08) = 0 DO ;
  326.   END ; (* retrace_wait *)
  327.  
  328.  
  329.  PROCEDURE get_monitor_type ;
  330.   (* find out what kind of display we are using *)
  331.   (* A hercules card is a mono card *)
  332.   VAR
  333.    regs : dos_rec ;
  334.   BEGIN
  335.    WITH regs DO
  336.     BEGIN
  337.      ah := $12 ;
  338.      bh := $03 ;
  339.      bl := $10 ;
  340.     END ;
  341.    intr($10,Dos.Registers(regs)) ;
  342.    IF regs.bh < 2
  343.     THEN
  344.      BEGIN
  345.       monitor_kind := ega_monitor ;
  346.       screen_base := ptr(color_base,0) ;
  347.      END
  348.     ELSE
  349.      BEGIN
  350.       regs.ax := $0F00 ;
  351.       intr($10,Dos.Registers(regs)) ;
  352.       IF regs.al < 7
  353.        THEN
  354.         BEGIN
  355.          monitor_kind := color_monitor ;
  356.          screen_base := ptr(color_base,0) ;
  357.         END
  358.        ELSE
  359.         BEGIN
  360.          monitor_kind := mono_monitor ;
  361.          screen_base := ptr(mono_base,0) ;
  362.         END
  363.      END ;
  364.   END ; (* get_monitor_type *)
  365.  
  366.  
  367.  PROCEDURE move_from_scrn(save_org,save_size : window_pos ;
  368.                           save_scrn : char_ptr) ;
  369.   (* Move data from physical screen memory-map area to save_scrn *)
  370.   (* i.e. reads the the screen *)
  371.   (* It moves characters and attributes starting at location given by *)
  372.   (* save_org. It copies save_size.x cols by save_size.y rows *)
  373.   (* Copy is performed on row at a time *)
  374.   (* This routine is extremely machine specific *)
  375.   VAR
  376.    physical_scrn : char_ptr ;
  377.    i : row_pos ;
  378.   BEGIN
  379.    physical_scrn := ptr(seg(screen_base^),ofs(screen_base^) +
  380.                         ((save_org.y - 1) * 80 + (save_org.x - 1)) * 2) ;
  381.    FOR i := 1 TO save_size.y DO
  382.     BEGIN
  383.      retrace_wait ;
  384.      move(physical_scrn^,save_scrn^,save_size.x * 2) ;
  385.      physical_scrn := ptr(seg(physical_scrn^),ofs(physical_scrn^) + 160) ;
  386.      save_scrn := ptr(seg(save_scrn^),ofs(save_scrn^) + save_size.x * 2) ;
  387.     END ;
  388.   END ; (* move_from_scrn *)
  389.  
  390.  
  391.  PROCEDURE move_to_scrn(save_org,save_size : window_pos ;
  392.                         save_scrn : char_ptr) ;
  393.   (* Move data from save_scrn to physical screen memory-map area, *)
  394.   (* i.e. displays data on the screen *)
  395.   (* It moves characters and attributes starting at location given by *)
  396.   (* save_org. It copies save_size.x cols by save_size.y rows *)
  397.   (* Copy is performed on row at a time *)
  398.   (* This routine is extremely machine specific *)
  399.   VAR
  400.    physical_scrn : char_ptr ;
  401.    i : row_pos ;
  402.   BEGIN
  403.    physical_scrn := ptr(seg(screen_base^),ofs(screen_base^) +
  404.                         ((save_org.y - 1) * 80 + (save_org.x - 1)) * 2) ;
  405.    FOR i := 1 TO save_size.y DO
  406.     BEGIN
  407.      retrace_wait ;
  408.      move(save_scrn^,physical_scrn^,save_size.x * 2) ;
  409.      physical_scrn := ptr(seg(physical_scrn^),ofs(physical_scrn^) + 160) ;
  410.      save_scrn := ptr(seg(save_scrn^),ofs(save_scrn^) + save_size.x * 2) ;
  411.     END ;
  412.   END ; (* move_to_scrn *)
  413.  
  414.  
  415.  PROCEDURE window_reverse ;
  416.   (* After this routine is called all text written to current window will be *)
  417.   (* displayed in reverse video *)
  418.   BEGIN
  419.    WITH window_list^ DO
  420.     BEGIN
  421.      textcolor(back_color) ;
  422.      textbackground(fore_color) ;
  423.     END ;
  424.   END ; (* window_reverse *)
  425.  
  426.  
  427.  PROCEDURE window_normal ;
  428.   (* returns to normal colors *)
  429.   (* After this routine is called all text written to current window will be *)
  430.   (* displayed in the colors declared when the window was opened *)
  431.   BEGIN
  432.    WITH window_list^ DO
  433.     BEGIN
  434.      textcolor(fore_color) ;
  435.      textbackground(back_color) ;
  436.     END ;
  437.   END ; (* window_normal *)
  438.  
  439.  
  440.  PROCEDURE window_write(s : string80) ;
  441.   (* Write a string to the window at the current cursor position in the *)
  442.   (* window described by the first item on the window list *)
  443.   (* Strings too long for the window are truncated at the right edge of *)
  444.   (* the window. All of the fooling around in last row is to prevent *)
  445.   (* the window from scrollong when you write to the lower left corner. *)
  446.   VAR
  447.    y_pos : byte ;
  448.  
  449.   PROCEDURE last_row ;
  450.    VAR
  451.     x_pos,i : byte ;
  452.     done : boolean ;
  453.  
  454.    PROCEDURE handle_last ;
  455.     (* This routine makes sonme BIOS calls to get the current screen *)
  456.     (* attribute and then pokes the character into the lower right hand *)
  457.     (* corner. There's probably better ways to do this. *)
  458.     VAR
  459.      attrib : byte ;
  460.      last_pos : counter ;
  461.      regs : dos_rec ;
  462.     BEGIN
  463.      WITH window_list^ DO
  464.       BEGIN
  465.        regs.ax := $0F00 ;
  466.        intr($10,Dos.Registers(regs)) ;
  467.        regs.ax := $0200 ;
  468.        regs.dh := (abs_org.y - 1) + (y_pos - 1) ;
  469.        regs.dl := (abs_org.x - 1) + (x_pos - 2) ;
  470.        intr($10,Dos.Registers(regs)) ;
  471.        regs.ax := $0800 ;
  472.        intr($10,Dos.Registers(regs)) ;
  473.        attrib := regs.ah ;
  474.        last_pos := (((abs_org.y - 1) + (y_pos - 1)) * 80
  475.                    + (abs_org.x - 1) + (x_pos - 1)) * 2 ;
  476.        mem[seg(screen_base^) : last_pos] := ord(s[i]) ;
  477.        mem[seg(screen_base^) : last_pos + 1] := attrib ;
  478.        gotoxy(window_size.x,y_pos) ;
  479.        done := true ;
  480.       END ;
  481.     END ; (* handle_last *)
  482.  
  483.    BEGIN
  484.     WITH window_list^ DO
  485.      BEGIN
  486.       i := 1 ;
  487.       done := false ;
  488.       WHILE (i <= length(s)) AND (NOT done) DO
  489.        BEGIN
  490.         x_pos := wherex ;
  491.         IF (x_pos = window_size.x) AND (y_pos = window_size.y)
  492.          THEN handle_last
  493.         ELSE IF x_pos = window_size.x
  494.          THEN
  495.           BEGIN
  496.            write(s[i]) ;
  497.            gotoxy(window_size.x,y_pos) ;
  498.            done := true ;
  499.           END
  500.         ELSE write(s[i]) ;
  501.         i := i + 1 ;
  502.        END ;
  503.      END ;
  504.    END ; (* last_row *)
  505.  
  506.   BEGIN
  507.    y_pos := wherey ;
  508.    WITH window_list^ DO
  509.     IF y_pos = window_size.y
  510.      THEN last_row
  511.      ELSE
  512.       BEGIN
  513.        write(copy(s,1,min(length(s),window_size.x - wherex + 1))) ;
  514.        IF wherey <> y_pos
  515.         THEN gotoxy(window_size.x,y_pos) ;
  516.       END ;
  517.   END ; (* window_write *)
  518.  
  519.  
  520.  PROCEDURE window_writeln(s : string80) ;
  521.   (* write a string to the current window and the move cursor to *)
  522.   (* start of the next line *)
  523.   BEGIN
  524.    window_write(s) ;
  525.    IF wherey < window_list^.window_size.y
  526.     THEN gotoxy(1,wherey + 1) ;
  527.   END ; (* window_writeln *)
  528.  
  529.  
  530.  PROCEDURE get_window_co_ords(s_ptr : window_ptr ;
  531.                             VAR act_org,act_size : window_pos) ;
  532.   (* Get the actual origin and size of the window described by *)
  533.   (* s_ptr. The physical size of the window includes the frame. The *)
  534.   (* size and origin in the descriptor do not. *)
  535.   BEGIN
  536.    WITH s_ptr^ DO
  537.     IF has_frame
  538.      THEN
  539.       BEGIN
  540.        act_org.x := min(max(abs_org.x - 1,1),80) ;
  541.        act_org.y := min(max(abs_org.y - 1,1),25) ;
  542.        act_size.x := max(min(window_size.x + 2,81 - act_org.x),1) ;
  543.        act_size.y := max(min(window_size.y + 2,26 - act_org.y),1) ;
  544.       END
  545.      ELSE
  546.       BEGIN
  547.        act_org := abs_org ;
  548.        act_size.x := max(min(window_size.x,81 - act_org.x),1) ;
  549.        act_size.y := max(min(window_size.y,26 - act_org.y),1) ;
  550.       END ;
  551.   END ; (* get_window_co_ords *)
  552.  
  553.  
  554.  PROCEDURE save_window ;
  555.   (* save the date from the current window in the windows save area *)
  556.   (* If the window doesn't have a save area yet, allocate one for it *)
  557.   (* We don't allocate any storage for data for the window until it *)
  558.   (* is switched out *)
  559.   (* move_from_screen does the actual move from the screen *)
  560.   VAR
  561.    save_size,save_org : window_pos ;
  562.   BEGIN
  563.    IF window_list <> NIL
  564.     THEN
  565.      WITH window_list^ DO
  566.       BEGIN
  567.        cursor_pos.x := wherex ;
  568.        cursor_pos.y := wherey ;
  569.        get_window_co_ords(window_list,save_org,save_size) ;
  570.        IF scrn_area = NIL
  571.         THEN getmem(scrn_area,2 * save_size.x * save_size.y) ;
  572.        move_from_scrn(save_org,save_size,scrn_area) ;
  573.       END ;
  574.   END ; (* save_window *)
  575.  
  576.  
  577.  PROCEDURE ins_desc(p : window_ptr) ;
  578.   (* Insert a window descriptor at the front of the window list *)
  579.   BEGIN
  580.    p^.next_window :=window_list ;
  581.    IF window_list = NIL
  582.     THEN last_window := p
  583.     ELSE window_list^.prev_window := p ;
  584.    p^.prev_window := NIL ;
  585.    window_list := p ;
  586.   END ; (* ins_desc *)
  587.  
  588.  
  589.  PROCEDURE del_desc(del_ptr : window_ptr) ;
  590.   (* delete a descriptor from the window list *)
  591.   BEGIN
  592.    IF del_ptr = window_list
  593.     THEN
  594.      BEGIN
  595.       window_list := del_ptr^.next_window ;
  596.       window_list^.prev_window := NIL ;
  597.      END
  598.     ELSE
  599.      BEGIN
  600.       del_ptr^.prev_window^.next_window := del_ptr^.next_window ;
  601.       IF del_ptr^.next_window <> NIL
  602.        THEN del_ptr^.next_window^.prev_window := del_ptr^.prev_window ;
  603.      END ;
  604.    IF window_list = NIL
  605.     THEN last_window := NIL
  606.    ELSE IF del_ptr = last_window
  607.     THEN last_window := del_ptr^.prev_window ;
  608.   END ; (* scrn_del_desc *)
  609.  
  610.  
  611.  FUNCTION open_window(org_x : col_pos ; org_y : row_pos ; size_x : col_pos ;
  612.                       size_y : row_pos ; use_frame : boolean ; title : string80 ;
  613.                       f_color,b_color,frame_color : color) : window_ptr ;
  614.    (* Create a new window and place it at front of the window list *)
  615.    (* This window becomes the current window and is displayed on the screen *)
  616.    (* The old window is saved and can be restored *)
  617.    (* Returns a pointer to the descriptor of the new window *)
  618.    (* org_x,org_y - the upper left hand corner of the window on the PC *)
  619.    (*               screen. Co-ordinates are measured from (1,1). The frame *)
  620.    (*               is not part of the window, it is outside. *)
  621.    (* size_x,size_y - the number of columns and rows in the window. The *)
  622.    (*                 frame is not included *)
  623.    (* use_frame - true if you want a frame around the window. If use_frame *)
  624.    (*             is false, title and frame_color are ignored *)
  625.    (* title - string printed on top line of frame *)
  626.    (* f_color - the text color *)
  627.    (* b_color - the background color *)
  628.    (* frame_color - color of the frame, if present *)
  629.  
  630.   PROCEDURE create_descriptor ;
  631.    (* create a window descriptor and insert it in the window list *)
  632.    VAR
  633.     p : window_ptr ;
  634.    BEGIN
  635.     getmem(p,sizeof(window_desc)) ;
  636.     WITH p^ DO
  637.      BEGIN
  638.       abs_org.x := org_x ;
  639.       abs_org.y := org_y ;
  640.       window_size.x := min(size_x,81 - abs_org.x) ;
  641.       window_size.y := min(max(2,size_y),26 - abs_org.y) ;
  642.       cursor_pos.x := 1 ;
  643.       cursor_pos.y := 1 ;
  644.       has_frame := use_frame ;
  645.       fore_color := f_color ;
  646.       back_color := b_color ;
  647.       scrn_area := NIL ;
  648.       ins_desc(p) ;
  649.      END ;
  650.    END ; (* create_descriptor *)
  651.  
  652.   BEGIN
  653.    IF window_list <> NIL
  654.     THEN save_window ;
  655.    create_descriptor ;
  656.    WITH window_list^ DO
  657.     BEGIN
  658.      IF use_frame
  659.       THEN draw_frame(abs_org.x - 1,abs_org.y - 1,abs_org.x + window_size.x,
  660.                       abs_org.y + window_size.y,title,frame_color) ;
  661.      window(abs_org.x,abs_org.y,abs_org.x + window_size.x - 1,
  662.             abs_org.y + window_size.y - 1) ;
  663.      textcolor(fore_color) ;
  664.      textbackground(back_color) ;
  665.      clrscr ;
  666.     END ;
  667.    open_window := window_list ;
  668.   END ; (* open_window *)
  669.  
  670.  
  671.  PROCEDURE display_window(win_ptr : window_ptr) ;
  672.   (* display the window whose descriptor is win_ptr on the screen *)
  673.   (* this routine is called by other routines and shouldn't be called *)
  674.   (* directly. Use use_window instead *)
  675.   VAR
  676.    save_size,save_org : window_pos ;
  677.   BEGIN
  678.    WITH win_ptr^ DO
  679.     BEGIN
  680.      get_window_co_ords(win_ptr,save_org,save_size) ;
  681.      move_to_scrn(save_org,save_size,scrn_area) ;
  682.     END ;
  683.   END ; (* display_window *)
  684.  
  685.  
  686.  PROCEDURE use_window(win_ptr : window_ptr) ;
  687.   (* make win_ptr the current window, display it and restore cursor *)
  688.   (* to its original position. The old window is saved and becomes the *)
  689.   (* second window on the list *)
  690.   BEGIN
  691.    IF win_ptr <> NIL
  692.     THEN
  693.      IF win_ptr <> window_list
  694.       THEN
  695.        BEGIN
  696.         save_window ;
  697.         del_desc(win_ptr) ;
  698.         ins_desc(win_ptr) ;
  699.         display_window(win_ptr) ;
  700.         WITH window_list^ DO
  701.          BEGIN
  702.           window(abs_org.x,abs_org.y,abs_org.x + window_size.x - 1,
  703.                  abs_org.y + window_size.y - 1) ;
  704.           gotoxy(cursor_pos.x,cursor_pos.y) ;
  705.           textcolor(fore_color) ;
  706.           textbackground(back_color) ;
  707.          END ;
  708.        END ;
  709.   END ; (* use_window *)
  710.  
  711.  
  712.  PROCEDURE scrn_refresh ;
  713.   (* Re-draw the entire screen. The screen is assembled in a memory *)
  714.   (* buffer before being moved to physical screen. The screen is assembled *)
  715.   (* from the last window forward. We assemble the screen in memory *)
  716.   (* to prevent the annoying screen blank which occurs when you assemble *)
  717.   (* dirctly in the screen area *)
  718.   (* screen - 4000 byte memory region to assemeble the screen *)
  719.   VAR
  720.    physical_scrn,save_scrn,screen : char_ptr ;
  721.    save_size,save_org : window_pos ;
  722.  
  723.   PROCEDURE scrn_fill(win_ptr : window_ptr) ;
  724.    (* This routine is like move_to_scrn, except it moves the data to *)
  725.    (* the buffer rather than the actual screen *)
  726.    BEGIN
  727.     IF win_ptr <> NIL
  728.      THEN
  729.       BEGIN
  730.        WITH win_ptr^ DO
  731.         BEGIN
  732.          get_window_co_ords(win_ptr,save_org,save_size) ;
  733.          physical_scrn := ptr(seg(screen^),ofs(screen^) +
  734.                              ((save_org.y - 1) * 80 + (save_org.x - 1)) * 2) ;
  735.          save_scrn := scrn_area ;
  736.          FOR i := 1 TO save_size.y DO
  737.           BEGIN
  738.            move(save_scrn^,physical_scrn^,save_size.x * 2) ;
  739.            physical_scrn := ptr(seg(physical_scrn^),ofs(physical_scrn^) + 160) ;
  740.            save_scrn := ptr(seg(save_scrn^),ofs(save_scrn^) + save_size.x * 2 ) ;
  741.           END ;
  742.         END ;
  743.        scrn_fill(win_ptr^.prev_window) ;
  744.       END ;
  745.    END ; (* scrn_fill *)
  746.  
  747.   BEGIN
  748.    getmem(screen,4000) ;
  749.    fillchar(screen^,4000,chr(0)) ;
  750.    scrn_fill(last_window) ;
  751.    save_org.x := 1 ;
  752.    save_org.y := 1 ;
  753.    save_size.x := 80 ;
  754.    save_size.y := 25 ;
  755.    move_to_scrn(save_org,save_size,screen) ;
  756.    freemem(screen,4000) ;
  757.    IF window_list <> NIL
  758.     THEN
  759.      WITH window_list^ DO
  760.       BEGIN
  761.        window(abs_org.x,abs_org.y,abs_org.x + window_size.x - 1,
  762.               abs_org.y + window_size.y - 1) ;
  763.        gotoxy(cursor_pos.x,cursor_pos.y) ;
  764.        textcolor(fore_color) ;
  765.        textbackground(back_color) ;
  766.       END
  767.     ELSE window(1,1,80,25) ;
  768.   END ; (* scrn_refresh *)
  769.  
  770.  
  771.  PROCEDURE close_window(win_ptr : window_ptr) ;
  772.   (* remove the window from the window_list, and then call scrn_refesh *)
  773.   (* update the screen. If win_ptr is the current window, the next window *)
  774.   (* becomes the active window *)
  775.   VAR
  776.    save_org,save_size : window_pos ;
  777.  
  778.   FUNCTION found_window : boolean ;
  779.    VAR
  780.     p : window_ptr ;
  781.     found : boolean ;
  782.    BEGIN
  783.     found := false ;
  784.     p := window_list ;
  785.     WHILE (p <> NIL) AND (NOT found) DO
  786.      BEGIN
  787.       found := (win_ptr = p) ;
  788.       p := p^.next_window ;
  789.      END ;
  790.     found_window := found ;
  791.    END ; (* found_window *)
  792.  
  793.   BEGIN
  794.    IF found_window
  795.     THEN
  796.      BEGIN
  797.       IF win_ptr <> window_list
  798.        THEN save_window ;
  799.       get_window_co_ords(win_ptr,save_org,save_size) ;
  800.       del_desc(win_ptr) ;
  801.       IF win_ptr^.scrn_area <> NIL
  802.        THEN freemem(win_ptr^.scrn_area,2 * save_size.x * save_size.y) ;
  803.       freemem(win_ptr,sizeof(window_desc)) ;
  804.       scrn_refresh ;
  805.         menu;
  806.      END ;
  807.   END ; (* close_window *)
  808.  
  809. (* ///////////////////// Window routines for this program ////////// *)
  810.  
  811.  PROCEDURE wait ;
  812.   (* Display a message at bottom of screen and and wait for user to *)
  813.   (* press a key *)
  814.   VAR
  815.    ch : char ;
  816.    old_window : window_ptr ;
  817.   BEGIN
  818.    old_window := window_list ;
  819.    use_window(message_window) ;
  820.    clrscr ;
  821.    gotoxy(1,2) ;
  822.    window_write('Press any key to continue ') ;
  823.     Read(kbd,ch);
  824.    clrscr ;
  825.    use_window(old_window) ;
  826.   END ; (* wait *)
  827.  
  828.  
  829.  PROCEDURE init_windows ;
  830.   (* Initialize windows for this program *)
  831.   BEGIN
  832.     ClrScr;
  833.    get_monitor_type ;
  834.    IF monitor_kind = mono_monitor
  835.     THEN button_fore := blue
  836.     ELSE button_fore := white ;
  837.    button_back := red ;
  838.    window_list := NIL ;
  839.    message_window := open_window(2,23,78,2,false,'',white,black,white) ;
  840.    main_window := open_window(2,2,78,20,true,'HyperText',white,blue,white) ;
  841.    gotoxy(10,5) ;
  842.    window_writeln('HYPE - Copyright [c] 1987 Knowledge Garden Inc.  ') ;
  843.    window_writeln('                          473A Malden Bridge Rd. ') ;
  844.    window_writeln('                          Nassau, NY 12123') ;
  845.     window_writeln('          Enhanced (Slightly !) and Recompiled by');
  846.     window_writeln('                   Brian Corll - June 1988');
  847.    wait ;
  848.    clrscr ;
  849.   END ; (* init_windows *)
  850.  
  851.  
  852.  PROCEDURE finish_up ;
  853.   (* Clean up screen before leaving *)
  854.   BEGIN
  855.    window(1,1,80,25) ;
  856.    textcolor(white) ;
  857.    textbackground(black) ;
  858.    clrscr ;
  859.   END ; (* finish_up *)
  860.  
  861.  
  862.  PROCEDURE error(msg : string80) ;
  863.   (* Display a message and wait for the user to read it *)
  864.   VAR
  865.    error_window : window_ptr ;
  866.   BEGIN
  867.    error_window := open_window(10,10,60,3,true,'Error',white,red,white) ;
  868.    window_writeln('') ;
  869.    window_write(msg) ;
  870.    wait ;
  871.    close_window(error_window) ;
  872.   END ; (* error *)
  873.  
  874. (* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ *)
  875.  
  876.  FUNCTION got_file : boolean ;
  877.   (* Called from main program block to get the file name typed after *)
  878.   (* the program at the DOS prompt *)
  879.   (* If the file cannot be found, display an error message and quit *)
  880.   VAR
  881.    f_name : string80 ;
  882.   BEGIN
  883.    f_name := paramstr(1) ;
  884.    IF f_name = ''
  885.     THEN
  886.      BEGIN
  887.       error('Missing file name -- Try ''hype filename''') ;
  888.       got_file := false ;
  889.      END
  890.    ELSE IF open(main_file,f_name)
  891.     THEN got_file := true
  892.    ELSE
  893.     BEGIN
  894.      error(concat('Unable to open ',f_name)) ;
  895.      got_file := false ;
  896.     END ;
  897.   END ; (* got_file *)
  898.  
  899.  
  900.  PROCEDURE process_file(title : string80 ; VAR f : text_file ;
  901.                         text_window : window_ptr) ;
  902.   (* The actual hypertext routine *)
  903.   (* Reads file f starting at current line until eof or ..end(title) *)
  904.   (* builds a linked list of line descriptors and displays them one page *)
  905.   (* at a time in text_window *)
  906.   (* first_line - start of list of lines *)
  907.   (* last_line - last line *)
  908.   (* mark_win_org,mark_win_size,mark_fore,mark_back - window parameters *)
  909.   (*              for threaded text display *)
  910.   VAR
  911.    first_line,last_line : line_ptr ;
  912.    mark_win_org,mark_win_size : window_pos ;
  913.    mark_fore,mark_back : color ;
  914.  
  915.   PROCEDURE release_list(list : line_ptr) ;
  916.    (* free memory used by line descriptors and text *)
  917.    VAR
  918.     p : line_ptr ;
  919.    BEGIN
  920.     WHILE list <> NIL DO
  921.      BEGIN
  922.       p := list ;
  923.       list := list^.next_line ;
  924.       freemem(p^.txt,length(p^.txt^) + 1) ;
  925.       freemem(p,sizeof(line_desc)) ;
  926.      END ;
  927.    END ; (* release_list *)
  928.  
  929.   PROCEDURE read_file(VAR f : text_file ; f_title : string80 ;
  930.                       VAR first,last : line_ptr) ;
  931.    (* read file f until eof or ..end(f_title) *)
  932.    (* build linked list of text lines *)
  933.    (* lines beginning with .. are processed separately, only lines *)
  934.    (* pertaining to concept f_title are processed *)
  935.    (* first,last point to the start and end of the line list *)
  936.    (* We only allocate enough storage for the actual characters in the line, *)
  937.    (* not all 255 characters *)
  938.    VAR
  939.     line : string255 ;
  940.     p : line_ptr ;
  941.     done : boolean ;
  942.  
  943.    PROCEDURE insert_line(lne : line_ptr) ;
  944.     (* insert a line at the end of the line list *)
  945.     BEGIN
  946.      lne^.next_line := NIL ;
  947.      lne^.prev_line := last ;
  948.      IF last = NIL
  949.       THEN first := lne
  950.       ELSE last^.next_line := lne ;
  951.      last := lne ;
  952.     END ; (* insert_line *)
  953.  
  954.    PROCEDURE process_dots ;
  955.     (* process lines beginning with dots *)
  956.  
  957.     PROCEDURE process_end ;
  958.      (* process ..end *)
  959.      (* if ..end(f_title) then we are done with this concept *)
  960.      BEGIN
  961.       delete(line,1,4) ;
  962.       strip_leading_blanks(line) ;
  963.       IF copy(line,1,length(f_title)) = f_title
  964.        THEN done := true ;
  965.      END ; (* process_end *)
  966.  
  967.     PROCEDURE process_window ;
  968.      (* process ..window(f_title) - sets window parameteres for this concept *)
  969.      (* syntax is ..window(f_title) fore_color,back_color,org_x,org_y, *)
  970.      (*                            size_x,size_y   *)
  971.  
  972.      FUNCTION read_num(def : integer) : integer ;
  973.       (* read next number from line *)
  974.       VAR
  975.        comma_pos : byte ;
  976.        num : string80 ;
  977.  
  978.       FUNCTION get_num(num_str : string80) : integer ;
  979.        VAR
  980.         finished : boolean ;
  981.         n : string80 ;
  982.        BEGIN
  983.         n := '' ;
  984.         finished := false ;
  985.         WHILE NOT finished DO
  986.          IF num_str = ''
  987.           THEN finished := true
  988.          ELSE IF num_str[1] IN ['0' .. '9']
  989.           THEN
  990.            BEGIN
  991.             n := concat(n,num_str[1]) ;
  992.             delete(num_str,1,1) ;
  993.            END
  994.          ELSE finished := true ;
  995.         get_num := tointeger(n) ;
  996.        END ; (* get_num *)
  997.  
  998.       BEGIN
  999.        comma_pos := pos(',',line) ;
  1000.        IF comma_pos > 0
  1001.         THEN
  1002.          BEGIN
  1003.           num := copy(line,1,comma_pos - 1) ;
  1004.           delete(line,1,comma_pos) ;
  1005.          END
  1006.         ELSE
  1007.          BEGIN
  1008.           num := line ;
  1009.           line := '' ;
  1010.          END ;
  1011.        strip_leading_blanks(num) ;
  1012.        IF num = ''
  1013.         THEN read_num := def
  1014.         ELSE read_num := get_num(num) ;
  1015.       END ; (* read_num *)
  1016.  
  1017.      BEGIN
  1018.       delete(line,1,7) ;
  1019.       strip_leading_blanks(line) ;
  1020.       IF copy(line,1,length(f_title)) = f_title
  1021.        THEN
  1022.         BEGIN
  1023.          delete(line,1,length(f_title)) ;
  1024.          strip_leading_blanks(line) ;
  1025.          delete(line,1,1) ;
  1026.          mark_fore := abs(read_num(def_fore_color)) MOD 16 ;
  1027.          mark_back := abs(read_num(def_back_color)) MOD 16 ;
  1028.          mark_win_org.x := max(min(read_num(mark_win_org.x),80),1) ;
  1029.          mark_win_org.y := max(min(read_num(mark_win_org.y),25),1) ;
  1030.          mark_win_size.x := max(min(read_num(mark_win_size.x),80),1) ;
  1031.          mark_win_size.y := max(min(read_num(mark_win_size.y),25),1) ;
  1032.         END ;
  1033.      END ; (* process_window *)
  1034.  
  1035.     PROCEDURE process_new_file ;
  1036.      (* process ..file(f_title) file_name *)
  1037.      (* read a list of lines from file_name and attach them to the end *)
  1038.      (* of the current list *)
  1039.      VAR
  1040.       new_file : text_file ;
  1041.       new_file_name : string80 ;
  1042.  
  1043.      PROCEDURE read_new_file ;
  1044.       VAR
  1045.        new_start,new_last : line_ptr ;
  1046.       BEGIN
  1047.        read_file(new_file,f_title,new_start,new_last) ;
  1048.        IF new_start <> NIL
  1049.         THEN
  1050.          BEGIN
  1051.           new_start^.prev_line := last ;
  1052.           IF last = NIL
  1053.            THEN first := new_start
  1054.            ELSE last^.next_line := new_start ;
  1055.           last := new_last ;
  1056.          END ;
  1057.        close(new_file) ;
  1058.       END ; (* read_new_file *)
  1059.  
  1060.      BEGIN
  1061.       delete(line,1,5) ;
  1062.       strip_leading_blanks(line) ;
  1063.       IF copy(line,1,length(f_title)) = f_title
  1064.        THEN
  1065.         BEGIN
  1066.          delete(line,1,length(f_title)) ;
  1067.          strip_leading_blanks(line) ;
  1068.          delete(line,1,1) ;
  1069.          strip_leading_blanks(line) ;
  1070.          new_file_name := line ;
  1071.          IF open(new_file,new_file_name)
  1072.           THEN read_new_file
  1073.           ELSE error(concat(new_file_name,' can not be read.')) ;
  1074.         END ;
  1075.      END ; (* process_new_file *)
  1076.  
  1077.     BEGIN
  1078.      line := toupper(copy(line,3,255)) ;
  1079.      strip_trailing_blanks(line) ;
  1080.      IF copy(line,1,4) = 'END('
  1081.       THEN process_end
  1082.      ELSE IF copy(line,1,7) = 'WINDOW('
  1083.       THEN process_window
  1084.      ELSE IF copy(line,1,5) = 'FILE('
  1085.       THEN process_new_file ;
  1086.     END ; (* process_dots *)
  1087.  
  1088.    BEGIN
  1089.     f_title := toupper(f_title) ;
  1090.     first := NIL ;
  1091.     last := NIL ;
  1092.     done := false ;
  1093.     WHILE (NOT eof(f)) AND (NOT done) DO
  1094.      BEGIN
  1095.       readln(f,line) ;
  1096.       IF copy(line,1,2) = '..'
  1097.        THEN process_dots
  1098.        ELSE
  1099.         BEGIN
  1100.          getmem(p,sizeof(line_desc)) ;
  1101.          getmem(p^.txt,length(line) + 1) ;
  1102.          p^.txt^ := line ;
  1103.          insert_line(p) ;
  1104.         END ;
  1105.      END ;
  1106.    END ; (* read_file *)
  1107.  
  1108.   PROCEDURE display_list(first,last : line_ptr ; disp_window : window_ptr) ;
  1109.    (* display the list pointed to by first in disp_window *)
  1110.    (* read keyboard until F10 or Esc is pressed *)
  1111.    (* left and right arrows move among marked text, Enter selects text *)
  1112.    (* for display *)
  1113.    (* Text is displayed one page at a time - PgUp and PgDn page *)
  1114.    (* mark_list is a linked list of highlighted text on the current page *)
  1115.    (*           of the disp_window *)
  1116.    (* mark is the current mark, i.e. the one with the button color *)
  1117.    (* top_of_page points to first line on the page *)
  1118.    VAR
  1119.     done : boolean ;
  1120.     top_of_page : line_ptr ;
  1121.     mark,mark_list,last_mark : mark_ptr ;
  1122.  
  1123.  
  1124.    PROCEDURE move_to_mark(m_ptr : mark_ptr) ;
  1125.     (* move to the highlighted region of screen pointed to by m_ptr *)
  1126.     (* redisplay text in button colors so that user can see where we are *)
  1127.     VAR
  1128.      p : mark_ptr ;
  1129.  
  1130.     PROCEDURE remove_old_mark ;
  1131.      (* return previous marked text to reverse video *)
  1132.      BEGIN
  1133.       gotoxy(mark^.mark_pos.x,mark^.mark_pos.y) ;
  1134.       window_reverse ;
  1135.       window_write(mark^.mark_text^) ;
  1136.       window_normal ;
  1137.      END ; (* remove_old_mark *)
  1138.  
  1139.     BEGIN
  1140.      IF m_ptr <> NIL
  1141.       THEN
  1142.        BEGIN
  1143.         IF mark <> NIL
  1144.          THEN remove_old_mark ;
  1145.         p := mark_list ;
  1146.         WHILE (p <> NIL) AND (p <> m_ptr) DO
  1147.          p := p^.next_mark ;
  1148.         IF p <> NIL
  1149.          THEN
  1150.           BEGIN
  1151.            mark := p ;
  1152.            gotoxy(mark^.mark_pos.x,mark^.mark_pos.y) ;
  1153.            textcolor(button_fore) ;
  1154.            textbackground(button_back) ;
  1155.            window_write(mark^.mark_text^) ;
  1156.            window_normal ;
  1157.            gotoxy(mark^.mark_pos.x,mark^.mark_pos.y) ;
  1158.           END ;
  1159.        END ;
  1160.     END ; (* move_to_mark *)
  1161.  
  1162.    PROCEDURE display_page ;
  1163.     (* display a page of text in disp_window *)
  1164.     (* marked text is displayed inreverse video *)
  1165.     (* move mark to first item on mark list *)
  1166.     VAR
  1167.      line_cnt : counter ;
  1168.      p : line_ptr ;
  1169.  
  1170.     PROCEDURE release_marks ;
  1171.      (* release the old mark list - the mark list is rebuilt each *)
  1172.      (* time a page is displayed *)
  1173.      VAR
  1174.       m_ptr : mark_ptr ;
  1175.      BEGIN
  1176.       WHILE mark_list <> NIL DO
  1177.        BEGIN
  1178.         m_ptr := mark_list ;
  1179.         mark_list := mark_list^.next_mark ;
  1180.         freemem(m_ptr^.mark_text,length(m_ptr^.mark_text^) + 1) ;
  1181.         freemem(m_ptr,sizeof(mark_desc)) ;
  1182.        END ;
  1183.       mark := NIL ;
  1184.       last_mark := NIL ;
  1185.      END ; (* release_marks *)
  1186.  
  1187.     PROCEDURE write_the_line(s : string255) ;
  1188.      (* write the line on the screen *)
  1189.      (* if text is marked add it to list and display inreverse video *)
  1190.      VAR
  1191.       mark_loc : byte ;
  1192.  
  1193.      PROCEDURE add_mark ;
  1194.       (* add this text to list and save its co-ordinates *)
  1195.       VAR
  1196.        m_ptr : mark_ptr ;
  1197.        ps : integer ;
  1198.       BEGIN
  1199.        getmem(m_ptr,sizeof(mark_desc)) ;
  1200.        m_ptr^.mark_pos.x := wherex ;
  1201.        m_ptr^.mark_pos.y := wherey ;
  1202.        delete(s,1,1) ;
  1203.        ps := pred(pos(mark_char,s)) ;
  1204.        IF ps < 0
  1205.         THEN ps := length(s) ;
  1206.        getmem(m_ptr^.mark_text,ps + 1) ;
  1207.        m_ptr^.mark_text^ := copy(s,1,ps) ;
  1208.        window_reverse ;
  1209.        window_write(m_ptr^.mark_text^) ;
  1210.        window_normal ;
  1211.        delete(s,1,succ(ps)) ;
  1212.        m_ptr^.next_mark := NIL ;
  1213.        m_ptr^.prev_mark := last_mark ;
  1214.        IF last_mark = NIL
  1215.         THEN mark_list := m_ptr
  1216.         ELSE last_mark^.next_mark := m_ptr ;
  1217.        last_mark := m_ptr ;
  1218.       END ; (* add_mark *)
  1219.  
  1220.      BEGIN
  1221.       IF s <> ''
  1222.        THEN
  1223.         BEGIN
  1224.          mark_loc := pos(mark_char,s) ;
  1225.          IF mark_loc > 0
  1226.           THEN
  1227.            BEGIN
  1228.             window_write(copy(s,1,pred(mark_loc))) ;
  1229.             delete(s,1,pred(mark_loc)) ;
  1230.             add_mark ;
  1231.             write_the_line(s) ;
  1232.            END
  1233.          ELSE window_write(s) ;
  1234.         END ;
  1235.      END ; (* write_the_line *)
  1236.  
  1237.     BEGIN
  1238.      release_marks ;
  1239.      clrscr ;
  1240.      p := top_of_page ;
  1241.      line_cnt := 1 ;
  1242.      WHILE (p <> NIL) AND (line_cnt <= disp_window^.window_size.y) DO
  1243.       BEGIN
  1244.        gotoxy(1,line_cnt) ;
  1245.        IF copy(p^.txt^,1,2) <> '..'
  1246.         THEN
  1247.          BEGIN
  1248.           write_the_line(p^.txt^) ;
  1249.           line_cnt := succ(line_cnt) ;
  1250.          END ;
  1251.        p := p^.next_line ;
  1252.       END ;
  1253.      move_to_mark(mark_list)
  1254.     END ; (* display_page *)
  1255.  
  1256.    PROCEDURE handle_keys ;
  1257.     (* read the keyboard - ignore everything but keys displayed on bottom *)
  1258.     (* of screen *)
  1259.     VAR
  1260.      ch : char ;
  1261.  
  1262.     PROCEDURE exit_prog ;
  1263.      (* F10 - pressed erase screen and quit *)
  1264.      BEGIN
  1265.       finish_up ;
  1266.       halt(0) ;
  1267.      END ; (* exit_prog *)
  1268.  
  1269.     PROCEDURE page_forward ;
  1270.      (* display previous page *)
  1271.      (* count backwards until we get to it *)
  1272.      VAR
  1273.       p : line_ptr ;
  1274.       line_cnt : counter ;
  1275.      BEGIN
  1276.       p := top_of_page ;
  1277.       line_cnt := 1 ;
  1278.       WHILE (p <> NIL) AND (line_cnt < disp_window^.window_size.y) DO
  1279.        BEGIN
  1280.         p := p^.next_line ;
  1281.         line_cnt := succ(line_cnt) ;
  1282.        END ;
  1283.       IF p <> NIL
  1284.        THEN
  1285.         IF p^.next_line <> NIL
  1286.          THEN
  1287.           BEGIN
  1288.            top_of_page := p^.next_line ;
  1289.            display_page ;
  1290.           END ;
  1291.      END ; (* page_forward *)
  1292.  
  1293.     PROCEDURE page_back ;
  1294.      (* display next page *)
  1295.      (* count forwards until we get to it *)
  1296.      VAR
  1297.       p : line_ptr ;
  1298.       line_cnt : counter ;
  1299.      BEGIN
  1300.       p := top_of_page ;
  1301.       line_cnt := disp_window^.window_size.y ;
  1302.       WHILE (p <> NIL) AND (line_cnt >= 1) do
  1303.        BEGIN
  1304.         p := p^.prev_line ;
  1305.         line_cnt := pred(line_cnt) ;
  1306.        END ;
  1307.       IF p <> NIL
  1308.        THEN
  1309.         BEGIN
  1310.          top_of_page := p ;
  1311.          display_page ;
  1312.         END ;
  1313.      END ; (* page_back *)
  1314.  
  1315.     PROCEDURE move_to_next_mark ;
  1316.      (* move to next mark on screen, if at end go back to first *)
  1317.      BEGIN
  1318.       IF mark_list <> NIL
  1319.        THEN
  1320.         BEGIN
  1321.          IF mark^.next_mark <> NIL
  1322.           THEN move_to_mark(mark^.next_mark)
  1323.           ELSE move_to_mark(mark_list) ;
  1324.         END ;
  1325.      END ; (* move_to_next_mark *)
  1326.  
  1327.     PROCEDURE move_to_prev_mark ;
  1328.      (* move to prev mark on screen, if at first go to end *)
  1329.      BEGIN
  1330.       IF mark_list <> NIL
  1331.        THEN
  1332.         BEGIN
  1333.          IF mark^.prev_mark <> NIL
  1334.           THEN move_to_mark(mark^.prev_mark)
  1335.           ELSE move_to_mark(last_mark) ;
  1336.         END ;
  1337.      END ; (* move_to_prev_mark *)
  1338.  
  1339.     PROCEDURE process_mark ;
  1340.      (* process the text under the button *)
  1341.      (* find its lable in the file, open a window and display it *)
  1342.      VAR
  1343.       mark_start,mark_end : line_ptr ;
  1344.       mark_window : window_ptr ;
  1345.  
  1346.      FUNCTION found_mark : boolean ;
  1347.       VAR
  1348.        found : boolean ;
  1349.        mark_str,line : string255 ;
  1350.       BEGIN
  1351.        mark_str := toupper(mark^.mark_text^) ;
  1352.        found := false ;
  1353.        reset(f) ;
  1354.        WHILE (NOT eof(f)) AND (NOT found) DO
  1355.         BEGIN
  1356.          readln(f,line) ;
  1357.          found := (toupper(copy(line,3,255)) = mark_str) ;
  1358.         END ;
  1359.        found_mark := found ;
  1360.       END ; (* found_mark *)
  1361.  
  1362.      PROCEDURE set_window_parameters ;
  1363.       (* set default window paramters *)
  1364.       BEGIN
  1365.        mark_win_org.x := (disp_window^.abs_org.x + 2) MOD 8 ;
  1366.        mark_win_org.y := (disp_window^.abs_org.y + 2) MOD 8 ;
  1367.        mark_win_size.x := def_window_size_x ;
  1368.        mark_win_size.y := def_window_size_y ;
  1369.        mark_fore := def_fore_color ;
  1370.        mark_back := def_back_color ;
  1371.       END ; (* set_window_parameters *)
  1372.  
  1373.      BEGIN
  1374.       IF mark_list <> NIL
  1375.        THEN
  1376.         IF found_mark
  1377.          THEN
  1378.           BEGIN
  1379.            set_window_parameters ;
  1380.            read_file(f,mark^.mark_text^,mark_start,mark_end) ;
  1381.            mark_window := open_window(mark_win_org.x,mark_win_org.y,
  1382.                                       mark_win_size.x,mark_win_size.y,
  1383.                                       true,mark^.mark_text^,mark_fore,
  1384.                                       mark_back,mark_fore) ;
  1385.            display_list(mark_start,mark_end,mark_window) ;
  1386.            close_window(mark_window) ;
  1387.            use_window(disp_window) ;
  1388.            release_list(mark_start) ;
  1389.           END
  1390.          ELSE
  1391.           BEGIN
  1392.            error(concat('''',mark^.mark_text^,''' could not be found.')) ;
  1393.               menu;
  1394.           END ;
  1395.      END ; (* process_mark *)
  1396.  
  1397.     BEGIN
  1398.       Read(kbd,ch);
  1399.      IF ch = enter
  1400.       THEN process_mark
  1401.      ELSE IF ch = esc
  1402.       THEN
  1403.        IF keypressed
  1404.         THEN
  1405.          BEGIN
  1406.           read(kbd,ch) ;
  1407.           CASE ch OF
  1408.            down_arrow : move_to_next_mark ;
  1409.            up_arrow  : move_to_prev_mark ;
  1410.            PgUp        : page_back ;
  1411.            PgDn        : page_forward ;
  1412.            F10         : exit_prog ;
  1413.           END ;
  1414.          END
  1415.         ELSE done := true ;
  1416.     END ; (* handle_keys *)
  1417.  
  1418.    BEGIN
  1419.     done := false ;
  1420.      menu;
  1421.     mark := NIL ;
  1422.     mark_list := NIL ;
  1423.     last_mark := NIL ;
  1424.     top_of_page := first ;
  1425.     display_page ;
  1426.     WHILE NOT done DO
  1427.      handle_keys ;
  1428.    END ; (* display_list *)
  1429.  
  1430.   BEGIN
  1431.     SetTextBuf(f,Buffer);
  1432.    reset(f) ;
  1433.    read_file(f,title,first_line,last_line) ;
  1434.    display_list(first_line,last_line,text_window) ;
  1435.    release_list(first_line) ;
  1436.   END ; (* process_file *)
  1437.  
  1438.  
  1439.  BEGIN
  1440.   init_windows ;
  1441.   IF got_file
  1442.    THEN
  1443.     BEGIN
  1444.      process_file('MAIN',main_file,main_window) ;
  1445.      close(main_file) ;
  1446.     END ;
  1447.   finish_up ;
  1448.  END.
  1449.