1. /*
  2. * @(#)DefaultTreeSelectionModel.java 1.26 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.tree;
  8. import java.beans.PropertyChangeListener;
  9. import java.io.*;
  10. import java.util.BitSet;
  11. import java.util.EventListener;
  12. import java.util.Vector;
  13. import javax.swing.event.*;
  14. import javax.swing.DefaultListSelectionModel;
  15. /**
  16. * Implementation of TreeSelectionModel. Listeners are notified whenever
  17. * the paths in the selection change, not the rows. In order
  18. * to be able to track row changes you may wish to become a listener
  19. * for expansion events on the tree and test for changes from there.
  20. * <p>resetRowSelection is called from any of the methods that update
  21. * the selected paths. If you subclass and of these methods to
  22. * filter what is allowed to be selected, be sure and message
  23. * resetRowSelection if you do not message super.
  24. * <p>
  25. * <strong>Warning:</strong>
  26. * Serialized objects of this class will not be compatible with
  27. * future Swing releases. The current serialization support is appropriate
  28. * for short term storage or RMI between applications running the same
  29. * version of Swing. A future release of Swing will provide support for
  30. * long term persistence.
  31. *
  32. * @version 1.26 11/29/01
  33. * @author Scott Violet
  34. */
  35. public class DefaultTreeSelectionModel extends Object implements Cloneable, Serializable, TreeSelectionModel
  36. {
  37. /** Property name for selectionMode. */
  38. public static final String SELECTION_MODE_PROPERTY = "selectionMode";
  39. /** Used to messaged registered listeners. */
  40. protected SwingPropertyChangeSupport changeSupport;
  41. /** Paths that are currently selected. Will be null if nothing is
  42. * currently selected. */
  43. protected TreePath[] selection;
  44. /** Event listener list. */
  45. protected EventListenerList listenerList = new EventListenerList();
  46. /** Provides a row for a given path. */
  47. transient protected RowMapper rowMapper;
  48. /** Handles maintaining the list selection model. */
  49. protected DefaultListSelectionModel listSelectionModel;
  50. /** Mode for the selection, will be either SINGLE_TREE_SELECTION,
  51. * CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION.
  52. */
  53. protected int selectionMode;
  54. /** Last path that was added. */
  55. protected TreePath leadPath;
  56. /** Index of the lead path in selection. */
  57. protected int leadIndex;
  58. /** Lead row. */
  59. protected int leadRow;
  60. /**
  61. * Creates a new instance of DefaultTreeSelectionModel that is
  62. * empty, and having a selection mode of DISCONTIGUOUS_TREE_SELECTION.
  63. */
  64. public DefaultTreeSelectionModel() {
  65. listSelectionModel = new DefaultListSelectionModel();
  66. selectionMode = DISCONTIGUOUS_TREE_SELECTION;
  67. leadIndex = leadRow = -1;
  68. }
  69. /**
  70. * Sets the RowMapper instance. This instance is used to determine
  71. * what row corresponds to what path.
  72. */
  73. public void setRowMapper(RowMapper newMapper) {
  74. rowMapper = newMapper;
  75. resetRowSelection();
  76. }
  77. /**
  78. * Returns the RowMapper instance that is able to map a path to a
  79. * row.
  80. */
  81. public RowMapper getRowMapper() {
  82. return rowMapper;
  83. }
  84. /**
  85. * Sets the selection model, which must be one of SINGLE_TREE_SELECTION,
  86. * CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION.
  87. */
  88. public void setSelectionMode(int mode) {
  89. int oldMode = selectionMode;
  90. selectionMode = mode;
  91. if(selectionMode != TreeSelectionModel.SINGLE_TREE_SELECTION &&
  92. selectionMode != TreeSelectionModel.CONTIGUOUS_TREE_SELECTION &&
  93. selectionMode != TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
  94. selectionMode = TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION;
  95. if(oldMode != selectionMode && changeSupport != null)
  96. changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY,
  97. new Integer(oldMode),
  98. new Integer(selectionMode));
  99. }
  100. /**
  101. * Returns the selection mode.
  102. */
  103. public int getSelectionMode() {
  104. return selectionMode;
  105. }
  106. /**
  107. * Sets the selection to path. If this represents a change, then
  108. * the TreeSelectionListeners are notified.
  109. *
  110. * @param path new path to select
  111. */
  112. public void setSelectionPath(TreePath path) {
  113. if(path == null)
  114. setSelectionPaths(null);
  115. else {
  116. TreePath[] newPaths = new TreePath[1];
  117. newPaths[0] = path;
  118. setSelectionPaths(newPaths);
  119. }
  120. }
  121. /**
  122. * Sets the selection to the paths in paths. If this represents a
  123. * change the TreeSelectionListeners are notified. Potentially
  124. * paths will be held by the reciever, in other words don't change
  125. * any of the objects in the array once passed in.
  126. *
  127. * @param paths new selection.
  128. */
  129. public void setSelectionPaths(TreePath[] pPaths) {
  130. boolean differs = false;
  131. int newCount, newCounter, oldCount, oldCounter;
  132. TreePath[] paths = pPaths;
  133. if(paths == null)
  134. newCount = 0;
  135. else
  136. newCount = paths.length;
  137. if(selection == null)
  138. oldCount = 0;
  139. else
  140. oldCount = selection.length;
  141. if((newCount + oldCount) != 0) {
  142. if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION) {
  143. /* If single selection and more than one path, only allow
  144. first. */
  145. if(newCount > 1) {
  146. paths = new TreePath[1];
  147. paths[0] = pPaths[0];
  148. newCount = 1;
  149. }
  150. }
  151. else if(selectionMode ==
  152. TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) {
  153. /* If contiguous selection and paths aren't contiguous,
  154. only select the first path item. */
  155. if(newCount > 0 && !arePathsContiguous(paths)) {
  156. paths = new TreePath[1];
  157. paths[0] = pPaths[0];
  158. newCount = 1;
  159. }
  160. }
  161. boolean found;
  162. int validCount = 0;
  163. TreePath beginLeadPath = leadPath;
  164. Vector cPaths = new Vector(newCount + oldCount);
  165. leadPath = null;
  166. /* Find the paths that are new. */
  167. for(newCounter = 0; newCounter < newCount; newCounter++) {
  168. found = false;
  169. if(paths[newCounter] != null) {
  170. validCount++;
  171. for(oldCounter = 0; oldCounter < oldCount; oldCounter++) {
  172. if(selection[oldCounter] != null &&
  173. selection[oldCounter].equals(paths[newCounter])) {
  174. selection[oldCounter] = null;
  175. oldCounter = oldCount;
  176. found = true;
  177. }
  178. }
  179. if(!found)
  180. cPaths.addElement(new PathPlaceHolder
  181. (paths[newCounter], true));
  182. if(leadPath == null)
  183. leadPath = paths[newCounter];
  184. }
  185. }
  186. /* Get the paths that were selected but no longer selected. */
  187. for(oldCounter = 0; oldCounter < oldCount; oldCounter++)
  188. if(selection[oldCounter] != null)
  189. cPaths.addElement(new PathPlaceHolder
  190. (selection[oldCounter], false));
  191. /* If the validCount isn't equal to newCount it means there
  192. are some null in paths, remove them and set selection to
  193. the new path. */
  194. if(validCount == 0)
  195. selection = null;
  196. else if (validCount != newCount) {
  197. selection = new TreePath[validCount];
  198. for(newCounter = 0, validCount = 0; newCounter < newCount;
  199. newCounter++)
  200. if(paths[newCounter] != null)
  201. selection[validCount++] = paths[newCounter];
  202. }
  203. else {
  204. selection = new TreePath[paths.length];
  205. System.arraycopy(paths, 0, selection, 0, paths.length);
  206. }
  207. if(selection != null)
  208. insureUniqueness();
  209. updateLeadIndex();
  210. resetRowSelection();
  211. /* Notify of the change. */
  212. if(cPaths.size() > 0)
  213. notifyPathChange(cPaths, beginLeadPath);
  214. }
  215. }
  216. /**
  217. * Adds path to the current selection. If path is not currently
  218. * in the selection the TreeSelectionListeners are notified.
  219. *
  220. * @param path the new path to add to the current selection.
  221. */
  222. public void addSelectionPath(TreePath path) {
  223. if(path != null) {
  224. TreePath[] toAdd = new TreePath[1];
  225. toAdd[0] = path;
  226. addSelectionPaths(toAdd);
  227. }
  228. }
  229. /**
  230. * Adds paths to the current selection. If any of the paths in
  231. * paths are not currently in the selection the TreeSelectionListeners
  232. * are notified.
  233. *
  234. * @param path the new path to add to the current selection.
  235. */
  236. public void addSelectionPaths(TreePath[] paths) {
  237. int newPathLength = ((paths == null) ? 0 : paths.length);
  238. if(newPathLength > 0) {
  239. if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION &&
  240. selection != null && selection.length > 0)
  241. setSelectionPaths(paths);
  242. else if(selectionMode == TreeSelectionModel.
  243. CONTIGUOUS_TREE_SELECTION && !canPathsBeAdded(paths)) {
  244. if(arePathsContiguous(paths))
  245. setSelectionPaths(paths);
  246. else {
  247. TreePath[] newPaths = new TreePath[1];
  248. newPaths[0] = paths[0];
  249. setSelectionPaths(newPaths);
  250. }
  251. }
  252. else {
  253. boolean didCopyPaths, inSelection;
  254. int counter, validCount;
  255. int oldCount, oldCounter;
  256. TreePath beginLeadPath = leadPath;
  257. Vector cPaths = null;
  258. if(selection == null)
  259. oldCount = 0;
  260. else
  261. oldCount = selection.length;
  262. didCopyPaths = false;
  263. leadPath = null;
  264. /* Determine the paths that aren't currently in the
  265. selection. */
  266. for(counter = 0, validCount = 0; counter < paths.length;
  267. counter++) {
  268. if(paths[counter] != null) {
  269. inSelection = false;
  270. for (oldCounter = 0; oldCounter < oldCount;
  271. oldCounter++) {
  272. if(paths[counter].equals(selection[oldCounter])) {
  273. oldCounter = oldCount;
  274. if(!didCopyPaths) {
  275. /* Copy the paths so that we can mess with
  276. the array. */
  277. TreePath[] copiedPaths;
  278. copiedPaths = new TreePath[paths.length];
  279. System.arraycopy(paths, 0, copiedPaths,
  280. 0, paths.length);
  281. paths = copiedPaths;
  282. didCopyPaths = true;
  283. }
  284. paths[counter] = null;
  285. inSelection = true;
  286. }
  287. }
  288. if(!inSelection) {
  289. validCount++;
  290. if(cPaths == null)
  291. cPaths = new Vector();
  292. cPaths.addElement(new PathPlaceHolder
  293. (paths[counter], true));
  294. }
  295. if(leadPath == null)
  296. leadPath = paths[counter];
  297. }
  298. }
  299. if(leadPath == null)
  300. leadPath = beginLeadPath;
  301. if(validCount > 0) {
  302. TreePath newSelection[] = new TreePath[oldCount +
  303. validCount];
  304. /* And build the new selection. */
  305. if(oldCount > 0)
  306. System.arraycopy(selection, 0, newSelection, 0,
  307. oldCount);
  308. if(validCount != paths.length) {
  309. /* Some of the paths in paths are already in
  310. the selection. */
  311. int validCounter;
  312. for(counter = validCounter = 0; counter < paths.length;
  313. counter++) {
  314. if (paths[counter] != null)
  315. newSelection[oldCount + validCounter++] =
  316. paths[counter];
  317. }
  318. }
  319. else
  320. System.arraycopy(paths, 0, newSelection, oldCount,
  321. validCount);
  322. selection = newSelection;
  323. insureUniqueness();
  324. updateLeadIndex();
  325. resetRowSelection();
  326. notifyPathChange(cPaths, beginLeadPath);
  327. }
  328. else
  329. leadPath = beginLeadPath;
  330. }
  331. }
  332. }
  333. /**
  334. * Removes path from the selection. If path is in the selection
  335. * The TreeSelectionListeners are notified.
  336. *
  337. * @param path the path to remove from the selection.
  338. */
  339. public void removeSelectionPath(TreePath path) {
  340. if(path != null) {
  341. TreePath[] rPath = new TreePath[1];
  342. rPath[0] = path;
  343. removeSelectionPaths(rPath);
  344. }
  345. }
  346. /**
  347. * Removes paths from the selection. If any of the paths in paths
  348. * are in the selection the TreeSelectionListeners are notified.
  349. *
  350. * @param path the path to remove from the selection.
  351. */
  352. public void removeSelectionPaths(TreePath[] paths) {
  353. if (paths != null && selection != null && paths.length > 0) {
  354. if(!canPathsBeRemoved(paths)) {
  355. /* Could probably do something more interesting here! */
  356. clearSelection();
  357. }
  358. else {
  359. int oldCount, oldCounter;
  360. int removeCount, removeCounter;
  361. int toRemoveCount = 0;
  362. TreePath beginLeadPath = leadPath;
  363. Vector pathsToRemove = null;
  364. oldCount = selection.length;
  365. /* Find the paths that can be removed. */
  366. for (removeCounter = 0; removeCounter < paths.length;
  367. removeCounter++) {
  368. if(paths[removeCounter] != null) {
  369. if(leadPath != null &&
  370. leadPath.equals(paths[removeCounter]))
  371. leadPath = null;
  372. for(oldCounter = 0; oldCounter < oldCount;
  373. oldCounter++) {
  374. if(paths[removeCounter].equals
  375. (selection[oldCounter])){
  376. selection[oldCounter] = null;
  377. oldCounter = oldCount;
  378. if(pathsToRemove == null)
  379. pathsToRemove = new Vector(paths.length);
  380. if(!pathsToRemove.contains
  381. (paths[removeCounter]))
  382. pathsToRemove.addElement
  383. (new PathPlaceHolder
  384. (paths[removeCounter], false));
  385. }
  386. }
  387. }
  388. }
  389. if(pathsToRemove != null) {
  390. removeCount = pathsToRemove.size();
  391. if(removeCount == selection.length)
  392. selection = null;
  393. else {
  394. int validCount = 0;
  395. TreePath[] newSelection;
  396. newSelection = new TreePath[selection.length -
  397. removeCount];
  398. for(oldCounter = 0; oldCounter < oldCount;
  399. oldCounter++)
  400. if(selection[oldCounter] != null)
  401. newSelection[validCount++] =
  402. selection[oldCounter];
  403. selection = newSelection;
  404. }
  405. if(leadPath == null && selection != null)
  406. leadPath = selection[0];
  407. updateLeadIndex();
  408. resetRowSelection();
  409. notifyPathChange(pathsToRemove, beginLeadPath);
  410. }
  411. }
  412. }
  413. }
  414. /**
  415. * Returns the first path in the selection.
  416. */
  417. public TreePath getSelectionPath() {
  418. if(selection != null)
  419. return selection[0];
  420. return null;
  421. }
  422. /**
  423. * Returns the paths in the selection.
  424. */
  425. public TreePath[] getSelectionPaths() {
  426. if(selection != null) {
  427. int pathSize = selection.length;
  428. TreePath[] result = new TreePath[pathSize];
  429. System.arraycopy(selection, 0, result, 0, pathSize);
  430. return result;
  431. }
  432. return null;
  433. }
  434. /**
  435. * Returns the number of paths that are selected.
  436. */
  437. public int getSelectionCount() {
  438. return (selection == null) ? 0 : selection.length;
  439. }
  440. /**
  441. * Returns true if the path, path, is in the current selection.
  442. */
  443. public boolean isPathSelected(TreePath path) {
  444. if (selection != null) {
  445. for (int counter = 0; counter < selection.length; counter++)
  446. if(selection[counter].equals(path))
  447. return true;
  448. }
  449. return false;
  450. }
  451. /**
  452. * Returns true if the selection is currently empty.
  453. */
  454. public boolean isSelectionEmpty() {
  455. return (selection == null);
  456. }
  457. /**
  458. * Empties the current selection. If this represents a change in the
  459. * current selection, the selection listeners are notified.
  460. */
  461. public void clearSelection() {
  462. if(selection != null) {
  463. int selSize = selection.length;
  464. boolean[] newness = new boolean[selSize];
  465. for(int counter = 0; counter < selSize; counter++)
  466. newness[counter] = false;
  467. TreeSelectionEvent event = new TreeSelectionEvent
  468. (this, selection, newness, leadPath, null);
  469. leadPath = null;
  470. leadIndex = leadRow = -1;
  471. selection = null;
  472. resetRowSelection();
  473. fireValueChanged(event);
  474. }
  475. }
  476. /**
  477. * Adds x to the list of listeners that are notified each time the
  478. * selection changes.
  479. *
  480. * @param x the new listener to be added.
  481. */
  482. public void addTreeSelectionListener(TreeSelectionListener x) {
  483. listenerList.add(TreeSelectionListener.class, x);
  484. }
  485. /**
  486. * Removes x from the list of listeners that are notified each time
  487. * the selection changes.
  488. *
  489. * @param x the listener to remove.
  490. */
  491. public void removeTreeSelectionListener(TreeSelectionListener x) {
  492. listenerList.remove(TreeSelectionListener.class, x);
  493. }
  494. /*
  495. * Notify all listeners that have registered interest for
  496. * notification on this event type. The event instance
  497. * is lazily created using the parameters passed into
  498. * the fire method.
  499. * @see EventListenerList
  500. */
  501. protected void fireValueChanged(TreeSelectionEvent e) {
  502. // Guaranteed to return a non-null array
  503. Object[] listeners = listenerList.getListenerList();
  504. // TreeSelectionEvent e = null;
  505. // Process the listeners last to first, notifying
  506. // those that are interested in this event
  507. for (int i = listeners.length-2; i>=0; i-=2) {
  508. if (listeners[i]==TreeSelectionListener.class) {
  509. // Lazily create the event:
  510. // if (e == null)
  511. // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  512. ((TreeSelectionListener)listeners[i+1]).valueChanged(e);
  513. }
  514. }
  515. }
  516. /**
  517. * Returns all of the currently selected rows.
  518. */
  519. public int[] getSelectionRows() {
  520. // This is currently rather expensive. Needs
  521. // to be better support from ListSelectionModel to speed this up.
  522. if(rowMapper != null && selection != null) {
  523. return rowMapper.getRowsForPaths(selection);
  524. }
  525. return null;
  526. }
  527. /**
  528. * Gets the first selected row.
  529. */
  530. public int getMinSelectionRow() {
  531. return listSelectionModel.getMinSelectionIndex();
  532. }
  533. /**
  534. * Gets the last selected row.
  535. */
  536. public int getMaxSelectionRow() {
  537. return listSelectionModel.getMaxSelectionIndex();
  538. }
  539. /**
  540. * Returns true if the row identitifed by row is selected.
  541. */
  542. public boolean isRowSelected(int row) {
  543. return listSelectionModel.isSelectedIndex(row);
  544. }
  545. /**
  546. * Recalculates what rows are selected by asking the RowMapper for the
  547. * row for each path.
  548. */
  549. public void resetRowSelection() {
  550. listSelectionModel.clearSelection();
  551. if(selection != null && rowMapper != null) {
  552. int aRow;
  553. int validCount = 0;
  554. int[] rows = rowMapper.getRowsForPaths(selection);
  555. for(int counter = 0, maxCounter = selection.length;
  556. counter < maxCounter; counter++) {
  557. aRow = rows[counter];
  558. if(aRow != -1)
  559. listSelectionModel.addSelectionInterval(aRow, aRow);
  560. }
  561. if(leadIndex != -1)
  562. leadRow = rows[leadIndex];
  563. insureRowContinuity();
  564. }
  565. else
  566. leadRow = -1;
  567. }
  568. /**
  569. * Returns the lead selection index. That is the last index that was
  570. * added.
  571. */
  572. public int getLeadSelectionRow() {
  573. return leadRow;
  574. }
  575. /**
  576. * Returns the last path that was added.
  577. */
  578. public TreePath getLeadSelectionPath() {
  579. return leadPath;
  580. }
  581. /**
  582. * Add a PropertyChangeListener to the listener list.
  583. * The listener is registered for all properties.
  584. * <p>
  585. * A PropertyChangeEvent will get fired in response to an
  586. * explicit setFont, setBackground, or SetForeground on the
  587. * current component. Note that if the current component is
  588. * inheriting its foreground, background, or font from its
  589. * container, then no event will be fired in response to a
  590. * change in the inherited property.
  591. *
  592. * @param listener The PropertyChangeListener to be added
  593. */
  594. public synchronized void addPropertyChangeListener(
  595. PropertyChangeListener listener) {
  596. if (changeSupport == null) {
  597. changeSupport = new SwingPropertyChangeSupport(this);
  598. }
  599. changeSupport.addPropertyChangeListener(listener);
  600. }
  601. /**
  602. * Remove a PropertyChangeListener from the listener list.
  603. * This removes a PropertyChangeListener that was registered
  604. * for all properties.
  605. *
  606. * @param listener The PropertyChangeListener to be removed
  607. */
  608. public synchronized void removePropertyChangeListener(
  609. PropertyChangeListener listener) {
  610. if (changeSupport == null) {
  611. return;
  612. }
  613. changeSupport.removePropertyChangeListener(listener);
  614. }
  615. /**
  616. * Useful for CONTIGUOUS_TREE_SELECTION. If the rows that are selected
  617. * are not contiguous then the selection is reset to be contiguous.
  618. * Or if the selection mode is single selection and more than one
  619. * this is selected the selection is reset.
  620. */
  621. protected void insureRowContinuity() {
  622. if(selectionMode == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION &&
  623. selection != null && rowMapper != null) {
  624. DefaultListSelectionModel lModel = listSelectionModel;
  625. int min = lModel.getMinSelectionIndex();
  626. if(min != -1) {
  627. for(int counter = min,
  628. maxCounter = lModel.getMaxSelectionIndex();
  629. counter <= maxCounter; counter++) {
  630. if(!lModel.isSelectedIndex(counter)) {
  631. if(counter == min) {
  632. clearSelection();
  633. }
  634. else {
  635. TreePath[] newSel = new TreePath[counter - min];
  636. System.arraycopy(selection, 0, newSel,
  637. 0, counter - min);
  638. setSelectionPaths(newSel);
  639. break;
  640. }
  641. }
  642. }
  643. }
  644. }
  645. else if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION &&
  646. selection != null && selection.length > 1) {
  647. setSelectionPath(selection[0]);
  648. }
  649. }
  650. /**
  651. * Returns true if the paths are contiguous.
  652. */
  653. protected boolean arePathsContiguous(TreePath[] paths) {
  654. if(rowMapper == null || paths.length < 2)
  655. return true;
  656. else {
  657. BitSet bitSet = new BitSet(32);
  658. int anIndex, counter, min;
  659. int pathCount = paths.length;
  660. int validCount = 0;
  661. TreePath[] tempPath = new TreePath[1];
  662. tempPath[0] = paths[0];
  663. min = rowMapper.getRowsForPaths(tempPath)[0];
  664. for(counter = 0; counter < pathCount; counter++) {
  665. if(paths[counter] != null) {
  666. tempPath[0] = paths[counter];
  667. anIndex = rowMapper.getRowsForPaths(tempPath)[0];
  668. if(anIndex == -1 || anIndex < (min - pathCount) ||
  669. anIndex > (min + pathCount))
  670. return false;
  671. if(anIndex < min)
  672. min = anIndex;
  673. if(!bitSet.get(anIndex)) {
  674. bitSet.set(anIndex);
  675. validCount++;
  676. }
  677. }
  678. }
  679. int maxCounter = validCount + min;
  680. for(counter = min; counter < maxCounter; counter++)
  681. if(!bitSet.get(counter))
  682. return false;
  683. }
  684. return true;
  685. }
  686. /**
  687. * Returns true if the paths can be added without breaking the
  688. * continuity of the model.
  689. */
  690. protected boolean canPathsBeAdded(TreePath[] paths) {
  691. if(paths == null || paths.length == 0 || rowMapper == null ||
  692. selection == null || selectionMode ==
  693. TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
  694. return true;
  695. else {
  696. BitSet bitSet = new BitSet();
  697. DefaultListSelectionModel lModel = listSelectionModel;
  698. int anIndex;
  699. int counter;
  700. int min = lModel.getMinSelectionIndex();
  701. int max = lModel.getMaxSelectionIndex();
  702. TreePath[] tempPath = new TreePath[1];
  703. if(min != -1) {
  704. for(counter = min; counter <= max; counter++) {
  705. if(lModel.isSelectedIndex(min))
  706. bitSet.set(counter);
  707. }
  708. }
  709. else {
  710. tempPath[0] = paths[0];
  711. min = max = rowMapper.getRowsForPaths(tempPath)[0];
  712. }
  713. for(counter = paths.length - 1; counter >= 0; counter--) {
  714. if(paths[counter] != null) {
  715. tempPath[0] = paths[counter];
  716. anIndex = rowMapper.getRowsForPaths(tempPath)[0];
  717. min = Math.min(anIndex, min);
  718. max = Math.max(anIndex, max);
  719. if(anIndex == -1)
  720. return false;
  721. bitSet.set(anIndex);
  722. }
  723. }
  724. for(counter = min; counter <= max; counter++)
  725. if(!bitSet.get(counter))
  726. return false;
  727. }
  728. return true;
  729. }
  730. /**
  731. * Returns true if the paths can be removed without breaking the
  732. * continuity of the model.
  733. * This is rather expensive.
  734. */
  735. protected boolean canPathsBeRemoved(TreePath[] paths) {
  736. if(rowMapper == null || selection == null ||
  737. selectionMode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
  738. return true;
  739. else {
  740. boolean found;
  741. BitSet bitSet = new BitSet();
  742. int counter, rCounter;
  743. int pathCount = paths.length;
  744. int anIndex;
  745. int min = -1;
  746. int validCount = 0;
  747. TreePath[] tPaths = new TreePath[pathCount];
  748. TreePath[] tempPath = new TreePath[1];
  749. /* Determine the rows for the removed entries. */
  750. System.arraycopy(paths, 0, tPaths, 0, pathCount);
  751. for(counter = selection.length - 1; counter >= 0; counter--) {
  752. found = false;
  753. for(rCounter = 0; rCounter < pathCount; rCounter++) {
  754. if(tPaths[rCounter] != null && selection[counter].
  755. equals(tPaths[rCounter])) {
  756. tPaths[rCounter] = null;
  757. found = true;
  758. break;
  759. }
  760. }
  761. if(!found) {
  762. tempPath[0] = selection[counter];
  763. anIndex = rowMapper.getRowsForPaths(tempPath)[0];
  764. if(anIndex != -1 && !bitSet.get(anIndex)) {
  765. validCount++;
  766. if(min == -1)
  767. min = anIndex;
  768. else
  769. min = Math.min(min, anIndex);
  770. bitSet.set(anIndex);
  771. }
  772. }
  773. }
  774. /* Make sure they are contiguous. */
  775. if(validCount > 1) {
  776. for(counter = min + validCount - 1; counter >= min;
  777. counter--)
  778. if(!bitSet.get(counter))
  779. return false;
  780. }
  781. }
  782. return true;
  783. }
  784. /**
  785. * Notifies listeners of a change in path. changePaths should contain
  786. * instances of PathPlaceHolder.
  787. *
  788. */
  789. protected void notifyPathChange(Vector changedPaths,
  790. TreePath oldLeadSelection) {
  791. int cPathCount = changedPaths.size();
  792. boolean[] newness = new boolean[cPathCount];
  793. TreePath[] paths = new TreePath[cPathCount];
  794. PathPlaceHolder placeholder;
  795. for(int counter = 0; counter < cPathCount; counter++) {
  796. placeholder = (PathPlaceHolder)changedPaths.elementAt(counter);
  797. newness[counter] = placeholder.isNew;
  798. paths[counter] = placeholder.path;
  799. }
  800. TreeSelectionEvent event = new TreeSelectionEvent
  801. (this, paths, newness, oldLeadSelection, leadPath);
  802. fireValueChanged(event);
  803. }
  804. /**
  805. * Updates the leadIndex instance variable.
  806. */
  807. protected void updateLeadIndex() {
  808. if(leadPath != null) {
  809. if(selection == null) {
  810. leadPath = null;
  811. leadIndex = leadRow = -1;
  812. }
  813. else {
  814. leadRow = leadIndex = -1;
  815. for(int counter = selection.length - 1; counter >= 0;
  816. counter--) {
  817. if(selection[counter].equals(leadPath)) {
  818. leadIndex = counter;
  819. break;
  820. }
  821. }
  822. }
  823. }
  824. }
  825. /**
  826. * Insures that all the elements in path are unique. This does not
  827. * check for a null selection!
  828. */
  829. protected void insureUniqueness() {
  830. int compareCounter;
  831. int dupCount = 0;
  832. int indexCounter;
  833. for(compareCounter = 0; compareCounter < selection.length;
  834. compareCounter++) {
  835. if(selection[compareCounter] != null) {
  836. for(indexCounter = compareCounter + 1; indexCounter <
  837. selection.length; indexCounter++) {
  838. if (selection[indexCounter] != null &&
  839. selection[compareCounter].equals(selection
  840. [indexCounter])){
  841. dupCount++;
  842. selection[indexCounter] = null;
  843. }
  844. }
  845. }
  846. }
  847. if(dupCount > 0) {
  848. /* Squash the duplicates. */
  849. TreePath[] newSelection = new TreePath[selection.length-
  850. dupCount];
  851. for (int counter = 0, validCounter = 0; counter < selection.length;
  852. counter++)
  853. if(selection[counter] != null)
  854. newSelection[validCounter++] = selection[counter];
  855. selection = newSelection;
  856. }
  857. }
  858. /**
  859. * Returns a string that displays and identifies this
  860. * object's properties.
  861. *
  862. * @return a String representation of this object
  863. */
  864. public String toString() {
  865. int selCount = getSelectionCount();
  866. StringBuffer retBuffer = new StringBuffer();
  867. int[] rows;
  868. if(rowMapper != null)
  869. rows = rowMapper.getRowsForPaths(selection);
  870. else
  871. rows = null;
  872. retBuffer.append(getClass().getName() + " " + hashCode() + " [ ");
  873. for(int counter = 0; counter < selCount; counter++) {
  874. if(rows != null)
  875. retBuffer.append(selection[counter].toString() + "@" +
  876. Integer.toString(rows[counter])+ " ");
  877. else
  878. retBuffer.append(selection[counter].toString() + " ");
  879. }
  880. retBuffer.append("]");
  881. return retBuffer.toString();
  882. }
  883. /**
  884. * Returns a clone of the reciever with the same selection.
  885. * selectionListeners, and PropertyListeners are not duplicated.
  886. *
  887. * @exception CloneNotSupportedException if the receiver does not
  888. * both (a) implement the Cloneable interface and (b) define a
  889. * <code>clone</code> method.
  890. */
  891. public Object clone() throws CloneNotSupportedException {
  892. DefaultTreeSelectionModel clone = (DefaultTreeSelectionModel)
  893. super.clone();
  894. clone.changeSupport = null;
  895. if(selection != null) {
  896. int selLength = selection.length;
  897. clone.selection = new TreePath[selLength];
  898. System.arraycopy(selection, 0, clone.selection, 0, selLength);
  899. }
  900. clone.listenerList = new EventListenerList();
  901. clone.listSelectionModel = (DefaultListSelectionModel)
  902. listSelectionModel.clone();
  903. return clone;
  904. }
  905. // Serialization support.
  906. private void writeObject(ObjectOutputStream s) throws IOException {
  907. Object[] tValues;
  908. s.defaultWriteObject();
  909. // Save the rowMapper, if it implements Serializable
  910. if(rowMapper != null && rowMapper instanceof Serializable) {
  911. tValues = new Object[2];
  912. tValues[0] = "rowMapper";
  913. tValues[1] = rowMapper;
  914. }
  915. else
  916. tValues = new Object[0];
  917. s.writeObject(tValues);
  918. }
  919. private void readObject(ObjectInputStream s)
  920. throws IOException, ClassNotFoundException {
  921. Object[] tValues;
  922. s.defaultReadObject();
  923. tValues = (Object[])s.readObject();
  924. if(tValues.length > 0 && tValues[0].equals("rowMapper"))
  925. rowMapper = (RowMapper)tValues[1];
  926. }
  927. }
  928. /**
  929. * Holds a path and whether or not it is new.
  930. */
  931. class PathPlaceHolder {
  932. protected boolean isNew;
  933. protected TreePath path;
  934. PathPlaceHolder(TreePath path, boolean isNew) {
  935. this.path = path;
  936. this.isNew = isNew;
  937. }
  938. }