package textbender.o.swing.table; // Copyright 2003-2005, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import java.beans.*; import java.util.prefs.Preferences; import javax.swing.event.*; import javax.swing.JTable; import javax.swing.table.*; import textbender.o.awt.RunBuffer; import textbender.g.hold.*; import textbender.o.swing.event.TableColumnModelListener0; import textbender.g.util.prefs.IntegerPreference; import static textbender.o.awt.FontX.EN; /** Preference storage for the column widths of a table. * Stores under keys "column.c.width", where c is the column index. */ public class ColumnWidthsPreference { /** Constructs a ColumnWidthsPreference. * Begins to automatically store changes only after you call restore(). *

* There is currently no way to remove it from the columnModel. * Create one only per columnModel, or they will accumulate. *

* * @param columnModel whose column widths to store and restore * @param preferences per {@linkplain #preferences() preferences}() * @param runBuffer (a slow one is fine) */ public ColumnWidthsPreference( TableColumnModel columnModel, Preferences preferences, RunBuffer runBuffer ) { assert java.awt.EventQueue.isDispatchThread(); this.columnModel = columnModel; this.preferences = preferences; this.runBuffer = runBuffer; } // ------------------------------------------------------------------------------------ /** Returns the preference node used for storage. */ public Preferences preferences() { return preferences; } private final Preferences preferences; /** Restores the preferred widths, and begins automatically storing changes. * Normally, called once only; subsequent calls should have no effect. */ public void restore() { assert java.awt.EventQueue.isDispatchThread(); if( columnReflector == null ) columnReflector = new ColumnReflector(); // lazilly // though normally called only once for( int c = 0; c < columnReflector.storedWidthArray.length; ++c ) { int preferredWidth = columnReflector.storedWidthArray[c].get(); if( preferredWidth == noPreference ) continue; columnModel.getColumn( c ).setPreferredWidth( preferredWidth ); } restore_complete = true; } private boolean restore_complete; //// P r i v a t e /////////////////////////////////////////////////////////////////////// private final TableColumnModel columnModel; private final static int noPreference = -1; private final RunBuffer runBuffer; // ==================================================================================== private ColumnReflector columnReflector; // final when initialized /** Tracks columns in the model, as they change. */ private final class ColumnReflector extends TableColumnModelListener0 { private ColumnReflector() { mirror(); } // fire up private IntegerPreference[] storedWidthArray; private void mirror() { spool.unwind(); spool = new Spool1(); storedWidthArray = new IntegerPreference[columnModel.getColumnCount()]; for( int c = 0; c < storedWidthArray.length; ++c ) { final TableColumn column = columnModel.getColumn( c ); String specificKey = "column." + Integer.toString(c) + ".width"; storedWidthArray[c] = new IntegerPreference ( preferences, specificKey, /*default*/noPreference ); column.addPropertyChangeListener( storer ); spool.add( new Hold() { public void release() { column.removePropertyChangeListener( storer ); } }); } } private Spool spool = new Spool0(); // - T a b l e - C o l u m n - M o d e l - L i s t e n e r -------------- public void columnAdded( TableColumnModelEvent e ) { assert java.awt.EventQueue.isDispatchThread(); mirror(); storer.runLater(); } public void columnRemoved( TableColumnModelEvent e ) { assert java.awt.EventQueue.isDispatchThread(); mirror(); storer.runLater(); } }; // ==================================================================================== private final Storer storer = new Storer(); private final class Storer implements PropertyChangeListener, Runnable { private void runLater() { runBuffer.schedule( Storer.this ); } // - P r o p e r t y - C h a n g e - L i s t e n e r -------------------- public void propertyChange( PropertyChangeEvent e ) { assert java.awt.EventQueue.isDispatchThread(); if( !restore_complete ) return; // fudge to prevent redundant re-storage of changes during initial restore if( !e.getPropertyName().equals( "preferredWidth" )) return; runLater(); // later and coalesced, because these events can be rapid-fire } // - R u n n a b l e ---------------------------------------------------- public void run() // store them all, they tend to change as a group, and we are well enough buffered here { assert java.awt.EventQueue.isDispatchThread(); for( int c = 0; c < columnReflector.storedWidthArray.length; ++c ) { if( c == columnModel.getColumnCount() ) { assert false; break; } TableColumn column = columnModel.getColumn( c ); columnReflector.storedWidthArray[c].put( column.getWidth() ); } } }; }