;**
;**		BRIEF -- Basic Reconfigurable Interactive Editing Facility
;**
;**		Written by Dave Nanian and Michael Strickman.
;**
;**		Revision history:
;**		-----------------
;**		6 February 1986				Split up search and translate patterns.
;**
;**		23 January 1986				Added regular expression toggle code.
;**											Changed search prompt handling to show
;**											previous pattern.  Created kbd_translate
;**											to allow use of last search pattern as
;**											translate pattern, and to handle regular
;**											expression toggling.
;**
;**		4 November 1985				Added set_backup assignment (Ctrl-w).
;**

;**		system.m:
;**
;**		This macro file contains the definitions for the standard system
;**	macros such as next_word, previous_word, etc.
;**

(macro system
	(
		(int		_dir											;** Direction of last search.
					_reg_exp										;** Use regular expressions?
		)
		(string	_s_pat										;** Value of last search pattern.
					_t_pat										;** Value of last translate pattern.
		)
		(global	_dir
					_reg_exp
					_s_pat
					_t_pat
		)
		(++ _reg_exp)											;** Regular expressions are ON.

		(assign_to_key "%t" "next_word")					;** Assigned to Ctrl-Right Arrow.
		(assign_to_key "%s" "previous_word")			;** Assigned to Ctrl-Left Arrow.
		(assign_to_key "%1" "edit_next_buffer")		;** Assigned to Alt-n.
		(assign_to_key "%\;" "switch_window")			;** Assigned to F1.
		(assign_to_key "%!" "display_file_name")		;** Assigned to Alt-f.
		(assign_to_key "%S" "delete")						;** Assigned to Del.

		(assign_to_key "%?" "kbd_fwd_search")			;** Assgined to F5.
		(assign_to_key "%" "kbd_fwd_search")			;** Assigned to Alt-s.
		(assign_to_key "%@" "kbd_translate")			;** Assigned to F6.
		(assign_to_key "%#20" "kbd_translate")			;** Assigned to Alt-t.
		(assign_to_key "%X" "kbd_back_search")			;** Assigned to Shift-F5.
		(assign_to_key "%Y" "search_again")				;** Assigned to Shift-F6.
		(assign_to_key "%m" "toggle_re")					;** Assigned to Alt-F6.
		(autoload "buf_list" "buf_list")					;** Where to find buf_list.
		(assign_to_key "%0" "buf_list")					;** Assigned to Alt-b.
		(assign_to_key "%" "write_it")					;** Assigned to Alt-w.
		(assign_to_key "^w" "set_backup")				;** Assigned to Ctrl-w.
		(assign_to_key "#10" "open_line")				;** Assigned to Ctrl-Enter.

		;** Set up autoloads for standard system macros.

		(autoload "menus" "_menu_process" "_menu_parse" "_menu_to_semi" "_menu_exit")

		(assign_to_key "%#35" "help")						;** Assigned to Alt-h.
		(register_macro 2 "_context_help")				;** Gets called for context help.
		(register_macro 3 "_do_beep")						;** Beep for invalid keystrokes.
	)
)

;**
;**	switch_window:
;**
;**		This macro calls the built-in function to change windows.  If the
;**	window change is sucessful, the name of the file in the new window's
;**	buffer is displayed on the status line.
;**

(macro switch_window
	(if (> (change_window) 0)
		(display_file_name)
	)
)

;**
;**		display_file_name:
;**
;**		Displays the current buffer's file name on the status line.  If the
;**	name is too large, as much of the end as will fit is displayed, preceded
;**	by "...".

(macro display_file_name
	(
		(string	file_name)

		(int		len) 

		(inq_names file_name NULL)

		(if (inq_modified)
			(+= file_name "*")
		)
		(if (<= (= len (strlen file_name)) 42)
			(message "File: %s" file_name)
		;else
			(message "File: ...%s" (substr file_name
				(+ (index (substr file_name (- len 38)) (substr file_name 3 1)) (- len 39))))
		)
	)
)

;**
;**		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)
				(display_file_name)
			)
		;else
			(error "No other buffers.")
		)
	)
)

;**
;**		write_it:
;**
;**		This macro decides what to do when Alt-w is pressed -- if a block
;**	is marked, then the "write_block" primitive is called.  Otherwise,
;**	write_buffer is called to write out the entire buffer.
;**

