/* ** BRIEF -- Basic Reconfigurable Interactive Editing Facility ** ** Written by Dave Nanian and Michael Strickman. */ #include "dialog.h" #include "win_ctrl.h" /* ** help.cb: ** ** This macro contains the BRIEF help system. Display of menus and the like ** is handled by the BRIEF dialog manager. */ #define FIRST_MENU_LINE 3 #define FIRST_MENU_COL 20 int _user_kbd; string prev_func; // name of the previous menu action function int process_help_menu (string buf_name, string file_name, ~int height, ~int width); int _help_add_button (~string buffer_name); void to_top (void); string search_path (string path, string name); int display_help (string topic, ~string help_file); void mouse_help(int event, int modifier, int parm2, int parm3); void mouse_help_menu(int event, int modifier, int parm2, int parm3); void _help_down(); void _help_up(); void _help_exit (); /* ** help: ** ** This macro is used as the main help command. It's called when ** the user presses Alt-h. */ void help () { _user_kbd = inq_keyboard (); process_help_menu ("Help Menu", "help.mnu"); } /* ** _context_help: ** ** This macro is used as the "help" registered macro. It is called ** when a prompt is displayed on the status line and the user presses ** Alt-h. Write a replacement macro if you want your own help to be ** be available for your own commands. The macro should check if the ** last command is one of your macros, and if so call display_help ** with the name of your help file; if not, just call _context_help ** (so that all replacement macros are eventually called). */ void _context_help () { _user_kbd = inq_keyboard (); display_help (inq_command ()); } /* ** process_help_menu: ** ** This macro takes four parameters: the buffer name, file name, ** height and width of the menu. It sets things up and calls the ** dialog manager to deal with the actual display and manipulation of ** the menus. (If height & width are omitted, menu is sized ** automatically based on the contents of the menu buffer.) */ int process_help_menu (string buf_name, string file_name, ~int height, ~int width) { int startline, startcol; /* ** Successive menus are offset by a constant amount from the ** first one. The dialog manager automatically keeps track of ** the current menu level. Note that if height and width are ** omitted, height == startline and width == startcol. */ height += startline = FIRST_MENU_LINE + _dialog_level; width += startcol = FIRST_MENU_COL + _dialog_level * 2; /* ** Here we call the dialog manager to create the help menu. ** Whenever an interesting event occurs, the dialog manager will ** call process_help_event. ** ** Note that process_help_event function may call this ** routine to display an additional menu. */ _process_menu (startcol, height, width, startline, buf_name, "Select a Category", file_name, NULL, "process_help_event", TRUE); returns (TRUE); } /* ** process_help_event: ** ** Action macro called by the dialog manager. Used to add buttons ** to the menu or handle picking from the menu. */ int process_help_event (int key, ...) { string action; /* ** If a menu button was picked, we parse the action off ** the menu button and execute it. */ if (key == DIALOG_INIT) { /* ** Setup a mouse event handler to allow button 2 click to ** exit the current menu and Ctrl+ button 2 click to exit help */ prev_func = inq_mouse_action(); set_mouse_action( "mouse_help_menu" ); } else if (key == DIALOG_PICK_MENU) { get_parm (2, action); execute_macro (substr (action, index (action, ";") + 1)); } else if (key == DIALOG_CREATE_MENU) { get_parm (2, action); _help_add_button (action); } returns (TRUE); } /* ** display_help: ** ** display_help takes a help keyword and displays the appropriate ** help information in an overlapping window. It uses the global ** variable _dialog_level to tie the grey - and Esc keystrokes in ** with the rest of the menu package (grey - will remove the window, ** and Esc will remove windows until the _dialog_level is 0). ** ** The optional second parameter is the name of the help file. */ int display_help (string, ~string) { int displayed_help, orig_buffer, file_buffer; string commands = "help.txt"; get_parm (1, commands); if (file_buffer = create_buffer ("Help File", search_path (_dialog_dir, commands), 1)) { string topic; get_parm (0, topic); message ("Locating help text..."); orig_buffer = set_buffer (file_buffer); tabs (3); if (search_fwd (("\xc" + topic) + "\xc") || search_back (("\xc" + topic) + "\xc")) { int loc; message ("Help display for %s.", topic); if (loc = index (commands = read (), "\xc\"")) commands = substr (commands, loc + 1, strlen (commands) - (loc + 2)); else commands = ""; beginning_of_line (); down (); /* ** hide the zoom button and horizontial scroll bars for the help ** information windows */ set_ctrl_state( ZOOM_BTN, HIDE_CTRL ); // dont display a zoom button set_ctrl_state( HORZ_SCROLL, HIDE_CTRL ); // dont display a horizontial scroll bar if (inq_called () == "_context_help") { create_window (2, 21, 76, 2, " for assignment, or to exit help"); } else { create_window (2, 21, 76, 2, " for menu, for assignment, to exit help"); } attach_buffer (file_buffer); keyboard_push (); assign_to_key ("", "_exit"); assign_to_key ("", "_help_exit"); assign_to_key ("", "_help_up"); assign_to_key ("", "_help_down"); assign_to_key ("", "_help_assignment " + commands); /* establish the mouse action handler for help */ set_mouse_action( "mouse_help" ); refresh (); process (); keyboard_pop (); delete_window (); search_back (("\xc" + topic) + "\xc"); beginning_of_line (); displayed_help++; /* ** allow display of zoom button and horizontial scroll bar ** for subsequent window creation */ set_ctrl_state( ZOOM_BTN, SHOW_CTRL ); // display a zoom button set_ctrl_state( HORZ_SCROLL, SHOW_CTRL ); // display a horizontial scroll bar } else error ("No help available for %s.", topic); set_buffer (orig_buffer); } else error ("Help file not found."); returns (displayed_help); } /* ** mouse_help ** ** Handle all the mouse events of interest for help text windows ** ** Mouse assignments ** BTN1_CLICK - display key assignment i.e. ** BTN2_CLICK - to previous screen i.e. ** BTN2_CLICK + CTRL - exit help i.e. ** SET_WIN outside text window - close the window ** */ void mouse_help ( int event, // the event code int modifier, // modifier keys int parm2, // line or scroll bar code int parm3 ) // column or thumb position { /* handle the events of interest */ switch ( event ){ case BTN1_CLICK: // display key assignment execute_macro( inq_assignment( "" )); case CLOSE_WIN: // close the window case BTN2_CLICK: // to previous screen switch ( modifier ){ case 0: // to previous screen exit(); case KB_CTRL: // exit the help system _help_exit(); } case SET_WIN: // click outside window is close window if ( parm2 != inq_window() ) exit(); case VSCROLL: // vertical scroll bar event switch ( parm2 ){ case SB_BOTTOM: // end of this help text { int old_line, line; inq_position (old_line); // save current position search_fwd ("\xc"); // look for next topic in help file inq_position (line); // get its position if (line - old_line <= 18) { /* current topic is only one "page", go to original position */ move_abs (old_line); } else { /* current topic is more than one "page" go to last page */ move_abs ( line - 18 ); } } case SB_TOP: // top of this help text { int old_line, line; inq_position (old_line); // save current position search_back ("\xc"); // look for next topic in help file inq_position (line); // get its position if (old_line - line <= 18) { /* current topic is only one "page", go to original position */ move_abs (old_line); } else { /* current topic is more than one "page" go to first page */ move_abs ( line+1 ); } } case SB_LINEDOWN: case SB_PAGEDOWN: // page down request _help_down(); case SB_LINEUP: case SB_PAGEUP: // page up request _help_up(); } } } /* ** _help_down: ** ** _help_down is called when the PgDn key is pressed: it ensures that ** the user does not go past the end of the help file. Since the command ** name begins with an underscore, the current command is not reassigned. */ void _help_down () { int line, col, old_line, old_col; inq_position (old_line, old_col); search_fwd ("\xc"); inq_position (line, col); move_abs (old_line, old_col); page_down (); if (line - old_line <= 18) move_abs (old_line, old_col); } /* ** _help_up: ** ** Goes up a page in the help file. Since the command name begins ** with an underscore, the current command is not reassigned. */ void _help_up () { int line, col, old_line, old_col; inq_position (old_line, old_col); search_back ("\xc"); inq_position (line, col); move_abs (old_line, old_col); page_up (); if (old_line - line <= 18) move_abs (old_line, old_col); } /* ** key_specific_help: ** ** Gets help for the user using the keys, not the menus. */ void key_specific_help () { int saved_level = _dialog_level, key_value; string sequence, assignment; /* ** The dialog level is saved and restored when we enter and exit ** key-specific help. During execution, we set the _dialog_level to ** 0; a press of Esc will then only remove the help screen. ** ** If this wasn't done, extra "exit" calls would be done, and ** the next time a window came up it would be removed immediately. */ _dialog_level = 0; keyboard_push (_user_kbd); message ("Press the key you want help on, to exit."); while (key_value != key_to_int ("")) { while ((key_value = read_char ()) == -1); sprintf (sequence, "#%u", key_value); /* ** If the key assignment we have obtained is ambiguous, we keep ** appending additional key assignments to it until we find a ** command (or fail). */ while ((assignment = inq_assignment (sequence)) == "ambiguous") { message ("Press next key in sequence, to exit."); while ((key_value = read_char ()) == -1); if (key_value == key_to_int ("")) break; sprintf (sequence, "%s#%u", sequence, key_value); } if (key_value == key_to_int ("")) break; if (display_help (assignment)) message ("Press the key you want help on, to exit."); } _dialog_level = saved_level; keyboard_pop (1); message (""); } /* ** _help_assignment: ** ** This macro takes a command name as a parameter, creates a window ** and displays the keys assigned to that command. */ _help_assignment (string commands) { string this_command, keys; int old_buf, key_buf, loc, comma_loc, semi_loc, key_read, have_assignment; keyboard_push (_user_kbd); old_buf = set_buffer (key_buf = create_buffer ("Keys", NULL, 1)); tabs (4); while (strlen (commands)) { if (semi_loc && (semi_loc < comma_loc || !comma_loc)) insert ("\n\nSee also:\n"); comma_loc = index (commands, ","); semi_loc = index (commands, ";"); if (semi_loc && (semi_loc < comma_loc || !comma_loc)) loc = semi_loc; else loc = comma_loc; if (loc) { this_command = substr (commands, 1, loc - 1); commands = substr (commands, loc + 1); } else { this_command = commands; commands = ""; } if (this_command != "") { ++have_assignment; if ((keys = inq_assignment (this_command, 1)) == "") keys = inq_assignment ("execute_macro", 1) + "<-and> " + this_command; insert ("\n \"" + this_command + "\" is assigned to:\n\t\t"); while (loc = index (keys, "<-")) { insert (substr (keys, 1, loc - 1)); keys = substr (keys, loc); switch (substr (keys, 1, index (keys, ">"))) { case "<-and>": keys = substr (keys, 7); case "<-also>": { insert ("\n\t\t"); keys = substr (keys, 8); } default: { keys = ""; /* ** Note: this breaks from the loop, not the switch. */ break; } } } insert (keys + "\n"); } } /* ** If any assignments were found, we display them in a window. */ if (have_assignment) { keyboard_push (); assign_to_key ("", "_exit"); assign_to_key ("", "_exit"); assign_to_key ("", "_assign_up"); assign_to_key ("", "_assign_down"); top_of_buffer (); create_window (6, 15, 72, 4, " or to exit, or to scroll"); attach_buffer (key_buf); /* establish the mouse action handler for this help window */ set_mouse_action( "mouse_help" ); refresh (); process (); keyboard_pop (); delete_window (); } if (!have_assignment) beep (); delete_buffer (key_buf); set_buffer (old_buf); keyboard_pop (1); } void _assign_up () { page_up (); } void _assign_down () { page_down (); move_rel (inq_position () * -1); } /* ** _help_exit: ** ** Exits from the help system; calls exit to remove the help ** window, and pushes back an if the dialog manager is active ** to remove the other (menu) windows. */ void _help_exit () { if (_dialog_level) push_back (key_to_int ("")); exit (); } /* ** _help_add_button: ** ** Used to add buttons to existing menus. This macro does nothing, ** but you can write a replacement macro for it that checks the only ** parameter (the buffer name) and adds buttons. When your replacement ** macro is called, the menu buffer will be current and the menu will ** be unformatted (just as it is on disk). ** Make sure your replacement calls _help_add_button so that all ** other replacement macros eventually get called. */ int _help_add_button (~string) { returns (TRUE); } /* ** mouse_help_menu ** ** Sub-class the default menu handler for the help system ** ** Mouse action assignments: ** default menu actions plus ** Click 2 - exit this menu ** Dbl Click 2 - exit the help system ** */ void mouse_help_menu(int event, // the event code int modifier, // modifier keys int parm2, // line or scroll bar code int parm3 ) // column or thumb position { /* handle the events of interest */ switch ( event ){ case BTN2_CLICK: // to previous screen switch ( modifier ){ case 0: // exit this menu exit(); case KB_CTRL: // exit the help system _help_exit(); } default: /* let the previous event handler for menus process the event */ execute_macro( prev_func, event, modifier, parm2, parm3 ); } }