/*******************************************************************************
* Copyright (c) 2014 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.javafx.tester.adapter;
import java.awt.event.KeyEvent;
import java.util.Iterator;
import java.util.concurrent.Callable;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import org.eclipse.jubula.rc.common.exception.StepExecutionException;
import org.eclipse.jubula.rc.common.listener.EventLock;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IComboComponent;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IMenuItemComponent;
import org.eclipse.jubula.rc.common.util.IndexConverter;
import org.eclipse.jubula.rc.javafx.driver.EventThreadQueuerJavaFXImpl;
import org.eclipse.jubula.rc.javafx.tester.MenuTester;
import org.eclipse.jubula.rc.javafx.tester.util.WindowsUtil;
import org.eclipse.jubula.tools.internal.constants.TimeoutConstants;
/**
* Adapter for a ChoiceBox. This Control is like a ComboBox, but is not
* editable. To display the Items the ComboBox uses a ContextMenu, unlike the
* ComboBox which uses a ListView.
*
* @author BREDEX GmbH
* @created 24.3.2014
*/
public class ChoiceBoxAdapter extends JavaFXComponentAdapter<ChoiceBox<?>>
implements IComboComponent {
/**
* Creates an object with the adapted ChoiceBox.
*
* @param objectToAdapt
* this must be an object of the Type <code>ChoiceBox</code>
*/
public ChoiceBoxAdapter(ChoiceBox<?> objectToAdapt) {
super(objectToAdapt);
}
@Override
public String getText() {
return EventThreadQueuerJavaFXImpl.invokeAndWait(
"getText", new Callable<String>() { //$NON-NLS-1$
@Override
public String call() throws Exception {
ObservableList<Node> children = getRealComponent()
.getChildrenUnmodifiable();
Label text = null;
for (Node node : children) {
if (node instanceof Label) {
text = (Label) node;
}
}
if (text != null) {
return text.getText();
}
return null;
}
});
}
@Override
public boolean isEditable() {
// The ChoiceBox is not editable
return false;
}
@Override
public void selectAll() {
// Not possible, because the ChoiceBox is not editable and therefore
// there is
// no Text which could be select.
StepExecutionException.throwUnsupportedAction();
}
@Override
public int getSelectedIndex() {
return EventThreadQueuerJavaFXImpl.invokeAndWait(
"getSelectedIndex", new Callable<Integer>() { //$NON-NLS-1$
@Override
public Integer call() throws Exception {
return getRealComponent().getSelectionModel()
.getSelectedIndex();
}
});
}
@Override
public void select(int index) {
MenuTester tester = openMenu();
tester.selectMenuItemByIndexpath(Integer.toString(
IndexConverter.toUserIndex(index)));
}
@Override
public void input(String text, boolean replace) {
// Not possible, because the ChoiceBox is not editable.
StepExecutionException.throwUnsupportedAction();
}
@Override
public String[] getValues() {
MenuTester tester = openMenu();
IMenuItemComponent[] items = tester.getMenuAdapter().getItems();
String[] result = new String[items.length];
for (int i = 0; i < items.length; i++) {
result[i] = items[i].getText();
}
closeMenu((ContextMenu) tester.getComponent().getRealComponent());
return result;
}
/**
* Opens the list of choices for the ChoiceBox.
*
* @return A MenuTester object to select Items. This is Possible because the
* ChoiceBox uses a ContextMenu.
*/
private MenuTester openMenu() {
getRobot().click(getRealComponent(), null);
return EventThreadQueuerJavaFXImpl.invokeAndWait(
"select", new Callable<MenuTester>() { //$NON-NLS-1$
@Override
public MenuTester call() throws Exception {
MenuTester menuTester = null;
Iterator<Window> iter = WindowsUtil.getWindowIterator();
long timeout = TimeoutConstants.
SERVER_TIMEOUT_WAIT_FOR_POPUP;
long done = System.currentTimeMillis() + timeout;
long now;
do {
if (!iter.hasNext()) {
iter = WindowsUtil.getWindowIterator();
}
Window w = iter.next();
if (w instanceof ContextMenu
&& ((ContextMenu) w).getOwnerNode().equals(
getRealComponent())) {
menuTester = new MenuTester();
menuTester.setComponent(w);
break;
}
now = System.currentTimeMillis();
timeout = done - now;
} while (timeout > 0);
return menuTester;
}
});
}
/**
* Closes the Menu of a ChoiceBox and waits for it to be closed
*
* @param m
* the menu
* @return true if menu was closed successfully, false if not;
*/
private boolean closeMenu(final ContextMenu m) {
final EventLock eventLock = new EventLock();
final EventHandler<Event> shownHandler = new EventHandler<Event>() {
@Override
public void handle(Event event) {
synchronized (eventLock) {
eventLock.notifyAll();
}
}
};
EventThreadQueuerJavaFXImpl.invokeAndWait(
"addCloseHandler", new Callable<Void>() { //$NON-NLS-1$
@Override
public Void call() throws Exception {
m.addEventHandler(
WindowEvent.WINDOW_HIDDEN, shownHandler);
return null;
}
});
boolean successful = false;
getRobot().keyType(null, KeyEvent.VK_ESCAPE);
try {
if (m.isShowing()) {
synchronized (eventLock) {
eventLock
.wait(TimeoutConstants.
SERVER_TIMEOUT_WAIT_FOR_POPUP);
}
}
} catch (InterruptedException e) {
// ignore
} finally {
successful = EventThreadQueuerJavaFXImpl.invokeAndWait("closeMenu", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
m.removeEventHandler(
WindowEvent.WINDOW_HIDDEN, shownHandler);
return !m.isShowing();
}
});
}
return successful;
}
}