/******************************************************************************* * Copyright (c) 2012 Original authors 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: * Original authors and others - initial API and implementation ******************************************************************************/ package org.simantics.browsing.ui.nattable.override; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.nebula.widgets.nattable.command.ILayerCommand; import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiRowHideCommand; import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommand; import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent; import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent; import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; import org.eclipse.nebula.widgets.nattable.layer.LabelStack; import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; import org.eclipse.nebula.widgets.nattable.painter.cell.BackgroundPainter; import org.eclipse.nebula.widgets.nattable.painter.cell.CellPainterWrapper; import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel; import org.eclipse.nebula.widgets.nattable.tree.TreeLayer; import org.eclipse.nebula.widgets.nattable.tree.config.DefaultTreeLayerConfiguration; import org.eclipse.nebula.widgets.nattable.tree.config.TreeConfigAttributes; import org.eclipse.nebula.widgets.nattable.tree.painter.IndentedTreeImagePainter; import it.unimi.dsi.fastutil.ints.IntRBTreeSet; public class TreeLayer2 extends AbstractRowHideShowLayer2 { //private static final Log log = LogFactory.getLog(TreeLayer.class); public static final String TREE_COLUMN_CELL = "TREE_COLUMN_CELL"; //$NON-NLS-1$ public static final int TREE_COLUMN_NUMBER = 0; /** * Flag to configure whether the tree column should be identified by * position or by index. Default is position. */ private boolean useTreeColumnIndex = false; /** * The ITreeRowModelListener that is used to get information about the tree * structure. */ private final ITreeRowModel> treeRowModel; /** * Collection of all row indexes that are hidden if tree nodes are * collapsed. *
* Note: This collection is only in use if the used {@link ITreeRowModel} * implementation is returning the row indexes of affected rows on * expand/collapse. There are also implementations that use another approach * where the hide/show approach is not used (e.g. GlazedListTreeRowModel) *
*/ private final IntRBTreeSet hiddenRowIndexes = new IntRBTreeSet(); /** * The IndentedTreeImagePainter that paints indentation to the left of the * configured base painter and icons for expand/collapse if possible, to * render tree structure accordingly. */ private IndentedTreeImagePainter indentedTreeImagePainter; /** * Creates a TreeLayer instance based on the given information. Will use a * default IndentedTreeImagePainter that uses 10 pixels for indentation and * simple + and - icons for expand/collapse icons. It also uses the * DefaultTreeLayerConfiguration. * * @param underlyingLayer * The underlying layer on whose top this layer will be set. * @param treeRowModel * The ITreeRowModelListener that is used to get information * about the tree structure. */ public TreeLayer2(IUniqueIndexLayer underlyingLayer, ITreeRowModel> treeRowModel) { this(underlyingLayer, treeRowModel, new IndentedTreeImagePainter()); } /** * Creates a TreeLayer instance based on the given information. Allows to * specify the IndentedTreeImagePainter while using the * DefaultTreeLayerConfiguration. * * @param underlyingLayer * The underlying layer on whose top this layer will be set. * @param treeRowModel * The ITreeRowModelListener that is used to get information * about the tree structure. * @param indentedTreeImagePainter * The IndentedTreeImagePainter that paints indentation to the * left of the configured base painter and icons for * expand/collapse if possible, to render tree structure * accordingly. */ public TreeLayer2( IUniqueIndexLayer underlyingLayer, ITreeRowModel> treeRowModel, IndentedTreeImagePainter indentedTreeImagePainter) { this(underlyingLayer, treeRowModel, indentedTreeImagePainter, true); } /** * Creates a TreeLayer instance based on the given information. Will use a * default IndentedTreeImagePainter that uses 10 pixels for indentation and * simple + and - icons for expand/collapse icons. * * @param underlyingLayer * The underlying layer on whose top this layer will be set. * @param treeRowModel * The ITreeRowModelListener that is used to get information * about the tree structure. * @param useDefaultConfiguration *true
to use the DefaultTreeLayerConfiguration,
* false
if you want to specify your own
* configuration.
*/
public TreeLayer2(
IUniqueIndexLayer underlyingLayer,
ITreeRowModel> treeRowModel,
boolean useDefaultConfiguration) {
this(underlyingLayer,
treeRowModel,
new IndentedTreeImagePainter(),
useDefaultConfiguration);
}
/**
* Creates a TreeLayer instance based on the given information.
*
* @param underlyingLayer
* The underlying layer on whose top this layer will be set.
* @param treeRowModel
* The ITreeRowModelListener that is used to get information
* about the tree structure.
* @param indentedTreeImagePainter
* The IndentedTreeImagePainter that paints indentation to the
* left of the configured base painter and icons for
* expand/collapse if possible, to render tree structure
* accordingly.
* @param useDefaultConfiguration
* true
to use the DefaultTreeLayerConfiguration,
* false
if you want to specify your own
* configuration.
*/
public TreeLayer2(
IUniqueIndexLayer underlyingLayer,
ITreeRowModel> treeRowModel,
IndentedTreeImagePainter indentedTreeImagePainter,
boolean useDefaultConfiguration) {
super(underlyingLayer);
this.treeRowModel = treeRowModel;
if (useDefaultConfiguration) {
addConfiguration(new DefaultTreeLayerConfiguration2(this));
}
this.indentedTreeImagePainter = indentedTreeImagePainter;
registerCommandHandler(new TreeExpandCollapseCommandHandler(this));
registerCommandHandler(new TreeCollapseAllCommandHandler(this));
registerCommandHandler(new TreeExpandAllCommandHandler(this));
registerCommandHandler(new TreeExpandToLevelCommandHandler(this));
}
@Override
public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) {
LabelStack configLabels = super.getConfigLabelsByPosition(columnPosition, rowPosition);
if (isTreeColumn(columnPosition)) {
configLabels.addLabelOnTop(TREE_COLUMN_CELL);
int rowIndex = getRowIndexByPosition(rowPosition);
configLabels.addLabelOnTop(
DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + this.treeRowModel.depth(rowIndex));
if (!this.treeRowModel.hasChildren(rowIndex)) {
configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE);
} else {
if (this.treeRowModel.isCollapsed(rowIndex)) {
configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE);
} else {
configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE);
}
}
}
return configLabels;
}
/**
* @return The ITreeRowModelListener that is used to get information about
* the tree structure.
*/
public ITreeRowModel> getModel() {
return this.treeRowModel;
}
/**
* @return The IndentedTreeImagePainter that paints indentation to the left
* of the configured base painter and icons for expand/collapse if
* possible, to render tree structure accordingly.
*
* @deprecated since 1.1 the configured TreeImagePainter should be used
* instead of the hard referenced one
*/
@Deprecated
public IndentedTreeImagePainter getIndentedTreeImagePainter() {
return this.indentedTreeImagePainter;
}
/**
* @return The ICellPainter that is used to paint the images in the tree by
* the IndentedTreeImagePainter. Usually it is some type of
* TreeImagePainter that paints expand/collapse/leaf icons regarding
* the node state.null
if set explicitly to the
* IndentedTreeImagePainter!
*
* @deprecated since 1.1 the configured TreeImagePainter should be used
* instead of the hard referenced one
*/
@Deprecated
public ICellPainter getTreeImagePainter() {
return this.indentedTreeImagePainter != null ? this.indentedTreeImagePainter
.getTreeImagePainter() : null;
}
/**
* @param columnPosition
* The column position to check.
* @return true
if the given column position is the tree
* column, false
if not.
*/
private boolean isTreeColumn(int columnPosition) {
if (this.useTreeColumnIndex)
return getColumnIndexByPosition(columnPosition) == TREE_COLUMN_NUMBER;
return columnPosition == TREE_COLUMN_NUMBER;
}
@Override
public ICellPainter getCellPainter(
int columnPosition, int rowPosition,
ILayerCell cell, IConfigRegistry configRegistry) {
ICellPainter cellPainter = super.getCellPainter(
columnPosition, rowPosition, cell, configRegistry);
if (cell.getConfigLabels().hasLabel(TREE_COLUMN_CELL)) {
ICellPainter treeCellPainter = configRegistry.getConfigAttribute(
TreeConfigAttributes.TREE_STRUCTURE_PAINTER,
cell.getDisplayMode(),
cell.getConfigLabels().getLabels());
if (treeCellPainter != null) {
ICellPainter innerWrapper = treeCellPainter;
IndentedTreeImagePainter treePainter = null;
if (innerWrapper instanceof IndentedTreeImagePainter) {
treePainter = (IndentedTreeImagePainter) innerWrapper;
} else {
while (treePainter == null
&& innerWrapper != null
&& innerWrapper instanceof CellPainterWrapper
&& ((CellPainterWrapper) innerWrapper).getWrappedPainter() != null) {
innerWrapper = ((CellPainterWrapper) innerWrapper).getWrappedPainter();
if (innerWrapper instanceof IndentedTreeImagePainter) {
treePainter = (IndentedTreeImagePainter) innerWrapper;
}
}
}
if (treePainter != null) {
treePainter.setBaseCellPainter(cellPainter);
cellPainter = treeCellPainter;
} else {
// log error
// log.warn("There is no IndentedTreeImagePainter found for TREE_STRUCTURE_PAINTER, " //$NON-NLS-1$
// + "using local configured IndentedTreeImagePainter as fallback"); //$NON-NLS-1$
// fallback
this.indentedTreeImagePainter.setBaseCellPainter(cellPainter);
cellPainter = new BackgroundPainter(this.indentedTreeImagePainter);
}
} else {
// backwards compatibility fallback
this.indentedTreeImagePainter.setBaseCellPainter(cellPainter);
cellPainter = new BackgroundPainter(this.indentedTreeImagePainter);
}
}
return cellPainter;
}
@Override
public boolean isRowIndexHidden(int rowIndex) {
return this.hiddenRowIndexes.contains(rowIndex)
|| isHiddenInUnderlyingLayer(rowIndex);
}
@Override
public Collectiontrue
if the row at the given index is hidden in the
* underlying layer false
if not.
*/
private boolean isHiddenInUnderlyingLayer(int rowIndex) {
IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();
return (underlyingLayer.getRowPositionByIndex(rowIndex) == -1);
}
@Override
public boolean doCommand(ILayerCommand command) {
// special command transformations are needed to hide also child nodes
if (command instanceof RowHideCommand) {
return handleRowHideCommand((RowHideCommand) command);
} else if (command instanceof MultiRowHideCommand) {
return handleMultiRowHideCommand((MultiRowHideCommand) command);
}
return super.doCommand(command);
}
/**
* Checks if the given command tries to hide a row that is a node that is
* not collapsed and has children. In that case also the child rows need to
* be hidden.
*
* @param command
* The {@link RowHideCommand} to process
* @return true
if the command has been handled,
* false
otherwise
*/
protected boolean handleRowHideCommand(RowHideCommand command) {
// transform position to index
if (command.convertToTargetLayer(this)) {
int rowIndex = getRowIndexByPosition(command.getRowPosition());
if (this.treeRowModel.hasChildren(rowIndex)
&& !this.treeRowModel.isCollapsed(rowIndex)) {
Listtrue
if the command has been handled,
* false
otherwise
*/
protected boolean handleMultiRowHideCommand(MultiRowHideCommand command) {
// transform position to index
if (command.convertToTargetLayer(this)) {
Listtrue
if the column index is used to determine the
* tree column, false
if the column position is used.
* Default is false
.
*/
public boolean isUseTreeColumnIndex() {
return this.useTreeColumnIndex;
}
/**
* Configure whether (column index == 0) or (column position == 0) should be
* performed to identify the tree column.
*
* @param useTreeColumnIndex
* true
if the column index should be used to
* determine the tree column, false
if the column
* position should be used.
*/
public void setUseTreeColumnIndex(boolean useTreeColumnIndex) {
this.useTreeColumnIndex = useTreeColumnIndex;
}
/**
* @since 1.4
*/
@Override
public Collection