package org.obo.app.swing;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.TableColumn;
import org.apache.log4j.Logger;
/**
* Persists user changes to column widths and order for a table using java.util.prefs. Column
* titles should be unique within the table. If the column content (count or titles) is changed
* between uses of a table, columns will be reset to the default order until the user moves them
* again. Not only persists changes, but enacts the ordering on the table with orderColumns
*/
public class TableColumnPrefsSaver implements PropertyChangeListener, TableColumnModelListener {
private int defaultColumnWidth;
private JTable table;
private String autoSaveName;
/**
* Constructs a TableColumnPrefsSaver for the given table using a default column width of 150.
* Column widths and order are persisted to the preferences datastore. The autoSaveName should
* be sufficiently unique to avoid conflicts with saved states for other tables.
*/
public TableColumnPrefsSaver(JTable aTable, String autoSaveName) {
this(aTable, autoSaveName, 150);
}
/**
* Constructs a TableColumnPrefsSaver for the given table. Column widths and order are persisted
* to the preferences datastore. The autoSaveName should be sufficiently unique to avoid conflicts
* with saved states for other tables. If no column width values are already saved for this table, the
* defaultColumnWidth will be used.
*/
public TableColumnPrefsSaver(JTable aTable, String autoSaveName, int defaultColumnWidth) {
this.table = aTable;
this.autoSaveName = autoSaveName;
this.defaultColumnWidth = defaultColumnWidth;
this.table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
this.sizeColumns();
for (TableColumn column : this.getColumns()) {
column.addPropertyChangeListener(this);
}
this.orderColumns();
this.table.getColumnModel().addColumnModelListener(this);
}
@Override
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals("width")) {
this.saveColumnWidth((TableColumn)(e.getSource()));
}
}
@Override
public void columnAdded(TableColumnModelEvent e) {
this.saveColumnOrdering();
}
@Override
public void columnMarginChanged(ChangeEvent e) {}
@Override
public void columnMoved(TableColumnModelEvent e) {
this.saveColumnOrdering();
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
this.saveColumnOrdering();
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {}
/**
* Removes this object from listening to any tables or table columns.
*/
public void dispose() {
for (TableColumn column : this.getColumns()) {
column.removePropertyChangeListener(this);
}
this.table.getColumnModel().removeColumnModelListener(this);
}
private void sizeColumns() {
for (TableColumn column : this.getColumns()) {
final int width = this.getWidthPrefs().getInt(this.getColumnKey(column), this.defaultColumnWidth);
column.setPreferredWidth(width);
}
}
private void saveColumnWidth(TableColumn column) {
this.getWidthPrefs().putInt(this.getColumnKey(column), column.getWidth());
}
private void orderColumns() {
final List<String> prefNames;
try {
prefNames = Arrays.asList(this.getOrderPrefs().keys());
} catch (BackingStoreException e) {
log().error("Failed to read table column order from prefs", e);
return;
}
final List<String> columnKeys = this.getAllColumnKeys();
if (prefNames.containsAll(columnKeys) && columnKeys.containsAll(prefNames)) {
for (TableColumn column : this.getColumns()) {
final int newIndex = this.getOrderPrefs().getInt(this.getColumnKey(column), 0);
final int currentIndex = this.getIndexOfColumn(column);
final int columnCount = this.table.getColumnCount();
if ((newIndex < columnCount) && (currentIndex < columnCount)) {
this.table.getColumnModel().moveColumn(currentIndex, newIndex);
}
}
}
this.saveColumnOrdering();
}
private void saveColumnOrdering() {
try {
this.getOrderPrefs().clear();
} catch (BackingStoreException e) {
log().error("Unable to store table column ordering", e);
return;
}
for (int i = 0; i < this.table.getColumnModel().getColumnCount(); i++) {
this.getOrderPrefs().putInt(this.getColumnKey(this.table.getColumnName(i)), i);
}
}
private String getColumnName(TableColumn column) {
return column.getHeaderValue().toString();
}
private String getColumnKey(TableColumn column) {
return this.getColumnKey(this.getColumnName(column));
}
private String getColumnKey(String name) {
final String hashCode = "" + name.hashCode();
final int spaceForName = Preferences.MAX_KEY_LENGTH - hashCode.length();
final String shortName = (name.length() > spaceForName) ? name.substring(0, spaceForName) : name;
return shortName + hashCode;
}
private List<String> getAllColumnKeys() {
List<String> keys = new ArrayList<String>();
for (TableColumn column : this.getColumns()) {
keys.add(this.getColumnKey(column));
}
return keys;
}
private List<TableColumn> getColumns() {
return Collections.list(this.table.getColumnModel().getColumns());
}
private int getIndexOfColumn(TableColumn column) {
return this.getColumns().indexOf(column);
}
private Preferences getPrefsRoot() {
return Preferences.userNodeForPackage(this.getClass()).node(this.autoSaveName);
}
private Preferences getWidthPrefs() {
return this.getPrefsRoot().node("width");
}
private Preferences getOrderPrefs() {
return this.getPrefsRoot().node("order");
}
private static Logger log() {
return Logger.getLogger(TableColumnPrefsSaver.class);
}
}