Theodore Norvell
Theodore Norvell

Reputation: 16221

Given a Swing container and coordinates, how can I find the source for a manufactured mouse event?

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

Answers (1)

camickr
camickr

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

Related Questions