############################################################################ # # Name: 1.26 # # Title: bibleref list display utilities # # Author: Richard L. Goerwitz # # Version: listutil.icn # ############################################################################ # # Contains: # # display_list(), which displays all passages in a given bitmap # list by calling listlist(), which converts the bitmaps to # human-readable format, and writes them to the screen # # listlist(), on which see above, # # writelist(), which writes a bitmap list in human readable form # to a file. # ############################################################################ # # Links: uses push_shell(), which is in ./passutil.icn # ############################################################################ # Declared in bibleref.icn. l is the list itself, pos is the viewer's # last position in the list. old_n is the last passage in the list that # was viewed. s is a brief indication of the search string used to gen- # erate the list. # record lst(l,pos,old_n,s,long) global _subscripts # get around unreachable coexpression bug procedure display_list() # # Display a retrieved "hit list." # local passage, subscr # # We're in the midst of a scanning expression in the main # command loop. If the "d" command was given with no arguments, # then default to the last list retrieved. # if pos(0) then { subscr := (0 ~= *lists) | { err_message("No lists have been created yet.") fail } } # # If we are not at pos(0) then we are to go to a specific list # in the list history. # else { if not { tab(upto('-+' ++ &digits)) & subscr := integer(tab(many('-+' ++ &digits))) & pos(0) } # If we don't have an integer, then the user has screwed # up somehow. then { err_message("Garbage characters after \"d\" command.") fail } # Check to be sure the subscript give is in range. lists[subscr] | { err_message("There is no list number "||subscr||".") fail } } # # While listlist gives us a valid passage reference, display it. # When it returns &null or fails, quit. # repeat { # Display the list (defaults above to the last list in the list # history). Use listlist(). Arg 1 is the lst record, which # contains a list and a position to start viewing that list at. # The second arg to listlist tells the user the length of the # list. The last arg specifies how many display columns we can # eat up before truncating. passage := listlist( lists[subscr], "length = "||*lists[subscr].l||"; "|| "search string = "||lists[subscr].s, getval("co")-10 ) | fail if /passage then return else display_passage(passage) } end procedure listlist(lst_rec, msg, width) local l, i, ss, j, n, ref, prompt, rsp, rsp2 static lines initial lines := getval("li")-3 # "l" is a record of type lst, containing the list to print # "msg" is the message to put on the status line # "width" gives the length to which lines in l are truncated # "offset" gives the starting offset in l at which to begin # printing (default 0). l := lst_rec.l repeat { i := 0 while *l >= (ss := lst_rec.pos + (lines > (i +:= 1))) do { iputs(igoto(getval("cm"), 1, i)) normal(); iputs(getval("ce")) writes(" ", left(ss||".",7)) ref := convertb(l[ss], kjv_filename) if \lst_rec.long then ref ||:= ": " || bitmap_2_text(l[ss], kjv_filename) if ss = \lst_rec.old_n then { # Boldface last viewed passage from this list. boldface(); writes(ref[1:\width|0]) normal() } else writes(ref[1:\width|0]) } # If we haven't reached the end of the displayable screen, # then clear lines until we reach it... if i-1 ~= lines+1 then { every j := i to lines do { iputs(igoto(getval("cm"), 1, j)) normal(); iputs(getval("ce")) } } # Display message on status line. status_line("-- " || \msg || " --" | "", "", "c") # If we're to the end of the screen, and there's still more to # display, then... if *l > lines-1 & (lst_rec.pos+i) ~= (*l+1) then { rsp := snarf_input("Press !/a/b/c/m/n/p/s/v/w (q = quit viewing): ") case map(rsp) of { "" : lst_rec.pos := (*l > (lst_rec.pos+1)) "!" : push_shell() "/" : lst_rec.pos := search_list(lst_rec,rsp,lines) "?" : lst_rec.pos := search_list(lst_rec,rsp,lines) "a" : write_list(l, "append") "b" : lst_rec.pos := (0 < lst_rec.pos-lines+1) | 0 "c" : next "l" : lst_rec.long := { if /lst_rec.long then 1 else &null } "m" : lst_rec.pos := (*l > (lst_rec.pos+i-1)) "n" : { n := \lst_rec.old_n+1 | { err_message("Type \"v\" to set the current passage.") next } if 0 < n <= *l then { if not (lst_rec.pos < n < lst_rec.pos+lines) then lst_rec.pos := (0 < n-2) | n-1 lst_rec.old_n := n & (return l[n]) } else err_message("Out of range.") } "p" : { n := \lst_rec.old_n-1 | { err_message("Type \"v\" to set the current passage.") next } if 0 < n <= *l then { if not (lst_rec.pos < n < lst_rec.pos+lines) then lst_rec.pos := (0 < n-2) | n-1 lst_rec.old_n := n & (return l[n]) } else err_message("Out of range.") } "q" : break "s" : { l := (lst_rec.l := sort(lst_rec.l)) lst_rec.old_n := &null } "v" : { rsp2 := snarf_input("View which passage: ") rsp2 == "q" & next rsp2 == "" & rsp2 := lst_rec.old_n if n := integer(rsp2) then { if 0 < n <= *l then { if not (lst_rec.pos < n < lst_rec.pos+lines) then lst_rec.pos := (0 < n-2) | n-1 lst_rec.old_n := n return l[n] } else err_message("Out of range.") } else err_message("Invalid passage number.") } "w" : write_list(l) default : { if rsp ? { lst_rec.pos := search_list(lst_rec,tab(any('?/')),lines) } then next else { if n := integer(rsp)-1 then { if 0 <= n < *l then lst_rec.pos := n else err_message("Out of range.") } else { err_message("Invalid command.") } } } } } # ...otherwise, we've displayed everything. else { prompt := "c/n/p/s/v/w (q to go to previous menu): " if *l <= lines then rsp:= snarf_input("Press !/a/" || prompt) | next else rsp := snarf_input("Press !/a/b/" || prompt) | next case map(rsp) of { "!" : push_shell() "/" : lst_rec.pos := search_list(lst_rec,rsp,lines) "?" : lst_rec.pos := search_list(lst_rec,rsp,lines) "a" : write_list(l,"append") "b" : lst_rec.pos := (0 < lst_rec.pos-lines+1) | 0 "c"|"" : next "l" : lst_rec.long := { if /lst_rec.long then 1 else &null } "m" : next # no more to display; just stay were we are "n" : { n := \lst_rec.old_n+1 | { err_message("No passage viewed yet from this list.") next } if 0 < n <= *l then { if not (lst_rec.pos < n < lst_rec.pos+lines) then lst_rec.pos := (0 < n-2) | n-1 lst_rec.old_n := n & (return l[n]) } else err_message("Out of range.") } "p" : { n := \lst_rec.old_n-1 | { err_message("No passage viewed yet from this list.") next } if 0 < n <= *l then { if not (lst_rec.pos < n < lst_rec.pos+lines) then lst_rec.pos := (0 < n-2) | n-1 lst_rec.old_n := n & (return l[n]) } else err_message("Out of range.") } "q" : break "s" : { l := (lst_rec.l := sort(lst_rec.l)) lst_rec.old_n := &null } "v" : { rsp2 := snarf_input("View which passage: ") rsp2 == "q" & next rsp2 == "" & rsp2 := lst_rec.old_n if n := integer(rsp2) then { if 0 < n <= *l then { if not (lst_rec.pos < n < lst_rec.pos+lines) then lst_rec.pos := (0 < n-2) | n-1 lst_rec.old_n := n return l[n] } else err_message("Out of range.") } else err_message("Invalid passage number.") } "w" : write_list(l) default : { if rsp ? { lst_rec.pos := search_list(lst_rec,tab(any('?/')),lines) } then next else { if n := integer(rsp)-1 then { if 0 <= n < *l then lst_rec.pos := n else err_message("Out of range.") } else { err_message("Invalid command.") } } } } } } return end procedure write_list(l, switch) # # Used for writing the contents of a displayed list to a file. # local fname, outtext, i, firstchar # Get straight whether we are appending or clobbering. (\switch := "a") | (switch := "w") until \outtext do { fname := snarf_input("Enter filename (! for shell; q to quit): ") fname ? { firstchar := move(1) | "" case firstchar of { "" : fail "!" : push_shell() & initialize_screen() "q" : pos(0) & fail default : { outtext := open(fname, switch) | { case switch of { "a" : err_message("Can't append to "|| fname ||".") "w" : err_message("Can't write to "|| fname ||".") default : quit("write_passage","internal error",80) } } } } } } message("Writing...") every i := 1 to *l do write(outtext, " ", left(i||".",7), convertb(l[i], kjv_filename)) close(outtext) message("Done.") return end procedure show_lists() # # Display stats for all generated lists. So far the only routine # that can generate lists is search_database(). Every time it # generates a list, it inserts that list into the global list, # lists (declared and initialized in bibleref.icn). # local expanded_list, i, result # # We're in the midst of a scanning expression in the main # command loop. If the "l" command was given with no arguments, # then default to the last list retrieved. # if not pos(0) then { err_message("Garbage characters after \"l\" command.") fail } *lists = 0 & { err_message("No lists in memory.") fail } repeat { result := listlistS(lists, "list of viewable lists", getval("co")-10) | fail if /result then return else result ? display_list() } end procedure listlistS(all_lists, msg, width) local l, offset, i, ss, j, prompt, rsp, rsp2 static lines, last_viewed initial lines := getval("li")-3 # "all_lists" is a list of records of type lst # "msg" is the message to put on the status line # "width" gives the length to which lines in l are truncated l := all_lists offset := 0 repeat { i := 0 while *l >= (ss := offset + (lines > (i +:= 1))) do { iputs(igoto(getval("cm"), 1, i)) normal(); iputs(getval("ce")) writes(" ", left(ss||".",5)) writes("length = " || *l[i].l || ", search pattern = " || l[i].s) } # If we haven't reached the end of the displayable screen, # then clear lines until we reach it... if i-1 ~= lines+1 then { every j := i to lines do { iputs(igoto(getval("cm"), 1, j)) normal(); iputs(getval("ce")) } } # Display message on status line. status_line("-- " || \msg || " --" | "", "", "c") # If we're to the end of the screen, and there's still more to # display, then... if *l > lines-1 & (offset+i) ~= (*l+1) then { rsp := snarf_input("Press !/b/c/m/v (q = quit viewing): ") case map(rsp) of { "" : offset := (*l > (offset+1)) "!" : push_shell() "b" : offset := (0 < offset-lines+1) | 0 "c" : next "m" : (offset := (*l > (offset+i-1))) "q" : break "v" : { rsp2 := snarf_input("View which list: ") rsp2 == "q" & next rsp2 == "" & rsp2 := \last_viewed if last_viewed := integer(rsp2) then { if 0 < last_viewed <= *l then return last_viewed else err_message("Out of range.") } else err_message("Invalid list number.") } default : { err_message("Command invalid in this context.") } } } # ...otherwise, we've displayed everything. else { prompt := "c/v (q to go to previous menu): " if *l <= lines then rsp:= snarf_input("Press !/" || prompt) | next else rsp := snarf_input("Press !/b/" || prompt) | next case map(rsp) of { "!" : push_shell() "b" : offset := (0 < offset-lines+1) | 0 "c"|"" : next "m" : next # no more to display; just stay were we are "q" : break "v" : { rsp2 := snarf_input("View which list: ") rsp2 == "q" & next rsp2 == "" & rsp2 := \last_viewed if last_viewed := integer(rsp2) then { if 0 < last_viewed <= *l then return last_viewed else err_message("Out of range.") } else err_message("Invalid list number.") } default : { err_message("Command invalid in this context.") } } } } return end procedure search_list(lst_rec, direction, lines) local resp, subscript, ltrs, last_ltrs # global _subscripts direction == ("?"|"/") | fail if pos(0) then { resp := snarf_input("Enter pattern (q to abort): ") resp == (""|"q") & fail } else resp := tab(0) if direction == "/" then _subscripts := create { lst_rec.pos+2 to *lst_rec.l } else _subscripts := create { lst_rec.pos to 1 by -1 } message("Searching...") # A probably futile attempt at optimization.... # last_ltrs := { # if resp ? (tab(any(&digits)) | &null, tab(many(&letters)), pos(0)) # then "" else &null # } while subscript := @_subscripts do { # See above on futile attempts.... # if \last_ltrs then { # convertb(lst_rec.l[subscript],kjv_filename) ? # ltrs := (tab(any(&digits)) | "") || tab(many(&letters)) # (last_ltrs ~==:= ltrs) | next # } if findre(resp, convertb(lst_rec.l[subscript],kjv_filename)) then { message("Done.") return subscript-1 } } err_message("Pattern not found.") return lst_rec.pos end