; BRIEF -- Basic Reconfigurable Interactive Editing Facility
; Written by Dave Nanian and Michael Strickman.

(extern _dialog_action_func
        _dialog_dir
        _dialog_menu_prefix
        _process_menu
)

(extern _dialog_level
        _dialog_type
        _dialog_esc
        _dialog_row
        _dialog_col
        _dialog_field_keymap
        _dialog_list_keymap
        _dialog_menu_keymap
        _dialog_size
        _dialog_data_buf
        _dialog_disp_buf
        _dialog_mode
        _dialog_menu_time
)

(extern display_file_name
        to_bottom
)

; buffers.m:

; This file contains all of the standard BRIEF macros for buffer
; manipulation.

; buf_list:

; This macro provides the user with a list of buffers that can be
; manipulated and scrolled through.  It is of interest because it
; simulates an array of buffer ids using a system buffer.  Macro
; programmers interested in using arrays should examine this code
; carefully before implementing an array system of their own.

; Change history:

; Added wrap around to menu light bar.
; Only the file path and name now appear in the list, with an asterisk
; in column 1 to indicate a changed file.
; The window now occupies up to the full screen length if enough
; buffers are open but only enough lines for all buffers if less
; than screen length.  It also auto sizes to the width of the longest
; file name + 1 (To allow for the asterisk against changed files).
; Since this could make for very narrow windows, the message at the
; bottom of the window has been cut to a list of active keys.
; The buffer list no longer contains the buffer number.  This is the
; same as the "line" number on the command line.

; Jon O'Brien (cody@cix) 4/10/89

; Added an improved _bookmark_menu macro I got from the Tec Rap board

; Jon O'Brien (cody@cix) 20/12/89

