/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: KeyStrokePair.java
*
* Copyright (c) 2003 Sun Microsystems and Static Free Software
*
* Electric(tm) 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 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.user.ui;
import com.sun.electric.tool.Client;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import javax.swing.KeyStroke;
/**
* User: gainsley
* Date: Apr 6, 2004
* Time: 12:31:27 PM
*/
public class KeyStrokePair {
/** prefix KeyStroke */ private KeyStroke prefixStroke;
/** primary KeyStroke */ private KeyStroke stroke;
/** cache of defined KeyStrokePairs */ private static HashMap<KeyStrokePair,KeyStrokePair> cache = new HashMap<KeyStrokePair,KeyStrokePair>();
/** separator for toString() */ private static final String sep = ", ";
/** list of special keyStrokes */ private static int[] specialKeyStrokes = {
KeyEvent.VK_AMPERSAND,
KeyEvent.VK_INSERT,
KeyEvent.VK_HOME,
KeyEvent.VK_PAGE_UP,
KeyEvent.VK_DELETE,
KeyEvent.VK_END,
KeyEvent.VK_PAGE_DOWN,
KeyEvent.VK_MINUS,
KeyEvent.VK_EQUALS,
KeyEvent.VK_BACK_SPACE,
KeyEvent.VK_OPEN_BRACKET,
KeyEvent.VK_CLOSE_BRACKET,
KeyEvent.VK_SLASH,
KeyEvent.VK_BACK_SLASH,
KeyEvent.VK_SEMICOLON,
KeyEvent.VK_QUOTE,
KeyEvent.VK_ENTER,
KeyEvent.VK_PERIOD,
KeyEvent.VK_COMMA,
KeyEvent.VK_SLASH,
'>',
'<',
KeyEvent.VK_BACK_QUOTE,
KeyEvent.VK_LEFT,
KeyEvent.VK_RIGHT,
KeyEvent.VK_UP,
KeyEvent.VK_DOWN
};
private KeyStrokePair() {}
/**
* Factory method to get a new KeyStrokePair. KeyStrokePairs are unique,
* therefore two objects of the same pair of key strokes will be the same object.
* Which means == is the same as .equals().
* @param prefixStroke the prefix stroke
* @param stroke the primary stroke
* @return a new KeyStrokePair
*/
public static KeyStrokePair getKeyStrokePair(KeyStroke prefixStroke, KeyStroke stroke) {
return getCachedKeyStrokePair(prefixStroke, stroke);
}
/**
* Get a KeyStrokePair from a String representation. The string consists
* of two KeyStroke strings separated by a separator (currently ", ").
* The KeyStroke strings are generated by keyStrokeToString.
* @param keyString the string to be converted to a key stroke pair
* @return a KeyStrokePair. May return null if invalid keyString
*/
public static KeyStrokePair getKeyStrokePair(String keyString) {
KeyStroke prefixStroke = null;
KeyStroke stroke = null;
// otherwise, split by separator
String [] strokes = keyString.split(sep);
if (strokes.length == 1) {
// only one stroke
stroke = stringToKeyStroke(strokes[0]);
return getCachedKeyStrokePair(prefixStroke, stroke);
}
if (strokes.length == 2) {
prefixStroke = stringToKeyStroke(strokes[0]);
stroke = stringToKeyStroke(strokes[1]);
return getCachedKeyStrokePair(prefixStroke, stroke);
}
return null;
}
// --------------------- String to Key and Back Conversions -------------------
/**
* Convert this KeyStrokePair to a string.
* @return a string representing this KeyStrokePair.
*/
public String toString() {
if (stroke == null) return "";
if (prefixStroke == null) return keyStrokeToString(stroke);
return keyStrokeToString(prefixStroke)+ sep + keyStrokeToString(stroke);
}
public static String getStringFromKeyStroke(KeyStroke key)
{
String id = "";
if (key.getKeyCode() == KeyEvent.VK_UNDEFINED ) // not recognized? like >
id = String.valueOf(key.getKeyChar());
else
id = KeyEvent.getKeyText(key.getKeyCode());
return id;
}
/**
* Converts KeyStroke to String that can be parsed by
* KeyStroke.getKeyStroke(String s). For some reason the
* KeyStroke class has a method to parse a KeyStroke String
* identifier, but not one to make one.
*/
public static String keyStrokeToString(KeyStroke key) {
if (key == null) return "";
String mods = KeyEvent.getKeyModifiersText(key.getModifiers());
String id = getStringFromKeyStroke(key);
// change key to lower case, unless the shift modifier was pressed
//if ((key.getModifiers() & InputEvent.SHIFT_DOWN_MASK) == 0) id = id.toLowerCase();
if (mods.equals("")) return id;
mods = mods.replace('+', ' ');
mods = mods.toLowerCase();
return mods + " " + id;
}
public static KeyStroke stringToKeyStroke(String str) {
// change NUMPAD-# to NUMPAD#
str = str.replaceAll("NumPad\\-", "NUMPAD");
// Hack: getKeyStroke does not understand mac's Command key.
// Hack tested on Leopard
if (str.length() > 0 && Client.isOSMac())
{
// Checking if the first character is a MaCOSX special one.
char ctl = 8963; // ctl
char command = 8984; // command
char shift = 8679;
boolean hasCtl = false, hasCmd = false, hasShift = false;
for (int i = 0; i < str.length(); i++)
{
char c = str.charAt(i);
if (!hasCtl && c == ctl)
hasCtl = true;
if (!hasCmd && c == command)
hasCmd = true;
if (!hasShift && c == shift)
hasShift = true;
if (hasCtl && hasCmd && hasShift)
break;
}
if (hasCtl)
str = str.replace(ctl, (char)32);
if (hasCmd)
str = str.replace(command, (char)32);
if (hasShift)
str = str.replace(shift, (char)32);
str = str.trim();
KeyStroke key = KeyStroke.getKeyStroke(str);
if (key != null)
{
int val = key.getModifiers();
if (hasCtl)
val |= InputEvent.CTRL_DOWN_MASK;
if (hasShift)
val |= InputEvent.SHIFT_DOWN_MASK;
if (hasCmd)
val |= InputEvent.META_DOWN_MASK;
key = KeyStroke.getKeyStroke(key.getKeyCode(), val);
return key;
}
}
// Hack: getKeyStroke does not understand mac's Command key.
// Hack tested on Tiger
if (str.matches(".*?command.*")) {
// must be a mac, get mac command key
str = str.replaceAll("command", "");
str = str.trim();
KeyStroke key = KeyStroke.getKeyStroke(str);
int command_mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
if (key != null) {
key = KeyStroke.getKeyStroke(key.getKeyCode(), key.getModifiers() | command_mask);
return key;
}
return null;
}
KeyStroke key = KeyStroke.getKeyStroke(str);
if (key == null) // Doesn't seem to handle properly special keyEvent
{
for (int i=0; i<specialKeyStrokes.length; i++)
{
KeyStroke k = KeyStroke.getKeyStroke(specialKeyStrokes[i], 0);
if (str.equals(keyStrokeToString(k))) { key = k; break; }
k = KeyStroke.getKeyStroke(specialKeyStrokes[i], InputEvent.SHIFT_DOWN_MASK);
if (str.equals(keyStrokeToString(k))) { key = k; break; }
k = KeyStroke.getKeyStroke(specialKeyStrokes[i], InputEvent.CTRL_DOWN_MASK);
if (str.equals(keyStrokeToString(k))) { key = k; break; }
k = KeyStroke.getKeyStroke(specialKeyStrokes[i], InputEvent.META_DOWN_MASK);
if (str.equals(keyStrokeToString(k))) { key = k; break; }
k = KeyStroke.getKeyStroke(specialKeyStrokes[i], InputEvent.ALT_DOWN_MASK);
if (str.equals(keyStrokeToString(k))) { key = k; break; }
}
}
return key;
}
// ----------------------------- Other methods --------------------------------
public KeyStroke getPrefixStroke() { return prefixStroke; }
public KeyStroke getStroke() { return stroke; }
/**
* Returns a numeric value for this object that is likely to be unique.
* Uses hash codes of prefixStroke and stroke (which are derived from fields
* of those KeyStrokes. see KeyStroke.hashCode().
* @return an int that represents this object
*/
public int hashCode() {
int prefixCode = (prefixStroke == null) ? 0 : prefixStroke.hashCode();
int strokeCode = (stroke == null) ? 0 : stroke.hashCode();
//System.out.println("Generated hash code: "+ (int)((prefixCode + 1) * strokeCode));
return (prefixCode + 1) * strokeCode;
}
private static KeyStrokePair getCachedKeyStrokePair(KeyStroke prefixStroke, KeyStroke stroke) {
if (prefixStroke == null && stroke == null) return null; // don't allow null, null
KeyStrokePair k = new KeyStrokePair();
k.prefixStroke = prefixStroke;
k.stroke = stroke;
// this looks funny but works cause hashCode is constructed from object
// contents, not address of object.
if (!cache.containsKey(k)) {
cache.put(k, k);
} else {
k = cache.get(k);
}
return k;
}
}