1 /* DefaultTreeSelectionModel.java
2 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.tree;
41 import java.beans.PropertyChangeListener;
42 import java.io.IOException;
43 import java.io.ObjectInputStream;
44 import java.io.ObjectOutputStream;
45 import java.io.Serializable;
46 import java.util.Arrays;
47 import java.util.BitSet;
48 import java.util.EventListener;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.Vector;
53 import javax.swing.DefaultListSelectionModel;
54 import javax.swing.event.EventListenerList;
55 import javax.swing.event.SwingPropertyChangeSupport;
56 import javax.swing.event.TreeSelectionEvent;
57 import javax.swing.event.TreeSelectionListener;
60 * The implementation of the default tree selection model. The installed
61 * listeners are notified about the path and not the row changes. If you
62 * specifically need to track the row changes, register the listener for the
65 * @author Andrew Selkirk
66 * @author Audrius Meskauskas
68 public class DefaultTreeSelectionModel
69 implements Cloneable, Serializable, TreeSelectionModel
73 * According to the API docs, the method
74 * {@link DefaultTreeSelectionModel#notifyPathChange} should
75 * expect instances of a class PathPlaceHolder in the Vector parameter.
76 * This seems to be a non-public class, so I can only make guesses about the
79 private static class PathPlaceHolder
82 * The path that we wrap.
87 * Indicates if the path is new or already in the selection.
92 * Creates a new instance.
94 * @param p the path to wrap
95 * @param n if the path is new or already in the selection
97 PathPlaceHolder(TreePath p, boolean n)
105 * Use serialVersionUID for interoperability.
107 static final long serialVersionUID = 3288129636638950196L;
110 * The name of the selection mode property.
112 public static final String SELECTION_MODE_PROPERTY = "selectionMode";
115 * Our Swing property change support.
117 protected SwingPropertyChangeSupport changeSupport;
120 * The current selection.
122 protected TreePath[] selection;
125 * Our TreeSelectionListeners.
127 protected EventListenerList listenerList;
130 * The current RowMapper.
132 protected transient RowMapper rowMapper;
135 * The current listSelectionModel.
137 protected DefaultListSelectionModel listSelectionModel;
140 * The current selection mode.
142 protected int selectionMode;
145 * The path that has been added last.
147 protected TreePath leadPath;
150 * The index of the last added path.
152 protected int leadIndex;
155 * The row of the last added path according to the RowMapper.
157 protected int leadRow = -1;
160 * A supporting datastructure that is used in addSelectionPaths() and
161 * removeSelectionPaths(). It contains currently selected paths.
163 * @see #addSelectionPaths(TreePath[])
164 * @see #removeSelectionPaths(TreePath[])
165 * @see #setSelectionPaths(TreePath[])
167 private transient HashSet selectedPaths;
170 * A supporting datastructure that is used in addSelectionPaths() and
171 * removeSelectionPaths(). It contains the paths that are added or removed.
173 * @see #addSelectionPaths(TreePath[])
174 * @see #removeSelectionPaths(TreePath[])
175 * @see #setSelectionPaths(TreePath[])
177 private transient HashSet tmpPaths;
180 * Constructs a new DefaultTreeSelectionModel.
182 public DefaultTreeSelectionModel()
184 setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
185 listSelectionModel = new DefaultListSelectionModel();
186 listenerList = new EventListenerList();
188 tmpPaths = new HashSet();
189 selectedPaths = new HashSet();
193 * Creates a clone of this DefaultTreeSelectionModel with the same selection.
194 * The cloned instance will have the same registered listeners, the listeners
195 * themselves will not be cloned. The selection will be cloned.
197 * @exception CloneNotSupportedException should not be thrown here
198 * @return a copy of this DefaultTreeSelectionModel
200 public Object clone() throws CloneNotSupportedException
202 DefaultTreeSelectionModel cloned =
203 (DefaultTreeSelectionModel) super.clone();
204 cloned.changeSupport = null;
205 cloned.selection = (TreePath[]) selection.clone();
206 cloned.listenerList = new EventListenerList();
207 cloned.listSelectionModel =
208 (DefaultListSelectionModel) listSelectionModel.clone();
209 cloned.selectedPaths = new HashSet();
210 cloned.tmpPaths = new HashSet();
216 * Returns a string that shows this object's properties.
217 * The returned string lists the selected tree rows, if any.
219 * @return a string that shows this object's properties
221 public String toString()
223 if (isSelectionEmpty())
224 return "[selection empty]";
227 StringBuffer b = new StringBuffer("selected rows: [");
228 for (int i = 0; i < selection.length; i++)
230 b.append(getRow(selection[i]));
233 b.append(", lead " + getLeadSelectionRow());
242 * @exception IOException TODO
244 private void writeObject(ObjectOutputStream value0) throws IOException
253 * @exception IOException TODO
254 * @exception ClassNotFoundException TODO
256 private void readObject(ObjectInputStream value0) throws IOException,
257 ClassNotFoundException
263 * Sets the RowMapper that should be used to map between paths and their rows.
265 * @param mapper the RowMapper to set
268 public void setRowMapper(RowMapper mapper)
275 * Returns the RowMapper that is currently used to map between paths and their
278 * @return the current RowMapper
281 public RowMapper getRowMapper()
287 * Sets the current selection mode. Possible values are
288 * {@link #SINGLE_TREE_SELECTION}, {@link #CONTIGUOUS_TREE_SELECTION} and
289 * {@link #DISCONTIGUOUS_TREE_SELECTION}.
291 * @param mode the selection mode to be set
292 * @see #getSelectionMode
293 * @see #SINGLE_TREE_SELECTION
294 * @see #CONTIGUOUS_TREE_SELECTION
295 * @see #DISCONTIGUOUS_TREE_SELECTION
297 public void setSelectionMode(int mode)
299 int oldMode = selectionMode;
300 selectionMode = mode;
301 // Make sure we have a valid selection mode.
302 if (selectionMode != SINGLE_TREE_SELECTION
303 && selectionMode != CONTIGUOUS_TREE_SELECTION
304 && selectionMode != DISCONTIGUOUS_TREE_SELECTION)
305 selectionMode = DISCONTIGUOUS_TREE_SELECTION;
307 // Fire property change event.
308 if (oldMode != selectionMode && changeSupport != null)
309 changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, oldMode,
314 * Returns the current selection mode.
316 * @return the current selection mode
317 * @see #setSelectionMode
318 * @see #SINGLE_TREE_SELECTION
319 * @see #CONTIGUOUS_TREE_SELECTION
320 * @see #DISCONTIGUOUS_TREE_SELECTION
322 public int getSelectionMode()
324 return selectionMode;
328 * Sets this path as the only selection. If this changes the selection the
329 * registered TreeSelectionListeners are notified.
331 * @param path the path to set as selection
333 public void setSelectionPath(TreePath path)
335 TreePath[] paths = null;
337 paths = new TreePath[]{ path };
338 setSelectionPaths(paths);
342 * Get the number of the tree row for the given path.
344 * @param path the tree path
345 * @return the tree row for this path or -1 if the path is not visible.
347 int getRow(TreePath path)
349 RowMapper mapper = getRowMapper();
351 if (mapper instanceof AbstractLayoutCache)
353 // The absolute majority of cases, unless the TreeUI is very
354 // seriously rewritten
355 AbstractLayoutCache ama = (AbstractLayoutCache) mapper;
356 return ama.getRowForPath(path);
358 else if (mapper != null)
360 // Generic non optimized implementation.
361 int[] rows = mapper.getRowsForPaths(new TreePath[] { path });
362 if (rows.length == 0)
371 * Sets the paths as selection. This method checks for duplicates and removes
372 * them. If this changes the selection the registered TreeSelectionListeners
375 * @param paths the paths to set as selection
377 public void setSelectionPaths(TreePath[] paths)
380 if (selection != null)
381 oldLength = selection.length;
384 newLength = paths.length;
385 if (newLength > 0 || oldLength > 0)
387 // For SINGLE_TREE_SELECTION and for CONTIGUOUS_TREE_SELECTION with
388 // a non-contiguous path, we only allow the first path element.
389 if ((selectionMode == SINGLE_TREE_SELECTION && newLength > 1)
390 || (selectionMode == CONTIGUOUS_TREE_SELECTION && newLength > 0
391 && ! arePathsContiguous(paths)))
393 paths = new TreePath[] { paths[0] };
397 Vector changedPaths = null;
400 TreePath oldLeadPath = leadPath;
401 for (int i = 0; i < newLength; i++)
403 if (paths[i] != null && ! tmpPaths.contains(paths[i]))
406 tmpPaths.add(paths[i]);
407 if (! selectedPaths.contains(paths[i]))
409 if (changedPaths == null)
410 changedPaths = new Vector();
411 changedPaths.add(new PathPlaceHolder(paths[i], true));
416 // Put together the new selection.
417 TreePath[] newSelection = null;
420 if (validPaths != newLength)
422 // Some of the paths are already selected, put together
423 // the new selection carefully.
424 newSelection = new TreePath[validPaths];
425 Iterator newPaths = tmpPaths.iterator();
427 for (int i = 0; newPaths.hasNext(); i++)
428 newSelection[i] = (TreePath) newPaths.next();
432 newSelection = new TreePath[paths.length];
433 System.arraycopy(paths, 0, newSelection, 0, paths.length);
437 // Find paths that have been selected, but are no more.
438 for (int i = 0; i < oldLength; i++)
440 if (selection[i] != null && ! tmpPaths.contains(selection[i]))
442 if (changedPaths == null)
443 changedPaths = new Vector();
444 changedPaths.add(new PathPlaceHolder(selection[i], false));
448 // Perform changes and notification.
449 selection = newSelection;
450 HashSet tmp = selectedPaths;
451 selectedPaths = tmpPaths;
455 // Not necessary, but required according to the specs and to tests.
456 if (selection != null)
460 if (changedPaths != null && changedPaths.size() > 0)
461 notifyPathChange(changedPaths, oldLeadPath);
466 * Adds a path to the list of selected paths. This method checks if the path
467 * is already selected and doesn't add the same path twice. If this changes
468 * the selection the registered TreeSelectionListeners are notified.
470 * The lead path is changed to the added path. This also happen if the
471 * passed path was already selected before.
473 * @param path the path to add to the selection
475 public void addSelectionPath(TreePath path)
479 TreePath[] add = new TreePath[]{ path };
480 addSelectionPaths(add);
485 * Adds the paths to the list of selected paths. This method checks if the
486 * paths are already selected and doesn't add the same path twice. If this
487 * changes the selection the registered TreeSelectionListeners are notified.
489 * @param paths the paths to add to the selection
491 public void addSelectionPaths(TreePath[] paths)
493 int length = paths != null ? paths.length : 0;
496 if (selectionMode == SINGLE_TREE_SELECTION)
497 setSelectionPaths(paths);
498 else if (selectionMode == CONTIGUOUS_TREE_SELECTION
499 && ! canPathsBeAdded(paths))
501 if (arePathsContiguous(paths))
502 setSelectionPaths(paths);
504 setSelectionPaths(new TreePath[] { paths[0] });
508 Vector changedPaths = null;
511 TreePath oldLeadPath = leadPath;
513 if (selection != null)
514 oldPaths = selection.length;
516 for (i = 0; i < length; i++)
518 if (paths[i] != null)
520 if (! selectedPaths.contains(paths[i]))
523 if (changedPaths == null)
524 changedPaths = new Vector();
525 changedPaths.add(new PathPlaceHolder(paths[i], true));
526 selectedPaths.add(paths[i]);
527 tmpPaths.add(paths[i]);
534 TreePath[] newSelection = new TreePath[oldPaths + validPaths];
536 System.arraycopy(selection, 0, newSelection, 0, oldPaths);
537 if (validPaths != paths.length)
539 // Some of the paths are already selected, put together
540 // the new selection carefully.
541 Iterator newPaths = tmpPaths.iterator();
543 while (newPaths.hasNext())
545 newSelection[i] = (TreePath) newPaths.next();
550 System.arraycopy(paths, 0, newSelection, oldPaths,
552 selection = newSelection;
556 if (changedPaths != null && changedPaths.size() > 0)
557 notifyPathChange(changedPaths, oldLeadPath);
560 leadPath = oldLeadPath;
567 * Removes the path from the selection. If this changes the selection the
568 * registered TreeSelectionListeners are notified.
570 * @param path the path to remove
572 public void removeSelectionPath(TreePath path)
575 removeSelectionPaths(new TreePath[]{ path });
579 * Removes the paths from the selection. If this changes the selection the
580 * registered TreeSelectionListeners are notified.
582 * @param paths the paths to remove
584 public void removeSelectionPaths(TreePath[] paths)
586 if (paths != null && selection != null && paths.length > 0)
588 if (! canPathsBeRemoved(paths))
592 Vector pathsToRemove = null;
593 for (int i = paths.length - 1; i >= 0; i--)
595 if (paths[i] != null && selectedPaths.contains(paths[i]))
597 if (pathsToRemove == null)
598 pathsToRemove = new Vector();
599 selectedPaths.remove(paths[i]);
600 pathsToRemove.add(new PathPlaceHolder(paths[i],
604 if (pathsToRemove != null)
606 int numRemove = pathsToRemove.size();
607 TreePath oldLead = leadPath;
608 if (numRemove == selection.length)
612 selection = new TreePath[selection.length - numRemove];
613 Iterator keep = selectedPaths.iterator();
614 for (int valid = 0; keep.hasNext(); valid++)
615 selection[valid] = (TreePath) keep.next();
618 if (leadPath != null && ! selectedPaths.contains(leadPath))
620 if (selection != null)
621 leadPath = selection[selection.length - 1];
625 else if (selection != null)
626 leadPath = selection[selection.length - 1];
631 notifyPathChange(pathsToRemove, oldLead);
638 * Returns the first path in the selection. This is especially useful when the
639 * selectionMode is {@link #SINGLE_TREE_SELECTION}.
641 * @return the first path in the selection
643 public TreePath getSelectionPath()
645 if ((selection == null) || (selection.length == 0))
652 * Returns the complete selection.
654 * @return the complete selection
656 public TreePath[] getSelectionPaths()
662 * Returns the number of paths in the selection.
664 * @return the number of paths in the selection
666 public int getSelectionCount()
668 if (selection == null)
671 return selection.length;
675 * Checks if a given path is in the selection.
677 * @param path the path to check
678 * @return <code>true</code> if the path is in the selection,
679 * <code>false</code> otherwise
681 public boolean isPathSelected(TreePath path)
683 if (selection == null)
686 for (int i = 0; i < selection.length; i++)
688 if (selection[i].equals(path))
695 * Checks if the selection is empty.
697 * @return <code>true</code> if the selection is empty, <code>false</code>
700 public boolean isSelectionEmpty()
702 return (selection == null) || (selection.length == 0);
706 * Removes all paths from the selection. Fire the unselection event.
708 public void clearSelection()
710 if (selection != null)
712 int selectionLength = selection.length;
713 boolean[] news = new boolean[selectionLength];
714 Arrays.fill(news, false);
715 TreeSelectionEvent event = new TreeSelectionEvent(this, selection,
721 selectedPaths.clear();
724 fireValueChanged(event);
729 * Adds a <code>TreeSelectionListener</code> object to this model.
731 * @param listener the listener to add
733 public void addTreeSelectionListener(TreeSelectionListener listener)
735 listenerList.add(TreeSelectionListener.class, listener);
739 * Removes a <code>TreeSelectionListener</code> object from this model.
741 * @param listener the listener to remove
743 public void removeTreeSelectionListener(TreeSelectionListener listener)
745 listenerList.remove(TreeSelectionListener.class, listener);
749 * Returns all <code>TreeSelectionListener</code> added to this model.
751 * @return an array of listeners
754 public TreeSelectionListener[] getTreeSelectionListeners()
756 return (TreeSelectionListener[]) getListeners(TreeSelectionListener.class);
762 * @param event the event to fire.
764 protected void fireValueChanged(TreeSelectionEvent event)
766 TreeSelectionListener[] listeners = getTreeSelectionListeners();
768 for (int i = 0; i < listeners.length; ++i)
769 listeners[i].valueChanged(event);
773 * Returns all added listeners of a special type.
775 * @param listenerType the listener type
776 * @return an array of listeners
779 public <T extends EventListener> T[] getListeners(Class<T> listenerType)
781 return listenerList.getListeners(listenerType);
785 * Returns the currently selected rows.
787 * @return the currently selected rows
789 public int[] getSelectionRows()
792 if (rowMapper != null && selection != null)
794 rows = rowMapper.getRowsForPaths(selection);
797 // Find invisible rows.
799 for (int i = rows.length - 1; i >= 0; i--)
805 // Clean up invisible rows.
808 if (invisible == rows.length)
812 int[] newRows = new int[rows.length - invisible];
814 for (int i = rows.length - 1; i >= 0; i--)
818 newRows[visCount] = rows[i];
831 * Returns the smallest row index from the selection.
833 * @return the smallest row index from the selection
835 public int getMinSelectionRow()
837 return listSelectionModel.getMinSelectionIndex();
841 * Returns the largest row index from the selection.
843 * @return the largest row index from the selection
845 public int getMaxSelectionRow()
847 return listSelectionModel.getMaxSelectionIndex();
851 * Checks if a particular row is selected.
853 * @param row the index of the row to check
854 * @return <code>true</code> if the row is in this selection,
855 * <code>false</code> otherwise
856 * @throws NullPointerException if the row mapper is not set (can only happen
857 * if the user has plugged in the custom incorrect TreeUI
860 public boolean isRowSelected(int row)
862 return listSelectionModel.isSelectedIndex(row);
866 * Updates the mappings from TreePaths to row indices.
868 public void resetRowSelection()
870 listSelectionModel.clearSelection();
871 if (selection != null && rowMapper != null)
873 int[] rows = rowMapper.getRowsForPaths(selection);
874 // Update list selection model.
875 for (int i = 0; i < rows.length; i++)
879 listSelectionModel.addSelectionInterval(row, row);
881 // Update lead selection.
882 if (leadIndex != -1 && rows != null)
883 leadRow = rows[leadIndex];
884 else if (leadPath != null)
886 TreePath[] tmp = new TreePath[]{ leadPath };
887 rows = rowMapper.getRowsForPaths(tmp);
888 leadRow = rows != null ? rows[0] : -1;
892 insureRowContinuity();
899 * getLeadSelectionRow
903 public int getLeadSelectionRow()
909 * getLeadSelectionPath
913 public TreePath getLeadSelectionPath()
919 * Adds a <code>PropertyChangeListener</code> object to this model.
921 * @param listener the listener to add.
923 public void addPropertyChangeListener(PropertyChangeListener listener)
925 if (changeSupport == null)
926 changeSupport = new SwingPropertyChangeSupport(this);
927 changeSupport.addPropertyChangeListener(listener);
931 * Removes a <code>PropertyChangeListener</code> object from this model.
933 * @param listener the listener to remove.
935 public void removePropertyChangeListener(PropertyChangeListener listener)
937 if (changeSupport != null)
938 changeSupport.removePropertyChangeListener(listener);
942 * Returns all added <code>PropertyChangeListener</code> objects.
944 * @return an array of listeners.
947 public PropertyChangeListener[] getPropertyChangeListeners()
949 PropertyChangeListener[] listeners = null;
950 if (changeSupport != null)
951 listeners = changeSupport.getPropertyChangeListeners();
953 listeners = new PropertyChangeListener[0];
958 * Makes sure the currently selected paths are valid according to the current
959 * selectionMode. If the selectionMode is set to
960 * {@link #CONTIGUOUS_TREE_SELECTION} and the selection isn't contiguous then
961 * the selection is reset to the first set of contguous paths. If the
962 * selectionMode is set to {@link #SINGLE_TREE_SELECTION} and the selection
963 * has more than one path, the selection is reset to the contain only the
966 protected void insureRowContinuity()
968 if (selectionMode == CONTIGUOUS_TREE_SELECTION && selection != null
969 && rowMapper != null)
971 int min = listSelectionModel.getMinSelectionIndex();
974 int max = listSelectionModel.getMaxSelectionIndex();
975 for (int i = min; i <= max; i++)
977 if (! listSelectionModel.isSelectedIndex(i))
983 TreePath[] newSelection = new TreePath[i - min];
984 int[] rows = rowMapper.getRowsForPaths(selection);
985 for (int j = 0; j < rows.length; j++)
988 newSelection[rows[j] - min] = selection[j];
990 setSelectionPaths(newSelection);
997 else if (selectionMode == SINGLE_TREE_SELECTION && selection != null
998 && selection.length > 1)
999 setSelectionPath(selection[0]);
1003 * Returns <code>true</code> if the paths are contiguous (take subsequent
1004 * rows in the diplayed tree view. The method returns <code>true</code> if
1005 * we have no RowMapper assigned.
1007 * @param paths the paths to check for continuity
1008 * @return <code>true</code> if the paths are contiguous or we have no
1009 * RowMapper assigned
1011 protected boolean arePathsContiguous(TreePath[] paths)
1013 if (rowMapper == null || paths.length < 2)
1016 int length = paths.length;
1017 TreePath[] tmp = new TreePath[1];
1019 int min = rowMapper.getRowsForPaths(tmp)[0];
1020 BitSet selected = new BitSet();
1022 for (int i = 0; i < length; i++)
1024 if (paths[i] != null)
1027 int[] rows = rowMapper.getRowsForPaths(tmp);
1029 return false; // No row mapping yet, can't be selected.
1031 if (row == -1 || row < (min - length) || row > (min + length))
1032 return false; // Not contiguous.
1033 min = Math.min(min, row);
1034 if (! selected.get(row))
1042 int max = valid + min;
1043 for (int i = min; i < max; i++)
1044 if (! selected.get(i))
1045 return false; // Not contiguous.
1050 * Checks if the paths can be added. This returns <code>true</code> if:
1052 * <li><code>paths</code> is <code>null</code> or empty</li>
1053 * <li>we have no RowMapper assigned</li>
1054 * <li>nothing is currently selected</li>
1055 * <li>selectionMode is {@link #DISCONTIGUOUS_TREE_SELECTION}</li>
1056 * <li>adding the paths to the selection still results in a contiguous set of
1059 * @param paths the paths to check
1060 * @return <code>true</code> if the paths can be added with respect to the
1063 protected boolean canPathsBeAdded(TreePath[] paths)
1065 if (paths == null || paths.length == 0 || rowMapper == null
1066 || selection == null || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1069 BitSet selected = new BitSet();
1070 int min = listSelectionModel.getMinSelectionIndex();
1071 int max = listSelectionModel.getMaxSelectionIndex();
1072 TreePath[] tmp = new TreePath[1];
1075 // Set the bitmask of selected elements.
1076 for (int i = min; i <= max; i++)
1082 min = rowMapper.getRowsForPaths(tmp)[0];
1085 // Mark new paths as selected.
1086 for (int i = paths.length - 1; i >= 0; i--)
1088 if (paths[i] != null)
1091 int[] rows = rowMapper.getRowsForPaths(tmp);
1093 return false; // Now row mapping yet, can't be selected.
1096 return false; // Now row mapping yet, can't be selected.
1097 min = Math.min(min, row);
1098 max = Math.max(max, row);
1102 // Now look if the new selection would be contiguous.
1103 for (int i = min; i <= max; i++)
1104 if (! selected.get(i))
1110 * Checks if the paths can be removed without breaking the continuity of the
1111 * selection according to selectionMode.
1113 * @param paths the paths to check
1114 * @return <code>true</code> if the paths can be removed with respect to the
1117 protected boolean canPathsBeRemoved(TreePath[] paths)
1119 if (rowMapper == null || isSelectionEmpty()
1120 || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1123 HashSet set = new HashSet();
1124 for (int i = 0; i < selection.length; i++)
1125 set.add(selection[i]);
1127 for (int i = 0; i < paths.length; i++)
1128 set.remove(paths[i]);
1130 TreePath[] remaining = new TreePath[set.size()];
1131 Iterator iter = set.iterator();
1133 for (int i = 0; i < remaining.length; i++)
1134 remaining[i] = (TreePath) iter.next();
1136 return arePathsContiguous(remaining);
1140 * Notify the installed listeners that the given patches have changed. This
1141 * method will call listeners if invoked, but it is not called from the
1142 * implementation of this class.
1144 * @param vPaths the vector of the changed patches
1145 * @param oldLeadSelection the old selection index
1147 protected void notifyPathChange(Vector vPaths, TreePath oldLeadSelection)
1150 int numChangedPaths = vPaths.size();
1151 boolean[] news = new boolean[numChangedPaths];
1152 TreePath[] paths = new TreePath[numChangedPaths];
1153 for (int i = 0; i < numChangedPaths; i++)
1155 PathPlaceHolder p = (PathPlaceHolder) vPaths.get(i);
1160 TreeSelectionEvent event = new TreeSelectionEvent(this, paths, news,
1163 fireValueChanged(event);
1167 * Updates the lead selection row number after changing the lead selection
1170 protected void updateLeadIndex()
1173 if (leadPath != null)
1176 if (selection == null)
1180 for (int i = selection.length - 1; i >= 0 && leadIndex == -1; i--)
1182 if (selection[i] == leadPath)
1190 * This method exists due historical reasons and returns without action
1191 * (unless overridden). For compatibility with the applications that override
1192 * it, it is still called from the {@link #setSelectionPaths(TreePath[])} and
1193 * {@link #addSelectionPaths(TreePath[])}.
1195 protected void insureUniqueness()
1197 // Following the API 1.4, the method should return without action.