/* * Copyright (c) 2016, grossmann * 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.nattable.impl.plugin; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.eclipse.nebula.widgets.nattable.NatTable; import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand; import org.eclipse.nebula.widgets.nattable.grid.GridRegion; import org.eclipse.nebula.widgets.nattable.layer.DataLayer; import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; import org.eclipse.nebula.widgets.nattable.layer.LabelStack; import org.eclipse.nebula.widgets.nattable.layer.cell.AggregateConfigLabelAccumulator; import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; import org.eclipse.nebula.widgets.nattable.painter.layer.NatGridLayerPainter; import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer; import org.eclipse.nebula.widgets.nattable.reorder.event.ColumnReorderEvent; import org.eclipse.nebula.widgets.nattable.resize.command.InitializeAutoResizeColumnsCommand; import org.eclipse.nebula.widgets.nattable.resize.event.ColumnResizeEvent; import org.eclipse.nebula.widgets.nattable.selection.IRowSelectionModel; import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent; import org.eclipse.nebula.widgets.nattable.ui.action.IMouseAction; import org.eclipse.nebula.widgets.nattable.ui.matcher.IMouseEventMatcher; import org.eclipse.nebula.widgets.nattable.util.GCFactory; import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; import org.eclipse.swt.SWT; 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.MouseMoveListener; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; 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.ITableDataModel; import org.jowidgets.common.model.ITableDataModelListener; import org.jowidgets.common.model.ITableDataModelObservable; import org.jowidgets.common.types.Dimension; 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.widgets.IControlCommon; 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.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.nattable.impl.plugin.configuration.JoNatTableConfigurator; import org.jowidgets.nattable.impl.plugin.layer.JoColumnReorderLayer; import org.jowidgets.nattable.impl.plugin.layer.NatTableLayers; import org.jowidgets.nattable.impl.plugin.listener.CellSelectionListener; import org.jowidgets.nattable.impl.plugin.painter.ClickedColumnConfigLabelAccumulator; import org.jowidgets.nattable.impl.plugin.painter.HoveredColumnConfigLabelAccumulator; import org.jowidgets.spi.dnd.IDragSourceSpi; 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.dnd.SwtDragSource; import org.jowidgets.spi.impl.swt.common.image.SwtImageRegistry; import org.jowidgets.spi.impl.swt.common.util.MouseUtil; import org.jowidgets.spi.impl.swt.common.widgets.SwtControl; import org.jowidgets.spi.widgets.ITableSpi; import org.jowidgets.spi.widgets.setup.ITableSetupSpi; import org.jowidgets.util.EmptyCheck; import org.jowidgets.util.Interval; import org.jowidgets.util.NullCompatibleEquivalence; class NatTableImplSpi extends SwtControl implements ITableSpi { private final NatTable table; private final NatTableLayers tableLayers; private final IRowSelectionModel<Integer> rowSelectionModel; private final ITableDataModel dataModel; private final ITableColumnModelSpi columnModel; private final IGenericWidgetFactory widgetFactory; private final IDragSourceSpi dragSource; private final TableCellObservable tableCellObservable; private final TableCellPopupDetectionObservable tableCellPopupDetectionObservable; private final TableColumnPopupDetectionObservable tableColumnPopupDetectionObservable; private final TableColumnObservable tableColumnObservable; private final TableSelectionObservable tableSelectionObservable; private final TableModelListener tableModelListener; private final TableColumnModelListener tableColumnModelListener; private final ColumnLayerListener columnLayerListener; private final TableSelectionListener tableSelectionListener; private final HoveredColumnConfigLabelAccumulator hoveredColumnLabelAccumulator; private final ClickedColumnConfigLabelAccumulator clickedColumnLabelAccumulator; //ignore and use later for cell editing support (finals) -> begin @SuppressWarnings("unused") private final ITableCellEditorFactory<? extends ITableCellEditor> editorFactory; @SuppressWarnings("unused") private final ICustomWidgetFactory editorCustomWidgetFactory; // <-end ignore and use later for cell editing support //ignore and use later for cell editing support (mutables) -> begin @SuppressWarnings("unused") private boolean editable; @SuppressWarnings("unused") private ITableCellEditor tableCellEditor; @SuppressWarnings("unused") private ITableCell editTableCell; @SuppressWarnings("unused") private final int editRowIndex; @SuppressWarnings("unused") private final int editColumnIndex; @SuppressWarnings("unused") private long stopEditTimestamp; // <-end ignore and use later for cell editing support private boolean setWidthInvokedOnModel; private ArrayList<Integer> lastSelection; NatTableImplSpi( final NatTableLayers tableLayers, final IGenericWidgetFactory widgetFactory, final Object parentUiReference, final ITableSetupSpi setup, final SwtImageRegistry imageRegistry) { super(new NatTable((Composite) parentUiReference, getStyle(setup), tableLayers.getGridLayer()), imageRegistry); this.table = getUiReference(); this.tableLayers = tableLayers; this.rowSelectionModel = tableLayers.getSelectionModel(); this.widgetFactory = widgetFactory; this.tableCellObservable = new TableCellObservable(); this.tableCellPopupDetectionObservable = new TableCellPopupDetectionObservable(); this.tableColumnPopupDetectionObservable = new TableColumnPopupDetectionObservable(); this.tableColumnObservable = new TableColumnObservable(); this.tableSelectionObservable = new TableSelectionObservable(); this.tableModelListener = new TableModelListener(); this.tableColumnModelListener = new TableColumnModelListener(); this.columnLayerListener = new ColumnLayerListener(); this.tableSelectionListener = new TableSelectionListener(); this.hoveredColumnLabelAccumulator = new HoveredColumnConfigLabelAccumulator(); this.clickedColumnLabelAccumulator = new ClickedColumnConfigLabelAccumulator(); this.editorCustomWidgetFactory = new EditorCustomWidgetFactory(); this.dataModel = setup.getDataModel(); this.columnModel = setup.getColumnModel(); this.editorFactory = setup.getEditor(); this.editable = true; this.editRowIndex = -1; this.editColumnIndex = -1; final SwtDragSource swtDragSource = (SwtDragSource) super.getDragSource(); this.dragSource = new NatTableDragSource(swtDragSource, this); setMenuDetectListener(new TableMenuDetectListener()); table.addMouseListener(new CellSelectionListener(table, swtDragSource, rowSelectionModel)); table.addMouseListener(new TableCellMouseListener()); table.addMouseMoveListener(new HeaderHoverMouseMoveListener()); table.addMouseTrackListener(new HeaderHoverAndClickMouseTrackAdapter()); table.addMouseListener(new HeaderMouseClickModeMouseListener()); table.getUiBindingRegistry().registerSingleClickBinding(new TableHeaderMouseEventMatcher(), new TableHeaderClickAction()); table.addDisposeListener(new NatTableDisposeListener()); tableLayers.getSelectionLayer().addLayerListener(tableSelectionListener); tableLayers.getColumnReorderLayer().addLayerListener(columnLayerListener); final AggregateConfigLabelAccumulator columnLabelAccumulator = new AggregateConfigLabelAccumulator(); columnLabelAccumulator.add(hoveredColumnLabelAccumulator, clickedColumnLabelAccumulator); tableLayers.getColumnHeaderLayer().setConfigLabelAccumulator(columnLabelAccumulator); JoNatTableConfigurator.configureNatTable(table, setup.getColumnModel(), imageRegistry); JoNatTableConfigurator.registerUiBindingsToNatTable(table, hoveredColumnLabelAccumulator); final NatTableTooltip tooltip = new NatTableTooltip( table, dataModel, columnModel, GridRegion.COLUMN_HEADER, GridRegion.BODY); table.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(final DisposeEvent e) { tooltip.dispose(); } }); } private static int getStyle(final ITableSetupSpi setup) { int result = SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.V_SCROLL | SWT.H_SCROLL; if (setup.hasBorder()) { result = result | SWT.BORDER; } return result; } @Override public NatTable getUiReference() { return (NatTable) super.getUiReference(); } @Override public IDragSourceSpi getDragSource() { return dragSource; } @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); } @Override public void setRowHeight(final int height) { tableLayers.getDataLayer().setDefaultRowHeight(height); final NatGridLayerPainter gridPainter = (NatGridLayerPainter) table.getLayerPainter(); gridPainter.setDefaultRowHeight(height); table.refresh(false); } @Override public void setEditable(final boolean editable) { this.editable = editable; } @Override public void setEnabled(final boolean enabled) { if (isEditing() && !enabled) { stopEditing(); } super.setEnabled(enabled); } @Override public Dimension getMinSize() { return new Dimension(40, 40); } @Override public void resetFromModel() { final ITableDataModelObservable dataModelObservable = dataModel.getTableDataModelObservable(); if (dataModelObservable != null) { dataModelObservable.removeDataModelListener(tableModelListener); } final ITableColumnModelObservable columnModelObservable = columnModel.getTableColumnModelObservable(); if (columnModelObservable != null) { columnModelObservable.removeColumnModelListener(tableColumnModelListener); } setColumnWidthFromModel(); setSelection(dataModel.getSelection()); if (dataModelObservable != null) { dataModelObservable.addDataModelListener(tableModelListener); } if (columnModelObservable != null) { columnModelObservable.addColumnModelListener(tableColumnModelListener); } table.refresh(false); } private void setColumnWidthFromModel() { for (int columnIndex = 0; columnIndex < columnModel.getColumnCount(); columnIndex++) { setColumnWidthFromModel(columnIndex); } } private void setColumnWidthFromModel(final int columnIndex) { final DataLayer dataLayer = tableLayers.getDataLayer(); final int width = columnModel.getColumn(columnIndex).getWidth(); if (width >= 0) { //the data layer is not reordered so the index must be used here instead //of position (index and position are the same in this layer) if (width != dataLayer.getColumnWidthByPosition(columnIndex)) { dataLayer.setColumnWidthByPosition(columnIndex, width); } } } @Override public Position getCellPosition(final int rowIndex, final int columnIndex) { final Rectangle rectangle = table.getBoundsByPosition(columnIndex, rowIndex); if (rectangle != null) { return new Position(rectangle.x, rectangle.y); } else { return null; } } @Override public Dimension getCellSize(final int rowIndex, final int columnIndex) { final Rectangle rectangle = table.getBoundsByPosition(columnIndex, rowIndex); if (rectangle != null) { return new Dimension(rectangle.width, rectangle.height); } else { return null; } } @Override public int getColumnAtPosition(final Position position) { final Point point = new Point(position.getX(), position.getY()); final int columnPositionByX = table.getColumnPositionByX(point.x); final int columnIndex = table.getColumnIndexByPosition(columnPositionByX); if (columnIndex >= 0) { return columnIndex; } else { return -1; } } @Override public int getRowAtPosition(final Position position) { final Point point = new Point(position.getX(), position.getY()); final int rowPositionByY = table.getRowPositionByY(point.y); if (rowPositionByY <= 0) { return -1; } else { return table.getRowIndexByPosition(rowPositionByY); } } @Override public void pack(final TablePackPolicy policy) { //TODO consider TablePackPolicy for (int columnPosition = 0; columnPosition < table.getColumnCount(); columnPosition++) { pack(columnPosition); } } @Override public void pack(final int columnIndex, final TablePackPolicy policy) { //TODO consider TablePackPolicy pack(tableLayers.getColumnReorderLayer().getColumnPositionByIndex(columnIndex)); } private void pack(final int columnPosition) { final InitializeAutoResizeColumnsCommand command = new InitializeAutoResizeColumnsCommand( table, columnPosition, table.getConfigRegistry(), new GCFactory(table)); table.doCommand(command); } @Override public ArrayList<Integer> getColumnPermutation() { final ColumnReorderLayer reorderLayer = tableLayers.getColumnReorderLayer(); final ArrayList<Integer> result = new ArrayList<Integer>(); for (int position = 0; position < table.getColumnCount(); position++) { result.add(Integer.valueOf(reorderLayer.getColumnIndexByPosition(position))); } return result; } @Override public void setColumnPermutation(final List<Integer> permutation) { final JoColumnReorderLayer reorderLayer = tableLayers.getColumnReorderLayer(); reorderLayer.setColumnIndexOrder(permutation); } @Override public ArrayList<Integer> getSelection() { if (!EmptyCheck.isEmpty(lastSelection)) { return lastSelection; } else { return new ArrayList<Integer>(); } } @Override public void setSelection(final List<Integer> selection) { if (!NullCompatibleEquivalence.equals(selection, lastSelection)) { tableLayers.getSelectionLayer().removeLayerListener(tableSelectionListener); if (selection != null) { lastSelection = new ArrayList<Integer>(selection); rowSelectionModel.clearSelection(); for (final Integer selected : selection) { rowSelectionModel.addSelection(0, selected.intValue()); } } else { lastSelection = new ArrayList<Integer>(); rowSelectionModel.clearSelection(); } tableSelectionObservable.fireSelectionChanged(); tableLayers.getSelectionLayer().addLayerListener(tableSelectionListener); table.refresh(false); } } @Override public void scrollToRow(final int rowIndex) { final ViewportLayer viewPort = tableLayers.getViewPortLayer(); viewPort.moveRowPositionIntoViewport(rowIndex); } @Override public boolean isColumnPopupDetectionSupported() { return true; } @Override public Interval<Integer> getVisibleRows() { final ViewportLayer viewPort = tableLayers.getViewPortLayer(); final int originY = viewPort.getOrigin().getY(); final int firstRow = viewPort.getRowPositionByY(originY); final int lastRow = viewPort.getRowPositionByY(originY + viewPort.getClientAreaHeight()); return new Interval<Integer>(getWrapperInteger(firstRow), getWrapperInteger(lastRow)); } private Integer getWrapperInteger(final int value) { if (value != -1) { return Integer.valueOf(value); } else { return null; } } private CellIndices getExternalCellIndices(final Point point) { final int rowPositionByY = table.getRowPositionByY(point.y); if (rowPositionByY <= 0) { return null; } final int columnPositionByX = table.getColumnPositionByX(point.x); final int columnIndex = table.getColumnIndexByPosition(columnPositionByX); final int rowIndex = table.getRowIndexByPosition(rowPositionByY); if (columnIndex >= 0 && rowIndex >= 0) { return new CellIndices(rowIndex, columnIndex); } else { return null; } } private void setSelectionChangedIfNeccessary() { if (!NullCompatibleEquivalence.equals(lastSelection, rowSelectionModel.getSelectedRowObjects())) { lastSelection = new ArrayList<Integer>(rowSelectionModel.getSelectedRowObjects()); tableSelectionObservable.fireSelectionChanged(); } } @Override public boolean editCell(final int row, final int column) { //TODO must be implemented return false; } @Override public void stopEditing() { //TODO must be implemented } @Override public void cancelEditing() { //TODO must be implemented } @Override public boolean isEditing() { //TODO must be implemented return false; } private final class EditorCustomWidgetFactory implements ICustomWidgetFactory { @Override public <WIDGET_TYPE extends IControlCommon> WIDGET_TYPE create( final IWidgetDescriptor<? extends WIDGET_TYPE> descriptor) { return widgetFactory.create(table, descriptor); } } private final class HeaderHoverMouseMoveListener implements MouseMoveListener { @Override public void mouseMove(final MouseEvent event) { final int col = table.getColumnPositionByX(event.x); final int row = table.getRowPositionByY(event.y); final boolean button1Pressed = (event.stateMask & SWT.BUTTON1) != 0; final boolean hoverChanged; if (row == 0 && col >= 0 && !button1Pressed) {//do not show hovered state when resizing or clicking hoverChanged = hoveredColumnLabelAccumulator.setColumnIndex(col); } else if (!button1Pressed) { hoverChanged = hoveredColumnLabelAccumulator.clearColumnIndex(); } else { hoverChanged = false; } if (hoverChanged) { tableLayers.getColumnHeaderLayer().doCommand(new VisualRefreshCommand()); } } } private final class HeaderMouseClickModeMouseListener extends MouseAdapter { @Override public void mouseUp(final MouseEvent e) { final boolean changed = clickedColumnLabelAccumulator.clearColumnIndex(); if (changed) { tableLayers.getColumnHeaderLayer().doCommand(new VisualRefreshCommand()); } } @Override public void mouseDown(final MouseEvent event) { final int col = table.getColumnPositionByX(event.x); final int row = table.getRowPositionByY(event.y); final boolean changed; if (row == 0 && col >= 0 && event.button == 1 && table.getCursor() == null) { //Hack description for: "table.getCursor() == null" //->do not set click mode if resize cursor is shown changed = clickedColumnLabelAccumulator.setColumnIndex(col); } else { changed = clickedColumnLabelAccumulator.clearColumnIndex(); } if (changed) { tableLayers.getColumnHeaderLayer().doCommand(new VisualRefreshCommand()); } } } private final class HeaderHoverAndClickMouseTrackAdapter extends MouseTrackAdapter { @Override public void mouseExit(final MouseEvent e) { final boolean hoverChanged = hoveredColumnLabelAccumulator.clearColumnIndex(); final boolean clickChanged = clickedColumnLabelAccumulator.clearColumnIndex(); if (hoverChanged || clickChanged) { tableLayers.getColumnHeaderLayer().doCommand(new VisualRefreshCommand()); } } } private final class TableCellMouseListener 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 TableHeaderClickAction implements IMouseAction { @Override public void run(final NatTable natTable, final MouseEvent e) { if (e.button == 1) { final int columnPositionByX = table.getColumnPositionByX(e.x); final int columnIndex = table.getColumnIndexByPosition(columnPositionByX); final Set<Modifier> modifier = MouseUtil.getModifier(e.stateMask); tableColumnObservable.fireMouseClicked(new TableColumnMouseEvent(columnIndex, modifier)); } } } private final class TableHeaderMouseEventMatcher implements IMouseEventMatcher { @Override public boolean matches(final NatTable natTable, final MouseEvent event, final LabelStack regionLabels) { if (regionLabels != null && regionLabels.hasLabel(GridRegion.COLUMN_HEADER)) { return true; } else { return false; } } } 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 //Not sure if this is necessary for nattable but it does not harm anyway setSelectionChangedIfNeccessary(); //stop editing before popup opens stopEditing(); Point point = new Point(e.x, e.y); point = table.toControl(point); final Position position = new Position(point.x, point.y); final int rowPositionByY = table.getRowPositionByY(point.y); final int columnPositionByX = table.getColumnPositionByX(point.x); final int rowIndex = table.getRowIndexByPosition(rowPositionByY); final int columnIndex = table.getColumnIndexByPosition(columnPositionByX); if (rowPositionByY == 0) { tableColumnPopupDetectionObservable.firePopupDetected(new TableColumnPopupEvent(columnIndex, position)); } else { tableCellPopupDetectionObservable.firePopupDetected(new TableCellPopupEvent(rowIndex, columnIndex, position)); } } } private final class TableSelectionListener implements ILayerListener { @Override public void handleLayerEvent(final ILayerEvent event) { if (event instanceof CellSelectionEvent) { setSelectionChangedIfNeccessary(); } } } 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) { updateRows(rowIndices); } @Override public void dataChanged() { table.refresh(false); } @Override public void selectionChanged() { setSelection(dataModel.getSelection()); } private void updateRows(final int[] rowIndices) { table.refresh(false); } } private final class TableColumnModelListener implements ITableColumnModelListener { @Override public void columnsAdded(final int[] columnIndices) { stopEditing(); table.refresh(false); } @Override public void columnsRemoved(final int[] columnIndices) { stopEditing(); table.refresh(false); } @Override public void columnsChanged(final int[] columnIndices) { if (!setWidthInvokedOnModel) { stopEditing(); for (int i = 0; i < columnIndices.length; i++) { setColumnWidthFromModel(columnIndices[i]); } table.refresh(false); } } } private final class ColumnLayerListener implements ILayerListener { @Override public void handleLayerEvent(final ILayerEvent event) { if (event instanceof ColumnReorderEvent) { tableColumnObservable.fireColumnPermutationChanged(); } else if (event instanceof ColumnResizeEvent) { final ColumnReorderLayer reorderLayer = tableLayers.getColumnReorderLayer(); final int columnPosition = ((ColumnResizeEvent) event).getColumnPositionRanges().iterator().next().start; final int columnIndex = reorderLayer.getColumnIndexByPosition(columnPosition); final int width = reorderLayer.getColumnWidthByPosition(columnPosition); setWidthInvokedOnModel = true; columnModel.getColumn(columnIndex).setWidth(width); setWidthInvokedOnModel = false; final TableColumnResizeEvent joResizeEvent = new TableColumnResizeEvent(columnIndex, width); tableColumnObservable.fireColumnResized(joResizeEvent); } } } 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 NatTableDisposeListener implements DisposeListener { @Override public void widgetDisposed(final DisposeEvent event) { final ITableDataModelObservable dataModelObservable = dataModel.getTableDataModelObservable(); if (dataModelObservable != null) { dataModelObservable.removeDataModelListener(tableModelListener); } final ITableColumnModelObservable columnModelObservable = columnModel.getTableColumnModelObservable(); if (columnModelObservable != null) { columnModelObservable.removeColumnModelListener(tableColumnModelListener); } } } }