(macro write_it
	(
		(int		old_msg_level)

		(= old_msg_level (inq_msg_level))
		(set_msg_level 0)

		(if (inq_marked)
			(write_block)
		;else
			(write_buffer)
		)
		(set_msg_level old_msg_level)
	)
)

;**
;**		next_word:
;**
;**		Locates the next word in the file using a regular expression.  If
;**	there are no following words in the file, the cursor does not move.
;**

(macro next_word
	(
		(int		ret_val)

		(save_position)

		(if (! (index " \t" (read 1)))
			(next_char)
		)
		(= ret_val (search_fwd "<|[ \\t]\\c[~ \\t\\n]"))
		(restore_position (! ret_val))
		(returns ret_val)
	)
)

;**
;**		previous_word:
;**
;**		Locates the previous word in the file using a regular expression.  If
;**	there are no preceding words in the file, the cursor does not move.
;**

(macro previous_word
	(
		(int		ret_val
					col
		)
		(save_position)
		(inq_position NULL col)
		(prev_char)

		(if (|| (> col 2) (index " \t" (read 1)))
			(prev_char)
		)
		(= ret_val (search_back "<|[ \\t]\\c[~ \\t\\n]"))
		(restore_position (! ret_val))
		(returns ret_val)
	)
)

;**
;**		Search macros:
;**
;**		The following few macros implement repetitive searching (i.e. search
;**	again).
;**

#define	FORWARD	0
#define	BACK		1

(macro kbd_fwd_search
	(
		(string		pattern
						prompt
		)
		(int			old_msg_level
						ret_code
		)
		(= old_msg_level (inq_msg_level))
		(set_msg_level 0)

		(if _reg_exp
			(= prompt " Search for: ")
		;else
			(= prompt " Search (RE off) for: ")
		)
		(if (strlen _s_pat)
			(= ret_code (get_parm 0 pattern (_sprompt prompt _s_pat) 1))
		;else
			(= ret_code (get_parm 0 pattern prompt))
		)
		(if ret_code
			(
				(= _dir FORWARD)

				(if (&& (strlen _s_pat) (strlen pattern))
					(
						(push_back (atoi pattern 0))
						(= ret_code (get_parm 0 pattern prompt))
					)
				)
				(if ret_code
					(
						(if (strlen pattern)
							(= _s_pat pattern)
						)
						(search_fwd _s_pat _reg_exp)
					)
				)
			)
		)
		(set_msg_level old_msg_level)
	)
)

(macro kbd_back_search
	(
		(string		pattern
						prompt
		)
		(int			old_msg_level
						ret_code
		)
		(= old_msg_level (inq_msg_level))
		(set_msg_level 0)

		(if _reg_exp
			(= prompt " Search for: ")
		;else
			(= prompt " Search (RE off) for: ")
		)
		(if (strlen _s_pat)
			(= ret_code (get_parm 0 pattern (_sprompt prompt _s_pat) 1))
		;else
			(= ret_code (get_parm 0 pattern prompt))
		)
		(if ret_code
			(
				(= _dir BACK)

				(if (&& (strlen _s_pat) (strlen pattern))
					(
						(push_back (atoi pattern 0))
						(= ret_code (get_parm 0 pattern prompt))
					)
				)
				(if ret_code
					(
						(if (strlen pattern)
							(= _s_pat pattern)
						)
						(search_back _s_pat _reg_exp)
					)
				)
			)
		)
		(set_msg_level old_msg_level)
	)
)

;**
;**		kbd_translate:
;**
;**		This routine handles calls to translate from the keyboard level,
;**	making sure that the call heeds the value of the regular expression
;**	toggle.  As a special bonus, BRIEF will use the value of the last
;**	thing searched for as the translation pattern if no pattern is
;**	entered.
;**

(macro kbd_translate
	(
		(string		default_pattern
						pattern
						prompt
		)
		(int			old_msg_level
						ret_code
		)
		(= old_msg_level (inq_msg_level))
		(set_msg_level 0)

		(if _reg_exp
			(= prompt "Pattern: ")
		;else
			(= prompt "Pattern (RE off): ")
		)
		(if (! (strlen _t_pat))
			(= default_pattern _s_pat)
		;else
			(= default_pattern _t_pat)
		)
		(if (strlen default_pattern)
			(= ret_code (get_parm 0 pattern (_sprompt prompt default_pattern) 1))
		;else
			(= ret_code (get_parm 0 pattern prompt))
		)
		(if ret_code
			(
				(if (&& (strlen default_pattern) (strlen pattern))
					(
						(push_back (atoi pattern 0))
						(= ret_code (get_parm 0 pattern prompt))
					)
				)
				(if ret_code
					(
						(if (! (strlen pattern))
							(= pattern default_pattern)
						;else
							(= _t_pat pattern)
						)
						(translate pattern NULL NULL _reg_exp)
					)
				)
			)
		)
		(set_msg_level old_msg_level)
	)
)

