Files
CydandClaude Fable 5 db7745fcd0 sda4: commit the Glaze developer hard-drive dump
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>
2026-07-04 19:41:15 -05:00

1452 lines
36 KiB
Plaintext

/*
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
**
**
** dialog.cb:
**
** This file contains all of the standard BRIEF macros for the
** dialog and menu manager.
*/
#define DIALOG_MAIN
#include "dialog.h"
#include "dlg.h"
#include "win_ctrl.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.
*/
/*
** Global variables.
*/
int _dialog_level,
_dialog_type,
_dialog_row,
_dialog_col,
_dialog_field_keymap,
_dialog_list_keymap,
_dialog_menu_keymap,
_dialog_button_keymap,
_dialog_size,
_dialog_data_buf,
_dialog_disp_buf,
_dialog_radio_buf,
_dialog_mouse_buf,
_dialog_mode,
_dialog_menu_time,
_dialog_picked,
_dialog_pb_state;
string _dialog_action_func,
_dialog_dir,
_dialog_menu_prefix,
_dialog_default_pb;
/*
** 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.
*/
void _init ()
{
if (first_time ())
{
/*
** 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 ();
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 ("<Keypad-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 ();
assign_to_key ("<F10>", "_dialog_f10");
assign_to_key ("<Enter>", "_dialog_next_and_save");
assign_to_key ("<Keypad-Enter>", "_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);
/*
** Create a keymap for use with buttons.
*/
keyboard_push ();
assign_to_key ("<Space>", "_dlg_pick_btn");
assign_to_key ("<Backspace>", "_dlg_beep");
assign_to_key ("<Home>", "_dlg_beep");
assign_to_key ("<End>", "_dlg_beep");
assign_to_key ("<Left>", "_dlg_btn_prev");
assign_to_key ("<Up>", "_dlg_btn_prev");
assign_to_key ("<Right>", "_dlg_btn_next");
assign_to_key ("<Down>", "_dlg_btn_next");
assign_to_key ("<Tab>", "_dlg_btn_tab_next");
assign_to_key ("<Shift-Tab>", "_dlg_btn_tab_prev");
_dialog_button_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>", "_field_insert 32");
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);
/*
** Autoload the list, field, menu and button functions.
*/
autoload ("dlg_list", "_list_next", "_list_prev", "_list_highlight",
"_list_home", "_list_end", "_list_go", "_list_button",
"_is_list");
autoload ("dlg_fld", "_field_insert", "_field_bksp", "_field_left",
"_field_right", "_field_del", "_field_end", "_field_home",
"_field_del_line", "_field_delete_to_eol",
"_dialog_validate", "is_filename", "at_eol",
"_field_contents");
autoload ("dlg_menu", "_menu_highlight", "_dialog_menu_home",
"_dialog_menu_end", "_dialog_menu_pgup",
"_dialog_menu_pgdn", "_dialog_menu_down",
"_dialog_menu_up", "_menu_go", "_dialog_menu_pick",
"_menu_button", "mouse_menu");
autoload ("dlg_butn", "_dialog_check_button", "_dialog_is_checked",
"_dialog_check_radio", "_dialog_radio_checked",
"_dlg_goto_btn", "_dlg_check_btn", "_dlg_push_btn",
"_dlg_radio_btn", "_dlg_radio_grp", "_dlg_pick_btn",
"_dlg_select_push", "_dlg_btn_tab_next",
"_dlg_btn_tab_prev", "_dlg_btn_next", "_dlg_btn_prev",
"_is_push_button");
autoload ( "dlg_mous", "mouse_dialog" );
}
}
/*
** _remove_dialog_manager:
**
** You should call this macro whenever you are finished using the
** dialog manager. It frees the keymaps created when we load.
** 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.
*/
void _remove_dialog_manager (void)
{
if (first_time ())
{
keyboard_push (_dialog_menu_keymap);
keyboard_push (_dialog_list_keymap);
keyboard_push (_dialog_field_keymap);
keyboard_push (_dialog_button_keymap);
keyboard_pop ();
keyboard_pop ();
keyboard_pop ();
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.
*/
void _dialog_f10 (void)
{
/*
** 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 ();
}
void _dialog_esc (void)
{
int curr_level;
execute_macro (_dialog_action_func, DIALOG_ESCAPE);
curr_level = _dialog_level;
while (curr_level--)
_exit ();
}
void _dialog_grey_minus (void)
{
/*
** 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.
*/
void _format_dialog ()
{
int pos,
open_paren,
default_btn,
in_group,
first_in_group;
string line,
group;
_dialog_default_pb = "";
default_btn = 1;
_dialog_pb_state = TRUE; // no need to draw pb
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 (in_group ? FIND_RADIO : FIND_ANY, TRUE, FALSE))
{
pos = index (line = _dialog_set_globals (), "\"") + 1;
line = substr (line, pos, rindex (line, "\"") - pos);
next_char ();
/*
** Move this up here when I can explain why...
*/
open_paren = TRUE;
switch (_dialog_type)
{
case LIST:
{
/*
** 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";
}
}
}
case GROUP:
{
/*
** Disallow 'nested' groups.
*/
if (in_group)
{
delete_line ();
beginning_of_line ();
error ("Extra 'GROUP' label ignored.");
}
else
{
group = line;
in_group++;
first_in_group = TRUE;
}
continue;
}
case ENDGROUP:
{
/*
** Can't have one of these w/o a preceeding 'Group' label.
*/
if (!in_group)
{
delete_line ();
beginning_of_line ();
error ("Extra 'ENDGROUP' ignored.");
}
else
{
//set_buffer (_dialog_radio_buf);
//insert ("[EndGroup:%s]\n", group);
group = "";
in_group--;
}
continue;
}
case RADIO:
{
int radio_char;
/*
** If we are not in a 'Group' section, toss this line.
*/
if (!in_group)
{
delete_line ();
beginning_of_line ();
error ("Missing 'GROUP' label -- Radio button ignored.");
continue;
}
/*
** For each radio button, store the button's group, label,
** position, and status in _dialog_radio_buf. By default, the
** first radio button of each group is set to 'on' and all others
** in the same group are set to 'off'
*/
set_buffer (_dialog_radio_buf);
if (first_in_group)
{
radio_char = RADIO_ON;
first_in_group = FALSE;
}
else
radio_char = RADIO_OFF;
insert ("%s\"%s\"(%d,%d)%c\n", group, line, _dialog_row,
_dialog_col, radio_char);
set_buffer (_dialog_data_buf);
sprintf (line, RADIO_BTN, radio_char, line);
}
case PUSH:
{
/*
** If this pushbutton is the default, set _dialog_default_pb.
*/
if (search_string ("<[ \t]@P*([ \t0-9]+,[ \t0-9])[ \t]@DEFAULT", group))
_dialog_default_pb = line;
sprintf (line, PUSH_BUTTON, line);
}
case CHECK:
{
/*
** Build Checkbox button and label for insertion;
*/
line = CHECK_BOX + " " + line;
}
}
/*
** 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 ();
}
/*
** If this is not a static-text control, assign a mouse event to
** the control.
*/
if (_dialog_type != TEXT)
{
int width,
ctrl_width;
/* If the control is not a button, set the ctrl_width to the
** maximum width of the field.
*/
if (_dialog_type != CHECK && _dialog_type != PUSH &&
_dialog_type != RADIO && _dialog_type != LIST)
ctrl_width = _dialog_size - _dialog_col - 1;
else
ctrl_width = strlen (line);
width = ctrl_width;
/* set to the mouse buffer */
set_buffer(_dialog_mouse_buf);
/* Insert the type of control in the buffer. */
move_abs(_dialog_row, _dialog_col + (_dialog_type == LIST));
if (_dialog_type == LIST)
ctrl_width -= 3;
for ( ;ctrl_width > 0; ctrl_width --)
insert("%d", _dialog_type);
/* get rid of extra chars inserted */
delete_char(width);
}
set_buffer (_dialog_data_buf);
}
}
top_of_buffer ();
search_fwd (FIND_CONTROL, 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.
*/
int _process_dialog_box (int lx, int by, int rx, int ty, string, string, ~string, ~int, string)
{
int old_buf_id,
created_buffer,
old_type,
old_row,
old_col,
old_size,
old_data_buf,
old_disp_buf,
old_radio_buf, // *SKY*
old_mode,
old_def_pb_state;
string pathname,
old_action_func,
old_def_pb;
/*
** Save the current value of the global variable _dialog_data_buf.
*/
old_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 = search_path (_dialog_dir, pathname);
/*
** If the file exists and is not already a buffer in BRIEF, put it
** in a buffer. Otherwise, use the file's buffer ID.
*/
if (exist (pathname) && !(_dialog_data_buf = inq_buffer (pathname)))
{
_dialog_data_buf = create_buffer ("Information", pathname, TRUE);
created_buffer = 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 = old_data_buf;
return (FALSE);
}
message ("Creating dialog box...");
/*
** Here we save the old 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++;
old_action_func = _dialog_action_func;
old_type = _dialog_type;
old_row = _dialog_row;
old_col = _dialog_col;
old_size = _dialog_size;
old_disp_buf = _dialog_disp_buf;
old_radio_buf = _dialog_radio_buf;
old_mode = _dialog_mode;
old_def_pb_state = _dialog_pb_state;
old_def_pb = _dialog_default_pb;
_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 (5, pathname);
get_parm (8, _dialog_action_func);
_dialog_size = (rx - lx) - 1;
keyboard_push (_dialog_list_keymap);
/*
** Create a non-file system buffer for managing radio buttons.
*/
_dialog_radio_buf = create_buffer ("RadioButtons", NULL, TRUE); // *SKY*
/*
** Create a non-file system buffer for managing mouse info.
*/
_dialog_mouse_buf = create_buffer ("MouseInfo", NULL, TRUE);
/*
** 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 ();
set_ctrl_state( ZOOM_BTN, HIDE_CTRL); // hide the zoom button for this window
set_ctrl_state( HORZ_SCROLL, HIDE_CTRL); // hide the horz scroll bar for this window
set_ctrl_state( VERT_SCROLL, HIDE_CTRL); // hide the vertical scroll bar for this window
create_window (lx, by, rx, ty, pathname);
attach_buffer (_dialog_disp_buf);
set_ctrl_state( ZOOM_BTN, SHOW_CTRL); // show the zoom button
set_ctrl_state( HORZ_SCROLL, SHOW_CTRL); // show the horz scroll bar
set_ctrl_state( VERT_SCROLL, SHOW_CTRL); // show the vertical scroll bar for this window
/*
** 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");
/* assign a mouse handler for the dialog box */
set_mouse_action( "mouse_dialog" );
/*
** 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);
delete_buffer (_dialog_radio_buf); // *SKY*
delete_buffer (_dialog_mouse_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 = old_action_func;
_dialog_type = old_type;
_dialog_row = old_row;
_dialog_col = old_col;
_dialog_size = old_size;
_dialog_disp_buf = old_disp_buf;
_dialog_data_buf = old_data_buf;
_dialog_radio_buf = old_radio_buf; // *SKY*
_dialog_mode = old_mode;
_dialog_pb_state = old_def_pb_state;
_dialog_default_pb = old_def_pb;
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.
*/
void _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 (FIND_CONTROL, TRUE, FALSE))
if (execute_macro (_dialog_action_func, DIALOG_F10))
{
_exit ();
return;
}
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.
*/
void _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 (FIND_CONTROL, TRUE, FALSE))
beginning_of_line ();
_dialog_enter ();
}
}
void _dialog_prev ()
{
if (_dialog_exit ())
{
set_buffer (_dialog_data_buf);
if (!(up () && search_back (FIND_CONTROL, TRUE, FALSE)))
{
top_of_buffer ();
search_fwd (FIND_CONTROL, TRUE, FALSE);
}
_dialog_enter ();
}
}
void _dialog_home ()
{
if (_dialog_exit ())
{
set_buffer (_dialog_data_buf);
top_of_buffer ();
search_fwd (FIND_CONTROL, TRUE, FALSE);
_dialog_enter ();
}
}
void _dialog_end ()
{
if (_dialog_exit ())
{
set_buffer (_dialog_data_buf);
end_of_buffer ();
search_back (FIND_CONTROL, 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.
*/
string _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 (CONTROL_TYPES, upper (substr (ltrim (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.
*/
void _dialog_enter ()
{
int offset,
len;
string field;
_dialog_set_globals ();
set_buffer (_dialog_disp_buf);
move_abs (_dialog_row, _dialog_col);
if (_dialog_type == PUSH && _dialog_pb_state)
_dlg_select_push (_dialog_default_pb, 0);
else if (!_dialog_pb_state && _dialog_type != PUSH)
_dlg_select_push (_dialog_default_pb, 1); /* show defaul pb if it exists */
/*
** Perform the appropriate initializations and highlighting based on
** the control type.
*/
switch (_dialog_type)
{
case LIST:
{
/*
** Adjust _dialog_col to compensate for a possible initial tab.
*/
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));
}
case CHECK:
case PUSH:
case RADIO:
{
/*
** Position the cursor in the check box or button.
*/
use_local_keyboard (_dialog_button_keymap);
_dialog_col++;
next_char ();
if (_dialog_type == CHECK)
execute_macro (_dialog_action_func, DIALOG_ENTER_CHECK, _dialog_row, _dlg_check_btn ());
else if (_dialog_type == PUSH)
execute_macro (_dialog_action_func, DIALOG_ENTER_PUSH, _dialog_row, _dlg_push_btn ());
else
execute_macro (_dialog_action_func, DIALOG_ENTER_RADIO, _dialog_row, _dlg_radio_btn ());
}
default:
{
/*
** 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.
*/
int _dialog_exit ()
{
int retval;
switch (_dialog_type)
{
case 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);
}
}
case CHECK:
{
if (retval = execute_macro (_dialog_action_func, DIALOG_EXIT_CHECK, _dialog_row, _dlg_check_btn ()))
use_local_keyboard (0);
}
case PUSH:
{
if (retval = execute_macro (_dialog_action_func, DIALOG_EXIT_PUSH, _dialog_row, _dlg_push_btn ()))
use_local_keyboard (0);
}
case RADIO:
{
if (retval = execute_macro (_dialog_action_func, DIALOG_EXIT_RADIO, _dialog_row, _dlg_radio_btn ()))
use_local_keyboard (0);
}
default:
{
string 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);
}
/*
** _invalid_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.
*/
replacement void _invalid_key ()
{
int key = read_char ();
if ((key & 0xff) && (key & 0xff) < 128
&& _dialog_mode
&& (_dialog_data_buf == inq_buffer () || _dialog_disp_buf == inq_buffer ()))
{
if (_dialog_mode == DIALOG_MENU_MODE)
_menu_go (key);
else if (_dialog_type == LIST)
_list_go (key);
else if (_dialog_type == CHECK || _dialog_type == PUSH || _dialog_type == RADIO)
beep ();
else
_field_insert (key);
refresh ();
}
else
{
push_back (key);
_invalid_key ();
}
}
/*
** _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.
*/
void _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.
*/
int _process_menu (int lx, int by, int rx, int ty, string, string, ~string, ~int, string, ~int, ~int, ~string)
{
int old_buf_id,
old_data_buf_id,
old_size,
old_mode,
menu_buf_id,
created_buffer,
fast_mode,
screen_x,
screen_y,
retval;
string pathname,
old_action_func;
extern void mouse_menu();
/*
** 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 = search_path (_dialog_dir, pathname);
/*
** If the file exists already, we put it in a buffer.
** Note that we use a spare string variable, old_
** action_func, to get the buffer name parameter.
*/
if (exist (pathname))
{
get_parm (4, old_action_func);
menu_buf_id = create_buffer (old_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 old value of _dialog_action_func
** and increment the _dialog_level counter so recursive
** invocations will know about this one.
*/
_dialog_level++;
_dialog_menu_prefix = "";
old_action_func = _dialog_action_func;
old_mode = _dialog_mode;
old_data_buf_id = _dialog_data_buf;
_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 (_dialog_data_buf = 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);
/*
** 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 = old_action_func;
_dialog_mode = old_mode;
_dialog_data_buf = old_data_buf_id;
return (FALSE);
}
_dialog_picked = 0;
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.
*/
set_ctrl_state( ZOOM_BTN, HIDE_CTRL); // hide the zoom button for this window
set_ctrl_state( HORZ_SCROLL, HIDE_CTRL); // hide the horz scroll bar for this window
create_window (lx, by, rx, ty, pathname);
set_ctrl_state( ZOOM_BTN, SHOW_CTRL); // show the zoom button
set_ctrl_state( HORZ_SCROLL, SHOW_CTRL); // show the horz scroll bar
attach_buffer (menu_buf_id);
message ("Menu created.");
/*
** Establish an action routine for menu mouse events.
** Note: the mouse action routine is set BEFORE the
** DIALOG_INIT action function is called to allow the default
** mouse event handler to be sub-classed when processing
** the DIALOG_INIT message.
*/
set_mouse_action( "mouse_menu" );
/*
** 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 ();
/*
** Here we set parameter 10 to indicate the button number picked,
** set parameter 11 to the text of the picked button, and clear
** clear _dialog_picked so that nested menus work properly.
**
** A zero value for parameter 10 indicates the user pressed <Esc>
** or <Keypad minus> to abort the menu.
**
** A nonzero value indicates the user pressed <Enter> to select
** a button, and the button number becomes the parameter value.
** The button text is in parameter 11.
*/
put_parm (10, _dialog_picked);
put_parm (11, _dialog_picked ? _menu_button () : "");
_dialog_picked = 0;
/*
** 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 = old_action_func;
_dialog_mode = old_mode;
_dialog_size = old_size;
_dialog_data_buf = old_data_buf_id;
returns (TRUE);
}
/*
** For invalid keys.
*/
void _dlg_beep (void)
{
beep ();
}