;** Refer to a place in a buffer with a word. Spot is identified by a fully
;** qualified file name, line and colume counts and the line count of the
;** uppermost line.
;** 
;** Hang the "goto word" on top of the "goto" function. Differentiate by
;** checking for numeric. If an empty return is pressed, display a menu of
;** mark words with their corresponding line contents (if the file is being
;** editted).
;** Gotomark will ask the user if it's ok to load a file that is not current.
;** 
;** Save the mark words and positions when exiting Brief. Reload on the first
;** mark or gotomark call. Use an environment variable to name the save file.
;** 
;** Use the init macro call to setup the key assignments. Don't load the save
;** file until the facility is used. Don't slow the startup if marks are not
;** used.

#include "dialog.h"
#define	ESCAPE	283

(macro _init
	(
		(assign_to_key "<Ctrl-m>" "mark_spot")
		(assign_to_key "<Alt-g>" "goto_spot")
	)
)

(macro mark_spot
	(
		(string	filename
					mark_name
					mark_string
					mark_file)
		(int		window_buffer
					top_line
					curr_line
					curr_col)

		(if (! (get_parm 0 mark_name "Mark name? "))
			(
				(message "Command cancelled.")
				(return)
			)
		)
		(if (check_name mark_name) (return))
		;** Find the current position. Also find the top line so one can
		;** restore the exact screen position when one returns.
		(inq_position curr_line curr_col)
		(top_of_window)
		(inq_position top_line)
		(move_abs curr_line curr_col)
		(inq_names mark_file)

		(if (! (= window_buffer (make_spot_current))) (return))

		;** Delete any old tags with the same name
		(if (> (find_mark mark_name) 0) (delete_line))

		(end_of_buffer)
		(insert mark_name)
		; Only show the mark name in the dialog box
		(insert "                    ")
		(sprintf mark_string "%s %d %d %d \n" mark_file top_line curr_line 
			curr_col)
		(insert mark_string)

		(set_buffer window_buffer) ; All done, return to the original buffer.
	)
)

(macro make_spot_current
	(
		(string	filename)
		(int		mark_buffer
					window_buffer)
		(global	mark_buffer)

		(= window_buffer (inq_buffer)) ; return to this buffer when done.
		;** Make the mark buffer current. If this is the first time, create
		;** the buffer.
		(if (first_time)
			(
				;** If a restore file is not specified, use a default
				(if (== (= filename (lower (inq_environment "MARKSPOT"))) "")
					(= filename "\\brief\\markspot.rst")
					)
				(if (! (= mark_buffer (create_buffer "markspot.rst" filename 1)))
					(
						(error "Invalid MARKSPOT file name. Markspot disabled")
						(return 0)
					)
				)
			)
		)
		(set_buffer mark_buffer)
		(return window_buffer)
	)
)

(macro goto_spot
	(
		(string	mark_name
					mark_file)
		(int		line_number
					top_line
					curr_line
					curr_col
					window_buffer
					markspot_line)
		(global	markspot_line)

		(if (! (get_parm 0 mark_name "Go to line/mark: "))
			(
				(message "Command cancelled.")
				(return)
			)
		)
		(if (= line_number (atoi mark_name))
			(goto_line line_number) ;** If it's numeric, go to it
		; else
			(
				(if (! (= window_buffer (make_spot_current))) (return))

				(if (== "" mark_name)
					(
						(end_of_buffer)
						(inq_position curr_line)
						(if (== 1 curr_line)
							(
								(error "No marks to display.")
								; Return to the original buffer.
								(set_buffer window_buffer)
								(return)
							)
						)
						;** Get rid of the trailing newline placed there by 
						;** create_buffer
						(delete_char)

						(= markspot_line 0)
						(_process_menu 30 12 50 5 "Mark names" "Select a mark" 
							NULL mark_buffer "pick_spot" 1)
						(end_of_buffer)
						(insert "\n") ;** put it back
						(if (! markspot_line)
							(
								(set_buffer window_buffer)
								(return)
							)
						)
						(move_abs markspot_line 1)
					)
				; else
					(if (<= (find_mark mark_name) 0)
						(
							(error "Mark '%s' not found." mark_name)
							; Return to the original buffer.
							(set_buffer window_buffer)
							(return)
						)
					)
				)

				(get_mark_pos mark_name mark_file top_line curr_line curr_col)
				(if (quick_edit_file mark_file)
					(
						(set_top_left top_line 1)
						(move_abs curr_line curr_col)
					)
				)
			)
		)
	)
)

