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 }