26c928fc45
These are the preferred way to get instances of Boolean, Byte, Short, Character, Integer, Long, Float, and Double object. It's always made sense for Boolean objects, and with 1.5 these factory methods were blessed as the proper way to get instances unless one absolutely needed a distinct object. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4145 542714f4-19e9-0310-aa3c-eee0fc999fb1
721 lines
24 KiB
Java
721 lines
24 KiB
Java
//
|
|
// $Id$
|
|
//
|
|
// Narya library - tools for developing networked games
|
|
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
|
// http://www.threerings.net/code/narya/
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License as published
|
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library 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
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
package com.threerings.util;
|
|
|
|
import java.awt.KeyEventDispatcher;
|
|
import java.awt.KeyboardFocusManager;
|
|
import java.awt.Window;
|
|
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.event.WindowEvent;
|
|
import java.awt.event.WindowFocusListener;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import javax.swing.JComponent;
|
|
import javax.swing.SwingUtilities;
|
|
import javax.swing.event.AncestorEvent;
|
|
import javax.swing.event.AncestorListener;
|
|
|
|
import com.samskivert.swing.Controller;
|
|
import com.samskivert.util.HashIntMap;
|
|
import com.samskivert.util.Interval;
|
|
import com.samskivert.util.ObserverList;
|
|
import com.samskivert.util.RunAnywhere;
|
|
|
|
import com.threerings.util.keybd.Keyboard;
|
|
|
|
/**
|
|
* The keyboard manager observes keyboard actions on a particular
|
|
* component and posts commands associated with the key presses to the
|
|
* {@link Controller} hierarchy. It allows specifying the key repeat
|
|
* rate, and will begin repeating a key immediately after it is held down
|
|
* rather than depending on the system-specific key repeat delay/rate.
|
|
*/
|
|
public class KeyboardManager
|
|
implements KeyEventDispatcher, AncestorListener, WindowFocusListener
|
|
{
|
|
/**
|
|
* An interface to be implemented by those that care to be notified
|
|
* whenever an event (either a key press or a key release) occurs for
|
|
* any key while the keyboard manager is active. We use this custom
|
|
* interface rather than the more standard {@link
|
|
* java.awt.event.KeyListener} interface so that we needn't create key
|
|
* pressed and released event objects each time a (potentially
|
|
* artificially-generated) event occurs.
|
|
*/
|
|
public interface KeyObserver
|
|
{
|
|
/**
|
|
* Called whenever a key event occurs for a particular key.
|
|
*/
|
|
public void handleKeyEvent (int id, int keyCode, long timestamp);
|
|
}
|
|
|
|
/**
|
|
* Constructs a keyboard manager that is initially disabled. The
|
|
* keyboard manager should not be enabled until it has been supplied
|
|
* with a target component and translator via {@link #setTarget}.
|
|
*/
|
|
public KeyboardManager ()
|
|
{
|
|
// capture low-level keyboard events via the keyboard focus manager
|
|
KeyboardFocusManager.getCurrentKeyboardFocusManager().
|
|
addKeyEventDispatcher(this);
|
|
}
|
|
|
|
/**
|
|
* Resets the keyboard manager, clearing any target and key translator
|
|
* in use and disabling the keyboard manager if it is currently
|
|
* active.
|
|
*/
|
|
public void reset ()
|
|
{
|
|
setEnabled(false);
|
|
_target = null;
|
|
_xlate = null;
|
|
_focus = false;
|
|
}
|
|
|
|
/**
|
|
* Initializes the keyboard manager with the supplied target component
|
|
* and key translator and disables the keyboard manager if it is
|
|
* currently active.
|
|
*
|
|
* @param target the component whose keyboard events are to be observed.
|
|
* @param xlate the key translator used to map keyboard events to
|
|
* controller action commands.
|
|
*/
|
|
public void setTarget (JComponent target, KeyTranslator xlate)
|
|
{
|
|
setEnabled(false);
|
|
|
|
// save off references
|
|
_target = target;
|
|
_xlate = xlate;
|
|
}
|
|
|
|
/**
|
|
* Registers a key observer that will be notified of all key events
|
|
* while the keyboard manager is active.
|
|
*/
|
|
public void registerKeyObserver (KeyObserver obs)
|
|
{
|
|
_observers.add(obs);
|
|
}
|
|
|
|
/**
|
|
* Removes the supplied key observer from the list of observers to be
|
|
* notified of all key events while the keyboard manager is active.
|
|
*/
|
|
public void removeKeyObserver (KeyObserver obs)
|
|
{
|
|
_observers.remove(obs);
|
|
}
|
|
|
|
/**
|
|
* Sets whether the keyboard manager processes keyboard input.
|
|
*/
|
|
public void setEnabled (boolean enabled)
|
|
{
|
|
// report incorrect usage
|
|
if (enabled && _target == null) {
|
|
Log.warning("Attempt to enable uninitialized keyboard manager!");
|
|
Thread.dumpStack();
|
|
return;
|
|
}
|
|
|
|
// ignore NOOPs
|
|
if (enabled == _enabled) {
|
|
return;
|
|
}
|
|
|
|
if (!enabled) {
|
|
if (Keyboard.isAvailable()) {
|
|
// restore the original key auto-repeat settings
|
|
Keyboard.setKeyRepeat(_nativeRepeat);
|
|
}
|
|
|
|
// clear out all of our key states
|
|
releaseAllKeys();
|
|
_keys.clear();
|
|
|
|
// cease listening to all of our business
|
|
if (_window != null) {
|
|
_window.removeWindowFocusListener(this);
|
|
_window = null;
|
|
}
|
|
_target.removeAncestorListener(this);
|
|
|
|
// note that we no longer have the focus
|
|
_focus = false;
|
|
|
|
} else {
|
|
// listen to ancestor events so that we can cease our business
|
|
// if we lose the focus
|
|
_target.addAncestorListener(this);
|
|
|
|
// if we're already showing, listen to window focus events,
|
|
// else we have to wait until the target is added since it
|
|
// doesn't currently have a window
|
|
if (_target.isShowing() && _window == null) {
|
|
_window = SwingUtilities.getWindowAncestor(_target);
|
|
if (_window != null) {
|
|
_window.addWindowFocusListener(this);
|
|
}
|
|
}
|
|
|
|
// assume the keyboard focus since we were just enabled
|
|
_focus = true;
|
|
|
|
if (Keyboard.isAvailable()) {
|
|
// note whether key auto-repeating was enabled
|
|
_nativeRepeat = Keyboard.isKeyRepeatEnabled();
|
|
|
|
// disable native key auto-repeating so that we can
|
|
// definitively ascertain key pressed/released events
|
|
Keyboard.setKeyRepeat(false);
|
|
}
|
|
}
|
|
|
|
// save off our new enabled state
|
|
_enabled = enabled;
|
|
}
|
|
|
|
/**
|
|
* Sets the expected delay in milliseconds between each key
|
|
* press/release event the keyboard manager should expect to receive
|
|
* while a key is repeating.
|
|
*/
|
|
public void setRepeatDelay (long delay)
|
|
{
|
|
_repeatDelay = delay;
|
|
}
|
|
|
|
/**
|
|
* Releases all keys and ceases any hot repeating action that may be
|
|
* going on.
|
|
*/
|
|
public void releaseAllKeys ()
|
|
{
|
|
long now = System.currentTimeMillis();
|
|
Iterator iter = _keys.elements();
|
|
while (iter.hasNext()) {
|
|
((KeyInfo)iter.next()).release(now);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the keyboard manager gains focus and should begin
|
|
* handling keys again if it was previously enabled.
|
|
*/
|
|
protected void gainedFocus ()
|
|
{
|
|
if (Keyboard.isAvailable()) {
|
|
// disable key auto-repeating
|
|
Keyboard.setKeyRepeat(false);
|
|
}
|
|
|
|
// note that we've regained the focus
|
|
_focus = true;
|
|
}
|
|
|
|
/**
|
|
* Called when the keyboard manager loses focus and should cease
|
|
* handling keys.
|
|
*/
|
|
protected void lostFocus ()
|
|
{
|
|
if (Keyboard.isAvailable()) {
|
|
// restore key auto-repeating
|
|
Keyboard.setKeyRepeat(_nativeRepeat);
|
|
}
|
|
|
|
// clear out all of our keyboard state
|
|
releaseAllKeys();
|
|
// note that we no longer have the focus
|
|
_focus = false;
|
|
}
|
|
|
|
// documentation inherited from interface KeyEventDispatcher
|
|
public boolean dispatchKeyEvent (KeyEvent e)
|
|
{
|
|
// bail if we're not enabled, we haven't the focus, or we're not
|
|
// showing on-screen
|
|
if (!_enabled || !_focus || !_target.isShowing()) {
|
|
// Log.info("dispatchKeyEvent [enabled=" + _enabled +
|
|
// ", focus=" + _focus +
|
|
// ", showing=" + ((_target == null) ? "N/A" :
|
|
// "" + _target.isShowing()) + "].");
|
|
return false;
|
|
}
|
|
|
|
// handle key press and release events
|
|
switch (e.getID()) {
|
|
case KeyEvent.KEY_PRESSED:
|
|
return keyPressed(e);
|
|
|
|
case KeyEvent.KEY_RELEASED:
|
|
return keyReleased(e);
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when Swing notifies us that a key has been pressed while the
|
|
* keyboard manager is active.
|
|
*
|
|
* @return true to swallow the key event
|
|
*/
|
|
protected boolean keyPressed (KeyEvent e)
|
|
{
|
|
logKey("keyPressed", e);
|
|
|
|
// get the action command associated with this key
|
|
int keyCode = e.getKeyCode();
|
|
boolean hasCommand = _xlate.hasCommand(keyCode);
|
|
if (hasCommand) {
|
|
// get the info object for this key, creating one if necessary
|
|
KeyInfo info = (KeyInfo)_keys.get(keyCode);
|
|
if (info == null) {
|
|
info = new KeyInfo(keyCode);
|
|
_keys.put(keyCode, info);
|
|
}
|
|
|
|
// remember the last time this key was pressed
|
|
info.setPressTime(RunAnywhere.getWhen(e));
|
|
}
|
|
|
|
// notify any key observers of the key press
|
|
notifyObservers(KeyEvent.KEY_PRESSED, e.getKeyCode(),
|
|
RunAnywhere.getWhen(e));
|
|
|
|
return hasCommand;
|
|
}
|
|
|
|
/**
|
|
* Called when Swing notifies us that a key has been released while
|
|
* the keyboard manager is active.
|
|
*
|
|
* @return true to swallow the key event
|
|
*/
|
|
protected boolean keyReleased (KeyEvent e)
|
|
{
|
|
logKey("keyReleased", e);
|
|
|
|
// get the info object for this key
|
|
KeyInfo info = (KeyInfo)_keys.get(e.getKeyCode());
|
|
if (info != null) {
|
|
// remember the last time we received a key release
|
|
info.setReleaseTime(RunAnywhere.getWhen(e));
|
|
}
|
|
|
|
// notify any key observers of the key release
|
|
notifyObservers(KeyEvent.KEY_RELEASED, e.getKeyCode(),
|
|
RunAnywhere.getWhen(e));
|
|
|
|
return (info != null);
|
|
}
|
|
|
|
/**
|
|
* Notifies all registered key observers of the supplied key event.
|
|
* This method provides a thread-safe manner in which to notify the
|
|
* observers, which is necessary since the {@link KeyInfo} objects do
|
|
* various antics from the interval manager thread whilst we may do
|
|
* other notification from the AWT thread when normal key events are
|
|
* handled.
|
|
*/
|
|
protected synchronized void notifyObservers (
|
|
int id, int keyCode, long timestamp)
|
|
{
|
|
_keyOp.init(id, keyCode, timestamp);
|
|
_observers.apply(_keyOp);
|
|
}
|
|
|
|
/**
|
|
* Logs the given message and key.
|
|
*/
|
|
protected void logKey (String msg, KeyEvent e)
|
|
{
|
|
if (DEBUG_EVENTS) {
|
|
int keyCode = e.getKeyCode();
|
|
Log.info(msg + " [key=" + KeyEvent.getKeyText(keyCode) + "].");
|
|
}
|
|
}
|
|
|
|
// documentation inherited from interface AncestorListener
|
|
public void ancestorAdded (AncestorEvent e)
|
|
{
|
|
gainedFocus();
|
|
|
|
if (_window == null) {
|
|
_window = SwingUtilities.getWindowAncestor(_target);
|
|
_window.addWindowFocusListener(this);
|
|
}
|
|
}
|
|
|
|
// documentation inherited from interface AncestorListener
|
|
public void ancestorMoved (AncestorEvent e)
|
|
{
|
|
// nothing for now
|
|
}
|
|
|
|
// documentation inherited from interface AncestorListener
|
|
public void ancestorRemoved (AncestorEvent e)
|
|
{
|
|
lostFocus();
|
|
|
|
if (_window != null) {
|
|
_window.removeWindowFocusListener(this);
|
|
_window = null;
|
|
}
|
|
}
|
|
|
|
// documentation inherited from interface WindowFocusListener
|
|
public void windowGainedFocus (WindowEvent e)
|
|
{
|
|
gainedFocus();
|
|
}
|
|
|
|
// documentation inherited from interface WindowFocusListener
|
|
public void windowLostFocus (WindowEvent e)
|
|
{
|
|
lostFocus();
|
|
}
|
|
|
|
protected class KeyInfo extends Interval
|
|
{
|
|
/**
|
|
* Constructs a key info object for the given key code.
|
|
*/
|
|
public KeyInfo (int keyCode)
|
|
{
|
|
_keyCode = keyCode;
|
|
_keyText = KeyEvent.getKeyText(_keyCode);
|
|
_pressCommand = _xlate.getPressCommand(_keyCode);
|
|
_releaseCommand = _xlate.getReleaseCommand(_keyCode);
|
|
int rate = _xlate.getRepeatRate(_keyCode);
|
|
_pressDelay = (rate == 0) ? 0 : (1000L / rate);
|
|
_repeatDelay = _xlate.getRepeatDelay(_keyCode);
|
|
}
|
|
|
|
/**
|
|
* Sets the last time the key was pressed.
|
|
*/
|
|
public synchronized void setPressTime (long time)
|
|
{
|
|
if (_lastPress == 0 && _pressCommand != null) {
|
|
// post the initial key press command
|
|
postPress(time);
|
|
}
|
|
|
|
if (!_scheduled && _pressDelay > 0) {
|
|
// register an interval to post the key press command
|
|
// until the key is decidedly released
|
|
if (_repeatDelay > 0) {
|
|
schedule(_repeatDelay, _pressDelay);
|
|
|
|
} else {
|
|
schedule(_pressDelay, true);
|
|
}
|
|
_scheduled = true;
|
|
|
|
if (DEBUG_EVENTS) {
|
|
Log.info("Pressing key [key=" + _keyText + "].");
|
|
}
|
|
}
|
|
|
|
_lastPress = time;
|
|
_lastRelease = time;
|
|
}
|
|
|
|
/**
|
|
* Sets the last time the key was released.
|
|
*/
|
|
public synchronized void setReleaseTime (long time)
|
|
{
|
|
release(time);
|
|
_lastRelease = time;
|
|
|
|
// handle key release events received so quickly after the key
|
|
// press event that the press/release times are exactly equal
|
|
// and, in intervalExpired(), we would therefore be unable to
|
|
// distinguish between the key being initially pressed and the
|
|
// actual true key release that's taken place.
|
|
|
|
// the only case I can think of that might result in this
|
|
// happening is if the event manager class queues up a key
|
|
// press and release event succession while other code is
|
|
// executing, and when it comes time for it to dispatch the
|
|
// events in its queue it manages to dispatch both of them to
|
|
// us really-lickety-split. one would still think at least a
|
|
// few milliseconds should pass between the press and release,
|
|
// but in any case, we arguably ought to be watching for and
|
|
// handling this case for posterity even though it would seem
|
|
// unlikely or impossible, and so, now we do, which is a good
|
|
// thing since it appears this does in fact happen, and not so
|
|
// infrequently.
|
|
if (_lastPress == _lastRelease) {
|
|
if (DEBUG_EVENTS) {
|
|
Log.warning("Insta-releasing key due to equal key " +
|
|
"press/release times [key=" + _keyText + "].");
|
|
}
|
|
release(time);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Releases the key if pressed and cancels any active key repeat
|
|
* interval.
|
|
*/
|
|
public synchronized void release (long timestamp)
|
|
{
|
|
// bail if we're not currently pressed
|
|
if (_lastPress == 0) {
|
|
return;
|
|
}
|
|
|
|
if (DEBUG_EVENTS) {
|
|
Log.info("Releasing key [key=" + _keyText + "].");
|
|
}
|
|
|
|
// remove the repeat interval
|
|
if (_scheduled) {
|
|
cancel();
|
|
_scheduled = false;
|
|
}
|
|
|
|
if (_releaseCommand != null) {
|
|
// post the key release command
|
|
postRelease(timestamp);
|
|
}
|
|
|
|
// clear out the last press and release timestamps
|
|
_lastPress = _lastRelease = 0;
|
|
}
|
|
|
|
// documentation inherited
|
|
public synchronized void expired ()
|
|
{
|
|
long now = System.currentTimeMillis();
|
|
long deltaPress = now - _lastPress;
|
|
long deltaRelease = now - _lastRelease;
|
|
|
|
if (KeyboardManager.DEBUG_INTERVAL) {
|
|
Log.info("Interval [key=" + _keyText +
|
|
", deltaPress=" + deltaPress +
|
|
", deltaRelease=" + deltaRelease + "].");
|
|
}
|
|
|
|
// handle a normal interval where we either (a) create a
|
|
// sub-interval if we can't yet determine definitively
|
|
// whether the key is still down, (b) cease repeating if
|
|
// we're certain the key is now up, or (c) repeat the key
|
|
// command if we're certain the key is still down
|
|
if (_lastRelease != _lastPress) {
|
|
// if (deltaRelease < _repeatDelay) {
|
|
// // register a one-shot sub-interval to
|
|
// // definitively check whether the key was released
|
|
// long delay = _repeatDelay - deltaRelease;
|
|
// _siid = IntervalManager.register(
|
|
// this, delay, Long.valueOf(_lastPress), false);
|
|
// if (KeyboardManager.DEBUG_INTERVAL) {
|
|
// Log.info("Registered sub-interval " +
|
|
// "[id=" + _siid + "].");
|
|
// }
|
|
|
|
// } else {
|
|
// we know the key was released, so cease repeating
|
|
release(now);
|
|
// }
|
|
|
|
} else if (_lastPress != 0 && _pressCommand != null) {
|
|
// post the key press command again
|
|
postPress(now);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Old stuff- sub interval stuff was commented out prior to my
|
|
* reworking of Interval, I'll be damned if I'm going to convert this
|
|
* code that wasn't even being used.
|
|
} else if (id == _siid) {
|
|
// handle the sub-interval that checks whether the key has
|
|
// really been released since the normal interval expired
|
|
// at an inopportune time for a definitive check
|
|
|
|
// clear out the non-recurring sub-interval identifier
|
|
_siid = -1;
|
|
|
|
// make sure the key hasn't been pressed again since the
|
|
// sub-interval was registered
|
|
if (_lastPress != ((Long)arg).longValue()) {
|
|
if (KeyboardManager.DEBUG_INTERVAL) {
|
|
Log.warning("Key pressed since sub-interval was " +
|
|
"registered, aborting release check " +
|
|
"[key=" + _keyText + "].");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// provide the last word on whether the key was released
|
|
if ((_lastRelease != _lastPress) &&
|
|
deltaRelease >= _repeatDelay) {
|
|
release(now);
|
|
|
|
} else if (_pressCommand != null) {
|
|
// post the key command again
|
|
postPress(now);
|
|
}
|
|
}
|
|
}
|
|
**/
|
|
|
|
/**
|
|
* Posts the press command for this key and notifies all key
|
|
* observers of the key press.
|
|
*/
|
|
protected void postPress (long timestamp)
|
|
{
|
|
notifyObservers(KeyEvent.KEY_PRESSED, _keyCode, timestamp);
|
|
Controller.postAction(_target, _pressCommand);
|
|
}
|
|
|
|
/**
|
|
* Posts the release command for this key and notifies all key
|
|
* observers of the key release.
|
|
*/
|
|
protected void postRelease (long timestamp)
|
|
{
|
|
notifyObservers(KeyEvent.KEY_RELEASED, _keyCode, timestamp);
|
|
Controller.postAction(_target, _releaseCommand);
|
|
}
|
|
|
|
/** Returns a string representation of the key info object. */
|
|
public String toString ()
|
|
{
|
|
return "[key=" + _keyText + "]";
|
|
}
|
|
|
|
/** True if we are a scheduled interval. */
|
|
protected boolean _scheduled = false;
|
|
|
|
/** The last time a key released event was received for this key. */
|
|
protected long _lastRelease;
|
|
|
|
/** The last time a key pressed event was received for this key. */
|
|
protected long _lastPress;
|
|
|
|
/** The press action command associated with this key. */
|
|
protected String _pressCommand;
|
|
|
|
/** The release action command associated with this key. */
|
|
protected String _releaseCommand;
|
|
|
|
/** A text representation of this key. */
|
|
protected String _keyText;
|
|
|
|
/** The key code associated with this key info object. */
|
|
protected int _keyCode;
|
|
|
|
/** The milliseconds to sleep between sending repeat key commands. */
|
|
protected long _pressDelay;
|
|
|
|
/** The delay in milliseconds before auto-repeating the key press. */
|
|
protected long _repeatDelay;
|
|
}
|
|
|
|
/** An observer operation to notify observers of a key event. */
|
|
protected static class KeyObserverOp implements ObserverList.ObserverOp
|
|
{
|
|
/** Initialized the operation with its parameters. */
|
|
public void init (int id, int keyCode, long timestamp)
|
|
{
|
|
_id = id;
|
|
_keyCode = keyCode;
|
|
_timestamp = timestamp;
|
|
}
|
|
|
|
// documentation inherited from interface ObserverList.ObserverOp
|
|
public boolean apply (Object observer)
|
|
{
|
|
((KeyObserver)observer).handleKeyEvent(_id, _keyCode, _timestamp);
|
|
return true;
|
|
}
|
|
|
|
/** The key event id. */
|
|
protected int _id;
|
|
|
|
/** The key code. */
|
|
protected int _keyCode;
|
|
|
|
/** The key event timestamp. */
|
|
protected long _timestamp;
|
|
}
|
|
|
|
/** Whether to output debugging info for individual key events. */
|
|
protected static final boolean DEBUG_EVENTS = false;
|
|
|
|
/** Whether to output debugging info for interval callbacks. */
|
|
protected static final boolean DEBUG_INTERVAL = false;
|
|
|
|
/** The default repeat delay. */
|
|
protected static final long DEFAULT_REPEAT_DELAY = 50L;
|
|
|
|
/** The expected approximate milliseconds between each key
|
|
* release/press event while the key is being auto-repeated. */
|
|
protected long _repeatDelay = DEFAULT_REPEAT_DELAY;
|
|
|
|
/** A hashtable mapping key codes to {@link KeyInfo} objects. */
|
|
protected HashIntMap _keys = new HashIntMap();
|
|
|
|
/** Whether the keyboard manager currently has the keyboard focus. */
|
|
protected boolean _focus;
|
|
|
|
/** Whether the keyboard manager is accepting keyboard input. */
|
|
protected boolean _enabled;
|
|
|
|
/** The window containing our target component whose focus events we
|
|
* care to observe, or null if we're not observing a window. */
|
|
protected Window _window;
|
|
|
|
/** The component that receives keyboard events and that we associate
|
|
* with posted controller commands. */
|
|
protected JComponent _target;
|
|
|
|
/** The translator that maps keyboard events to controller commands. */
|
|
protected KeyTranslator _xlate;
|
|
|
|
/** The list of key observers. */
|
|
protected ObserverList _observers =
|
|
new ObserverList(ObserverList.FAST_UNSAFE_NOTIFY);
|
|
|
|
/** The operation used to notify observers of actual key events. */
|
|
protected KeyObserverOp _keyOp = new KeyObserverOp();
|
|
|
|
/** Whether native key auto-repeating was enabled when the keyboard
|
|
* manager was last enabled. */
|
|
protected boolean _nativeRepeat;
|
|
}
|