/*******************************************************************************
* 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.util.List;
import java.util.concurrent.Callable;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.Toggle;
import javafx.scene.layout.VBox;
import org.eclipse.jubula.rc.common.driver.ClickOptions;
import org.eclipse.jubula.rc.common.driver.IRobot;
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.IMenuComponent;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IMenuItemComponent;
import org.eclipse.jubula.rc.javafx.driver.EventThreadQueuerJavaFXImpl;
import org.eclipse.jubula.tools.internal.constants.TimeoutConstants;
import org.eclipse.jubula.tools.internal.objects.event.EventFactory;
import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent;
/**
* Adapter for a MenuItem. The actual values are gained via the Node behind a
* MenuItem.
*
* @author BREDEX GmbH
* @param <M>
* @created 10.2.2014
*/
public class MenuItemAdapter<M extends MenuItem>
extends AbstractMenuAdapter<M>
implements IMenuItemComponent {
/**
* Creates an adapter for a MenuItem.
*
* @param objectToAdapt
* the object which needs to be adapted
*/
public MenuItemAdapter(M objectToAdapt) {
super(objectToAdapt);
}
@Override
public boolean isSelected() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("isSelected", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
MenuItem item = getRealComponent();
if (item instanceof Toggle) {
return ((Toggle) item).isSelected();
} else if (item instanceof CheckMenuItem) {
return ((CheckMenuItem) item).isSelected();
}
return false;
}
});
}
@Override
public String getText() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("getText", //$NON-NLS-1$
new Callable<String>() {
@Override
public String call() throws Exception {
return getRealComponent().getText();
}
});
}
@Override
public boolean isEnabled() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("isEnabled", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return !getRealComponent().isDisable();
}
});
}
@Override
public boolean isExisting() {
return getRealComponent() != null;
}
@Override
public boolean isShowing() {
return EventThreadQueuerJavaFXImpl.invokeAndWait("isShowing", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return getRealComponent().isVisible();
}
});
}
@Override
public boolean hasSubMenu() {
final MenuItem item = getRealComponent();
return EventThreadQueuerJavaFXImpl.invokeAndWait("hasSubMenu", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
if (item instanceof Menu) {
return ((Menu) item).getItems().size() > 0;
}
return false;
}
});
}
@Override
public boolean isSeparator() {
final MenuItem item = getRealComponent();
return EventThreadQueuerJavaFXImpl.invokeAndWait("hasSubMenu", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return item instanceof SeparatorMenuItem;
}
});
}
@Override
public void selectMenuItem() {
clickMenuItem();
}
@Override
public IMenuComponent openSubMenu() {
final MenuItem item = getRealComponent();
if (!(item instanceof Menu)) {
throw new StepExecutionException("unexpected item found", //$NON-NLS-1$
EventFactory.createActionError(TestErrorEvent.NOT_FOUND));
}
clickMenuItem();
if (!waitforSubmenuToOpen((Menu) getRealComponent())) {
throw new StepExecutionException("submenu could not be opened", //$NON-NLS-1$
EventFactory.createActionError(TestErrorEvent.
POPUP_NOT_FOUND));
}
return new MenuAdapter((Menu) getRealComponent());
}
/**
* This method realizes the waiting for the submenu to open
*
* @param menu
* the menu on whose submenu should be waited for it to open
* @return true if the submenu opened successfully, false if not
*/
protected boolean waitforSubmenuToOpen(final Menu menu) {
final EventLock eventLock = new EventLock();
final EventHandler<Event> shownHandler =
new EventHandler<Event>() {
@Override
public void handle(Event event) {
synchronized (eventLock) {
eventLock.notifyAll();
}
}
};
EventThreadQueuerJavaFXImpl
.invokeAndWait("addShownListener", new Callable<Void>() { //$NON-NLS-1$
@Override
public Void call() throws Exception {
menu.addEventHandler(Menu.ON_SHOWN, shownHandler);
return null;
}
});
boolean result = false;
try {
if (!menu.isShowing()) {
synchronized (eventLock) {
eventLock.wait(TimeoutConstants.
SERVER_TIMEOUT_WAIT_FOR_POPUP);
}
}
} catch (InterruptedException e) {
// ignore
} finally {
result = EventThreadQueuerJavaFXImpl.
invokeAndWait("openSubMenu", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
menu.removeEventHandler(Menu.ON_SHOWN,
shownHandler);
return menu.isShowing();
}
});
}
return result;
}
/**
* Clicks on a menu item
*/
protected void clickMenuItem() {
final IRobot robot = getRobot();
final MenuItem item = getRealComponent();
Node[] nodes = EventThreadQueuerJavaFXImpl.invokeAndWait(
"clickMenuItem", new Callable<Node[]>() { //$NON-NLS-1$
@Override
public Node[] call() throws Exception {
if (item.isDisable()) {
throw new StepExecutionException(
"menu item not enabled", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.
MENU_ITEM_NOT_ENABLED));
}
Parent p = (Parent) item.getParentPopup().getSkin()
.getNode();
List<Node> ctxtMCont = p.getChildrenUnmodifiable();
VBox mBox = null;
for (Node node : ctxtMCont) {
if (node instanceof VBox) {
mBox = (VBox) node;
break;
}
}
if (mBox == null) {
throw new StepExecutionException("No Menu was found", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.
POPUP_NOT_FOUND));
}
int itemIndex = item.getParentPopup().getItems().
indexOf(item);
/* item.getParentMenu().getItems()
.indexOf(item);*/
Node itemNode = mBox.getChildrenUnmodifiable().get(
itemIndex);
Node itemOwner = item.getParentPopup().getOwnerNode();
return new Node[] { itemNode, itemOwner };
}
});
// if the owner of this Menu is an instance of MenuButton, it could be
// part of a MenuBar. Therefore, we have to move vertical at first.
if (nodes[1] != null && nodes[1] instanceof MenuButton) {
robot.click(
nodes[0],
null,
ClickOptions.create()
.setFirstHorizontal(false));
} else {
robot.click(
nodes[0],
null);
}
}
}