/*******************************************************************************
* Copyright (c) 2012 Google, Inc.
* 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:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package com.windowtester.runtime.swt.internal.text;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.Locale;
import javax.swing.KeyStroke;
import org.eclipse.swt.SWT;
import com.windowtester.internal.debug.Logger;
import com.windowtester.runtime.IUIContext;
import com.windowtester.runtime.swt.internal.selector.DisplayEventDispatcher;
/**
* Makes uses of the key maps for the various locales to support non US keyboard
* text entry.
*
* KeyStrokeMap is used to get the mapping for the char according to locale
*/
public class KeyMapTextEntryStrategy implements ITextEntryStrategy {
/**
* Thrown when there is a key map file but there is no mapping for a
* particular character.
*/
public static class UnMappedKeyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public UnMappedKeyException(String msg) {
super(msg);
}
}
//TODO: this is used as a fallback ---> as soon as all of the methods use keymaps, remove
private final ITextEntryStrategy defaultStrategy = new UIDriverTextEntryStrategy();
//a dispatcher for raw SWT events
private final DisplayEventDispatcher dispatcher = new DisplayEventDispatcher();
/**
* Enter text method that uses the key map
* @param ui
* @param txt
*/
public void enterText(IUIContext ui, String txt) {
char[] ch = txt.toCharArray();
for (int i=0;i < ch.length;i++) {
keyStroke(ch[i]);
}
}
/**
* using WT dispatcher to dispatch the key press and release events
* @param ch
*/
public void keyStroke(char ch) {
KeyStroke ks = KeyStrokeMap.getKeyStroke(ch);
if (ks == null){
// use default mapping to get keystroke, for
//a Japanese locale
//System.out.println("Using default map for " + ch);
ks = KeyStrokeMap.getDefaultKeyStroke(ch);
}
if (ks == null){ // still can't find a key stroke
// throw UnMappedKeyException
throw new UnMappedKeyException("Key map not found for " + ch +
" for locale " + Locale.getDefault().toString());
}
int keycode = ks.getKeyCode();
int mod = ks.getModifiers();
Logger.log("Char '" + ch + "' generated by keycode="
+ keycode + " mod=" + mod);
boolean isModifier = true;
switch(keycode) {
case KeyEvent.VK_ALT_GRAPH:
mod |= InputEvent.ALT_GRAPH_MASK; break;
case KeyEvent.VK_ALT:
mod |= InputEvent.ALT_MASK; break;
case KeyEvent.VK_SHIFT:
mod |= InputEvent.SHIFT_MASK; break;
case KeyEvent.VK_CONTROL:
mod |= InputEvent.CTRL_MASK; break;
case KeyEvent.VK_META:
mod |= InputEvent.META_MASK;break;
default: isModifier = false; break;
}
// check if accent key, if so press accent key
int accentKeycode = KeyStrokeMap.getAccentKey(ch);
if (accentKeycode != 0)
accentCharKeyPress(mod,keycode,accentKeycode);
else {
setModifiers(mod, true);
// press key
if (!isModifier) {
dispatcher.keyDown(ch); // for some reason $ becomes the +/- char on Mac, so see above
dispatcher.keyUp(ch);
}
setModifiers(mod, false);
}
}
/** Press or release the appropriate modifiers corresponding to the given
mask.
*/
public void setModifiers(int modifiers, boolean press) {
// boolean altGraph = (modifiers & InputEvent.ALT_GRAPH_MASK) != 0;
boolean shift = (modifiers & InputEvent.SHIFT_MASK) != 0;
boolean alt = (modifiers & InputEvent.ALT_MASK) != 0;
boolean ctrl = (modifiers & InputEvent.CTRL_MASK) != 0;
// boolean meta = (modifiers & InputEvent.META_MASK) != 0;
if (press) {
// if (altGraph) keyPress(KeyEvent.VK_ALT_GRAPH);
if (alt) dispatcher.keyDown(SWT.ALT);
if (shift) dispatcher.keyDown(SWT.SHIFT);
if (ctrl) dispatcher.keyDown(SWT.CTRL);
// if (meta) _dispatcher.keyDown(KeyEvent.VK_META);
}
else {
// For consistency, release in the reverse order of press
// if (meta) _dispatcher.keyUp(KeyEvent.VK_META);
if (ctrl) dispatcher.keyUp(SWT.CTRL);
if (shift) dispatcher.keyUp(SWT.SHIFT);
if (alt) dispatcher.keyUp(SWT.ALT);
//if (altGraph) _dispatcher.keyUp(KeyEvent.VK_ALT_GRAPH);
}
}
/**
* Enters the accent char - using 2 key presses, first the dead key and
* then the vowel
* @param keycode
* @param accentCode
*/
private void accentCharKeyPress(int modifiers,int keycode, int accentCode){
char ch = 0;
switch(keycode){
case 65:
ch = 'a';
break;
case 69:
ch = 'e';
break;
case 73:
ch = 'i';
break;
case 79:
ch = 'o';
break;
case 85:
ch = 'u';
break;
}
// first press dead key
pressDeadkey(accentCode);
setModifiers(modifiers, true);
dispatcher.keyDown(ch);
dispatcher.keyUp(ch);
setModifiers(modifiers, false);
}
/**
* Press the appropriate accent key as specified in the code
* @param code
* 1 - circumflex ^
* 2 - umlaut
* 3 - grave accent `
* 4 - acute accent
* 5 - tilda ~
*/
private void pressDeadkey(int code){
KeyStroke aks = null;
char ch = 0;
switch(code){
case 1:
ch = '^';
aks = KeyStrokeMap.getKeyStroke(ch);
break;
case 2:
ch = '�';
aks = KeyStrokeMap.getKeyStroke(ch);
break;
case 3:
ch = '`';
aks = KeyStrokeMap.getKeyStroke(ch);
break;
case 4:
ch = '�';
aks = KeyStrokeMap.getKeyStroke(ch);
break;
case 5:
ch = '~';
aks = KeyStrokeMap.getKeyStroke(ch);
break;
}
// if we have got the keystroke for the dead key, then press it
if (aks != null){
//int keycode = aks.getKeyCode();
int mod = aks.getModifiers();
setModifiers(mod, true);
dispatcher.keyDown(ch);
dispatcher.keyUp(ch);
setModifiers(mod, false);
}
}
public void keyClick(IUIContext ui, int key) {
// TODO implement this using the keymap
defaultStrategy.keyClick(ui, key);
}
public void keyClick(IUIContext ui, char key) {
// TODO implement this using the keymap
defaultStrategy.keyClick(ui, key);
}
public void keyClick(IUIContext ui, int ctrl, char c) {
// TODO implement this using the keymap
defaultStrategy.keyClick(ui, ctrl, c);
}
public void keyDown(IUIContext ui, char key) {
// TODO implement this using the keymap
defaultStrategy.keyDown(ui, key);
}
public void keyDown(IUIContext ui, int key) {
// TODO implement this using the keymap
defaultStrategy.keyDown(ui, key);
}
public void keyUp(IUIContext ui, char key) {
// TODO implement this using the keymap
defaultStrategy.keyUp(ui, key);
}
public void keyUp(IUIContext ui, int key) {
// TODO implement this using the keymap
defaultStrategy.keyUp(ui, key);
}
}