blob: 8869950ca5d23ccdf3e55d3aa14abe023a71e809 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2020 Original authors and others.
*
* 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:
* Original authors and others - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.hideshow;
import java.util.Arrays;
import java.util.Collection;
import java.util.Properties;
import java.util.StringTokenizer;
import org.eclipse.collections.api.list.primitive.MutableIntList;
import org.eclipse.collections.api.set.primitive.MutableIntSet;
import org.eclipse.collections.impl.factory.primitive.IntLists;
import org.eclipse.collections.impl.factory.primitive.IntSets;
import org.eclipse.nebula.widgets.nattable.hideshow.command.ColumnHideCommandHandler;
import org.eclipse.nebula.widgets.nattable.hideshow.command.ColumnShowCommandHandler;
import org.eclipse.nebula.widgets.nattable.hideshow.command.HideColumnByIndexCommandHandler;
import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiColumnHideCommandHandler;
import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiColumnShowCommandHandler;
import org.eclipse.nebula.widgets.nattable.hideshow.command.ShowAllColumnsCommandHandler;
import org.eclipse.nebula.widgets.nattable.hideshow.event.HideColumnPositionsEvent;
import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowColumnPositionsEvent;
import org.eclipse.nebula.widgets.nattable.hideshow.indicator.HideIndicatorConstants;
import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.IStructuralChangeEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.StructuralChangeEventHelper;
import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff;
import org.eclipse.nebula.widgets.nattable.persistence.IPersistable;
import org.eclipse.nebula.widgets.nattable.util.ArrayUtil;
/**
* Layer to add support for column hide/show feature to a NatTable. Technically
* the columns are hidden by this layer which leads to a index-position
* transformation. With percentage sizing this this leads to gaps for hidden
* columns as the size of the other columns is not re-calculated. For percentage
* sizing and size increase use the {@link ResizeColumnHideShowLayer}.
*
* @see ResizeColumnHideShowLayer
*/
public class ColumnHideShowLayer extends AbstractColumnHideShowLayer implements IColumnHideShowLayer {
public static final String PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES = ".hiddenColumnIndexes"; //$NON-NLS-1$
private MutableIntSet hiddenColumnIndexes = IntSets.mutable.empty();
public ColumnHideShowLayer(IUniqueIndexLayer underlyingLayer) {
super(underlyingLayer);
registerCommandHandler(new MultiColumnHideCommandHandler(this));
registerCommandHandler(new ColumnHideCommandHandler(this));
registerCommandHandler(new ShowAllColumnsCommandHandler(this));
registerCommandHandler(new MultiColumnShowCommandHandler(this));
registerCommandHandler(new ColumnShowCommandHandler(this));
registerCommandHandler(new HideColumnByIndexCommandHandler(this));
}
@Override
public void handleLayerEvent(ILayerEvent event) {
if (event instanceof IStructuralChangeEvent) {
IStructuralChangeEvent structuralChangeEvent = (IStructuralChangeEvent) event;
if (structuralChangeEvent.isHorizontalStructureChanged()) {
Collection<StructuralDiff> columnDiffs = structuralChangeEvent.getColumnDiffs();
if (columnDiffs != null && !columnDiffs.isEmpty()
&& !StructuralChangeEventHelper.isReorder(columnDiffs)) {
StructuralChangeEventHelper.handleColumnDelete(
columnDiffs,
this.underlyingLayer,
this.hiddenColumnIndexes,
false);
StructuralChangeEventHelper.handleColumnInsert(
columnDiffs,
this.underlyingLayer,
this.hiddenColumnIndexes,
false);
}
}
}
super.handleLayerEvent(event);
}
// Persistence
@Override
public void saveState(String prefix, Properties properties) {
if (this.hiddenColumnIndexes.size() > 0) {
properties.setProperty(
prefix + PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES,
this.hiddenColumnIndexes.toSortedList().makeString(IPersistable.VALUE_SEPARATOR));
} else {
properties.remove(prefix + PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES);
}
super.saveState(prefix, properties);
}
@Override
public void loadState(String prefix, Properties properties) {
// Bug 396925: always clear the state of the hidden columns, whether
// there is a state saved or not
this.hiddenColumnIndexes = IntSets.mutable.empty();
String property = properties.getProperty(prefix + PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES);
if (property != null) {
StringTokenizer tok = new StringTokenizer(property, IPersistable.VALUE_SEPARATOR);
while (tok.hasMoreTokens()) {
String index = tok.nextToken();
this.hiddenColumnIndexes.add(Integer.parseInt(index));
}
}
super.loadState(prefix, properties);
}
@Override
public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) {
LabelStack configLabels = super.getConfigLabelsByPosition(columnPosition, rowPosition);
// we need to check the hidden state of an adjacent position via the
// underlying layer as in the hide layer the position might be
// hidden
int underlyingPosition = localToUnderlyingColumnPosition(columnPosition);
int leftColumnIndex = this.underlyingLayer.getColumnIndexByPosition(underlyingPosition - 1);
if (isColumnIndexHidden(leftColumnIndex)) {
configLabels.addLabel(HideIndicatorConstants.COLUMN_LEFT_HIDDEN);
}
int rightColumnIndex = this.underlyingLayer.getColumnIndexByPosition(underlyingPosition + 1);
if (isColumnIndexHidden(rightColumnIndex)) {
configLabels.addLabel(HideIndicatorConstants.COLUMN_RIGHT_HIDDEN);
}
return configLabels;
}
// Hide/show
@Override
public boolean isColumnIndexHidden(int columnIndex) {
return this.hiddenColumnIndexes.contains(columnIndex);
}
@Override
public Collection<Integer> getHiddenColumnIndexes() {
return ArrayUtil.asIntegerList(this.hiddenColumnIndexes.toSortedArray());
}
@Override
public int[] getHiddenColumnIndexesArray() {
return this.hiddenColumnIndexes.toSortedArray();
}
@Override
public boolean hasHiddenColumns() {
return !this.hiddenColumnIndexes.isEmpty();
}
/**
* {@inheritDoc}
*
* @since 1.6
*/
@Override
public void hideColumnPositions(int... columnPositions) {
int[] columnIndexes = Arrays.stream(columnPositions)
.map(this::getColumnIndexByPosition)
.sorted()
.toArray();
this.hiddenColumnIndexes.addAll(columnIndexes);
invalidateCache();
fireLayerEvent(new HideColumnPositionsEvent(this, columnPositions, columnIndexes));
}
@Override
public void hideColumnPositions(Collection<Integer> columnPositions) {
hideColumnPositions(columnPositions.stream().mapToInt(Integer::intValue).toArray());
}
@Override
public void hideColumnIndexes(int... columnIndexes) {
int[] columnPositions = Arrays.stream(columnIndexes)
.map(this::getColumnPositionByIndex)
.sorted()
.toArray();
this.hiddenColumnIndexes.addAll(columnIndexes);
invalidateCache();
fireLayerEvent(new HideColumnPositionsEvent(this, columnPositions, columnIndexes));
}
@Override
public void hideColumnIndexes(Collection<Integer> columnIndexes) {
hideColumnIndexes(columnIndexes.stream().mapToInt(Integer::intValue).toArray());
}
/**
* {@inheritDoc}
*
* @since 1.6
*/
@Override
public void showColumnIndexes(int... columnIndexes) {
MutableIntList toProcess = IntLists.mutable.of(columnIndexes);
// only handle column indexes that are hidden
toProcess.retainAll(this.hiddenColumnIndexes);
this.hiddenColumnIndexes.removeAll(toProcess);
invalidateCache();
int[] positions = getColumnPositionsByIndexes(toProcess.toArray());
fireLayerEvent(new ShowColumnPositionsEvent(this, positions));
}
@Override
public void showColumnIndexes(Collection<Integer> columnIndexes) {
showColumnIndexes(columnIndexes.stream().mapToInt(Integer::intValue).toArray());
}
@Override
public void showColumnPosition(int columnPosition, boolean showToLeft, boolean showAll) {
MutableIntSet columnIndexes = IntSets.mutable.empty();
int underlyingPosition = localToUnderlyingColumnPosition(columnPosition);
if (showToLeft) {
int leftColumnIndex = this.underlyingLayer.getColumnIndexByPosition(underlyingPosition - 1);
if (showAll) {
int move = 1;
while (isColumnIndexHidden(leftColumnIndex)) {
columnIndexes.add(leftColumnIndex);
move++;
leftColumnIndex = this.underlyingLayer.getColumnIndexByPosition(underlyingPosition - move);
}
} else if (isColumnIndexHidden(leftColumnIndex)) {
columnIndexes.add(leftColumnIndex);
}
} else {
int rightColumnIndex = this.underlyingLayer.getColumnIndexByPosition(underlyingPosition + 1);
if (showAll) {
int move = 1;
while (isColumnIndexHidden(rightColumnIndex)) {
columnIndexes.add(rightColumnIndex);
move++;
rightColumnIndex = this.underlyingLayer.getColumnIndexByPosition(underlyingPosition + move);
}
} else if (isColumnIndexHidden(rightColumnIndex)) {
columnIndexes.add(rightColumnIndex);
}
}
if (!columnIndexes.isEmpty()) {
showColumnIndexes(columnIndexes.toArray());
}
}
@Override
public void showAllColumns() {
int[] hidden = this.hiddenColumnIndexes.toSortedArray();
this.hiddenColumnIndexes = IntSets.mutable.empty();
invalidateCache();
fireLayerEvent(new ShowColumnPositionsEvent(this, getColumnPositionsByIndexes(hidden)));
}
@Override
public Collection<String> getProvidedLabels() {
Collection<String> result = super.getProvidedLabels();
result.add(HideIndicatorConstants.COLUMN_LEFT_HIDDEN);
result.add(HideIndicatorConstants.COLUMN_RIGHT_HIDDEN);
return result;
}
}