1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52:
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58:
59:
68: public class DefaultTreeSelectionModel
69: implements Cloneable, Serializable, TreeSelectionModel
70: {
71:
72:
79: private static class PathPlaceHolder
80: {
81:
84: TreePath path;
85:
86:
89: boolean isNew;
90:
91:
97: PathPlaceHolder(TreePath p, boolean n)
98: {
99: path = p;
100: isNew = n;
101: }
102: }
103:
104:
107: static final long serialVersionUID = 3288129636638950196L;
108:
109:
112: public static final String SELECTION_MODE_PROPERTY = "selectionMode";
113:
114:
117: protected SwingPropertyChangeSupport changeSupport;
118:
119:
122: protected TreePath[] selection;
123:
124:
127: protected EventListenerList listenerList;
128:
129:
132: protected transient RowMapper rowMapper;
133:
134:
137: protected DefaultListSelectionModel listSelectionModel;
138:
139:
142: protected int selectionMode;
143:
144:
147: protected TreePath leadPath;
148:
149:
152: protected int leadIndex;
153:
154:
157: protected int leadRow = -1;
158:
159:
167: private transient HashSet selectedPaths;
168:
169:
177: private transient HashSet tmpPaths;
178:
179:
182: public DefaultTreeSelectionModel()
183: {
184: setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
185: listSelectionModel = new DefaultListSelectionModel();
186: listenerList = new EventListenerList();
187: leadIndex = -1;
188: tmpPaths = new HashSet();
189: selectedPaths = new HashSet();
190: }
191:
192:
200: public Object clone() throws CloneNotSupportedException
201: {
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();
211:
212: return cloned;
213: }
214:
215:
221: public String toString()
222: {
223: if (isSelectionEmpty())
224: return "[selection empty]";
225: else
226: {
227: StringBuffer b = new StringBuffer("selected rows: [");
228: for (int i = 0; i < selection.length; i++)
229: {
230: b.append(getRow(selection[i]));
231: b.append(' ');
232: }
233: b.append(", lead " + getLeadSelectionRow());
234: return b.toString();
235: }
236: }
237:
238:
244: private void writeObject(ObjectOutputStream value0) throws IOException
245: {
246:
247: }
248:
249:
256: private void readObject(ObjectInputStream value0) throws IOException,
257: ClassNotFoundException
258: {
259:
260: }
261:
262:
268: public void setRowMapper(RowMapper mapper)
269: {
270: rowMapper = mapper;
271: resetRowSelection();
272: }
273:
274:
281: public RowMapper getRowMapper()
282: {
283: return rowMapper;
284: }
285:
286:
297: public void setSelectionMode(int mode)
298: {
299: int oldMode = selectionMode;
300: selectionMode = mode;
301:
302: if (selectionMode != SINGLE_TREE_SELECTION
303: && selectionMode != CONTIGUOUS_TREE_SELECTION
304: && selectionMode != DISCONTIGUOUS_TREE_SELECTION)
305: selectionMode = DISCONTIGUOUS_TREE_SELECTION;
306:
307:
308: if (oldMode != selectionMode && changeSupport != null)
309: changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, oldMode,
310: selectionMode);
311: }
312:
313:
322: public int getSelectionMode()
323: {
324: return selectionMode;
325: }
326:
327:
333: public void setSelectionPath(TreePath path)
334: {
335: TreePath[] paths = null;
336: if (path != null)
337: paths = new TreePath[]{ path };
338: setSelectionPaths(paths);
339: }
340:
341:
347: int getRow(TreePath path)
348: {
349: RowMapper mapper = getRowMapper();
350:
351: if (mapper instanceof AbstractLayoutCache)
352: {
353:
354:
355: AbstractLayoutCache ama = (AbstractLayoutCache) mapper;
356: return ama.getRowForPath(path);
357: }
358: else if (mapper != null)
359: {
360:
361: int[] rows = mapper.getRowsForPaths(new TreePath[] { path });
362: if (rows.length == 0)
363: return - 1;
364: else
365: return rows[0];
366: }
367: return -1;
368: }
369:
370:
377: public void setSelectionPaths(TreePath[] paths)
378: {
379: int oldLength = 0;
380: if (selection != null)
381: oldLength = selection.length;
382: int newLength = 0;
383: if (paths != null)
384: newLength = paths.length;
385: if (newLength > 0 || oldLength > 0)
386: {
387:
388:
389: if ((selectionMode == SINGLE_TREE_SELECTION && newLength > 1)
390: || (selectionMode == CONTIGUOUS_TREE_SELECTION && newLength > 0
391: && ! arePathsContiguous(paths)))
392: {
393: paths = new TreePath[] { paths[0] };
394: newLength = 1;
395: }
396:
397: Vector changedPaths = null;
398: tmpPaths.clear();
399: int validPaths = 0;
400: TreePath oldLeadPath = leadPath;
401: for (int i = 0; i < newLength; i++)
402: {
403: if (paths[i] != null && ! tmpPaths.contains(paths[i]))
404: {
405: validPaths++;
406: tmpPaths.add(paths[i]);
407: if (! selectedPaths.contains(paths[i]))
408: {
409: if (changedPaths == null)
410: changedPaths = new Vector();
411: changedPaths.add(new PathPlaceHolder(paths[i], true));
412: }
413: leadPath = paths[i];
414: }
415: }
416:
417: TreePath[] newSelection = null;
418: if (validPaths != 0)
419: {
420: if (validPaths != newLength)
421: {
422:
423:
424: newSelection = new TreePath[validPaths];
425: Iterator newPaths = tmpPaths.iterator();
426: validPaths = 0;
427: for (int i = 0; newPaths.hasNext(); i++)
428: newSelection[i] = (TreePath) newPaths.next();
429: }
430: else
431: {
432: newSelection = new TreePath[paths.length];
433: System.arraycopy(paths, 0, newSelection, 0, paths.length);
434: }
435: }
436:
437:
438: for (int i = 0; i < oldLength; i++)
439: {
440: if (selection[i] != null && ! tmpPaths.contains(selection[i]))
441: {
442: if (changedPaths == null)
443: changedPaths = new Vector();
444: changedPaths.add(new PathPlaceHolder(selection[i], false));
445: }
446: }
447:
448:
449: selection = newSelection;
450: HashSet tmp = selectedPaths;
451: selectedPaths = tmpPaths;
452: tmpPaths = tmp;
453: tmpPaths.clear();
454:
455:
456: if (selection != null)
457: insureUniqueness();
458: updateLeadIndex();
459: resetRowSelection();
460: if (changedPaths != null && changedPaths.size() > 0)
461: notifyPathChange(changedPaths, oldLeadPath);
462: }
463: }
464:
465:
475: public void addSelectionPath(TreePath path)
476: {
477: if (path != null)
478: {
479: TreePath[] add = new TreePath[]{ path };
480: addSelectionPaths(add);
481: }
482: }
483:
484:
491: public void addSelectionPaths(TreePath[] paths)
492: {
493: int length = paths != null ? paths.length : 0;
494: if (length > 0)
495: {
496: if (selectionMode == SINGLE_TREE_SELECTION)
497: setSelectionPaths(paths);
498: else if (selectionMode == CONTIGUOUS_TREE_SELECTION
499: && ! canPathsBeAdded(paths))
500: {
501: if (arePathsContiguous(paths))
502: setSelectionPaths(paths);
503: else
504: setSelectionPaths(new TreePath[] { paths[0] });
505: }
506: else
507: {
508: Vector changedPaths = null;
509: tmpPaths.clear();
510: int validPaths = 0;
511: TreePath oldLeadPath = leadPath;
512: int oldPaths = 0;
513: if (selection != null)
514: oldPaths = selection.length;
515: int i;
516: for (i = 0; i < length; i++)
517: {
518: if (paths[i] != null)
519: {
520: if (! selectedPaths.contains(paths[i]))
521: {
522: validPaths++;
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]);
528: }
529: leadPath = paths[i];
530: }
531: }
532: if (validPaths > 0)
533: {
534: TreePath[] newSelection = new TreePath[oldPaths + validPaths];
535: if (oldPaths > 0)
536: System.arraycopy(selection, 0, newSelection, 0, oldPaths);
537: if (validPaths != paths.length)
538: {
539:
540:
541: Iterator newPaths = tmpPaths.iterator();
542: i = oldPaths;
543: while (newPaths.hasNext())
544: {
545: newSelection[i] = (TreePath) newPaths.next();
546: i++;
547: }
548: }
549: else
550: System.arraycopy(paths, 0, newSelection, oldPaths,
551: validPaths);
552: selection = newSelection;
553: insureUniqueness();
554: updateLeadIndex();
555: resetRowSelection();
556: if (changedPaths != null && changedPaths.size() > 0)
557: notifyPathChange(changedPaths, oldLeadPath);
558: }
559: else
560: leadPath = oldLeadPath;
561: tmpPaths.clear();
562: }
563: }
564: }
565:
566:
572: public void removeSelectionPath(TreePath path)
573: {
574: if (path != null)
575: removeSelectionPaths(new TreePath[]{ path });
576: }
577:
578:
584: public void removeSelectionPaths(TreePath[] paths)
585: {
586: if (paths != null && selection != null && paths.length > 0)
587: {
588: if (! canPathsBeRemoved(paths))
589: clearSelection();
590: else
591: {
592: Vector pathsToRemove = null;
593: for (int i = paths.length - 1; i >= 0; i--)
594: {
595: if (paths[i] != null && selectedPaths.contains(paths[i]))
596: {
597: if (pathsToRemove == null)
598: pathsToRemove = new Vector();
599: selectedPaths.remove(paths[i]);
600: pathsToRemove.add(new PathPlaceHolder(paths[i],
601: false));
602: }
603: }
604: if (pathsToRemove != null)
605: {
606: int numRemove = pathsToRemove.size();
607: TreePath oldLead = leadPath;
608: if (numRemove == selection.length)
609: selection = null;
610: else
611: {
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();
616: }
617:
618: if (leadPath != null && ! selectedPaths.contains(leadPath))
619: {
620: if (selection != null)
621: leadPath = selection[selection.length - 1];
622: else
623: leadPath = null;
624: }
625: else if (selection != null)
626: leadPath = selection[selection.length - 1];
627: else
628: leadPath = null;
629: updateLeadIndex();
630: resetRowSelection();
631: notifyPathChange(pathsToRemove, oldLead);
632: }
633: }
634: }
635: }
636:
637:
643: public TreePath getSelectionPath()
644: {
645: if ((selection == null) || (selection.length == 0))
646: return null;
647: else
648: return selection[0];
649: }
650:
651:
656: public TreePath[] getSelectionPaths()
657: {
658: return selection;
659: }
660:
661:
666: public int getSelectionCount()
667: {
668: if (selection == null)
669: return 0;
670: else
671: return selection.length;
672: }
673:
674:
681: public boolean isPathSelected(TreePath path)
682: {
683: if (selection == null)
684: return false;
685:
686: for (int i = 0; i < selection.length; i++)
687: {
688: if (selection[i].equals(path))
689: return true;
690: }
691: return false;
692: }
693:
694:
700: public boolean isSelectionEmpty()
701: {
702: return (selection == null) || (selection.length == 0);
703: }
704:
705:
708: public void clearSelection()
709: {
710: if (selection != null)
711: {
712: int selectionLength = selection.length;
713: boolean[] news = new boolean[selectionLength];
714: Arrays.fill(news, false);
715: TreeSelectionEvent event = new TreeSelectionEvent(this, selection,
716: news, leadPath,
717: null);
718: leadPath = null;
719: leadIndex = 0;
720: leadRow = 0;
721: selectedPaths.clear();
722: selection = null;
723: resetRowSelection();
724: fireValueChanged(event);
725: }
726: }
727:
728:
733: public void addTreeSelectionListener(TreeSelectionListener listener)
734: {
735: listenerList.add(TreeSelectionListener.class, listener);
736: }
737:
738:
743: public void removeTreeSelectionListener(TreeSelectionListener listener)
744: {
745: listenerList.remove(TreeSelectionListener.class, listener);
746: }
747:
748:
754: public TreeSelectionListener[] getTreeSelectionListeners()
755: {
756: return (TreeSelectionListener[]) getListeners(TreeSelectionListener.class);
757: }
758:
759:
764: protected void fireValueChanged(TreeSelectionEvent event)
765: {
766: TreeSelectionListener[] listeners = getTreeSelectionListeners();
767:
768: for (int i = 0; i < listeners.length; ++i)
769: listeners[i].valueChanged(event);
770: }
771:
772:
779: public <T extends EventListener> T[] getListeners(Class<T> listenerType)
780: {
781: return listenerList.getListeners(listenerType);
782: }
783:
784:
789: public int[] getSelectionRows()
790: {
791: int[] rows = null;
792: if (rowMapper != null && selection != null)
793: {
794: rows = rowMapper.getRowsForPaths(selection);
795: if (rows != null)
796: {
797:
798: int invisible = 0;
799: for (int i = rows.length - 1; i >= 0; i--)
800: {
801: if (rows[i] == -1)
802: invisible++;
803:
804: }
805:
806: if (invisible > 0)
807: {
808: if (invisible == rows.length)
809: rows = null;
810: else
811: {
812: int[] newRows = new int[rows.length - invisible];
813: int visCount = 0;
814: for (int i = rows.length - 1; i >= 0; i--)
815: {
816: if (rows[i] != -1)
817: {
818: newRows[visCount] = rows[i];
819: visCount++;
820: }
821: }
822: rows = newRows;
823: }
824: }
825: }
826: }
827: return rows;
828: }
829:
830:
835: public int getMinSelectionRow()
836: {
837: return listSelectionModel.getMinSelectionIndex();
838: }
839:
840:
845: public int getMaxSelectionRow()
846: {
847: return listSelectionModel.getMaxSelectionIndex();
848: }
849:
850:
860: public boolean isRowSelected(int row)
861: {
862: return listSelectionModel.isSelectedIndex(row);
863: }
864:
865:
868: public void resetRowSelection()
869: {
870: listSelectionModel.clearSelection();
871: if (selection != null && rowMapper != null)
872: {
873: int[] rows = rowMapper.getRowsForPaths(selection);
874:
875: for (int i = 0; i < rows.length; i++)
876: {
877: int row = rows[i];
878: if (row != -1)
879: listSelectionModel.addSelectionInterval(row, row);
880: }
881:
882: if (leadIndex != -1 && rows != null)
883: leadRow = rows[leadIndex];
884: else if (leadPath != null)
885: {
886: TreePath[] tmp = new TreePath[]{ leadPath };
887: rows = rowMapper.getRowsForPaths(tmp);
888: leadRow = rows != null ? rows[0] : -1;
889: }
890: else
891: leadRow = -1;
892: insureRowContinuity();
893: }
894: else
895: leadRow = -1;
896: }
897:
898:
903: public int getLeadSelectionRow()
904: {
905: return leadRow;
906: }
907:
908:
913: public TreePath getLeadSelectionPath()
914: {
915: return leadPath;
916: }
917:
918:
923: public void addPropertyChangeListener(PropertyChangeListener listener)
924: {
925: if (changeSupport == null)
926: changeSupport = new SwingPropertyChangeSupport(this);
927: changeSupport.addPropertyChangeListener(listener);
928: }
929:
930:
935: public void removePropertyChangeListener(PropertyChangeListener listener)
936: {
937: if (changeSupport != null)
938: changeSupport.removePropertyChangeListener(listener);
939: }
940:
941:
947: public PropertyChangeListener[] getPropertyChangeListeners()
948: {
949: PropertyChangeListener[] listeners = null;
950: if (changeSupport != null)
951: listeners = changeSupport.getPropertyChangeListeners();
952: else
953: listeners = new PropertyChangeListener[0];
954: return listeners;
955: }
956:
957:
966: protected void insureRowContinuity()
967: {
968: if (selectionMode == CONTIGUOUS_TREE_SELECTION && selection != null
969: && rowMapper != null)
970: {
971: int min = listSelectionModel.getMinSelectionIndex();
972: if (min != -1)
973: {
974: int max = listSelectionModel.getMaxSelectionIndex();
975: for (int i = min; i <= max; i++)
976: {
977: if (! listSelectionModel.isSelectedIndex(i))
978: {
979: if (i == min)
980: clearSelection();
981: else
982: {
983: TreePath[] newSelection = new TreePath[i - min];
984: int[] rows = rowMapper.getRowsForPaths(selection);
985: for (int j = 0; j < rows.length; j++)
986: {
987: if (rows[j] < i)
988: newSelection[rows[j] - min] = selection[j];
989: }
990: setSelectionPaths(newSelection);
991: break;
992: }
993: }
994: }
995: }
996: }
997: else if (selectionMode == SINGLE_TREE_SELECTION && selection != null
998: && selection.length > 1)
999: setSelectionPath(selection[0]);
1000: }
1001:
1002:
1011: protected boolean arePathsContiguous(TreePath[] paths)
1012: {
1013: if (rowMapper == null || paths.length < 2)
1014: return true;
1015:
1016: int length = paths.length;
1017: TreePath[] tmp = new TreePath[1];
1018: tmp[0] = paths[0];
1019: int min = rowMapper.getRowsForPaths(tmp)[0];
1020: BitSet selected = new BitSet();
1021: int valid = 0;
1022: for (int i = 0; i < length; i++)
1023: {
1024: if (paths[i] != null)
1025: {
1026: tmp[0] = paths[i];
1027: int[] rows = rowMapper.getRowsForPaths(tmp);
1028: if (rows == null)
1029: return false;
1030: int row = rows[0];
1031: if (row == -1 || row < (min - length) || row > (min + length))
1032: return false;
1033: min = Math.min(min, row);
1034: if (! selected.get(row))
1035: {
1036: selected.set(row);
1037: valid++;
1038: }
1039:
1040: }
1041: }
1042: int max = valid + min;
1043: for (int i = min; i < max; i++)
1044: if (! selected.get(i))
1045: return false;
1046: return true;
1047: }
1048:
1049:
1063: protected boolean canPathsBeAdded(TreePath[] paths)
1064: {
1065: if (paths == null || paths.length == 0 || rowMapper == null
1066: || selection == null || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1067: return true;
1068:
1069: BitSet selected = new BitSet();
1070: int min = listSelectionModel.getMinSelectionIndex();
1071: int max = listSelectionModel.getMaxSelectionIndex();
1072: TreePath[] tmp = new TreePath[1];
1073: if (min != -1)
1074: {
1075:
1076: for (int i = min; i <= max; i++)
1077: selected.set(i);
1078: }
1079: else
1080: {
1081: tmp[0] = paths[0];
1082: min = rowMapper.getRowsForPaths(tmp)[0];
1083: max = min;
1084: }
1085:
1086: for (int i = paths.length - 1; i >= 0; i--)
1087: {
1088: if (paths[i] != null)
1089: {
1090: tmp[0] = paths[i];
1091: int[] rows = rowMapper.getRowsForPaths(tmp);
1092: if (rows == null)
1093: return false;
1094: int row = rows[0];
1095: if (row == -1)
1096: return false;
1097: min = Math.min(min, row);
1098: max = Math.max(max, row);
1099: selected.set(row);
1100: }
1101: }
1102:
1103: for (int i = min; i <= max; i++)
1104: if (! selected.get(i))
1105: return false;
1106: return true;
1107: }
1108:
1109:
1117: protected boolean canPathsBeRemoved(TreePath[] paths)
1118: {
1119: if (rowMapper == null || isSelectionEmpty()
1120: || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1121: return true;
1122:
1123: HashSet set = new HashSet();
1124: for (int i = 0; i < selection.length; i++)
1125: set.add(selection[i]);
1126:
1127: for (int i = 0; i < paths.length; i++)
1128: set.remove(paths[i]);
1129:
1130: TreePath[] remaining = new TreePath[set.size()];
1131: Iterator iter = set.iterator();
1132:
1133: for (int i = 0; i < remaining.length; i++)
1134: remaining[i] = (TreePath) iter.next();
1135:
1136: return arePathsContiguous(remaining);
1137: }
1138:
1139:
1147: protected void notifyPathChange(Vector vPaths, TreePath oldLeadSelection)
1148: {
1149:
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++)
1154: {
1155: PathPlaceHolder p = (PathPlaceHolder) vPaths.get(i);
1156: news[i] = p.isNew;
1157: paths[i] = p.path;
1158: }
1159:
1160: TreeSelectionEvent event = new TreeSelectionEvent(this, paths, news,
1161: oldLeadSelection,
1162: leadPath);
1163: fireValueChanged(event);
1164: }
1165:
1166:
1170: protected void updateLeadIndex()
1171: {
1172: leadIndex = -1;
1173: if (leadPath != null)
1174: {
1175: leadRow = -1;
1176: if (selection == null)
1177: leadPath = null;
1178: else
1179: {
1180: for (int i = selection.length - 1; i >= 0 && leadIndex == -1; i--)
1181: {
1182: if (selection[i] == leadPath)
1183: leadIndex = i;
1184: }
1185: }
1186: }
1187: }
1188:
1189:
1195: protected void insureUniqueness()
1196: {
1197:
1198: }
1199: }