tags:

views:

145

answers:

1

The problem:

I am trying to debug some focus-related problems in my Java Swing application. There are times when some components seem to be grabbing focus and I cannot figure out where in code this is happening.

What I have tried:

  • A VetoableChangeListener with KeyboardFocusManager (for focusOwner). This does give me information about which components lose and gain focus, but it does not help me pin point where in code the focus is being requested.

  • A custom KeyboardFocusManager. But in that too I can intervene only when it receives events. By that time the call stack of the call to requestFocus is already lost.

  • A custom EventQueue. But there too I am able to intervene in the dispatchEvent method which is again called from the EDT. Again the call stack is lost (interestingly the postEvent(AWTEvent) is not called).

Question:

What I am looking for is the call stack when the call to requestFocusInWindow is made. Is it possible to get this information. Perhaps, if I could redefine the method used to post the event in EventQueue, then I can print the stack dump. However the EventQueue.postEvent(AWTEvent) does not get invoked.

Can anyone suggest a solution which will help me get the state of the stack when the call to requestFocus or requestFocusInWIndow may have been made?

+3  A: 

It seems they (Sun) really don't want you to do that. At first glance there don't appear to be any virtual methods in that path that can easily be overridden, not in EventQueue (postEvent is used only for invokeLater and synthesising events from application code) nor in KeyboardFocusManager (as you've discovered, the overridable methods are called later from the dispatch loop.)

Fortunately, if you are using the Sun JRE, there is a place you can insert code, but it's not pretty:

Component.requestFocus() calls the static KeyboardFocusManager.setMostRecentFocusOwner(Component), which updates a private static Map called mostRecentFocusOwners.

So if you can access that static Map using reflection, you can replace it with a forwarding Map that traces calls to its put method:

import com.google.common.collect.ForwardingMap;

// ...

Field mrfoField = KeyboardFocusManager.class.getDeclaredField("mostRecentFocusOwners");
mrfoField.setAccessible(true);
final Map delegate = (Map) mrfoField.get(null);
Map mrfo = new ForwardingMap() {
    public Object put(Object key, Object value) {
        new Throwable().printStackTrace();
        return super.put(key, value);
    }
    protected Map delegate() {
        return delegate;
    }
};
mrfoField.set(null, mrfo);

And this will catch calls to requestFocus and give you the stack traces.

finnw