| /******************************************************************************* |
| * Copyright (c) 2016, 2020 Dirk Fauth. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.extension.e4.selection; |
| |
| import java.util.List; |
| |
| import javax.inject.Inject; |
| |
| import org.eclipse.e4.ui.workbench.modeling.ESelectionService; |
| import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider; |
| import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; |
| import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; |
| import org.eclipse.nebula.widgets.nattable.selection.RowSelectionModel; |
| import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; |
| import org.eclipse.nebula.widgets.nattable.selection.SelectionUtils; |
| import org.eclipse.nebula.widgets.nattable.selection.event.ColumnSelectionEvent; |
| import org.eclipse.nebula.widgets.nattable.selection.event.ISelectionEvent; |
| |
| /** |
| * Implementation of {@link ILayerListener} to support E4 selection handling. |
| * Needs to be set to the {@link SelectionLayer} |
| * |
| * <pre> |
| * E4SelectionListener<Person> esl = new E4SelectionListener<>(service, selectionLayer, bodyDataProvider); |
| * selectionLayer.addLayerListener(esl); |
| * </pre> |
| * |
| * @param <T> |
| * The type of objects provided by the {@link IRowDataProvider} |
| */ |
| public class E4SelectionListener<T> implements ILayerListener { |
| /** |
| * The {@link ESelectionService} to set the selection to. |
| */ |
| private ESelectionService selectionService; |
| /** |
| * The {@link SelectionLayer} that is used to retrieve the selection. |
| */ |
| private SelectionLayer selectionLayer; |
| /** |
| * The {@link IRowDataProvider} to access the selected row data. |
| */ |
| private IRowDataProvider<T> rowDataProvider; |
| /** |
| * Flag to determine if only fully selected rows should be used to populate |
| * the selection or if any selection should be populated. Default is to only |
| * populate fully selected rows. |
| */ |
| private boolean fullySelectedRowsOnly = true; |
| /** |
| * Flag to configure whether a selection should be set to the |
| * {@link ESelectionService} if the row selection changes or if just another |
| * column is selected. |
| */ |
| private boolean handleSameRowSelection = false; |
| /** |
| * Locally stored previous selection which is used to determine if a event |
| * for changed selection should be fired. It is used to avoid firing events |
| * if the same row is selected again (default). If handleSameRowSelection is |
| * set to <code>true</code>, this value is not evaluated at runtime. |
| */ |
| private List<T> previousSelection; |
| |
| /** |
| * Flag to configure if row selection should be provided in case a column |
| * selection happened. |
| * <p> |
| * To understand this flag consider a {@link SelectionLayer} that is |
| * configured for full row selection by using the {@link RowSelectionModel}. |
| * On performing column selection, all rows would get selected. This matches |
| * the specification because of the {@link RowSelectionModel} and this |
| * listener would process every selected row. |
| * </p> |
| * <p> |
| * As described in Bug 421848 this causes serious issues for huge datasets. |
| * To avoid such issues by still providing the ability to perform and |
| * provide multiple row selections, this flag was introduced. Setting the |
| * value to <code>false</code> will avoid processing in case of column |
| * selections. |
| * </p> |
| * |
| * @see RowSelectionModel |
| */ |
| private boolean processColumnSelection = true; |
| |
| /** |
| * Flag to configure whether a list of data values should be set as |
| * selection or a single value. Needed for creating typed selection |
| * listeners. |
| */ |
| private boolean multiSelection = true; |
| |
| /** |
| * Create a {@link E4SelectionListener} and registers it to the given |
| * {@link SelectionLayer}. |
| * |
| * @param service |
| * The {@link ESelectionService} to which the selection should be |
| * published to. |
| * @param selectionLayer |
| * The {@link SelectionLayer} that is used to retrieve the |
| * selection. |
| * @param rowDataProvider |
| * The {@link IRowDataProvider} to access the selected row data. |
| */ |
| @Inject |
| public E4SelectionListener( |
| ESelectionService service, |
| SelectionLayer selectionLayer, |
| IRowDataProvider<T> rowDataProvider) { |
| |
| this.selectionService = service; |
| this.selectionLayer = selectionLayer; |
| this.rowDataProvider = rowDataProvider; |
| } |
| |
| @Override |
| public void handleLayerEvent(ILayerEvent event) { |
| if (event instanceof ISelectionEvent) { |
| List<T> selection = SelectionUtils.getSelectedRowObjects(this.selectionLayer, this.rowDataProvider, this.fullySelectedRowsOnly); |
| if ((this.handleSameRowSelection || !selection.equals(this.previousSelection)) |
| && !(!this.processColumnSelection && event instanceof ColumnSelectionEvent)) { |
| try { |
| if (this.multiSelection) { |
| this.selectionService.setSelection(selection); |
| } else { |
| this.selectionService.setSelection(!selection.isEmpty() ? selection.get(0) : null); |
| } |
| } finally { |
| this.previousSelection = selection; |
| } |
| } |
| } |
| } |
| |
| /** |
| * |
| * @return <code>true</code> if only fully selected rows should be used to |
| * populate the selection or if any selection should be populated. |
| * Default is <code>true</code>. |
| */ |
| public boolean isFullySelectedRowsOnly() { |
| return this.fullySelectedRowsOnly; |
| } |
| |
| /** |
| * |
| * @param fullySelectedRowsOnly |
| * <code>true</code> if only fully selected rows should be used |
| * to populate the selection <code>false</code> if any selection |
| * should be populated. |
| */ |
| public void setFullySelectedRowsOnly(boolean fullySelectedRowsOnly) { |
| this.fullySelectedRowsOnly = fullySelectedRowsOnly; |
| } |
| |
| /** |
| * |
| * @return <code>true</code> if a selection should only be handled by the |
| * {@link ESelectionService} if the row selection changes or if just |
| * another column is selected. |
| */ |
| public boolean isHandleSameRowSelection() { |
| return this.handleSameRowSelection; |
| } |
| |
| /** |
| * |
| * @param handleSameRowSelection |
| * <code>true</code> if a selection should only be handled by the |
| * {@link ESelectionService} if the row selection changes or if |
| * just another column is selected. |
| */ |
| public void setHandleSameRowSelection(boolean handleSameRowSelection) { |
| this.handleSameRowSelection = handleSameRowSelection; |
| } |
| |
| /** |
| * |
| * @return <code>true</code> if column selection should trigger setting the |
| * selection via {@link ESelectionService} (that means basically a |
| * <i>select all</i>), <code>false</code> if not (needed for huge |
| * datasets to avoid a <i>select all</i>). |
| */ |
| public boolean isProcessColumnSelection() { |
| return this.processColumnSelection; |
| } |
| |
| /** |
| * Configure whether column selections should start row selection processing |
| * or not. |
| * <p> |
| * This is necessary to handle issues with huge datasets. Dependent on |
| * different configurations, selecting a column can cause the selection of |
| * all rows in a table, which would then lead to populate the whole dataset |
| * as selection via this provider. Setting the |
| * {@link E4SelectionListener#processColumnSelection} flag to |
| * <code>false</code> will skip processing to avoid such issues. |
| * </p> |
| * |
| * @param processColumnSelection |
| * <code>true</code> to process row selection in case of column |
| * selections (default) <code>false</code> to skip processing in |
| * case of column selections to avoid issues on large datasets. |
| */ |
| public void setProcessColumnSelection(boolean processColumnSelection) { |
| this.processColumnSelection = processColumnSelection; |
| } |
| |
| /** |
| * |
| * @return <code>true</code> if a list of data values should be set as |
| * selection or a single value. |
| */ |
| public boolean isMultiSelection() { |
| return this.multiSelection; |
| } |
| |
| /** |
| * Set this value to <code>true</code> if a list of values should be set as |
| * selection to the {@link ESelectionService} or <code>false</code> if only |
| * a single value object should be set. |
| * |
| * @param multiSelection |
| * <code>true</code> if a list of data values should be set as |
| * selection or a single value. |
| */ |
| public void setMultiSelection(boolean multiSelection) { |
| this.multiSelection = multiSelection; |
| } |
| } |