package com.onaro.util.jfc.grouping;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.BitSet;
import java.util.Stack;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
/**
* A selection model that recursively selects the rows of the selection model of the
* {@link GroupingTable} (the selection of the GroupingTable may contain
* groups and rows, this model will select only the rows in all the selected groups and
* sub-groups).
*/
public class SourceTableGroupSelectionModel extends DefaultListSelectionModel implements TreeSelectionListener {
private static final long serialVersionUID = 1L;
/**
* The tree-model of the {@link GroupingTable}. Used for finding all the rows
* in a group.
*/
private TreeModel treeModel;
private boolean tableRebuilding = false;
private TreeSelectionModel treeSelectionModel;
/**
* Initialize the selection model for the given {@link GroupingTable}. Register
* for selection model changes in the GroupingTable.
*
* @param groupingTable the GroupingTable who's selection model
* is being translated
*/
public SourceTableGroupSelectionModel(GroupingTable groupingTable) {
JTree tree = groupingTable.getTree();
treeModel = tree.getModel();
treeSelectionModel = tree.getSelectionModel();
tree.addTreeSelectionListener(this);
groupingTable.addPropertyChangeListener(GroupingTable.REBUILDING_PROPERTY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (GroupingTable.REBUILDING_PROPERTY.equals(evt.getPropertyName())) {
tableRebuilding = ((GroupingTable)evt.getSource()).isRebuilding();
if (!tableRebuilding)
rebuildFromTree();
}
}
});
}
/**
* Whenever selection change in the tree, update this model, except if the grouping table is now rebuilding.
* @param e
*/
public void valueChanged(TreeSelectionEvent e) {
if (tableRebuilding) return;
BitSet addedRows = new BitSet();
BitSet removedRows = new BitSet();
TreePath[] paths = e.getPaths();
for (int i = 0; i < paths.length; ++i) {
translatePathToRow(paths[i], e.isAddedPath(i) ? addedRows : removedRows);
}
/**
* Remove paths that are both "removed" and "added".
*/
BitSet tmpAdded = new BitSet();
tmpAdded.or(addedRows);
addedRows.andNot(removedRows);
removedRows.andNot(tmpAdded);
/**
* Notify the listeners of this selection model of the change that was
* detected.
*/
if (addedRows.cardinality() > 0 || removedRows.cardinality() > 0) {
setValueIsAdjusting(true);
int min = addedRows.nextSetBit(0);
while (min >= 0) {
int max = addedRows.nextClearBit(min);
addSelectionInterval(min, max-1);
min = addedRows.nextSetBit(max);
}
min = removedRows.nextSetBit(0);
while (min >= 0) {
int max = removedRows.nextClearBit(min);
removeSelectionInterval(min, max-1);
min = removedRows.nextSetBit(max);
}
setValueIsAdjusting(false);
}
}
/**
* Find all the rows that descend from the nodes of the path and register them with the addedRows/removedRows.
* @param path the tree-selection-path to convert to row
* @param rows holds the converted row numbers
*/
private void translatePathToRow(TreePath path, BitSet rows) {
Stack