// Copyright 2013 SICK AG. All rights reserved.
package de.sick.guicheck.swing;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import com.sun.javafx.robot.FXRobot;
import de.sick.guicheck.fx.GcUtilsFX;
/**
* A JavaFX robot which automatically waits for the windowing thread to become idle. Input device methods like mouse or
* keyboard actions are delegated to the {@link FXRobot} given in the constructor.
* <p>
* Every method which triggers an input action, like mouse or keyboard, checks if the platform is still alive, calls the
* corresponding method of the robot and waits for the UI becoming idle.
*
* @see FXRobot
* @author linggol (created)
*/
public class GcRobotSwing
{
private final Robot m_robot;
private final GcWindowSwing m_window;
GcRobotSwing(GcWindowSwing window, Robot robot)
{
m_window = window;
m_robot = robot;
}
/**
* @see Robot#keyPress(int)
*/
public GcRobotSwing keyPress(int code)
{
m_robot.keyPress(code);
GcUtilsSwing.waitForIdle();
return this;
}
/**
* @see Robot#keyRelease(int)
*/
public GcRobotSwing keyRelease(int code)
{
m_robot.keyRelease(code);
GcUtilsSwing.waitForIdle();
return this;
}
/**
* @see Robot#keyPress(int)
* @see Robot#keyRelease(int)
*/
public GcRobotSwing keyType(int... codes)
{
for (int c : codes)
{
keyPress(c);
keyRelease(c);
}
return this;
}
/**
* Type the given string on the keyboard. The component having the focus will get the resulting {@link KeyEvent}
*/
public GcRobotSwing keyType(String s)
{
for (char c : s.toCharArray())
{
keyType(c);
}
return this;
}
/**
* Type the given character on the keyboard. The component having the focus will get the resulting {@link KeyEvent}
*/
public GcRobotSwing keyType(char c)
{
boolean l_isUpperCase = Character.isUpperCase(c);
int l_code = KeyEvent.getExtendedKeyCodeForChar(c);
if (l_code != KeyEvent.VK_UNDEFINED)
{
try
{
if (l_isUpperCase)
{
keyPress(KeyEvent.VK_SHIFT);
}
keyPress(l_code);
keyRelease(l_code);
if (l_isUpperCase)
{
keyRelease(KeyEvent.VK_SHIFT);
}
return this;
}
catch (IllegalArgumentException e)
{
// The key code isn't known
// We create a custom event for this character
}
}
// We didn't get a valid key code for the character, so create a custom KEY_TYPED event
// Forget the key event if there is no focus owner at the moment
final Component l_comp = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (l_comp != null)
{
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(new KeyEvent(l_comp, KeyEvent.KEY_TYPED, System.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, c));
}
return this;
}
/**
* @see Robot#mouseWheel(int)
*/
public GcRobotSwing mouseWheel(int wheelAmt)
{
m_robot.mouseWheel(wheelAmt);
GcUtilsFX.waitForIdle();
return this;
}
/**
* Moves the mouse to the center point of the component given via name.
*
* @see Robot#mouseMove(int, int)
*/
public GcRobotSwing mouseMoveToCenter(String name)
{
return mouseMoveToCenter(m_window.component(name));
}
/**
* Moves the mouse to the center point of the component given via class.
*
* @see Robot#mouseMove(int, int)
*/
public GcRobotSwing mouseMoveToCenter(Class<?> clazz)
{
return mouseMoveToCenter(m_window.component(clazz));
}
/**
* Moves the mouse to the center point of the given component.
*
* @see Robot#mouseMove(int, int)
*/
public GcRobotSwing mouseMoveToCenter(GcComponentSwing<?> component)
{
Rectangle l_bounds = component.getComponent().getBounds();
return mouseMove(component, (int)(l_bounds.getWidth() / 2), (int)(l_bounds.getHeight() / 2));
}
/**
* Moves the mouse to the given point on the screen.
*
* @see Robot#mouseMove(int, int)
*/
public GcRobotSwing mouseMove(int x, int y)
{
m_robot.mouseMove(x, y);
GcUtilsFX.waitForIdle();
return this;
}
/**
* Moves the mouse to the given point relative to the component given by name.
*
* @see Robot#mouseMove(int, int)
*/
public GcRobotSwing mouseMove(String name, int x, int y)
{
return mouseMove(m_window.component(name), x, y);
}
/**
* Moves the mouse to the given point relative to the component given by clazz.
*
* @see Robot#mouseMove(int, int)
*/
public GcRobotSwing mouseMove(Class<?> clazz, int x, int y)
{
return mouseMove(m_window.component(clazz), x, y);
}
/**
* Moves the mouse to the given point relative to the component.
*
* @see Robot#mouseMove(int, int)
*/
public GcRobotSwing mouseMove(GcComponentSwing<?> component, int x, int y)
{
Point l_point = new Point(x, y);
SwingUtilities.convertPointToScreen(l_point, component.getComponent());
return mouseMove((int)l_point.getX(), (int)l_point.getY());
}
/**
* Presses the primary mouse button.
*/
public GcRobotSwing mousePress()
{
mousePress(InputEvent.BUTTON1_MASK);
return this;
}
/**
* Presses the secondary mouse button.
*/
public GcRobotSwing mousePressSecondary()
{
mousePress(InputEvent.BUTTON2_MASK);
return this;
}
/**
* Releases the primary mouse button.
*/
public GcRobotSwing mouseRelease()
{
mouseRelease(InputEvent.BUTTON1_MASK);
return this;
}
/**
* Releases the secondary mouse button.
*/
public GcRobotSwing mouseReleaseSecondary()
{
mouseRelease(InputEvent.BUTTON2_MASK);
return this;
}
/**
* Clicks the primary mouse button. Automatically adds mousePress and mouseRelease calls.
*/
public GcRobotSwing mouseClick()
{
mouseClick(InputEvent.BUTTON1_MASK);
return this;
}
/**
* Clicks the secondary mouse button. Automatically adds mousePress and mouseRelease calls.
*/
public GcRobotSwing mouseClickSecondary()
{
mouseClick(InputEvent.BUTTON3_MASK);
return this;
}
/**
* Double-clicks the primary mouse button. Automatically adds mousePress and mouseRelease calls.
*/
public GcRobotSwing mouseDblClick()
{
mouseClick(InputEvent.BUTTON1_MASK);
mouseClick(InputEvent.BUTTON1_MASK);
return this;
}
/**
* Double-clicks the secondary mouse button. Automatically adds mousePress and mouseRelease calls.
*/
public GcRobotSwing mouseDblClickSecondary()
{
mouseClick(InputEvent.BUTTON2_MASK);
mouseClick(InputEvent.BUTTON2_MASK);
return this;
}
private void mouseClick(int buttons)
{
mousePress(buttons);
mouseRelease(buttons);
GcUtilsSwing.waitForIdle();
}
private void mousePress(int buttons)
{
m_robot.mousePress(buttons);
GcUtilsSwing.waitForIdle();
}
private void mouseRelease(int buttons)
{
m_robot.mouseRelease(buttons);
GcUtilsSwing.waitForIdle();
}
/**
* Set the focus to the component given via name. Setting the focus is done via an explicit call to
* {@link JComponent#requestFocus()} and not using any method of the {@link Robot}.
*/
public GcRobotSwing focus(String name)
{
return focus(m_window.component(name));
}
/**
* Set the focus to the given component. Setting the focus is done via an explicit call to
* {@link JComponent#requestFocus()} and not using any method of the {@link Robot}.
*/
public GcRobotSwing focus(final GcComponentSwing<?> component)
{
GcUtilsSwing.runLaterAndWait(new Runnable()
{
@Override
public void run()
{
component.getComponent().requestFocus();
}
});
GcUtilsSwing.waitForIdle(10, 1);
return this;
}
}