/****************************************************************************
* Copyright 2008-2011 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.
*
* Initial Contributors:
* Håkan Råberg
* Manish Chakravarty
* Pavan K S
***************************************************************************/
package com.thoughtworks.krypton.driver.web.user.gtk;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.internal.gtk.GdkEventButton;
import org.eclipse.swt.internal.gtk.GdkEventKey;
import org.eclipse.swt.internal.gtk.GdkEventMotion;
import org.eclipse.swt.internal.gtk.OS;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import com.thoughtworks.krypton.driver.web.user.KeyTranslator;
import com.thoughtworks.krypton.driver.web.user.User;
public class GtkUser implements User {
private static final int GDK_MOTION_NOTIFY = 3;
private final Shell window;
private KeyTranslator translator = new GtkKeyTranslator();
private boolean shift;
public GtkUser(Shell window) {
this.window = window;
}
public void click(int x, int y) {
mouseMoveEvent(x, y, 0);
mouseEvent(OS.GDK_BUTTON_PRESS, x, y, 1);
mouseEvent(OS.GDK_BUTTON_RELEASE, x, y, 1);
}
public void rightClick(int x, int y) {
mouseMoveEvent(x, y, 0);
mouseEvent(OS.GDK_BUTTON_PRESS, x, y, 2);
mouseEvent(OS.GDK_BUTTON_RELEASE, x, y, 2);
}
public void doubleClick(int x, int y) {
mouseMoveEvent(x, y, 0);
mouseEvent(OS.GDK_BUTTON_PRESS, x, y, 1);
mouseEvent(OS.GDK_BUTTON_RELEASE, x, y, 1);
mouseEvent(OS.GDK_2BUTTON_PRESS, x, y, 1);
mouseEvent(OS.GDK_BUTTON_RELEASE, x, y, 1);
}
public void dragAndDrop(int startX, int startY, int endX, int endY) {
mouseMoveEvent(startX, startY, 0);
mouseEvent(OS.GDK_BUTTON_PRESS, startX, startY, 1);
mouseMoveEvent(endX, endY, OS.GDK_BUTTON1_MASK);
mouseEvent(OS.GDK_BUTTON_RELEASE, endX, endY, 1);
}
public void type(String string) {
try {
window.setRedraw(false);
for (char c : string.toCharArray()) {
key(shift ? Character.toUpperCase(c) : c);
}
} finally {
window.setRedraw(true);
}
}
public void key(int keyCode) {
if (translator.shouldTranslateKey(keyCode)) {
int translatedKey = translator.translate(keyCode).keyCode;
keyEvent(OS.GDK_KEY_PRESS, translatedKey, false);
keyEvent(OS.GDK_KEY_RELEASE, translatedKey, false);
} else {
keyEvent(OS.GDK_KEY_PRESS, keyCode, true);
keyEvent(OS.GDK_KEY_RELEASE, keyCode, true);
}
}
public void key(int keyCode, int modifiers) {
throw new UnsupportedOperationException();
}
public boolean isShiftDown() {
return shift;
}
public void shiftDown() {
shift = true;
}
public void shiftUp() {
shift = false;
}
private void mouseEvent(int type, final int x, final int y, int button) {
int event = OS.malloc(GdkEventButton.sizeof);
GdkEventButton gdkEventButton = new GdkEventButton();
int handle = getHandle();
Point point = window.toDisplay(x, y);
gdkEventButton.type = type;
gdkEventButton.x = x;
gdkEventButton.y = y;
gdkEventButton.x_root = point.x;
gdkEventButton.y_root = point.y;
gdkEventButton.window = handle;
gdkEventButton.button = button;
gdkEventButton.time = (int) System.currentTimeMillis();
gdkEventButton.state = type == OS.GDK_BUTTON_RELEASE ? (button == 1 ?
OS.GDK_BUTTON1_MASK : OS.GDK_BUTTON2_MASK)
: 0;
OS.memmove(event, gdkEventButton, GdkEventButton.sizeof);
OS.gtk_main_do_event(event);
OS.free(event);
}
private void mouseMoveEvent(int x, int y, int state) {
int event = OS.malloc(GdkEventMotion.sizeof);
Point point = window.toDisplay(x, y);
OS.memmove(event, new int[] { GDK_MOTION_NOTIFY,
getHandle(), 0,
(int) System.currentTimeMillis(), 0, doubleToLongHiBits(x), doubleToLongLowBits(x),
doubleToLongHiBits(y), doubleToLongLowBits(y), 0, state, 0, 0, doubleToLongHiBits(point.x), doubleToLongLowBits(point.x), doubleToLongHiBits(point.y), doubleToLongLowBits(point.y) },
GdkEventMotion.sizeof);
OS.gtk_main_do_event(event);
pumpEvents();
OS.free(event);
}
private int doubleToLongHiBits(int value) {
return (int) (Double.doubleToLongBits((double) value) >> 32);
}
private int doubleToLongLowBits(int value) {
return (int) (Double.doubleToLongBits((double) value) & 0xffffffffl);
}
private void keyEvent(int type, int c, boolean unicode) {
int event = OS.malloc(GdkEventKey.sizeof);
OS.memmove(event, new int[] { type,
getHandle(), 0,
(int) System.currentTimeMillis(), 0,
unicode ? OS.gdk_unicode_to_keyval(c) : c, 0, 0, 0 },
GdkEventKey.sizeof);
OS.gtk_main_do_event(event);
pumpEvents();
OS.free(event);
}
private void pumpEvents() {
while (!window.isDisposed() && window.getDisplay().readAndDispatch())
;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
private int getHandle() {
for (Control c : window.getChildren()) {
if (c instanceof Browser) {
return findTargetWindow(OS.GTK_WIDGET_WINDOW(c.handle));
}
}
return -1;
}
private int findTargetWindow(int window) {
int windows = OS.gdk_window_get_children(window);
int length = OS.g_list_length(windows);
if (length == 0) {
return window;
}
return findTargetWindow(OS.g_list_nth_data(windows, 0));
}
}