blob: 9d3c876cbd894772b734e53f28785ddadff9c4b6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 Dirk Fauth and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.nattable.extension.glazedlists.hideshow;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider;
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.matchers.AbstractMatcherEditor;
import ca.odell.glazedlists.matchers.CompositeMatcherEditor;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.MatcherEditor;
/**
* Adds the functionality for manually hiding rows in a NatTable that is based on GlazedLists.
* Technically it will filter items by id in the {@link FilterList}.
* <p>
* Note: If you want to add row hide/show AND filtering to your NatTable, you need to use the
* DefaultGlazedListsStaticFilterStrategy and add the {@link Matcher} that is used by
* this command as a static {@link MatcherEditor}. Otherwise these two functions will not work
* correctly together.
*
* @author Dirk Fauth
*
* TODO add IPersistable
*/
public class GlazedListsRowHideShowLayer<T> extends AbstractLayerTransform implements IUniqueIndexLayer {
/**
* The collection of row id's that are hidden.
*/
private final Set<Serializable> rowIdsToHide = new HashSet<Serializable>();
/**
* The {@link IRowIdAccessor} that is used to extract the id out of the row object.
* This is necessary to determine the row object to hide in terms of content.
*/
private final IRowIdAccessor<T> rowIdAccessor;
/**
* The {@link IRowDataProvider} needed to get the object at a given position so it
* is possible to retrieve the id that is used for filtering.
*/
private final IRowDataProvider<T> rowDataProvider;
/**
* The {@link MatcherEditor} that is used to filter the rows with the specified id's.
*/
private final HideRowMatcherEditor hideRowByIdMatcherEditor = new HideRowMatcherEditor();
/**
* Creates a {@link GlazedListsRowHideShowLayer} for adding row hide/show for GlazedLists based
* NatTables. Using this constructor will only instantiate this layer. To use this correctly
* you need to register the local row hide {@link MatcherEditor} against the {@link FilterList} yourself.
* @param underlyingLayer The underlying layer.
* @param rowDataProvider The {@link IRowDataProvider} needed to get the object at a given position so it
* is possible to retrieve the id that is used for filtering.
* @param rowIdAccessor The {@link IRowIdAccessor} that is used to extract the id out of the row object.
* This is necessary to determine the row object to hide in terms of content.
*/
public GlazedListsRowHideShowLayer(
ILayer underlyingLayer, IRowDataProvider<T> rowDataProvider, IRowIdAccessor<T> rowIdAccessor) {
super(underlyingLayer);
if (rowIdAccessor == null) {
throw new IllegalArgumentException("rowIdAccessor can not be null!"); //$NON-NLS-1$
}
this.rowDataProvider = rowDataProvider;
this.rowIdAccessor = rowIdAccessor;
registerCommandHandlers();
}
/**
* Creates a {@link GlazedListsRowHideShowLayer} for adding row hide/show for GlazedLists based
* NatTables. Using this constructor will add the {@link MatcherEditor} to the given {@link FilterList}.
* This might not work correctly in combination with other filters like e.g. the filter row.
* @param underlyingLayer The underlying layer.
* @param rowDataProvider The {@link IRowDataProvider} needed to get the object at a given position so it
* is possible to retrieve the id that is used for filtering.
* @param rowIdAccessor The {@link IRowIdAccessor} that is used to extract the id out of the row object.
* This is necessary to determine the row object to hide in terms of content.
* @param filterList The {@link FilterList} to apply the local row hide {@link Matcher} to.
*/
public GlazedListsRowHideShowLayer(
ILayer underlyingLayer, IRowDataProvider<T> rowDataProvider, IRowIdAccessor<T> rowIdAccessor, FilterList<T> filterList) {
super(underlyingLayer);
if (rowIdAccessor == null) {
throw new IllegalArgumentException("rowIdAccessor can not be null!"); //$NON-NLS-1$
}
if (filterList == null) {
throw new IllegalArgumentException("filterList can not be null!"); //$NON-NLS-1$
}
this.rowDataProvider = rowDataProvider;
this.rowIdAccessor = rowIdAccessor;
filterList.setMatcherEditor(this.hideRowByIdMatcherEditor);
registerCommandHandlers();
}
/**
* Creates a {@link GlazedListsRowHideShowLayer} for adding row hide/show for GlazedLists based
* NatTables. Using this constructor will add the {@link MatcherEditor} to the given
* {@link CompositeMatcherEditor}. This way it is possible to add more filter logic than only the
* row hide filter.
* @param underlyingLayer The underlying layer.
* @param rowDataProvider The {@link IRowDataProvider} needed to get the object at a given position so it
* is possible to retrieve the id that is used for filtering.
* @param rowIdAccessor The {@link IRowIdAccessor} that is used to extract the id out of the row object.
* This is necessary to determine the row object to hide in terms of content.
* @param matcherEditor The {@link CompositeMatcherEditor} to which the local row hide {@link Matcher}
* should be added.
*/
public GlazedListsRowHideShowLayer(
ILayer underlyingLayer, IRowDataProvider<T> rowDataProvider, IRowIdAccessor<T> rowIdAccessor, CompositeMatcherEditor<T> matcherEditor) {
super(underlyingLayer);
if (rowIdAccessor == null) {
throw new IllegalArgumentException("rowIdAccessor can not be null!"); //$NON-NLS-1$
}
if (matcherEditor == null) {
throw new IllegalArgumentException("matcherEditor can not be null!"); //$NON-NLS-1$
}
this.rowDataProvider = rowDataProvider;
this.rowIdAccessor = rowIdAccessor;
matcherEditor.getMatcherEditors().add(this.hideRowByIdMatcherEditor);
registerCommandHandlers();
}
/**
* Register the {@link ILayerCommandHandler} that will handle the row hide/show events
* for this layer.
*/
protected void registerCommandHandlers() {
registerCommandHandler(new GlazedListsRowHideCommandHandler<T>(this));
registerCommandHandler(new GlazedListsMultiRowHideCommandHandler<T>(this));
registerCommandHandler(new GlazedListsShowAllRowsCommandHandler<T>(this));
registerCommandHandler(new GlazedListsMultiRowShowCommandHandler<T>(this));
}
/**
* Hide the rows at the given positions. Will collect the id's of the rows at
* the given positions as this layer operates on content rather than positions.
* @param rowPositions The positions of the rows to hide.
*/
public void hideRowPositions(Collection<Integer> rowPositions) {
Collection<Serializable> rowIds = new HashSet<Serializable>();
for (Integer rowPos : rowPositions) {
int rowIndex = getRowIndexByPosition(rowPos);
rowIds.add(rowIdAccessor.getRowId(rowDataProvider.getRowObject(rowIndex)));
}
hideRows(rowIds);
}
/**
* Hide the rows at the given indexes. Will collect the id's of the rows at
* the given positions as this layer operates on content rather than positions.
* @param rowIndexes The indexes of the rows to hide.
*/
public void hideRowIndexes(Collection<Integer> rowIndexes) {
Collection<Serializable> rowIds = new HashSet<Serializable>();
for (Integer rowIndex : rowIndexes) {
rowIds.add(rowIdAccessor.getRowId(rowDataProvider.getRowObject(rowIndex)));
}
hideRows(rowIds);
}
/**
* Show the rows with the given indexes again. Will collect the id's of the rows at
* the given positions as this layer operates on content rather than positions.
* @param rowIndexes The indexes of the rows that should be showed again.
*/
public void showRowIndexes(Collection<Integer> rowIndexes) {
Collection<Serializable> rowIds = new HashSet<Serializable>();
for (Integer rowIndex : rowIndexes) {
rowIds.add(rowIdAccessor.getRowId(rowDataProvider.getRowObject(rowIndex)));
}
showRows(rowIds);
}
/**
* Hide the rows that contain the objects with the given row ids.
* @param rowIds The row ids of the rows that should be hidden.
*/
public void hideRows(Collection<Serializable> rowIds) {
this.rowIdsToHide.addAll(rowIds);
this.hideRowByIdMatcherEditor.fireChange();
}
/**
* Show the rows that contain the objects with the given row ids.
* Will of course only have impact if those rows are currently hidden.
* @param rowIds The row ids of the rows that should be showed.
*/
public void showRows(Collection<Serializable> rowIds) {
this.rowIdsToHide.removeAll(rowIds);
this.hideRowByIdMatcherEditor.fireChange();
}
/**
* Show all rows that where previously hidden.
*/
public void showAllRows() {
this.rowIdsToHide.clear();
this.hideRowByIdMatcherEditor.fireChange();
}
/**
* @return The {@link MatcherEditor} that is used to filter the rows with the specified id's.
*/
public MatcherEditor<T> getMatcherEditor() {
return this.hideRowByIdMatcherEditor;
}
@Override
public int getColumnPositionByIndex(int columnIndex) {
return ((IUniqueIndexLayer) getUnderlyingLayer()).getColumnPositionByIndex(columnIndex);
}
@Override
public int getRowPositionByIndex(int rowIndex) {
return ((IUniqueIndexLayer) getUnderlyingLayer()).getRowPositionByIndex(rowIndex);
}
/**
* {@link MatcherEditor} implementation that will only match objects that are not hidden by
* id. It also enables to fire change events to indicate changes to the filter.
*
* @author Dirk Fauth
*
*/
class HideRowMatcherEditor extends AbstractMatcherEditor<T> {
/**
* The {@link Matcher} that is used to filter the rows with the specified id's.
*/
private final Matcher<T> hideRowByIdMatcher = new Matcher<T>() {
@Override
public boolean matches(T rowObject) {
return !rowIdsToHide.contains(rowIdAccessor.getRowId(rowObject));
}
};
@Override
public Matcher<T> getMatcher() {
return this.hideRowByIdMatcher;
}
/**
* Fire a change so the listeners can be informed about a change for the {@link Matcher}
*/
public void fireChange() {
fireChanged(this.hideRowByIdMatcher);
}
}
}