1:
37:
38:
39: package ;
40:
41: import ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54:
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60:
61:
70: public class DefaultTreeSelectionModel
71: implements Cloneable, Serializable, TreeSelectionModel
72: {
73:
74:
81: private static class PathPlaceHolder
82: {
83:
86: TreePath path;
87:
88:
91: boolean isNew;
92:
93:
99: PathPlaceHolder(TreePath p, boolean n)
100: {
101: path = p;
102: isNew = n;
103: }
104: }
105:
106:
109: static final long serialVersionUID = 3288129636638950196L;
110:
111:
114: public static final String SELECTION_MODE_PROPERTY = "selectionMode";
115:
116:
119: protected SwingPropertyChangeSupport changeSupport;
120:
121:
124: protected TreePath[] selection;
125:
126:
129: protected EventListenerList listenerList;
130:
131:
134: protected transient RowMapper rowMapper;
135:
136:
139: protected DefaultListSelectionModel listSelectionModel;
140:
141:
144: protected int selectionMode;
145:
146:
149: protected TreePath leadPath;
150:
151:
154: protected int leadIndex;
155:
156:
159: protected int leadRow = -1;
160:
161:
169: private transient HashSet<TreePath> selectedPaths;
170:
171:
179: private transient HashSet<TreePath> tmpPaths;
180:
181:
184: public DefaultTreeSelectionModel()
185: {
186: setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
187: listSelectionModel = new DefaultListSelectionModel();
188: listenerList = new EventListenerList();
189: leadIndex = -1;
190: tmpPaths = new HashSet<TreePath>();
191: selectedPaths = new HashSet<TreePath>();
192: }
193:
194:
202: public Object clone() throws CloneNotSupportedException
203: {
204: DefaultTreeSelectionModel cloned =
205: (DefaultTreeSelectionModel) super.clone();
206: cloned.changeSupport = null;
207: cloned.selection = (TreePath[]) selection.clone();
208: cloned.listenerList = new EventListenerList();
209: cloned.listSelectionModel =
210: (DefaultListSelectionModel) listSelectionModel.clone();
211: cloned.selectedPaths = new HashSet<TreePath>();
212: cloned.tmpPaths = new HashSet<TreePath>();
213:
214: return cloned;
215: }
216:
217:
223: public String toString()
224: {
225: if (isSelectionEmpty())
226: return "[selection empty]";
227: else
228: {
229: CPStringBuilder b = new CPStringBuilder("selected rows: [");
230: for (int i = 0; i < selection.length; i++)
231: {
232: b.append(getRow(selection[i]));
233: b.append(' ');
234: }
235: b.append(", lead " + getLeadSelectionRow());
236: return b.toString();
237: }
238: }
239:
240:
246: private void writeObject(ObjectOutputStream value0) throws IOException
247: {
248:
249: }
250:
251:
258: private void readObject(ObjectInputStream value0) throws IOException,
259: ClassNotFoundException
260: {
261:
262: }
263:
264:
270: public void setRowMapper(RowMapper mapper)
271: {
272: rowMapper = mapper;
273: resetRowSelection();
274: }
275:
276:
283: public RowMapper getRowMapper()
284: {
285: return rowMapper;
286: }
287:
288:
299: public void setSelectionMode(int mode)
300: {
301: int oldMode = selectionMode;
302: selectionMode = mode;
303:
304: if (selectionMode != SINGLE_TREE_SELECTION
305: && selectionMode != CONTIGUOUS_TREE_SELECTION
306: && selectionMode != DISCONTIGUOUS_TREE_SELECTION)
307: selectionMode = DISCONTIGUOUS_TREE_SELECTION;
308:
309:
310: if (oldMode != selectionMode && changeSupport != null)
311: changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, oldMode,
312: selectionMode);
313: }
314:
315:
324: public int getSelectionMode()
325: {
326: return selectionMode;
327: }
328:
329:
335: public void setSelectionPath(TreePath path)
336: {
337: TreePath[] paths = null;
338: if (path != null)
339: paths = new TreePath[]{ path };
340: setSelectionPaths(paths);
341: }
342:
343:
349: int getRow(TreePath path)
350: {
351: RowMapper mapper = getRowMapper();
352:
353: if (mapper instanceof AbstractLayoutCache)
354: {
355:
356:
357: AbstractLayoutCache ama = (AbstractLayoutCache) mapper;
358: return ama.getRowForPath(path);
359: }
360: else if (mapper != null)
361: {
362:
363: int[] rows = mapper.getRowsForPaths(new TreePath[] { path });
364: if (rows.length == 0)
365: return - 1;
366: else
367: return rows[0];
368: }
369: return -1;
370: }
371:
372:
379: public void setSelectionPaths(TreePath[] paths)
380: {
381: int oldLength = 0;
382: if (selection != null)
383: oldLength = selection.length;
384: int newLength = 0;
385: if (paths != null)
386: newLength = paths.length;
387: if (newLength > 0 || oldLength > 0)
388: {
389:
390:
391: if ((selectionMode == SINGLE_TREE_SELECTION && newLength > 1)
392: || (selectionMode == CONTIGUOUS_TREE_SELECTION && newLength > 0
393: && ! arePathsContiguous(paths)))
394: {
395: paths = new TreePath[] { paths[0] };
396: newLength = 1;
397: }
398:
399: Vector<PathPlaceHolder> changedPaths = null;
400: tmpPaths.clear();
401: int validPaths = 0;
402: TreePath oldLeadPath = leadPath;
403: for (int i = 0; i < newLength; i++)
404: {
405: if (paths[i] != null && ! tmpPaths.contains(paths[i]))
406: {
407: validPaths++;
408: tmpPaths.add(paths[i]);
409: if (! selectedPaths.contains(paths[i]))
410: {
411: if (changedPaths == null)
412: changedPaths = new Vector<PathPlaceHolder>();
413: changedPaths.add(new PathPlaceHolder(paths[i], true));
414: }
415: leadPath = paths[i];
416: }
417: }
418:
419: TreePath[] newSelection = null;
420: if (validPaths != 0)
421: {
422: if (validPaths != newLength)
423: {
424:
425:
426: newSelection = new TreePath[validPaths];
427: Iterator<TreePath> newPaths = tmpPaths.iterator();
428: validPaths = 0;
429: for (int i = 0; newPaths.hasNext(); i++)
430: newSelection[i] = newPaths.next();
431: }
432: else
433: {
434: newSelection = new TreePath[paths.length];
435: System.arraycopy(paths, 0, newSelection, 0, paths.length);
436: }
437: }
438:
439:
440: for (int i = 0; i < oldLength; i++)
441: {
442: if (selection[i] != null && ! tmpPaths.contains(selection[i]))
443: {
444: if (changedPaths == null)
445: changedPaths = new Vector<PathPlaceHolder>();
446: changedPaths.add(new PathPlaceHolder(selection[i], false));
447: }
448: }
449:
450:
451: selection = newSelection;
452: HashSet<TreePath> tmp = selectedPaths;
453: selectedPaths = tmpPaths;
454: tmpPaths = tmp;
455: tmpPaths.clear();
456:
457:
458: if (selection != null)
459: insureUniqueness();
460: updateLeadIndex();
461: resetRowSelection();
462: if (changedPaths != null && changedPaths.size() > 0)
463: notifyPathChange(changedPaths, oldLeadPath);
464: }
465: }
466:
467:
477: public void addSelectionPath(TreePath path)
478: {
479: if (path != null)
480: {
481: TreePath[] add = new TreePath[]{ path };
482: addSelectionPaths(add);
483: }
484: }
485:
486:
493: public void addSelectionPaths(TreePath[] paths)
494: {
495: int length = paths != null ? paths.length : 0;
496: if (length > 0)
497: {
498: if (selectionMode == SINGLE_TREE_SELECTION)
499: setSelectionPaths(paths);
500: else if (selectionMode == CONTIGUOUS_TREE_SELECTION
501: && ! canPathsBeAdded(paths))
502: {
503: if (arePathsContiguous(paths))
504: setSelectionPaths(paths);
505: else
506: setSelectionPaths(new TreePath[] { paths[0] });
507: }
508: else
509: {
510: Vector<PathPlaceHolder> changedPaths = null;
511: tmpPaths.clear();
512: int validPaths = 0;
513: TreePath oldLeadPath = leadPath;
514: int oldPaths = 0;
515: if (selection != null)
516: oldPaths = selection.length;
517: int i;
518: for (i = 0; i < length; i++)
519: {
520: if (paths[i] != null)
521: {
522: if (! selectedPaths.contains(paths[i]))
523: {
524: validPaths++;
525: if (changedPaths == null)
526: changedPaths = new Vector<PathPlaceHolder>();
527: changedPaths.add(new PathPlaceHolder(paths[i], true));
528: selectedPaths.add(paths[i]);
529: tmpPaths.add(paths[i]);
530: }
531: leadPath = paths[i];
532: }
533: }
534: if (validPaths > 0)
535: {
536: TreePath[] newSelection = new TreePath[oldPaths + validPaths];
537: if (oldPaths > 0)
538: System.arraycopy(selection, 0, newSelection, 0, oldPaths);
539: if (validPaths != paths.length)
540: {
541:
542:
543: Iterator<TreePath> newPaths = tmpPaths.iterator();
544: i = oldPaths;
545: while (newPaths.hasNext())
546: {
547: newSelection[i] = newPaths.next();
548: i++;
549: }
550: }
551: else
552: System.arraycopy(paths, 0, newSelection, oldPaths,
553: validPaths);
554: selection = newSelection;
555: insureUniqueness();
556: updateLeadIndex();
557: resetRowSelection();
558: if (changedPaths != null && changedPaths.size() > 0)
559: notifyPathChange(changedPaths, oldLeadPath);
560: }
561: else
562: leadPath = oldLeadPath;
563: tmpPaths.clear();
564: }
565: }
566: }
567:
568:
574: public void removeSelectionPath(TreePath path)
575: {
576: if (path != null)
577: removeSelectionPaths(new TreePath[]{ path });
578: }
579:
580:
586: public void removeSelectionPaths(TreePath[] paths)
587: {
588: if (paths != null && selection != null && paths.length > 0)
589: {
590: if (! canPathsBeRemoved(paths))
591: clearSelection();
592: else
593: {
594: Vector<PathPlaceHolder> pathsToRemove = null;
595: for (int i = paths.length - 1; i >= 0; i--)
596: {
597: if (paths[i] != null && selectedPaths.contains(paths[i]))
598: {
599: if (pathsToRemove == null)
600: pathsToRemove = new Vector<PathPlaceHolder>();
601: selectedPaths.remove(paths[i]);
602: pathsToRemove.add(new PathPlaceHolder(paths[i],
603: false));
604: }
605: }
606: if (pathsToRemove != null)
607: {
608: int numRemove = pathsToRemove.size();
609: TreePath oldLead = leadPath;
610: if (numRemove == selection.length)
611: selection = null;
612: else
613: {
614: selection = new TreePath[selection.length - numRemove];
615: Iterator<TreePath> keep = selectedPaths.iterator();
616: for (int valid = 0; keep.hasNext(); valid++)
617: selection[valid] = keep.next();
618: }
619:
620: if (leadPath != null && ! selectedPaths.contains(leadPath))
621: {
622: if (selection != null)
623: leadPath = selection[selection.length - 1];
624: else
625: leadPath = null;
626: }
627: else if (selection != null)
628: leadPath = selection[selection.length - 1];
629: else
630: leadPath = null;
631: updateLeadIndex();
632: resetRowSelection();
633: notifyPathChange(pathsToRemove, oldLead);
634: }
635: }
636: }
637: }
638:
639:
645: public TreePath getSelectionPath()
646: {
647: if ((selection == null) || (selection.length == 0))
648: return null;
649: else
650: return selection[0];
651: }
652:
653:
658: public TreePath[] getSelectionPaths()
659: {
660: return selection;
661: }
662:
663:
668: public int getSelectionCount()
669: {
670: if (selection == null)
671: return 0;
672: else
673: return selection.length;
674: }
675:
676:
683: public boolean isPathSelected(TreePath path)
684: {
685: if (selection == null)
686: return false;
687:
688: for (int i = 0; i < selection.length; i++)
689: {
690: if (selection[i].equals(path))
691: return true;
692: }
693: return false;
694: }
695:
696:
702: public boolean isSelectionEmpty()
703: {
704: return (selection == null) || (selection.length == 0);
705: }
706:
707:
710: public void clearSelection()
711: {
712: if (selection != null)
713: {
714: int selectionLength = selection.length;
715: boolean[] news = new boolean[selectionLength];
716: Arrays.fill(news, false);
717: TreeSelectionEvent event = new TreeSelectionEvent(this, selection,
718: news, leadPath,
719: null);
720: leadPath = null;
721: leadIndex = 0;
722: leadRow = 0;
723: selectedPaths.clear();
724: selection = null;
725: resetRowSelection();
726: fireValueChanged(event);
727: }
728: }
729:
730:
735: public void addTreeSelectionListener(TreeSelectionListener listener)
736: {
737: listenerList.add(TreeSelectionListener.class, listener);
738: }
739:
740:
745: public void removeTreeSelectionListener(TreeSelectionListener listener)
746: {
747: listenerList.remove(TreeSelectionListener.class, listener);
748: }
749:
750:
756: public TreeSelectionListener[] getTreeSelectionListeners()
757: {
758: return (TreeSelectionListener[]) getListeners(TreeSelectionListener.class);
759: }
760:
761:
766: protected void fireValueChanged(TreeSelectionEvent event)
767: {
768: TreeSelectionListener[] listeners = getTreeSelectionListeners();
769:
770: for (int i = 0; i < listeners.length; ++i)
771: listeners[i].valueChanged(event);
772: }
773:
774:
781: public <T extends EventListener> T[] getListeners(Class<T> listenerType)
782: {
783: return listenerList.getListeners(listenerType);
784: }
785:
786:
791: public int[] getSelectionRows()
792: {
793: int[] rows = null;
794: if (rowMapper != null && selection != null)
795: {
796: rows = rowMapper.getRowsForPaths(selection);
797: if (rows != null)
798: {
799:
800: int invisible = 0;
801: for (int i = rows.length - 1; i >= 0; i--)
802: {
803: if (rows[i] == -1)
804: invisible++;
805:
806: }
807:
808: if (invisible > 0)
809: {
810: if (invisible == rows.length)
811: rows = null;
812: else
813: {
814: int[] newRows = new int[rows.length - invisible];
815: int visCount = 0;
816: for (int i = rows.length - 1; i >= 0; i--)
817: {
818: if (rows[i] != -1)
819: {
820: newRows[visCount] = rows[i];
821: visCount++;
822: }
823: }
824: rows = newRows;
825: }
826: }
827: }
828: }
829: return rows;
830: }
831:
832:
837: public int getMinSelectionRow()
838: {
839: return listSelectionModel.getMinSelectionIndex();
840: }
841:
842:
847: public int getMaxSelectionRow()
848: {
849: return listSelectionModel.getMaxSelectionIndex();
850: }
851:
852:
862: public boolean isRowSelected(int row)
863: {
864: return listSelectionModel.isSelectedIndex(row);
865: }
866:
867:
870: public void resetRowSelection()
871: {
872: listSelectionModel.clearSelection();
873: if (selection != null && rowMapper != null)
874: {
875: int[] rows = rowMapper.getRowsForPaths(selection);
876:
877: for (int i = 0; i < rows.length; i++)
878: {
879: int row = rows[i];
880: if (row != -1)
881: listSelectionModel.addSelectionInterval(row, row);
882: }
883:
884: if (leadIndex != -1 && rows != null)
885: leadRow = rows[leadIndex];
886: else if (leadPath != null)
887: {
888: TreePath[] tmp = new TreePath[]{ leadPath };
889: rows = rowMapper.getRowsForPaths(tmp);
890: leadRow = rows != null ? rows[0] : -1;
891: }
892: else
893: leadRow = -1;
894: insureRowContinuity();
895: }
896: else
897: leadRow = -1;
898: }
899:
900:
905: public int getLeadSelectionRow()
906: {
907: return leadRow;
908: }
909:
910:
915: public TreePath getLeadSelectionPath()
916: {
917: return leadPath;
918: }
919:
920:
925: public void addPropertyChangeListener(PropertyChangeListener listener)
926: {
927: if (changeSupport == null)
928: changeSupport = new SwingPropertyChangeSupport(this);
929: changeSupport.addPropertyChangeListener(listener);
930: }
931:
932:
937: public void removePropertyChangeListener(PropertyChangeListener listener)
938: {
939: if (changeSupport != null)
940: changeSupport.removePropertyChangeListener(listener);
941: }
942:
943:
949: public PropertyChangeListener[] getPropertyChangeListeners()
950: {
951: PropertyChangeListener[] listeners = null;
952: if (changeSupport != null)
953: listeners = changeSupport.getPropertyChangeListeners();
954: else
955: listeners = new PropertyChangeListener[0];
956: return listeners;
957: }
958:
959:
968: protected void insureRowContinuity()
969: {
970: if (selectionMode == CONTIGUOUS_TREE_SELECTION && selection != null
971: && rowMapper != null)
972: {
973: int min = listSelectionModel.getMinSelectionIndex();
974: if (min != -1)
975: {
976: int max = listSelectionModel.getMaxSelectionIndex();
977: for (int i = min; i <= max; i++)
978: {
979: if (! listSelectionModel.isSelectedIndex(i))
980: {
981: if (i == min)
982: clearSelection();
983: else
984: {
985: TreePath[] newSelection = new TreePath[i - min];
986: int[] rows = rowMapper.getRowsForPaths(selection);
987: for (int j = 0; j < rows.length; j++)
988: {
989: if (rows[j] < i)
990: newSelection[rows[j] - min] = selection[j];
991: }
992: setSelectionPaths(newSelection);
993: break;
994: }
995: }
996: }
997: }
998: }
999: else if (selectionMode == SINGLE_TREE_SELECTION && selection != null
1000: && selection.length > 1)
1001: setSelectionPath(selection[0]);
1002: }
1003:
1004:
1013: protected boolean arePathsContiguous(TreePath[] paths)
1014: {
1015: if (rowMapper == null || paths.length < 2)
1016: return true;
1017:
1018: int length = paths.length;
1019: TreePath[] tmp = new TreePath[1];
1020: tmp[0] = paths[0];
1021: int min = rowMapper.getRowsForPaths(tmp)[0];
1022: BitSet selected = new BitSet();
1023: int valid = 0;
1024: for (int i = 0; i < length; i++)
1025: {
1026: if (paths[i] != null)
1027: {
1028: tmp[0] = paths[i];
1029: int[] rows = rowMapper.getRowsForPaths(tmp);
1030: if (rows == null)
1031: return false;
1032: int row = rows[0];
1033: if (row == -1 || row < (min - length) || row > (min + length))
1034: return false;
1035: min = Math.min(min, row);
1036: if (! selected.get(row))
1037: {
1038: selected.set(row);
1039: valid++;
1040: }
1041:
1042: }
1043: }
1044: int max = valid + min;
1045: for (int i = min; i < max; i++)
1046: if (! selected.get(i))
1047: return false;
1048: return true;
1049: }
1050:
1051:
1065: protected boolean canPathsBeAdded(TreePath[] paths)
1066: {
1067: if (paths == null || paths.length == 0 || rowMapper == null
1068: || selection == null || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1069: return true;
1070:
1071: BitSet selected = new BitSet();
1072: int min = listSelectionModel.getMinSelectionIndex();
1073: int max = listSelectionModel.getMaxSelectionIndex();
1074: TreePath[] tmp = new TreePath[1];
1075: if (min != -1)
1076: {
1077:
1078: for (int i = min; i <= max; i++)
1079: selected.set(i);
1080: }
1081: else
1082: {
1083: tmp[0] = paths[0];
1084: min = rowMapper.getRowsForPaths(tmp)[0];
1085: max = min;
1086: }
1087:
1088: for (int i = paths.length - 1; i >= 0; i--)
1089: {
1090: if (paths[i] != null)
1091: {
1092: tmp[0] = paths[i];
1093: int[] rows = rowMapper.getRowsForPaths(tmp);
1094: if (rows == null)
1095: return false;
1096: int row = rows[0];
1097: if (row == -1)
1098: return false;
1099: min = Math.min(min, row);
1100: max = Math.max(max, row);
1101: selected.set(row);
1102: }
1103: }
1104:
1105: for (int i = min; i <= max; i++)
1106: if (! selected.get(i))
1107: return false;
1108: return true;
1109: }
1110:
1111:
1119: protected boolean canPathsBeRemoved(TreePath[] paths)
1120: {
1121: if (rowMapper == null || isSelectionEmpty()
1122: || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1123: return true;
1124:
1125: HashSet<TreePath> set = new HashSet<TreePath>();
1126: for (int i = 0; i < selection.length; i++)
1127: set.add(selection[i]);
1128:
1129: for (int i = 0; i < paths.length; i++)
1130: set.remove(paths[i]);
1131:
1132: TreePath[] remaining = new TreePath[set.size()];
1133: Iterator<TreePath> iter = set.iterator();
1134:
1135: for (int i = 0; i < remaining.length; i++)
1136: remaining[i] = iter.next();
1137:
1138: return arePathsContiguous(remaining);
1139: }
1140:
1141:
1149: protected void notifyPathChange(Vector<PathPlaceHolder> vPaths,
1150: TreePath oldLeadSelection)
1151: {
1152:
1153: int numChangedPaths = vPaths.size();
1154: boolean[] news = new boolean[numChangedPaths];
1155: TreePath[] paths = new TreePath[numChangedPaths];
1156: for (int i = 0; i < numChangedPaths; i++)
1157: {
1158: PathPlaceHolder p = vPaths.get(i);
1159: news[i] = p.isNew;
1160: paths[i] = p.path;
1161: }
1162:
1163: TreeSelectionEvent event = new TreeSelectionEvent(this, paths, news,
1164: oldLeadSelection,
1165: leadPath);
1166: fireValueChanged(event);
1167: }
1168:
1169:
1173: protected void updateLeadIndex()
1174: {
1175: leadIndex = -1;
1176: if (leadPath != null)
1177: {
1178: leadRow = -1;
1179: if (selection == null)
1180: leadPath = null;
1181: else
1182: {
1183: for (int i = selection.length - 1; i >= 0 && leadIndex == -1; i--)
1184: {
1185: if (selection[i] == leadPath)
1186: leadIndex = i;
1187: }
1188: }
1189: }
1190: }
1191:
1192:
1198: protected void insureUniqueness()
1199: {
1200:
1201: }
1202: }