001/* ---------------------------------------------------------------------------- 002 The Kiwi Toolkit - A Java Class Library 003 Copyright (C) 1998-2004 Mark A. Lindner 004 005 This library is free software; you can redistribute it and/or 006 modify it under the terms of the GNU General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This library is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 General Public License for more details. 014 015 You should have received a copy of the GNU General Public License 016 along with this library; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 02111-1307, USA. 019 020 The author may be contacted at: mark_a_lindner@yahoo.com 021 ---------------------------------------------------------------------------- 022 $Log: KTreeModelTreeAdapter.java,v $ 023 Revision 1.1 2004/05/31 07:30:26 markl 024 Final cleanup and bugfixes of kiwi.ui.model. 025 026 Revision 1.5 2003/01/19 09:33:06 markl 027 Javadoc & comment header updates. 028 029 Revision 1.4 2001/03/12 04:54:33 markl 030 Source code cleanup. 031 032 Revision 1.3 1999/02/26 10:24:12 markl 033 Made some fixes in the handling of tree structure change and node removal 034 events 035 036 Revision 1.2 1999/01/10 03:12:19 markl 037 added GPL header & RCS tag 038 ---------------------------------------------------------------------------- 039*/ 040 041package kiwi.ui.model; 042 043import kiwi.event.*; 044 045import java.util.*; 046import javax.swing.*; 047import javax.swing.event.*; 048import javax.swing.tree.*; 049 050/** An adapter that allows a Swing <code>JTree</code> to be used with the 051 * <code>StaticTreeModel</code> and <code>DynamicTreeModel</code> data models. 052 * The adapter translates messages and events between the <code>JTree</code> 053 * and the <code>ITreeModel</code> implementation. 054 * 055 * @see javax.swing.JTree 056 * @see kiwi.ui.model.KTreeModel 057 * 058 * @author Mark Lindner 059 * @since Kiwi 2.0 060 */ 061 062public class KTreeModelTreeAdapter implements TreeModel, KTreeModelListener 063 { 064 private KTreeModel model = null; 065 private Vector listeners; 066 private JTree jtree; 067 private TreeWillExpandListener treeWillExpandListener = null; 068 private TreeExpansionListener treeExpansionListener = null; 069 private static final int emptyIndexList[] = new int[0]; 070 private static final Object emptyObjectList[] = new Object[0]; 071 072 /** Construct a new tree model tree adapter. Creates a new adapter for the 073 * specified JFC tree component. 074 * 075 * @param jtree The JFC tree component to be associated with this adapter. 076 */ 077 078 public KTreeModelTreeAdapter(JTree jtree) 079 { 080 this.jtree = jtree; 081 jtree.setShowsRootHandles(true); 082 listeners = new Vector(); 083 084 jtree.addTreeWillExpandListener(treeWillExpandListener 085 = new TreeWillExpandListener() 086 { 087 public void treeWillExpand(TreeExpansionEvent evt) 088 { 089 Object node = evt.getPath().getLastPathComponent(); 090 091 if(model != null) 092 model.preloadChildren(node); 093 } 094 095 public void treeWillCollapse(TreeExpansionEvent evt) 096 { 097 } 098 }); 099 100 jtree.addTreeExpansionListener(treeExpansionListener 101 = new TreeExpansionListener() 102 { 103 public void treeExpanded(TreeExpansionEvent evt) 104 { 105 } 106 107 public void treeCollapsed(TreeExpansionEvent evt) 108 { 109 Object node = evt.getPath().getLastPathComponent(); 110 111 if(model != null) 112 model.releaseChildren(node); 113 } 114 }); 115 } 116 117 /** Set the tree model to be used by this adapter. The adapter adds itself 118 * as a listener of the tree model. If a model was already set prior to this 119 * call, it is replaced, and the adapter removes itself as a listener from 120 * that model. 121 * 122 * @param model The model to set. 123 * @see #getTreeModel 124 */ 125 126 public void setTreeModel(KTreeModel model) 127 { 128 if(this.model != null) 129 this.model.removeTreeModelListener(this); 130 131 this.model = model; 132 model.addTreeModelListener(this); 133 134// model.preloadChildren(model.getRoot()); // should this be here? 135 136 fireTreeStructureChanged(model.getRoot()); 137 } 138 139 /** Get the tree model in use by this adapter. Returns the tree model 140 * currently associated with this adapter (may be <code>null</code> if no 141 * model has been set). 142 * 143 * @see #setTreeModel 144 */ 145 146 public KTreeModel getTreeModel() 147 { 148 return(model); 149 } 150 151 /** Add a tree model listener. Adds a <code>TreeModelListener</code> to this 152 * adapter's list of tree model listeners. 153 * 154 * @param listener The listener to add. 155 * @see #removeTreeModelListener 156 */ 157 158 public void addTreeModelListener(TreeModelListener listener) 159 { 160 listeners.addElement(listener); 161 } 162 163 /** Remove a tree model listener. Removes a <code>TreeModelListener</code> 164 * from this adapter's list of tree model listeners. 165 * 166 * @param listener The listener to remove. 167 * @see #addTreeModelListener 168 */ 169 170 public void removeTreeModelListener(TreeModelListener listener) 171 { 172 listeners.removeElement(listener); 173 } 174 175 /** Invoked when nodes are added to the model. */ 176 177 public void nodesAdded(KTreeModelEvent evt) 178 { 179 fireTreeNodesInserted(evt.getNode(), evt.getStartIndex(), 180 evt.getEndIndex()); 181 } 182 183 /** Invoked when nodes are removed from the model. */ 184 185 public void nodesRemoved(KTreeModelEvent evt) 186 { 187 fireTreeNodesRemoved(evt.getNode(), evt.getStartIndex(), 188 evt.getEndIndex()); 189 } 190 191 /** Invoked when nodes are changed in the model. */ 192 193 public void nodesChanged(KTreeModelEvent evt) 194 { 195 fireTreeNodesChanged(evt.getNode(), evt.getStartIndex(), 196 evt.getEndIndex()); 197 } 198 199 /** Invoked when the structure of a portion of the model is changed. */ 200 201 public void structureChanged(KTreeModelEvent evt) 202 { 203 fireTreeStructureChanged(evt.getNode()); 204 } 205 206 /** Invoked when the structure of the entire model has changed. */ 207 208 public void dataChanged(KTreeModelEvent evt) 209 { 210 fireTreeStructureChanged(null); 211 } 212 213 /** Get the child of a node. (Implementation of <code>TreeModel</code>.) 214 */ 215 216 public Object getChild(Object parent, int index) 217 { 218 if(model == null) 219 return(null); 220 221 return(model.getChild(parent, index)); 222 } 223 224 /** Get the child count of a node. (Implementation of 225 * <code>TreeModel</code>.) 226 */ 227 228 public int getChildCount(Object parent) 229 { 230 if(model == null) 231 return(0); 232 233 return(model.getChildCount(parent)); 234 } 235 236 /** Determine if a node is a leaf. (Implementation of 237 * <code>TreeModel</code>.) 238 */ 239 240 public boolean isLeaf(Object node) 241 { 242 if(model == null) 243 return(true); 244 245 return(! model.isExpandable(node)); 246 } 247 248 /** Handle <code>JTree</code> events. (Implementation of 249 * <code>TreeModel</code>.) 250 */ 251 252 public void valueForPathChanged(TreePath path, Object newValue) 253 { 254 } 255 256 /** Get the index of a child within its parent. (Implementation of 257 * <code>TreeModel</code>.) 258 */ 259 260 public int getIndexOfChild(Object parent, Object child) 261 { 262 if(model == null) 263 return(-1); 264 265 return(model.getIndexOfChild(parent, child)); 266 } 267 268 /** Get the root of the tree. (Implementation of <code>TreeModel</code>.) 269 */ 270 271 public Object getRoot() 272 { 273 if(model == null) 274 return(null); 275 276 return(model.getRoot()); 277 } 278 279 /** Compute a <code>TreePath</code> for a given node. 280 * 281 * @param node The node. 282 * @return A <code>TreePath</code> to the given node. 283 * @see javax.swing.tree.TreePath 284 */ 285 286 public TreePath getPathForNode(Object node) 287 { 288 Vector v = new Vector(); 289 Object n = node; 290 291 if(n != null) 292 { 293 v.insertElementAt(n, 0); 294 while((n = model.getParent(n)) != null) 295 v.insertElementAt(n, 0); 296 } 297 298 Object path[] = new Object[v.size()]; 299 v.copyInto(path); 300 301 return(new TreePath(path)); 302 } 303 304 /** Fire a <i>tree nodes inserted</i> event. This event notifies the 305 * <code>JTree</code> that new nodes have been added to the tree data model. 306 * 307 * @param parent The parent of the new nodes. 308 * @param startIndex The offset of the first node in the range of nodes that 309 * were added. 310 * @param endIndex The offset of the last node in the range of nodes that 311 * were added. 312 */ 313 314 protected void fireTreeNodesInserted(Object parent, int startIndex, 315 int endIndex) 316 { 317 TreeModelEvent evt = null; 318 319 Enumeration e = listeners.elements(); 320 while(e.hasMoreElements()) 321 { 322 if(evt == null) 323 { 324 int indices[] = emptyIndexList; 325 Object objects[] = emptyObjectList; 326 327 if((startIndex >= 0) && (endIndex >= 0)) 328 { 329 int len = endIndex - startIndex + 1; 330 indices = new int[len]; 331 objects = new Object[len]; 332 333 for(int i = 0, pos = startIndex; i < len; i++, pos++) 334 { 335 indices[i] = pos; 336 objects[i] = model.getChild(parent, pos); 337 } 338 } 339 340 evt = new TreeModelEvent(this, getPathForNode(parent), indices, 341 objects); 342 } 343 344 TreeModelListener l = (TreeModelListener)e.nextElement(); 345 346 l.treeNodesInserted(evt); 347 } 348 } 349 350 /** Fire a <i>tree nodes removed</i> event. This event notifies the 351 * <code>JTree</code> that a node has been removed from the tree data model. 352 * 353 * @param parent The parent of the nodes that were removed. 354 * @param startIndex The offset of the first node in the range of nodes that 355 * were removed. 356 * @param endIndex The offset of the last node in the range of nodes that 357 * were removed. 358 */ 359 360 protected void fireTreeNodesRemoved(Object parent, int startIndex, 361 int endIndex) 362 { 363 TreeModelEvent evt = null; 364 365 Enumeration e = listeners.elements(); 366 while(e.hasMoreElements()) 367 { 368 if(evt == null) 369 { 370 int indices[] = emptyIndexList; 371 Object objects[] = emptyObjectList; 372 373 if((startIndex >= 0) && (endIndex >= 0)) 374 { 375 int len = endIndex - startIndex + 1; 376 indices = new int[len]; 377 objects = new Object[len]; 378 379 for(int i = 0, pos = startIndex; i < len; i++, pos++) 380 { 381 indices[i] = pos; 382 objects[i] = model.getChild(parent, pos); 383 } 384 } 385 386 evt = new TreeModelEvent(this, getPathForNode(parent), indices, 387 objects); 388 } 389 TreeModelListener l = (TreeModelListener)e.nextElement(); 390 l.treeNodesRemoved(evt); 391 } 392 } 393 394 /** Fire a <i>tree nodes changed</i> event. This event notifies the 395 * <code>JTree</code> that a node has changed in some way. 396 * 397 * @param parent The parent of the nodes that changed. 398 * @param startIndex The offset of the first node in the range of nodes that 399 * changed. 400 * @param endIndex The offset of the last node in the range of nodes that 401 * changed. 402 */ 403 404 protected void fireTreeNodesChanged(Object parent, int startIndex, 405 int endIndex) 406 { 407 TreeModelEvent evt = null; 408 409 Enumeration e = listeners.elements(); 410 while(e.hasMoreElements()) 411 { 412 if(evt == null) 413 { 414 int indices[] = emptyIndexList; 415 Object objects[] = emptyObjectList; 416 417 if((startIndex >= 0) && (endIndex >= 0)) 418 { 419 int len = endIndex - startIndex + 1; 420 indices = new int[len]; 421 objects = new Object[len]; 422 423 for(int i = 0, pos = startIndex; i < len; i++, pos++) 424 { 425 indices[i] = pos; 426 objects[i] = model.getChild(parent, pos); 427 } 428 } 429 430 evt = new TreeModelEvent(this, getPathForNode(parent), indices, 431 objects); 432 } 433 434 TreeModelListener l = (TreeModelListener)e.nextElement(); 435 l.treeNodesChanged(evt); 436 } 437 } 438 439 /** Fire a <i>tree structure changed</i> event. This event notifies the 440 * <code>JTree</code> that the subtree rooted at a given node has changed in 441 * some major way. 442 * 443 * @param node The node that is the root of the subtree that changed. 444 */ 445 446 protected void fireTreeStructureChanged(Object node) 447 { 448 TreeModelEvent evt = null; 449 450 Enumeration e = listeners.elements(); 451 while(e.hasMoreElements()) 452 { 453 if(evt == null) 454 evt = new TreeModelEvent(this, (node == null) ? null 455 : getPathForNode(node)); 456 457 TreeModelListener l = (TreeModelListener)e.nextElement(); 458 l.treeStructureChanged(evt); 459 } 460 } 461 462 /** Dispose of the adapter. Causes the adapter to detach its listeners from 463 * its associated <code>JTree</code> component, and then null out its 464 * references to the <code>JTree</code> and to the associated 465 * <code>KTreeModel</code>. 466 */ 467 468 public void dispose() 469 { 470 if(treeWillExpandListener != null) 471 jtree.removeTreeWillExpandListener(treeWillExpandListener); 472 if(treeExpansionListener != null) 473 jtree.removeTreeExpansionListener(treeExpansionListener); 474 treeWillExpandListener = null; 475 treeExpansionListener = null; 476 477 if(model != null) 478 model.removeTreeModelListener(this); 479 480 model = null; 481 jtree = null; 482 } 483 484 } 485 486/* end of source file */