Un-ignored: the dev drive is the ground truth the restoration and emulator work constantly reference (DPL3/LIBDPL + VRENDER i860 renderer source, BT/RP live+dev game trees, VGL_LABS pod boot, scene/audio content). Kept in-repo for the pod-owner community. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1769 lines
41 KiB
Objective-C
1769 lines
41 KiB
Objective-C
;**
|
|
;** BRIEF -- Basic Reconfigurable Interactive Editing Facility
|
|
;**
|
|
;** Written by Dave Nanian and Michael Strickman.
|
|
;**
|
|
|
|
;**
|
|
;** dialog.m:
|
|
;**
|
|
;** This file contains all of the standard BRIEF macros for the
|
|
;** dialog and menu manager.
|
|
;**
|
|
;** Revision history:
|
|
;** -----------------
|
|
;**
|
|
|
|
#include "dialog.h"
|
|
|
|
;**
|
|
;** Dialog Manager Package: Note to Macro Programmers
|
|
;**
|
|
;** Input to an interactive macro must ultimately come from the
|
|
;** keyboard. In addition to the primitive calls for keyboard
|
|
;** handling (assign_to_key, get_parm, read_char, etc.) we have
|
|
;** provided two high-level calls which provide a simple, consistent,
|
|
;** and attractive user interface. We strongly recommend that you
|
|
;** use the built-in dialog manager calls in your macros instead of
|
|
;** writing your own user interface.
|
|
;**
|
|
|
|
(extern _exit
|
|
add_to_path
|
|
to_bottom
|
|
center_line
|
|
)
|
|
|
|
;**
|
|
;** init:
|
|
;**
|
|
;** Initialize the dialog manager package. This must always be
|
|
;** the first call to the dialog manager; it is run whenever the
|
|
;** file is loaded.
|
|
;**
|
|
|
|
(macro _init
|
|
(
|
|
(if (first_time)
|
|
(
|
|
(int _dialog_level
|
|
_dialog_type
|
|
_dialog_row
|
|
_dialog_col
|
|
_dialog_field_keymap
|
|
_dialog_list_keymap
|
|
_dialog_menu_keymap
|
|
_dialog_size
|
|
_dialog_data_buf
|
|
_dialog_disp_buf
|
|
_dialog_mode
|
|
)
|
|
(string _dialog_action_func
|
|
_dialog_dir
|
|
)
|
|
(global _dialog_action_func
|
|
_dialog_dir
|
|
_dialog_level
|
|
_dialog_type
|
|
_dialog_row
|
|
_dialog_col
|
|
_dialog_field_keymap
|
|
_dialog_list_keymap
|
|
_dialog_menu_keymap
|
|
_dialog_size
|
|
_dialog_data_buf
|
|
_dialog_disp_buf
|
|
_dialog_mode
|
|
)
|
|
|
|
;** First we get the name of the directory in which the
|
|
;** menu files and help files are kept.
|
|
|
|
(if (! (strlen (= _dialog_dir (inq_environment "BHELP"))))
|
|
(= _dialog_dir "/brief/help")
|
|
)
|
|
|
|
;** We now create 3 keymaps; the only assignments they have
|
|
;** in common are the grey - and the Esc key.
|
|
|
|
;** Here we create a keymap for use with menus. Note that
|
|
;** at every level you can use the INIT and TERM hooks to
|
|
;** add your own key assignments to this keymap; just
|
|
;** remember to restore the old assignments, or your keys
|
|
;** will be wrong when you return to a lower-level menu.
|
|
|
|
(keyboard_push)
|
|
(keyboard_typeables)
|
|
(assign_to_key "<Down>" "_dialog_menu_down")
|
|
(assign_to_key "<Space>" "_dialog_menu_down")
|
|
(assign_to_key "<Up>" "_dialog_menu_up")
|
|
(assign_to_key "<Backspace>" "_dialog_menu_up")
|
|
(assign_to_key "<Enter>" "_dialog_menu_pick")
|
|
(assign_to_key "<Esc>" "_dialog_esc")
|
|
(assign_to_key "<Keypad minus>" "_dialog_grey_minus")
|
|
(assign_to_key "<Home>" "_dialog_menu_home")
|
|
(assign_to_key "<Ctrl-Home>" "_dialog_menu_home")
|
|
(assign_to_key "<End>" "_dialog_menu_end")
|
|
(assign_to_key "<Ctrl-End>" "_dialog_menu_end")
|
|
(assign_to_key "<PgUp>" "_dialog_menu_pgup")
|
|
(assign_to_key "<PgDn>" "_dialog_menu_pgdn")
|
|
(= _dialog_menu_keymap (inq_keyboard))
|
|
(keyboard_pop TRUE)
|
|
|
|
;** Here we create a keymap for use with lists. It also
|
|
;** contains the general field-to-field key assignments.
|
|
|
|
(keyboard_push)
|
|
(keyboard_typeables)
|
|
(assign_to_key "<F10>" "_dialog_f10")
|
|
(assign_to_key "#13" "_dialog_next_and_save")
|
|
(assign_to_key "<Esc>" "_dialog_esc")
|
|
(assign_to_key "<Keypad minus>" "_dialog_grey_minus")
|
|
(assign_to_key "<Tab>" "_dialog_next")
|
|
(assign_to_key "<Down>" "_dialog_next")
|
|
(assign_to_key "<Shift-Tab>" "_dialog_prev")
|
|
(assign_to_key "<Up>" "_dialog_prev")
|
|
(assign_to_key "<Ctrl-Home>" "_dialog_home")
|
|
(assign_to_key "<Ctrl-End>" "_dialog_end")
|
|
(assign_to_key "<Backspace>" "_list_prev")
|
|
(assign_to_key "<Left>" "_list_prev")
|
|
(assign_to_key "<Space>" "_list_next")
|
|
(assign_to_key "<Right>" "_list_next")
|
|
(assign_to_key "<Home>" "_list_home")
|
|
(assign_to_key "<End>" "_list_end")
|
|
(= _dialog_list_keymap (inq_keyboard))
|
|
(keyboard_pop TRUE)
|
|
|
|
;** Here we create a keymap for use with typed fields.
|
|
;** When we're done, we restore the previous keymap.
|
|
|
|
(keyboard_push)
|
|
(assign_to_key "<Backspace>" "_field_bksp")
|
|
(assign_to_key "<Space>" "self_insert")
|
|
(assign_to_key "<Left>" "_field_left")
|
|
(assign_to_key "<Right>" "_field_right")
|
|
(assign_to_key "<Del>" "_field_del")
|
|
(assign_to_key "<Home>" "_field_home")
|
|
(assign_to_key "<End>" "_field_end")
|
|
(assign_to_key "<Alt-k>" "_field_delete_to_eol")
|
|
(assign_to_key "<Alt-d>" "_field_del_line")
|
|
(assign_to_key "<Ins>" "insert_mode")
|
|
(assign_to_key "<Alt-i>" "insert_mode")
|
|
(= _dialog_field_keymap (inq_keyboard))
|
|
(keyboard_pop TRUE)
|
|
|
|
;** Here we register a macro that will be invoked
|
|
;** whenever a typed character is inserted. It calls
|
|
;** one of three special-purpose macros depending on
|
|
;** whether or not we were in a menu, list or typed field.
|
|
(register_macro 0 "_typeable_key")
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _remove_dialog_manager:
|
|
;**
|
|
;** You should call this macro whenever you are finished using the
|
|
;** dialog manager. It will free the keymaps created in dialog_manager
|
|
;** and unregister the macro used for going to a menu button by name.
|
|
;** It would be nice to have this function unload the macro file, but
|
|
;** that's for a future release.
|
|
;** Don't call this macro if you plan to use the dialog manager
|
|
;** again in the same session.
|
|
;**
|
|
|
|
(macro _remove_dialog_manager
|
|
(if (first_time)
|
|
(
|
|
(unregister_macro 0 "_typeable_key")
|
|
(keyboard_push _dialog_menu_keymap)
|
|
(keyboard_pop)
|
|
(keyboard_push _dialog_list_keymap)
|
|
(keyboard_pop)
|
|
(keyboard_push _dialog_field_keymap)
|
|
(keyboard_pop)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
;**
|
|
;** _dialog_f10, _dialog_esc, _dialog_grey_minus:
|
|
;**
|
|
;** Call the action function with an appropriate code and exit
|
|
;** from the dialog box, menu, or entire dialog manager.
|
|
;**
|
|
|
|
(macro _dialog_f10
|
|
(
|
|
;** When the F10 key is pressed, we have to verify the
|
|
;** current field before we can exit. This function is
|
|
;** only used in DIALOG_DBOX_MODE.
|
|
|
|
(if (&& (_dialog_exit) (execute_macro _dialog_action_func DIALOG_F10))
|
|
(_exit)
|
|
)
|
|
)
|
|
)
|
|
|
|
(macro _dialog_esc
|
|
(
|
|
(int curr_level)
|
|
|
|
(execute_macro _dialog_action_func DIALOG_ESCAPE)
|
|
(= curr_level _dialog_level)
|
|
|
|
(while curr_level
|
|
(
|
|
(_exit)
|
|
(-- curr_level)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
(macro _dialog_grey_minus
|
|
(
|
|
;** When the grey - key is pressed, we immediately exit one level.
|
|
|
|
(execute_macro _dialog_action_func DIALOG_GREY_MINUS)
|
|
(_exit)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _format_dialog:
|
|
;**
|
|
;** Parses the information buffer and formats the display buffer,
|
|
;** leaving the display buffer current.
|
|
;**
|
|
|
|
(macro _format_dialog
|
|
(
|
|
(int pos
|
|
open_paren
|
|
)
|
|
(string line)
|
|
|
|
(set_buffer _dialog_disp_buf)
|
|
(tabs 2)
|
|
(set_buffer _dialog_data_buf)
|
|
(inq_names NULL NULL line)
|
|
(execute_macro _dialog_action_func DIALOG_CREATE_DBOX NULL line)
|
|
(top_of_buffer)
|
|
|
|
;** Display all fields. We allow white space before the
|
|
;** format commands in the buffer.
|
|
|
|
(while (search_fwd "<[ \\t]@\\c[FLINTS]" TRUE FALSE)
|
|
(
|
|
(= pos (+ (index (= line (_dialog_set_globals)) "\"") 1))
|
|
(= line (substr line pos (- (rindex line "\"") pos)))
|
|
(next_char)
|
|
|
|
;** If we are formatting a list, we first make sure there's
|
|
;** a current item. If there isn't, we must skip over the
|
|
;** field since it could create serious errors later. If
|
|
;** there is a current item but it's not the first item,
|
|
;** we prepend a tab to the list so we'll be able to find
|
|
;** the first button. We also stick a tab at the end of the
|
|
;** list so we can find the end of the last item.
|
|
|
|
(if (== _dialog_type LIST)
|
|
(if (= open_paren (index line "("))
|
|
(
|
|
(if (> open_paren 1)
|
|
(= line (+ "\t" line))
|
|
)
|
|
(-- _dialog_col)
|
|
(+= line "\t")
|
|
)
|
|
)
|
|
;else
|
|
(= open_paren TRUE)
|
|
)
|
|
|
|
;** Likewise, if we encounter a field in the data buffer
|
|
;** that is too long, we delete it and skip over it
|
|
;** in the formatting process.
|
|
|
|
(= pos (- (+ _dialog_col (strlen line)) _dialog_size))
|
|
|
|
(if (|| (! open_paren) (> pos 0))
|
|
(
|
|
(if open_paren
|
|
(error "Field %d columns too wide for window." pos)
|
|
;else
|
|
(error "No current item in list.")
|
|
)
|
|
(delete_line)
|
|
(beginning_of_line)
|
|
)
|
|
;else
|
|
(
|
|
(set_buffer _dialog_disp_buf)
|
|
(move_abs _dialog_row _dialog_col)
|
|
|
|
;** This makes sure we're not already past end of line
|
|
;** if a field is blank to begin with.
|
|
|
|
(if (strlen line)
|
|
(insert line)
|
|
;else
|
|
(
|
|
(insert "\t")
|
|
(prev_char)
|
|
(delete_char)
|
|
)
|
|
)
|
|
(set_buffer _dialog_data_buf)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(top_of_buffer)
|
|
(search_fwd "<[ \\t]@\\c[FLINS]" TRUE FALSE)
|
|
(_dialog_enter)
|
|
)
|
|
)
|
|
|
|
|
|
;**
|
|
;** _process_dialog_box:
|
|
;**
|
|
;** Creates a dialog box window on the screen and a display
|
|
;** buffer to go in it. Makes sure an information buffer exists.
|
|
;** Calls _format_dialog to prepare the display buffer. Then makes
|
|
;** the first field in the dialog box current and highlights it.
|
|
;** Enters interactive mode, calling the user's action function
|
|
;** whenever "interesting" events occur.
|
|
;**
|
|
;** When the user exits, this function deletes the display buffer
|
|
;** and dialog box window, deletes the data buffer as well if that
|
|
;** buffer is a system buffer, pops the keymaps that were created,
|
|
;** restores the editing mode and tab/space mode, etc.
|
|
;**
|
|
|
|
(macro _process_dialog_box
|
|
(
|
|
(int lx
|
|
by
|
|
rx
|
|
ty
|
|
old_buf_id
|
|
created_buffer
|
|
former_type
|
|
former_row
|
|
former_col
|
|
former_size
|
|
former_data_buf
|
|
former_disp_buf
|
|
former_mode
|
|
)
|
|
(string pathname
|
|
former_action_func
|
|
)
|
|
|
|
;** Save the current value of the global variable _dialog_data_buf.
|
|
|
|
(= former_data_buf _dialog_data_buf)
|
|
|
|
;** If we are passed an existing buffer id, we use that buffer;
|
|
;** if not, but we are passed the name of an existing file,
|
|
;** we create the buffer. If we get neither parameter, we
|
|
;** fail.
|
|
|
|
(if (&& (! (get_parm 7 _dialog_data_buf)) (get_parm 6 pathname))
|
|
(
|
|
;** If the file name is absolute (which we determine by
|
|
;** the presence of special characters) we don't look for
|
|
;** it in the BHELP directory.
|
|
|
|
(if (! (search_string "[:/\\\\]" pathname NULL TRUE))
|
|
(= pathname (add_to_path _dialog_dir pathname))
|
|
)
|
|
|
|
;** If the file exists, we put it in a buffer.
|
|
|
|
(if (= created_buffer (exist pathname))
|
|
(= _dialog_data_buf
|
|
(create_buffer "Information" pathname TRUE))
|
|
)
|
|
)
|
|
)
|
|
|
|
;** Make sure that we have a valid buffer id at this point (we
|
|
;** don't care if both the pathname and buffer id were specified;
|
|
;** we just want to have something to display).
|
|
|
|
(if (! _dialog_data_buf)
|
|
(
|
|
(error "Can't create dialog box.")
|
|
(= _dialog_data_buf former_data_buf)
|
|
(return FALSE)
|
|
)
|
|
)
|
|
|
|
(message "Creating dialog box...")
|
|
|
|
;** Here we save the former value of _dialog_action_func
|
|
;** and increment the _dialog_level counter so recursive
|
|
;** invocations will know about this one. We also save
|
|
;** the values of all the other global variables.
|
|
|
|
(++ _dialog_level)
|
|
(= former_action_func _dialog_action_func)
|
|
(= former_type _dialog_type)
|
|
(= former_row _dialog_row)
|
|
(= former_col _dialog_col)
|
|
(= former_size _dialog_size)
|
|
(= former_disp_buf _dialog_disp_buf)
|
|
(= former_mode _dialog_mode)
|
|
|
|
(= _dialog_mode DIALOG_DBOX_MODE)
|
|
|
|
(= old_buf_id (inq_buffer))
|
|
|
|
;** Here we create a non-file system buffer which we
|
|
;** can display in the new window. This buffer will be
|
|
;** formatted by the information in _dialog_data_buf.
|
|
;** At this point, the pathname is no longer needed, so we
|
|
;** put the string to new uses (getting the buffer name
|
|
;** and later the window-bottom message).
|
|
|
|
(get_parm 4 pathname)
|
|
(= _dialog_disp_buf (create_buffer pathname NULL TRUE))
|
|
|
|
(get_parm 0 lx)
|
|
(get_parm 1 by)
|
|
(get_parm 2 rx)
|
|
(get_parm 3 ty)
|
|
(get_parm 5 pathname)
|
|
(get_parm 8 _dialog_action_func)
|
|
(= _dialog_size (- (- rx lx) 1))
|
|
(keyboard_push _dialog_list_keymap)
|
|
|
|
;** Now we call _format_dialog to design the display buffer
|
|
;** and we create a window to put it in. We postpone
|
|
;** refreshing the display.
|
|
|
|
(_format_dialog)
|
|
(create_window lx by rx ty pathname)
|
|
(attach_buffer _dialog_disp_buf)
|
|
|
|
;** We save the old value of Use Tab Characters in the
|
|
;** pathname variable, and the old Editing Mode in lx.
|
|
|
|
(= lx (inq_mode))
|
|
(= pathname (use_tab_char "n"))
|
|
|
|
;** Call the action function to indicate we are done initializing.
|
|
;** This is a good place for you to put your own keys into the
|
|
;** keymap; by default, your key assignments will go into the
|
|
;** global keymap for both lists and typed fields, but you may
|
|
;** select one of the local keymaps and put assignments into it
|
|
;** as long as you change back. A typical use of this feature
|
|
;** would be to install a help function by assigning it to Alt-h.
|
|
;** Don't try to change the basic functioning of either the
|
|
;** lists or the fields, however.
|
|
|
|
(message "Dialog box created.")
|
|
(execute_macro _dialog_action_func DIALOG_INIT)
|
|
(refresh)
|
|
(process)
|
|
|
|
(execute_macro _dialog_action_func DIALOG_TERM)
|
|
|
|
;** When we get here (after the user presses one of the three
|
|
;** keys F10, Esc, and Grey -) we start cleaning up. First
|
|
;** we unassociate any current local keymap and pop the
|
|
;** list keymap. We preserve both keymaps for future use.
|
|
|
|
(use_local_keyboard 0)
|
|
(keyboard_pop TRUE)
|
|
|
|
;** Now we restore some modes.
|
|
|
|
(if (!= lx (inq_mode))
|
|
(insert_mode)
|
|
)
|
|
(use_tab_char pathname)
|
|
|
|
;** After processing the dialog box, we restore the
|
|
;** previous state and return TRUE. We only delete the
|
|
;** information buffer if we created it and if it is
|
|
;** not visible in any windows. We delete the display
|
|
;** buffer regardless.
|
|
|
|
(delete_window)
|
|
(set_buffer _dialog_data_buf)
|
|
|
|
(if (&& created_buffer (! (inq_views)))
|
|
(delete_buffer _dialog_data_buf)
|
|
)
|
|
(delete_buffer _dialog_disp_buf)
|
|
(set_buffer old_buf_id)
|
|
|
|
;** Decrement the dialog level and restore the values of the
|
|
;** globals. If the level becomes zero, we're leaving the
|
|
;** dialog manager for good, so we re-run the new file
|
|
;** macros and clear the message line.
|
|
|
|
(if (! (-- _dialog_level))
|
|
(
|
|
(call_registered_macro 1)
|
|
(message "")
|
|
)
|
|
)
|
|
(= _dialog_action_func former_action_func)
|
|
(= _dialog_type former_type)
|
|
(= _dialog_row former_row)
|
|
(= _dialog_col former_col)
|
|
(= _dialog_size former_size)
|
|
(= _dialog_disp_buf former_disp_buf)
|
|
(= _dialog_data_buf former_data_buf)
|
|
(= _dialog_mode former_mode)
|
|
(returns TRUE)
|
|
)
|
|
)
|
|
|
|
|
|
;**
|
|
;** _dialog_next_and_save:
|
|
;**
|
|
;** Acts just like _dialog_next, but when invoked on the last field
|
|
;** in the dialog manager, simulates a press of F10.
|
|
;**
|
|
|
|
(macro _dialog_next_and_save
|
|
(
|
|
;** If we can leave the current field (the input is valid),
|
|
;** we look for another field to enter. If there is none,
|
|
;** we pretend F10 was pressed, and we signal to save the screen.
|
|
|
|
(if (_dialog_exit)
|
|
(
|
|
(set_buffer _dialog_data_buf)
|
|
(end_of_line)
|
|
|
|
;** If we have no other field to go to, we pretend F10 was
|
|
;** pressed; if it's OK to exit, we exit one process level.
|
|
;** If we can't exit, we reenter the same field. (If we
|
|
;** have another field to begin with, we enter it.)
|
|
|
|
(if (! (search_fwd "<[ \\t]@\\c[FLINS]" TRUE FALSE))
|
|
(if (execute_macro _dialog_action_func DIALOG_F10)
|
|
(
|
|
(_exit)
|
|
(return TRUE)
|
|
)
|
|
;else
|
|
(beginning_of_line)
|
|
)
|
|
)
|
|
(_dialog_enter)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_next, _dialog_prev, _dialog_home, _dialog_end:
|
|
;**
|
|
;** These four macros move the highlight to another field and set
|
|
;** all the global variables for that field. There is some redundant
|
|
;** code, because performance is fairly important.
|
|
;**
|
|
|
|
(macro _dialog_next
|
|
(if (_dialog_exit)
|
|
(
|
|
(set_buffer _dialog_data_buf)
|
|
|
|
;** Find the line on which the desired field is defined.
|
|
|
|
(end_of_line)
|
|
(if (! (search_fwd "<[ \\t]@\\c[FLINS]" TRUE FALSE))
|
|
(beginning_of_line)
|
|
)
|
|
(_dialog_enter)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
(macro _dialog_prev
|
|
(if (_dialog_exit)
|
|
(
|
|
(set_buffer _dialog_data_buf)
|
|
(if (! (&& (up) (search_back "<[ \\t]@\\c[FLINS]" TRUE FALSE)))
|
|
(down)
|
|
)
|
|
(_dialog_enter)
|
|
)
|
|
)
|
|
)
|
|
|
|
(macro _dialog_home
|
|
(if (_dialog_exit)
|
|
(
|
|
(set_buffer _dialog_data_buf)
|
|
(top_of_buffer)
|
|
(search_fwd "<[ \\t]@\\c[FLINS]" TRUE FALSE)
|
|
(_dialog_enter)
|
|
)
|
|
)
|
|
)
|
|
|
|
(macro _dialog_end
|
|
(if (_dialog_exit)
|
|
(
|
|
(set_buffer _dialog_data_buf)
|
|
(end_of_buffer)
|
|
(search_back "<[ \\t]@\\c[FLINS]" TRUE FALSE)
|
|
(_dialog_enter)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_set_globals:
|
|
;**
|
|
;** This macro sets the global variables _dialog_type, _dialog_row,
|
|
;** and _dialog_col by reading information from the data buffer. It
|
|
;** returns the line read.
|
|
;**
|
|
|
|
(macro _dialog_set_globals
|
|
(
|
|
(string line)
|
|
|
|
;** We require that the row and column numbers follow
|
|
;** the first ( and , in the string, respectively,
|
|
;** although there may be whitespace in between. The
|
|
;** only two double quotes in the string must surround
|
|
;** the text of the field.
|
|
|
|
(= _dialog_type (index "FLINTS" (upper (substr (= line (read)) 1 1))))
|
|
(= _dialog_row (atoi (substr line (+ (index line "(") 1))))
|
|
(= _dialog_col (atoi (substr line (+ (index line ",") 1))))
|
|
(returns line)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_enter:
|
|
;**
|
|
;** Sets up to enter a list or a field based on the data buffer
|
|
;** and global variables.
|
|
;**
|
|
|
|
(macro _dialog_enter
|
|
(
|
|
(int offset
|
|
len
|
|
)
|
|
(string field)
|
|
|
|
(_dialog_set_globals)
|
|
(set_buffer _dialog_disp_buf)
|
|
(move_abs _dialog_row _dialog_col)
|
|
|
|
;** We perform the appropriate initializations and highlighting
|
|
;** depending on the type of the field. If we are entering
|
|
;** a list, we may have to reset _dialog_col to compensate for
|
|
;** an initial tab.
|
|
|
|
(if (== _dialog_type LIST)
|
|
(
|
|
(insert_mode TRUE)
|
|
(-- _dialog_col)
|
|
(prev_char)
|
|
(= field (read))
|
|
(= len (- (- (index field ")") (= offset (index field "("))) 1))
|
|
(move_rel 0 (- offset 1))
|
|
(delete_char)
|
|
(insert "\t")
|
|
(move_rel 0 len)
|
|
(delete_char)
|
|
(insert "\t")
|
|
(move_rel 0 -2)
|
|
(drop_anchor)
|
|
(move_rel 0 (- 1 len))
|
|
(execute_macro _dialog_action_func DIALOG_ENTER_LIST _dialog_row
|
|
(substr field (++ offset) len))
|
|
)
|
|
|
|
;else
|
|
(
|
|
;** Highlight the field and call the action function.
|
|
(use_local_keyboard _dialog_field_keymap)
|
|
(end_of_line)
|
|
(drop_anchor)
|
|
(move_abs 0 _dialog_col)
|
|
(execute_macro _dialog_action_func DIALOG_ENTER_FIELD _dialog_row
|
|
(_field_contents))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_exit:
|
|
;**
|
|
;** This routine is called whenever the user is leaving a field or
|
|
;** a list. First, if the user is leaving a typed field, it calls the
|
|
;** built-in data validation routine for that type. If the data is
|
|
;** invalid, it prints an error message and returns FALSE. If the data
|
|
;** is valid, it calls the action function with either the list or field
|
|
;** parameter and the value of the list or field. If the action function
|
|
;** returns TRUE (i.e. the field's contents were still valid), it
|
|
;** removes the highlight (if any), and unassociates the current local
|
|
;** keymap. Returns what the action function returned.
|
|
;**
|
|
|
|
(macro _dialog_exit
|
|
(
|
|
(int retval)
|
|
|
|
(string value)
|
|
|
|
(if (== _dialog_type LIST)
|
|
(
|
|
;** Note that in the execute_macro call below, if the action
|
|
;** function never gets its last parameter, _list_button
|
|
;** never gets called.
|
|
|
|
(if (= retval (execute_macro _dialog_action_func DIALOG_EXIT_LIST
|
|
_dialog_row (_list_button)))
|
|
(
|
|
;** Change the highlight back to parens.
|
|
|
|
(raise_anchor)
|
|
(prev_char)
|
|
(translate "\\t{*}\\t" "(\\0)" FALSE TRUE)
|
|
)
|
|
)
|
|
)
|
|
|
|
;else
|
|
(
|
|
(= value (_field_contents))
|
|
(if (= retval (&& (_dialog_validate value)
|
|
(execute_macro _dialog_action_func DIALOG_EXIT_FIELD _dialog_row value)))
|
|
(
|
|
(raise_anchor)
|
|
(use_local_keyboard 0)
|
|
(message "")
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(returns retval)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _list_next, _list_prev:
|
|
;**
|
|
;** Moves the mark from one button to the next or previous button
|
|
;** on a list. Lists do not wrap.
|
|
;**
|
|
|
|
(macro _list_next
|
|
(
|
|
(raise_anchor)
|
|
(save_position)
|
|
(search_fwd "{\\t\\c[~\\t]}|\\n" TRUE)
|
|
(restore_position (at_eol))
|
|
(_list_highlight)
|
|
)
|
|
)
|
|
|
|
(macro _list_prev
|
|
(
|
|
(int col
|
|
len
|
|
)
|
|
(raise_anchor)
|
|
(inq_position NULL col)
|
|
|
|
;** If we move past the beginning of the list when we move two
|
|
;** columns left, there is no previous button. If we are still
|
|
;** in the list, we are at the end of the previous button.
|
|
;** All we have to do is move one past the previous tab character.
|
|
;** This we do in 2 steps because we have to read, starting at
|
|
;** the beginning of the list.
|
|
|
|
(if (> (= len (- (-= col 2) _dialog_col)) 0)
|
|
(
|
|
(move_abs 0 _dialog_col)
|
|
(move_rel 0 (rindex (read len) "\t"))
|
|
)
|
|
)
|
|
(_list_highlight)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _list_highlight:
|
|
;**
|
|
;** Highlights the list button where the cursor is. Also, since
|
|
;** this function is called by all the functions that change the value
|
|
;** of a list, calls the action function with that code.
|
|
;**
|
|
|
|
(macro _list_highlight
|
|
(
|
|
(string value)
|
|
|
|
(int len)
|
|
|
|
(= len (- (index (= value (read)) "\t") 2))
|
|
(move_rel 0 len)
|
|
(drop_anchor)
|
|
(move_rel 0 (- 0 len))
|
|
(execute_macro _dialog_action_func DIALOG_ALTER_LIST _dialog_row (substr value 1 len))
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _list_home, _list_end:
|
|
;**
|
|
;** Moves the highlight to the first or last button on a list.
|
|
;**
|
|
|
|
(macro _list_home
|
|
(
|
|
(move_abs 0 _dialog_col)
|
|
(_list_next)
|
|
)
|
|
)
|
|
|
|
(macro _list_end
|
|
(
|
|
(end_of_line)
|
|
(_list_prev)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _list_go:
|
|
;**
|
|
;** Reads a character (that was just inserted; _list_go is
|
|
;** called as a type 0 registered macro) from the buffer and deletes
|
|
;** it, then looks for a matching button before the end of the list.
|
|
;** If there is none, looks for one starting with the first item.
|
|
;** If there is still none, stays put. (If a button is found,
|
|
;** _list_go moves to it and highlights it.)
|
|
;**
|
|
;** Note that alphabetic keys pressed will only find buttons
|
|
;** beginning with the upper case versions of the letters.
|
|
;**
|
|
|
|
(macro _list_go
|
|
(
|
|
(int col
|
|
offset
|
|
)
|
|
(string pattern)
|
|
|
|
(raise_anchor)
|
|
(prev_char)
|
|
(= pattern (+ "\t" (upper (read 1))))
|
|
(delete_char)
|
|
|
|
(if (= offset (index (read) pattern))
|
|
(move_rel 0 offset)
|
|
;else
|
|
(
|
|
(save_position)
|
|
(move_abs 0 _dialog_col)
|
|
(if (= offset (index (read) pattern))
|
|
(move_rel 0 offset)
|
|
;else
|
|
(beep)
|
|
)
|
|
(restore_position (! offset))
|
|
)
|
|
)
|
|
(_list_highlight)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _typeable_key:
|
|
;**
|
|
;** For dialog boxes, if we are in a list, calls _list_go. Otherwise,
|
|
;** calls _field_insert. For menus, calls _menu_go. For other cases,
|
|
;** does nothing.
|
|
;**
|
|
|
|
(macro _typeable_key
|
|
(if _dialog_mode
|
|
(if (== _dialog_mode DIALOG_MENU_MODE)
|
|
(_menu_go)
|
|
;else
|
|
(if (== _dialog_type LIST)
|
|
(_list_go)
|
|
;else
|
|
(_field_insert)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _field_insert:
|
|
;**
|
|
;** Makes sure that there's still room left in the window; if not,
|
|
;** removes the last key inserted, beeps and prints an error message.
|
|
;** Handles both insert and overstrike mode.
|
|
;**
|
|
|
|
(macro _field_insert
|
|
(
|
|
(int last_col)
|
|
|
|
(if (inq_marked)
|
|
(
|
|
(raise_anchor)
|
|
(delete_to_eol)
|
|
)
|
|
)
|
|
(save_position)
|
|
|
|
(if (inq_mode)
|
|
(end_of_line)
|
|
)
|
|
(inq_position NULL last_col)
|
|
(restore_position)
|
|
|
|
(if (>= last_col _dialog_size)
|
|
(
|
|
(prev_char)
|
|
(delete_char)
|
|
(error "Field is full.")
|
|
(beep)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _field_bksp:
|
|
;**
|
|
;** Backs the cursor up in a typed field (if it's not already at
|
|
;** the beginning of the field) and deletes it. In overstrike
|
|
;** mode, this function replaces the deleted character with a space.
|
|
;**
|
|
|
|
(macro _field_bksp
|
|
(
|
|
(int cur_col)
|
|
|
|
(inq_position NULL cur_col)
|
|
|
|
(if (inq_marked)
|
|
(
|
|
(raise_anchor)
|
|
(delete_to_eol)
|
|
)
|
|
)
|
|
|
|
(if (> cur_col _dialog_col)
|
|
(
|
|
(left)
|
|
(if (! (at_eol))
|
|
(
|
|
(delete_char)
|
|
(if (! (inq_mode))
|
|
(
|
|
(insert " ")
|
|
(prev_char)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _field_left, _field_right:
|
|
;**
|
|
;** Moves the cursor to the left or to the right in the field, if
|
|
;** possible. If the field is marked (which implies that the cursor
|
|
;** is still at the beginning of the field), the highlight is removed.
|
|
;**
|
|
|
|
(macro _field_left
|
|
(
|
|
(int cur_col)
|
|
|
|
(raise_anchor)
|
|
(inq_position NULL cur_col)
|
|
|
|
(if (> cur_col _dialog_col)
|
|
(left)
|
|
)
|
|
)
|
|
)
|
|
|
|
(macro _field_right
|
|
(
|
|
(int cur_col)
|
|
|
|
(raise_anchor)
|
|
(inq_position NULL cur_col)
|
|
|
|
(if (< cur_col (- _dialog_size 1))
|
|
(right)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _field_del:
|
|
;**
|
|
;** Deletes a character, as long as it's not a newline. Works the
|
|
;** same way in both insert and overstrike modes. If the field is still
|
|
;** marked, deletes the whole thing.
|
|
;**
|
|
|
|
(macro _field_del
|
|
(
|
|
(if (! (at_eol))
|
|
(if (inq_marked)
|
|
(
|
|
(raise_anchor)
|
|
(delete_to_eol)
|
|
)
|
|
;else
|
|
(delete_char)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _field_end, _field_home:
|
|
;**
|
|
;** Removes the mark if there is one, then positions the cursor
|
|
;** at the end or beginning of the field.
|
|
;**
|
|
|
|
(macro _field_end
|
|
(
|
|
(raise_anchor)
|
|
(end_of_line)
|
|
)
|
|
)
|
|
|
|
(macro _field_home
|
|
(
|
|
(raise_anchor)
|
|
(move_abs 0 _dialog_col)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _field_del_line, _field_delete_to_eol:
|
|
;**
|
|
;** Deletes either the entire field or the field contents after the
|
|
;** cursor position, removing any mark first.
|
|
;**
|
|
|
|
(macro _field_del_line
|
|
(
|
|
(move_abs 0 _dialog_col)
|
|
(_field_delete_to_eol)
|
|
)
|
|
)
|
|
|
|
(macro _field_delete_to_eol
|
|
(
|
|
(raise_anchor)
|
|
(delete_to_eol)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_validate:
|
|
;**
|
|
;** Determines if the current field meets predefined criteria for
|
|
;** being a nonblank string, integer, or file name. (Used for all typed
|
|
;** fields.) Returns TRUE if the string satisfied the criteria for
|
|
;** the current type, _dialog_type, or if the current type was not
|
|
;** a nonblank string, integer, or file name type.
|
|
;**
|
|
|
|
(macro _dialog_validate
|
|
(
|
|
(string value)
|
|
|
|
(int retval)
|
|
|
|
(= retval TRUE)
|
|
(get_parm 0 value)
|
|
|
|
(switch _dialog_type
|
|
|
|
FILENAME
|
|
(if (! (= retval (is_filename value)))
|
|
(error "This field must be a filename.")
|
|
)
|
|
INTEGER
|
|
(if (! (= retval (search_string
|
|
"<[ \\t]@{[\\-+][0-9]+}|{[0-9]+}[ \t]@\n"
|
|
(+ value "\n") NULL TRUE)))
|
|
(error "This field must be an integer.")
|
|
)
|
|
NONBLANK
|
|
(if (! (= retval (search_string
|
|
"[~ \\t\\n]" value NULL TRUE)))
|
|
(error "This field may not be blank.")
|
|
)
|
|
)
|
|
|
|
(if (! retval)
|
|
(beep)
|
|
)
|
|
(returns retval)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** is_filename:
|
|
;**
|
|
;** Returns TRUE if a string contains a syntactically correct
|
|
;** filename. Note: this regular expression will catch most, but
|
|
;** not all, incorrect file names. Among the cases that it will not
|
|
;** catch are two drive specifiers, extra or misplaced periods, omitted
|
|
;** filename (just extension), and name or extension too long.
|
|
;**
|
|
|
|
(macro is_filename
|
|
(
|
|
(string name)
|
|
|
|
(get_parm 0 name)
|
|
|
|
(returns (search_string
|
|
"<{[a-z]:}@[\\\\/]@{[~- \"%*-,/:-?\\[-\\]|]+[\\\\/]}@[~- \"%*-,/:-?\\[-\\]|]+>"
|
|
(+ name "\n") NULL TRUE FALSE))
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** at_eol:
|
|
;**
|
|
;** Returns TRUE if the current position is at the end of a line,
|
|
;** FALSE otherwise.
|
|
;**
|
|
|
|
(macro at_eol
|
|
(returns (== (read 1) "\n"))
|
|
)
|
|
|
|
;**
|
|
;** _format_menu:
|
|
;**
|
|
;** Automatically centers each line of the menu buffer in the
|
|
;** window, and moves the rest of the line out of sight. Normally not
|
|
;** done, since it's faster if you format your own menus.
|
|
;**
|
|
|
|
(macro _format_menu
|
|
(
|
|
(int button_offset
|
|
button_length
|
|
)
|
|
(string button
|
|
text
|
|
)
|
|
(top_of_buffer)
|
|
|
|
(while (! (inq_position))
|
|
(
|
|
(= button (_menu_button))
|
|
(= button_offset (index button ";"))
|
|
(= text (ltrim (substr button (+ button_offset 1))))
|
|
(= button (trim (substr button 1 (- button_offset 1))))
|
|
(= button_length (strlen button))
|
|
(= button_offset (+ (/ (- _dialog_size button_length) 2) 1))
|
|
|
|
(delete_to_eol)
|
|
(move_abs 0 button_offset)
|
|
(insert button)
|
|
(move_abs 0 (+ _dialog_size 1))
|
|
(insert (+ ";" text))
|
|
(beginning_of_line)
|
|
(down)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _process_menu:
|
|
;**
|
|
;** Creates and processes a menu, calling the user when interesting
|
|
;** events occur.
|
|
;**
|
|
|
|
(macro _process_menu
|
|
(
|
|
(int lx
|
|
by
|
|
rx
|
|
ty
|
|
old_buf_id
|
|
menu_buf_id
|
|
old_size
|
|
created_buffer
|
|
former_mode
|
|
fast_mode
|
|
screen_x
|
|
screen_y
|
|
retval
|
|
)
|
|
(string pathname
|
|
former_action_func
|
|
)
|
|
|
|
;** If we are passed an existing buffer id, we use that buffer;
|
|
;** if not, but we are passed the name of an existing file,
|
|
;** we create the buffer. If we get neither parameter, we
|
|
;** fail.
|
|
|
|
(if (&& (! (get_parm 7 menu_buf_id)) (get_parm 6 pathname))
|
|
(
|
|
;** If the file name is absolute (which we determine by
|
|
;** the presence of special characters) we don't look for
|
|
;** it in the BHELP directory.
|
|
|
|
(if (! (search_string "[:/\\\\]" pathname NULL TRUE))
|
|
(= pathname (add_to_path _dialog_dir pathname))
|
|
)
|
|
|
|
;** If the file exists already, we put it in a buffer.
|
|
;** Note that we use a spare string variable, former_
|
|
;** action_func, to get the buffer name parameter.
|
|
|
|
(if (exist pathname)
|
|
(
|
|
(get_parm 4 former_action_func)
|
|
(= menu_buf_id
|
|
(create_buffer former_action_func pathname TRUE))
|
|
(= created_buffer TRUE)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
;** We have a menu buffer ID if we got one as a parameter or if
|
|
;** create_buffer above succeeded. If we have one, we can create
|
|
;** the menu. If not, we return immediately so that the
|
|
;** ensuing code does not have to be part of the if statement;
|
|
;** this saves precious stack space, allowing for more nesting.
|
|
|
|
(if (! menu_buf_id)
|
|
(
|
|
(error "Can't create menu.")
|
|
(return FALSE)
|
|
)
|
|
)
|
|
|
|
(= old_buf_id (inq_buffer))
|
|
(message "Creating menu...")
|
|
|
|
;** Here we save the former value of _dialog_action_func
|
|
;** and increment the _dialog_level counter so recursive
|
|
;** invocations will know about this one.
|
|
|
|
(++ _dialog_level)
|
|
(= former_action_func _dialog_action_func)
|
|
(= former_mode _dialog_mode)
|
|
(= _dialog_mode DIALOG_MENU_MODE)
|
|
|
|
;** Now that we've created the menu buffer, we call the
|
|
;** menu creation event in case we want to do any raw
|
|
;** processing on the menu (like adding our own buttons).
|
|
;** For this event, the menu buffer is guaranteed current.
|
|
;** We pass the buffer name to the action function, reusing
|
|
;** a string variable again.
|
|
|
|
(get_parm 8 _dialog_action_func)
|
|
(set_buffer menu_buf_id)
|
|
(inq_names NULL NULL pathname)
|
|
(execute_macro _dialog_action_func DIALOG_CREATE_MENU NULL
|
|
pathname)
|
|
|
|
;** We reuse pathname to hold the bottom line message.
|
|
;** Note that if lx == rx or by == ty, we have to calculate
|
|
;** rx (width) and by (height) automatically.
|
|
|
|
(get_parm 5 pathname)
|
|
(get_parm 0 lx)
|
|
(get_parm 1 by)
|
|
(get_parm 2 rx)
|
|
(get_parm 3 ty)
|
|
|
|
;** If either window dimension is zero, we size the window
|
|
;** automatically. It has to be at least 1 line x 14 cols
|
|
;** (larger if a long bottom line message is to be displayed).
|
|
|
|
(if (|| (== lx rx) (== ty by))
|
|
(
|
|
(top_of_buffer)
|
|
|
|
;** Use the first semicolon, if any, and the number
|
|
;** of lines to get the optimum size. (No semicolon:
|
|
;** use a 14-column window.)
|
|
|
|
(if (search_fwd ";" FALSE)
|
|
(inq_position NULL rx)
|
|
;else
|
|
(= rx 15)
|
|
)
|
|
(+= rx lx)
|
|
(end_of_buffer)
|
|
(inq_position by)
|
|
(+= by (+ ty 1))
|
|
(top_of_buffer)
|
|
|
|
;** Make sure the window will fit on the screen;
|
|
;** if it won't, shrink it. Leave room for the
|
|
;** "shadows".
|
|
|
|
(inq_screen_size screen_y screen_x)
|
|
(-= screen_y 4)
|
|
(-= screen_x 3)
|
|
|
|
(if (< screen_y by)
|
|
(= by screen_y)
|
|
)
|
|
(if (< screen_x rx)
|
|
(= rx screen_x)
|
|
)
|
|
)
|
|
)
|
|
|
|
;** If either dimension is too small, display an error
|
|
;** message and not the window. Note that we duplicate
|
|
;** all the "cleanup" code here so we can return
|
|
;** immediately. That's because the code size is less
|
|
;** crucial than the maximum nesting depth here, since
|
|
;** the macro is intended to be called recursively and
|
|
;** nesting in macros consumes a ton of stack space.
|
|
|
|
(= screen_x (+ (strlen pathname) 2))
|
|
(= screen_y (- rx lx))
|
|
|
|
(if (|| (|| (<= (- by ty) 1) (<= screen_y 14))
|
|
(<= screen_y screen_x))
|
|
(
|
|
(error "Window would be too small.")
|
|
|
|
(if (&& created_buffer (! (inq_views)))
|
|
(delete_buffer menu_buf_id)
|
|
)
|
|
(set_buffer old_buf_id)
|
|
|
|
(if (! (-- _dialog_level))
|
|
(
|
|
(call_registered_macro 1)
|
|
(message "")
|
|
)
|
|
)
|
|
(= _dialog_action_func former_action_func)
|
|
(= _dialog_mode former_mode)
|
|
(return FALSE)
|
|
)
|
|
)
|
|
|
|
(= old_size _dialog_size)
|
|
(= _dialog_size (- (- rx lx) 1))
|
|
(keyboard_push _dialog_menu_keymap)
|
|
|
|
;** If we were passed a tenth parameter, and it was TRUE,
|
|
;** we change to "fast" mode (meaning the window is already
|
|
;** formatted--centered, if desired, and no visible semicolons).
|
|
|
|
(if (! (&& (get_parm 9 fast_mode) fast_mode))
|
|
(_format_menu)
|
|
)
|
|
|
|
;** Now we display the menu window, but we postpone refreshing
|
|
;** the display until we highlight the first button.
|
|
|
|
(create_window lx by rx ty pathname)
|
|
(attach_buffer menu_buf_id)
|
|
(message "Menu created.")
|
|
|
|
;** Go to the top of the buffer, call the init event,
|
|
;** highlight the first selectable line, redraw the screen,
|
|
;** and go into interactive mode.
|
|
|
|
(top_of_buffer)
|
|
(execute_macro _dialog_action_func DIALOG_INIT)
|
|
(_dialog_menu_home)
|
|
(refresh)
|
|
(process)
|
|
|
|
;** Remove the highlight, then the keyboard, then the window.
|
|
|
|
(raise_anchor)
|
|
(execute_macro _dialog_action_func DIALOG_TERM)
|
|
(keyboard_pop TRUE)
|
|
(delete_window)
|
|
|
|
;** Restore the old current buffer and delete the menu
|
|
;** buffer, if we created the buffer ourselves and
|
|
;** it's not visible in any window.
|
|
|
|
(if (&& created_buffer (! (inq_views)))
|
|
(delete_buffer menu_buf_id)
|
|
)
|
|
(set_buffer old_buf_id)
|
|
|
|
;** Decrement the menu level. If it is zero,
|
|
;** we're leaving the menu package for good--so re-run the
|
|
;** new file macros, and clear the message line.
|
|
|
|
(if (! (-- _dialog_level))
|
|
(
|
|
(call_registered_macro 1)
|
|
(message "")
|
|
)
|
|
)
|
|
(= _dialog_action_func former_action_func)
|
|
(= _dialog_mode former_mode)
|
|
(= _dialog_size old_size)
|
|
(returns TRUE)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _menu_highlight:
|
|
;**
|
|
;** Calls the menu alter event. If it succeeds, highlights the
|
|
;** current menu line using a line mark.
|
|
;**
|
|
|
|
(macro _menu_highlight
|
|
(
|
|
(int line)
|
|
|
|
(inq_position line)
|
|
|
|
(if (execute_macro _dialog_action_func DIALOG_MOVE_MENU line (_menu_button))
|
|
(
|
|
(raise_anchor)
|
|
(drop_anchor 3)
|
|
(returns TRUE)
|
|
)
|
|
;else
|
|
(returns FALSE)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_menu_home:
|
|
;**
|
|
;** Moves the selection to the first selectable item on the menu.
|
|
;** Calls the action function with the new line number and menu button
|
|
;** text.
|
|
;**
|
|
|
|
(macro _dialog_menu_home
|
|
(
|
|
(top_of_buffer)
|
|
|
|
(while (! (_menu_highlight))
|
|
(down)
|
|
)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_menu_end:
|
|
;**
|
|
;** Moves to the last selectable item on the menu. Calls the
|
|
;** action function with the new line number and menu button text.
|
|
;**
|
|
|
|
(macro _dialog_menu_end
|
|
(
|
|
(end_of_buffer)
|
|
(beginning_of_line)
|
|
(to_bottom)
|
|
|
|
(while (! (_menu_highlight))
|
|
(up)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
;**
|
|
;** _dialog_menu_pgup, _dialog_menu_pgdn:
|
|
;**
|
|
;** Pages up or down in a menu.
|
|
;**
|
|
|
|
(macro _dialog_menu_pgup
|
|
(
|
|
(page_up)
|
|
|
|
(if (! (|| (_menu_highlight) (_dialog_menu_up)))
|
|
(_dialog_menu_down)
|
|
)
|
|
)
|
|
)
|
|
|
|
(macro _dialog_menu_pgdn
|
|
(
|
|
(page_down)
|
|
|
|
(while (inq_position)
|
|
(up)
|
|
)
|
|
|
|
(if (! (|| (_menu_highlight) (_dialog_menu_down)))
|
|
(_dialog_menu_up)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
;**
|
|
;** _dialog_menu_down:
|
|
;**
|
|
;** Moves the current selection down, stopping at next selectable
|
|
;** item; if the bottom of the menu is encountered, returns to the
|
|
;** previous position. Returns whether or not it succeeded in moving.
|
|
;** Calls the action function with the new line number and menu
|
|
;** button text as parameters.
|
|
;**
|
|
|
|
(macro _dialog_menu_down
|
|
(
|
|
(int failed)
|
|
|
|
(save_position)
|
|
|
|
(while TRUE
|
|
(
|
|
(down)
|
|
|
|
(if (inq_position)
|
|
(
|
|
(++ failed)
|
|
(break)
|
|
)
|
|
;else
|
|
(if (_menu_highlight)
|
|
(break)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(restore_position failed)
|
|
(returns (! failed))
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_menu_up:
|
|
;**
|
|
;** Moves the current selection up to the previous selectable
|
|
;** item, if there are any. Returns TRUE if it succeeded.
|
|
;**
|
|
|
|
(macro _dialog_menu_up
|
|
(
|
|
(int moved)
|
|
|
|
(save_position)
|
|
|
|
(while (up)
|
|
(if (_menu_highlight)
|
|
(
|
|
(++ moved)
|
|
(break)
|
|
)
|
|
)
|
|
)
|
|
(if (! moved)
|
|
(set_top_left 1 1)
|
|
)
|
|
(restore_position (! moved))
|
|
(returns moved)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _menu_go:
|
|
;**
|
|
;** Reads a character (that was just inserted; _menu_go is
|
|
;** called by a type 0 registered macro) from the buffer and deletes
|
|
;** it, then looks for a matching button before the end of the menu.
|
|
;** If there is none, looks for one starting with the first item.
|
|
;** If there is still none, stays put. (If a selectable button is found,
|
|
;** _menu_go moves to it and highlights it.)
|
|
;**
|
|
|
|
(macro _menu_go
|
|
(
|
|
(string pattern)
|
|
|
|
(int end_line
|
|
after_line
|
|
start_line
|
|
start_col
|
|
failed
|
|
)
|
|
|
|
;** Figure out what key was pressed.
|
|
|
|
(prev_char)
|
|
(= pattern (read 1))
|
|
(delete_char)
|
|
|
|
;** If the character is a regular expression character, we make
|
|
;** sure to put a backslash in front of it.
|
|
|
|
(if (index "?*@+|\\<>%${}[]~-" pattern)
|
|
(= pattern (+ "\\" pattern))
|
|
)
|
|
(= pattern (+ "<[ \\t]@" pattern))
|
|
|
|
;** Find the last line of the window; if the search takes us
|
|
;** outside it, we want to center the line in the window.
|
|
|
|
(inq_position start_line start_col)
|
|
(end_of_window)
|
|
(inq_position end_line)
|
|
(move_abs start_line start_col)
|
|
|
|
;** Search from the current position to EOF, breaking if
|
|
;** a selectable line is found. If the pattern is not found,
|
|
;** start searching from the top of the file down.
|
|
|
|
(while (! failed)
|
|
(
|
|
(next_char)
|
|
|
|
(if (<= (search_fwd pattern TRUE FALSE) 0)
|
|
(
|
|
(move_abs start_line start_col)
|
|
(drop_anchor)
|
|
(top_of_buffer)
|
|
|
|
(while (! failed)
|
|
(
|
|
(if (<= (search_fwd pattern TRUE FALSE TRUE) 0)
|
|
(++ failed)
|
|
;else
|
|
(if (_menu_highlight)
|
|
(
|
|
(inq_position after_line)
|
|
|
|
(if (> after_line end_line)
|
|
(center_line)
|
|
)
|
|
(return)
|
|
)
|
|
)
|
|
)
|
|
(next_char)
|
|
)
|
|
)
|
|
(raise_anchor)
|
|
)
|
|
;else
|
|
(if (_menu_highlight)
|
|
(
|
|
(inq_position after_line)
|
|
|
|
(if (> after_line end_line)
|
|
(center_line)
|
|
)
|
|
(return)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(move_abs start_line start_col)
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _dialog_menu_pick:
|
|
;**
|
|
;** Processes the menu line that is selected when the user presses
|
|
;** Enter.
|
|
;**
|
|
|
|
(macro _dialog_menu_pick
|
|
(
|
|
(int line)
|
|
|
|
(inq_position line)
|
|
(execute_macro _dialog_action_func DIALOG_PICK_MENU line (_menu_button))
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _menu_button:
|
|
;**
|
|
;** Returns the text of the current menu button, with leading and
|
|
;** trailing white space removed.
|
|
;**
|
|
|
|
(macro _menu_button
|
|
(returns (trim (ltrim (read))))
|
|
)
|
|
|
|
;**
|
|
;** _list_button:
|
|
;**
|
|
;** Returns the text of the current list button, excluding any
|
|
;** surrounding parentheses or tabs.
|
|
;**
|
|
|
|
(macro _list_button
|
|
(
|
|
(string button)
|
|
|
|
(= button (read))
|
|
(returns (substr button 1 (- (index button "\t") 1)))
|
|
)
|
|
)
|
|
|
|
;**
|
|
;** _field_contents:
|
|
;**
|
|
;** Returns the contents of the current field, not including the
|
|
;** newline, and regardless of the cursor position.
|
|
;**
|
|
|
|
(macro _field_contents
|
|
(
|
|
(string field)
|
|
|
|
(save_position)
|
|
(move_abs 0 _dialog_col)
|
|
(= field (read))
|
|
(restore_position)
|
|
(returns (substr field 1 (- (strlen field) 1)))
|
|
)
|
|
)
|