/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.ui.test.util;
import com.google.dart.tools.internal.corext.refactoring.util.ExecutionUtils;
import org.eclipse.core.runtime.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Scrollable;
/**
* Event emulator used for send different mouse events to given SWT control.
*/
public class EventSender {
/**
* Posts low-level {@link SWT.MouseDown} event.
*/
public static void postMouseDown(int button) {
postMouseDownUp(SWT.MouseDown, button);
}
/**
* Posts low-level {@link SWT.MouseMove} event with absolute coordinates.
*/
public static void postMouseMoveAbs(int x, int y) {
Event event = new Event();
event.type = SWT.MouseMove;
event.x = x;
event.y = y;
postEvent(event);
}
/**
* Posts low-level {@link SWT.MouseMove} event with absolute coordinates.
*/
public static void postMouseMoveAbs(Point p) {
postMouseMoveAbs(p.x, p.y);
}
/**
* Posts low-level {@link SWT.MouseUp} event.
*/
public static void postMouseUp(int button) {
postMouseDownUp(SWT.MouseUp, button);
}
public static void sendCharacter(boolean shift, char character) {
if (shift) {
postKeyEvent(SWT.KeyDown, SWT.SHIFT, (char) 0);
}
postKeyEvent(SWT.KeyDown, 0, character);
postKeyEvent(SWT.KeyUp, 0, character);
if (shift) {
postKeyEvent(SWT.KeyUp, SWT.SHIFT, (char) 0);
}
}
public static void sendCharacter(char character) {
sendCharacter(Character.isUpperCase(character), character);
}
public static void sendKey(int key) {
postKeyEvent(SWT.KeyDown, key, (char) 0);
postKeyEvent(SWT.KeyUp, key, (char) 0);
}
/**
* Sends "key" event with modifiers.
*/
public static void sendKey(int modifiers, int key) {
postModifiers(SWT.KeyDown, modifiers);
try {
postKeyEvent(SWT.KeyDown, key, (char) 0);
postKeyEvent(SWT.KeyUp, key, (char) 0);
} finally {
postModifiers(SWT.KeyUp, modifiers);
}
}
public static void sendText(String text) {
char[] charArray = text.toCharArray();
for (int i = 0; i < charArray.length; i++) {
char c = charArray[i];
sendCharacter(c);
}
}
/**
* Waits given number of milliseconds and runs events loop every 1 millisecond.<br>
* At least one events loop will be executed.
*/
public static void waitEventLoop(int time) {
try {
long start = System.currentTimeMillis();
do {
Thread.sleep(0);
while (Display.getCurrent().readAndDispatch()) {
// do nothing
}
} while (System.currentTimeMillis() - start < time);
} catch (Throwable e) {
throw ExecutionUtils.propagate(e);
}
}
/**
* Check that the current {@link Thread} is the UI thread and posts {@link Event} into the
* {@link Display}.
*/
private static void postEvent(Event event) {
Display display = Display.getCurrent();
Assert.isNotNull(display, "Events can only be sent from the UI thread");
display.post(event);
}
/**
* Posts low-level {@link SWT.KeyDown} or {@link SWT.KeyUp} event.
*/
private static void postKeyEvent(int type, int keyCode, char character) {
Event event = new Event();
event.type = type;
event.keyCode = keyCode;
event.character = character;
postEvent(event);
}
/**
* Posts modifiers up/down event.
*/
private static void postModifiers(int event, int modifiers) {
if ((modifiers & SWT.SHIFT) != 0) {
postKeyEvent(event, SWT.SHIFT, (char) 0);
}
if ((modifiers & SWT.CTRL) != 0) {
postKeyEvent(event, SWT.CTRL, (char) 0);
}
if ((modifiers & SWT.ALT) != 0) {
postKeyEvent(event, SWT.CTRL, (char) 0);
}
}
/**
* Posts low-level {@link SWT.MouseDown} or {@link SWT.MouseUp} event.
*/
private static void postMouseDownUp(int type, int button) {
Event event = new Event();
event.type = type;
event.button = button;
postEvent(event);
}
private static final void updateStateMask(Event event, int button) {
switch (button) {
case 1:
event.stateMask |= SWT.BUTTON1;
break;
case 2:
event.stateMask |= SWT.BUTTON2;
break;
case 3:
event.stateMask |= SWT.BUTTON3;
break;
case 4:
event.stateMask |= SWT.BUTTON4;
break;
case 5:
event.stateMask |= SWT.BUTTON5;
break;
}
}
private final Control control;
private int stateMask;
private int dragButton;
private int lastDragX;
private int lastDragY;
/**
* Constructor to create event emulator for given <code>control</code>.
*/
public EventSender(Control control) {
this.control = control;
}
/**
* Emulates mouse click using button <code>1</code> in last location.
*/
public void click() {
click(1);
}
/**
* Emulates mouse click in last location.
*/
public void click(int button) {
click(lastDragX, lastDragY, button);
}
/**
* Emulate mouse click use given location <code>(x, y)</code> and <code>button</code>.
*/
public void click(int x, int y, int button) {
Event event = createEvent(x, y, button);
control.notifyListeners(SWT.MouseDown, event);
updateStateMask(event, button);
control.notifyListeners(SWT.MouseUp, event);
}
/**
* Emulate mouse click use given location and <code>button</code>.
*/
public void click(Point location, int button) {
click(location.x, location.y, button);
}
public Event createKeyEvent(int key, char c) {
Event event = new Event();
event.widget = control;
event.stateMask = stateMask;
event.keyCode = key;
event.character = c;
return event;
}
/**
* Emulates CTRL key down.
*/
public void ctrlDown() {
setStateMask(SWT.CTRL);
}
/**
* Emulates CTRL key up.
*/
public void ctrlUp() {
setStateMask(SWT.NONE);
}
/**
* Emulate mouse double click use given location <code>(x, y)</code> and <code>button</code>.
*/
public void doubleClick(int x, int y, int button) {
Event event = createEvent(x, y, button);
control.notifyListeners(SWT.MouseDown, event);
updateStateMask(event, button);
control.notifyListeners(SWT.MouseUp, event);
event.stateMask = stateMask;
control.notifyListeners(SWT.MouseDown, event);
control.notifyListeners(SWT.MouseDoubleClick, event);
updateStateMask(event, button);
control.notifyListeners(SWT.MouseUp, event);
}
/**
* Emulate mouse drag to given location <code>(x, y)</code>.
*/
public void dragTo(int x, int y) {
saveLastMouseLocation(x, y);
// send event
Event event = createEvent(x, y, dragButton);
updateStateMask(event, dragButton);
control.notifyListeners(SWT.MouseMove, event);
// process "async" runnables
waitEventLoop(0);
}
/**
* Ending emulate operation mouse drag.
*/
public void endDrag() {
Event event = createEvent(lastDragX, lastDragY, dragButton);
updateStateMask(event, dragButton);
control.notifyListeners(SWT.MouseUp, event);
dragButton = 0;
lastDragX = 0;
lastDragY = 0;
}
public void keyDown(int key) {
keyDown(key, (char) key);
}
public void keyDown(int key, char c) {
Event event = createKeyEvent(key, c);
control.notifyListeners(SWT.KeyDown, event);
}
public void keyUp(int key) {
keyUp(key, (char) key);
}
public void keyUp(int key, char c) {
Event event = createKeyEvent(key, c);
control.notifyListeners(SWT.KeyUp, event);
}
/**
* Emulate mouse enter to given location <code>(x, y)</code>.
*/
public void mouseEnter(int x, int y) {
Event event = createEvent(x, y, 0);
control.notifyListeners(SWT.MouseEnter, event);
}
/**
* Sends {@link SWT#MouseHover} event with given location.
*/
public void mouseHover(int x, int y) {
Event event = createEvent(x, y, 0);
control.notifyListeners(SWT.MouseHover, event);
}
/**
* Sends {@link SWT#MouseHover} event with given location.
*/
public void mouseHover(Point location) {
mouseEnter(location.x, location.y);
}
/**
* Emulate mouse move to given location <code>(x, y)</code>.
*/
public EventSender moveTo(int x, int y) {
saveLastMouseLocation(x, y);
// send event
Event event = createEvent(x, y, 0);
control.notifyListeners(SWT.MouseMove, event);
// process "async" runnables
waitEventLoop(0);
return this;
}
/**
* Posts low-level {@link SWT.MouseMove} event with coordinates relative to control.
*/
public void postMouseMove(int x, int y) {
Display display = Display.getCurrent();
Point p = display.map(control, null, x, y);
postMouseMoveAbs(p.x, p.y);
}
public void postMouseMove(int x, int y, int button) {
Display display = Display.getCurrent();
Point p = display.map(control, null, x, y);
// post event
Event event = new Event();
event.type = SWT.MouseMove;
event.x = p.x;
event.y = p.y;
event.button = button;
postEvent(event);
}
/**
* Posts low-level {@link SWT.MouseMove} event with coordinates relative to control.
*/
public void postMouseMove(Point p) {
postMouseMove(p.x, p.y);
}
public void setStateMask(int stateMask) {
this.stateMask = stateMask;
}
/**
* Scrolls vertical {@link ScrollBar} and sends {@link SWT#Selection} event.
*/
public void setVerticalBarSelection(int selection) {
ScrollBar verticalBar = ((Scrollable) control).getVerticalBar();
verticalBar.setSelection(selection);
verticalBar.notifyListeners(SWT.Selection, new Event());
}
/**
* Start emulate operation drag use given location <code>(x, y)</code> and <code>button</code>.
*/
public void startDrag(int x, int y, int button) {
saveLastMouseLocation(x, y);
dragButton = button;
//
Event event = createEvent(x, y, button);
control.notifyListeners(SWT.MouseDown, event);
}
private Event createEvent(int x, int y, int button) {
Event event = new Event();
event.widget = control;
event.stateMask = stateMask;
event.button = button;
event.x = x;
event.y = y;
return event;
}
/**
* Remembers this mouse location as last.
*/
private void saveLastMouseLocation(int x, int y) {
lastDragX = x;
lastDragY = y;
}
}