// *** HideableTreeModel ***
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
 

public class HideableTreeModel implements TreeModel {

	private Vector modelListeners = new Vector();
	private Object root = null;
    
        
        public HideableTreeModel(TreeNode _root) {
            super();
            setRoot(_root);            
	}
  
        
	public Object getRoot() {
		return this.root;
	}
 

        protected void setRoot(Object r) {
            this.root = r;
	}
        

        public Object[] getPathToRoot(Object node) {
            return getPathToRoot(node, 0);
	}
 

        private Object[] getPathToRoot(Object node, int i) {
            Object anode[];
            if(node == null) {
                if(i == 0) {
                    return null;
                }
                anode = new Object[i];
            } else {
                i++;
                if(node == getRoot()) {
                    anode = new Object[i];
                } else {
                    anode = getPathToRoot(getParent(node), i);
                }
                anode[anode.length - i] = node;
            }
            return anode;
	}
  

        public void addTreeModelListener(TreeModelListener l) {
            modelListeners.addElement(l);
	}
 

        public void removeTreeModelListener(TreeModelListener l) {
            modelListeners.removeElement(l);
	}
 

	public void reload() {
            reload(getRoot());
	}
 

        public void reload(Object node) {
            if(node != null) {
                TreePath tp = new TreePath(getPathToRoot(node));
                fireTreeStructureChanged(new TreeModelEvent(this, tp));
            }
	}
 

        public void valueForPathChanged(TreePath path, Object newValue) {
            nodeChanged(path.getLastPathComponent());
	}
 
	public void nodeInserted(Object node, Object child) {
            nodeInserted(node, child, -1);
	}
 

        public void nodeInserted(Object node, Object child, int index) {
            if(index < 0) {
                index = getIndexOfChild(node, child);
            }
            if(node != null && child != null && index >= 0) {
                TreePath tp = new TreePath(getPathToRoot(node));
                int[] ai = { index };
                Object[] ac = { child };
                fireTreeNodesInserted(new TreeModelEvent(this, tp, ai, ac));
            }
	}
 

        public void nodeRemoved(Object node, Object child, int index) {
            if(node != null && child != null && index >= 0) {
                TreePath tp = new TreePath(getPathToRoot(node));
                int[] ai = { index };
                Object[] ac = { child };
                fireTreeNodesRemoved(new TreeModelEvent(this, tp, ai, ac));
            }
	}
 

        public void nodeChanged(Object node) {
            if(node != null) {
                TreePath tp = new TreePath(getPathToRoot(node));
                fireTreeNodesChanged(new TreeModelEvent(this, tp, null, null));
            }
	}
 

        protected void fireTreeNodesChanged(TreeModelEvent event) {
            for(int i = 0; i < modelListeners.size(); i++) {
                ((TreeModelListener)modelListeners.elementAt(i)).treeNodesChanged(event);
            }
	}
 

        protected void fireTreeNodesInserted(TreeModelEvent event) {
            for(int i = 0; i < modelListeners.size(); i++) {
                ((TreeModelListener)modelListeners.elementAt(i)).treeNodesInserted(event);
            }
	}
 

        protected void fireTreeNodesRemoved(TreeModelEvent event) {
            for(int i = 0; i < modelListeners.size(); i++) {
                ((TreeModelListener)modelListeners.elementAt(i)).treeNodesRemoved(event);
            }
	}
 
	protected void fireTreeStructureChanged(TreeModelEvent event) {
            for(int i = 0; i < modelListeners.size(); i++) {
                ((TreeModelListener)modelListeners.elementAt(i)).treeStructureChanged(event);
            }
	}
 

        public ArrayList getExpandedPaths(JTree tree) {
		ArrayList expandedPaths = new ArrayList();
		addExpandedPaths(tree, tree.getPathForRow(0), expandedPaths);
		return expandedPaths;
	}
 

        private void addExpandedPaths(JTree tree, TreePath path, ArrayList pathlist) {
            Enumeration aEnum = tree.getExpandedDescendants(path);
            while(aEnum.hasMoreElements()) {
                TreePath tp = (TreePath) aEnum.nextElement();
                pathlist.add(tp);
                addExpandedPaths(tree, tp, pathlist);
            }
	}
 

        public void expandPaths(JTree tree, ArrayList pathlist) {
            for(int i = 0; i < pathlist.size(); i++) {
                tree.expandPath((TreePath)pathlist.get(i));
            }
	}
                

        public boolean isLeaf(Object _oNode) {
            if(_oNode instanceof TreeNode) {
                return ((TreeNode) _oNode).isLeaf();
            }
            return true;
	}
        
        
        
	public Object getParent(Object node) {
            if(node != getRoot() && (node instanceof TreeNode)) {
                return ((TreeNode)node).getParent();
            }
            return null;
	}
        

        public boolean isNodeVisible(Object node) {
            if(node != getRoot()) {
                if(node instanceof HideableMutableTreeNode) {
                        return ((HideableMutableTreeNode)node).isVisible();
                }
            }
            return true;
	}
 

        public boolean setNodeVisible(Object node, boolean v) {
            // can't hide root
            if(node != getRoot()) {
                if(node instanceof HideableMutableTreeNode) {
                    HideableMutableTreeNode n = (HideableMutableTreeNode)node;
                    if(v != n.isVisible()) {
                        TreeNode parent = n.getParent();
                        if(v) {
                            // need to get index after showing...
                            n.setVisible(v);
                            int index = getIndexOfChild(parent, n);
                            nodeInserted(parent, n, index);
                        } else {
                            // need to get index before hiding...
                            int index = getIndexOfChild(parent, n);
                            n.setVisible(v);
                            nodeRemoved(parent, n, index);
                        }
                    }
                    return true;
                }
            }
            return false;
	}
 

        public boolean isPathToNodeVisible(Object node) {
            Object[] path = getPathToRoot(node);
            for(int i = 0; i < path.length; i++) {
                if(!isNodeVisible(path[i])) {
                    return false;
                }
            }
            return true;
	}
 

        public void ensurePathToNodeVisible(Object node) {
            Object[] path = getPathToRoot(node);
            for(int i = 0; i < path.length; i++) {
                setNodeVisible(path[i], true);
            }
	}
 

        public Object getChild(Object parent, int index) {
            if(parent instanceof TreeNode) {
                TreeNode p = (TreeNode) parent;
                for(int i = 0, j = -1; i < p.getChildCount(); i++) {
                    TreeNode pc = (TreeNode)p.getChildAt(i);
                    if(isNodeVisible(pc)) {
                        j++;
                    }
                    if(j == index) {
                        return pc;
                    }
                }
            }
            return null;
	}
 

        public int getChildCount(Object parent) {
            int count = 0;
            if(parent instanceof TreeNode) {
                TreeNode p = (TreeNode) parent;
                for(int i = 0; i < p.getChildCount(); i++) {
                    TreeNode pc = (TreeNode)p.getChildAt(i);
                    if(isNodeVisible(pc)) {
                        count++;
                    }
                }
            }
            return count;
	}
 

        public int getIndexOfChild(Object parent, Object child) {
            int index = -1;
            if(parent instanceof TreeNode && child instanceof TreeNode) {
                TreeNode p = (TreeNode)parent;
                TreeNode c = (TreeNode)child;
                if(isNodeVisible(c)) {
                    index = 0;
                    for(int i = 0; i < p.getChildCount(); i++) {
                        TreeNode pc = (TreeNode)p.getChildAt(i);
                        if(pc.equals(c)) {
                            return index;
                        }
                        if(isNodeVisible(pc)) {
                            index++;
                        }
                    }
                }
            }
            return index;
	}
}