/*
* KeyEventTranslator.java - Hides some warts of AWT event API
* :tabSize=8:indentSize=8:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 2003 Slava Pestov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package freemind.preferences.layout;
//{{{ Imports
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
/**
* In conjunction with the <code>KeyEventWorkaround</code>, hides some warts in
* the AWT key event API.
*
* @author Slava Pestov
* @version $Id: KeyEventTranslator.java,v 1.1.2.2 2005/05/12 21:56:57
* christianfoltin Exp $
*/
public class KeyEventTranslator {
// {{{ addTranslation() method
/**
* Adds a keyboard translation.
*
* @param key1
* Translate this key
* @param key2
* Into this key
* @since jEdit 4.2pre3
*/
public static void addTranslation(Key key1, Key key2) {
transMap.put(key1, key2);
} // }}}
// {{{ translateKeyEvent() method
/**
* Pass this an event from
* {@link KeyEventWorkaround#processKeyEvent(java.awt.event.KeyEvent)}.
*
* @since jEdit 4.2pre3
*/
public static Key translateKeyEvent(KeyEvent evt) {
int modifiers = evt.getModifiers();
Key returnValue = null;
switch (evt.getID()) {
case KeyEvent.KEY_PRESSED:
int keyCode = evt.getKeyCode();
if ((keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9)
|| (keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z)) {
if (KeyEventWorkaround.ALTERNATIVE_DISPATCHER)
return null;
else {
returnValue = new Key(modifiersToString(modifiers),
// fc, 12.5.2005: changed to upper case as Freemind seems to
// need this.
'\0', Character.toUpperCase((char) keyCode));
}
} else {
if (keyCode == KeyEvent.VK_TAB) {
evt.consume();
returnValue = new Key(modifiersToString(modifiers),
keyCode, '\0');
} else if (keyCode == KeyEvent.VK_SPACE) {
// for SPACE or S+SPACE we pass the
// key typed since international
// keyboards sometimes produce a
// KEY_PRESSED SPACE but not a
// KEY_TYPED SPACE, eg if you have to
// do a "<space> to insert ".
if ((modifiers & ~InputEvent.SHIFT_MASK) == 0)
returnValue = null;
else {
returnValue = new Key(modifiersToString(modifiers), 0,
' ');
}
} else {
returnValue = new Key(modifiersToString(modifiers),
keyCode, '\0');
}
}
break;
case KeyEvent.KEY_TYPED:
char ch = evt.getKeyChar();
switch (ch) {
case '\n':
case '\t':
case '\b':
return null;
case ' ':
if ((modifiers & ~InputEvent.SHIFT_MASK) != 0)
return null;
}
int ignoreMods;
if (KeyEventWorkaround.ALT_KEY_PRESSED_DISABLED) {
/* on MacOS, A+ can be user input */
ignoreMods = (InputEvent.SHIFT_MASK | InputEvent.ALT_GRAPH_MASK | InputEvent.ALT_MASK);
} else {
/* on MacOS, A+ can be user input */
ignoreMods = (InputEvent.SHIFT_MASK | InputEvent.ALT_GRAPH_MASK);
}
if ((modifiers & InputEvent.ALT_GRAPH_MASK) == 0
&& evt.getWhen() - KeyEventWorkaround.lastKeyTime < 750
&& (KeyEventWorkaround.modifiers & ~ignoreMods) != 0) {
if (KeyEventWorkaround.ALTERNATIVE_DISPATCHER) {
returnValue = new Key(modifiersToString(modifiers), 0, ch);
} else
return null;
} else {
if (ch == ' ') {
returnValue = new Key(modifiersToString(modifiers), 0, ch);
} else
returnValue = new Key(null, 0, ch);
}
break;
default:
return null;
}
/*
* I guess translated events do not have the 'evt' field set so
* consuming won't work. I don't think this is a problem as nothing uses
* translation anyway
*/
Key trans = (Key) transMap.get(returnValue);
if (trans == null)
return returnValue;
else
return trans;
} // }}}
// {{{ parseKey() method
/**
* Converts a string to a keystroke. The string should be of the form
* <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i> is any
* combination of A for Alt, C for Control, S for Shift or M for Meta, and
* <i>shortcut</i> is either a single character, or a keycode name from the
* <code>KeyEvent</code> class, without the <code>VK_</code> prefix.
*
* @param keyStroke
* A string description of the key stroke
* @since jEdit 4.2pre3
*/
public static Key parseKey(String keyStroke) {
if (keyStroke == null)
return null;
int index = keyStroke.indexOf('+');
int modifiers = 0;
if (index != -1) {
for (int i = 0; i < index; i++) {
switch (Character.toUpperCase(keyStroke.charAt(i))) {
case 'A':
modifiers |= a;
break;
case 'C':
modifiers |= c;
break;
case 'M':
modifiers |= m;
break;
case 'S':
modifiers |= s;
break;
}
}
}
String key = keyStroke.substring(index + 1);
if (key.length() == 1) {
return new Key(modifiersToString(modifiers), 0, key.charAt(0));
} else if (key.length() == 0) {
// Log.log(Log.ERROR,DefaultInputHandler.class,
// "Invalid key stroke: " + keyStroke);
return null;
} else if (key.equals("SPACE")) {
return new Key(modifiersToString(modifiers), 0, ' ');
} else {
int ch;
try {
ch = KeyEvent.class.getField("VK_".concat(key)).getInt(null);
} catch (Exception e) {
// Log.log(Log.ERROR,DefaultInputHandler.class,
// "Invalid key stroke: "
// + keyStroke);
return null;
}
return new Key(modifiersToString(modifiers), ch, '\0');
}
} // }}}
// {{{ setModifierMapping() method
/**
* Changes the mapping between symbolic modifier key names (<code>C</code>,
* <code>A</code>, <code>M</code>, <code>S</code>) and Java modifier flags.
*
* You can map more than one Java modifier to a symobolic modifier, for
* example :
* <p>
* <code><pre>
* setModifierMapping(
* InputEvent.CTRL_MASK,
* InputEvent.ALT_MASK | InputEvent.META_MASK,
* 0,
* InputEvent.SHIFT_MASK);
* <pre></code>
* </p>
*
* You cannot map a Java modifer to more than one symbolic modifier.
*
* @param c
* The modifier(s) to map the <code>C</code> modifier to
* @param a
* The modifier(s) to map the <code>A</code> modifier to
* @param m
* The modifier(s) to map the <code>M</code> modifier to
* @param s
* The modifier(s) to map the <code>S</code> modifier to
*
* @since jEdit 4.2pre3
*/
public static void setModifierMapping(int c, int a, int m, int s) {
int duplicateMapping = ((c & a) | (c & m) | (c & s) | (a & m) | (a & s) | (m & s));
if ((duplicateMapping & InputEvent.CTRL_MASK) != 0) {
throw new IllegalArgumentException(
"CTRL is mapped to more than one modifier");
}
if ((duplicateMapping & InputEvent.ALT_MASK) != 0) {
throw new IllegalArgumentException(
"ALT is mapped to more than one modifier");
}
if ((duplicateMapping & InputEvent.META_MASK) != 0) {
throw new IllegalArgumentException(
"META is mapped to more than one modifier");
}
if ((duplicateMapping & InputEvent.SHIFT_MASK) != 0) {
throw new IllegalArgumentException(
"SHIFT is mapped to more than one modifier");
}
KeyEventTranslator.c = c;
KeyEventTranslator.a = a;
KeyEventTranslator.m = m;
KeyEventTranslator.s = s;
} // }}}
// {{{ getSymbolicModifierName() method
/**
* Returns a the symbolic modifier name for the specified Java modifier
* flag.
*
* @param mod
* A modifier constant from <code>InputEvent</code>
*
* @since jEdit 4.2pre3
*/
public static String getSymbolicModifierName(int mod) {
if ((mod & c) != 0)
return "control";
else if ((mod & a) != 0)
return "alt";
else if ((mod & m) != 0)
return "meta";
else if ((mod & s) != 0)
return "shift";
else
return "";
} // }}}
// {{{ modifiersToString() method
public static String modifiersToString(int mods) {
StringBuffer buf = null;
if ((mods & InputEvent.CTRL_MASK) != 0) {
if (buf == null)
buf = new StringBuffer();
else
buf.append(GrabKeyDialog.MODIFIER_SEPARATOR);
buf.append(getSymbolicModifierName(InputEvent.CTRL_MASK));
}
if ((mods & InputEvent.ALT_MASK) != 0) {
if (buf == null)
buf = new StringBuffer();
else
buf.append(GrabKeyDialog.MODIFIER_SEPARATOR);
buf.append(getSymbolicModifierName(InputEvent.ALT_MASK));
}
if ((mods & InputEvent.META_MASK) != 0) {
if (buf == null)
buf = new StringBuffer();
else
buf.append(GrabKeyDialog.MODIFIER_SEPARATOR);
buf.append(getSymbolicModifierName(InputEvent.META_MASK));
}
if ((mods & InputEvent.SHIFT_MASK) != 0) {
if (buf == null)
buf = new StringBuffer();
else
buf.append(GrabKeyDialog.MODIFIER_SEPARATOR);
buf.append(getSymbolicModifierName(InputEvent.SHIFT_MASK));
}
if (buf == null)
return null;
else
return buf.toString();
} // }}}
// {{{ getModifierString() method
/**
* Returns a string containing symbolic modifier names set in the specified
* event.
*
* @param evt
* The event
*
* @since jEdit 4.2pre3
*/
public static String getModifierString(InputEvent evt) {
StringBuffer buf = new StringBuffer();
if (evt.isControlDown())
buf.append(getSymbolicModifierName(InputEvent.CTRL_MASK));
if (evt.isAltDown())
buf.append(getSymbolicModifierName(InputEvent.ALT_MASK));
if (evt.isMetaDown())
buf.append(getSymbolicModifierName(InputEvent.META_MASK));
if (evt.isShiftDown())
buf.append(getSymbolicModifierName(InputEvent.SHIFT_MASK));
return (buf.length() == 0 ? null : buf.toString());
} // }}}
static int c, a, m, s;
// {{{ Private members
private static Map transMap = new HashMap();
static {
if (GrabKeyDialog.isMacOS()) {
setModifierMapping(InputEvent.META_MASK, /* == C+ */
InputEvent.CTRL_MASK, /* == A+ */
/* M+ discarded by key event workaround! */
InputEvent.ALT_MASK, /* == M+ */
InputEvent.SHIFT_MASK /* == S+ */);
} else {
setModifierMapping(InputEvent.CTRL_MASK, InputEvent.ALT_MASK,
InputEvent.META_MASK, InputEvent.SHIFT_MASK);
}
} // }}}
// {{{ Key class
public static class Key {
public String modifiers;
public int key;
public char input;
public Key(String modifiers, int key, char input) {
this.modifiers = modifiers;
this.key = key;
this.input = input;
}
public int hashCode() {
return key + input;
}
public boolean equals(Object o) {
if (o instanceof Key) {
Key k = (Key) o;
if ((modifiers.equals(k.modifiers)) && key == k.key
&& input == k.input) {
return true;
}
}
return false;
}
public String toString() {
return (modifiers == null ? "" : modifiers) + "<"
+ Integer.toString(key, 16) + ","
+ Integer.toString(input, 16) + ">";
}
} // }}}
}