Java Swing calls a number of methods with either an
InputEvent or an
ActionEvent instance as a parameter.
Each of these classes provide a
getModifiers() method that can be used to find the current state of the
MouseEvent subclass of
InputEvent there are even more details available, such as the exact key or button that was pressed.
However, what if some of these details are required without having an event instance available?
For example, I was looking to add a shortcut to a
If the CTRL button is being held down while a node is expanded or collapsed, all child nodes should recursively do the same.
The plan seemed fairly simple: Call
JTree.addTreeExpansionListener(TreeExpansionListener), adding a listener that detects if the CTRL button is being held down, then recursively call
Unfortunately, the methods on
TreeExpansionListener (as well as
TreeWillExpandListener) only provide a
TreeExpansionEvent, which does not extend either
ActionEvent and does not provide any of the above details.
I thought I could easily trap these details myself, through the use of
Unfortunately, due to the event model in Swing, these listeners are not called until after the tree expand or collapse methods have been processed.
Attempting to add these listeners to the component's parent also failed, as the events are first dispatched to the child.
There is also no API method to get the current keyboard or mouse state "from anywhere" without having access to one of the event instances, short of writing some custom JNI code.
Changing my requirements to modify the GUI instead of making the keyboard modifier work would also have been an undesirable possibility.
This situation is not particular to
There are many other subclasses of
AWTEventListener that pass subclasses of
EventObject (other than subclasses of
AWTEvent) that do not provide means for accessing the desired details.
I did find some possible solutions:
Use of a glass pane. Basically, it is set as visible and intercepts all events. Unfortunately, this requires then forwarding all events to the desired destination - including a non-trivial amount of work to determine the original destination. This would not scale well, and does not work well with a decorator-type pattern. While it may be a good feature to remember for other specific uses, this certainly is not the first solution I would recommend for this scenario.
In each of the below possible solutions, the idea is to find the event when it is available, and keep a reference to the latest someplace for later:
A rather hacked solution - and another one that I do not at all recommend: Get the current
Toolkit.getSystemEventQueue(). Then, replace it with a subclassed version using
push(). In the subclassed version, override the
dispatchEventmethod to capture and store the state of any mouse and/or keyboard events. Note that if not done properly, this can result in serious performance and other issues.
JFrameor any other component that is the target for extension. Override
processEvent, or more efficiently,
processMouseEvent. Unfortunately, this does not allow for these listeners to be added as a decorator after the instance is created.
The winner: Add a listener using
Toolkit.addAWTEventListener(AWTEventListener listener, long eventMask). This is my current favorite and go-forward approach, as it works well with decorators and is better-performing than #1. By passing an appropriate
eventMask, this assures that the listener only receives events that it is remotely interested in. For example, in my scenario, I'm using
AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK.
AWTEventListeneradded here in in effect for the entire application, and again, can result in serious performance and other issues if not done properly. Adding such a listener to store only the last keyboard- or mouse-related event should only need to be done once, and suited for reuse throughout the entire application. (Continually adding additional listeners for each use would certainly not scale well.) Look for a utility version of this functionality in an upcoming release of MarkUtils-Swing.