/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.rc.swing.driver; import java.awt.AWTError; import java.awt.AWTEvent; import java.awt.AWTException; import java.awt.Component; import java.awt.Container; import java.awt.Event; import java.awt.Frame; import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import javax.swing.CellRendererPane; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.UIManager; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.Validate; import org.eclipse.jubula.rc.common.driver.ClickOptions; import org.eclipse.jubula.rc.common.driver.ClickOptions.ClickModifier; import org.eclipse.jubula.rc.common.driver.DragAndDropHelper; import org.eclipse.jubula.rc.common.driver.IEventThreadQueuer; import org.eclipse.jubula.rc.common.driver.IMouseMotionTracker; import org.eclipse.jubula.rc.common.driver.IRobot; import org.eclipse.jubula.rc.common.driver.IRobotEventConfirmer; import org.eclipse.jubula.rc.common.driver.IRobotEventInterceptor; import org.eclipse.jubula.rc.common.driver.IRobotFactory; import org.eclipse.jubula.rc.common.driver.IRunnable; import org.eclipse.jubula.rc.common.driver.InterceptorOptions; import org.eclipse.jubula.rc.common.driver.KeyTyper; import org.eclipse.jubula.rc.common.driver.MouseMovementStrategy; import org.eclipse.jubula.rc.common.driver.RobotTiming; import org.eclipse.jubula.rc.common.exception.RobotException; import org.eclipse.jubula.rc.common.exception.StepExecutionException; import org.eclipse.jubula.rc.common.logger.AutServerLogger; import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IComponent; import org.eclipse.jubula.rc.common.util.LocalScreenshotUtil; import org.eclipse.jubula.rc.common.util.PointUtil; import org.eclipse.jubula.rc.common.util.PropertyUtil; import org.eclipse.jubula.rc.swing.utils.SwingUtils; import org.eclipse.jubula.toolkit.enums.ValueSets; import org.eclipse.jubula.toolkit.enums.ValueSets.InteractionMode; import org.eclipse.jubula.tools.internal.constants.TimingConstantsServer; import org.eclipse.jubula.tools.internal.i18n.I18n; import org.eclipse.jubula.tools.internal.objects.event.EventFactory; import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent; /** * <p> * AWT/Swing implementation of the <code>IRobot</code> interface. It * uses the {@link java.awt.Robot}to move the mouse and perform clicks. Any * mouse move or click is intercepted and confirmed using the appropriate * AWT/Swing implementations of * {@link org.eclipse.jubula.rc.swing.driver.IRobotEventInterceptor}and * {@link org.eclipse.jubula.rc.swing.driver.IRobotEventConfirmer}. * </p> * * <p> * The <code>click()</code> and <code>move()</code> implementations expect * that the graphics component is of type {@link java.awt.Component}and the * constraints object is <code>null</code> or of type * {@link java.awt.Rectangle}. * </p> * * @author BREDEX GmbH * @created 17.03.2005 */ public class RobotAwtImpl implements IRobot<Rectangle> { /** the timeout to flush native events (only relevant for Java 7) */ private static final int FLUSH_TIMEOUT = 10000; /** the logger */ private static AutServerLogger log = new AutServerLogger(RobotAwtImpl.class); /** max retries to get the location on screen */ private static final int MAX_RETRIES = 1; /** ID of Metal Look and Feel */ private static final String METAL_LAF_ID = "Metal"; //$NON-NLS-1$ /** The AWT Robot instance. */ private Robot m_robot; /** The toolkit utilities */ private EventFlusher m_eventFlusher; /** The event interceptor. */ private IRobotEventInterceptor m_interceptor; /** The mouse motion tracker. */ private IMouseMotionTracker m_mouseMotionTracker; /** The event thread queuer. */ private IEventThreadQueuer m_queuer; /** * Scrolls a component to visible. The Scroller assumes that the component is * embedded (directly or indirectly) into a <code>JScrollPane</code>( * <code>JViewPort</code> more precisely). Hierarchies of scrollpanes are * also supported. The default mechanism of * {@link JComponent#scrollRectToVisible(java.awt.Rectangle) is not used} * here because of limitations and bugs: <br> * <code>JViewPort</code> overrides <code>scrollRectToVisible()</code>, * but doesn't call <code>super.scrollRectToVisible()</code>, so that * hierarchies of scrollpanes don't work. <br> * <code>JTextField</code> interprets <code>scrollRectToVisible()</code> * in a different way by scrolling the containing text, not the component * itself. Again, <code>super.scrollRectToVisible()</code> is not called. */ private class Scroller { /** The component to scroll to visible. */ private Component m_component; /** * @param component The component to scroll to visible. */ public Scroller(Component component) { m_component = component; } /** * Scrolls the component to visible. * @param component The component. * @param aRect The bounds. */ private void scrollRectToVisible(Component component, Rectangle aRect) { Container parent; int dx = component.getX(); int dy = component.getY(); for (parent = component.getParent(); (parent != null) && !(parent instanceof JComponent) && !(parent instanceof CellRendererPane); parent = parent .getParent()) { Rectangle bounds = parent.getBounds(); dx += bounds.x; dy += bounds.y; } if ((parent != null) && !(parent instanceof CellRendererPane)) { aRect.x += dx; aRect.y += dy; if (parent instanceof JComponent) { ((JComponent)parent).scrollRectToVisible(aRect); Point p1 = getLocation(m_component, null); Point p2 = parent.getLocationOnScreen(); aRect.x = p1.x - p2.x; aRect.y = p1.y - p2.y; } } if (parent != null) { scrollRectToVisible(parent, aRect); } } /** * Scrolls the component passed to the constructor to visible. * @param aRect The bounds of the component. */ public void scrollRectToVisible(Rectangle aRect) { scrollRectToVisible(m_component, aRect); } } /** * Creates a new instance. * @param factory The Robot factory instance. * @throws RobotException If the AWT-Robot cannot be created. */ public RobotAwtImpl(IRobotFactory factory) throws RobotException { // HERE init robot in the AWT Event Dispatch Thread on Linux ??? (see // http://www.netbeans.org/issues/show_bug.cgi?id=37476) try { m_robot = new Robot(); m_robot.setAutoWaitForIdle(false); m_robot.setAutoDelay(0); } catch (AWTException awte) { log.error(awte); m_robot = null; throw new RobotException(awte); } catch (SecurityException se) { log.error(se); m_robot = null; throw new RobotException(se); } m_interceptor = factory.getRobotEventInterceptor(); m_mouseMotionTracker = factory.getMouseMotionTracker(); m_queuer = factory.getEventThreadQueuer(); m_eventFlusher = new EventFlusher(m_robot, FLUSH_TIMEOUT); } /** * Gets a location inside the component. If <code>offset</code> is * <code>null</code>, it returns the middle of the component otherwise it * adds the offset to the upper left corner. * @param component the component to get the location for * @param offset the offset * @throws IllegalArgumentException if <code>component</code> is null * @return the <b>global </b> coordinates of <code>component</code> */ private Point getLocation(Component component, final Point offset) throws IllegalArgumentException { Validate.notNull(component, "component must not be null"); //$NON-NLS-1$ final Component comp = component; IRunnable<Point> runnable = new IRunnable<Point>() { public Point run() { Point pos = comp.getLocationOnScreen(); if (offset == null) { pos.x += comp.getBounds().width / 2; pos.y += comp.getBounds().height / 2; } else { pos.x += offset.x; pos.y += offset.y; } return pos; } }; Point point = null; StepExecutionException exc = null; int retries = 0; do { try { point = m_queuer.invokeAndWait("getLocation", runnable); //$NON-NLS-1$ } catch (StepExecutionException e) { exc = e; retries++; log.error("getLocation failed - " + retries); //$NON-NLS-1$ try { Thread.sleep(TimingConstantsServer .GET_LOCATION_RETRY_DELAY); } catch (InterruptedException e1) { log.error(e1.getLocalizedMessage()); } } } while (point == null && retries < MAX_RETRIES); if (point == null) { throw exc; } return point; } /** * Implementation of the mouse click. The mouse is moved into the graphics * component by calling <code>moveImpl()</code> before performing the click. * @param graphicsComponent The graphics component to click on * @param constraints The constraints, must be a * <code>java.awt.Rectangle</code> or <code>null</code>. * The constraints are <em>relative</em> to the * location/origin of the <code>graphicsComponent</code>. * @param clickOptions The click options * @param xPos xPos in component * @param yPos yPos in component * @param yAbsolute true if y-position should be absolute * @param xAbsolute true if x-position should be absolute * @throws RobotException If the click delay is interrupted or the event confirmation receives a timeout. */ private void clickImpl(Object graphicsComponent, Rectangle constraints, ClickOptions clickOptions, int xPos, boolean xAbsolute, int yPos, boolean yAbsolute) throws RobotException { moveImpl(graphicsComponent, constraints, xPos, xAbsolute, yPos, yAbsolute, clickOptions); clickImpl(graphicsComponent, clickOptions); } /** * Clicks at the current mouse position. * * @param graphicsComponent The component used for confirming the click. * @param clickOptions Configuration for the click. */ private void clickImpl(Object graphicsComponent, ClickOptions clickOptions) { int buttonMask = getButtonMask(clickOptions.getMouseButton()); int clickCount = clickOptions.getClickCount(); int[] modifierMask = getModifierMask(clickOptions.getClickModifier()); if (clickCount > 0) { IRobotEventConfirmer confirmer = null; if (clickOptions.isConfirmClick()) { InterceptorOptions options = new InterceptorOptions( new long[] { AWTEvent.MOUSE_EVENT_MASK }); confirmer = m_interceptor.intercept(options); } try { pressModifier(modifierMask); RobotTiming.sleepPreClickDelay(); for (int i = 0; i < clickCount; i++) { m_robot.mousePress(buttonMask); RobotTiming.sleepPostMouseDownDelay(); m_eventFlusher.flush(); m_robot.mouseRelease(buttonMask); RobotTiming.sleepPostMouseUpDelay(); m_eventFlusher.flush(); } if (confirmer != null) { confirmer.waitToConfirm(graphicsComponent, new ClickAwtEventMatcher(clickOptions)); } } finally { releaseModifier(modifierMask); } } } /** * @param modifierMask * array of modifiers to press before click */ private void pressModifier(int[] modifierMask) { for (int i = 0; i < modifierMask.length; i++) { keyPress(null, modifierMask[i]); } } /** * @param modifierMask * array of modifiers release after click */ private void releaseModifier(int[] modifierMask) { for (int i = 0; i < modifierMask.length; i++) { keyRelease(null, modifierMask[i]); } } /** * @param clickModifier the click modifier to use for this click * @return an array of modifiers to press before click and release after click */ private int[] getModifierMask(ClickModifier clickModifier) { int[] modifier = new int[0]; if (clickModifier.hasModifiers(ClickModifier.M1)) { modifier = ArrayUtils.add(modifier, SwingUtils.getSystemDefaultModifier()); } if (clickModifier.hasModifiers(ClickModifier.M2)) { modifier = ArrayUtils.add(modifier, SwingUtils.getSystemModifier2()); } if (clickModifier.hasModifiers(ClickModifier.M3)) { modifier = ArrayUtils.add(modifier, SwingUtils.getSystemModifier3()); } if (clickModifier.hasModifiers(ClickModifier.M4)) { modifier = ArrayUtils.add(modifier, SwingUtils.getSystemModifier4()); } return modifier; } /** * Checks if the mouse has to be moved on <code>p</code> or if the mouse * pointer already resides on this location. * @param p The point to move to * @return <code>true</code> if the mouse pointer resides on a different point, otherwise <code>false</code>. */ private boolean isMouseMoveRequired(Point p) { boolean result = true; Point point = m_mouseMotionTracker.getLastMousePointOnScreen(); if (point != null) { result = !point.equals(p); if (log.isDebugEnabled()) { MouseEvent event = (MouseEvent)m_mouseMotionTracker .getLastMouseMotionEvent(); if (event != null) { log.debug("Last mouse motion event point: " //$NON-NLS-1$ + event.getPoint()); } log.debug("Last converted screen point : " + point); //$NON-NLS-1$ log.debug("Required screen point : " + p); //$NON-NLS-1$ log.debug("Mouse move required? : " + result); //$NON-NLS-1$ } } return result; } /** * Implementation of the mouse move. The mouse is moved into the graphics component. * @param graphicsComponent The component to move to * @param constraints The more specific constraints. Use this, for example * when you want the click point to be relative to a part * of the component (e.g. tree node, table cell, etc) * rather than the overall component itself. May be * <code>null</code>. * @param xPos xPos in component * @param yPos yPos in component * @param xAbsolute true if x-position should be absolute * @param yAbsolute true if y-position should be absolute * @param clickOptions The click options * @throws StepExecutionException If the click delay is interrupted or the * event confirmation receives a timeout. */ private void moveImpl(Object graphicsComponent, final Rectangle constraints, final int xPos, final boolean xAbsolute, final int yPos, final boolean yAbsolute, ClickOptions clickOptions) throws StepExecutionException { if (clickOptions.isScrollToVisible()) { ensureComponentVisible((Component)graphicsComponent, constraints); m_eventFlusher.flush(); } Component component = (Component)graphicsComponent; Rectangle bounds = new Rectangle( getLocation(component, new Point(0, 0))); bounds.width = component.getWidth(); bounds.height = component.getHeight(); if (component instanceof Frame) { Frame window = (Frame) component; if (bounds.x < 0 && checkExtendedState(window, Frame.MAXIMIZED_HORIZ)) { bounds.width += 2 * bounds.x; bounds.x = 0; } if (bounds.y < 0 && checkExtendedState(window, Frame.MAXIMIZED_VERT)) { bounds.height += 2 * bounds.y; bounds.y = 0; } } if (constraints != null) { bounds.x += constraints.x; bounds.y += constraints.y; bounds.height = constraints.height; bounds.width = constraints.width; } Point p = PointUtil.calculateAwtPointToGo(xPos, xAbsolute, yPos, yAbsolute, bounds); // Move if necessary if (isMouseMoveRequired(p)) { if (log.isDebugEnabled()) { log.debug("Moving mouse to: " + p); //$NON-NLS-1$ } IRobotEventConfirmer confirmer = null; if (clickOptions.isConfirmClick()) { InterceptorOptions options = new InterceptorOptions(new long[]{ AWTEvent.MOUSE_MOTION_EVENT_MASK}); confirmer = m_interceptor.intercept(options); } Point startpoint = m_mouseMotionTracker.getLastMousePointOnScreen(); if (startpoint == null) { // If there is no starting point the center of the root component is used Component root = SwingUtilities.getRoot(component); Component c = (root != null) ? root : component; startpoint = getLocation(c, null); } Point[] mouseMove = MouseMovementStrategy. getMovementPath(startpoint, p, clickOptions.getStepMovement(), clickOptions.getFirstHorizontal()); for (int i = 0; i < mouseMove.length; i++) { m_robot.mouseMove(mouseMove[i].x, mouseMove[i].y); m_eventFlusher.flush(); } if (confirmer != null) { confirmer.waitToConfirm(component, new MouseMovedAwtEventMatcher()); } } } /** * * @param frame the <code>JFrame</code> to to check if it is maximized * @param stateBits see <code>getExtendedState()</code> * @return <code>true</code> if the specified maximize state is set. */ private boolean checkExtendedState(Frame frame, int stateBits) { return (frame.getExtendedState() & stateBits) == stateBits; } /** * {@inheritDoc} */ public void click(Object graphicsComponent, Rectangle constraints) throws RobotException { click(graphicsComponent, constraints, ClickOptions.create()); } /** * {@inheritDoc} */ public void click(Object graphicsComponent, Rectangle constraints, ClickOptions clickOptions) throws RobotException { clickImpl(graphicsComponent, constraints, clickOptions, 50, false, 50, false); } /** * Gets the InputEvent-ButtonMask of the given mouse button number * @param button the button number * @return the InputEvent button mask */ private int getButtonMask(int button) { if (button == InteractionMode.primary.rcIntValue()) { return InputEvent.BUTTON1_MASK; } if (button == InteractionMode.tertiary.rcIntValue()) { return InputEvent.BUTTON2_MASK; } if (button == InteractionMode.secondary.rcIntValue()) { return InputEvent.BUTTON3_MASK; } throw new RobotException("unsupported mouse button", null); //$NON-NLS-1$ } /** * {@inheritDoc} */ public void clickAtCurrentPosition(Object graphicsComponent, int clickCount, int button) { ClickOptions clickOptions = new ClickOptions(); clickOptions.setClickCount(clickCount); clickOptions.setMouseButton(button); clickImpl(graphicsComponent, clickOptions); } /** * {@inheritDoc} */ public void move(Object graphicsComponent, Rectangle constraints) throws RobotException { moveImpl(graphicsComponent, constraints, 50, false, 50, false, ClickOptions.create()); } /** * {@inheritDoc} */ public void type(Object graphicsComponent, char character) throws RobotException { Validate.notNull(graphicsComponent, "The graphic component must not be null"); //$NON-NLS-1$ try { int modifier = 0; Component component = (Component)graphicsComponent; KeyEvent ke = new KeyEvent(component, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), modifier, KeyEvent.VK_UNDEFINED, character); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke); ke = new KeyEvent(component, KeyEvent.KEY_TYPED, System.currentTimeMillis(), modifier, KeyEvent.VK_UNDEFINED, character); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke); IRobotEventConfirmer confirmer = m_interceptor.intercept( new InterceptorOptions(new long[]{AWTEvent.KEY_EVENT_MASK})); ke = new KeyEvent(component, KeyEvent.KEY_RELEASED, System.currentTimeMillis(), modifier, KeyEvent.VK_UNDEFINED, character); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke); confirmer.waitToConfirm(component, new DefaultAwtEventMatcher(KeyEvent.KEY_RELEASED)); } catch (AWTError awte) { log.error(awte); throw new RobotException(awte); } catch (SecurityException se) { log.error(se); throw new RobotException(se); } } /** * {@inheritDoc} */ public void type(Object graphicsComponent, String text) throws RobotException { if (text != null) { for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); type(graphicsComponent, ch); } } } /** * {@inheritDoc} */ public void keyType(Object graphicsComponent, int keycode) throws RobotException { try { InterceptorOptions options = new InterceptorOptions(new long[]{ AWTEvent.KEY_EVENT_MASK}); IRobotEventConfirmer confirmer = m_interceptor.intercept(options); try { m_robot.keyPress(keycode); m_eventFlusher.flush(); } finally { m_robot.keyRelease(keycode); m_eventFlusher.flush(); } confirmer.waitToConfirm(graphicsComponent, new KeyAwtEventMatcher( KeyEvent.KEY_RELEASED)); } catch (IllegalArgumentException e) { throw new RobotException(e); } } /** * {@inheritDoc} */ public String getSystemModifierSpec() { String keyStrokeSpec = ValueSets.Modifier.control.rcValue(); if (!(UIManager.getLookAndFeel().getID().equals(METAL_LAF_ID))) { if (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() == Event.META_MASK) { keyStrokeSpec = ValueSets.Modifier.meta.rcValue(); } else if (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() == Event.ALT_MASK) { keyStrokeSpec = ValueSets.Modifier.alt.rcValue(); } } return keyStrokeSpec; } /** * Implements the key press or release. * @param graphicsComponent The component, may be <code>null</code> * @param keyCode The key code * @param press If <code>true</code>, the key is pressed, otherwise released */ private void keyPressReleaseImpl(Object graphicsComponent, int keyCode, boolean press) { InterceptorOptions options = new InterceptorOptions(new long[]{ AWTEvent.KEY_EVENT_MASK}); IRobotEventConfirmer confirmer = m_interceptor.intercept(options); if (press) { m_robot.keyPress(keyCode); m_eventFlusher.flush(); } else { m_robot.keyRelease(keyCode); m_eventFlusher.flush(); } confirmer.waitToConfirm(graphicsComponent, new KeyAwtEventMatcher( press ? KeyEvent.KEY_PRESSED : KeyEvent.KEY_RELEASED)); } /** * {@inheritDoc} */ public void keyPress(Object graphicsComponent, int keycode) throws RobotException { keyPressReleaseImpl(graphicsComponent, keycode, true); } /** * {@inheritDoc} */ public void keyRelease(Object graphicsComponent, int keycode) throws RobotException { keyPressReleaseImpl(graphicsComponent, keycode, false); } /** * a method to turn the toggle keys caps-lock, num-lock and scroll-lock on and off. * @param obj Component * @param key to set key Event * @param activated boolean */ public void keyToggle(Object obj, int key, boolean activated) { Validate.notNull(obj, "The graphic component must not be null"); //$NON-NLS-1$ if ((activated && !((Component)obj).getToolkit().getLockingKeyState( key)) || (!activated && ((Component)obj).getToolkit() .getLockingKeyState(key))) { keyPressReleaseImpl(null, key, true); keyPressReleaseImpl(null, key, false); } } /** * {@inheritDoc} */ public void keyStroke(String keyStrokeSpec) throws RobotException { try { KeyTyper.getInstance().type(keyStrokeSpec, m_interceptor, new DefaultAwtEventMatcher(KeyEvent.KEY_PRESSED), new KeyReleasedEventMatcher()); } catch (AWTException e) { throw new RobotException(e); } } /** * Ensures that the passed component is visible. * @param component The component. * @param bounds Optional bounds inside the component. If not <code>null</code>, the bounds are scrolled to visible. * @throws RobotException If the component's screen location cannot be calculated. */ private void ensureComponentVisible(final Component component, final Rectangle bounds) throws RobotException { m_queuer.invokeAndWait("ensureVisible", new IRunnable<Void>() { //$NON-NLS-1$ public Void run() { Rectangle rectangle = bounds != null ? new Rectangle(bounds) : SwingUtilities.getLocalBounds(component); if (log.isDebugEnabled()) { log.debug("Scrolling rectangle to visible: " + rectangle); //$NON-NLS-1$ } Scroller scroller = new Scroller(component); scroller.scrollRectToVisible(rectangle); return null; } }); } /** * {@inheritDoc} */ public void scrollToVisible(Object graphicsComponent, Rectangle constraints) throws RobotException { ensureComponentVisible((Component)graphicsComponent, constraints); } /** * {@inheritDoc} */ public void activateApplication(String method) throws RobotException { try { Window window = getActiveWindow(); if (window == null) { return; } WindowActivationMethod wam = WindowActivationMethod.createWindowActivationMethod( method, m_robot, m_queuer); wam.activate(window); // Verify that window was successfully activated Window activeWindow = m_queuer.invokeAndWait( "getActiveWindow", //$NON-NLS-1$ new IRunnable<Window>() { public Window run() throws StepExecutionException { if (Frame.getFrames().length == 0) { return null; } for (int i = 0; i < Frame.getFrames().length; ++i) { Window curWindow = Frame.getFrames()[i]; while (curWindow.getOwner() != null) { curWindow = curWindow.getOwner(); } if (curWindow.isFocused()) { return curWindow; } } return null; } }); if (activeWindow != window) { throw new StepExecutionException( I18n.getString( TestErrorEvent.WINDOW_ACTIVATION_FAILED, true), EventFactory.createActionError( TestErrorEvent.WINDOW_ACTIVATION_FAILED)); } } catch (Exception exc) { throw new RobotException(exc); } } /** * @return The current mouse position as a Point * {@inheritDoc} */ public Point getCurrentMousePosition() { return m_mouseMotionTracker.getLastMousePointOnScreen(); } /** * Guesses the active window. Returns null if no active window is found. * @return the active window */ private Window getActiveWindow() { return m_queuer.invokeAndWait( "getActiveWindow", //$NON-NLS-1$ new IRunnable<Window>() { public Window run() throws StepExecutionException { if (Frame.getFrames().length == 0) { return null; } for (int i = 0; i < Frame.getFrames().length; ++i) { Window window = Frame.getFrames()[i]; while (window.getOwner() != null) { window = window.getOwner(); } if (window.isVisible()) { return window; } } return null; } }); } /** * * {@inheritDoc} */ public boolean isMouseInComponent(Object graphicsComponent) { Component comp = (Component)graphicsComponent; final Point currMousePos = getCurrentMousePosition(); if (currMousePos == null) { return false; } final Point treeLocUpperLeft = comp.getLocationOnScreen(); final Rectangle bounds = comp.getBounds(); final Point treeLocLowerRight = new Point( bounds.width + treeLocUpperLeft.x, bounds.height + treeLocUpperLeft.y); final boolean x1 = currMousePos.x >= treeLocUpperLeft.x; final boolean x2 = currMousePos.x < treeLocLowerRight.x; final boolean y1 = currMousePos.y >= treeLocUpperLeft.y; final boolean y2 = currMousePos.y < treeLocLowerRight.y; return x1 && x2 && y1 && y2; } /** * Presses the given mouse button on the given component in the given * constraints. <br> * <b>Note:</b> Use only for Drag and Drop! * To click with the mouse, use click-methods! * @param graphicsComponent the component where to press the mouse button. * If null, the mouse is pressed at the current location. * @param constraints A constraints object used by the Robot implementation, may be <code>null</code>. * @param button the mouse button which is to be pressed. */ public void mousePress(Object graphicsComponent, Rectangle constraints, int button) { DragAndDropHelper.getInstance().setDragMode(true); if (graphicsComponent != null) { move(graphicsComponent, constraints); } RobotTiming.sleepPreClickDelay(); m_robot.mousePress(getButtonMask(button)); m_eventFlusher.flush(); } /** * Releases the given mouse button on the given component in the given * constraints. <br> * <b>Note:</b> Use only for Drag and Drop! * To click with the mouse, use click-methods! * @param graphicsComponent The graphics component. * If null, the mouse button is released at the current location. * @param constraints A constraints object used by the Robot implementation, may be <code>null</code>. * @param button the mouse button. */ public void mouseRelease(Object graphicsComponent, Rectangle constraints, int button) throws RobotException { if (graphicsComponent != null) { move(graphicsComponent, constraints); } RobotTiming.sleepPreClickDelay(); m_robot.mouseRelease(getButtonMask(button)); m_eventFlusher.flush(); DragAndDropHelper.getInstance().setDragMode(false); } /** * {@inheritDoc} */ public void click(Object graphicsComponent, Rectangle constraints, ClickOptions clickOptions, int xPos, boolean xAbsolute, int yPos, boolean yAbsolute) throws RobotException { clickImpl(graphicsComponent, constraints, clickOptions, xPos, xAbsolute, yPos, yAbsolute); } /** * {@inheritDoc} */ public String getPropertyValue(Object graphicsComponent, String propertyName) throws RobotException { return PropertyUtil.getPropertyValue(graphicsComponent, propertyName); } /** {@inheritDoc} */ public BufferedImage createFullScreenCapture() { return LocalScreenshotUtil.createFullScreenCapture(); } /** * {@inheritDoc} */ public void shakeMouse() { /** number of pixels by which a "mouse shake" offsets the mouse cursor */ final int mouseShakeOffset = 10; Point origin = getCurrentMousePosition(); try { m_robot.mouseMove( origin.x + mouseShakeOffset, origin.y + mouseShakeOffset); m_robot.mouseMove( origin.x - mouseShakeOffset, origin.y - mouseShakeOffset); } finally { m_robot.mouseMove(origin.x, origin.y); } } /** {@inheritDoc} */ public Rectangle getComponentBounds(IComponent component) { Component realComponent = (Component) component.getRealComponent(); Point screenLocation = realComponent.getLocationOnScreen(); Rectangle rec = realComponent.getBounds(); rec.setLocation(screenLocation.x, screenLocation.y); return rec; } }