| /******************************************************************************* |
| * 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 |
| * Dirk Fauth <dirk.fauth@googlemail.com> - Bug 453707 |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.extension.glazedlists.tree; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.nebula.widgets.nattable.tree.AbstractTreeRowModel; |
| |
| import ca.odell.glazedlists.TreeList; |
| |
| public class GlazedListTreeRowModel<T> extends AbstractTreeRowModel<T> { |
| |
| public GlazedListTreeRowModel(GlazedListTreeData<T> treeData) { |
| super(treeData); |
| } |
| |
| /** |
| * Note: The check only works for visible elements, therefore a check for an |
| * element that is below an collapsed element does not work. |
| */ |
| @Override |
| public boolean isCollapsed(int index) { |
| if (!this.getTreeData().isValidIndex(index)) { |
| return false; |
| } |
| |
| return !this.getTreeData().getTreeList().isExpanded(index); |
| } |
| |
| /** |
| * Collapse the tree node at the given visible index. |
| * |
| * @param index |
| * The index of the node in the collection that should be |
| * collapsed. |
| * |
| * @return Returns an empty list because the list transformation for |
| * expand/collapse is handled by GlazedLists {@link TreeList}. |
| */ |
| @Override |
| public List<Integer> collapse(int index) { |
| if (this.getTreeData().isValidIndex(index)) { |
| this.getTreeData().getTreeList().setExpanded(index, false); |
| notifyListeners(); |
| } |
| |
| return new ArrayList<>(); |
| } |
| |
| /** |
| * Will check the whole TreeList for its nodes and collapses them if they |
| * are expanded. After executing this method, all nodes in the TreeList will |
| * be collapsed. |
| * |
| * @return Returns an empty list because the list transformation for |
| * expand/collapse is handled by GlazedLists {@link TreeList}. |
| */ |
| @Override |
| public List<Integer> collapseAll() { |
| TreeList<T> treeList = this.getTreeData().getTreeList(); |
| treeList.getReadWriteLock().writeLock().lock(); |
| try { |
| // iterating directly over the TreeList is a lot faster than |
| // checking the nodes |
| // which is related that on collapsing we only need to iterate once |
| // from bottom to top |
| for (int i = (treeList.size() - 1); i >= 0; i--) { |
| /* |
| * Checks if the node at the given visible index has children |
| * and is collapsible. If it is it will be collapsed otherwise |
| * skipped. This backwards searching and collapsing mechanism is |
| * necessary to ensure to really get every collapsible node in |
| * the whole tree structure. |
| */ |
| if (hasChildren(i) && !isCollapsed(i)) { |
| treeList.setExpanded(i, false); |
| } |
| } |
| } finally { |
| treeList.getReadWriteLock().writeLock().unlock(); |
| } |
| |
| notifyListeners(); |
| return new ArrayList<>(); |
| } |
| |
| /** |
| * Expand the tree node at the given visible index. |
| * |
| * @param index |
| * The index of the node in the collection that should be |
| * expanded. |
| * |
| * @return Returns an empty list because the list transformation for |
| * expand/collapse is handled by GlazedLists {@link TreeList}. |
| */ |
| @Override |
| public List<Integer> expand(int index) { |
| if (this.getTreeData().isValidIndex(index)) { |
| this.getTreeData().getTreeList().setExpanded(index, true); |
| notifyListeners(); |
| } |
| |
| return new ArrayList<>(); |
| } |
| |
| @Override |
| public List<Integer> expandToLevel(int parentIndex, int level) { |
| if (this.getTreeData().isValidIndex(parentIndex)) { |
| internalExpandToLevel(parentIndex, level); |
| notifyListeners(); |
| } |
| |
| return new ArrayList<>(); |
| } |
| |
| /** |
| * Performs the expand operations iteratively without notifying the |
| * listeners while processing. |
| * |
| * @param parentIndex |
| * The index of the node in the collection that should be |
| * expanded. |
| * @param level |
| * The level to which the tree nodes should be expanded. |
| */ |
| protected void internalExpandToLevel(int parentIndex, int level) { |
| if (this.getTreeData().isValidIndex(parentIndex) |
| && depth(parentIndex) <= (level - 1)) { |
| |
| this.getTreeData().getTreeList().setExpanded(parentIndex, true); |
| |
| List<Integer> directChildren = getDirectChildIndexes(parentIndex); |
| // iterate backwards because the indexes are changing on expand |
| Collections.sort(directChildren, Collections.reverseOrder()); |
| for (Integer child : directChildren) { |
| if (hasChildren(child) && depth(child) <= (level - 1)) { |
| internalExpandToLevel(child, level); |
| } |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @return Returns an empty list because the list transformation for |
| * expand/collapse is handled by GlazedLists {@link TreeList}. |
| */ |
| @Override |
| public List<Integer> expandAll() { |
| internalExpandAll(); |
| notifyListeners(); |
| return new ArrayList<>(); |
| } |
| |
| /** |
| * Performs the expand operations iteratively without notifying the |
| * listeners while processing. |
| */ |
| protected void internalExpandAll() { |
| TreeList<T> treeList = this.getTreeData().getTreeList(); |
| |
| boolean expandPerformed = false; |
| treeList.getReadWriteLock().writeLock().lock(); |
| try { |
| // iterating directly over the TreeList is a lot faster than |
| // checking the nodes |
| for (int i = (treeList.size() - 1); i >= 0; i--) { |
| /* |
| * Checks if the node at the given visible index has children |
| * and is expandable. If it is it will be expanded otherwise |
| * skipped. This backwards searching and expanding mechanism is |
| * necessary to ensure to really get every expandable node in |
| * the whole tree structure. |
| */ |
| if (hasChildren(i) && isCollapsed(i)) { |
| treeList.setExpanded(i, true); |
| expandPerformed = true; |
| } |
| } |
| } finally { |
| treeList.getReadWriteLock().writeLock().unlock(); |
| } |
| |
| // if at least one element was expanded we need to perform the step |
| // again as we are only able to retrieve the visible nodes |
| if (expandPerformed) { |
| expandAll(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @return Returns an empty list because the list transformation for |
| * expand/collapse is handled by GlazedLists {@link TreeList}. |
| */ |
| @Override |
| public List<Integer> expandToLevel(int level) { |
| internalExpandToLevel(level); |
| notifyListeners(); |
| return new ArrayList<>(); |
| } |
| |
| /** |
| * Performs the expand operations iteratively without notifying the |
| * listeners while processing. |
| * |
| * @param level |
| * The level to which the tree nodes should be expanded. |
| */ |
| protected void internalExpandToLevel(int level) { |
| TreeList<T> treeList = this.getTreeData().getTreeList(); |
| |
| boolean expandPerformed = false; |
| treeList.getReadWriteLock().writeLock().lock(); |
| try { |
| // iterating directly over the TreeList is a lot faster than |
| // checking the nodes |
| for (int i = (treeList.size() - 1); i >= 0; i--) { |
| /* |
| * Checks if the node at the given visible index has children, |
| * is expandable and is on a level below the given level. If it |
| * is it will be expanded otherwise skipped. This backwards |
| * searching and expanding mechanism is necessary to ensure to |
| * really get every expandable node in the whole tree structure. |
| */ |
| if (hasChildren(i) && isCollapsed(i) && treeList.getTreeNode(i).path().size() <= level) { |
| treeList.setExpanded(i, true); |
| expandPerformed = true; |
| } |
| } |
| } finally { |
| treeList.getReadWriteLock().writeLock().unlock(); |
| } |
| |
| // if at least one element was expanded we need to perform the step |
| // again as we are only able to retrieve the visible nodes |
| if (expandPerformed) { |
| expandToLevel(level); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @since 1.6 |
| */ |
| @Override |
| public GlazedListTreeData<T> getTreeData() { |
| return (GlazedListTreeData<T>) super.getTreeData(); |
| } |
| } |