00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 package com.realtime.crossfire.jxclient.gui.keybindings;
00023
00024 import com.realtime.crossfire.jxclient.gui.commandlist.CommandList;
00025 import com.realtime.crossfire.jxclient.gui.commandlist.CommandListType;
00026 import com.realtime.crossfire.jxclient.gui.textinput.GUICommandFactory;
00027 import java.awt.event.KeyEvent;
00028 import java.io.BufferedWriter;
00029 import java.io.File;
00030 import java.io.FileInputStream;
00031 import java.io.FileNotFoundException;
00032 import java.io.FileOutputStream;
00033 import java.io.IOException;
00034 import java.io.InputStreamReader;
00035 import java.io.LineNumberReader;
00036 import java.io.OutputStreamWriter;
00037 import java.util.Collection;
00038 import java.util.HashSet;
00039 import org.jetbrains.annotations.NotNull;
00040 import org.jetbrains.annotations.Nullable;
00041
00046 public class KeyBindings {
00047
00051 @NotNull
00052 private final GUICommandFactory guiCommandFactory;
00053
00057 @NotNull
00058 private final Collection<KeyBinding> keybindings = new HashSet<KeyBinding>();
00059
00064 private boolean modified = false;
00065
00069 @Nullable
00070 private final File file;
00071
00075 @Nullable
00076 private KeyCodeMap keyCodeMap = null;
00077
00084 public KeyBindings(@Nullable final File file, @NotNull final GUICommandFactory guiCommandFactory) {
00085 this.file = file;
00086 this.guiCommandFactory = guiCommandFactory;
00087 }
00088
00093 @Nullable
00094 public File getFile() {
00095 return file;
00096 }
00097
00106 public void addKeyBindingAsKeyCode(final int keyCode, final int modifiers, @NotNull final CommandList cmdList, final boolean isDefault) {
00107 addKeyBinding(new KeyCodeKeyBinding(keyCode, modifiers, cmdList, isDefault));
00108 }
00109
00117 public void addKeyBindingAsKeyChar(final char keyChar, @NotNull final CommandList cmdList, final boolean isDefault) {
00118 addKeyBinding(new KeyCharKeyBinding(keyChar, cmdList, isDefault));
00119 }
00120
00125 private void addKeyBinding(@NotNull final KeyBinding keyBinding) {
00126 keybindings.remove(keyBinding);
00127 keybindings.add(keyBinding);
00128 modified = true;
00129 try {
00130 saveKeyBindings();
00131 } catch (final IOException ex) {
00132 System.err.println("Cannot write keybindings file "+file+": "+ex.getMessage());
00133 }
00134 }
00135
00141 public void deleteKeyBindingAsKeyCode(final int keyCode, final int modifiers) {
00142 deleteKeyBinding(getKeyBindingAsKeyCode(keyCode, modifiers));
00143 }
00144
00149 private void deleteKeyBinding(@Nullable final KeyBinding keyBinding) {
00150 if (keyBinding != null) {
00151 keybindings.remove(keyBinding);
00152 modified = true;
00153 try {
00154 saveKeyBindings();
00155 } catch (final IOException ex) {
00156 System.err.println("Cannot write keybindings file "+file+": "+ex.getMessage());
00157 }
00158 }
00159 }
00160
00165 public void loadKeyBindings() throws IOException {
00166 modified = false;
00167
00168 if (file == null) {
00169 return;
00170 }
00171
00172 try {
00173 final FileInputStream fis = new FileInputStream(file);
00174 try {
00175 final InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
00176 try {
00177 final LineNumberReader lnr = new LineNumberReader(isr);
00178 try {
00179 while (true) {
00180 final String line = lnr.readLine();
00181 if (line == null) {
00182 break;
00183 }
00184
00185 try {
00186 parseKeyBinding(line, false);
00187 } catch (final InvalidKeyBindingException ex) {
00188 System.err.println("ignoring invalid key binding ("+ex.getMessage()+"): "+line);
00189 }
00190 }
00191 } finally {
00192 lnr.close();
00193 }
00194 } finally {
00195 isr.close();
00196 }
00197 } finally {
00198 fis.close();
00199 }
00200 } catch (final FileNotFoundException ignored) {
00201
00202 keybindings.clear();
00203 } catch (final IOException ex) {
00204 keybindings.clear();
00205 modified = false;
00206 throw ex;
00207 }
00208
00209 modified = false;
00210 }
00211
00216 public void saveKeyBindings() throws IOException {
00217 if (file == null || !modified) {
00218 return;
00219 }
00220
00221 if (keybindings.size() <= 0) {
00222 if (!file.delete()) {
00223 throw new IOException("cannot delete file");
00224 }
00225 return;
00226 }
00227
00228 final FileOutputStream fos = new FileOutputStream(file);
00229 try {
00230 final OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
00231 try {
00232 final BufferedWriter bw = new BufferedWriter(osw);
00233 try {
00234 for (final KeyBinding keyBinding : keybindings) {
00235 if (keyBinding.isDefault()) {
00236
00237 } else if (keyBinding instanceof KeyCodeKeyBinding) {
00238 if (keyCodeMap == null) {
00239 keyCodeMap = new KeyCodeMap();
00240 }
00241
00242 final KeyCodeKeyBinding keyCodeKeyBinding = (KeyCodeKeyBinding)keyBinding;
00243 bw.write("code ");
00244 bw.write(keyCodeMap.getKeyName(keyCodeKeyBinding.getKeyCode()));
00245 bw.write(' ');
00246 bw.write(Integer.toString(keyCodeKeyBinding.getModifiers()));
00247 bw.write(' ');
00248 bw.write(GUICommandFactory.encode(keyCodeKeyBinding.getCommandString()));
00249 bw.newLine();
00250 } else {
00251 throw new AssertionError("Cannot encode "+keyBinding.getClass().getName());
00252 }
00253 }
00254 } finally {
00255 bw.close();
00256 }
00257 } finally {
00258 osw.close();
00259 }
00260 } finally {
00261 fos.close();
00262 }
00263 }
00264
00272 @Nullable
00273 private KeyBinding getKeyBindingAsKeyCode(final int keyCode, final int modifiers) {
00274 for (final KeyBinding keyBinding : keybindings) {
00275 if (keyBinding.matchesKeyCode(keyCode, modifiers)) {
00276 return keyBinding;
00277 }
00278 }
00279
00280 return null;
00281 }
00282
00289 @Nullable
00290 private KeyBinding getKeyBindingAsKeyChar(final char keyChar) {
00291 for (final KeyBinding keyBinding : keybindings) {
00292 if (keyBinding.matchesKeyChar(keyChar)) {
00293 return keyBinding;
00294 }
00295 }
00296
00297 return null;
00298 }
00299
00307 public void parseKeyBinding(@NotNull final String line, final boolean isDefault) throws InvalidKeyBindingException {
00308 if (line.startsWith("char ")) {
00309 if (!isDefault) {
00310
00311
00312 return;
00313 }
00314
00315 final String[] tmp = line.substring(5).split(" +", 2);
00316 if (tmp.length != 2) {
00317 throw new InvalidKeyBindingException("syntax error");
00318 }
00319
00320 try {
00321 final char keyChar = (char)Integer.parseInt(tmp[0]);
00322 final CommandList commandList = new CommandList(CommandListType.AND);
00323 commandList.add(guiCommandFactory.createCommandDecode(tmp[1]));
00324 addKeyBindingAsKeyChar(keyChar, commandList, isDefault);
00325 } catch (final NumberFormatException ex) {
00326 final InvalidKeyBindingException keyBindingException = new InvalidKeyBindingException("syntax error");
00327 keyBindingException.initCause(ex);
00328 throw keyBindingException;
00329 }
00330 } else if (line.startsWith("code ")) {
00331 final String[] tmp = line.substring(5).split(" +", 3);
00332 if (tmp.length != 3) {
00333 throw new InvalidKeyBindingException("syntax error");
00334 }
00335
00336 if (keyCodeMap == null) {
00337 keyCodeMap = new KeyCodeMap();
00338 }
00339
00340 final int keyCode;
00341 try {
00342 keyCode = keyCodeMap.getKeyCode(tmp[0]);
00343 } catch (final NoSuchKeyCodeException ex) {
00344 final InvalidKeyBindingException keyBindingException = new InvalidKeyBindingException("invalid key code: "+tmp[0]);
00345 keyBindingException.initCause(ex);
00346 throw keyBindingException;
00347 }
00348
00349 final int modifiers;
00350 try {
00351 modifiers = Integer.parseInt(tmp[1]);
00352 } catch (final NumberFormatException ex) {
00353 final InvalidKeyBindingException keyBindingException = new InvalidKeyBindingException("invalid modifier: "+tmp[1]);
00354 keyBindingException.initCause(ex);
00355 throw keyBindingException;
00356 }
00357
00358 final CommandList commandList = new CommandList(CommandListType.AND);
00359 commandList.add(guiCommandFactory.createCommandDecode(tmp[2]));
00360 addKeyBindingAsKeyCode(keyCode, modifiers, commandList, isDefault);
00361 } else {
00362 throw new InvalidKeyBindingException("syntax error");
00363 }
00364 }
00365
00371 public boolean handleKeyPress(@NotNull final KeyEvent e) {
00372 final KeyBinding keyBindingCode = getKeyBindingAsKeyCode(e.getKeyCode(), e.getModifiers());
00373 if (keyBindingCode != null) {
00374 executeKeyBinding(keyBindingCode);
00375 return true;
00376 }
00377
00378 final KeyBinding keyBindingChar = getKeyBindingAsKeyChar(e.getKeyChar());
00379 if (keyBindingChar != null) {
00380 executeKeyBinding(keyBindingChar);
00381 return true;
00382 }
00383
00384 return false;
00385 }
00386
00391 private static void executeKeyBinding(@NotNull final KeyBinding keyBinding) {
00392 keyBinding.getCommands().execute();
00393 }
00394
00400 public Collection<KeyBinding> getBindingsForPartialCommand(@NotNull final String commandStart) {
00401 final Collection<KeyBinding> matches = new HashSet<KeyBinding>();
00402
00403 for (final KeyBinding binding : keybindings) {
00404 if (binding.getCommandString().startsWith(commandStart)) {
00405 matches.add(binding);
00406 }
00407 }
00408
00409 return matches;
00410 }
00411 }