001    /*
002     * $Id: DefaultTreeTableModel.java,v 1.2 2005/10/10 18:01:38 rbair Exp $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.swingx.treetable;
023    
024    import javax.swing.event.TreeModelEvent;
025    import javax.swing.event.TreeModelListener;
026    import javax.swing.tree.TreeNode;
027    import javax.swing.tree.TreePath;
028    
029    /**
030     * DefaultTreeTableModel is a concrete implementation of <code>AbstractTreeTableModel</code>
031     * and is provided purely as a convenience. Applications that use <code>JXTreeTable</code>
032     * are expected to provide their own implementation of a <code>TreeTableModel</code>,
033     * perhaps by extending this class.
034     *
035     * @author Ramesh Gupta
036     */
037    public class DefaultTreeTableModel extends AbstractTreeTableModel {
038    
039        protected boolean asksAllowsChildren;
040    
041        public DefaultTreeTableModel() {
042            this(null);
043        }
044    
045        public DefaultTreeTableModel(TreeNode root) {
046            this(root, false);
047        }
048    
049        public DefaultTreeTableModel(TreeNode root, boolean asksAllowsChildren) {
050            super(root);
051            this.asksAllowsChildren = asksAllowsChildren;
052        }
053    
054        public void setRoot(TreeNode root) {
055            Object oldRoot = this.root;
056            this.root = root;
057            if (root == null && oldRoot != null) {
058                fireTreeStructureChanged(this, null);
059            }
060            else {
061                nodeStructureChanged(root);
062            }
063        }
064    
065        /*
066         * Notifies all listeners that have registered interest for
067         * notification on this event type.  The event instance
068         * is lazily created using the parameters passed into
069         * the fire method.
070         *
071         * @param source the node where the tree model has changed
072         * @param path the path to the root node
073         * @see EventListenerList
074         */
075        private void fireTreeStructureChanged(Object source, TreePath path) {
076            // Guaranteed to return a non-null array
077            Object[] listeners = listenerList.getListenerList();
078            TreeModelEvent e = null;
079            // Process the listeners last to first, notifying
080            // those that are interested in this event
081            for (int i = listeners.length - 2; i >= 0; i -= 2) {
082                if (listeners[i] == TreeModelListener.class) {
083                    // Lazily create the event:
084                    if (e == null)
085                        e = new TreeModelEvent(source, path);
086                    ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e);
087                }
088            }
089        }
090    
091        public boolean asksAllowsChildren() {
092            return asksAllowsChildren;
093        }
094    
095        public void setAsksAllowsChildren(boolean newValue) {
096            asksAllowsChildren = newValue;
097        }
098    
099        public Object getValueAt(Object node, int column) {
100            /**@todo Implement this org.jdesktopx.swing.treetable.TreeTableModel abstract method*/
101            return node + "@column " + column;
102        }
103    
104        public void setValueAt(Object value, Object node, int column) {
105            /**@todo Implement this org.jdesktopx.swing.treetable.TreeTableModel abstract method*/
106        }
107    
108        public TreeNode[] getPathToRoot(TreeNode node) {
109            return getPathToRoot(node, 0);
110        }
111    
112        protected TreeNode[] getPathToRoot(TreeNode node, int depth) {
113            TreeNode[] retNodes;
114            // This method recurses, traversing towards the root in order
115            // size the array. On the way back, it fills in the nodes,
116            // starting from the root and working back to the original node.
117    
118            /* Check for null, in case someone passed in a null node, or
119               they passed in an element that isn't rooted at root. */
120            if (node == null) {
121                if (depth == 0)
122                    return null;
123                else
124                    retNodes = new TreeNode[depth];
125            }
126            else {
127                depth++;
128                if (node == root)
129                    retNodes = new TreeNode[depth];
130                else
131                    retNodes = getPathToRoot(node.getParent(), depth);
132                retNodes[retNodes.length - depth] = node;
133            }
134            return retNodes;
135        }
136    
137        /**
138         * @param node
139         * @return true if the specified node is a leaf node; false otherwise
140         */
141        public boolean isLeaf(Object node) {
142            if (node instanceof TreeNode) {
143                if (asksAllowsChildren) {
144                    return!((TreeNode) node).getAllowsChildren();
145                }
146            }
147            return super.isLeaf(node);
148        }
149    
150        public void reload() {
151            TreeNode treeNode;
152            try {
153                treeNode = (TreeNode) root;
154            }
155            catch (ClassCastException ex) {
156                return;
157            }
158    
159            reload(treeNode);
160        }
161    
162        public void reload(TreeNode node) {
163            if (node != null) {
164                fireTreeStructureChanged(this, getPathToRoot(node), null, null);
165            }
166        }
167    
168        /**
169         * Invoke this method after you've inserted some TreeNodes into
170         * node.  childIndices should be the index of the new elements and
171         * must be sorted in ascending order.
172         */
173        public void nodesWereInserted(TreeNode node, int[] childIndices) {
174            if (listenerList != null && node != null && childIndices != null
175                && childIndices.length > 0) {
176                int cCount = childIndices.length;
177                Object[] newChildren = new Object[cCount];
178    
179                for (int counter = 0; counter < cCount; counter++)
180                    newChildren[counter] = node.getChildAt(childIndices[counter]);
181                fireTreeNodesInserted(this, getPathToRoot(node), childIndices,
182                                      newChildren);
183            }
184        }
185    
186        /**
187         * Invoke this method after you've removed some TreeNodes from
188         * node.  childIndices should be the index of the removed elements and
189         * must be sorted in ascending order. And removedChildren should be
190         * the array of the children objects that were removed.
191         */
192        public void nodesWereRemoved(TreeNode node, int[] childIndices,
193                                     Object[] removedChildren) {
194            if (node != null && childIndices != null) {
195                fireTreeNodesRemoved(this, getPathToRoot(node), childIndices,
196                                     removedChildren);
197            }
198        }
199    
200        /**
201         * Invoke this method after you've changed how the children identified by
202         * childIndicies are to be represented in the tree.
203         */
204        public void nodesChanged(TreeNode node, int[] childIndices) {
205            if (node != null) {
206                if (childIndices != null) {
207                    int cCount = childIndices.length;
208    
209                    if (cCount > 0) {
210                        Object[] cChildren = new Object[cCount];
211    
212                        for (int counter = 0; counter < cCount; counter++)
213                            cChildren[counter] = node.getChildAt
214                                (childIndices[counter]);
215                        fireTreeNodesChanged(this, getPathToRoot(node),
216                                             childIndices, cChildren);
217                    }
218                }
219                else if (node == getRoot()) {
220                    fireTreeNodesChanged(this, getPathToRoot(node), null, null);
221                }
222            }
223        }
224    
225        /**
226         * Invoke this method if you've totally changed the children of
227         * node and its childrens children...  This will post a
228         * treeStructureChanged event.
229         */
230        public void nodeStructureChanged(TreeNode node) {
231            if (node != null) {
232                fireTreeStructureChanged(this, getPathToRoot(node), null, null);
233            }
234        }
235    }