;**
;**		toggle_re:
;**
;**		This macro toggles the state of the regular expression global
;**	variable, which controls whether regular expressions are used during
;**	search and translate.
;**

(macro toggle_re
	(
		(= _reg_exp (! _reg_exp))

		(if _reg_exp
			(message "Regular expressions on.")
		;else
			(message "Regular expressions off.")
		)
	)
)

;**
;**		_sprompt:
;**
;**		This routine ensures that the prompt created by the previous pattern
;**	search code isn't too large for the display area.
;**

(macro _sprompt
	(
		(string		part_1
						part_2
		)
		(int			len)

		(get_parm 0 part_1)
		(get_parm 1 part_2)

		(if (> (= len (strlen (+ part_1 part_2))) 47)
			(= part_2 (substr part_2 (- len 46)))
		)
		(returns (+ part_1 part_2))
	)
)

;**
;**		search_again:
;**
;**		This macro uses the information provided by fwd_search and back_search
;**	to search for the same pattern again, in the same direction.
;**

(macro search_again
	(
		(int		line
					col
					ret_code
					old_msg_level
		)
		(= old_msg_level (inq_msg_level))
		(set_msg_level 0)

		(if (strlen _s_pat)
			(
				(inq_position line col)

				(if (== _dir BACK)
					(
						(prev_char)
						(= ret_code (search_back _s_pat _reg_exp))
					)
				;else
					(
						(next_char)
						(= ret_code (search_fwd _s_pat _reg_exp))
					)
				)
				(if (! ret_code)
					(move_abs line col)
				)
			)
		;else
			(
				(error "No previous search pattern.")
				(beep)
			)
		)
		(set_msg_level old_msg_level)
	)
)

;**
;**		delete:
;**
;**		This macro implements block-sensitive delete.
;**

(macro delete
	(
		(if (inq_marked)
			(
				(int		old_msg_level)

				(= old_msg_level (inq_msg_level))
				(set_msg_level 0)
				(delete_block)
				(set_msg_level old_msg_level)
			)
		;else
			(delete_char)
		)
	)
)

;**
;**		open_line:
;**
;**		Adds a blank line after the current line, placing the cursor on the
;**	first column of the new line.  The normal macro for the Enter key is
;**	used to perform the function after the end of line.
;**

(macro open_line
	(
		(end_of_line)

		(if (== (inq_assignment "#13") "self_insert")
			(insert "\n")
		;else
			(execute_macro (inq_assignment "#13"))
		)
	)
)

;**
;**		_do_beep:
;**
;**		This macro calls the beep primitive -- beep cannot be registered.
;**

(macro _do_beep
	(beep)
)

;**
;**		_exit:
;**
;**		Performs the same function as exit, but does not register as
;**	a context-sensitive help macro.  No name beginning with _ does.
;**

(macro _exit
	(exit)
)

;**
;**		These macros, along with the menu package found in "menus.m",
;**	implements the BRIEF help facility.
;**

(macro help
	(
		(int		_help_done)

		(global	_help_done)

		(string	dir)

		(= dir (inq_environment "BHELP"))

		(if (= _help_done (strlen dir))
			(
				(if (! (index ":\\/" (substr dir _help_done)))
					(+= dir "/")
				)
				(= _help_done 0)
			)
		;else
			(= dir "/brief/help/")
		)
		(message "Entering help...")

		(if (_menu_process "help.mnu" "Help" dir)
			(error "No help files found in %s." (substr dir 1 (- (strlen dir) 1)))
		)
	)
)

(macro _context_help
	(
		(= _help_done 1)
		(_help_file (inq_command) "Help" (inq_command))
	)
)

;**
;**		_help_file:
;**
;**		_help_file takes the name of a help file in the BHELP directory,
;**	parses off the standard header information, and displays it in a window.
;**