;** Called by dialog manager to pick a mark name
(macro pick_spot
	(
		(int		event_type)

		(get_parm 0 event_type)
		(switch event_type
			DIALOG_PICK_MENU
				(
					(get_parm 1 markspot_line)
					(push_back ESCAPE) ; Fool the dialog mgr into finishing
				)
		)
		(returns TRUE)
	)
)

;** returns <= 0 if mark NOT found, > 0 otherwise. The first param is the 
;** name of the mark to find.
(macro find_mark
	(
		(string	mark_name)

		(get_parm 0 mark_name)
		(move_abs 1 1)
		(return (search_fwd (+ "<" (+ mark_name " "))))
	)
)

;** Checks parm 0 for a valid name All numeric OR 1 alpha+n alpha numeric OR
;** a return. (leading blanks will be trimmed however)
(macro check_name
	(
		(string	name)

		(get_parm 0 name)
		(put_parm 0 (= name (ltrim name)))

		(if (! (search_string "[~0-9 ][~ ]@" name))
			(
				(error "Invalid mark name '%s'." name)
				(return 1)
			)
		)
		(return 0)
	)
)

;** Returns: mark_name mark_file top_line curr_line curr_col
(macro get_mark_pos
	(
		(put_parm 0 (get_string))
		(put_parm 1 (get_string))
		(put_parm 2 (get_int))
		(put_parm 3 (get_int))
		(put_parm 4 (get_int))
	)
)

(macro get_string
	(
		(string	word
					c)

		(while (== " " (read 1)) (next_char)) ; skip blanks
		(while (!= " " (= c (read 1))) 
			(
				(+= word c)
				(next_char)
			)
		)
		(next_char)
		(return word)
	)
)

(macro get_int
	(
		(string	word
					c)

		(while (== " " (read 1)) (next_char)) ; skip blanks
		(while (!= " " (= c (read 1))) 
			(
				(+= word c)
				(next_char)
			)
		)
		(next_char)
		(return (atoi word))
	)
)

;** Call this to save the spot file for future use.
(macro save_spot
	(
		(string	filename)
		(int		old_buffer)

		(if (!= 0 mark_buffer)	
			(
				(= old_buffer (make_spot_current))
				(if (<= (write_buffer) 0) (error "Write of spot file failed"))
				(set_buffer old_buffer)
			)
		)
	)
)

;** quick_edit_file looks for a current buffer with the file specified in
;** parm 0. If none is found, the user is prompted if they wish to perform
;** a normal edit-file on it.	(edit_file invokes too many macros to be used
;** indiscriminantly.
(macro quick_edit_file
	(
		(string	filename
					filename_of_buf
					prompt
					ans)
		(int		starting_buffer
					current_buffer)

		(get_parm 0 filename)
		(= starting_buffer (next_buffer)) ; Get out of the current (system) buf
		(set_buffer starting_buffer)
		(inq_names filename_of_buf)
		;** If the first buffer is it, switch to it.
		(if (== filename filename_of_buf)
			(
				(attach_buffer starting_buffer)
				(return 1)
			)
		)
		;** Scan for the buffer requested
		(while (!= starting_buffer (= current_buffer (next_buffer)))
			(
				(set_buffer current_buffer)
				(inq_names filename_of_buf)
				(if (== filename filename_of_buf)
					(
						(attach_buffer current_buffer)
						(return 1)
					)
				)
			)
		)
		;** Ask the user if they wish to edit the file.
		(= prompt (+ (+ "Edit " filename) "? "))
		(if (|| (! (get_parm 1 ans prompt)) (!= ans "y"))
			(
				(message "Goto mark cancelled.")
				(return 0)
			)
		)
		(edit_file filename)
		(return 1)
	)
)
