sub fancy_selection( long dir_selection, string new_dir( ) ) ! Written by Michael W. Wheeler (mww@tntech.bitnet) ! VAX Basic V3.1 ! Copyright (c) by Michael W. Wheeler, September 1987 ! This program is intended for Public Domain, and may not be sold or ! marketed in any form without the permision and written consent ! from the author Michael W. Wheeler. I retain all copyrights to ! this program, in either the original or modified forms, and no ! violation, deletion, or change of the copyright notice is ! allowed. Futhermore, I will have no liability or responsibilty ! to any user with respect to loss or damage caused directly or ! indirectly by this program. ! Labels ! Forever: ! Constants %include "$rmsdef" %from %library "sys$library:basic$starlet.tlb" %include "$smgtrmptr" %from %library "sys$library:basic$starlet.tlb" external word constant io$_readvblk, io$m_noecho, io$m_nofiltr, & io$m_escape declare word constant num_field = 3%, & max_recurse = 8%, & max_jump = 9%, & maxdirs = 2000% ! Types record iosblk word io_status, xfr_len word terminator, terminator_len end record record terminatorblk long mask_size long mask_location end record ! Variables declare real x, y declare long sys_status, cur_row, cur_col, col_diff, & terminator_mask(4%), i, page_number, & recurse_count, recurse_save(max_recurse,4%), & context, page_size, jump_count, & jump_save(max_jump,4%), dot, num_rows declare word io_function, logical_col, end_col, text_len declare byte recurse_flag declare string r_arrow, ctrl_f, h_tab, space, l_arrow, ctrl_b, & ctrl_d, backspace, up_arrow, ctrl_p, dn_arrow, & ctrl_n, ctrl_l, ctrl_w, ctrl_e, ctrl_a, ctrl_m, & do_key, ctrl_v, next_scrn, prev_scrn, & esc_low_v, esc_cap_v, ctrl_z, ctrl_r, ctrl_u, & help, q_mark, delete_key, recurse_dir, & search_dir, dir_file, ctrl_j, cur_dir, & jump_dir(max_jump), start_dir, parent_dir declare iosblk iosb declare terminatorblk terminators common (io_l) long term_table, col_len, num_cols, tt_cols, tt_rows common (io_w) word chan, max_dir, cursor_len, start_row, start_col common (txt) string text = 5 ! Procedures external sub read_dirs( string, string dim ( ) ) external sub display_dirs( long, string dim ( ) ) ! Functions external long function sys$qiow, lib$find_file external word function find_last by desc ( string, string ) ! Initialize these variables (used as constants). r_arrow = esc + "[C" ctrl_f = chr$(6%) h_tab = chr$(9%) space = " " l_arrow = esc + "[D" ctrl_b = chr$(2%) ctrl_d = chr$(4%) backspace = chr$(8%) delete_key = chr$(127%) up_arrow = esc + "[A" ctrl_p = chr$(16%) dn_arrow = esc + "[B" ctrl_n = chr$(14%) ctrl_l = chr$(12%) ctrl_w = chr$(23%) ctrl_e = chr$(5%) ctrl_a = chr$(1%) ctrl_m = chr$(13%) do_key = esc + "[29~" ctrl_v = chr$(22%) next_scrn = esc + "[6~" prev_scrn = esc + "[5~" esc_low_v = esc + "v" esc_cap_v = esc + "V" ctrl_z = chr$(26%) ctrl_r = chr$(18%) ctrl_u = chr$(21%) help = esc + "[28~" q_mark = chr$(63%) ctrl_j = chr$(10%) terminator_mask(1%) = X"FFFFFFFF"L ! All control keys (31-0) terminator_mask(2%) = X"FC00FFFF"L ! Everything but 0-9 (63-32) terminator_mask(3%) = X"FFFFFFFF"L ! Everything im ascii range (95-64) terminator_mask(4%) = X"FFFFFFFF"L ! EVeryghing in ascii range (127-96) terminators::mask_size = 16% terminators::mask_location = loc(terminator_mask(1%)) start_dir = mid(new_dir(dir_selection), 1%, find_last(".", new_dir(dir_selection)) - 1%) + "]" page_size = (tt_rows - (start_row - 1%)) * num_cols ! Initialize variables. recurse_count = 0% page_number = 0% cur_row = start_row cur_col = start_col + cursor_len + num_field dir_selection = 1% ! Set up ^C trapping. when error use error_trap y% = ctrlc call display_dirs( page_number, new_dir( ) ) call set_cursor_abs( cur_row, cur_col, term_table, chan ) io_function = (io$_readvblk or io$m_noecho or io$m_escape or io$m_nofiltr) forever: sys_status = sys$qiow(, chan by value, & io_function by value, & iosb,,, & text by ref, & 5% by value,, & terminators by ref,,) if (sys_status and 1%) = 0% then call lib$signal(sys_status by value) end if if iosb::xfr_len = 0 then text_len = iosb::terminator_len else text_len = iosb::xfr_len end if select mid(text,1%,text_len) case = r_arrow, = ctrl_f, = h_tab, = space if dir_selection < max_dir then ! Can we advance? if cur_col >= (col_len * (num_cols - 1%)) then ! At end of row? cur_col = start_col + cursor_len + num_field if cur_row = tt_rows then ! At last row of screen? cur_row = start_row page_number = page_number + 1% call display_dirs( page_number, new_dir( ) ) else ! Not at end of page just go to the next row. cur_row = cur_row + 1% end if else ! No special case exists just advance cursor to the next column. cur_col = cur_col + col_len end if dir_selection = dir_selection + 1% ! Increment array index. ! Position the cursor. call set_cursor_abs(cur_row, cur_col, term_table, chan) end if case = l_arrow, = ctrl_b, = ctrl_d, = delete_key if dir_selection > 1% then ! Can we move backward? ! Can we move left on the same row? if cur_col > (start_col + cursor_len + num_field) then cur_col = cur_col - col_len else ! Do we need to go back a page? if (page_number <> 0%) and (cur_row = start_row) then page_number = page_number - 1% cur_row = tt_rows cur_col = col_len * (num_cols - 1%) + cursor_len + num_field + 1% call display_dirs( page_number, new_dir( ) ) else ! No. Just go to end of previous row. cur_col = col_len * (num_cols - 1%) + cursor_len + num_field + 1% cur_row = cur_row - 1% end if end if dir_selection = dir_selection - 1% call set_cursor_abs(cur_row, cur_col, term_table, chan) end if case = dn_arrow, = ctrl_n if dir_selection < max_dir then ! Can we advance any? ! Can we advance on the same page? if (dir_selection + num_cols) <= (page_size * (page_number + 1%)) then ! Can we advance by a full row? if dir_selection <= (max_dir - num_cols) then cur_row = cur_row + 1% dir_selection = dir_selection + num_cols call set_cursor_abs(cur_row, cur_col, term_table, chan) else end_col = mod(max_dir,num_cols) logical_col = (cur_col / col_len) + 1% if (end_col > 0%) and (logical_col > end_col) then dir_selection = dir_selection + end_col + (num_cols - logical_col) cur_col = (end_col - 1%) * col_len + cursor_len + num_field + 1% cur_row = cur_row + 1% call set_cursor_abs(cur_row, cur_col, term_table, chan) end if end if else ! Advancing by row will put us on the next page. Display it. ! Can we advance by a full row? if dir_selection <= (max_dir - num_cols) then dir_selection = dir_selection + num_cols page_number = page_number + 1% cur_row = start_row call display_dirs( page_number, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) else end_col = mod(max_dir,num_cols) logical_col = (cur_col / col_len) + 1% if (end_col > 0%) and (logical_col > end_col) then dir_selection = dir_selection + end_col + (num_cols - logical_col) cur_col = (end_col - 1%) * col_len + cursor_len + num_field + 1% page_number = page_number + 1% cur_row = start_row call display_dirs( page_number, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) end if end if end if end if case = up_arrow, = ctrl_p ! Can we move up a row? if dir_selection > num_cols then ! Are we at the top of a page? if cur_row > start_row then ! No. cur_row = cur_row - 1% dir_selection = dir_selection - num_cols call set_cursor_abs(cur_row, cur_col, term_table, chan) else ! Yes, at top of a page. ! Is there a previous page? if page_number > 0% then page_number = page_number - 1% call display_dirs( page_number, new_dir( ) ) dir_selection = dir_selection - num_cols cur_row = tt_rows call set_cursor_abs(cur_row, cur_col, term_table, chan) end if end if end if case = ctrl_e logical_col = (cur_col / col_len) + 1% col_diff = num_cols - logical_col end_col = mod(max_dir,num_cols) ! Check to see if already at max column size. if col_diff > 0% then ! Are we on the last row of directories? if dir_selection > (max_dir - end_col) then ! Last column position might be less than max column size. col_diff = end_col - logical_col end if ! Check to see if already at end of column. if col_diff > 0% then cur_col = cur_col + (col_len * col_diff) end if dir_selection = dir_selection + col_diff call set_cursor_abs(cur_row, cur_col, term_table, chan) end if case = ctrl_a, = backspace ! Check to make sure we are not already at the first column. if cur_col > (start_col + cursor_len + num_field) then logical_col = (cur_col / col_len) + 1% cur_col = start_col + cursor_len + num_field dir_selection = dir_selection - (logical_col - 1%) call set_cursor_abs(cur_row, cur_col, term_table, chan) end if case = ctrl_j ! Should we really do an upward recursion or simply unwind one step. if recurse_count = 0% then ! Nothing to unwind - check to see if there is room on the jump ! stack to push another level. if jump_count < 9% then context = 0% search_dir = "[" + string$(jump_count + 1%, 45%) + "]*.dir" sys_status = lib$find_file( search_dir, dir_file, context ) ! if status is not successful then user can't go up ! another level. if sys_status = rms$_normal then cur_dir = mid(new_dir(dir_selection), 1%, find_last(".", new_dir(dir_selection)) - 1%) + "]" i = jump_count ! Have we been here before? If so unwind the ! stack to this point. while (i > 0%) and (jump_dir(jump_count) <> cur_dir) i = i - 1% next if (jump_dir(i) = cur_dir) then ! Unwind to here. jump_count = i else if (cur_dir = start_dir) and (jump_count > 0%) then ! Unwind to start. jump_count = 0% else ! Push where we were on the jump stacks. jump_count = jump_count + 1% jump_save(jump_count, 1%) = page_number jump_save(jump_count, 2%) = cur_row jump_save(jump_count, 3%) = cur_col jump_save(jump_count, 4%) = dir_selection jump_dir(jump_count) = cur_dir dot = find_last(".", new_dir(dir_selection) ) if dot then parent_dir = mid(new_dir(dir_selection), 1%, dot - 1%) + "]" else parent_dir = new_dir(dir_selection) end if call read_dirs( search_dir, new_dir( ) ) i = max_dir ! Find the position of the directory that ! we just came up from. while (i > 0%) and (new_dir(i) <> parent_dir) i = i - 1% next if i = 0% then ! Couldn't find it. (should get here...I hope.) ! Go to top. dir_selection = 1% cur_row = start_row cur_col = start_col + cursor_len + num_field page_number = 0% else ! Position cursor on top of the directory ! that we just came up from. dir_selection = i page_number = dir_selection / (page_size + 1%) if dir_selection >= num_cols then end_col = mod(dir_selection,num_cols) if end_col = 0% then end_col = num_cols end if else end_col = dir_selection end if cur_col = (end_col - 1%) * col_len + start_col + cursor_len + num_field num_rows = tt_rows - start_row + 1% cur_row = (dir_selection / num_cols) - (num_rows * page_number) + start_row - 1% if end_col < num_cols then cur_row = cur_row + 1 end if end if call display_dirs( page_number, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) end if end if end if end if else ! Unwind one level from recursion stack. page_number = recurse_save(recurse_count, 1%) cur_row = recurse_save(recurse_count, 2%) cur_col = recurse_save(recurse_count, 3%) dir_selection = recurse_save(recurse_count, 4%) recurse_count = recurse_count - 1% dot = find_last(".", recurse_dir) if dot then recurse_dir = mid(recurse_dir, 1%, dot - 1%) + "]" else recurse_dir = mid(recurse_dir, 1%, len(recurse_dir) - 1%) + ".-]" end if if recurse_flag then ! Need to reconstruct the search directory. dot = find_last(".", recurse_dir) if dot then recurse_flag = 0% recurse_dir = mid(recurse_dir, 1%, dot - 1%) + "]" else recurse_dir = mid(recurse_dir, 1%, len(recurse_dir) - 1%) + ".-]" end if end if call read_dirs ( recurse_dir, new_dir( ) ) call display_dirs( page_number, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan ) end if case = ctrl_r ! Check to see if we need to perform a real downward recursion ! or can we simply unwind one level from the jump stack. if new_dir(dir_selection) = jump_dir(jump_count) then ! Unwind one level from jump stack. page_number = jump_save(jump_count, 1%) cur_row = jump_save(jump_count, 2%) cur_col = jump_save(jump_count, 3%) dir_selection = jump_save(jump_count, 4%) search_dir = jump_dir(jump_count) + "*.dir" jump_count = jump_count - 1% call read_dirs( search_dir, new_dir( ) ) call display_dirs( 0%, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) else ! Is there room on the recursion stack to push another level? if recurse_count < 8% then ! Save current level on recursion stack. recurse_dir = new_dir(dir_selection) search_dir = mid(recurse_dir, 1%, instr(1%, recurse_dir, "]")) + "*.dir" recurse_flag = -1% context = 0% sys_status = lib$find_file( search_dir, dir_file, context ) if sys_status = rms$_normal then recurse_flag = 0% recurse_count = recurse_count + 1% recurse_save(recurse_count, 1%) = page_number recurse_save(recurse_count, 2%) = cur_row recurse_save(recurse_count, 3%) = cur_col recurse_save(recurse_count, 4%) = dir_selection recurse_dir = new_dir(dir_selection) dir_selection = 1% cur_row = start_row cur_col = start_col + cursor_len + num_field page_number = 0% call read_dirs( recurse_dir, new_dir( ) ) call display_dirs( 0%, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) end if end if end if case = ctrl_u ! Control-X gets returned to us as a control-U (bummer) ! Expand the directory name at top of screen. call set_cursor_abs(1%, 1%, term_table, chan) call output( smg$k_erase_to_end_line, term_table, chan ) i = len(new_dir(dir_selection)) if i <= tt_cols then print #2%, new_dir(dir_selection); else print #2%, mid(new_dir(dir_selection), i - tt_cols + 1%, tt_cols); end if call set_cursor_abs(cur_row, cur_col, term_table, chan) case = esc_low_v, = esc_cap_v, = prev_scrn ! Can we go back a page? if page_number > 0% then ! Yes. Decrement page number and redisplay the directories. page_number = page_number - 1% call display_dirs( page_number, new_dir( ) ) dir_selection = dir_selection - (tt_rows - (start_row - 1%)) * num_cols call set_cursor_abs(cur_row, cur_col, term_table, chan) else ! No. Move to beginning of current page. if dir_selection > 1% then dir_selection = 1% cur_row = start_row cur_col = start_col + cursor_len + num_field call set_cursor_abs(cur_row, cur_col, term_table, chan) end if end if case = ctrl_v, = next_scrn ! Is there a next page? if max_dir > ((page_number + 1%) * page_size) then ! Yes. Display it. page_number = page_number + 1% ! Can we advance by a full page? if dir_selection <= (max_dir - page_size) then ! Yes. Increment array index by page_size. dir_selection = dir_selection + page_size else ! No. Go to bottom of next page. dir_selection = max_dir end_col = mod(max_dir,num_cols) if end_col > 0% then cur_col = (end_col - 1%) * col_len + cursor_len + num_field + 1% else cur_col = (num_cols - 1%) * col_len + cursor_len + num_field + 1% end if cur_row = max_dir / page_size if mod(max_dir,page_size) > 0% then cur_row = cur_row + 1% end if end if call display_dirs( page_number, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) else ! There is no next page. Move to bottom of current page. if dir_selection < max_dir then dir_selection = max_dir end_col = mod(max_dir,num_cols) if end_col > 0% then cur_col = (end_col - 1%) * col_len + cursor_len + num_field + 1% else cur_col = (num_cols - 1%) * col_len + cursor_len + num_field + 1% end if cur_row = mod(max_dir,page_size) if cur_row <> 0% then cur_row = cur_row / num_cols + start_row - 1% else cur_row = tt_rows end if x = num_cols y = mod(max_dir,page_size) x = y / x + start_row - 1.0 if x > cur_row then cur_row = cur_row + 1% end if call set_cursor_abs(cur_row, cur_col, term_table, chan) end if end if case = ctrl_w, = ctrl_l ! Refresh the screen. call display_dirs( page_number, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) case = ctrl_z ! Exit into the directory that contains the directories ! that are currently being displayed. dir_selection = 1% dot = find_last(".",new_dir(1%)) if dot = 0% then new_dir(1%)=mid(new_dir(1%),1%,instr(0%,new_dir(1%),"["))+"000000]" dot = len(new_dir(1%)) end if new_dir(1%) = mid(new_dir(1%), 1%, dot - 1%) + "]" call set_cursor_abs(1%, 1%, term_table, chan) call output( smg$k_erase_whole_display, term_table, chan ) exit sub case = help, = q_mark ! Display help. call output( smg$k_erase_whole_display, term_table, chan ) call set_cursor_abs( 1%, 1%, term_table, chan) call help ! Continue where we were. call display_dirs( page_number, new_dir( ) ) call set_cursor_abs(cur_row, cur_col, term_table, chan) case = ctrl_m, = do_key ! The user has chosen the desired directory. call set_cursor_abs(1%, 1%, term_table, chan) call output( smg$k_erase_whole_display, term_table, chan ) exit sub case else ! Let's check to see if it's a number of a directory. ! Better set error trapping just incase it's not. when error in i = val(mid(text,1%,text_len)) if (i > 0%) and (i <= max_dir) then dir_selection = i call set_cursor_abs(1%, 1%, term_table, chan) call output( smg$k_erase_whole_display, term_table, chan ) exit sub end if use ! Oops. Not a number - must have been garbage. ! Throw it away and continue. continue forever end when end select ! Let's make the compiler think that this might be false so it ! won't complain about unreachable code. if (text <> ctrl_m) and (text <> do_key ) then goto forever end if end when ! End when error for ^C trapping. handler error_trap ! Is it a programmable ^C trap? if err=28% then ! Yes. Clear screen and exit with successful status. y% = ctrlc call set_cursor_abs(1%, 1%, term_table, chan) call output( smg$k_erase_whole_display, term_table, chan ) call sys$exit( 1% by value ) else ! Not expecting anything else...better report it. print #2%, ert$(err) end if end handler subend