import javax.swing.tree.TreePath;
import javax.swing.tree.TreeModel;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.*;

import java.io.IOException;
import java.io.ObjectInputStream;


// Delegate TreeModelListener method to the outer class
// also some minor bug fixes
// ----------------------------------------------------------------
// BUG / IN                       JTree          BasicTreeUI/Caches
//
// 1. transition to null root
//    keeps expanded paths          X
// 2. transition to null root
//    keeps selection               X              hides this(4.)
// 3. treeStructureChanged for
//    root expands it silently      *                  *
// 4. treeStructureChanged for
//    root deselects it                                *
// 5. root change keeps old
//    root selected                 X              hides this(4.)
//
// X fixed, * not fixable (because it occurs/is assumed so in TreeUI)

public class FTree2
    extends JTree
{
    private transient TreeModelListener delegate;


    public FTree2(TreeModel data)
    {
        super(data);

        init();
    }

    private void readObject(ObjectInputStream in)
        throws IOException, ClassNotFoundException
    {
        in.defaultReadObject();

        init();
    }

    private void init()
    {
        delegate = FTree2.super.createTreeModelListener();
    }



    public void setModel(TreeModel value)
    {
        TreeModelListener oldTreeModelListener = treeModelListener;
        TreeModel old = getModel();

        super.setModel(value);

        // tricky because super.setModel() may just have created the listener
        // but this does not happen in de-serialization, so it's easier
        // this way.
        if (oldTreeModelListener != null)
            ((Listener)treeModelListener).sourceReplaced(old, getModel());
    }


    protected void rootChanged(TreeModelEvent e)
    {
        delegate.treeNodesChanged(e);
    }

    protected void childrenChanged(TreeModelEvent e)
    {
        delegate.treeNodesChanged(e);
    }

    protected void childrenAdded(TreeModelEvent e)
    {
        delegate.treeNodesInserted(e);
    }

    protected void childrenRemoved(TreeModelEvent e)
    {
        delegate.treeNodesRemoved(e);
    }

    protected void newRoot(TreeModelEvent e)
    {
        delegate.treeStructureChanged(e);

        clearSelection();

        if (e.getTreePath() == null)
            clearToggledPaths();
    }

    protected void structureChanged(TreeModelEvent e)
    {
        delegate.treeStructureChanged(e);
    }



    protected TreeModelListener createTreeModelListener()
    {
	 return new Listener();
    }


    protected class Listener
        extends AbstractTreeModelListener
    {
        public Listener()
        {
            super(getModel());
        }

        
        protected void rootChanged(TreeModelEvent e)
        {
            FTree2.this.rootChanged(e);
        }

        protected void childrenChanged(TreeModelEvent e)
        {
            FTree2.this.childrenChanged(e);
        }

        protected void childrenAdded(TreeModelEvent e)
        {
            FTree2.this.childrenAdded(e);
        }

        protected void childrenRemoved(TreeModelEvent e)
        {
            FTree2.this.childrenRemoved(e);
        }

        protected void newRoot(TreeModelEvent e)
        {
            FTree2.this.newRoot(e);
        }

        protected void structureChanged(TreeModelEvent e)
        {
            FTree2.this.structureChanged(e);
        }
    }
}


