/* ** BRIEF -- Basic Reconfigurable Interactive Editing Facility ** ** Written by Dave Nanian and Michael Strickman. */ /* ** keys.cb: ** ** This macro allows basic reconfiguration of the key assignments ** for the commands provided with BRIEF. It displays a menu containing ** a list of commands, and an adjacent window containing the key ** assignment(s) for the currently highlighted command. By selecting ** a command, the user can move into the window, and add or delete ** key assignments with Ins and Del. ** ** The macro uses the Dialog Manager for the menu it displays. The ** key assignments are changed by modifying and/or adding assign_to_key ** calls to keyboard.h. If no keyboard.h can be found, the macro can ** be used as an online quick reference card, to find the key sequence ** associated with each command. If a non-zero parameter is supplied, ** the macro inserts the key assignments into the current buffer, instead ** of keyboard.h. */ #include "dialog.h" #include "win_ctrl.h" int menu_buf, key_buf, include_buf, menu_win, key_win, orig_keyboard, key_parm; string search_text, bpath, include_file, button_text, assign_text; string default_mouse_handler; // default left side menu handler for mouse. string add_to_path (string path, string file); void _keys_message (); void _keys_menu_message (); string _get_command_str (); string search_path (string pathvar, string filename); void mouse_keys_left(); void mouse_keys_right(); void _dialog_menu_pick(); void mouse_menu(int, int, int, int); /* ** keys: ** ** This macro sets up the buffers needed: one for the command menu, ** one for the key assignment display, and one for keyboard.h. It then ** calls the Dialog Manager to process commands, and when the user ** finishes, it writes keyboard.h and registers a macro to recompile ** startup, if necessary. */ void keys () { int old_buffer = inq_buffer (); string message_str; orig_keyboard = inq_keyboard (); include_file = add_to_path (_dialog_dir, "commands.mnu"); if (!exist (include_file)) { error ("Unable to locate help files (commands.mnu)."); return; } menu_buf = create_buffer ("Commands", include_file, 1); set_buffer (menu_buf); search_text = read (); bpath = inq_environment ("BPATH"); if (substr (bpath, 1, 1) == ";") bpath = substr (bpath, 2); if (substr (bpath, 1, 1) == ".") bpath = substr (bpath, index (bpath, ";") + 1); if (bpath == "") bpath = "/brief/macros"; include_file = search_path (bpath, "keyboard.h"); key_parm = 0; get_parm (0, key_parm); if (!key_parm) include_buf = include_file == "" ? 0 : create_buffer ("Macro", include_file, 1); else { key_parm = old_buffer; include_buf = 0; } key_buf = create_buffer ("Assignments", NULL, 1); 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( CLOSE_BTN, HIDE_CTRL); // hide the close button for this window create_window (41, 21, 72, 1, include_buf || key_parm ? ", Del to delete, Ins to add" : "Current Key Assignments"); 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( CLOSE_BTN, SHOW_CTRL); // show the close button for this window key_win = inq_window (); attach_buffer (key_buf); set_buffer (key_buf); refresh (); if (include_buf || key_parm) sprintf (message_str, ", , %c, Enter, Ins, or Esc", 26); else message_str = ", , Enter, or Esc"; _process_menu (7, 1, 38, 1, NULL, message_str, NULL, menu_buf, "_keys_menu", 1); set_window (key_win); delete_window (); if (include_buf) { set_buffer (include_buf); if (inq_modified ()) { if (get_parm (NULL, message_str, "Save changes permanently [yn]? ", 1) && index ("nN", message_str)) message ("Changes only effective for this editing session."); else { message (""); // Clear cancelled message if there. register_macro (5, "_keys_exit"); write_buffer (); message ("Changes saved."); } } if (old_buffer != include_buf) delete_buffer (include_buf); } set_buffer (old_buffer); delete_buffer (menu_buf); delete_buffer (key_buf); call_registered_macro (1); } /* ** _keys_menu: ** ** This macro is the action routine for the command menu. It handles ** initialization, changing the menu selection, choosing a menu item, ** and terminating the menu. All other menu events are irrelevant. */ int _keys_menu (int event_type, ~, string) { int text_found, old_keyboard; switch (event_type) { case DIALOG_INIT: { menu_win = inq_window (); get_parm (2, button_text); _keys_menu_message (); button_text = substr (button_text, index (button_text, ";") + 1); sprintf (search_text, "assign_to_key (\"%%s\", \"%s\"", button_text); set_buffer (key_buf); old_keyboard = inq_keyboard (); keyboard_push (orig_keyboard); assign_text = inq_assignment (button_text, 1); if (assign_text == "") assign_text = _get_command_str () + button_text; while (!inq_position ()) delete_line (); insert (assign_text); top_of_buffer (); translate ("\\<-also\\>", "\n", 1); translate ("{\\<-and\\>}|{\\<-or\\>\\<*\\>}", "", 1); set_buffer (menu_buf); keyboard_pop (1); assign_to_key ("", "_dialog_menu_pick"); if (include_buf || key_parm) { assign_to_key ("", "_keys_insert"); if (key_parm) assign_to_key ("", "_keys_insert"); } if (include_buf) message ("Select item with Enter, add assignment with Ins."); else message (" and  to view key assignments."); /* sub-class the mouse event handler for the menu */ default_mouse_handler = inq_mouse_action(); // save current one set_mouse_action( "mouse_keys_left" ); // establich new one } case DIALOG_ALTER_MENU: { _keys_menu_message (); get_parm (2, button_text); button_text = substr (button_text, index (button_text, ";") + 1); sprintf (search_text, "assign_to_key (\"%%s\", \"%s\"", button_text); set_buffer (key_buf); old_keyboard = inq_keyboard (); keyboard_push (orig_keyboard); assign_text = inq_assignment (button_text, 1); if (assign_text == "") assign_text = _get_command_str () + button_text; while (!inq_position ()) delete_line (); insert (assign_text); top_of_buffer (); translate ("\\<-also\\>", "\n", 1); translate ("{\\<-and\\>}|{\\<-or\\>\\<*\\>}", "", 1); set_buffer (menu_buf); keyboard_pop (1); } case DIALOG_PICK_MENU: { if (!include_buf) { error ("Unable to find macro source; can't reconfigure."); return (1); } set_window (key_win); keyboard_push (); if (strlen (ltrim (read ()))) drop_anchor (3); _keys_message (); assign_to_key ("", "_keys_up"); assign_to_key ("", "_keys_down"); assign_to_key ("", "_keys_insert"); assign_to_key ("", "_keys_delete"); assign_to_key ("", "_keys_delete"); assign_to_key ("", "exit"); assign_to_key ("", "exit"); assign_to_key ("", "exit"); refresh (); /* specify the mouse event handler for the right-hand window */ set_mouse_action( "mouse_keys_right" ); process (); keyboard_pop (); /* If the window has not yet been reset, need to raise the ** anchor */ if (inq_window() != menu_win) raise_anchor (); set_window (menu_win); _keys_menu_message (); } case DIALOG_TERM: { message ("Returning to editing session..."); assign_to_key ("", "nothing"); assign_to_key ("", "nothing"); assign_to_key ("", "_dialog_menu_pick"); } } return (1); } /* ** _keys_up: ** ** When the user presses the up arrow in the Assignments window, ** this macro is called. If the user isn't at the top of the buffer, ** it moves the mark up one line. */ void _keys_up () { int line; inq_position (line); if (line > 1) { move_rel (-1, 0); raise_anchor (); drop_anchor (3); } } /* ** _keys_down: ** ** Likewise, when the user presses the down arrow in the Assignments ** window, this macro is called. If the user isn't at the bottom of the ** buffer, it moves the mark down one line. */ void _keys_down () { move_rel (1, 0); if (!inq_position ()) { raise_anchor (); drop_anchor (3); } else move_rel (-1, 0); } /* ** _keys_insert: ** ** This one does all the work. If the user wants to add a key ** assignment, it asks for the new assignment, gets rid of the old ** assignment (both in the Assignment window and in the keyboard.h ** file), and inserts the new assign_to_key statement at the bottom ** of the first macro in keyboard.h (the keyboard macro), or in the ** current buffer, depending on the parameter passed to keys. */ void _keys_insert () { int key_value, old_win = inq_window (), old_buffer, found, i, abort_insert; string key_string, insert_string, cur_key; old_buffer = inq_buffer (); set_window (key_win); message ("Type the key sequence; press Enter to end."); while ((key_value = read_char ()) == -1); insert ("\n"); up (); while (key_value != key_to_int ("")) { cur_key = int_to_key (key_value); error ("cur_key is %s", cur_key); insert (cur_key); key_string += cur_key; message ("Continue; press Enter to end, Esc to cancel.", key_string); if (++i == 5) break; while ((key_value = read_char ()) == -1); if (key_value == key_to_int ("")) { abort_insert = 1; break; } } delete_line (); beginning_of_line (); if (abort_insert) message ("Keystroke cancelled."); else { if (key_string == "") key_string = ""; message ("Processing assignment..."); keyboard_push (orig_keyboard); assign_to_key (key_string, button_text); keyboard_pop (1); save_position (); top_of_buffer (); found = search_fwd (key_string, 0, NULL, 0); restore_position (); set_buffer (old_buffer); if (found) error ("Key sequence already assigned."); else { sprintf (insert_string, "assign_to_key (\"%s\",", key_string); if (include_buf) { set_buffer (include_buf); top_of_buffer (); if (search_fwd (insert_string, 0)) { delete_line (); beginning_of_line (); } } insert_string += " \""; insert_string += button_text; if (include_buf) { if (search_fwd ("<\\c\\}")) { insert ("\t\t" + insert_string + "\");\n"); up (); sprintf (insert_string, "Assigned %s to %%s.", button_text); message (insert_string, key_string); } else error ("Unable to locate end of macro."); } else { if (key_parm) { set_buffer (key_parm); insert (insert_string + "\");\n"); } message ("String inserted in current buffer."); } set_buffer (key_buf); insert (key_string + "\n"); raise_anchor (); if (read () == "\n") delete_line (); up (); drop_anchor (3); } } set_buffer (old_buffer); set_window (old_win); } /* ** _keys_delete: ** ** This macro is called when the user deletes a key assignment. ** It assigns the key sequence to "nothing" in the original keyboard, ** and deletes the text from the Assignments window and the keyboard.h ** file (if it is there). */ void _keys_delete () { string key_string, search_str, command_str; key_string = trim (read ()); command_str = _get_command_str (); if (substr (key_string, 1, strlen (command_str)) != command_str) { keyboard_push (orig_keyboard); assign_to_key (key_string, "nothing"); set_buffer (include_buf); message ("Locating old assignment..."); top_of_buffer (); sprintf (search_str, search_text, key_string); if (search_fwd (search_str, 0, 0)) { delete_line (); beginning_of_line (); } else if (search_fwd ("<\\c[ \t]+)\n)")) { sprintf (search_str, "\t\tassign_to_key (\"%s\", \"nothing\");\n", key_string); insert (search_str); up (); } else error ("Unable to locate end of macro."); set_buffer (key_buf); set_window (key_win); delete_block (); if (inq_position ()) up (); if (strlen (ltrim (read ()))) drop_anchor (3); keyboard_pop (1); _keys_message (); } } /* ** _keys_exit: ** ** This macro is called when the user exits, if there have been ** any modifications to keyboard.h. It recompiles the startup macro, ** so the assignments will be in effect next time BRIEF is run. */ void _keys_exit (...) { int old_buffer = inq_buffer (); string command_string; if (include_buf = create_buffer ("Macros", include_file, 1)) { set_buffer (include_buf); message ("Recompiling startup macro..."); if (inq_modified ()) write_buffer (); sprintf (command_string, "cb %s >&temp.$b$", search_path (bpath, "startup.cb")); if (dos (command_string)) { int old_pause_on_error = pause_on_error (1); error ("Compile failed; run \"cb startup\". Press a key."); pause_on_error (old_pause_on_error); } else message ("Reconfiguration complete."); del ("temp.$b$"); } else error ("Unable to locate %s.", include_file); set_buffer (old_buffer); } /* ** _keys_message, _keys_menu_message: ** ** These macros simply display the appropriate message when ** they are called. */ void _keys_message () { message ("Press Ins to add assignment, Del to delete."); } void _keys_menu_message () { if (include_buf) message ("Select item with Enter, add assignment with Ins."); else message (" and  to view key assignments."); } /* ** _get_command_str: ** ** Gets the key sequence that is used for the execute_macro ** primitive. If multiple assignments exist, only the first one ** is used. */ string _get_command_str () { string command_str; int command_len; keyboard_push (orig_keyboard); command_str = inq_assignment ("execute_macro", 1); keyboard_pop (1); if (command_len = index (command_str, "<-and>")) command_str = substr (command_str, 1, command_len - 1); if (command_len = index (command_str, "<-also>")) command_str = substr (command_str, 1, command_len - 1); returns (command_str + " "); } /* ** mouse_keys_right ** ** Handle all the mouse events of interest for the right-hand ** window of the keys macro ** ** Mouse action assignments: ** default menu assignments plus ** Click 2 - insert new entry - like paste ** Ctrl+ Click 2 - delete entry - like cut ** If the user clicks in the other left-hand menu switch processing ** to that menu. Click outside any window exits. ** ** int event, the event code ** int modifier modifier keys ** int parm2 line or scroll bar code ** int parm3 column or thumb position ** */ void mouse_keys_right(int event, int modifier, int parm2, int parm3 ) { /* handle the events of interest */ switch ( event ){ case BTN1_DOWN: // highlite a menu entry case BTN1_UP: // highlite a menu entry case BTN1_MOVE: // highlite a menu entry case BTN1_CLICK: // pick a menu choice { int lines; if ( modifier == 0 ) // make sure no modifiers present { /* determine the number of lines in the buffer */ save_position(); end_of_buffer(); // position to the end of the buffer inq_position( lines ); restore_position(); if ( 0 < parm2 && parm2 <= lines ) { inq_position(lines); if (lines != parm2) // did the mouse move to a new entry? { raise_anchor(); // erase current highlight move_abs( parm2, 0 ); // position to choice that was clicked on drop_anchor(3); // highlight the menu choice } } } } case BTN2_CLICK: // insert or delete a key assignment switch ( modifier ) { case KB_CTRL: _keys_delete(); case 0: _keys_insert(); } case SET_WIN: // the user clicked in another window /* was the left-hand menu selected?? */ if ( parm2 == menu_win ) { /* ** YES - exit this mouse event handler and let the menu event handler ** for the left menu take over. Before exiting this process loop ** via the exit(), set the new current window to the left ** menu to allow the mouse events for the window to come thru. ** Specifically the BTN1_DOWN & BTN1_UP that follow the SET_WIN ** event. */ raise_anchor (); set_window( parm2 ); /* ** Switch the action handler to the left menu because the ** button down event will come thru here before Brief switches ** out of this process loop. We will figure out some way ** so this does not need to be done in the future. */ set_mouse_action( "mouse_keys_left" ); exit(); } else { /* a click outside both windows says to exit keys */ exit(); /* send the event to the left menu handler */ set_window( menu_win ); execute_macro( "mouse_keys_left", event, modifier, parm2, parm3 ); } /* ** Since there is no close button on the right window, see if the ** user clicked on the close button of the left window. If so ** exit keys. */ case CLOSE_WIN: if ( parm2 == menu_win ) { exit(); /* send the event to the left menu handler */ set_window( menu_win ); execute_macro( "mouse_keys_left", event, modifier, parm2, parm3 ); } } } /* ** mouse_keys_left ** ** Sub-class the default menu handler for the left menu ** ** int event, the event code ** int modifier modifier keys ** int parm2 line or scroll bar code ** int parm3 column or thumb position ** */ void mouse_keys_left(int event, int modifier, int parm2, int parm3) { /* handle the events of interest */ switch ( event ) { case SET_WIN: // if the user clicked in the right menu /* Jam an Enter to bring us to the right window */ if ( parm2 == key_win ) push_back(key_to_int("")); /* let the previous event handler for menus process the event */ else execute_macro( default_mouse_handler, event, modifier, parm2, parm3 ); default: /* let the previous event handler for menus process the event */ execute_macro( default_mouse_handler, event, modifier, parm2, parm3 ); } }