/*******************************************************************************
* 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.listener;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jubula.rc.common.exception.StepExecutionException;
import org.eclipse.jubula.rc.common.implclasses.table.Cell;
import org.eclipse.jubula.rc.common.listener.BaseAUTListener;
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.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
/**
* Tracks the currently selected cell (or row, if no column can be determined)
* for each Table.
*
* @author BREDEX GmbH
* @created Oct 15, 2008
*/
public class TableSelectionTracker implements BaseAUTListener, Listener {
/** the singular instance */
private static TableSelectionTracker instance;
/**
* Table => Cell
* Based on events received from Tables.
* This mapping is checked only if no mapping exists based on a TableCursor
* because this mapping only contains a row (no column).
*/
private Map m_tableToSelection = new HashMap();
/**
* Table => Cell
* Based on event received from TableCursors.
* This mapping is always checked first because it contains
* more specific information (column).
*/
private Map m_tableToCursorSelection = new HashMap();
/**
* TableCursor => Table
* Allows us to determine when to remove a mapping from
* <code>m_tableToCursorSelection</code>.
*/
private Map m_cursorToTable = new HashMap();
/**
* Private constructor for singleton
*/
private TableSelectionTracker() {
// Nothing to initialize
}
/**
*
* @return the single instance.
*/
public static TableSelectionTracker getInstance() {
if (instance == null) {
instance = new TableSelectionTracker();
}
return instance;
}
/**
* <strong>Must be called from the UI thread.</strong>
*
* @param table The table for which to find the selected cell.
* @return the currently selected cell in <code>table</code>. If the column
* of the selection cannot be determined (i.e. because only
* selection of an entire row is supported), then the column index
* for the returned cell will be <code>0</code>.
*
* @throws StepExecutionException if <code>table</code> has no selection.
*/
public Cell getSelectedCell(Table table) throws StepExecutionException {
// First try to get the selection from the TableCursor, as this
// also contains a column index.
Cell selectedCell = (Cell)m_tableToCursorSelection.get(table);
if (selectedCell == null) {
// Try finding the selected cell based on the currently focused
// component. If the currently focused component lies
// within the bounds of the table, then we assume that the component
// is a cell editor. We then take the center point of the component
// and try to determine which cell is being edited. We consider this
// edited cell as selected.
Display d = table.getDisplay();
Control focusControl = d.getFocusControl();
if (focusControl != null) {
Rectangle bounds = d.map(focusControl.getParent(), null,
focusControl.getBounds());
Point center = new Point(
bounds.x + (bounds.width / 2),
bounds.y + (bounds.height / 2));
if (d.map(table.getParent(), null, table.getBounds())
.contains(center)) {
for (int row = 0; row < table.getItemCount()
&& selectedCell == null; row++) {
for (int col = 0; col < table.getColumnCount()
&& selectedCell == null; col++) {
if (d.map(table, null, table.getItem(row)
.getBounds(col)).contains(center)) {
selectedCell = new Cell(row, col);
}
}
}
}
}
}
if (selectedCell == null) {
// As a fallback, use the selection of the Table itself, which
// only provides a row index.
// This fallback will be used whenever the given table does not
// use a TableCursor.
selectedCell = (Cell)m_tableToSelection.get(table);
}
if (selectedCell == null) {
throw new StepExecutionException("No table cell selected.", //$NON-NLS-1$
EventFactory.createActionError(
TestErrorEvent.NO_SELECTION));
}
return selectedCell;
}
/**
* {@inheritDoc}
*/
public long[] getEventMask() {
return new long [] {SWT.Selection, SWT.Dispose, SWT.Move};
}
/**
* {@inheritDoc}
*/
public void handleEvent(Event event) {
if ((event.type == SWT.Selection || event.type == SWT.DefaultSelection)
&& event.widget instanceof Table) {
handleTableSelection((Table)event.widget);
} else if (event.type == SWT.Dispose) {
handleDisposeEvent(event);
} else if (event.type == SWT.Move
&& event.widget instanceof TableCursor) {
handleTableCursorMoved((TableCursor)event.widget);
}
}
/**
* @param tableCursor The TableCursor that has moved.
*/
private void handleTableCursorMoved(TableCursor tableCursor) {
TableItem selectedRow = tableCursor.getRow();
if (selectedRow != null) {
// Something is selected
Table table = selectedRow.getParent();
int selectedRowIdx = table.indexOf(selectedRow);
int selectedColIdx = tableCursor.getColumn();
m_tableToCursorSelection.put(table,
new Cell(selectedRowIdx, selectedColIdx));
m_cursorToTable.put(tableCursor, table);
} else {
// No selection
m_tableToCursorSelection.remove(m_cursorToTable.get(tableCursor));
m_cursorToTable.remove(tableCursor);
}
}
/**
* @param event The dispose event to handle.
*/
private void handleDisposeEvent(Event event) {
if (event.widget instanceof Table) {
m_tableToSelection.remove(event.widget);
m_tableToCursorSelection.remove(event.widget);
} else if (event.widget instanceof TableCursor) {
TableCursor eventCursor = (TableCursor)event.widget;
m_tableToCursorSelection.remove(m_cursorToTable.get(eventCursor));
m_cursorToTable.remove(eventCursor);
}
}
/**
* @param table The table for which the selection has changed.
*/
private void handleTableSelection(Table table) {
int selectionIndex = table.getSelectionIndex();
if (selectionIndex != -1) {
// Something is selected
// Since Tables themselves only support selection of entire rows
// (rather than individual cells), we consider the first cell as
// selected.
m_tableToSelection.put(table, new Cell(selectionIndex, 0));
} else {
// No selection
m_tableToSelection.remove(table);
}
}
}