(macro buf_list
   (
      (int _buf_list   ;	Buffer that contains visible list.
           _buf_array   ;	Buffer that simulates an array.
           _end_buf   ;	The buffer to be attached on exit.
           _num_bufs   ;	The number of buffers in the list.
           _currbuf   ;	Current buffer No.
           _scrlines   ;	No of lines on screen
           _scrcols   ;	No of columns on screen
           _bottom   ;	Last row for window
           _max_len   ;	Length of longest buffer name
           _leftc   ;	Left window column
           _rightc   ;	Right window column
      )
      (global
              _buf_list
              _buf_array
              _end_buf
              _num_bufs
              _currbuf
              _scrlines
              _scrcols
              _bottom
              _max_len
              _leftc
              _rightc
      )
      (int start_buf   ;  The buffer we started on.
           curr_buf   ;  The buffer we're on (in the loop).
           modified   ;  Is the current buffer modified?
      )
      (string file_name   ;	The file name of the buffer.
              buffer_name   ;	The buffer name.
      )
      (= _num_bufs 0)
      (= _max_len 13)
      (= _leftc 0)
      (= _rightc 0)
      (= _currbuf 0)
      (inq_screen_size _scrlines _scrcols)
      ;  First, we create the array and list buffers, making sure that they
      ;  have the "system" attributes.	We use non-file buffers because this is
      ;  temporary information.
      ; 
      ;  The "_buf_list" buffer contains a textual list of all of the non
      ;  system buffers currently in memory.	"_buf_array" contains a list of the
      ;  corresponding buffer pointers.  Note that the buffer pointers are not
      ;  inserted into the buffer as is -- they are converted to string integers
      ;  first.  This prevents inserting bad characters like tab, return and EOF.
      (message "Creating buffer list...")
      ; First, we get the ID of the current buffer -- we have to
      ; restore it at the end of the macro.	After that, we create the
      ; array buffer and put it aside for the moment.  Note that the
      ; array buffer is a non-file buffer -- this prevents disk accesses
      ; since the file is held completely in memory.
      (= start_buf (= _end_buf (inq_buffer)))
      (= _buf_array (create_buffer "Buffer Array" NULL 1))
      ; Now, we create the buffer that the user will actually see on
      ; the screen, set it to be the current buffer, and set the tabs to
      ; the values we want (tabs default to every column).
      (set_buffer (= _buf_list (create_buffer "Buffers" NULL 1)))
      (tabs 5)
      ; This loop creates both buffer lists:  the non-system buffers
      ; are looped through and information about them are culled and added
      ; to the list.
      (while (!= start_buf curr_buf)
         (
            (if (! curr_buf)
               (= curr_buf start_buf)
            ;else
               (
                  (set_buffer _buf_list)
                  (insert "\n")
                  (set_buffer _buf_array)
                  (insert "\n")
               )
            )
            (++ _num_bufs)
            (message "Creating buffer list [#%d]..." _num_bufs)
            (set_buffer curr_buf)
            (inq_names file_name NULL buffer_name)
            (= modified (inq_modified))
            (set_buffer _buf_list)
            (if (> (strlen file_name) _max_len)
               (= _max_len (strlen file_name))
            )
            (insert (+ (if modified "*" " ") file_name))
            (set_buffer _buf_array)
            (insert "%d" curr_buf)
            (set_buffer curr_buf)
            (= curr_buf (next_buffer))
         )
      )
      (if (> _num_bufs (- _scrlines 3))
         (= _bottom (- _scrlines 3))
      ;else
         (= _bottom (+ _num_bufs 1))
      )
      (message "%d buffers in use" _num_bufs)
      ; At this point, the array buffer and list buffer have been created
      ; and filled in.	We create a window to display the list in, initialize
      ; the marked area, set up a keymap, and call process.  Process handles
      ; the mainline loop -- we need only provide action routines.
      (= _leftc (/ (- (- _scrcols _max_len) 4) 2))
      (= _rightc (+ (+ _leftc _max_len) 2))
      (create_window _leftc _bottom _rightc 0 ",E,D,W,ESC")
      (attach_buffer _buf_list)
      (keyboard_push)
      (assign_to_key "<Down>" "_buf_down")
      (assign_to_key "<Up>" "_buf_up")
      (assign_to_key "<PgUp>" "_buf_pgup")
      (assign_to_key "<PgDn>" "_buf_pgdn")
      (assign_to_key "<Home>" "_buf_home")
      (assign_to_key "<End>" "_buf_end")
      (assign_to_key "<Ctrl-PgUp>" "_buf_home")
      (assign_to_key "<Ctrl-PgDn>" "_buf_end")
      (assign_to_key "d" "_buf_delete")
      (assign_to_key "D" "_buf_delete")
      (assign_to_key "<Ctrl-minus>" "_buf_delete")
      (assign_to_key "e" "_buf_edit")
      (assign_to_key "E" "_buf_edit")
      (assign_to_key "<Alt-e>" "_buf_edit")
      (assign_to_key "W" "_buf_write")
      (assign_to_key "w" "_buf_write")
      (assign_to_key "<Alt-w>" "_buf_write")
      (assign_to_key "<Enter>" "_buf_edit")
      (assign_to_key "<Esc>" "exit")
      (move_abs 1 100)
      (drop_anchor)
      (move_abs 1 1)
      (set_buffer _buf_array)
      (move_abs 1 1)
      (set_buffer _buf_list)
      (refresh)
      (process)
      ; Now that we're back from the processing, we pop the keymap that
      ; we created, raise the assorted anchors, and delete the temporary
      ; window and the buffers.
      (keyboard_pop)
      (raise_anchor)
      (delete_window)
      (delete_buffer _buf_list)
      (delete_buffer _buf_array)
      ; If we end with a different buffer than we started with, we
      ; attach it to the current window and display its file name.
      ; Otherwise, we set the current buffer back, and call NEW_FILE
      ; to make sure its defaults are reset.
      (set_buffer _end_buf)
      (inq_names file_name)
      (edit_file file_name)
   )
)
(macro _buf_home
   (
      (raise_anchor)
      (top_of_buffer)
      (drop_anchor 3)
      (set_buffer _buf_array)
      (top_of_buffer)
      (set_buffer _buf_list)
   )
)
; _buf_down:
; This routine is called whenever the user presses a down arrow.
; It moves the array buffer and list buffer's current lines, ensuring
; the user does not go past the end of the buffer.
;  Now wraps around.  Jon O'Brien.
(macro _buf_down
   (
      (int line)
      (inq_position line)
      (if (== line _num_bufs)
         (
            (_buf_home)
            (return 1)
         )
      ;else
         (
            (raise_anchor)
            (move_abs (+ line 1) 0)
            (drop_anchor 3)
            (set_buffer _buf_array)
            (move_abs (+ line 1) 0)
            (set_buffer _buf_list)
            (returns 1)
         )
      )
   )
)
(macro _buf_end
   (
      (int num_lines)
      (= num_lines (- _bottom 1))
      (raise_anchor)
      (end_of_buffer)
      (beginning_of_line)
      (to_bottom)
      (drop_anchor 3)
      (set_buffer _buf_array)
      (end_of_buffer)
      (beginning_of_line)
      (set_buffer _buf_list)
   )
)
; _buf_up:
; This macro is called whenever the user presses the up arrow.  It
; moves up in the list and array buffers, not going past the top of the
; buffer.
;  Now wraps around.  Jon O'Brien.
(macro _buf_up
   (
      (int line)
      (inq_position line)
      (if (== line 1)
         (
            (_buf_end)
            (return 1)
         )
      ;else
         (
            (raise_anchor)
            (move_abs (- line 1) 0)
            (drop_anchor 3)
            (set_buffer _buf_array)
            (move_abs (- line 1) 0)
            (set_buffer _buf_list)
            (returns 1)
         )
      )
   )
)
; _buf_pgup:
; This macro is called whenever the user presses PgUp.  It moves up
; one screen in the list and array buffers, not going past the top of the
; buffer.
(macro _buf_pgup
   (
      (int num_lines)
      (= num_lines (- _bottom 1))
      (inq_position _currbuf)
      (if (>= num_lines _currbuf)
         (_buf_home)
      ;else
         (while (post-- num_lines)
            (_buf_up)
            )
      )
   )
)
; _buf_pgdn:
; This macro is called whenever the user presses PgDn.  It moves
; down one page/screen in the buffer list and the array.
(macro _buf_pgdn
   (
      (int num_lines)
      (= num_lines (- _bottom 1))
      (if (>= (+ num_lines _currbuf) _num_bufs)
         (_buf_end)
      ;else
         (while (post-- num_lines)
            (_buf_down)
            )
      )
   )
)
; _buf_delete:
; This routine is called when the user wants to delete a buffer.
; It makes sure the buffer is not in a window, and prompts the user
; if the buffer has been modified.
(macro _buf_delete
   (
      (int buf_to_delete)
      (string misc_str)
      (= misc_str "y")
      (set_buffer _buf_array)
      (= buf_to_delete (atoi (read)))
      (set_buffer buf_to_delete)
      (if (! (inq_views))
         (
            (if (inq_modified)
               (
                  (keyboard_flush)
                  (if (! (get_parm NULL misc_str "This buffer has not been saved.  Delete [ynw]? " 1))
                     (= misc_str "n")
                  )
               )
            )
            (if (|| (index "yY" misc_str) (&& (index "wW" misc_str) (> (_buf_write) 0)))
               (
                  (set_buffer _buf_list)
                  (delete_line)
                  (set_buffer _buf_array)
                  (delete_line)
                  (delete_buffer buf_to_delete)
                  (-- _num_bufs)
                  (set_buffer _buf_list)
                  (if (inq_position)
                     (_buf_up)
                  ;else
                     (
                        (raise_anchor)
                        (move_rel 0 0)
                        (drop_anchor 3)
                        (move_rel 0 0)
                     )
                  )
               )
            ;else
               (if (! (index "wW" misc_str))
                  (message "Buffer not deleted.")
               )
            )
         )
      ;else
         (error "You can't delete a viewed buffer.")
      )
      (set_buffer _buf_list)
   )
)
; _buf_edit:
; This routine is called when the user is on a buffer that he wants
; to edit.	It sets the global "_end_buf" variable to the current
; line's buffer, and calls exit so no further processing takes place.
(macro _buf_edit
   (
      (set_buffer _buf_array)
      (= _end_buf (atoi (read)))
      (set_buffer _buf_list)
      (exit)
   )
)
; _buf_write:
; This routine is called when the user wants to write a modified
; buffer.  It makes sure the buffer is modified -- if so, it's written
; and the information in the window is updated.
(macro _buf_write
   (
      (int old_msg_level
           ret_code
      )
      (set_buffer _buf_array)
      (set_buffer (atoi (read)))
      (if (inq_modified)
         (
            (= old_msg_level (inq_msg_level))
            (set_msg_level 0)
            (if (> (= ret_code (write_buffer)) 0)
               (
                  (set_msg_level old_msg_level)
                  (set_buffer _buf_list)
                  (beginning_of_line)
                  (delete_char)
                  (insert " ")
                  (beginning_of_line)
               )
            ;else
               (set_msg_level old_msg_level)
            )
         )
      ;else
         (error "Buffer not modified.")
      )
      (set_buffer _buf_list)
      (returns ret_code)
   )
)
; edit_file:
; This macro replaces the built-in edit_file function:  it displays
; the file name of the edited file if the edit operation was successful.
(replacement edit_file
   (
      (int ret_code
           old_msg_level
      )
      (= old_msg_level (inq_msg_level))
      (if (== (inq_called) "")
         (set_msg_level 0)
      )
      (= ret_code (edit_file))
      (set_msg_level old_msg_level)
      (if (== ret_code 1)
         (display_file_name)
      )
      (returns ret_code)
   )
)
; _multifile_edit_file:
; This macro is called by the file completion functions when more then
; one file is selected from the completion menu.
(macro _multifile_edit_file
   (
      (int completion_buf_id)
      (int old_buf_id)
      (string path)
      (string file_name)
      (get_parm 0 completion_buf_id)
      (get_parm 1 path)
      (= old_buf_id (set_buffer completion_buf_id))
      (top_of_buffer)
      (while (&& (! (inq_position)) (!= (= file_name (trim (ltrim (read)))) ""))
         (
            (edit_file (+ path file_name))
            (set_buffer completion_buf_id)
            (down)
         )
      )
      (set_buffer old_buf_id)
   )
)
; edit_next_buffer:
; Edits the next buffer in the buffer list in the current window.  If
; there are no other buffers, an error message is displayed.
(macro edit_next_buffer
   (
      (string file_name)
      (int new_buffer)
      (= new_buffer (next_buffer))
      (if (!= new_buffer (inq_buffer))
         (
            (set_buffer new_buffer)
            (inq_names file_name)
            (edit_file file_name)
         )
      ;else
         (error "No other buffers.")
      )
   )
)
; edit_prev_buffer:
; Edits the previous buffer in the buffer list.
(macro edit_prev_buffer
   (
      (string file_name)
      (int old_buffer)
      (= old_buffer (inq_buffer))
      (while (!= (next_buffer) old_buffer)
         (set_buffer (next_buffer))
         )
      (if (!= (inq_buffer) old_buffer)
         (
            (inq_names file_name)
            (edit_file file_name)
         )
      ;else
         (error "No other buffers.")
      )
   )
)
; delete_curr_buffer:
; This function deletes the current buffer, if it is unmodified,
; there is another buffer in the list to replace it, and it is only
; visible in one window.
; If the buffer is modified, the user is asked if he really wants to
; delete the buffer.  If he answers yes, the deletion is performed.
; If there are no other buffers, or the buffer is in an overlapping
; window and any other window, the deletion is not performed.
(macro delete_curr_buffer
   (
      (int old_buf_id
           new_buf_id
           window_buf_id
           orig_window_id
           curr_window_id
      )
      (string reply
              file_name
      )
      (= old_buf_id (inq_buffer))
      (= new_buf_id (next_buffer))
      (if (== old_buf_id new_buf_id)
         (
            (error "Can't delete: no other buffers.")
            (return 0)
         )
      )
      (= orig_window_id (inq_window))
      (if (== (inq_window_info) 1)
         (
            (= curr_window_id (next_window))
            (while (!= orig_window_id curr_window_id)
               (
                  (inq_window_info curr_window_id window_buf_id)
                  (if (== window_buf_id old_buf_id)
                     (
                        (error "Can't delete: buffer's windows overlap.")
                        (return 0)
                     )
                  )
                  (= curr_window_id (next_window curr_window_id))
               )
            )
         )
      )
      (if (inq_modified)
         (
            (keyboard_flush)
            (if (! (get_parm NULL reply "This buffer has not been saved.  Delete [ynw]? " 1))
               (return 0)
            ;else
               (if (|| (! (strlen reply)) (! (index "yYwW" reply)))
                  (
                     (message "Buffer not deleted.")
                     (return 0)
                  )
               )
            )
            (if (index "wW" reply)
               (
                  (int old_msg_level
                       ret_code
                  )
                  (= old_msg_level (inq_msg_level))
                  (set_msg_level 0)
                  (= ret_code (write_buffer))
                  (set_msg_level old_msg_level)
                  (if (<= ret_code 0)
                     (return 0)
                  )
               )
            )
         )
      )
      (set_buffer new_buf_id)
      (inq_names file_name NULL)
      (set_buffer old_buf_id)
      (edit_file file_name)
      (if (!= (inq_views old_buf_id) 0)
         (
            (= curr_window_id (next_window))
            (while (!= orig_window_id curr_window_id)
               (
                  (inq_window_info curr_window_id window_buf_id)
                  (if (== old_buf_id window_buf_id)
                     (
                        (set_window curr_window_id)
                        (edit_file file_name)
                     )
                  )
                  (= curr_window_id (next_window curr_window_id))
               )
            )
            (if (!= (inq_window) orig_window_id)
               (set_window orig_window_id)
            )
         )
      )
      (delete_buffer old_buf_id)
      (returns 1)
   )
)
; _bad_key:
; This routine adds menus to some prompts.
(replacement _bad_key
   (
      (int key_pressed)
      (= key_pressed (read_char))
      (if (&& (== (inq_command) "goto_bookmark") (== key_pressed (key_to_int "<Tab>")))
         (return (_bookmark_menu))
      )
      (push_back key_pressed)
      (return (_bad_key))
   )
)
;******** Integrated the macros below.   Jon O'Brien 20/12/89 *********

