blob: 336a543a6dcde1c52204b2a415b00233d29f7b49 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 IBM Corporation and Others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Daisuke SATO - initial API and implementation
*******************************************************************************/
package org.eclipse.actf.ai.key.keyui.impl;
import java.awt.event.KeyEvent;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.eclipse.actf.ai.key.keyui.Messages;
import org.eclipse.actf.ai.key.keyui.impl.KeyUIImpl.KC.Type;
import org.eclipse.actf.ai.navigator.IBrowserControl;
import org.eclipse.actf.ai.navigator.IManipulator;
import org.eclipse.actf.ai.navigator.INavigatorUI;
import org.eclipse.actf.ai.navigator.ui.NavigatorUIUtil;
import org.eclipse.actf.ai.xmlstore.IXMLInfo;
import org.eclipse.actf.ai.xmlstore.IXMLSelector;
import org.eclipse.actf.ai.xmlstore.IXMLStore;
import org.eclipse.actf.ai.xmlstore.IXMLStoreService;
import org.eclipse.actf.ai.xmlstore.XMLStoreException;
import org.eclipse.actf.ai.xmlstore.XMLStoreServiceUtil;
import org.eclipse.actf.util.win32.comclutch.ComPlugin;
import org.eclipse.actf.util.win32.keyhook.IKeyHook;
import org.eclipse.actf.util.win32.keyhook.IKeyHookListener;
import org.eclipse.actf.util.win32.keyhook.ISendEvent;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class KeyUIImpl implements IKeyHookListener, IManipulator {
private static boolean initialized = false;
public static final String PREFERENCES_NS = "http://www.ibm.com/xmlns/prod/AcTF/aiBrowser/preferences/1.0";
public static final int CMD_PASSTHROUGH = -1;
public static final int CMD_NOOP = 9999;
// for demo
public static final int CMD_SPEED_UP = 1300;
public static final int CMD_SPEED_DOWN = 1301;
public static final int CMD_PLAY_NEXT = 1302;
public static final int CMD_PLAY_PREV = 1303;
public static final int CMD_START_RECORDING = 1304;
private static final int VK_RETURN = 13;
private IManipulator.Mode mode = null;
private IBrowserControl browserControl;
private INavigatorUI navigatorUI;
private HashMap<KeyEntry, KC> keyConfigMapForTreeNavigation = new HashMap<KeyEntry, KC>();
private static IKeyHook keyHook;
private static ISendEvent sendEvent;
public KeyUIImpl() {
if (!initialized) {
initialized = true;
initialize();
}
}
public void initialize() {
keyHook = ComPlugin.getDefault().newKeyHook(this);
sendEvent = ComPlugin.getDefault().newSendEvent();
IXMLStoreService xmlStoreService = XMLStoreServiceUtil.getXMLStoreService();
IXMLSelector selector = xmlStoreService.getSelectorWithDocElem("UserPreferences", PREFERENCES_NS);
IXMLStore rootStore = xmlStoreService.getRootStore();
IXMLStore specifiedStroe = rootStore.specify(selector);
for (Iterator<IXMLInfo> i = specifiedStroe.getInfoIterator(); i.hasNext();) {
IXMLInfo info = i.next();
try {
read(info.getRootNode());
} catch (XMLStoreException e) {
e.printStackTrace();
}
}
initAccessKey();
}
private void initAccessKey() {
for (int i = 0; i < 10; i++) {
KC kc = new KC(KC.Type.ACCESSKEY, KeyEvent.VK_0 + i, KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK, null);
register(kc);
}
for (int i = 0; i < 26; i++) {
KC kc = new KC(KC.Type.ACCESSKEY, KeyEvent.VK_A + i, KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK, null);
register(kc);
}
}
private void read(Node rootNode) {
NodeList children = rootNode.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node commands = children.item(i);
KC.Type type = KC.Type.COMMAND;
if (commands.getNodeType() != Node.ELEMENT_NODE)
continue;
if (commands.getLocalName().equals("Commands")) {
type = KC.Type.COMMAND;
} else if (commands.getLocalName().equals("Functions")) {
type = KC.Type.FUNCTION;
} else {
continue;
}
NodeList commandList = commands.getChildNodes();
for (int j = 0; j < commandList.getLength(); j++) {
Node command = commandList.item(j);
if (command.getNodeType() != Node.ELEMENT_NODE)
continue;
NodeList keyList = command.getChildNodes();
ArrayList<KC> array = new ArrayList<KC>();
for (int k = 0; k < keyList.getLength(); k++) {
Node key = keyList.item(k);
if (key.getNodeType() != Node.ELEMENT_NODE)
continue;
KC kc = createKC(type, command, key);
register(kc);
if (!"none".equals(((Element) key).getAttribute("display")))
array.add(kc);
}
addMenu(commands, command, array);
}
}
}
private KC createKC(Type type, Node command, Node key) {
String name = command.getLocalName();
int vkey = 0;
int mod = 0;
if ("key".equals(key.getLocalName()) && PREFERENCES_NS.equals(key.getNamespaceURI())) {
String stroke = key.getTextContent();
stroke = stroke.trim();
String[] keys = stroke.split("[ \n\t\r]+");
for (int j = 0; j < keys.length; j++) {
try {
if ("VK_RETURN".equals(keys[j])) {
vkey = VK_RETURN;
} else if (keys[j].startsWith("VK_")) {
Field f = KeyEvent.class.getField(keys[j]);
vkey = f.getInt(KeyEvent.class);
} else if (keys[j].endsWith("_MASK")) {
Field f = KeyEvent.class.getField(keys[j]);
mod |= f.getInt(KeyEvent.class);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return new KC(type, vkey, mod, name);
}
private void addMenu(Node commands, Node command, ArrayList<KC> kc) {
String menuName = ((Element) commands).getAttribute("menu");
if (menuName.length() == 0)
return;
String groupName = ((Element) commands).getAttribute("menuGroup");
if (groupName.length() == 0)
return;
String name = command.getLocalName();
IMenuManager menu = NavigatorUIUtil.menuManager;
IMenuManager menu2 = menu.findMenuUsingPath(menuName);
if ("separator".equals(name)) {
menu2.appendToGroup(groupName, new Separator());
} else {
menu2.appendToGroup(groupName, new KeyAction(name, kc));
}
}
static class KeyEntry {
int vkey;
int mod;
@Override
public int hashCode() {
return (vkey | mod << 16);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof KeyEntry))
return false;
KeyEntry e = (KeyEntry) o;
return ((e.vkey == vkey) && (e.mod == mod));
}
KeyEntry(int vkey, int mod) {
this.vkey = vkey;
this.mod = mod;
}
}
private void register(KC kc) {
keyConfigMapForTreeNavigation.put(new KeyEntry(kc.vkey, kc.mod), kc);
keyHook.registerHookedKey(kc.vkey, kc.mod);
}
static class KC {
enum Type {
COMMAND, FUNCTION, NOOP, ACCESSKEY
}
int vkey;
int mod;
Type type;
String name;
enum Target {
INAVIGATORUI, IBROWSERCONTROL
}
Target target;
Method method;
int command;
KC(Type type, int vkey, int mod, String name) {
this.type = type;
this.vkey = vkey;
this.mod = mod;
this.name = name;
if (type == Type.FUNCTION) {
try {
method = INavigatorUI.class.getMethod(name);
target = Target.INAVIGATORUI;
} catch (Exception e) {
method = null;
}
if (method == null) {
try {
method = IBrowserControl.class.getMethod(name);
target = Target.IBROWSERCONTROL;
} catch (Exception e){
System.err.println("No such method:" + name);
}
}
} else if (type == Type.COMMAND) {
try {
Field field = KeyUIImpl.class.getField("CMD_" + name);
command = field.getInt(KeyUIImpl.class);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
public boolean hookedKey(int vkey, int modifier, boolean isUp) {
if (mode == IManipulator.TREE_NAVIGATION_MODE) {
KC kc = (KC) keyConfigMapForTreeNavigation.get(new KeyEntry(vkey, modifier));
if (kc != null) {
if (kc.type == KC.Type.COMMAND && kc.command == CMD_NOOP)
return true;
if (kc.type != KC.Type.ACCESSKEY && kc.method == null)
return false;
/*
if (vkey == VK_RETURN) {
if (!isUp)
call(kc);
} else {
if (isUp)
call(kc);
}*/
if (!isUp)
call(kc);
return true;
}
} else if (mode == IManipulator.FORM_INPUT_MODE) {
switch (vkey) {
case KeyEvent.VK_TAB:
break;
case VK_RETURN:
// navigatorUI.submitForm();
sendEvent.postKey(vkey, isUp);
break;
case KeyEvent.VK_ESCAPE:
navigatorUI.exitFormMode();
break;
default:
return false;
}
return true;
} else if (mode == IManipulator.KEYHOOK_DISABLED_MODE) {
return false;
}
return false;
}
private void call(KC kc) {
if (kc.type == KC.Type.COMMAND) {
switch (kc.command) {
// for demo
case CMD_SPEED_UP:
sendKey(KeyEvent.VK_UP, true);
break;
case CMD_SPEED_DOWN:
sendKey(KeyEvent.VK_DOWN, true);
break;
case CMD_PLAY_NEXT:
sendKey(KeyEvent.VK_RIGHT, false);
break;
case CMD_PLAY_PREV:
sendKey(KeyEvent.VK_LEFT, false);
break;
case CMD_START_RECORDING:
sendKey(KeyEvent.VK_S, true);
break;
case CMD_NOOP:
case CMD_PASSTHROUGH:
break;
}
} else if (kc.type == KC.Type.FUNCTION) {
try {
switch (kc.target) {
case IBROWSERCONTROL:
if (browserControl != null)
kc.method.invoke(browserControl, new Object[0]);
break;
case INAVIGATORUI:
if (navigatorUI != null)
kc.method.invoke(navigatorUI, new Object[0]);
break;
}
} catch (Exception e) {
e.printStackTrace();
}
} else if (kc.type == KC.Type.ACCESSKEY) {
navigatorUI.jumpToAccessKey((char) kc.vkey);
}
}
private void sendKey(int key, boolean ctrlFlag) {
long h = sendEvent.findWindow("SWT_Window0", "Sound Controller");
if (ctrlFlag) {
sendEvent.postKeyToWindow(h, KeyEvent.VK_CONTROL, false);
}
sendEvent.postKeyToWindow(h, key, false);
sendEvent.postKeyToWindow(h, key, true);
if (ctrlFlag) {
sendEvent.postKeyToWindow(h, KeyEvent.VK_CONTROL, true);
}
}
public void setNavigator(INavigatorUI navigatorUI) {
this.navigatorUI = navigatorUI;
}
public void setBrowserControl(IBrowserControl browserControl) {
this.browserControl = browserControl;
}
public void dispose() {
keyHook.dispose();
}
public void setMode(Mode mode) {
this.mode = mode;
if (mode == IManipulator.FORM_INPUT_MODE) {
keyHook.hookAll(true);
} else if (mode == IManipulator.KEYHOOK_DISABLED_MODE) {
keyHook.hookAll(false);
} else {
keyHook.hookAll(false);
}
}
class KeyAction extends Action {
String name;
ArrayList<KC> kcs;
public KeyAction(String name, ArrayList<KC> kcs) {
super();
this.name = name;
this.kcs = kcs;
StringBuffer sb = new StringBuffer();
sb.append(Messages.getString(name));
if (kcs.size() > 0)
sb.append(" (" + getKey(kcs) + ")");
setText(sb.toString());
}
private String getKey(ArrayList<KC> kcs) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < kcs.size(); i++) {
if (i != 0)
sb.append(" " + Messages.getString("Key.OR") + " ");
int vkey = kcs.get(i).vkey;
int mod = kcs.get(i).mod;
if (is(mod, KeyEvent.CTRL_MASK)) {
sb.append("Ctrl+");
}
if (is(mod, KeyEvent.ALT_MASK)) {
sb.append("Alt+");
}
if (is(mod, KeyEvent.SHIFT_MASK)) {
sb.append("Shift+");
}
String key = "";
if (('0' <= vkey && vkey <= '9') || ('A' <= vkey && vkey <= 'Z')) {
key = "" + (char) vkey;
} else if (KeyEvent.VK_F1 <= vkey && vkey <= KeyEvent.VK_F24) {
key = "F" + (vkey - KeyEvent.VK_F1 + 1);
} else {
switch (vkey) {
case VK_RETURN:
key = "Enter";
break;
case KeyEvent.VK_UP:
key = "Up";
break;
case KeyEvent.VK_DOWN:
key = "Down";
break;
case KeyEvent.VK_LEFT:
key = "Left";
break;
case KeyEvent.VK_RIGHT:
key = "Right";
break;
case KeyEvent.VK_HOME:
key = "Home";
break;
case KeyEvent.VK_END:
key = "End";
break;
case KeyEvent.VK_PAGE_UP:
key = "PageUp";
break;
case KeyEvent.VK_PAGE_DOWN:
key = "PageDown";
break;
case KeyEvent.VK_TAB:
key = "Tab";
break;
case KeyEvent.VK_PAUSE:
key = "Pause";
break;
case KeyEvent.VK_SPACE:
key = "Space";
break;
}
}
sb.append(key);
}
return sb.toString();
}
private boolean is(int a, int b) {
return (a & b) == b;
}
@Override
public void run() {
if (kcs.size() > 0)
call(kcs.get(0));
}
}
}