/*******************************************************************************
* Copyright (c) 2004, 2010 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.swt.tester.util;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.jubula.rc.common.driver.ClickOptions;
import org.eclipse.jubula.rc.common.driver.IEventThreadQueuer;
import org.eclipse.jubula.rc.common.driver.IRobot;
import org.eclipse.jubula.rc.common.driver.IRunnable;
import org.eclipse.jubula.rc.common.exception.StepExecutionException;
import org.eclipse.jubula.rc.common.implclasses.tree.AbstractTreeOperationContext;
import org.eclipse.jubula.rc.common.logger.AutServerLogger;
import org.eclipse.jubula.rc.common.util.SelectionUtil;
import org.eclipse.jubula.rc.common.util.Verifier;
import org.eclipse.jubula.rc.swt.utils.SwtPointUtil;
import org.eclipse.jubula.rc.swt.utils.SwtUtils;
import org.eclipse.jubula.tools.internal.constants.SwtToolkitConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
/**
* This context holds the tree, the tree model and supports access to the Robot.
* It also implements some general operations on trees.
*
* @author BREDEX GmbH
*/
public class TreeOperationContext
extends AbstractTreeOperationContext<Tree, TreeItem> {
/** The AUT Server logger. */
private static AutServerLogger log =
new AutServerLogger(TreeOperationContext.class);
/**
* Creates a new instance.
*
* @param queuer
* the queuer
* @param robot
* the Robot
* @param tree
* the tree
*/
public TreeOperationContext(IEventThreadQueuer queuer, IRobot robot,
Tree tree) {
super(queuer, robot, tree);
}
/**
* @param node
* The node.
* @param row
* The node row.
* @return The converted text
* @throws StepExecutionException
* If the method call fails.
*/
protected String convertValueToText(final TreeItem node, final int row)
throws StepExecutionException {
return getRenderedText(node);
}
/**
* {@inheritDoc}
*/
public String getRenderedText(final TreeItem node)
throws StepExecutionException {
return getQueuer().invokeAndWait("getText", new IRunnable<String>() { //$NON-NLS-1$
public String run() {
return CAPUtil.getWidgetText(node, node.getText());
}
});
}
/**
* {@inheritDoc}
*/
public boolean isVisible(final TreeItem node) {
return getQueuer().invokeAndWait("isVisible", //$NON-NLS-1$
new IRunnable<Boolean>() {
public Boolean run() {
TreeItem item = node;
boolean vis = true;
while (item != null && item.getParentItem() != null) {
vis = item.getParentItem().getExpanded();
item = item.getParentItem();
}
return vis;
}
});
}
/**
* {@inheritDoc}
*/
public Rectangle getVisibleRowBounds(Rectangle rowBounds) {
org.eclipse.swt.graphics.Rectangle r =
getQueuer().invokeAndWait("getVisibleRowBounds: " + rowBounds, //$NON-NLS-1$
new IRunnable<org.eclipse.swt.graphics.Rectangle>() {
public org.eclipse.swt.graphics.Rectangle run() {
Tree tree = getTree();
org.eclipse.swt.graphics.Rectangle visibleBounds =
tree.getClientArea();
return visibleBounds;
}
});
Rectangle visibleTreeBounds = new Rectangle(
r.x, r.y, r.width, r.height);
Rectangle visibleRowBounds = visibleTreeBounds.intersection(rowBounds);
return visibleRowBounds;
}
/**
* {@inheritDoc}
*/
public void collapseNode(final TreeItem node) {
final Tree tree = getTree();
boolean doAction = isExpanded(node);
if (doAction) {
if (log.isDebugEnabled()) {
log.debug("Collapsing node: " //$NON-NLS-1$
+ node);
}
getQueuer().invokeAndWait("collapse", new IRunnable<Void>() { //$NON-NLS-1$
public Void run() throws StepExecutionException {
Event collapseEvent = new Event();
collapseEvent.time = (int)System.currentTimeMillis();
collapseEvent.type = SWT.Collapse;
collapseEvent.widget = tree;
collapseEvent.item = node;
tree.notifyListeners(SWT.Collapse, collapseEvent);
node.setExpanded(false);
tree.update();
// Return value not used
return null;
}
});
}
}
/**
* {@inheritDoc}
*/
public void expandNode(final TreeItem node) {
final ClassLoader oldCl = Thread.currentThread()
.getContextClassLoader();
try {
final Tree tree = getTree();
boolean doAction = !isExpanded(node);
Thread.currentThread().setContextClassLoader(tree.getClass()
.getClassLoader());
getQueuer().invokeAndWait("Scroll Tree item: " + node //$NON-NLS-1$
+ " to visible", new IRunnable<Void>() { //$NON-NLS-1$
public Void run() {
tree.showItem(node);
return null;
}
});
final Rectangle nodeBounds = getNodeBounds(node);
Rectangle visibleNodeBounds = getVisibleRowBounds(nodeBounds);
org.eclipse.swt.graphics.Rectangle swtVisibleNodeBounds =
new org.eclipse.swt.graphics.Rectangle(
visibleNodeBounds.x,
visibleNodeBounds.y,
visibleNodeBounds.width,
visibleNodeBounds.height);
getRobot().move(tree, swtVisibleNodeBounds);
if (doAction) {
if (log.isDebugEnabled()) {
log.debug("Expanding node: " + node); //$NON-NLS-1$
log.debug("Node bounds : " + visibleNodeBounds); //$NON-NLS-1$
}
getQueuer().invokeAndWait("expand", new IRunnable<Void>() { //$NON-NLS-1$
public Void run() throws StepExecutionException {
Event expandEvent = new Event();
expandEvent.time = (int)System.currentTimeMillis();
expandEvent.type = SWT.Expand;
expandEvent.widget = tree;
expandEvent.item = node;
tree.notifyListeners(SWT.Expand, expandEvent);
node.setExpanded(true);
tree.update();
return null;
}
});
}
} finally {
Thread.currentThread().setContextClassLoader(oldCl);
}
}
/**
* {@inheritDoc}
*/
public TreeItem[] getRootNodes() {
return getQueuer().invokeAndWait("getRootNode", //$NON-NLS-1$
new IRunnable<TreeItem[]>() {
public TreeItem[] run() {
return getTree().getItems();
}
});
}
/**
* {@inheritDoc}
*/
public boolean isExpanded(final TreeItem node) {
// FIXME zeb: Verify that getExpanded() works like I think it does:
// It should return false if any of the parent nodes are
// collapsed.
return getQueuer().invokeAndWait("isExpanded: " + node, //$NON-NLS-1$
new IRunnable<Boolean>() {
public Boolean run() {
return node.getExpanded();
}
});
}
/**
* {@inheritDoc}
*/
public void clickNode(final TreeItem node, final ClickOptions clickOps) {
scrollNodeToVisible(node);
// Wait for all paint events resulting from the scroll to be processed
// before calculating bounds.
SwtUtils.waitForDisplayIdle(node.getDisplay());
org.eclipse.swt.graphics.Rectangle visibleRowBounds =
getQueuer().invokeAndWait(
"getVisibleNodeBounds " + node, new IRunnable<org.eclipse.swt.graphics.Rectangle>() { //$NON-NLS-1$
public org.eclipse.swt.graphics.Rectangle run() {
final Rectangle nodeBounds = getNodeBounds(node);
return SwtPointUtil.toSwtRectangle(
getVisibleRowBounds(nodeBounds));
}
});
getRobot().click(getTree(), visibleRowBounds,
clickOps.setScrollToVisible(false));
}
/**
* {@inheritDoc}
*/
public void toggleNodeCheckbox(final TreeItem node) {
scrollNodeToVisible(node);
getQueuer().invokeAndWait(
"selectNodeCheckbox", new IRunnable<Void>() { //$NON-NLS-1$
public Void run() {
Tree tree = getTree();
boolean toggledValue = !node.getChecked();
node.setChecked(toggledValue);
Event toggleEvent = new Event();
toggleEvent.type = SWT.Selection;
toggleEvent.detail = SWT.CHECK;
toggleEvent.widget = tree;
toggleEvent.item = node;
toggleEvent.button = SWT.BUTTON1;
toggleEvent.count = 1;
toggleEvent.display = node.getDisplay();
tree.notifyListeners(SWT.Selection, toggleEvent);
return null;
}
});
}
/**
* {@inheritDoc}
*/
public void verifyCheckboxSelection(final TreeItem node,
final boolean checked) {
scrollNodeToVisible(node);
Boolean checkSelected = getQueuer().invokeAndWait(
"verifyCheckboxSelection", new IRunnable<Boolean>() { //$NON-NLS-1$
public Boolean run() {
return node.getChecked();
}
});
Verifier.equals(checked, checkSelected.booleanValue());
}
/**
* {@inheritDoc}
*/
public void scrollNodeToVisible(final TreeItem node) {
getQueuer().invokeAndWait("showItem: " + node, //$NON-NLS-1$
new IRunnable<Void>() {
public Void run() {
getTree().showItem(node);
return null;
}
});
final Rectangle nodeBoundsRelativeToParent = getNodeBounds(node);
final Tree tree = getTree();
getQueuer().invokeAndWait("getNodeBoundsRelativeToParent", //$NON-NLS-1$
new IRunnable<Void>() {
public Void run() {
org.eclipse.swt.graphics.Point cellOriginRelativeToParent =
tree.getDisplay().map(
tree, tree.getParent(),
new org.eclipse.swt.graphics.Point(
nodeBoundsRelativeToParent.x,
nodeBoundsRelativeToParent.y));
nodeBoundsRelativeToParent.x =
cellOriginRelativeToParent.x;
nodeBoundsRelativeToParent.y =
cellOriginRelativeToParent.y;
return null;
}
});
Control parent = getQueuer().invokeAndWait("getParent", //$NON-NLS-1$
new IRunnable<Control>() {
public Control run() {
return tree.getParent();
}
});
getRobot().scrollToVisible(parent,
SwtPointUtil.toSwtRectangle(nodeBoundsRelativeToParent));
}
/**
* {@inheritDoc}
*/
public TreeItem getChild(final TreeItem parent, final int index) {
if (parent == null) {
TreeItem [] rootNodes = getRootNodes();
if (index < 0 || index >= rootNodes.length) {
// FIXME zeb: Handle child not found
return null;
}
return rootNodes[index];
}
return getQueuer().invokeAndWait(
"getChild: " + parent + "; With index: " + index, //$NON-NLS-1$ //$NON-NLS-2$
new IRunnable<TreeItem>() {
public TreeItem run() {
try {
return parent.getItem(index);
} catch (IllegalArgumentException iae) {
// FIXME zeb: Handle child not found
return null;
}
}
});
}
/**
* {@inheritDoc}
*/
public TreeItem getParent(final TreeItem child) {
return getQueuer().invokeAndWait("getParent: " + child, //$NON-NLS-1$
new IRunnable<TreeItem>() {
public TreeItem run() {
return child.getParentItem();
}
});
}
/**
* {@inheritDoc}
*/
public TreeItem getSelectedNode() {
return getSelectedNodes()[0];
}
/**
* {@inheritDoc}
*/
public TreeItem[] getSelectedNodes() {
return getQueuer().invokeAndWait("getSelectedNodes", //$NON-NLS-1$
new IRunnable<TreeItem[]>() {
public TreeItem[] run() {
TreeItem[] selectedItems = getTree().getSelection();
SelectionUtil.validateSelection(selectedItems);
return selectedItems;
}
});
}
/**
* {@inheritDoc}
*/
public TreeItem[] getChildren(final TreeItem parent) {
if (parent == null) {
return getRootNodes();
}
return getQueuer().invokeAndWait("getChildren: " + parent, //$NON-NLS-1$
new IRunnable<TreeItem[]>() {
public TreeItem[] run() {
return parent.getItems();
}
});
}
/**
* {@inheritDoc}
*/
public int getNumberOfChildren(final TreeItem parent) {
if (parent == null) {
return getRootNodes().length;
}
return getQueuer().invokeAndWait("getChildren: " + parent, //$NON-NLS-1$
new IRunnable<Integer>() {
public Integer run() {
return parent.getItemCount();
}
});
}
/**
* {@inheritDoc}
*/
public Collection<String> getNodeTextList(final TreeItem node) {
final Collection<String> res = new ArrayList<String>();
getQueuer().invokeAndWait("getNodeText: " + node, //$NON-NLS-1$
new IRunnable<Void>() {
public Void run() {
int colCount = getTree().getColumnCount();
for (int i = 0; i < colCount; i++) {
String textAtColumn = CAPUtil.getWidgetText(node,
SwtToolkitConstants.WIDGET_TEXT_KEY_PREFIX
+ i, node.getText(i));
if (textAtColumn != null) {
res.add(textAtColumn);
}
}
String text = CAPUtil.getWidgetText(node, node.getText());
if (text != null) {
res.add(text);
}
return null;
}
});
return res;
}
/**
* {@inheritDoc}
*/
public Rectangle getNodeBounds(final TreeItem node) {
org.eclipse.swt.graphics.Rectangle r =
getQueuer().invokeAndWait("getNodeBounds: " + node, //$NON-NLS-1$
new IRunnable<org.eclipse.swt.graphics.Rectangle>() {
public org.eclipse.swt.graphics.Rectangle run() {
Tree tree = getTree();
org.eclipse.swt.graphics.Rectangle bounds =
SwtUtils.getRelativeWidgetBounds(
node, tree);
return bounds;
}
});
Rectangle nodeBounds = new Rectangle(r.x, r.y, r.width, r.height);
return nodeBounds;
}
/**
* {@inheritDoc}
*/
public int getIndexOfChild(final TreeItem parent, final TreeItem child) {
if (parent != null) {
return getQueuer().invokeAndWait("getIndexOfChild", //$NON-NLS-1$
new IRunnable<Integer>() {
public Integer run() throws StepExecutionException {
return parent.indexOf(child);
}
});
}
TreeItem [] rootNodes = getRootNodes();
for (int i = 0; i < rootNodes.length; i++) {
if (rootNodes[i] == child) {
return i;
}
}
return -1;
}
/**
* {@inheritDoc}
*/
public boolean isLeaf(TreeItem node) {
if (getNumberOfChildren(node) == 0) {
return true;
}
return false;
}
}