/* * 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 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 { public int compare(Object o1, Object o2) { return ((ColumnState) o1).getViewIndex() - ((ColumnState) o2).getViewIndex(); } } }