/*
* Copyright (c) 2011, grossmann, Nikolaus Moll
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.spi.impl.swt.common.widgets;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.ToolTip;
import org.jowidgets.common.color.IColorConstant;
import org.jowidgets.common.image.IImageConstant;
import org.jowidgets.common.model.ITableCell;
import org.jowidgets.common.model.ITableColumnModelListener;
import org.jowidgets.common.model.ITableColumnModelObservable;
import org.jowidgets.common.model.ITableColumnModelSpi;
import org.jowidgets.common.model.ITableColumnSpi;
import org.jowidgets.common.model.ITableDataModel;
import org.jowidgets.common.model.ITableDataModelListener;
import org.jowidgets.common.model.ITableDataModelObservable;
import org.jowidgets.common.types.AlignmentHorizontal;
import org.jowidgets.common.types.Dimension;
import org.jowidgets.common.types.Markup;
import org.jowidgets.common.types.Modifier;
import org.jowidgets.common.types.MouseButton;
import org.jowidgets.common.types.Position;
import org.jowidgets.common.types.TablePackPolicy;
import org.jowidgets.common.types.TableSelectionPolicy;
import org.jowidgets.common.types.VirtualKey;
import org.jowidgets.common.widgets.IControlCommon;
import org.jowidgets.common.widgets.controller.IKeyEvent;
import org.jowidgets.common.widgets.controller.IKeyListener;
import org.jowidgets.common.widgets.controller.ITableCellListener;
import org.jowidgets.common.widgets.controller.ITableCellMouseEvent;
import org.jowidgets.common.widgets.controller.ITableCellPopupDetectionListener;
import org.jowidgets.common.widgets.controller.ITableColumnListener;
import org.jowidgets.common.widgets.controller.ITableColumnPopupDetectionListener;
import org.jowidgets.common.widgets.controller.ITableSelectionListener;
import org.jowidgets.common.widgets.descriptor.IWidgetDescriptor;
import org.jowidgets.common.widgets.editor.EditActivation;
import org.jowidgets.common.widgets.editor.ITableCellEditor;
import org.jowidgets.common.widgets.editor.ITableCellEditorFactory;
import org.jowidgets.common.widgets.factory.ICustomWidgetFactory;
import org.jowidgets.common.widgets.factory.IGenericWidgetFactory;
import org.jowidgets.spi.impl.controller.TableCellMouseEvent;
import org.jowidgets.spi.impl.controller.TableCellObservable;
import org.jowidgets.spi.impl.controller.TableCellPopupDetectionObservable;
import org.jowidgets.spi.impl.controller.TableCellPopupEvent;
import org.jowidgets.spi.impl.controller.TableColumnMouseEvent;
import org.jowidgets.spi.impl.controller.TableColumnObservable;
import org.jowidgets.spi.impl.controller.TableColumnPopupDetectionObservable;
import org.jowidgets.spi.impl.controller.TableColumnPopupEvent;
import org.jowidgets.spi.impl.controller.TableColumnResizeEvent;
import org.jowidgets.spi.impl.controller.TableSelectionObservable;
import org.jowidgets.spi.impl.swt.common.color.ColorCache;
import org.jowidgets.spi.impl.swt.common.image.SwtImageRegistry;
import org.jowidgets.spi.impl.swt.common.options.SwtOptions;
import org.jowidgets.spi.impl.swt.common.util.AlignmentConvert;
import org.jowidgets.spi.impl.swt.common.util.FontProvider;
import org.jowidgets.spi.impl.swt.common.util.ListenerUtil;
import org.jowidgets.spi.impl.swt.common.util.MouseUtil;
import org.jowidgets.spi.widgets.ITableSpi;
import org.jowidgets.spi.widgets.setup.ITableSetupSpi;
import org.jowidgets.util.ArrayUtils;
import org.jowidgets.util.Assert;
import org.jowidgets.util.EmptyCheck;
import org.jowidgets.util.Interval;
import org.jowidgets.util.NullCompatibleEquivalence;
public class TableImpl extends SwtControl implements ITableSpi {
private final SwtImageRegistry imageRegistry;
private final Table table;
private final ITableDataModel dataModel;
private final ITableColumnModelSpi columnModel;
private final IGenericWidgetFactory factory;
private final ICustomWidgetFactory editorCustomWidgetFactory;
private final TableCellObservable tableCellObservable;
private final TableCellPopupDetectionObservable tableCellPopupDetectionObservable;
private final TableColumnPopupDetectionObservable tableColumnPopupDetectionObservable;
private final TableColumnObservable tableColumnObservable;
private final TableSelectionObservable tableSelectionObservable;
private final ColumnSelectionListener columnSelectionListener;
private final ColumnControlListener columnControlListener;
private final TableModelListener tableModelListener;
private final TableColumnModelListener tableColumnModelListener;
private final DataListener dataListener;
private final EraseItemListener eraseItemListener;
private final ITableCellEditorFactory<? extends ITableCellEditor> editorFactory;
private final boolean columnsMoveable;
private final boolean columnsResizeable;
private final IColorConstant selectedForegroundColor;
private final IColorConstant selectedBackgroundColor;
private Method setFocusIndexMethod;
private int[] lastColumnOrder;
private boolean setWidthInvokedOnModel;
private ToolTip toolTip;
private boolean editable;
private TableEditor editor;
private ITableCellEditor tableCellEditor;
private ITableCell editTableCell;
private int editRowIndex;
private int editColumnIndex;
private long stopEditTimestamp;
private Integer rowHeight;
private int[] lastSelection;
public TableImpl(
final IGenericWidgetFactory factory,
final Object parentUiReference,
final ITableSetupSpi setup,
final SwtImageRegistry imageRegistry) {
super(new Table((Composite) parentUiReference, getStyle(setup)), imageRegistry);
this.imageRegistry = imageRegistry;
this.factory = factory;
this.editorCustomWidgetFactory = new EditorCustomWidgetFactory();
this.selectedForegroundColor = SwtOptions.getTableSelectedForegroundColor();
this.selectedBackgroundColor = SwtOptions.getTableSelectedBackgroundColor();
this.editable = true;
this.setWidthInvokedOnModel = false;
this.tableCellObservable = new TableCellObservable();
this.tableCellPopupDetectionObservable = new TableCellPopupDetectionObservable();
this.tableColumnPopupDetectionObservable = new TableColumnPopupDetectionObservable();
this.tableColumnObservable = new TableColumnObservable();
this.tableSelectionObservable = new TableSelectionObservable();
this.dataListener = new DataListener();
this.eraseItemListener = new EraseItemListener();
this.columnSelectionListener = new ColumnSelectionListener();
this.columnControlListener = new ColumnControlListener();
this.tableModelListener = new TableModelListener();
this.tableColumnModelListener = new TableColumnModelListener();
this.dataModel = setup.getDataModel();
this.columnModel = setup.getColumnModel();
this.columnsMoveable = setup.getColumnsMoveable();
this.columnsResizeable = setup.getColumnsResizeable();
this.editorFactory = setup.getEditor();
this.table = getUiReference();
try {
setFocusIndexMethod = table.getClass().getDeclaredMethod("setFocusIndex", int.class);
setFocusIndexMethod.setAccessible(true);
}
catch (final Exception e) {
//DO NOTHING, SET FOCUS INDEX WILL NOT WORK ONLY
}
table.setLinesVisible(true);
table.setHeaderVisible(setup.isHeaderVisible());
// fake column to fix windows table bug and to support no selection
final TableColumn fakeColumn = new TableColumn(table, SWT.NONE);
fakeColumn.setResizable(false);
fakeColumn.setMoveable(false);
fakeColumn.setWidth(0);
fakeColumn.setText("FAKE");
try {
this.editor = new TableEditor(table);
this.editor.grabHorizontal = true;
this.editor.grabVertical = true;
table.addMouseListener(new TableEditListener());
}
catch (final NoClassDefFoundError e) {
//RWT does not support TableCursor yet :-(
//this.cursor = null;
this.editor = null;
}
this.editRowIndex = -1;
this.editColumnIndex = -1;
table.addListener(SWT.SetData, dataListener);
table.addListener(SWT.EraseItem, eraseItemListener);
table.addMouseListener(new TableCellListener());
table.addSelectionListener(new TableSelectionListener());
setMenuDetectListener(new TableMenuDetectListener());
// ToolTip support
try {
this.toolTip = new ToolTip(table.getShell(), SWT.NONE);
}
catch (final NoClassDefFoundError error) {
//TODO MG rwt has no tooltip, may use a window instead.
//(New rwt version supports tooltips)
}
if (toolTip != null) {
final ToolTipListener toolTipListener = new ToolTipListener();
table.addListener(SWT.Dispose, toolTipListener);
table.addListener(SWT.KeyDown, toolTipListener);
table.addListener(SWT.MouseHover, toolTipListener);
table.addListener(SWT.MouseMove, toolTipListener);
}
table.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent arg0) {
final ITableDataModelObservable dataModelObservable = dataModel.getTableDataModelObservable();
if (dataModelObservable != null) {
dataModelObservable.removeDataModelListener(tableModelListener);
}
final ITableColumnModelObservable columnModelObservable = columnModel.getTableColumnModelObservable();
if (columnModelObservable != null) {
columnModelObservable.removeColumnModelListener(tableColumnModelListener);
}
}
});
table.addListener(SWT.MeasureItem, new Listener() {
@Override
public void handleEvent(final Event event) {
handleMeasureEvent(event);
}
});
table.addListener(SWT.EraseItem, new Listener() {
@Override
public void handleEvent(final Event event) {
handleMeasureEvent(event);
}
});
table.addListener(SWT.PaintItem, new Listener() {
@Override
public void handleEvent(final Event event) {
final TableItem item = (TableItem) event.item;
final int row = table.indexOf(item);
final int column = event.index - 1;
if (tableCellEditor != null && editColumnIndex == column && editRowIndex == row) {
final Rectangle bounds = item.getBounds(event.index);
tableCellEditor.setSize(new Dimension(bounds.width, event.height));
}
}
});
table.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(final TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_ESCAPE) {
cancelEditing();
}
if (e.detail == SWT.TRAVERSE_RETURN) {
if (editable) {
final ArrayList<Integer> selection = getSelection();
if (!isEditing() && getSelection().size() > 0) {
final int rowIndex = selection.get(0).intValue() - 1;
//this method adds one the row index for edit mode
navigateDown(rowIndex, rowIndex, 0);
}
else if (!isEditing() && dataModel.getRowCount() > 0) {
final int rowIndex = -1;
//this method adds one the row index for edit mode
navigateDown(rowIndex, rowIndex, 0);
}
}
}
}
});
}
private void handleMeasureEvent(final Event event) {
if (rowHeight != null) {
event.height = rowHeight.intValue();
}
if (SwtOptions.isTablePackWorkaround()) {
event.width = event.width + 1;
}
final TableItem item = (TableItem) event.item;
final int row = table.indexOf(item);
final int column = event.index - 1;
if (tableCellEditor != null && editColumnIndex == column && editRowIndex == row) {
final int newHeigth = tableCellEditor.getPreferredSize().getHeight() + 1;
if (rowHeight == null || newHeigth > rowHeight.intValue()) {
event.height = newHeigth;
rowHeight = newHeigth;
editor.layout();
}
}
}
@Override
public void setRowHeight(final int height) {
this.rowHeight = Integer.valueOf(height);
table.redraw();
}
@Override
public void setEditable(final boolean editable) {
this.editable = editable;
}
@Override
public void setEnabled(final boolean enabled) {
if (isEditing() && !enabled) {
stopEditing();
}
super.setEnabled(enabled);
}
private int getColumnCount() {
return table.getColumnCount() - 1;
}
@Override
public Table getUiReference() {
return (Table) super.getUiReference();
}
@Override
public Dimension getMinSize() {
return new Dimension(40, 40);
}
@Override
public void resetFromModel() {
table.setRedraw(false);
final ITableDataModelObservable dataModelObservable = dataModel.getTableDataModelObservable();
if (dataModelObservable != null) {
dataModelObservable.removeDataModelListener(tableModelListener);
}
final ITableColumnModelObservable columnModelObservable = columnModel.getTableColumnModelObservable();
if (columnModelObservable != null) {
columnModelObservable.removeColumnModelListener(tableColumnModelListener);
}
table.setItemCount(dataModel.getRowCount());
table.clearAll();
removeAllColumns();
addAllColumns();
setSelection(dataModel.getSelection());
if (dataModelObservable != null) {
dataModelObservable.addDataModelListener(tableModelListener);
}
if (columnModelObservable != null) {
columnModelObservable.addColumnModelListener(tableColumnModelListener);
}
table.setRedraw(true);
}
private void removeAllColumns() {
final int oldColumnCount = getColumnCount();
for (int columnIndex = 0; columnIndex < oldColumnCount; columnIndex++) {
removeColumnListener(columnIndex);
}
for (int columnIndex = 0; columnIndex < oldColumnCount; columnIndex++) {
removeColumn(0);
}
}
private void addAllColumns() {
final int columnsCount = columnModel.getColumnCount();
for (int columnIndex = 0; columnIndex < columnsCount; columnIndex++) {
addColumn(columnIndex, columnModel.getColumn(columnIndex));
}
for (int columnIndex = 0; columnIndex < columnsCount; columnIndex++) {
addColumnListener(columnIndex);
}
}
private void addColumn(final int externalIndex, final ITableColumnSpi joColumn) {
final int internalIndex = externalIndex + 1;
final TableColumn swtColumn = new TableColumn(table, SWT.NONE, internalIndex);
swtColumn.setMoveable(columnsMoveable);
swtColumn.setResizable(columnsResizeable);
setColumnData(swtColumn, joColumn);
}
private void setColumnData(final TableColumn swtColumn, final ITableColumnSpi joColumn) {
final String text = joColumn.getText();
final IImageConstant icon = joColumn.getIcon();
final AlignmentHorizontal alignment = joColumn.getAlignment();
if (text != null) {
swtColumn.setText(text);
}
else {
swtColumn.setText("");
}
if (icon != null) {
swtColumn.setImage(imageRegistry.getImage(icon));
}
else {
swtColumn.setImage(null);
}
if (alignment != null) {
swtColumn.setAlignment(AlignmentConvert.convert(alignment));
}
else {
swtColumn.setAlignment(SWT.LEFT);
}
if (joColumn.getWidth() != -1) {
if (swtColumn.getWidth() != joColumn.getWidth()) {
swtColumn.setWidth(joColumn.getWidth());
}
}
else {
if (swtColumn.getWidth() != 100) {
swtColumn.setWidth(100);
}
}
swtColumn.setToolTipText(joColumn.getToolTipText());
}
private void addColumnListener(final int externalIndex) {
final int internalIndex = externalIndex + 1;
final TableColumn column = table.getColumn(internalIndex);
if (column != null && !column.isDisposed()) {
column.addSelectionListener(columnSelectionListener);
column.addControlListener(columnControlListener);
}
}
private void removeColumnListener(final int externalIndex) {
final int internalIndex = externalIndex + 1;
final TableColumn column = table.getColumn(internalIndex);
if (column != null && !column.isDisposed()) {
column.removeSelectionListener(columnSelectionListener);
column.removeControlListener(columnControlListener);
}
}
private void removeColumn(final int externalIndex) {
final int internalIndex = externalIndex + 1;
final TableColumn column = table.getColumn(internalIndex);
if (column != null && !column.isDisposed()) {
column.dispose();
}
}
@Override
public Position getCellPosition(final int rowIndex, final int columnIndex) {
final int internalIndex = columnIndex + 1;
final Rectangle bounds = table.getItem(rowIndex).getBounds(internalIndex);
return new Position(bounds.x, bounds.y);
}
@Override
public Dimension getCellSize(final int rowIndex, final int columnIndex) {
final int internalIndex = columnIndex + 1;
final Rectangle bounds = table.getItem(rowIndex).getBounds(internalIndex);
return new Dimension(bounds.width, bounds.height);
}
@Override
public int getColumnAtPosition(final Position position) {
final Point point = new Point(position.getX(), position.getY());
final TableItem item = table.getItem(point);
if (item != null) {
for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) {
final int internalIndex = columnIndex + 1;
final Rectangle rect = item.getBounds(internalIndex);
if (rect.contains(point)) {
return columnIndex;
}
}
}
return -1;
}
@Override
public int getRowAtPosition(final Position position) {
final Point point = new Point(position.getX(), position.getY());
final TableItem item = table.getItem(point);
if (item != null) {
return table.indexOf(item);
}
return -1;
}
@Override
public void pack(final TablePackPolicy policy) {
table.setRedraw(false);
for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) {
packColumn(columnIndex, policy);
}
table.setRedraw(true);
}
@Override
public void pack(final int columnIndex, final TablePackPolicy policy) {
table.setRedraw(false);
packColumn(columnIndex, policy);
table.setRedraw(true);
}
private void packColumn(final int columnIndex, final TablePackPolicy policy) {
final int internalIndex = columnIndex + 1;
final Label textLabel = new Label(table, SWT.NONE);
final GC context = new GC(textLabel);
final TableColumn[] columns = table.getColumns();
final TableColumn column = columns[internalIndex];
boolean packed = false;
int max = 10;
if (policy.considerHeader()) {
context.setFont(table.getFont());
textLabel.setFont(table.getFont());
int width = context.textExtent(column.getText()).x;
if (column.getImage() != null) {
width += column.getImage().getBounds().width;
}
max = Math.max(max, width);
}
if (policy.considerData() && policy.considerAllData()) {
for (int i = 0; i < table.getItemCount(); i++) {
final TableItem item = table.getItem(i);
context.setFont(item.getFont(internalIndex));
textLabel.setFont(item.getFont(internalIndex));
int width = context.textExtent(item.getText(internalIndex)).x;
if (item.getImage(internalIndex) != null) {
width += item.getImage(internalIndex).getBounds().width + 5;
}
max = Math.max(max, width);
}
}
else if (policy.considerData()) {
packed = true;
column.pack();
}
if (packed) {
column.setWidth(Math.max(max + 15, column.getWidth()));
}
else {
column.setWidth(max + 15);
}
context.dispose();
textLabel.dispose();
}
@Override
public ArrayList<Integer> getColumnPermutation() {
final ArrayList<Integer> result = new ArrayList<Integer>();
for (final int index : table.getColumnOrder()) {
if (index == 0) {
continue;
}
result.add(Integer.valueOf(index - 1));
}
return result;
}
@Override
public void setColumnPermutation(final List<Integer> permutation) {
if (!table.isDisposed()) {
final int[] columnOrder = new int[permutation.size() + 1];
columnOrder[0] = 0;
int i = 1;
for (final Integer permutatedIndex : permutation) {
columnOrder[i] = permutatedIndex.intValue() + 1;
i++;
}
table.setRedraw(false);
table.setColumnOrder(columnOrder);
table.setRedraw(true);
}
}
@Override
public ArrayList<Integer> getSelection() {
final ArrayList<Integer> result = new ArrayList<Integer>();
final int[] selection = table.getSelectionIndices();
if (selection != null) {
for (final int index : selection) {
result.add(Integer.valueOf(index));
}
}
return result;
}
@Override
public void setSelection(final List<Integer> selection) {
if (!isSelectionEqualWithView(selection)) {
if (!EmptyCheck.isEmpty(selection)) {
final int[] newSelection = new int[selection.size()];
for (int i = 0; i < newSelection.length; i++) {
newSelection[i] = selection.get(i).intValue();
}
setSelectionWithoutShowSelection(newSelection);
}
else {
setSelectionWithoutShowSelection(new int[0]);
}
fireSelectionChangedIfNeccessary();
}
}
private void setSelectionWithoutShowSelection(final int[] indices) {
table.deselectAll();
if (indices.length == 0) {
return;
}
table.select(indices);
final int focusIndex = indices[0];
if (setFocusIndexMethod != null && focusIndex != -1) {
try {
setFocusIndexMethod.invoke(table, focusIndex);
}
catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void scrollToRow(final int rowIndex) {
Assert.paramInBounds(table.getItemCount() - 1, rowIndex, "rowIndex");
table.showItem(table.getItem(rowIndex));
}
@Override
public boolean isColumnPopupDetectionSupported() {
return true;
}
@Override
public Interval<Integer> getVisibleRows() {
final int rowCount = dataModel.getRowCount();
if (rowCount > 0) {
final int leftBoundary = table.getTopIndex();
final Dimension cellSize = getCellSize(leftBoundary, 0);
final Rectangle clientArea = table.getClientArea();
if (clientArea.height >= cellSize.getHeight()) {
final int visibleRows = (clientArea.height / cellSize.getHeight());
final int rigthBoundary = Math.min(rowCount - 1, leftBoundary + visibleRows);
return new Interval<Integer>(leftBoundary, rigthBoundary);
}
}
return new Interval<Integer>(null, null);
}
@Override
public void addTableCellListener(final ITableCellListener listener) {
tableCellObservable.addTableCellListener(listener);
}
@Override
public void removeTableCellListener(final ITableCellListener listener) {
tableCellObservable.removeTableCellListener(listener);
}
@Override
public void addTableCellPopupDetectionListener(final ITableCellPopupDetectionListener listener) {
tableCellPopupDetectionObservable.addTableCellPopupDetectionListener(listener);
}
@Override
public void removeTableCellPopupDetectionListener(final ITableCellPopupDetectionListener listener) {
tableCellPopupDetectionObservable.removeTableCellPopupDetectionListener(listener);
}
@Override
public void addTableColumnPopupDetectionListener(final ITableColumnPopupDetectionListener listener) {
tableColumnPopupDetectionObservable.addTableColumnPopupDetectionListener(listener);
}
@Override
public void removeTableColumnPopupDetectionListener(final ITableColumnPopupDetectionListener listener) {
tableColumnPopupDetectionObservable.addTableColumnPopupDetectionListener(listener);
}
@Override
public void addTableColumnListener(final ITableColumnListener listener) {
tableColumnObservable.addTableColumnListener(listener);
}
@Override
public void removeTableColumnListener(final ITableColumnListener listener) {
tableColumnObservable.removeTableColumnListener(listener);
}
@Override
public void addTableSelectionListener(final ITableSelectionListener listener) {
tableSelectionObservable.addTableSelectionListener(listener);
}
@Override
public void removeTableSelectionListener(final ITableSelectionListener listener) {
tableSelectionObservable.removeTableSelectionListener(listener);
}
private CellIndices getExternalCellIndices(final Point point) {
final TableItem item = table.getItem(point);
if (item != null) {
for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) {
final int internalIndex = columnIndex + 1;
final Rectangle rect = item.getBounds(internalIndex);
if (rect.contains(point)) {
final int rowIndex = table.indexOf(item);
if (rowIndex != -1) {
return new CellIndices(rowIndex, columnIndex);
}
}
}
}
return null;
}
private int getColumnIndex(final TableColumn columnOfInterest) {
if (columnOfInterest != null) {
final TableColumn[] columns = table.getColumns();
for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) {
final int internalIndex = columnIndex + 1;
if (columns[internalIndex] == columnOfInterest) {
return columnIndex;
}
}
}
return -1;
}
private boolean isSelectionEqualWithView(final List<Integer> selection) {
final int[] tableSelection = table.getSelectionIndices();
if (selection == null && (tableSelection == null || tableSelection.length == 0)) {
return true;
}
if (selection.size() != tableSelection.length) {
return false;
}
for (final int tablesSelected : tableSelection) {
if (!selection.contains(Integer.valueOf(tablesSelected))) {
return false;
}
}
return true;
}
private void showToolTip(final String message) {
toolTip.setMessage(message);
final Point location = Display.getCurrent().getCursorLocation();
toolTip.setLocation(location.x + 16, location.y + 16);
toolTip.setVisible(true);
}
private void fireSelectionChangedIfNeccessary() {
if (!NullCompatibleEquivalence.equals(lastSelection, table.getSelectionIndices())) {
lastSelection = table.getSelectionIndices();
tableSelectionObservable.fireSelectionChanged();
}
}
@Override
public boolean editCell(final int row, final int column) {
if (editor != null && this.editRowIndex != row || editColumnIndex != column) {
stopEditing();
this.editRowIndex = row;
this.editColumnIndex = column;
editTableCell = dataModel.getCell(editRowIndex, editColumnIndex);
activateEditorFromFactory();
}
return isEditing();
}
@Override
public void stopEditing() {
if (tableCellEditor != null) {
tableCellEditor.stopEditing(editTableCell, editRowIndex, editColumnIndex);
disposeEditor();
table.forceFocus();
}
}
@Override
public void cancelEditing() {
if (tableCellEditor != null) {
tableCellEditor.cancelEditing(editTableCell, editRowIndex, editColumnIndex);
disposeEditor();
table.forceFocus();
}
}
private void disposeEditor() {
((Control) tableCellEditor.getUiReference()).dispose();
tableCellEditor = null;
editTableCell = null;
editRowIndex = -1;
editColumnIndex = -1;
editor.setEditor(null);
editor.layout();
stopEditTimestamp = System.currentTimeMillis();
}
@Override
public boolean isEditing() {
return tableCellEditor != null;
}
private void activateEditorFromFactory() {
tableCellEditor = editorFactory.create(editTableCell, editRowIndex, editColumnIndex, editorCustomWidgetFactory);
if (tableCellEditor == null) {
return;
}
final int internalIndex = editColumnIndex + 1;
final Control uiReference = (Control) tableCellEditor.getUiReference();
tableCellEditor.startEditing(editTableCell, editRowIndex, editColumnIndex);
editor.setEditor(uiReference, table.getItem(editRowIndex), internalIndex);
Display.getCurrent().asyncExec(new Runnable() {
@Override
public void run() {
if (isEditing()) {
editor.layout();
tableCellEditor.requestFocus();
}
}
});
final TraverseListener traverseListener = new TraverseListener() {
@Override
public void keyTraversed(final TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_TAB_NEXT) {
e.doit = false;
}
else if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
e.doit = false;
}
}
};
ListenerUtil.addRecursiveTraverseListener(uiReference, traverseListener);
tableCellEditor.addKeyListener(new IKeyListener() {
@Override
public void keyReleased(final IKeyEvent event) {}
@Override
public void keyPressed(final IKeyEvent event) {
final boolean ctrl = event.getModifier().contains(Modifier.CTRL);
final boolean shift = event.getModifier().contains(Modifier.SHIFT);
final boolean enter = VirtualKey.ENTER.equals(event.getVirtualKey());
final boolean esc = VirtualKey.ESC.equals(event.getVirtualKey());
boolean left = VirtualKey.ARROW_LEFT.equals(event.getVirtualKey()) && shift;
left = left || (VirtualKey.TAB.equals(event.getVirtualKey()) && shift);
boolean right = VirtualKey.ARROW_RIGHT.equals(event.getVirtualKey()) && shift;
right = right || (VirtualKey.TAB.equals(event.getVirtualKey()) && !shift);
final boolean up = VirtualKey.ARROW_UP.equals(event.getVirtualKey()) && shift;
final boolean down = VirtualKey.ARROW_DOWN.equals(event.getVirtualKey()) && shift;
if (enter && ctrl) {
stopEditing();
}
if (enter) {
navigateDownLeft();
}
else if (esc) {
cancelEditing();
}
else if (right) {
navigateRight();
}
else if (left) {
navigateLeft();
}
else if (up) {
navigateUp();
}
else if (down) {
navigateDown();
}
}
});
}
private boolean navigateRight() {
if (isEditing()) {
return navigateRight(editRowIndex, editRowIndex, convertColumnIndexToView(editColumnIndex));
}
else {
return false;
}
}
private boolean navigateDown() {
if (isEditing()) {
return navigateDown(editRowIndex, editRowIndex, convertColumnIndexToView(editColumnIndex));
}
else {
return false;
}
}
private boolean navigateLeft() {
if (isEditing()) {
return navigateLeft(editRowIndex, editRowIndex, convertColumnIndexToView(editColumnIndex));
}
else {
return false;
}
}
private boolean navigateUp() {
if (isEditing()) {
return navigateUp(editRowIndex, editRowIndex, convertColumnIndexToView(editColumnIndex));
}
else {
return false;
}
}
private boolean navigateDownLeft() {
if (isEditing()) {
return navigateDown(editRowIndex, editRowIndex, 0);
}
else {
return false;
}
}
private boolean navigateRight(final int startRow, final int row, final int viewColumnIndex) {
if (viewColumnIndex + 1 < columnModel.getColumnCount()) {
if (editCell(row, convertColumnIndexToModel(viewColumnIndex + 1))) {
table.showColumn(table.getColumn(editor.getColumn()));
editor.layout();
return true;
}
else {
return navigateRight(startRow, row, viewColumnIndex + 1);
}
}
else if (row - startRow < 2) {
return navigateDown(startRow, row, 0);
}
else {
return false;
}
}
private boolean navigateDown(final int startRow, final int row, final int viewColumnIndex) {
if (dataModel.getRowCount() > row + 1) {
if (editCell(row + 1, convertColumnIndexToModel(viewColumnIndex))) {
setSelection(Collections.singletonList(Integer.valueOf(row + 1)));
table.showSelection();
table.showColumn(table.getColumn(editor.getColumn()));
editor.layout();
return true;
}
else if (row - startRow < 2) {
setSelection(Collections.singletonList(Integer.valueOf(row + 1)));
table.showSelection();
editor.layout();
return navigateRight(startRow, row + 1, viewColumnIndex);
}
else {
return false;
}
}
else {
return false;
}
}
private boolean navigateLeft(final int startRow, final int row, final int viewColumnIndex) {
if (viewColumnIndex > 0) {
if (editCell(row, convertColumnIndexToModel(viewColumnIndex - 1))) {
table.showColumn(table.getColumn(editor.getColumn()));
editor.layout();
return true;
}
else {
return navigateLeft(startRow, row, viewColumnIndex - 1);
}
}
else if (startRow - row < 2) {
return navigateUp(startRow, row, columnModel.getColumnCount() - 1);
}
else {
return false;
}
}
private boolean navigateUp(final int startRow, final int row, final int viewColumnIndex) {
if (row > 0) {
if (editCell(row - 1, convertColumnIndexToModel(viewColumnIndex))) {
setSelection(Collections.singletonList(Integer.valueOf(row - 1)));
table.showSelection();
table.showColumn(table.getColumn(editor.getColumn()));
editor.layout();
return true;
}
else if (startRow - row < 2) {
setSelection(Collections.singletonList(Integer.valueOf(row - 1)));
table.showSelection();
table.showColumn(table.getColumn(editor.getColumn()));
editor.layout();
return navigateLeft(startRow, row - 1, viewColumnIndex);
}
else {
return false;
}
}
else {
return false;
}
}
private int convertColumnIndexToView(final int modelIndex) {
final ArrayList<Integer> permutation = getColumnPermutation();
return permutation.indexOf(Integer.valueOf(modelIndex));
}
private int convertColumnIndexToModel(final int viewIndex) {
return getColumnPermutation().get(viewIndex).intValue();
}
private static int getStyle(final ITableSetupSpi setup) {
int result = SWT.VIRTUAL;
if (setup.hasBorder()) {
result = result | SWT.BORDER;
}
if (TableSelectionPolicy.MULTI_ROW_SELECTION == setup.getSelectionPolicy()) {
result = result | SWT.FULL_SELECTION | SWT.MULTI;
}
else if (TableSelectionPolicy.SINGLE_ROW_SELECTION == setup.getSelectionPolicy()) {
result = result | SWT.FULL_SELECTION;
}
else if (TableSelectionPolicy.NO_SELECTION == setup.getSelectionPolicy()) {
result = result | SWT.HIDE_SELECTION;
}
else {
throw new IllegalArgumentException("SelectionPolicy '" + setup.getSelectionPolicy() + "' is not known");
}
return result;
}
private final class EditorCustomWidgetFactory implements ICustomWidgetFactory {
@Override
public <WIDGET_TYPE extends IControlCommon> WIDGET_TYPE create(
final IWidgetDescriptor<? extends WIDGET_TYPE> descriptor) {
return factory.create(table, descriptor);
}
}
private final class EraseItemListener implements Listener {
@Override
public void handleEvent(final Event event) {
final GC gc = event.gc;
final Color oldBackground = gc.getBackground();
final TableItem item = (TableItem) event.item;
final int viewColumnIndex = event.index;
final int rowIndex = table.indexOf(item);
if (rowIndex < 0 || viewColumnIndex < 1) {
return;
}
final int modelColumnIndex = convertColumnIndexToModel(viewColumnIndex - 1);
final ITableCell cell = dataModel.getCell(rowIndex, modelColumnIndex);
final IColorConstant foreground = cell.getForegroundColor();
final IColorConstant background = cell.getBackgroundColor();
IColorConstant selectedForeground = cell.getSelectedForegroundColor();
IColorConstant selectedBackground = cell.getSelectedBackgroundColor();
if (selectedForeground == null) {
selectedForeground = selectedForegroundColor;
}
if (selectedBackground == null) {
selectedBackground = selectedBackgroundColor;
}
if ((selectedForeground != null || selectedBackground != null) && (event.detail & SWT.SELECTED) != 0) {
final Rectangle rect = item.getBounds(viewColumnIndex);
if (selectedForeground != null) {
gc.setForeground(ColorCache.getInstance().getColor(selectedForeground));
}
if (selectedBackground != null) {
gc.setBackground(ColorCache.getInstance().getColor(selectedBackground));
}
gc.fillRectangle(rect);
event.detail &= ~SWT.SELECTED;
return;
}
else if ((selectedForeground != null || selectedBackground != null) && (event.detail & SWT.HOT) != 0) {
final Rectangle rect = item.getBounds(viewColumnIndex);
if (foreground != null) {
gc.setForeground(ColorCache.getInstance().getColor(foreground));
}
if (background != null) {
gc.setBackground(ColorCache.getInstance().getColor(background));
}
gc.fillRectangle(rect);
event.detail &= ~SWT.HOT;
return;
}
//This fixes Bug 50163 by using workaround from comment 13
//(Visited url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=50163)
final Color backgroundColor = item.getBackground(viewColumnIndex);
if (backgroundColor != null) {
gc.setBackground(backgroundColor);
final Rectangle bounds = item.getBounds(viewColumnIndex);
gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
gc.setBackground(oldBackground);
}
}
}
private final class DataListener implements Listener {
@Override
public void handleEvent(final Event event) {
final TableItem item = (TableItem) event.item;
final int rowIndex = event.index;
for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) {
final int internalIndex = columnIndex + 1;
if (dataModel.getRowCount() > rowIndex) {
final ITableCell cell = dataModel.getCell(rowIndex, columnIndex);
final String text = cell.getText();
final IImageConstant icon = cell.getIcon();
final IColorConstant backgroundColor = cell.getBackgroundColor();
final IColorConstant foregroundColor = cell.getForegroundColor();
final Markup markup = cell.getMarkup();
if (text != null) {
item.setText(internalIndex, text);
}
else {
item.setText(internalIndex, "");
}
if (icon != null) {
item.setImage(internalIndex, imageRegistry.getImage(icon));
}
if (markup != null) {
final Font newFont = FontProvider.deriveFont(item.getFont(), markup);
item.setFont(internalIndex, newFont);
}
if (backgroundColor != null) {
item.setBackground(internalIndex, ColorCache.getInstance().getColor(backgroundColor));
}
if (foregroundColor != null) {
item.setForeground(internalIndex, ColorCache.getInstance().getColor(foregroundColor));
}
}
}
}
}
private final class TableCellListener extends MouseAdapter {
@Override
public void mouseUp(final MouseEvent e) {
final ITableCellMouseEvent mouseEvent = getMouseEvent(e, 1);
if (mouseEvent != null) {
tableCellObservable.fireMouseReleased(mouseEvent);
}
}
@Override
public void mouseDown(final MouseEvent e) {
final ITableCellMouseEvent mouseEvent = getMouseEvent(e, 1);
if (mouseEvent != null) {
tableCellObservable.fireMousePressed(mouseEvent);
}
}
@Override
public void mouseDoubleClick(final MouseEvent e) {
final ITableCellMouseEvent mouseEvent = getMouseEvent(e, 2);
if (mouseEvent != null) {
tableCellObservable.fireMouseDoubleClicked(mouseEvent);
}
}
private ITableCellMouseEvent getMouseEvent(final MouseEvent event, final int maxCount) {
try {
if (event.count > maxCount) {
return null;
}
}
catch (final NoSuchFieldError e) {
//RWT doesn't support count field :-(
//so the mouse down and mouse up may be fired twice at double clicks :-(
}
final MouseButton mouseButton = MouseUtil.getMouseButton(event);
if (mouseButton == null) {
return null;
}
final Point point = new Point(event.x, event.y);
final CellIndices indices = getExternalCellIndices(point);
if (indices != null) {
return new TableCellMouseEvent(
indices.getRowIndex(),
indices.getColumnIndex(),
mouseButton,
MouseUtil.getModifier(event.stateMask));
}
return null;
}
}
private final class TableEditListener extends MouseAdapter {
private Point lastPoint;
@Override
public void mouseUp(final MouseEvent e) {
if (((e.stateMask & SWT.BUTTON1) != 0) && editorFactory != null) {
final Point point = new Point(e.x, e.y);
if (!point.equals(lastPoint)) {
final CellIndices indices = getExternalCellIndices(point);
if (indices != null) {
final ITableCell cell = dataModel.getCell(indices.getRowIndex(), indices.getColumnIndex());
if (cell.isEditable() && editable && editor != null) {
if (EditActivation.SINGLE_CLICK.equals(editorFactory.getActivation(
cell,
indices.getRowIndex(),
indices.getColumnIndex(),
isEditing(),
stopEditTimestamp))) {
lastPoint = point;
startEdit(indices);
}
}
}
}
}
}
@Override
public void mouseDoubleClick(final MouseEvent e) {
final Point point = new Point(e.x, e.y);
if (!point.equals(lastPoint)) {
final CellIndices indices = getExternalCellIndices(point);
if (indices != null) {
final ITableCell cell = dataModel.getCell(indices.getRowIndex(), indices.getColumnIndex());
if (cell.isEditable() && editable && editor != null) {
if (editorFactory != null) {
if (EditActivation.DOUBLE_CLICK.equals(editorFactory.getActivation(
cell,
indices.getRowIndex(),
indices.getColumnIndex(),
isEditing(),
stopEditTimestamp))) {
lastPoint = point;
startEdit(indices);
}
}
else {
startEdit(indices);
}
}
}
}
}
private void startEdit(final CellIndices indices) {
if (editorFactory != null) {
editCell(indices.getRowIndex(), indices.getColumnIndex());
}
}
}
private final class TableMenuDetectListener implements MenuDetectListener {
@Override
public void menuDetected(final MenuDetectEvent e) {
//if a drag source is installed, selection event fires after menu detect but selection
//should always change before menu detection
fireSelectionChangedIfNeccessary();
//stop editing before popup opens
stopEditing();
// calculate position manually due to different behavior of Windows and Linux
final Rectangle tableBounds = table.getBounds();
final Point tableOrigin = table.getParent().toDisplay(new Point(tableBounds.x, tableBounds.y));
final Point tableEventPoint = new Point(e.x - tableOrigin.x, e.y - tableOrigin.y);
Point point = new Point(e.x, e.y);
point = table.toControl(point);
final Position position = new Position(point.x, point.y);
TableItem item = table.getItem(point);
//Menu detect on table cell
if (item != null && tableEventPoint.y > table.getHeaderHeight()) {
for (int colIndex = 0; colIndex < getColumnCount(); colIndex++) {
final int internalColIndex = colIndex + 1;
final Rectangle rect = item.getBounds(internalColIndex);
if (rect.contains(point)) {
final int rowIndex = table.indexOf(item);
if (rowIndex != -1) {
tableCellPopupDetectionObservable
.firePopupDetected(new TableCellPopupEvent(rowIndex, colIndex, position));
}
}
}
}
//Menu detect on header. Table has some item(s)
else if (table.getItemCount() > 0 && tableEventPoint.y < table.getHeaderHeight()) {
item = table.getItem(0);
fireColumnPopupDetected(item, point, position);
}
//Menu detect on header but table has no item.
//Just temporarily add an item to the table an remove it, after
//position was calculated.
else if (tableEventPoint.y < table.getHeaderHeight()) {
table.setRedraw(false);
table.removeListener(SWT.SetData, dataListener);
final TableItem dummyItem = new TableItem(table, SWT.NONE);
fireColumnPopupDetected(dummyItem, point, position);
dummyItem.dispose();
table.addListener(SWT.SetData, dataListener);
table.setRedraw(true);
}
else {
getPopupDetectionObservable().firePopupDetected(position);
}
}
private void fireColumnPopupDetected(final TableItem item, final Point point, final Position position) {
for (int colIndex = 0; colIndex < getColumnCount(); colIndex++) {
final int internalIndex = colIndex + 1;
final Rectangle rect = item.getBounds(internalIndex);
if (rect.x <= point.x && point.x <= rect.x + rect.width) {
tableColumnPopupDetectionObservable.firePopupDetected(new TableColumnPopupEvent(colIndex, position));
}
}
}
}
private final class ColumnSelectionListener extends SelectionAdapter {
@Override
public void widgetSelected(final SelectionEvent e) {
final TableColumn column = (TableColumn) e.widget;
if (column != null) {
final int columnIndex = getColumnIndex(column);
final Set<Modifier> modifier = MouseUtil.getModifier(e.stateMask);
tableColumnObservable.fireMouseClicked(new TableColumnMouseEvent(columnIndex, modifier));
}
}
}
private final class ColumnControlListener implements ControlListener {
private long lastResizeTime = 0;
@Override
public void controlResized(final ControlEvent e) {
final TableColumn column = (TableColumn) e.widget;
final long time = getTime(e);
if (time != -1) {
lastResizeTime = time;
}
if (column != null) {
final int columnIndex = getColumnIndex(column);
final int width = column.getWidth();
setWidthInvokedOnModel = true;
columnModel.getColumn(columnIndex).setWidth(width);
setWidthInvokedOnModel = false;
tableColumnObservable.fireColumnResized(new TableColumnResizeEvent(columnIndex, width));
}
}
@Override
public void controlMoved(final ControlEvent e) {
if (getTime(e) != lastResizeTime) {
final int[] columnOrder = table.getColumnOrder();
if (lastColumnOrder == null || !Arrays.equals(columnOrder, lastColumnOrder)) {
lastColumnOrder = columnOrder;
tableColumnObservable.fireColumnPermutationChanged();
}
}
}
private long getTime(final ControlEvent event) {
try {
return event.time;
}
catch (final NoSuchFieldError e) {
//RWT doesn't support time field :-(
}
return -1;
}
}
private final class TableSelectionListener extends SelectionAdapter {
@Override
public void widgetSelected(final SelectionEvent e) {
fireSelectionChangedIfNeccessary();
}
}
private final class TableModelListener implements ITableDataModelListener {
@Override
public void rowsAdded(final int[] rowIndices) {
updateRows(rowIndices);
}
@Override
public void rowsRemoved(final int[] rowIndices) {
updateRows(rowIndices);
}
@Override
public void rowsChanged(final int[] rowIndices) {
table.clear(rowIndices);
}
@Override
public void dataChanged() {
table.clearAll();
table.setItemCount(dataModel.getRowCount());
//Do redraw after invoke of virtual table clearAll(),
//see Bug: 202237 https://bugs.eclipse.org/bugs/show_bug.cgi?id=202237
table.redraw();
}
@Override
public void selectionChanged() {
setSelection(dataModel.getSelection());
}
private void updateRows(final int[] rowIndices) {
if (rowIndices != null && rowIndices.length > 0) {
table.clear(ArrayUtils.getMin(rowIndices), dataModel.getRowCount() - 1);
table.setItemCount(dataModel.getRowCount());
//Do redraw after invoke of virtual table clearAll(),
//see Bug: 202237 https://bugs.eclipse.org/bugs/show_bug.cgi?id=202237
table.redraw();
}
}
}
private final class TableColumnModelListener implements ITableColumnModelListener {
@Override
public void columnsAdded(final int[] columnIndices) {
stopEditing();
table.setRedraw(false);
table.clearAll();
Arrays.sort(columnIndices);
for (int i = 0; i < columnIndices.length; i++) {
final int addedIndex = columnIndices[i];
addColumn(addedIndex, columnModel.getColumn(addedIndex));
addColumnListener(addedIndex);
}
table.setRedraw(true);
}
@Override
public void columnsRemoved(final int[] columnIndices) {
stopEditing();
table.setRedraw(false);
table.clearAll();
Arrays.sort(columnIndices);
int removedColumnsCount = 0;
for (int i = 0; i < columnIndices.length; i++) {
final int removedIndex = columnIndices[i] - removedColumnsCount;
removeColumnListener(removedIndex);
removeColumn(removedIndex);
removedColumnsCount++;
}
table.setRedraw(true);
}
@Override
public void columnsChanged(final int[] columnIndices) {
stopEditing();
if (!setWidthInvokedOnModel) {
final TableColumn[] columns = table.getColumns();
for (final int changedIndex : columnIndices) {
final int internalIndex = changedIndex + 1;
setColumnData(columns[internalIndex], columnModel.getColumn(changedIndex));
}
}
}
}
private final class CellIndices {
private final int rowIndex;
private final int columnIndex;
private CellIndices(final int rowIndex, final int columnIndex) {
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
}
private int getRowIndex() {
return rowIndex;
}
private int getColumnIndex() {
return columnIndex;
}
@Override
public String toString() {
return "CellIndices [rowIndex=" + rowIndex + ", columnIndex=" + columnIndex + "]";
}
}
private final class ToolTipListener implements Listener {
@Override
public void handleEvent(final Event event) {
if (event.type == SWT.MouseHover) {
final CellIndices indices = getExternalCellIndices(new Point(event.x, event.y));
if (indices != null) {
toolTip.setVisible(false);
final ITableCell cell = dataModel.getCell(indices.rowIndex, indices.columnIndex);
if (cell.getToolTipText() != null) {
showToolTip(cell.getToolTipText());
}
}
}
else {
if (toolTip != null && !toolTip.isDisposed()) {
toolTip.setVisible(false);
}
}
}
}
}