/******************************************************************************* * Copyright (c) 2008, 2015 Ketan Padegaonkar and others. * 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: * Ketan Padegaonkar - initial API and implementation * Kristine Jetzke - Bug 420121 * Stephane Bouchet (Intel Corporation) - Bug 451547 * Patrick Tasse - Improve SWTBot menu API and implementation (Bug 479091) *******************************************************************************/ package org.eclipse.swtbot.swt.finder.widgets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swtbot.swt.finder.ReferenceBy; import org.eclipse.swtbot.swt.finder.SWTBot; import org.eclipse.swtbot.swt.finder.SWTBotWidget; import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException; import org.eclipse.swtbot.swt.finder.results.ArrayResult; import org.eclipse.swtbot.swt.finder.results.BoolResult; import org.eclipse.swtbot.swt.finder.results.IntResult; import org.eclipse.swtbot.swt.finder.results.ListResult; import org.eclipse.swtbot.swt.finder.results.Result; import org.eclipse.swtbot.swt.finder.results.StringResult; import org.eclipse.swtbot.swt.finder.results.VoidResult; import org.eclipse.swtbot.swt.finder.results.WidgetResult; import org.eclipse.swtbot.swt.finder.utils.MessageFormat; import org.eclipse.swtbot.swt.finder.utils.StringUtils; import org.eclipse.swtbot.swt.finder.utils.TableCollection; import org.eclipse.swtbot.swt.finder.utils.TableRow; import org.eclipse.swtbot.swt.finder.utils.internal.Assert; import org.eclipse.swtbot.swt.finder.waits.DefaultCondition; import org.hamcrest.SelfDescribing; /** * @author Ketan Padegaonkar <KetanPadegaonkar [at] gmail [dot] com> * @author Joshua Gosse <jlgosse [at] ca [dot] ibm [dot] com> * @version $Id$ */ @SWTBotWidget(clasz = Tree.class, preferredName = "tree", referenceBy = { ReferenceBy.LABEL }) public class SWTBotTree extends AbstractSWTBot<Tree> { /** * Constructs an instance of this object with the given tree. * * @param tree the widget. * @param description the description of the widget, this will be reported by {@link #toString()} * @throws WidgetNotFoundException if the widget is <code>null</code> or widget has been disposed. */ public SWTBotTree(Tree tree, SelfDescribing description) throws WidgetNotFoundException { super(tree, description); } /** * Constructs an instance of this object with the given tree. * * @param tree the widget. * @throws WidgetNotFoundException if the widget is <code>null</code> or widget has been disposed. */ public SWTBotTree(Tree tree) throws WidgetNotFoundException { this(tree, null); } /** * Gets the number of rows in the tree. * * @return the number of rows in the tree */ public int rowCount() { return syncExec(new IntResult() { public Integer run() { return widget.getItems().length; } }); } /** * Gets the column count of this tree. * * @return the number of columns in the tree */ public int columnCount() { return syncExec(new IntResult() { public Integer run() { return widget.getColumnCount(); } }); } /** * Gets the columns of this tree. * <p> * To interact with one of the columns, use {@link #header(String)}. * * @return the list of columns in the tree. */ public List<String> columns() { final int columnCount = columnCount(); return syncExec(new ListResult<String>() { public List<String> run() { String columns[] = new String[columnCount]; for (int i = 0; i < columnCount; i++) columns[i] = widget.getColumn(i).getText(); return new ArrayList<String>(Arrays.asList(columns)); } }); } /** * Gets the column matching the given label. * * @param label the header text. * @return the header of the tree. * @throws WidgetNotFoundException if the header is not found. * @since 2.1.2 */ public SWTBotTreeColumn header(final String label) throws WidgetNotFoundException { TreeColumn column = syncExec(new Result<TreeColumn>() { public TreeColumn run() { TreeColumn[] columns = widget.getColumns(); for (TreeColumn column : columns) { if (column.getText().equals(label)) return column; } return null; } }); return new SWTBotTreeColumn(column, widget); } /** * Gets the cell data for the given row/column index. * * @param row the row index. * @param column the column index. * @return the cell at the location specified by the row and column */ public String cell(final int row, final int column) { int rowCount = rowCount(); int columnCount = columnCount(); Assert.isLegal(row < rowCount, "The row number (" + row + ") is more than the number of rows(" + rowCount + ") in the tree."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Assert.isLegal(column < columnCount, "The column number (" + column + ") is more than the number of column(" + columnCount //$NON-NLS-1$ //$NON-NLS-2$ + ") in the tree."); //$NON-NLS-1$ return syncExec(new StringResult() { public String run() { TreeItem item = widget.getItem(row); return item.getText(column); } }); } /** * Gets the cell data for the given row/column information. * * @param row the row index. * @param columnName the column name. * @return the cell in the tree at the specified row and column header. */ public String cell(int row, String columnName) { List<String> columns = columns(); Assert.isLegal(columns.contains(columnName), "The column `" + columnName + "' is not found."); //$NON-NLS-1$ //$NON-NLS-2$ int columnIndex = columns.indexOf(columnName); if (columnIndex == -1) return ""; //$NON-NLS-1$ return cell(row, columnIndex); } /** * Gets the current selection count. * * @return the number of selected items. */ public int selectionCount() { return syncExec(new IntResult() { public Integer run() { return widget.getSelectionCount(); } }); } /** * Gets the table collection representing the selection. * * @return the selection in the tree */ public TableCollection selection() { final int columnCount = columnCount(); return syncExec(new Result<TableCollection>() { public TableCollection run() { final TableCollection selection = new TableCollection(); TreeItem[] items = widget.getSelection(); for (TreeItem item : items) { TableRow tableRow = new TableRow(); if (columnCount == 0) tableRow.add(item.getText()); else for (int j = 0; j < columnCount; j++) tableRow.add(item.getText(j)); selection.add(tableRow); } return selection; } }); } /** * Selects the items matching the array list. * * @param items the items to select. * @return this same instance. */ public SWTBotTree select(final String... items) { waitForEnabled(); setFocus(); final List<TreeItem> selection = new ArrayList<TreeItem>(); for (String item : items) { SWTBotTreeItem si = getTreeItem(item); selection.add(si.widget); } asyncExec(new VoidResult() { public void run() { if (!hasStyle(widget, SWT.MULTI) && items.length > 1) log.warn("Tree does not support SWT.MULTI, cannot make multiple selections"); //$NON-NLS-1$ widget.setSelection(selection.toArray(new TreeItem[selection.size()])); } }); notifySelect(); return this; } /** * Selects the items in the array. Useful for cases where you're selecting items whose names are not unique, or * items you've exposed one at a time while traversing the tree. * * @param items the items to select. * @return this same instance. */ public SWTBotTree select(final SWTBotTreeItem... items) { assertEnabled(); setFocus(); asyncExec(new VoidResult() { public void run() { List<TreeItem> selection = new ArrayList<TreeItem>(); for (SWTBotTreeItem treeItem : items) { selection.add(treeItem.widget); } if (!hasStyle(widget, SWT.MULTI) && items.length > 1) log.warn("Tree does not support SWT.MULTI, cannot make multiple selections"); //$NON-NLS-1$ widget.setSelection(selection.toArray(new TreeItem[] {})); } }); notifySelect(); return this; } /** * Unselects the selection in the tree. * * @return this same instance. */ public SWTBotTree unselect() { waitForEnabled(); asyncExec(new VoidResult() { public void run() { log.debug(MessageFormat.format("Unselecting all in {0}", widget)); //$NON-NLS-1$ widget.deselectAll(); } }); notifySelect(); return this; } /** * Select the indexes provided. * * @param indices the indices to select. * @return this same instance. */ public SWTBotTree select(final int... indices) { waitForEnabled(); setFocus(); asyncExec(new VoidResult() { public void run() { log.debug(MessageFormat.format("Selecting rows [{0}] in tree{1}", StringUtils.join(indices, ", "), this)); //$NON-NLS-1$ //$NON-NLS-2$ if (!hasStyle(widget, SWT.MULTI) && indices.length > 1) log.warn("Tree does not support SWT.MULTI, cannot make multiple selections"); //$NON-NLS-1$ TreeItem items[] = new TreeItem[indices.length]; for (int i = 0; i < indices.length; i++) items[i] = widget.getItem(indices[i]); widget.setSelection(items); } }); notifySelect(); return this; } /** * Notifies the tree widget about selection changes */ protected void notifySelect() { notify(SWT.MouseEnter); notify(SWT.MouseMove); notify(SWT.Activate); notify(SWT.FocusIn); notify(SWT.MouseDown); notify(SWT.Selection); notify(SWT.MouseUp); notify(SWT.MouseHover); notify(SWT.MouseMove); notify(SWT.MouseExit); notify(SWT.Deactivate); notify(SWT.FocusOut); } /** * Attempts to expand all nodes along the path specified by the node array parameter. * * @param nodes node path to expand * @return the last Tree item that was expanded. * @throws WidgetNotFoundException if any of the nodes on the path do not exist */ public SWTBotTreeItem expandNode(String... nodes) throws WidgetNotFoundException { // TODO: this method should be made iterative instead of recursive Assert.isNotEmpty((Object[]) nodes); log.debug(MessageFormat.format("Expanding nodes {0} in tree {1}", StringUtils.join(nodes, ">"), this)); waitForEnabled(); SWTBotTreeItem item = getTreeItem(nodes[0]).expand(); String[] tail = new String[nodes.length - 1]; System.arraycopy(nodes, 1, tail, 0, nodes.length - 1); if (tail.length > 0) { item = item.expandNode(tail); } return item; } /** * Collapses the node matching the node information. * * @param nodeText the text on the node. * @return the Tree item that was expanded. * @throws WidgetNotFoundException if the node is not found. */ public SWTBotTreeItem collapseNode(final String nodeText) throws WidgetNotFoundException { waitForEnabled(); return getTreeItem(nodeText).collapse(); } /** * Gets the visible row count. * * @return the number of visible rows */ public int visibleRowCount() { return syncExec(new IntResult() { public Integer run() { TreeItem[] items = widget.getItems(); return getVisibleChildrenCount(items); } private int getVisibleChildrenCount(TreeItem item) { if (item.getExpanded()) return getVisibleChildrenCount(item.getItems()); return 0; } private int getVisibleChildrenCount(TreeItem[] items) { int count = 0; for (TreeItem item : items) { int j = getVisibleChildrenCount(item) + 1; count += j; } return count; } }); } /** * Expands the nodes as if the plus sign was clicked. * * @param nodeText the node to be expanded. * @param recursive if the expansion should be recursive. * @return the tree item that was expanded. * @throws WidgetNotFoundException if the node is not found. */ public SWTBotTreeItem expandNode(final String nodeText, final boolean recursive) throws WidgetNotFoundException { waitForEnabled(); return syncExec(new Result<SWTBotTreeItem>() { public SWTBotTreeItem run() { SWTBotTreeItem item; try { item = getTreeItem(nodeText); expandNode(item); } catch (WidgetNotFoundException e) { throw new RuntimeException(e); } return item; } private void expandNode(SWTBotTreeItem item) throws WidgetNotFoundException { item.expand(); if (recursive) expandTreeItem(item.widget); } private void expandTreeItem(TreeItem node) throws WidgetNotFoundException { TreeItem[] items = node.getItems(); for (TreeItem item : items) { expandNode(new SWTBotTreeItem(item)); } } }); } /** * Gets the tree item matching the given name. * * @param nodeText the text on the node. * @return the tree item with the specified text. * @throws WidgetNotFoundException if the node was not found. */ public SWTBotTreeItem getTreeItem(final String nodeText) throws WidgetNotFoundException { try { new SWTBot().waitUntil(new DefaultCondition() { public String getFailureMessage() { return "Could not find node with text " + nodeText; //$NON-NLS-1$ } public boolean test() throws Exception { return getItem(nodeText) != null; } }); } catch (TimeoutException e) { throw new WidgetNotFoundException("Timed out waiting for tree item " + nodeText, e); //$NON-NLS-1$ } return new SWTBotTreeItem(getItem(nodeText)); } /** * Gets the item matching the given name. * * @param nodeText the text on the node. * @return the tree item with the specified text. */ private TreeItem getItem(final String nodeText) { return syncExec(new WidgetResult<TreeItem>() { public TreeItem run() { TreeItem[] items = widget.getItems(); for (TreeItem item : items) { if (item.getText().equals(nodeText)) return item; } return null; } }); } /** * Gets all the items in the tree. * * @return the list of all tree items in the tree, or an empty list if there are none. * @since 1.0 */ public SWTBotTreeItem[] getAllItems() { return syncExec(new ArrayResult<SWTBotTreeItem>() { public SWTBotTreeItem[] run() { TreeItem[] items = widget.getItems(); SWTBotTreeItem[] result = new SWTBotTreeItem[items.length]; for (int i = 0; i < items.length; i++) try { result[i] = new SWTBotTreeItem(items[i]); } catch (WidgetNotFoundException e) { return new SWTBotTreeItem[0]; } return result; } }); } /** * Gets if this tree has items within it. * * @return <code>true</code> if the tree has any items, <code>false</code> otherwise. * @since 1.0 */ public boolean hasItems() { return syncExec(new BoolResult() { public Boolean run() { return widget.getItemCount() > 0; } }); } }