/******************************************************************************* * Copyright (c) 2012 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 *******************************************************************************/ package org.eclipse.jubula.rc.swt.tester.adapter; import java.awt.Rectangle; import org.eclipse.jubula.rc.common.driver.ClickOptions; import org.eclipse.jubula.rc.common.driver.IRunnable; import org.eclipse.jubula.rc.common.exception.StepExecutionException; import org.eclipse.jubula.rc.common.implclasses.table.Cell; import org.eclipse.jubula.rc.common.logger.AutServerLogger; import org.eclipse.jubula.rc.common.tester.adapter.interfaces.ITableComponent; import org.eclipse.jubula.rc.common.util.IndexConverter; import org.eclipse.jubula.rc.common.util.MatchUtil; import org.eclipse.jubula.rc.swt.listener.TableSelectionTracker; import org.eclipse.jubula.rc.swt.tester.TableTester; import org.eclipse.jubula.rc.swt.tester.util.CAPUtil; 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.jubula.tools.internal.objects.event.EventFactory; import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableCursor; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; /** * Implements the Table interface for adapting a <code>SWT.Table</code> * * @author BREDEX GmbH */ public class TableAdapter extends ControlAdapter implements ITableComponent { /** the logger */ private static AutServerLogger log = new AutServerLogger( TableAdapter.class); /** the table */ private Table m_table; /** * * @param objectToAdapt graphics component which will be adapted */ public TableAdapter(Object objectToAdapt) { super(objectToAdapt); m_table = (Table) objectToAdapt; } /** {@inheritDoc} */ public int getColumnCount() { return getEventThreadQueuer().invokeAndWait( "getColumnCount", new IRunnable<Integer>() { //$NON-NLS-1$ public Integer run() { return m_table.getColumnCount(); } }); } /** {@inheritDoc} */ public int getRowCount() { return getEventThreadQueuer().invokeAndWait( "getRowCount", new IRunnable<Integer>() { //$NON-NLS-1$ public Integer run() { return m_table.getItemCount(); } }); } /** {@inheritDoc} */ public String getCellText(final int row, final int column) { return getEventThreadQueuer().invokeAndWait("getCellText", //$NON-NLS-1$ new IRunnable<String>() { public String run() { final TableItem item = m_table.getItem(row); String value = CAPUtil.getWidgetText(item, SwtToolkitConstants.WIDGET_TEXT_KEY_PREFIX + column, item.getText(column)); if (log.isDebugEnabled()) { log.debug("Getting cell text:"); //$NON-NLS-1$ log.debug("Row, col: " + row + ", " + column); //$NON-NLS-1$ //$NON-NLS-2$ log.debug("Value: " + value); //$NON-NLS-1$ } return value; } }); } /** {@inheritDoc} */ public String getColumnHeaderText(final int colIdx) { return getEventThreadQueuer().invokeAndWait("getColumnName", //$NON-NLS-1$ new IRunnable<String>() { public String run() { final TableColumn column = m_table.getColumn(colIdx); return CAPUtil.getWidgetText(column, column.getText()); } }); } /** {@inheritDoc} */ public int getColumnFromString(final String col, final String operator) { int column = -2; try { int usrIdxCol = Integer.parseInt(col); if (usrIdxCol == 0) { usrIdxCol = usrIdxCol + 1; } column = IndexConverter.toImplementationIndex( usrIdxCol); } catch (NumberFormatException nfe) { try { Boolean isVisible = getEventThreadQueuer().invokeAndWait( "getColumnFromString", //$NON-NLS-1$ new IRunnable<Boolean>() { public Boolean run() { return m_table.getHeaderVisible(); } }); if (!(isVisible.booleanValue())) { throw new StepExecutionException("No Header", //$NON-NLS-1$ EventFactory.createActionError( TestErrorEvent.NO_HEADER)); } Integer implCol = getEventThreadQueuer().invokeAndWait( "getColumnFromString", new IRunnable<Integer>() { //$NON-NLS-1$ public Integer run() throws StepExecutionException { for (int i = 0; i < m_table.getColumnCount(); i++) { String colHeader = getColumnHeaderText(i); if (MatchUtil.getInstance().match( colHeader, col, operator)) { return i; } } return -2; } }); column = implCol.intValue(); } catch (IllegalArgumentException iae) { //do nothing here } } return column; } /** {@inheritDoc} */ public String getRowText(final int rowIdx) { return getEventThreadQueuer().invokeAndWait("getRowText", //$NON-NLS-1$ new IRunnable<String>() { public String run() { final TableItem row = m_table.getItem(rowIdx); return CAPUtil.getWidgetText(row, row.getText()); } }); } /** {@inheritDoc} */ public int getRowFromString(final String row, final String operator) { int rowInt = -2; try { rowInt = IndexConverter.toImplementationIndex( Integer.parseInt(row)); if (rowInt == -1) { Boolean isVisible = getEventThreadQueuer().invokeAndWait( "getRowFromString", //$NON-NLS-1$ new IRunnable<Boolean>() { public Boolean run() { return m_table.getHeaderVisible(); } }); if (!(isVisible.booleanValue())) { throw new StepExecutionException("Header not visible", //$NON-NLS-1$ EventFactory.createActionError( TestErrorEvent.NO_HEADER)); } } } catch (NumberFormatException nfe) { Integer implRow = getEventThreadQueuer().invokeAndWait( "getRowFromString", //$NON-NLS-1$ new IRunnable<Integer>() { public Integer run() throws StepExecutionException { for (int i = 0; i < m_table.getItemCount(); i++) { String cellTxt = getCellText(i, 0); if (MatchUtil.getInstance().match( cellTxt, row, operator)) { return i; } } return -2; } }); rowInt = implRow.intValue(); } return rowInt; } /** {@inheritDoc} */ public Rectangle getBounds() { return getEventThreadQueuer().invokeAndWait("getBounds", //$NON-NLS-1$ new IRunnable<Rectangle>() { public Rectangle run() throws StepExecutionException { org.eclipse.swt.graphics.Rectangle rect = m_table .getBounds(); return new Rectangle(rect.x, rect.y, rect.width, rect.height); } }); } /** {@inheritDoc} */ public Rectangle getHeaderBounds(final int col) { return getEventThreadQueuer().invokeAndWait("getHeaderBounds", //$NON-NLS-1$ new IRunnable<Rectangle>() { public Rectangle run() throws StepExecutionException { org.eclipse.swt.graphics.Rectangle rect = m_table .getItem(0).getBounds(col); rect.y = m_table.getClientArea().y; return new Rectangle(rect.x, rect.y, rect.width, rect.height); } }); } /** {@inheritDoc} */ public Cell getSelectedCell() throws StepExecutionException { return getEventThreadQueuer().invokeAndWait( "getSelectedSell", new IRunnable<Cell>() { //$NON-NLS-1$ public Cell run() throws StepExecutionException { return TableSelectionTracker.getInstance() .getSelectedCell(m_table); } }); } /** {@inheritDoc} */ public boolean isHeaderVisible() { return getEventThreadQueuer().invokeAndWait("isHeaderVisible", //$NON-NLS-1$ new IRunnable<Boolean>() { public Boolean run() { return m_table.getHeaderVisible(); } }); } /** {@inheritDoc} */ public boolean isCellEditable(final int row, final int col) { final Control cellEditor = (Control) activateEditor(new Cell(row, col)); return getEventThreadQueuer().invokeAndWait("isCellEditable", //$NON-NLS-1$ new IRunnable<Boolean>() { public Boolean run() { return isEditable(cellEditor); } }); } /** {@inheritDoc} */ public boolean hasCellSelection() { TableItem[] selItems = getEventThreadQueuer() .invokeAndWait("hasCellSelection", //$NON-NLS-1$ new IRunnable<TableItem[]>() { public TableItem[] run() { return m_table.getSelection(); } }); return selItems.length > 0; } /** * {@inheritDoc} */ public Rectangle scrollCellToVisible(final int row, final int col) throws StepExecutionException { final Table table = m_table; getEventThreadQueuer().invokeAndWait("scrollCellToVisible", //$NON-NLS-1$ new IRunnable<Void>() { public Void run() { if (table.getColumnCount() > 0 || col > 0) { table.showColumn(table.getColumn(col)); } table.showItem(table.getItem(row)); return null; } }); checkRowColBounds(row, col); final org.eclipse.swt.graphics.Rectangle cBoundsRelToParent = SwtPointUtil.toSwtRectangle(TableTester.getCellBounds( getEventThreadQueuer(), m_table, row, col)); getEventThreadQueuer().invokeAndWait("getCellBoundsRelativeToParent", //$NON-NLS-1$ new IRunnable<Void>() { public Void run() { org.eclipse.swt.graphics.Point cOriginRelToParent = table.getDisplay().map(table, table.getParent(), new org.eclipse.swt.graphics.Point( cBoundsRelToParent.x, cBoundsRelToParent.y)); cBoundsRelToParent.x = cOriginRelToParent.x; cBoundsRelToParent.y = cOriginRelToParent.y; return null; } }); Control parent = getEventThreadQueuer().invokeAndWait("getParent", //$NON-NLS-1$ new IRunnable<Control>() { public Control run() { return table.getParent(); } }); getRobot().scrollToVisible(parent, cBoundsRelToParent); return getVisibleBounds(TableTester.getCellBounds( getEventThreadQueuer(), m_table, row, col)); } /** * Checks whether <code>0 <= value < count</code>. * @param value The value to check. * @param count The upper bound. */ private void checkBounds(int value, int count) { if (value < 0 || value >= count) { throw new StepExecutionException("Invalid row/column: " + value, //$NON-NLS-1$ EventFactory.createActionError( TestErrorEvent.INVALID_INDEX_OR_HEADER)); } } /** * Checks if the passed row and column are inside the bounds of the Table. * @param row The row * @param column The column * @throws StepExecutionException If the row or the column is outside of the Table's bounds. */ protected void checkRowColBounds(int row, int column) throws StepExecutionException { checkBounds(row, getRowCount()); // Corner case: Only check the bounds if the table is not being // used as a list or anything other than the first column // is being checked. int colCount = getColumnCount(); if (colCount > 0 || column > 0) { checkBounds(column, colCount); } } /** * Computes the visible cellBounds inside the visible bounds of the table.<br> * The result is the intersection of the visible bounds of the table and the * bounds of the cell. * @param cellBounds the bounds of the cell to click in. These bounds must * be relative to the table's location. * @return the visible cell bounds, relative to the table's location. */ private Rectangle getVisibleBounds(Rectangle cellBounds) { org.eclipse.swt.graphics.Rectangle r = getEventThreadQueuer() .invokeAndWait("getVisibleCellBounds: " + cellBounds, //$NON-NLS-1$ new IRunnable<org.eclipse.swt.graphics.Rectangle>() { public org.eclipse.swt.graphics.Rectangle run() { return m_table.getClientArea(); } }); Rectangle visibleTableBounds = new Rectangle( r.x, r.y, r.width, r.height); Rectangle visibleCellBounds = visibleTableBounds.intersection(cellBounds); return visibleCellBounds; } /** * @param cellEditor The cell editor to check. * @return <code>true</code> if the given cell editor is editable. */ private boolean isEditable(Control cellEditor) { if (cellEditor == null || cellEditor instanceof TableCursor || cellEditor == m_table) { // No actual editor found. return false; } return (cellEditor.getStyle() & SWT.READ_ONLY) == 0; } /** * @param cellEditor The cell editor to check. * @return <code>true</code> if the given editor is editable. Otherwise * <code>false</code>. */ private boolean invokeIsEditable(final Control cellEditor) { return getEventThreadQueuer().invokeAndWait("getSelectedCell", //$NON-NLS-1$ new IRunnable<Boolean>() { public Boolean run() { return isEditable(cellEditor); } }); } /** * {@inheritDoc} */ public Object activateEditor(final Cell cell) { Rectangle rect = scrollCellToVisible(cell.getRow(), cell.getCol()); Control editor = getTableCellEditor(cell, rect); // sometimes the editor only appears after doubleclick! if (!invokeIsEditable(editor)) { org.eclipse.swt.graphics.Rectangle cellBounds = new org.eclipse.swt.graphics.Rectangle( rect.x, rect.y, rect.width, rect.height); ClickOptions co = ClickOptions.create().setClickCount(2); Control clickTarget = editor == null || editor instanceof TableCursor ? m_table : editor; getRobot().click(clickTarget, cellBounds, co); editor = getTableCellEditor(cell, rect); } return editor; } /** * Gets the TableCellEditor of the given cell. * The Cell has to be activated before! * @param cell the cell. * @param rect * @return the TableCellEditor */ private Control getTableCellEditor(final Cell cell, Rectangle rect) { org.eclipse.swt.graphics.Rectangle swtRect = new org.eclipse.swt.graphics.Rectangle(rect.x, rect.y, rect.width, rect.height); getRobot().click(m_table, swtRect, ClickOptions.create().setClickCount(1)); return SwtUtils.getCursorControl(); } /** * {@inheritDoc} */ public String getText() { final Cell selectedCell = getSelectedCell(); return getCellText(selectedCell.getRow(), selectedCell.getCol()); } /** * {@inheritDoc} */ public Object getTableHeader() { return m_table; } /** * {@inheritDoc} */ public String getPropertyValueOfCell(final String name, final Object cell) { return getEventThreadQueuer().invokeAndWait( "getPropertyValueOfCell", new IRunnable<String>() { //$NON-NLS-1$ public String run() { return getRobot().getPropertyValue(cell, name); } }); } }