blob: 6263683c5053a363a70bd3ade56a5896bf3e95e7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 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
* Cédric Chabanois - http://swtbot.org/bugzilla/show_bug.cgi?id=16
* Hans Schwaebli - http://swtbot.org/bugzilla/show_bug.cgi?id=100
* http://www.inria.fr/ - http://swtbot.org/bugzilla/show_bug.cgi?id=114
* Hans Schwaebli - http://swtbot.org/bugzilla/show_bug.cgi?id=122
* Kristine Jetzke - Bug 259908
* Aparna Argade - Bug 508710
*******************************************************************************/
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.graphics.Rectangle;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
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.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.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>
* @version $Id$
*/
@SWTBotWidget(clasz = Table.class, preferredName = "table", referenceBy = { ReferenceBy.LABEL })
public class SWTBotTable extends AbstractSWTBot<Table> {
/** The last selected item */
private TableItem lastSelectionItem;
/**
* Constructs a new instance of this object.
*
* @param table the widget.
* @throws WidgetNotFoundException if the widget is <code>null</code> or widget has been disposed.
*/
public SWTBotTable(Table table) throws WidgetNotFoundException {
this(table, null);
}
/**
* Constructs a new instance of this object.
*
* @param table 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 SWTBotTable(Table table, SelfDescribing description) throws WidgetNotFoundException {
super(table, description);
}
/**
* Gets the row count.
*
* @return the number of rows in the table
*/
public int rowCount() {
return syncExec(new IntResult() {
public Integer run() {
return widget.getItemCount();
}
});
}
/**
* Gets the column count.
*
* @return the number of columns in the table
*/
public int columnCount() {
return syncExec(new IntResult() {
public Integer run() {
return widget.getColumnCount();
}
});
}
/**
* Gets the columns in this table.
*
* @return the list of columns in the table.
*/
public List<String> columns() {
return syncExec(new ListResult<String>() {
public List<String> run() {
ArrayList<String> result = new ArrayList<String>();
TableColumn[] columns = widget.getColumns();
for (int i : widget.getColumnOrder()) {
result.add(columns[i].getText());
}
return result;
}
});
}
/**
* @param column the text on the column.
* @return the index of the specified column.
* @since 1.3
*/
public int indexOfColumn(String column) {
return columns().indexOf(column);
}
/**
* Gets the column matching the given label.
*
* @param label the header text.
* @return the header of the table.
* @throws WidgetNotFoundException if the header is not found.
*/
public SWTBotTableColumn header(final String label) throws WidgetNotFoundException {
TableColumn column = syncExec(new Result<TableColumn>() {
public TableColumn run() {
TableColumn[] columns = widget.getColumns();
for (TableColumn column : columns) {
if (column.getText().equals(label))
return column;
}
return null;
}
});
return new SWTBotTableColumn(column, widget);
}
/**
* Gets the cell data for the given row/column index.
*
* @param row the row in the table.
* @param column the column in the table.
* @return the cell at the location specified by the row and column
*/
public String cell(final int row, final int column) {
assertIsLegalCell(row, column);
return syncExec(new StringResult() {
public String run() {
TableItem item = widget.getItem(row);
return item.getText(column);
}
});
}
/**
* Gets the cell data for the given row and column label.
*
* @param row the row in the table
* @param columnName the column title.
* @return the cell in the table at the specified row and columnheader
*/
public String cell(int row, String columnName) {
Assert.isLegal(columns().contains(columnName), "The column `" + columnName + "' is not found."); //$NON-NLS-1$ //$NON-NLS-2$
List<String> columns = columns();
int columnIndex = columns.indexOf(columnName);
if (columnIndex == -1)
return ""; //$NON-NLS-1$
return cell(row, columnIndex);
}
/**
* Gets the selected item count.
*
* @return the number of selected items.
*/
public int selectionCount() {
return syncExec(new IntResult() {
public Integer run() {
return widget.getSelectionCount();
}
});
}
/**
* Gets the selected items.
*
* @return the selection in the table
*/
public TableCollection selection() {
final int columnCount = columnCount();
return syncExec(new Result<TableCollection>() {
public TableCollection run() {
final TableCollection selection = new TableCollection();
TableItem[] items = widget.getSelection();
for (TableItem item : items) {
TableRow tableRow = new TableRow();
for (int j = 0; j < columnCount; j++)
tableRow.add(item.getText(j));
selection.add(tableRow);
}
return selection;
}
});
}
private void assertIsLegalRowIndex(final int rowIndex) {
Assert.isLegal(rowIndex < rowCount(), "The row number: " + rowIndex + " does not exist in the table"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Sets the selection to the given items. Replaces the current selection. If
* there is more than one item to select, the table must have the SWT.MULTI
* style.
*
* @param items
* the items to select in the table.
* @since 1.0
*/
public void select(final String... items) {
waitForEnabled();
setFocus();
int[] itemIndicies = new int[items.length];
for(int i = 0; i < items.length; i++) {
itemIndicies[i] = indexOf(items[i]);
Assert.isLegal(itemIndicies[i] >= 0, "Could not find item:" + items[i] + " in table"); //$NON-NLS-1$ //$NON-NLS-2$
}
select(itemIndicies);
}
/**
* Gets the index of the item matching the given item.
*
* @param item the item in the table.
* @return the index of the specified item in the table, or -1 if the item does not exist in the table.
* @since 1.0
*/
public int indexOf(final String item) {
return syncExec(new IntResult() {
public Integer run() {
TableItem[] items = widget.getItems();
for (int i = 0; i < items.length; i++) {
TableItem tableItem = items[i];
if (tableItem.getText().equals(item))
return i;
}
return -1;
}
});
}
/**
* @param item the item in the table.
* @return <code>true</code> if the table contains the specified item, <code>false</code> otherwise.
*/
public boolean containsItem(final String item) {
return indexOf(item) != -1;
}
/**
* Gets the index of the item matching the given item and the given column.
*
* @param item the index of the item in the table, or -1 if the item does not exist in the table.
* @param column the column for which to get the index of.
* @return the index of the specified item and of the specified column in the table.
* @since 1.3
*/
public int indexOf(final String item, final int column) {
return syncExec(new IntResult() {
public Integer run() {
TableItem[] items = widget.getItems();
for (int i = 0; i < items.length; i++) {
TableItem tableItem = items[i];
if (tableItem.getText(column).equals(item))
return i;
}
return -1;
}
});
}
/**
* Gets the index of the item matching the given item and the given column.
*
* @param item the index of the item in the table, or -1 if the item does not exist in the table.
* @param column the column for which to get the index of.
* @return the index of the specified item and of the specified column in the table.
* @since 1.3
*/
public int indexOf(final String item, final String column) {
return indexOf(item, indexOfColumn(column));
}
/**
* Unselect all selections.
*/
public void unselect() {
waitForEnabled();
setFocus();
log.debug(MessageFormat.format("Unselecting all in {0}", this)); //$NON-NLS-1$
TableItem[] selection = syncExec(new ArrayResult<TableItem>() {
public TableItem[] run() {
return widget.getSelection();
}
});
for (TableItem item : selection) {
unselect(item);
notifySelect(true);
}
}
/**
* Unselects the given table item.
*
* @param item
* table item to unselect
*/
private void unselect(final TableItem item) {
asyncExec(new VoidResult() {
public void run() {
widget.deselect(widget.indexOf(item));
lastSelectionItem = item;
}
});
}
/**
* Selects the given index items. Replaces the current selection. If there
* is more than one item to select, the table must have the SWT.MULTI style.
*
* @param indices
* the row indices to select in the table.
*/
public void select(final int... indices) {
waitForEnabled();
if (indices.length > 1) {
assertMultiSelect();
} else if (indices.length == 0) {
unselect();
return;
}
setFocus();
log.debug(MessageFormat.format("Selecting rows {0} in {1}", Arrays.toString(indices), this)); //$NON-NLS-1$ //$NON-NLS-2$
for (int i = 0; i < indices.length; i++) {
assertIsLegalRowIndex(indices[i]);
}
final List<TableItem> selection = new ArrayList<TableItem>();
for (int index : indices) {
selection.add(getItem(index));
}
for (int i = 0; i < selection.size(); i++) {
boolean add = (i != 0);
processSelection(selection.get(i), add);
notifySelect(add);
}
}
private void assertMultiSelect() {
Assert.isLegal(hasStyle(widget, SWT.MULTI), "Table does not support multi selection."); //$NON-NLS-1$
}
/**
* Selects a table item
*
* @param item
* the table item to select
* @param add
* true to add to current selection
*/
private void processSelection(final TableItem item, final boolean add) {
syncExec(new VoidResult() {
public void run() {
if (add) {
widget.select(widget.indexOf(item));
} else {
// removes earlier selection
widget.setSelection(item);
}
lastSelectionItem = item;
}
});
}
/**
* Notifies the selection.
*/
protected void notifySelect() {
notifySelect(false);
}
/**
* Notifies the selection.
*
* @param ctrl
* true if CTRL key should be pressed while sending the event,
* false otherwise.
* @since 2.6
*/
private void notifySelect(boolean ctrl) {
int stateMask1 = (ctrl) ? (SWT.NONE | SWT.CTRL) : SWT.NONE;
int stateMask2 = (ctrl) ? (SWT.BUTTON1 | SWT.CTRL) : SWT.BUTTON1;
notify(SWT.MouseEnter);
notify(SWT.MouseMove);
notify(SWT.Activate);
notify(SWT.FocusIn);
notify(SWT.MouseDown, createMouseEvent(0, 0, 1, stateMask1, 1));
notify(SWT.Selection, selectionEvent(stateMask2));
notify(SWT.MouseUp, createMouseEvent(0, 0, 1, stateMask2, 1));
notify(SWT.MouseHover);
notify(SWT.MouseMove);
notify(SWT.MouseExit);
notify(SWT.Deactivate);
notify(SWT.FocusOut);
}
private Event selectionEvent(int stateMask) {
Event createEvent = createEvent();
createEvent.item = lastSelectionItem;
createEvent.stateMask = stateMask;
return createEvent;
}
/**
* Click on the table on given cell. This can be used to activate a cellEditor on a cell.
*
* @param row the row in the table.
* @param column the column in the table.
* @since 1.2
*/
public void click(final int row, final int column) {
assertIsLegalCell(row, column);
// for some reason, it does not work without setting selection first
setFocus();
select(row);
asyncExec(new VoidResult() {
public void run() {
TableItem item = widget.getItem(row);
Rectangle cellBounds = item.getBounds(column);
clickXY(cellBounds.x + (cellBounds.width / 2), cellBounds.y + (cellBounds.height / 2));
}
});
}
/**
* Click on the table on given cell. This can be used to activate a cellEditor on a cell.
*
* @param row the row in the table.
* @param column the column in the table.
* @since 1.2
*/
public void doubleClick(final int row, final int column) {
assertIsLegalCell(row, column);
setFocus();
asyncExec(new VoidResult() {
public void run() {
TableItem item = widget.getItem(row);
Rectangle cellBounds = item.getBounds(column);
// for some reason, it does not work without setting selection first
widget.setSelection(row);
doubleClickXY(cellBounds.x + (cellBounds.width / 2), cellBounds.y + (cellBounds.height / 2));
}
});
}
/**
* Double-click on the table at given coordinates
*
* @param x the x co-ordinate of the click
* @param y the y co-ordinate of the click
*/
@Override
protected void doubleClickXY(int x, int y) {
log.debug(MessageFormat.format("Double-clicking on {0}", widget)); //$NON-NLS-1$
notify(SWT.MouseEnter);
notify(SWT.MouseMove);
notify(SWT.Activate);
notify(SWT.FocusIn);
notify(SWT.MouseDown, createMouseEvent(x, y, 1, SWT.NONE, 1));
notify(SWT.MouseUp, createMouseEvent(x, y, 1, SWT.BUTTON1, 1));
notify(SWT.Selection, createSelectionEvent(SWT.BUTTON1));
notify(SWT.MouseDoubleClick, createMouseEvent(x, y, 1, SWT.BUTTON1, 2));
notify(SWT.DefaultSelection); // super implementation misses this line. Required for notification of double click listeners.
notify(SWT.MouseHover);
notify(SWT.MouseMove);
notify(SWT.MouseExit);
notify(SWT.Deactivate);
notify(SWT.FocusOut);
log.debug(MessageFormat.format("Double-clicked on {0}", widget)); //$NON-NLS-1$
}
/**
* Asserts that the row and column are legal for this instance of the table.
*
* @param row the row number
* @param column the column number
* @since 1.2
*/
protected void assertIsLegalCell(final int row, final int column) {
int rowCount = rowCount();
int columnCount = columnCount(); // 0 if no TableColumn has been created by user
Assert.isLegal(row < rowCount, "The row number (" + row + ") is more than the number of rows(" + rowCount + ") in the table."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Assert.isLegal((column < columnCount) || ((columnCount == 0) && (column == 0)), "The column number (" + column //$NON-NLS-1$
+ ") is more than the number of column(" + columnCount + ") in the table."); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Gets the table item matching the given name.
*
* @param itemText the text on the node.
* @return the table item with the specified text.
* @throws WidgetNotFoundException if the node was not found.
* @since 1.3
*/
public SWTBotTableItem getTableItem(final String itemText) throws WidgetNotFoundException {
try {
new SWTBot().waitUntil(new DefaultCondition() {
public String getFailureMessage() {
return "Could not find node with text " + itemText; //$NON-NLS-1$
}
public boolean test() throws Exception {
return getItem(itemText) != null;
}
});
} catch (TimeoutException e) {
throw new WidgetNotFoundException("Timed out waiting for table item " + itemText, e); //$NON-NLS-1$
}
return new SWTBotTableItem(getItem(itemText));
}
/**
* Gets the item matching the given name.
*
* @param itemText the text on the node.
* @return the table item with the specified text.
*/
private TableItem getItem(final String itemText) {
return syncExec(new WidgetResult<TableItem>() {
public TableItem run() {
TableItem[] items = widget.getItems();
for (int i = 0; i < items.length; i++) {
TableItem item = items[i];
if (item.getText().equals(itemText))
return item;
}
return null;
}
});
}
/**
* Gets the table item matching the given row number.
*
* @param row the row number.
* @return the table item with the specified row.
* @throws WidgetNotFoundException if the node was not found.
* @since 2.0
*/
public SWTBotTableItem getTableItem(final int row) throws WidgetNotFoundException {
try {
new SWTBot().waitUntil(new DefaultCondition() {
public String getFailureMessage() {
return "Could not find table item for row " + row; //$NON-NLS-1$
}
public boolean test() throws Exception {
return getItem(row) != null;
}
});
} catch (TimeoutException e) {
throw new WidgetNotFoundException("Timed out waiting for table item in row " + row, e); //$NON-NLS-1$
}
return new SWTBotTableItem(getItem(row));
}
/**
* Gets the item matching the given row number.
*
* @param row the row number.
* @return the table item with the specified row.
*/
private TableItem getItem(final int row) {
return syncExec(new WidgetResult<TableItem>() {
public TableItem run() {
return widget.getItem(row);
}
});
}
}