home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 September / Simtel20_Sept92.cdr / msdos / telix / tlx_hype.arc / HYPE.PAS < prev    next >
Pascal/Delphi Source File  |  1987-05-27  |  44KB  |  1,439 lines

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