; This new macro is a substitute for _bookmark_menu (which is included
; in the buffers.m macro in Brief 2.1).  It is NOT a replacement macro for
; _bookmark_menu or goto_bookmark (which I couldn't figure out how to do)
; but a slightly modified _bookmark_menu (using the original BRIEF code).
; _bookmark_menu normally worked if a "<Tab>" was hit in reponse to the
; prompt "Go to bookmark [1-10]: " and puts up the bookmark list.  Not being
; capable of training myself to hit "<Tab>" to expand a prompt I stole the
; part of the macro which creates the list and incorporated it into a macro
; which could be assigned a key ("<Alt-j>" in my case) to pop the list up
; right off the bat (similar to the buffer_list macro usually assigned to
; "<Alt-b>".  The list was also modified to shorten the file name and display
; about 25 characters of the text of the file that follows the book mark.  If
; a newline or tab character was encountered then either "<CR>" or "<TB>"
; is shown respectively.
; I added:
; (assign_to_key "<Alt j>" "bookmark_menu")
; in my initials macro.

; Robert Briber 9/89
; Kensington, MD

; Note if you are using the scraps2 macro for displaying a menu of
; 10 virtual scrap buffers then the macro _get_string
; may be repeated.  One of the two (either here or in scraps2 could be
; deleted and the macro recompiled.
(macro _bookmark_menu
   (
      (int test_parm bookmark_number bookmark_buffer line_pos col_pos)
      (string temp_string)
      (int menu_buf
           old_buf
           old_buf2
           curr_buf
           line
           col
           i
           l
           num_bookmarks
           _bookmark
      )
      (string bookmark
              file_name
              text_string
      )
      (global _bookmark)
      (while (< i 10)        ;  count the number
         ;  of bookmarks
         (
            (if (goto_bookmark i curr_buf line col)
               (
                  (++ num_bookmarks)
               )
            )
            (++ i)
         )
      )
      (if (== num_bookmarks 0)
         (
            (beep)
            (error "No bookmarks have been dropped")
         )
      ;else
         (
            (int num_lines
                 num_cols
                 lx
                 by
                 rx
                 ty
            )
            (= old_buf (inq_buffer))
            (sprintf bookmark "%d bookmark%s " num_bookmarks (if (== num_bookmarks 1) "" "s"))
            (set_buffer (= menu_buf (create_buffer bookmark NULL 1)))
            (= num_bookmarks 0)
            (= i 1)
            (tabs 2 13 19 25 40 80)
            (while (<= i 10)
               (
                  (if (goto_bookmark i curr_buf line col)
                     (
                        (if (== (++ num_bookmarks) 1)
                           (insert "\n\tBookmark\tLine\tCol\tFile\tText\n\t--------\t----\t---\t----\t----")
                        )
                        (insert "\n")
                        (set_buffer curr_buf)
                        (inq_names file_name)
                        (= file_name (substr file_name (+ (rindex file_name "\\") 1) 12))
                        (= text_string (_get_string curr_buf line col))
                        (sprintf bookmark "\t%d\t%d\t%d\t%s\t%s\t;" i line col file_name text_string)
                        (sprintf bookmark "%s%d" bookmark i)
                        (set_buffer menu_buf)
                        (insert bookmark)
                     )
                  )
                  (++ i)
               )
            )
            (set_buffer old_buf)
            (= _bookmark -1)
            (inq_screen_size num_lines num_cols)
            (/= num_lines 2)
            (/= num_cols 2)
            (= lx (- num_cols 36))
            (= rx (+ num_cols 36))
            (= ty (- num_lines (+ 4 (/ (+ num_bookmarks 4) 2))))
            (= by (+ ty (+ num_bookmarks 5)))
            (_process_menu lx by rx ty NULL " or  to move, <Enter> to select, <Esc> to exit" NULL menu_buf "_bookmark_action" 1)
            (if (>= _bookmark 0)
               (
                  (goto_bookmark _bookmark)
               )
            )
         )
      )
   )
)
; 		_bookmark_action:
; 		This is the action routine for the dialog manager.
(macro _bookmark_action
   (
      (int event_type
           button_number
      )
      (string button_text)
      (get_parm 0 event_type)
      (switch event_type
         -5
         (
            (get_parm 2 button_text)
            (if (index button_text ";")
               (returns 1)
            ;else
               (returns 0)
            )
         )
         -6
         NULL -11
         (
            (get_parm 2 button_text)
            (= _bookmark (atoi (substr button_text (+ (rindex button_text ";") 1))))
            (_dialog_esc)
            (insert "")
            (returns 1)
         )
      )
   )
)
;  _get_string uses the buffer id passed to it to read 25 charaters of text
;  from the line and column position which are also passed to it.
;  the string is translated to remove any tabs and new lines (which are
;  translated to "<TB>" and "<CR>" repectively and then the string is
;  returned to the calling macro.	Also shows up in scraps2.
;  RMB 9/89
(macro _get_string
   (
      (string text_string)
      (int buff_id
           old_buff_id
           start_line
           start_col
      )
      (= buff_id 0)
      (get_parm 0 buff_id)
      (get_parm 1 start_line)
      (get_parm 2 start_col)
      (= old_buff_id (inq_buffer))
      (set_buffer buff_id)
      (save_position)        ;  read
      (move_abs start_line start_col)   ;  in 25 characters of text.
      (= text_string (read 25))   ;  Search string for newlines
      (if (index text_string "\n")   ;  and tabs.
         (
            (= text_string (substr text_string 1 (- (index text_string "\n") 1)))
            (+= text_string "<CR>")
         )        ;	truncate text at newline and
      )
      ;  indicate this with "<CR>".
      (if (index text_string "\t")
         (
            (string text_string2)   ;  substitute the string "<TB>" for
            (int k)   ;  any tab character found in the
            (= k 1)   ;	string.
            (while (<= k (strlen text_string))
               (
                  (if (== (substr text_string k 1) "\t")
                     (
                        (+= text_string2 "<TB>")
                     )
                  ;else
                     (
                        (+= text_string2 (substr text_string k 1))
                     )
                  )
                  (++ k)
               )
            )
            (= text_string text_string2)
         )
      )
      (if (> (strlen text_string) 25)
         (= text_string (substr text_string 1 25))
      )
      (+= text_string "...")
      (restore_position)
      (set_buffer old_buff_id)
      (returns text_string)
   )
)
