/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.ui.common.tree; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.events.TreeEvent; import org.eclipse.swt.events.TreeListener; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.teiid.core.designer.event.IChangeListener; import org.teiid.core.designer.event.IChangeNotifier; /** * TreeExpansionMonitor is a utility class that monitors the expanding & collapsing of * tree nodes such that it can quickly return a list of visible objects. The class works * by listening to tree expansion and collapse, then marking the child TreeItems with * a visibility key. * * @since 8.0 */ public class TreeExpansionMonitor implements TreeListener, IChangeNotifier { /** key for setting data on TreeItem */ private static final String VISIBLE = "TreeExpansionMonitor.visible"; //$NON-NLS-1$ /** this monitor's TreeViewer */ protected TreeViewer treeViewer; /** cached state */ protected List visibleNodes; protected List visibleTreeItems; /** IChangeListeners */ protected ArrayList listenerList = new ArrayList(); /** cached state monitor */ protected boolean isStale = true; /** * Construct an instance of TreeExpansionMonitor to monitor the specified TreeViewer. */ public TreeExpansionMonitor(TreeViewer treeViewer) { this.treeViewer = treeViewer; treeViewer.getTree().addTreeListener(this); // System.out.println("[TreeExpansionMonitor.ctor] Creating a new TreeExpansionMonitor..."); } /** * Unhooks this monitor from the TreeViewer. */ public void dispose() { Tree tree = treeViewer.getTree(); if ( tree != null && !tree.isDisposed() ) { tree.removeTreeListener( this ); } } public void setIsStale( boolean bIsStale ) { // System.out.println( "[TreeExpansionoMonitor.setIsStale] TOP" ); isStale = bIsStale; } /** * Return an ordered List of user objects that are displayed in the TreeViewer. * @result a List of objects representing the currently expanded state of the tree. * Any nodes that are beneath collapsed parent nodes will not be returned in this List. */ public List getVisibleObjects() { if ( isStale || visibleNodes == null ) { /* * jh Lyra enh: this is the only method that updates 'visibleTreeItems', so we * must fix this, because we use that array in other Lyra processing. */ // System.out.println("\n\n[TreeExpansionMonitor.getVisibleObjects] About to REFRESH the VISIBLE OBJECTS!!!!"); treeViewer.getTree().update(); // update visibleTreeItems TreeItem[] items = treeViewer.getTree().getItems(); visibleTreeItems = new ArrayList( items.length ); // update visibleNodes ArrayList nodes = new ArrayList(items.length); internalGetVisibleObjects( visibleTreeItems, nodes, items ); isStale = false; visibleNodes = Collections.unmodifiableList(nodes); } else { // System.out.println("[TreeExpansionMonitor.getVisibleObjects] will NOT refresh the visible objects " ); } // System.out.println("[TreeExpansionMonitor.getVisibleObjects] visible object count is: " + visibleNodes.size() ); return visibleNodes; } /** * Return a TreeNodeMap of user objects that are displayed in the TreeViewer. * Its key is the treenode and its value is the index in the visibleNodes list * @result a List of objects representing the currently expanded state of the tree. * Any nodes that are beneath collapsed parent nodes will not be returned in this List. */ public TreeNodeMap getVisibleObjectsAsMap() { if ( isStale || visibleNodes == null ) { /* * jh Lyra enh: this is the only method that updates 'visibleTreeItems', so we * must fix this, because we use that array in other Lyra processing. */ // System.out.println("\n\n[TreeExpansionMonitor.getVisibleObjects] About to REFRESH the VISIBLE OBJECTS!!!!"); treeViewer.getTree().update(); // update visibleTreeItems TreeItem[] items = treeViewer.getTree().getItems(); visibleTreeItems = new ArrayList( items.length ); // update visibleNodes ArrayList nodes = new ArrayList(items.length); internalGetVisibleObjects( visibleTreeItems, nodes, items ); isStale = false; visibleNodes = Collections.unmodifiableList(nodes); } else { // System.out.println("[TreeExpansionMonitor.getVisibleObjects] will NOT refresh the visible objects " ); } // System.out.println("[TreeExpansionMonitor.getVisibleObjects] visible object count is: " + visibleNodes.size() ); return new TreeNodeMap( visibleNodes ); } /** * Return an ordered List of tree items that are displayed in the TreeViewer. * @result a List of tree items representing the currently expanded state of the tree. * Any nodes that are beneath collapsed parent nodes will not be returned in this List. */ public List getVisibleTreeItems() { // run the visible objects method to refresh both collections getVisibleObjects(); return visibleTreeItems; } private void internalGetVisibleObjects(List listItems, List listObjects, TreeItem[] items) { for ( int i=0 ; i<items.length ; ++i ) { // items[i].getData() was returning null. Defect 12204 fix: Add null check. if ( isVisible(items[i]) && items[i].getData() != null) { // capture the tree item: listItems.add( items[i] ); // capture the tree item's object, unless null // System.out.println("[TreeExpansionMonitor.internalGetVisibleObjects] About to add: " + items[i].getData() ); listObjects.add( items[i].getData() ); // Recurse on, Garth! internalGetVisibleObjects( listItems, listObjects, items[i].getItems() ); } } } /* (non-Javadoc) * @see org.eclipse.swt.events.TreeListener#treeCollapsed(org.eclipse.swt.events.TreeEvent) */ @Override public void treeCollapsed(TreeEvent e) { TreeItem item = (TreeItem) e.item; TreeItem[] children = item.getItems(); for ( int i=0 ; i<children.length ; ++i ) { children[i].setData(VISIBLE, Boolean.FALSE); } fireChangeEvent(); // System.out.println("\n\nTreeExpansionMonitor.treeCollapsed: nVisibleObjects = " + getVisibleObjects().size()); } /* (non-Javadoc) * @see org.eclipse.swt.events.TreeListener#treeExpanded(org.eclipse.swt.events.TreeEvent) */ @Override public void treeExpanded(TreeEvent e) { // System.out.println("\n\nTreeExpansionMonitor.treeExpanded: TOP" ); TreeItem item = (TreeItem) e.item; TreeItem[] children = item.getItems(); for ( int i=0 ; i<children.length ; ++i ) { children[i].setData(VISIBLE, Boolean.TRUE); } // System.out.println("TreeExpansionMonitor.treeExpanded: nVisibleObjects = " + getVisibleObjects().size()); fireChangeEvent(); } /** * Method designed to provide a way to directly set the expanded state of all items to "VISIBLE = TRUE". * This was needed for the expandAllAction in DocumentTreeController */ public void handleAllExpanded() { TreeItem item = treeViewer.getTree().getTopItem(); // This is needed to get the ball rolling. item.setData(VISIBLE, Boolean.TRUE); expandItem(item); fireChangeEvent(); // System.out.println("TreeExpansionMonitor.handleAllExpanded: nVisibleObjects = " + getVisibleObjects().size()); } private void expandItem(TreeItem item) { // System.out.println("[TreeExpansionMonitor.expandItem] TOP; " + item ); TreeItem[] children = item.getItems(); for ( int i=0 ; i<children.length ; ++i ) { children[i].setData(VISIBLE, Boolean.TRUE); expandItem(children[i]); } } private void collapseItem(TreeItem item) { TreeItem[] children = item.getItems(); for ( int i=0 ; i<children.length ; ++i ) { children[i].setData(VISIBLE, Boolean.FALSE); collapseItem(children[i]); } } /** * Method designed to provide a way to directly set the collapsed state of all items to "VISIBLE = FALSE". * This was needed for the collapseAllAction in DocumentTreeController */ public void handleAllCollapsed() { TreeItem item = treeViewer.getTree().getTopItem(); collapseItem(item); // Make sure that top item is VISIBLE item.setData(VISIBLE, Boolean.TRUE); fireChangeEvent(); // System.out.println("TreeExpansionMonitor.handleAllCollapsed: nVisibleObjects = " + getVisibleObjects().size()); } /** * Determine if the specified TreeItem in this monitor's TreeViewer is visible. * NOTE: this method is only reliable when walked from the top row of the tree * to the bottom. * Inner hidden nodes may return true when an ancestor is actually not visible. * @param item * @return */ protected boolean isVisible(TreeItem item) { boolean result = true; // boolean result = false; Object obj = item.getData(VISIBLE); if ( obj != null ) { result = ((Boolean) obj).booleanValue(); } return result; } public void fireChangeEvent() { isStale = true; for ( Iterator iter = listenerList.iterator() ; iter.hasNext() ; ) { IChangeListener listener = (IChangeListener) iter.next(); listener.stateChanged(this); } } /* (non-Javadoc) * @see com.metamatrix.core.event.IChangeNotifier#addChangeListener(com.metamatrix.core.event.IChangeListener) */ @Override public void addChangeListener(IChangeListener listener) { if ( ! listenerList.contains(listener) ) { listenerList.add(listener); } } /* (non-Javadoc) * @see com.metamatrix.core.event.IChangeNotifier#removeChangeListener(com.metamatrix.core.event.IChangeListener) */ @Override public void removeChangeListener(IChangeListener listener) { listenerList.remove(listener); } }