Reputation: 16221
I have a container such as a JFrame or JComponent. The container may contain components such as buttons, panels, etc that might have mouse listeners. What I'd like to do is to create a MouseEvent
and add it to the system event queue, i.e. to simulate what happens when the user clicks on the container at a certain location. I know the coordinates of the click relative to the container, but I don't know how the container's children and grandchildren etc are arranged.
What I have so far is the following
Component source = ????? ;
final MouseEvent ev = new MouseEvent(source, id, now, modifiers, x, y,
xAbs, yAbs, clickCount, popupTrigger, button) ;
SwingUtilities.invokeAndWait(new Runnable() {
@Override public void run() {
EventQueue q = Toolkit.getDefaultToolkit().getSystemEventQueue() ;
q.postEvent(ev);
} });
This works if I can find the right subcomponent to be the source
. But how can I do that?
In java.awt.Container
there is a method called getMouseEventTarget
unfortunately this is not public.
I know I can do something similar to this with java.awt.Robot
, but I don't want to actually move the mouse pointer. I'd like to be able to do this in headless mode, where I don't think Robot
is likely to work.
Edit
Here is my attempt at a solution. It is based on the nonpublic method java.awt.Container.getMouseEventTarget. I would like to find a simpler and more correct solution.
static Triple<Component,Integer,Integer> searchForMouseEventSource( Component p, int x, int y ) {
synchronized( p.getTreeLock() ) {
return searchForMouseEventSourceImpl( p, x, y ) ;
}
}
static private Triple<Component,Integer,Integer> searchForMouseEventSourceImpl( Component comp, int x, int y ) {
// Based on getMouseEventTarget in java.awt.container
// Search for a descendant component that can take the event.
if( comp instanceof Container ) {
Container cont = (Container) comp ;
for (int i = 0; i < cont.getComponentCount(); i++) {
Component child = cont.getComponent(i) ;
if( child != null && child.isVisible() ) {
int childX = child.getX() ;
int childY = child.getY();
if( child.contains(x - childX, y - childY) ) {
Triple<Component,Integer,Integer> result
= searchForMouseEventSourceImpl( child, x-childX, y-childY );
if( result != null ) return result ;
}
}
}
}
// If we get here there, is no descendant that can take the event.
if( comp.contains(x,y) && takesMouseEvents( comp ) ) {
return new Triple<Component,Integer,Integer>( comp, x, y) ; }
else {
return null ; }
}
static boolean takesMouseEvents( Component comp ) {
// The following is what we want (from java.awt.Container)
//return (comp.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0
// || (comp.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0
// || (comp.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0
// || comp.mouseListener != null
// || comp.mouseMotionListener != null
// || comp.mouseWheelListener != null;
// The next line covers the last 3 cases, I think.
return comp.getMouseListeners().length != 0
|| comp.getMouseMotionListeners().length != 0
|| comp.getMouseWheelListeners().length != 0 ;
}
static class Triple<T,U,V> {
public T first ;
public U second ;
public V third ;
public Triple( T first, U second, V third ) {
this.first = first ; this.second = second ; this.third = third ; }
}
Upvotes: 0
Views: 180
Reputation: 324108
This works if I can find the right subcomponent to be the source. But how can I do that?
Component child = container.getComponentAt( Point );
Edit:
This might do what you want:
Component child = SwingUtilities.getDeepestComponentAt(parent, x, y)
Upvotes: 1