(macro _help_file
	(
		(int			orig_buffer
						file_buffer
						len
		)
		(string		msg
						file_name
		)
		(= file_name (inq_environment "BHELP"))

		(if (= len (strlen file_name))
			(
				(if (index ":\\/" (substr file_name len))
					(+= file_name "help")
				;else
					(+= file_name "/help")
				)
			)
		;else
			(= file_name "/brief/help/help")
		)
		(if (= file_buffer (create_buffer "Help File" file_name 1))
			(
				(= orig_buffer (inq_buffer))
				(set_buffer file_buffer)
		
				(get_parm 0 msg)
				(sprintf msg "%s" msg)
				(move_abs 1 1)
		      (message "Locating help text...")
		
				(if (search_fwd msg)
					(
						(get_parm 1 msg)
						(get_parm 2 msg)
		 				(message "Help display for %s." msg)
						(create_window 2 21 76 2 "Grey - for previous menu, Esc to exit")
						(move_rel 1 -100)
						(attach_buffer file_buffer)
		
						(keyboard_push)
						(assign_to_key "%#81" "_help_down")
						(assign_to_key "%#73" "_help_up")
						(assign_to_key "#27" "_help_exit")
						(assign_to_key "#18989" "_exit")
						(refresh)
						(process)
						(keyboard_pop)
		
						(if (inq_views)
							(delete_window)
						)
					)
				)
				(set_buffer orig_buffer)
			)
		;else
			(error "No help file found.")
		)
	)
)

;**
;**		_help_down:
;**
;**		_help_down is called when the PgDn key is pressed:  it makes sure that
;**	the user does not go past the end of the help file.
;**

(macro _help_down
	(
		(int			line
						col
						old_line
						old_col
		)
		(inq_position old_line old_col)
		(save_position)
		(search_fwd "")
		(inq_position line col)
		(restore_position)

		(page_down)

		(if (<= (- line old_line) 18)
			(move_abs old_line old_col)
		)
	)
)

;**
;**		_help_up:
;**
;**		Goes up a page in the help file.  DOES NOT REASSIGN CURRENT COMMAND.
;**

(macro _help_up
	(
		(int			line
						col
						old_line
						old_col
		)
		(inq_position old_line old_col)
		(save_position)
		(search_back "")
		(inq_position line col)
		(restore_position)

		(page_up)

		(if (<= (- old_line line) 18)
			(move_abs old_line old_col)
		)
	)
)

;**
;**		_help_exit:
;**
;**		Exits help and all menu levels, if any.
;**

(macro _help_exit
	(
		(if (! _help_done)
			(_menu_exit)
		)
		(exit)
		(= _help_done 1)
	)
)

;**
;**		_help_keys:
;**
;**		Gets help for the user using the keys, not the menus.
;**

(macro _help_keys
	(
		(int		key_value
					saved_map
					done
		)
		(string	to_check
					this_key
					binding
		)
		(= done 0)
		(= saved_map (inq_keyboard))
		(keyboard_pop 1)

		(while (! done)
			(
				(message "Press the key you want help on, Esc to exit.")
				(= binding "food")
				(= to_check "")

				(while (&& (! done) (|| (== binding "ambiguious") (== binding "food")))
					(
						(while (== (= key_value (read_char)) -1))

						(if (! key_value)
							(
								(sprintf this_key "%%#%d" (read_char))
								(+= to_check this_key)
								(= binding (inq_assignment to_check))
							)
						;else
							(if (!= key_value 283)
								(
									(while (&& (|| (== binding "nothing") (== binding "food")) (> key_value 256))
										(
											(sprintf this_key "#%d" key_value)
											(= binding (inq_assignment (+ to_check this_key)))
											(%= key_value 256)
										)
									)
									(if (!= binding "nothing")
										(+= to_check this_key)
									)
								)
							;else
								(= done 1)
							)
						)
					)
				)
				(if (! done)
					(
						(if (!= binding "nothing")
							(
 								(_help_file binding "Help" binding)
 								(= done _help_done)
 								(refresh)
							)
						;else
							(if (== binding "nothing")
								(beep)
							;else
								(message "Press next key in sequence, Esc to exit.")
							)
						)
					)
				)
			)
		)
		(message "Select an item with the return key.")
		(keyboard_push saved_map)
	)
)
