HOW TO USE THE BRIEF DIALOG MANAGER PACKAGE WHAT IS THE DIALOG MANAGER? The BRIEF dialog manager is a macro package containing tools for generating pop-up menus and dialog boxes. Menus allow a user to perform complicated tasks by choosing from lists of possible actions. Dialog boxes allow a user to answer questions and fill in forms, while possibly restricting the set of valid answers. Many of BRIEF's standard macros (Help, for example) use the dialog manager. You can use the dialog manager in your own macros, too. WHERE IS THE DIALOG MANAGER? Source code for the package is located in the files dialog.m and dialog.h; the compiled version is in dialog.cm. WHY USE THE DIALOG MANAGER? The BRIEF dialog manager provides a standard user interface for macros. If you write a macro that uses this interface properly, it will have the look and feel of the standard commands that are shipped with BRIEF, and any BRIEF user will be able to use it with minimal effort. Using the dialog manager conserves memory and avoids reinventing the wheel. Suppose you frequently use Help and three other macro packages with separate user interfaces. That's a lot of macros to load at once; BRIEF may spend a fair amount of time re-reading the disk. The macros will take up far less memory if they reuse the same code. WHAT CAPABILITIES ARE PROVIDED? The dialog manager is essentially just two macro calls, one to create and process a menu, and one to create and process a dialog box. Although the dialog manager contains dozens of other macros (as well as several global variables,) you should seldom need to deal with them. (Consult the source code for more information.) Menus are pop-up windows, within which one line ("button") is always highlighted. Pressing Enter ("picking") causes an action associated with the highlighted button to be executed. The up and down arrow keys move the highlight up or down. Windows scroll vertically as needed. Menus offer a choice of several actions. These actions may invoke other menus or dialog boxes. In Help, for example, picking some buttons causes a window of help information on a particular topic to appear; picking others causes another menu (more detailed) to appear. Dialog boxes are the other half of the dialog manager. Dialog boxes are pop-up windows that contain descriptive text and any of several kinds of input fields. A user may move between fields and change the contents of any field. These changes can be saved or cancelled as a group. The kinds of input fields are: Filenames Lists Integers Nonblank strings Strings LISTS Lists, like menus, are multiple-choice situations; however, lists are choices of values, not actions, and the values are usually mutually exclusive. One very common list has two choices: Yes and No. Lists appear on the screen on a single line, with items separated by whitespace. A highlight is used to mark the currently selected item while the user's cursor is on the field; when he or she leaves, the highlight changes to a pair of parentheses. A list can be paired with a text string, which is usually a question: Do you want to save your changes? (Yes)No Line drawing style: Single(Double)Mixed None FILENAMES, INTEGERS, NONBLANK STRINGS, AND STRINGS The number of possible answers to a question is often so great that a list is awkward or impossible. In such situations, it's best to let the user to type in his or her answer, then check it for validity. The user can be asked to re-enter an invalid answer. Filenames, Integers, Nonblank strings, and Strings behave identically except that they use different validation criteria. Strings are not checked at all. Nonblank strings may not be blank. Integer fields must contain an integer value, and Filename fields must contain a syntactically legal path name. Fields are one line deep and may extend from their beginning to the right edge of the window. The entire field is highlighted when the user's cursor enters it; the highlight disappears as soon as anything happens. When the user's cursor leaves, the field contents are validated. KEY ASSIGNMENTS The key assignments for moving around and between menus and dialog boxes are standardized. You should not change these key assignments, although you may add your own to the list. These key assignments are uniform throughout the dialog manager: Esc Exit all levels Keypad minus Exit 1 level Ctrl-Home Top of window Ctrl-End End of window Within menus, the additional assignments are: Enter Pick from menu Up arrow Previous item Down arrow Next item Backspace Previous item Space Next item Home First item End Last item PgUp Previous page PgDn Next page Alphanumeric character sequence Next item beginning with characters Within any dialog box field, the following keys are defined: Enter Next field, or save if on last field (== Ctrl-m) F10 Save dialog box and exit Up arrow Previous field Down arrow Next field Shift-Tab Previous field Tab Previous field Within lists only, additional assignments are: Left arrow Previous item Right arrow Next item Backspace Previous item Space Next item Home First item End Last item Alphanumeric character Next item beginning with character Within fields (excepting lists), additional key assignments are: Left arrow Left Right arrow Right Backspace Backspace Space Space Home Beginning of field End End of field Del Delete character Alt-k Delete to end Ins, Alt-i Toggle insert mode Alt-d Delete field When a field is highlighted, typing causes the contents of the field to be replaced by the new characters. MENUS HOW TO CREATE A MENU There are 3 steps involved in creating a menu. 1. Create a data file containing the names of the menu buttons and the actions you want the buttons to invoke. 2. Write a macro that calls the dialog manager package. 3. Write the action macros that will handle everything the user does in the menu. CREATING THE MENU DATA FILE Menu data files are usually given a .mnu extension and kept in the same directory as the help files (specified by BHELP). A menu file contains one line for each line of the menu. Each line consists of a button name and optional additional information. When the menu is displayed, the additional information should be invisible (just past the right edge of the window). The menu button is normally centered in the visible part of the line. For fastest operation, each line is normally pre-formatted; in this example (taken from help.mnu), spaces are used to center the buttons in the visible part of the window, which is delimited by the semicolons. Everything to the right of the semicolons is considered "additional information": Help on Help ;display_help "help" Quick Reference ;display_help "keyboard layout" Key-Specific Help ;key_specific_help You can also have the menu automatically formatted. If you do, you must use a semicolon as the delimiter, and leading/trailing whitespace around the buttons will be ignored: Help on Help;display_help "help" Quick Reference;display_help "keyboard layout" Key-Specific Help;key_specific_help Action macros, described below, are free to do whatever they want with the additional information. In this case, the information is a macro call (complete with a string parameter), and it is passed to execute_macro. CALLING UP THE MENU The call to create and process a menu is as follows: (_process_menu lx by rx ty title msg filename buf_id action fast picked_line picked_text) Parameters 0 through 3 are integers, the coordinates of the left (lx), bottom (by), right (rx), and top (ty) edges of the window. These are absolute screen coordinates; the top left corner of a 25x80 screen is (0, 0) and the lower right is (24, 79). If lx equals rx or ty equals by, the menu is positioned at (lx, ty) and the size will be computed automatically based on the number of lines on the menu and the column position of the first semicolon. (Be careful not to position menus so close to the bottom and left edges of the screen so that there's no room for automatic formatting.) Parameter 4 is the title of the menu, a string to be displayed in the top edge of the window. It may be at most 12 characters long. Parameter 5 is a string, a message to be displayed in the bottom edge of the window. Make sure it is shorter than the edge itself. You should supply either, but not both, of parameters 6 and 7. Supply NULL in the place of whichever parameter you omit. Parameter 6 is the filename of a menu data file (in the BHELP directory), while parameter 7 is the identifier of a buffer containing a menu data file. Use parameter 7 if you have a .mnu file already in memory and want to keep it there; otherwise, use parameter 6 and the dialog manager will delete the buffer when you're done with it. (If you use parameter 7, then the title you passed in parameter 4 will be ignored, and the buffer name already in existence will be used.) Note that parameter 6 is assumed to be the name of a file in the BHELP directory unless an absolute path name is supplied. The dialog manager assumes that any name containing the characters /, \, or : is an absolute path. Parameter 8 is a string containing the name of your main action macro, described below. Parameter 9 may be omitted. If it is passed and TRUE, your menu file is not formatted in any way, saving oodles of time when it pops up. If you use this parameter, make sure to pre-format your .mnu file. Parameter 10 is an optional integer variable which will be set to the number of the menu button that the user picks. It is only valid when the call to _process_menu returns TRUE. If the user presses or to escape from a menu, this parameter will be zero. Parameter 11 is an optional string variable which will be set to the text of the menu button that the user picks. It is only valud when the call to _process_menu returns TRUE and parameter 10 is nonzero. The dialog manager assumes the values you pass it are correct, so be careful. When the call to _process_menu is encountered, the menu will pop up and processing of the user's keystrokes will begin immediately. Processing will stop when the user presses Esc (or the keypad minus key), and control will return to your macro, just after the _process_menu call. _process_menu returns TRUE if it was able to create and process a menu, FALSE if an error occurred. EVENTS We have written the dialog manager to be as flexible as possible. Hence, the system leaves a lot of decisions and a lot of work up to your action macro. The dialog manager is event-driven; it quietly goes about its business until an "event" of significance occurs, at which point it taps your macro on the shoulder, informs it of the situation, and waits for a response before continuing. Here's a list of all significant events, for both menus and dialog boxes. Matching #define statements are contained in the file "dialog.h". You should #include this file in any macro file you write that calls the dialog manager, so that you can refer to the event types by symbolic name rather than by number. DIALOG_INIT The dialog manager has just been invoked DIALOG_TERM The dialog manager is exiting DIALOG_ENTER_FIELD The cursor has just entered a non-list field DIALOG_EXIT_FIELD The cursor has just left a non-list field DIALOG_ENTER_LIST The cursor has just entered a list DIALOG_EXIT_LIST The cursor has just left a list DIALOG_ALTER_LIST The current item in a list has just been changed DIALOG_ALTER_MENU The current item in a menu has just been changed DIALOG_MOVE_MENU (Same as DIALOG_ALTER_MENU) DIALOG_PICK_MENU The user has just selected a menu button with Enter DIALOG_ESCAPE The user has just pressed Esc to exit all levels DIALOG_F10 The user has just pressed F10 to save a dialog box DIALOG_GREY_MINUS The user has just pressed the keypad minus key DIALOG_CREATE_MENU A menu buffer has just been made current DIALOG_CREATE_DBOX A dialog box data buffer " HOW TO WRITE THE MENU ACTION MACROS When you're working with menus, only the invoke/exit events, the Esc/keypad minus events, and the events for changing or selecting menu items can happen. Your job is to provide a single macro that responds to these events; the name of this macro is parameter 8 in the call to _process_menu, and the macro is called by _process_menu whenever one of these events occurs. Suppose you call your action macro "action". When called, action will be passed three parameters: (action event_type line text) The first parameter is an integer matching one of the above #defines. The second is the number of the current line in the menu (note that this is also the number a user will see as Line: in the BRIEF message area). The third is the text of the entire button, including the delimiter and additional information (if any). In the BRIEF macro language, parameters in the calling function are only evaluated when the called function explicitly requests the parameter. This means that, for best performance, your action macro should never get a parameter it does not need. The normal strategy for processing events within a menu action macro is as follows: 1. Get the event type parameter. 2. If the event type is DIALOG_PICK_MENU, then get the line number or text parameters and process them as desired. Note that the dialog manager does NOT execute an action associated with a button automatically. Your action macro must parse the button text and execute the command. Commonly, a pick event will cause the action macro to pop up a window of text (such as help) and enter a process under the user's control. The window will remain visible until the user presses Esc or Keypad minus to signal he's done. If the user pressed Esc, the action macro should push the Esc back into the keyboard buffer so that a DIALOG_ESC event is generated as soon as the action macro returns. 3. If the event type is DIALOG_CREATE_MENU, the unformatted menu is the current buffer, and its name is the text parameter. You can use this event to add buttons to the menu. (If the event type is anything else, you usually don't need to do any more. However, if you want to add key assignments to the normal menu keymap, you can use DIALOG_INIT as a signal to add them, and DIALOG_TERM as a signal to remove them. And if you want to preclude the user from moving to a particular line of the menu (for example, a heading) then you can use DIALOG_MOVE_MENU.) 3. Return TRUE. The only time you should return FALSE is if the event was DIALOG_MOVE_MENU, and the line number or text was such that you don't want the cursor to be allowed to move to it (other lines will be tried until EOF or until DIALOG_MOVE_MENU succeeds). ACTION MACRO EXAMPLE The following macro, when called by the dialog manager, will handle two types of events. First, the user will not be allowed to move to line 1 or line 4. Second, when the user picks a button, this macro will use the additional information present in the .mnu file to call another macro. (macro action ( (int event_type line_no retval ) (string button_text) (get_parm 0 event_type) (= retval TRUE) (switch event_type DIALOG_MOVE_MENU ( (get_parm 1 line_no) (if (|| (== line_no 1) (== line_no 4)) (= retval FALSE) ) ) DIALOG_PICK_MENU ( (get_parm 2 button_text) ;** Trim the button name and treat the additional ;** information as a command that can be executed. (execute_macro (substr button_text (+ (index button_text ";") 1))) ) ) (returns retval) ) ) DIALOG BOXES HOW TO CREATE A DIALOG BOX As with menus, there are 3 steps to creating a dialog box: creating a data file, writing a macro that calls the dialog manager, and writing action macros. DATA FILES FOR DIALOG BOXES Dialog box data files must be kept in the BHELP directory. Like menu files, they contain one line for each item displayed. Each line contains the type of the input field, the row and column coordinates (relative to the window's origin) at which the field should be placed, and the field's initial contents. The number of fields is unlimited because dialog boxes, like menus, can scroll vertically. The row and column coordinates must be surrounded by parentheses and separated by a comma. The initial contents must be surrounded by double quotes. Whitespace may go just about anywhere, and you can have comments. The following example shows a number of legal ways of defining fields, and descriptive text, in a dialog box: ;** Get the speed using a 3-item list. Text at (1, 1) is "Speed:" List ( 1, 8 ) = "(Slow)Medium Fast" ;** Get the user's name. Make sure they enter one. text (3,1) "Name:" nonblank(3,9) "Your name goes here" ;** Prompt for user's age. Make sure it's numeric. T (5, 1) "Age:" I(5,9)"24" The first non-whitespace character on a line defines the type of the field. A 'T' means that this line is a descriptive text string that can be displayed only. The letters F, L, I, N, and S denote Filenames, Lists, Integers, Nonblank strings, and Strings. All fields, even lists and integers, are really strings. Hence, you should format the initial contents of every field as a quoted string, as in the example above. List strings have a special format. Items must be separated by single tab characters (since spaces are allowed in item names). The item that is to be initially highlighted should be surrounded by parentheses instead of tabs. Position lists as you would any other field. If a list begins with a (, the dialog manager will compensate for you. If you omit the current item in a list, or if you supply a field that's too wide for the window, the invalid field will not be displayed and an error message will. The dialog manager does not check to see if any fields overlap. Hence, only one input field is allowed per line. Text can be placed anywhere except to the right of an input field. HOW TO CALL UP A DIALOG BOX The call to create and process a dialog box is nearly identical to the call to create a menu, except that the macro name is _process_dialog_box: (_process_dialog_box lx by rx ty title msg filename buf_id action) All parameters have the same meaning as for _process_menu, except: 1. There is no automatic sizing for dialog boxes, so lx must not equal rx, and by must not equal ty. 2. Parameter 4, the title, is always used, even when a buffer ID is passed for parameter 7. 3. There are no parameters 9, 10, or 11. This call may return when the user presses F10, Esc, or the keypad minus key. _process_dialog_box returns TRUE if it was successful, FALSE if an error occurred. HOW TO WRITE THE ACTION MACROS The first two parameters passed to your action function are the event type and line number. The third parameter is either (for lists) the text of the current list item, not counting separator characters, or (for other field types) the full text of the field, not counting the newline at the end. Any event may occur in a dialog box except DIALOG_ALTER_MENU or DIALOG_PICK_MENU. However, the only events you normally have to worry about are DIALOG_EXIT_FIELD, DIALOG_EXIT_LIST, and DIALOG_F10. You can perform additional validation on the contents of a field when the user wants to move to another field (DIALOG_EXIT_FIELD). After the dialog manager has performed its built-in type checking on the field, your action macro will be called. If your action macro returns FALSE for any reason, the user will not be allowed to leave the field. You can use a String field coupled with your own validation function to check for almost any value. Lists are not validated, since all the possible answers are known in advance. When the user leaves a field or a list (DIALOG_EXIT_FIELD or DIALOG_EXIT_LIST), keep track of the value of that input field. There is no way to determine the value of an input field at any other time. We recommend you use a separate buffer, where each line represents the contents of one input field, for dialog boxes containing more than three or four input fields; otherwise, use string variables to store the current values. Treat DIALOG_F10 as a signal that the user is satisfied with his or her changes and wishes to save them. The current list or field will be exited before DIALOG_F10 occurs. When it does, process the values you have saved in your macro. (DIALOG_ESC and DIALOG_GREY_MINUS should just ignore the saved values.) EXAMPLE This data file ("example") defines a dialog box containing two lists. Text (1, 3) = "Do you want case-sensitive search?" List (1, 38) = "(Yes)No" Text (3, 3) = "Do you want regular expressions?" List (3, 38) = "(Yes)No" This macro puts up a dialog box containing the above information, and defines two string variables which are used by the action macro. Both variables are set to Yes, since the data file's defaults are Yes. (macro put_up_box ( (string case_sens reg_exp ) (global case_sens reg_exp ) (= case_sens "Yes") (= reg_exp "Yes") (_process_dialog_box 10 15 60 10 "Search" "Set search parameters" "example" NULL "action") ) ) The following macro will process the dialog box, and save the current values when F10 is pressed: (macro action ( (int event_type line_no ) (string button_text) (get_parm 0 event_type) (switch event_type DIALOG_EXIT_LIST ( (get_parm 1 line_no) (get_parm 2 button_text) (if (== line_no 1) (= case_sens button_text) ;else (= reg_exp button_text) ) ) DIALOG_F10 ( (search_case (if (== case_sens "Yes") 0 1)) (toggle_re (if (== reg_exp "Yes") 1 0)) ) ) (returns TRUE) ) ) A more sophisticated macro could keep the data file in a buffer and continuously alter it to make sure the correct values of case sensitivity and regular expressions were always displayed. That's all you need to know to design menus and dialog boxes in the BRIEF macro language. Enjoy.