/*
* Created on 08.02.2007
*
*/
package com.nexuiz.demorecorder.ui.swinggui.utils;
import java.awt.Component;
import java.beans.DefaultPersistenceDelegate;
import java.beans.XMLEncoder;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.SortOrder;
import javax.swing.RowSorter.SortKey;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.JXTaskPane;
import org.jdesktop.swingx.sort.SortUtils;
import org.jdesktop.swingx.table.TableColumnExt;
/**
* Container class for SwingX specific SessionStorage Properties. Is Factory for
* custom PersistanceDelegates
*/
public class XProperties {
/**
*
* Registers all custom PersistenceDelegates needed by contained Property
* classes.
*
*
* PersistenceDelegates are effectively static properties shared by all
* encoders. In other words: Register once on an arbitrary encoder makes
* them available for all. Example usage:
*
*
*
* new XProperties.registerPersistenceDelegates();
*
*
*
* PENDING JW: cleanup for 1.6 sorting/filtering incomplete. Missing storage
* - multiple sort keys
*
* PENDING JW: except for comparators: didn't before and is not state that's
* configurable by users ... so probably won't, not sure, need to revisit -
* comparator (?) - filters (?) - renderers/stringvalues (?) - enhanced
* sort-related table state (?)
*/
public void registerPersistenceDelegates() {
XMLEncoder encoder = new XMLEncoder(System.out);
encoder.setPersistenceDelegate(SortKeyState.class, new DefaultPersistenceDelegate(
new String[] { "ascending", "modelIndex" }));
encoder.setPersistenceDelegate(ColumnState.class, new DefaultPersistenceDelegate(
new String[] { "width", "preferredWidth", "modelIndex", "visible", "viewIndex" }));
encoder.setPersistenceDelegate(XTableState.class, new DefaultPersistenceDelegate(
new String[] { "columnStates", "sortKeyState", "horizontalScrollEnabled" }));
}
/**
* Session storage support for JXTaskPane.
*/
public static class XTaskPaneProperty implements Serializable {
private static final long serialVersionUID = -4069436038178318216L;
public Object getSessionState(Component c) {
checkComponent(c);
return new XTaskPaneState(((JXTaskPane) c).isCollapsed());
}
public void setSessionState(Component c, Object state) {
checkComponent(c);
if ((state != null) && !(state instanceof XTaskPaneState)) {
throw new IllegalArgumentException("invalid state");
}
((JXTaskPane) c).setCollapsed(((XTaskPaneState) state).isCollapsed());
}
private void checkComponent(Component component) {
if (component == null) {
throw new IllegalArgumentException("null component");
}
if (!(component instanceof JXTaskPane)) {
throw new IllegalArgumentException("invalid component");
}
}
}
public static class XTaskPaneState implements Serializable {
private static final long serialVersionUID = 3363688961112031969L;
private boolean collapsed;
public XTaskPaneState() {
this(false);
}
/**
* @param b
*/
public XTaskPaneState(boolean collapsed) {
this.setCollapsed(collapsed);
}
/**
* @param collapsed
* the collapsed to set
*/
public void setCollapsed(boolean collapsed) {
this.collapsed = collapsed;
}
/**
* @return the collapsed
*/
public boolean isCollapsed() {
return collapsed;
}
}
/**
* Session storage support for JXTable.
*/
public static class XTableProperty implements Serializable {
private static final long serialVersionUID = -5064142292091374301L;
public Object getSessionState(Component c) {
checkComponent(c);
JXTable table = (JXTable) c;
List columnStates = new ArrayList();
List columns = table.getColumns(true);
List visibleColumns = table.getColumns();
for (TableColumn column : columns) {
columnStates.add(new ColumnState((TableColumnExt) column, visibleColumns
.indexOf(column)));
}
XTableState tableState = new XTableState(columnStates
.toArray(new ColumnState[columnStates.size()]));
tableState.setHorizontalScrollEnabled(table.isHorizontalScrollEnabled());
List extends SortKey> sortKeys = null;
if (table.getRowSorter() != null) {
sortKeys = table.getRowSorter().getSortKeys();
}
// PENDING: store all!
if ((sortKeys != null) && (sortKeys.size() > 0)) {
tableState.setSortKey(sortKeys.get(0));
}
return tableState;
}
public void setSessionState(Component c, Object state) {
checkComponent(c);
JXTable table = (JXTable) c;
XTableState tableState = ((XTableState) state);
ColumnState[] columnState = tableState.getColumnStates();
List columns = table.getColumns(true);
if (canRestore(columnState, columns)) {
for (int i = 0; i < columnState.length; i++) {
columnState[i].configureColumn((TableColumnExt) columns.get(i));
}
restoreVisibleSequence(columnState, table.getColumnModel());
}
table.setHorizontalScrollEnabled(tableState.getHorizontalScrollEnabled());
if (tableState.getSortKey() != null) {
table.getRowSorter()
.setSortKeys(Collections.singletonList(tableState.getSortKey()));
}
}
private void restoreVisibleSequence(ColumnState[] columnStates, TableColumnModel model) {
List visibleStates = getSortedVisibleColumnStates(columnStates);
for (int i = 0; i < visibleStates.size(); i++) {
TableColumn column = model.getColumn(i);
int modelIndex = visibleStates.get(i).getModelIndex();
if (modelIndex != column.getModelIndex()) {
int currentIndex = -1;
for (int j = i + 1; j < model.getColumnCount(); j++) {
TableColumn current = model.getColumn(j);
if (current.getModelIndex() == modelIndex) {
currentIndex = j;
break;
}
}
model.moveColumn(currentIndex, i);
}
}
}
private List getSortedVisibleColumnStates(ColumnState[] columnStates) {
List visibleStates = new ArrayList();
for (ColumnState columnState : columnStates) {
if (columnState.getVisible()) {
visibleStates.add(columnState);
}
}
Collections.sort(visibleStates, new VisibleColumnIndexComparator());
return visibleStates;
}
/**
* Returns a boolean to indicate if it's reasonably safe to restore the
* properties of columns in the list from the columnStates. Here:
* returns true if the length of both are the same and the modelIndex of
* the items at the same position are the same, otherwise returns false.
*
* @param columnState
* @param columns
* @return
*/
private boolean canRestore(ColumnState[] columnState, List columns) {
if ((columnState == null) || (columnState.length != columns.size()))
return false;
for (int i = 0; i < columnState.length; i++) {
if (columnState[i].getModelIndex() != columns.get(i).getModelIndex()) {
return false;
}
}
return true;
}
private void checkComponent(Component component) {
if (component == null) {
throw new IllegalArgumentException("null component");
}
if (!(component instanceof JXTable)) {
throw new IllegalArgumentException("invalid component - expected JXTable");
}
}
}
public static class XTableState implements Serializable {
private static final long serialVersionUID = -3566913244872587438L;
ColumnState[] columnStates = new ColumnState[0];
boolean horizontalScrollEnabled;
SortKeyState sortKeyState;
public XTableState(ColumnState[] columnStates, SortKeyState sortKeyState,
boolean horizontalScrollEnabled) {
this.columnStates = copyColumnStates(columnStates);
this.sortKeyState = sortKeyState;
setHorizontalScrollEnabled(horizontalScrollEnabled);
}
public void setSortKey(SortKey sortKey) {
this.sortKeyState = new SortKeyState(sortKey);
}
private SortKey getSortKey() {
if (sortKeyState != null) {
return sortKeyState.getSortKey();
}
return null;
}
public XTableState(ColumnState[] columnStates) {
this.columnStates = copyColumnStates(columnStates);
}
public ColumnState[] getColumnStates() {
return copyColumnStates(this.columnStates);
}
public boolean getHorizontalScrollEnabled() {
return horizontalScrollEnabled;
}
public void setHorizontalScrollEnabled(boolean horizontalScrollEnabled) {
this.horizontalScrollEnabled = horizontalScrollEnabled;
}
private ColumnState[] copyColumnStates(ColumnState[] states) {
if (states == null) {
throw new IllegalArgumentException("invalid columnWidths");
}
ColumnState[] copy = new ColumnState[states.length];
System.arraycopy(states, 0, copy, 0, states.length);
return copy;
}
public SortKeyState getSortKeyState() {
return sortKeyState;
}
}
/**
* Quick hack to make SortKey encodable. How to write a PersistenceDelegate
* for a SortKey? Boils down to how to write a delegate for the
* uninstantiable class (SwingX) SortOrder which does enum-mimickry (defines
* privately intantiated constants)
*
*/
public static class SortKeyState implements Serializable {
private static final long serialVersionUID = 5819342622261460894L;
int modelIndex;
boolean ascending;
/**
* Constructor used by the custom PersistenceDelegate.
*
* @param ascending
* @param modelIndex
* @param comparator
*/
public SortKeyState(boolean ascending, int modelIndex) {
this.ascending = ascending;
this.modelIndex = modelIndex;
}
/**
* Constructor used by property.
*
* @param sortKey
*/
public SortKeyState(SortKey sortKey) {
this(SortUtils.isAscending(sortKey.getSortOrder()), sortKey.getColumn());
}
protected SortKey getSortKey() {
SortOrder sortOrder = getAscending() ? SortOrder.ASCENDING : SortOrder.DESCENDING;
return new SortKey(getModelIndex(), sortOrder);
}
public boolean getAscending() {
return ascending;
}
public int getModelIndex() {
return modelIndex;
}
}
public static class ColumnState implements Serializable {
private static final long serialVersionUID = 6037947151025126049L;
private int width;
private int preferredWidth;
private int modelIndex;
private boolean visible;
private int viewIndex;
/**
* Constructor used by the custom PersistenceDelegate.
*
* @param width
* @param preferredWidth
* @param modelColumn
* @param visible
* @param viewIndex
*/
public ColumnState(int width, int preferredWidth, int modelColumn, boolean visible,
int viewIndex) {
this.width = width;
this.preferredWidth = preferredWidth;
this.modelIndex = modelColumn;
this.visible = visible;
this.viewIndex = viewIndex;
}
/**
* Constructor used by the Property.
*
* @param columnExt
* @param viewIndex
*/
public ColumnState(TableColumnExt columnExt, int viewIndex) {
this(columnExt.getWidth(), columnExt.getPreferredWidth(), columnExt.getModelIndex(),
columnExt.isVisible(), viewIndex);
}
/**
* Restores column properties if the model index is the same as the
* column's model index. Does nothing otherwise.
*
*
* Here the properties are: width, preferredWidth, visible.
*
* @param columnExt
* the column to configure
*/
public void configureColumn(TableColumnExt columnExt) {
if (modelIndex != columnExt.getModelIndex())
return;
columnExt.setPreferredWidth(preferredWidth);
columnExt.setWidth(width);
columnExt.setVisible(visible);
}
public int getModelIndex() {
return modelIndex;
}
public int getViewIndex() {
return viewIndex;
}
public boolean getVisible() {
return visible;
}
public int getWidth() {
return width;
}
public int getPreferredWidth() {
return preferredWidth;
}
}
public static class VisibleColumnIndexComparator implements Comparator