I've read the definite tutorial on key bindings a few times, but my brain cache doesn't seem large enough to hold the complicated processes.
I was debugging a key binding problem (turned out I was using the wrong JComponent.WHEN_* condition), and I stumbled upon a concise and hilarious javadoc for the package private javax.swing.KeyboardManager by an (unfortunately) anonymous Java engineer.
My question is this: except for KeyEventDispatcher which is checked at the very beginning, does the description miss and/or mistake anything?
The KeyboardManager class is used to
help dispatch keyboard actions for the
WHEN_IN_FOCUSED_WINDOW style actions.
Actions with other conditions are
handled directly in JComponent.
Here's a description of the symantics
[sic] of how keyboard dispatching
should work atleast [sic] as I
understand it.
KeyEvents are dispatched to the
focused component. The focus manager
gets first crack at processing this
event. If the focus manager doesn't
want it, then the JComponent calls
super.processKeyEvent() this allows
listeners a chance to process the
event.
If none of the listeners "consumes"
the event then the keybindings get a
shot. This is where things start to
get interesting. First, KeyStokes
[sic] defined with the WHEN_FOCUSED
condition get a chance. If none of
these want the event, then the
component walks though it's [sic] parents
looked for actions of type
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
If no one has taken it yet, then it
winds up here. We then look for
components registered for
WHEN_IN_FOCUSED_WINDOW events and fire
to them. Note that if none of those
are found then we pass the event to
the menubars and let them have a crack
at it. They're handled differently.
Lastly, we check if we're looking at
an internal frame. If we are and no
one wanted the event then we move up
to the InternalFrame's creator and see
if anyone wants the event (and so on
and so on).
(UPDATE) If you've ever wondered about this bold warning in the key bindings guide:
Because the order of searching the components is unpredictable, avoid duplicate WHEN_IN_FOCUSED_WINDOW bindings!
It's because of this segment in KeyboardManager#fireKeyboardAction:
Object tmp = keyMap.get(ks);
if (tmp == null) {
// don't do anything
} else if ( tmp instanceof JComponent) {
...
} else if ( tmp instanceof Vector) { //more than one comp registered for this
Vector v = (Vector)tmp;
// There is no well defined order for WHEN_IN_FOCUSED_WINDOW
// bindings, but we give precedence to those bindings just
// added. This is done so that JMenus WHEN_IN_FOCUSED_WINDOW
// bindings are accessed before those of the JRootPane (they
// both have a WHEN_IN_FOCUSED_WINDOW binding for enter).
for (int counter = v.size() - 1; counter >= 0; counter--) {
JComponent c = (JComponent)v.elementAt(counter);
//System.out.println("Trying collision: " + c + " vector = "+ v.size());
if ( c.isShowing() && c.isEnabled() ) { // don't want to give these out
fireBinding(c, ks, e, pressed);
if (e.isConsumed())
return true;
}
}
So the order of searching is actually predictable, but obviously dependent on this particular implementation, so it's better not to rely on it at all. Keep it unpredictable.
(Javadoc and code is from jdk1.6.0_b105 on WinXP.)
解决方案
We need to start debugging from Component.dispatchEventImpl.
Just reading through the source comments of the method should give you the perfect idea of how events flow in Swing(you can also start one level up from EventQueue.pumpEventsForHeirarchy).
For clarity just let me give an extract from the code:
Set timestamp and modifiers of current event.; Pre-dispatchers. Do any necessary retargeting/reordering here before we notify AWTEventListeners.
Allow the Toolkit to pass this event to AWTEventListeners.
If no one has consumed a key event, allow the KeyboardFocusManager to process it.
Allow input methods to process the event
Pre-process any special events before delivery
Deliver event for normal processing
Special handling for 4061116 : Hook for browser to close modal dialogs.:)
Allow the peer to process the event. Except KeyEvents, they will be processed by peer after all KeyEventPostProcessors (see DefaultKeyboardFocusManager.dispatchKeyEvent())
Now you can match the above flow with your description to determine whether its right or not. But the point is you should really not depend on javadocs of private classes, the reason being that the developers usually dont care to update the comments of private classes when code changes, so the docs may become obsolete.