001: /*
002: * This is free software, licensed under the Gnu Public License (GPL)
003: * get a copy from <http://www.gnu.org/licenses/gpl.html>
004: *
005: * author: Henner Zeller <H.Zeller@acm.org>
006: */
007: package henplus.commands;
008:
009: import henplus.io.ConfigurationContainer;
010: import henplus.view.util.NameCompleter;
011: import henplus.CommandDispatcher;
012: import henplus.SQLSession;
013: import henplus.HenPlus;
014: import henplus.AbstractCommand;
015:
016: import henplus.view.*;
017: import java.util.StringTokenizer;
018: import java.util.Iterator;
019: import java.util.Map;
020: import java.util.TreeMap;
021: import java.util.HashMap;
022:
023: import org.gnu.readline.Readline;
024:
025: /**
026: * Command to bind function keys to commands.
027: */
028: public class KeyBindCommand extends AbstractCommand {
029: private final static String KEYBIND_FILENAME = "key-bindings";
030: private final static ColumnMetaData[] DRV_META;
031: static {
032: DRV_META = new ColumnMetaData[2];
033: DRV_META[0] = new ColumnMetaData("Key");
034: DRV_META[1] = new ColumnMetaData("execute command");
035: }
036:
037: private final ConfigurationContainer _config;
038:
039: private final Map/*<String:keyname,String:readlinename>*/_keyNames;
040: private final NameCompleter _functionKeyNameCompleter;
041: private final Map/*<String:key,String:cmd>*/_bindings;
042:
043: /**
044: * returns the command-strings this command can handle.
045: */
046: public String[] getCommandList() {
047: return new String[] { "list-key-bindings", "bind-key-cmd",
048: //"unbind-key",
049: //"test-bind-all" // uncomment for tests.
050: };
051: }
052:
053: public KeyBindCommand(HenPlus henplus) {
054: _keyNames = new HashMap();
055: //-- there are different mappings for some function keys and terminals
056: _keyNames.put("F1", new String[] { "\"\\e[11~\"", "\"\\e[[a\"",
057: "\"\\eOP\"" });
058: _keyNames.put("Shift-F1", new String[] { "\"\\e[25~\"",
059: "\"\\eO2P\"" });
060: _keyNames.put("F2", new String[] { "\"\\e[12~\"", "\"\\e[[b\"",
061: "\"\\eOQ\"" });
062: _keyNames.put("Shift-F2", new String[] { "\"\\e[26~\"",
063: "\"\\eO2Q\"" });
064: _keyNames.put("F3", new String[] { "\"\\e[13~\"", "\"\\e[[c\"",
065: "\"\\eOR\"" });
066: _keyNames.put("Shift-F3", new String[] { "\"\\e[28~\"",
067: "\"\\eO2R\"" });
068: _keyNames.put("F4", new String[] { "\"\\e[14~\"", "\"\\e[[d\"",
069: "\"\\eOS\"" });
070: _keyNames.put("Shift-F4", new String[] { "\"\\e[29~\"",
071: "\"\\eO2S\"" });
072: _keyNames.put("F5",
073: new String[] { "\"\\e[15~\"", "\"\\e[[e\"" });
074: _keyNames.put("Shift-F5", new String[] { "\"\\e[15;2~\"",
075: "\"\\e[31~\"" });
076: _keyNames.put("F6", new String[] { "\"\\e[17~\"" });
077: _keyNames.put("Shift-F6", new String[] { "\"\\e[17;2~\"",
078: "\"\\e[32~\"" });
079: _keyNames.put("F7", new String[] { "\"\\e[18~\"" });
080: _keyNames.put("Shift-F7", new String[] { "\"\\e[18;2~\"",
081: "\"\\e[33~\"" });
082: _keyNames.put("F8", new String[] { "\"\\e[19~\"" });
083: _keyNames.put("Shift-F8", new String[] { "\"\\e[19;2~\"",
084: "\"\\e[34~\"" });
085: // for the linux console, there seem no bindings for F9-F12
086: _keyNames.put("F9", new String[] { "\"\\e[20~\"" });
087: _keyNames.put("Shift-F9", new String[] { "\"\\e[20;2~\"" });
088: _keyNames.put("F10", new String[] { "\"\\e[21~\"" });
089: _keyNames.put("Shift-F10", new String[] { "\"\\e[21;2~\"" });
090: _keyNames.put("F11", new String[] { "\"\\e[23~\"" });
091: _keyNames.put("Shift-F11", new String[] { "\"\\e[23;2~\"" });
092: _keyNames.put("F12", new String[] { "\"\\e[24~\"" });
093: _keyNames.put("Shift-F12", new String[] { "\"\\e[24;2~\"" });
094: _functionKeyNameCompleter = new NameCompleter(_keyNames
095: .keySet());
096: _config = henplus
097: .createConfigurationContainer(KEYBIND_FILENAME);
098: _bindings = new TreeMap();
099: bindKey("F1", "help\n"); // a common default binding.
100: load();
101: }
102:
103: /**
104: * execute the command given.
105: */
106: public int execute(SQLSession session, String cmd, String param) {
107: StringTokenizer st = new StringTokenizer(param);
108: int argc = st.countTokens();
109:
110: if ("list-key-bindings".equals(cmd)) {
111: if (argc != 0)
112: return SYNTAX_ERROR;
113: showKeyBindings();
114: return SUCCESS;
115: }
116:
117: else if ("test-bind-all".equals(cmd)) {
118: testBindAll();
119: return SUCCESS;
120: }
121:
122: else /* bind-key-cmd-string */{
123: if (!st.hasMoreTokens())
124: return SYNTAX_ERROR;
125:
126: String key = st.nextToken();
127: key = _functionKeyNameCompleter.findCaseInsensitive(key);
128: if (key == null)
129: return SYNTAX_ERROR;
130:
131: String value = param.substring(key.length() + 1).trim();
132: value = stripQuotes(value);
133: if (value.length() == 0)
134: return SYNTAX_ERROR;
135:
136: return bindKey(key, value) ? SUCCESS : EXEC_FAILED;
137: }
138: }
139:
140: public Iterator complete(CommandDispatcher disp,
141: String partialCommand, final String lastWord) {
142: if (argumentCount(partialCommand) > ("".equals(lastWord) ? 1
143: : 2)) {
144: return null;
145: }
146: return _functionKeyNameCompleter.getAlternatives(lastWord);
147: }
148:
149: private String stripQuotes(String value) {
150: if (value.length() < 2)
151: return value;
152: if (value.startsWith("\"") && value.endsWith("\"")) {
153: value = value.substring(1, value.length() - 1);
154: } else if (value.startsWith("\'") && value.endsWith("\'")) {
155: value = value.substring(1, value.length() - 1);
156: }
157: return value;
158: }
159:
160: /**
161: * For tests: bind the key to its name representation.
162: */
163: private void testBindAll() {
164: Iterator it = _keyNames.keySet().iterator();
165: while (it.hasNext()) {
166: String keyName = (String) it.next();
167: bindKey(keyName, keyName);
168: }
169: }
170:
171: /**
172: * Bind a key with the symbolic name to the given string.
173: */
174: private boolean bindKey(String keyName, String cmd) {
175: String[] terminalValues = (String[]) _keyNames.get(keyName);
176: if (terminalValues == null) {
177: return false;
178: }
179: _bindings.put(keyName, cmd);
180:
181: /* quote "-characters */
182: StringBuffer binding = new StringBuffer();
183: int pos = 0;
184: int nowPos = 0;
185: while ((nowPos = cmd.indexOf('"', pos)) >= 0) {
186: binding.append(cmd.substring(pos, nowPos));
187: binding.append("\\\"");
188: pos = nowPos + 1;
189: }
190: if (pos < cmd.length()) {
191: binding.append(cmd.substring(pos));
192: }
193:
194: String bindCmd = binding.toString();
195: for (int i = 0; i < terminalValues.length; ++i) {
196: Readline.parseAndBind(terminalValues[i] + ": \"" + bindCmd
197: + "\"");
198: }
199: return true;
200: }
201:
202: public void shutdown() {
203: _config.storeProperties(_bindings, true, "Key-Bindings..");
204: }
205:
206: /**
207: * initial load of key bindings
208: */
209: private void load() {
210: Map bindings = _config.readProperties(_bindings);
211: Iterator it = bindings.entrySet().iterator();
212: while (it.hasNext()) {
213: Map.Entry entry = (Map.Entry) it.next();
214: bindKey((String) entry.getKey(), (String) entry.getValue());
215: }
216: }
217:
218: private void showKeyBindings() {
219: DRV_META[0].resetWidth();
220: DRV_META[1].resetWidth();
221: TableRenderer table = new TableRenderer(DRV_META, HenPlus.out());
222: Iterator it = _bindings.entrySet().iterator();
223: while (it.hasNext()) {
224: Map.Entry entry = (Map.Entry) it.next();
225: Column[] row = new Column[2];
226: row[0] = new Column((String) entry.getKey());
227: row[1] = new Column((String) entry.getValue());
228: table.addRow(row);
229: }
230: table.closeTable();
231: }
232:
233: public boolean requiresValidSession(String cmd) {
234: return false;
235: }
236:
237: /**
238: * return a descriptive string.
239: */
240: public String getShortDescription() {
241: return "handle function key bindings";
242: }
243:
244: public String getSynopsis(String cmd) {
245: if ("bind-key-cmd".equals(cmd)) {
246: return cmd + " <function-key> <command>";
247: }
248: return cmd;
249: }
250:
251: public String getLongDescription(String cmd) {
252: String dsc = null;
253: if ("list-key-bindings".equals(cmd)) {
254: dsc = "\tList all key bindings that have been set with\n"
255: + "\tbind-key-cmd";
256: } else if ("bind-key-cmd".equals(cmd)) {
257: dsc = "\tBind a key to a command. Keys can be the function keys\n"
258: + "\tF1, F2...F12 or Shift-F1...Shift-F12";
259: }
260: return dsc;
261: }
262: }
263:
264: /*
265: * Local variables:
266: * c-basic-offset: 4
267: * compile-command: "ant -emacs -find build.xml"
268: * End:
269: */
|