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 }