X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.browsing.ui.nattable%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fnattable%2Foverride%2FTreeLayer2.java;fp=bundles%2Forg.simantics.browsing.ui.nattable%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fnattable%2Foverride%2FTreeLayer2.java;h=a44944ce6a6792b9dbe36138168f2f4dd4bafdeb;hp=7d0f64e47b1b88129cf5f50359c47b708ab0c3f5;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 diff --git a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeLayer2.java b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeLayer2.java index 7d0f64e47..a44944ce6 100644 --- a/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeLayer2.java +++ b/bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/override/TreeLayer2.java @@ -1,577 +1,577 @@ -/******************************************************************************* - * 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.
- * Can be 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 Collection getHiddenRowIndexes() { - return this.hiddenRowIndexes; - } - - /** - * Performs an expand/collapse action dependent on the current state of the - * tree node at the given row index. - * - * @param parentIndex - * The index of the row that shows the tree node for which the - * expand/collapse action should be performed. - */ - public void expandOrCollapseIndex(int parentIndex) { - if (this.treeRowModel.isCollapsed(parentIndex)) { - expandTreeRow(parentIndex); - } else { - collapseTreeRow(parentIndex); - } - } - - /** - * Collapses the tree node for the given row index. - * - * @param parentIndex - * The index of the row that shows the node that should be - * collapsed - */ - public void collapseTreeRow(int parentIndex) { - List rowIndexes = this.treeRowModel.collapse(parentIndex); - List rowPositions = new ArrayList(); - for (Integer rowIndex : rowIndexes) { - int rowPos = getRowPositionByIndex(rowIndex); - // if the rowPos is negative, it is not visible because of hidden - // state in an underlying layer - if (rowPos >= 0) { - rowPositions.add(rowPos); - } - } - this.hiddenRowIndexes.addAll(rowIndexes); - invalidateCache(); - fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); - } - - /** - * Collapses all tree nodes in the tree. - */ - public void collapseAll() { - List rowIndexes = this.treeRowModel.collapseAll(); - List rowPositions = new ArrayList(); - for (Integer rowIndex : rowIndexes) { - int rowPos = getRowPositionByIndex(rowIndex); - // if the rowPos is negative, it is not visible because of hidden - // state in an underlying layer - if (rowPos >= 0) { - rowPositions.add(rowPos); - } - } - this.hiddenRowIndexes.addAll(rowIndexes); - invalidateCache(); - fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); - } - - /** - * Expands the tree node for the given row index. - * - * @param parentIndex - * The index of the row that shows the node that should be - * expanded - */ - public void expandTreeRow(int parentIndex) { - List rowIndexes = this.treeRowModel.expand(parentIndex); - // Bug 432865: iterating and removing every single item is faster than - // removeAll() - for (final Integer expandedChildRowIndex : rowIndexes) { - this.hiddenRowIndexes.remove(expandedChildRowIndex); - } - invalidateCache(); - fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); - } - - /** - * Expands the tree node for the given row index in the tree to a certain - * level. - * - * @param parentIndex - * The index of the row that shows the node that should be - * expanded - * @param level - * The level to which the tree node should be expanded. - */ - public void expandTreeRowToLevel(int parentIndex, int level) { - List rowIndexes = this.treeRowModel.expandToLevel(parentIndex, level); - // Bug 432865: iterating and removing every single item is faster than - // removeAll() - for (final Integer expandedChildRowIndex : rowIndexes) { - this.hiddenRowIndexes.remove(expandedChildRowIndex); - } - invalidateCache(); - fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); - } - - /** - * Expands all tree nodes in the tree. - */ - public void expandAll() { - List rowIndexes = this.treeRowModel.expandAll(); - this.hiddenRowIndexes.clear(); - invalidateCache(); - fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); - } - - /** - * Expands all tree nodes in the tree to a certain level. - * - * @param level - * The level to which the tree node should be expanded. - */ - public void expandAllToLevel(int level) { - List rowIndexes = this.treeRowModel.expandToLevel(level); - // Bug 432865: iterating and removing every single item is faster than - // removeAll() -// for (final Integer expandedChildRowIndex : rowIndexes) { -// this.hiddenRowIndexes.remove(expandedChildRowIndex); -// } - if (rowIndexes == null) - return; - for (int i = rowIndexes.size()-1; i>=0; i--) { - this.hiddenRowIndexes.remove(rowIndexes.get(i)); - } - invalidateCache(); - fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); - } - - /** - * Checks the underlying layer if the row is hidden by another layer. - * - * @param rowIndex - * The index of the row whose hidden state should be checked - * @return true 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)) { - List childIndexes = this.treeRowModel.getChildIndexes(rowIndex); - int[] childPositions = new int[childIndexes.size() + 1]; - childPositions[0] = command.getRowPosition(); - for (int i = 1; i < childIndexes.size() + 1; i++) { - int childPos = getRowPositionByIndex(childIndexes.get(i - 1)); - childPositions[i] = childPos; - } - return super.doCommand(new MultiRowHideCommand(this, childPositions)); - } - } - return super.doCommand(command); - } - - /** - * Checks if the given command tries to hide rows that are nodes that are - * not collapsed and have children. In that case also the child rows need to - * be hidden. - * - * @param command - * The {@link MultiRowHideCommand} to process - * @return true if the command has been handled, - * false otherwise - */ - protected boolean handleMultiRowHideCommand(MultiRowHideCommand command) { - // transform position to index - if (command.convertToTargetLayer(this)) { - List rowPositionsToHide = new ArrayList(); - for (Integer rowPos : command.getRowPositions()) { - rowPositionsToHide.add(rowPos); - int rowIndex = getRowIndexByPosition(rowPos); - if (this.treeRowModel.hasChildren(rowIndex) - && !this.treeRowModel.isCollapsed(rowIndex)) { - List childIndexes = this.treeRowModel.getChildIndexes(rowIndex); - for (Integer childIndex : childIndexes) { - rowPositionsToHide.add(getRowPositionByIndex(childIndex)); - } - } - } - - int[] childPositions = new int[rowPositionsToHide.size()]; - for (int i = 0; i < rowPositionsToHide.size(); i++) { - childPositions[i] = rowPositionsToHide.get(i); - } - return super.doCommand(new MultiRowHideCommand(this, childPositions)); - } - return super.doCommand(command); - } - - /** - * @return true 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 getProvidedLabels() { - Collection result = super.getProvidedLabels(); - - result.add(TreeLayer.TREE_COLUMN_CELL); - result.add(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE); - result.add(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE); - result.add(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE); - // configure 5 levels to be configurable via CSS - // if you need more you need to override this method - result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "0"); //$NON-NLS-1$ - result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "1"); //$NON-NLS-1$ - result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "2"); //$NON-NLS-1$ - result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "3"); //$NON-NLS-1$ - result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "4"); //$NON-NLS-1$ - - return result; - } -} +/******************************************************************************* + * 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.
+ * Can be 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 Collection getHiddenRowIndexes() { + return this.hiddenRowIndexes; + } + + /** + * Performs an expand/collapse action dependent on the current state of the + * tree node at the given row index. + * + * @param parentIndex + * The index of the row that shows the tree node for which the + * expand/collapse action should be performed. + */ + public void expandOrCollapseIndex(int parentIndex) { + if (this.treeRowModel.isCollapsed(parentIndex)) { + expandTreeRow(parentIndex); + } else { + collapseTreeRow(parentIndex); + } + } + + /** + * Collapses the tree node for the given row index. + * + * @param parentIndex + * The index of the row that shows the node that should be + * collapsed + */ + public void collapseTreeRow(int parentIndex) { + List rowIndexes = this.treeRowModel.collapse(parentIndex); + List rowPositions = new ArrayList(); + for (Integer rowIndex : rowIndexes) { + int rowPos = getRowPositionByIndex(rowIndex); + // if the rowPos is negative, it is not visible because of hidden + // state in an underlying layer + if (rowPos >= 0) { + rowPositions.add(rowPos); + } + } + this.hiddenRowIndexes.addAll(rowIndexes); + invalidateCache(); + fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); + } + + /** + * Collapses all tree nodes in the tree. + */ + public void collapseAll() { + List rowIndexes = this.treeRowModel.collapseAll(); + List rowPositions = new ArrayList(); + for (Integer rowIndex : rowIndexes) { + int rowPos = getRowPositionByIndex(rowIndex); + // if the rowPos is negative, it is not visible because of hidden + // state in an underlying layer + if (rowPos >= 0) { + rowPositions.add(rowPos); + } + } + this.hiddenRowIndexes.addAll(rowIndexes); + invalidateCache(); + fireLayerEvent(new HideRowPositionsEvent(this, rowPositions)); + } + + /** + * Expands the tree node for the given row index. + * + * @param parentIndex + * The index of the row that shows the node that should be + * expanded + */ + public void expandTreeRow(int parentIndex) { + List rowIndexes = this.treeRowModel.expand(parentIndex); + // Bug 432865: iterating and removing every single item is faster than + // removeAll() + for (final Integer expandedChildRowIndex : rowIndexes) { + this.hiddenRowIndexes.remove(expandedChildRowIndex); + } + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Expands the tree node for the given row index in the tree to a certain + * level. + * + * @param parentIndex + * The index of the row that shows the node that should be + * expanded + * @param level + * The level to which the tree node should be expanded. + */ + public void expandTreeRowToLevel(int parentIndex, int level) { + List rowIndexes = this.treeRowModel.expandToLevel(parentIndex, level); + // Bug 432865: iterating and removing every single item is faster than + // removeAll() + for (final Integer expandedChildRowIndex : rowIndexes) { + this.hiddenRowIndexes.remove(expandedChildRowIndex); + } + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Expands all tree nodes in the tree. + */ + public void expandAll() { + List rowIndexes = this.treeRowModel.expandAll(); + this.hiddenRowIndexes.clear(); + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Expands all tree nodes in the tree to a certain level. + * + * @param level + * The level to which the tree node should be expanded. + */ + public void expandAllToLevel(int level) { + List rowIndexes = this.treeRowModel.expandToLevel(level); + // Bug 432865: iterating and removing every single item is faster than + // removeAll() +// for (final Integer expandedChildRowIndex : rowIndexes) { +// this.hiddenRowIndexes.remove(expandedChildRowIndex); +// } + if (rowIndexes == null) + return; + for (int i = rowIndexes.size()-1; i>=0; i--) { + this.hiddenRowIndexes.remove(rowIndexes.get(i)); + } + invalidateCache(); + fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes)); + } + + /** + * Checks the underlying layer if the row is hidden by another layer. + * + * @param rowIndex + * The index of the row whose hidden state should be checked + * @return true 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)) { + List childIndexes = this.treeRowModel.getChildIndexes(rowIndex); + int[] childPositions = new int[childIndexes.size() + 1]; + childPositions[0] = command.getRowPosition(); + for (int i = 1; i < childIndexes.size() + 1; i++) { + int childPos = getRowPositionByIndex(childIndexes.get(i - 1)); + childPositions[i] = childPos; + } + return super.doCommand(new MultiRowHideCommand(this, childPositions)); + } + } + return super.doCommand(command); + } + + /** + * Checks if the given command tries to hide rows that are nodes that are + * not collapsed and have children. In that case also the child rows need to + * be hidden. + * + * @param command + * The {@link MultiRowHideCommand} to process + * @return true if the command has been handled, + * false otherwise + */ + protected boolean handleMultiRowHideCommand(MultiRowHideCommand command) { + // transform position to index + if (command.convertToTargetLayer(this)) { + List rowPositionsToHide = new ArrayList(); + for (Integer rowPos : command.getRowPositions()) { + rowPositionsToHide.add(rowPos); + int rowIndex = getRowIndexByPosition(rowPos); + if (this.treeRowModel.hasChildren(rowIndex) + && !this.treeRowModel.isCollapsed(rowIndex)) { + List childIndexes = this.treeRowModel.getChildIndexes(rowIndex); + for (Integer childIndex : childIndexes) { + rowPositionsToHide.add(getRowPositionByIndex(childIndex)); + } + } + } + + int[] childPositions = new int[rowPositionsToHide.size()]; + for (int i = 0; i < rowPositionsToHide.size(); i++) { + childPositions[i] = rowPositionsToHide.get(i); + } + return super.doCommand(new MultiRowHideCommand(this, childPositions)); + } + return super.doCommand(command); + } + + /** + * @return true 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 getProvidedLabels() { + Collection result = super.getProvidedLabels(); + + result.add(TreeLayer.TREE_COLUMN_CELL); + result.add(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE); + result.add(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE); + result.add(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE); + // configure 5 levels to be configurable via CSS + // if you need more you need to override this method + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "0"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "1"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "2"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "3"); //$NON-NLS-1$ + result.add(DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + "4"); //$NON-NLS-1$ + + return result; + } +}