bd80c348eb
There is no more SafeInterval, instead Intervals can be constructed with a RunQueue to use for expiring. PresentsDObjectMgr implements RunQueue. Client has a getRunQueue() method to get the client side RunQueue. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3283 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, new Long(_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;
|
|
}
|