blob: 3e20089501417097b1f996125d457394eac0c451 [file] [log] [blame]
/*******************************************************************************
* 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&lt;Person&gt; esl = new E4SelectionListener&lt;&gt;(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;
}
}