net.sf.cuf.ui.table.ColumnVisibilitySupport Maven / Gradle / Ivy
package net.sf.cuf.ui.table;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JTable;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
/**
* Support showing/hiding columns in a {@link JTable}. It has to be bound to a table instance upon creation.
*
* It assumes that there is a 1:1 mapping from model columns to view columns (each model column must be displayed in
* exactly one view column).
*
* Be careful when manipulating the column model of the table from the application.
* There might be conflicts with the column showing/hiding. Especially two points have
* to be observed: do not try to remove any hidden columns from the column model and do
* not break the 1:1 mapping between model and view columns.
*
* This class notifies listeners about each showing/hiding of columns.
*
* This class listens for changes of the column model of the table. Whenever the column model is replaced
* by another instance all the invisible columns are forgotten. Unfortunately we are not able to notice
* all changes. So the application may have to explicitly call {@link #clearInvisibleColumns}.
* See {@link SortingTable#createDefaultColumnsFromModel} for a possible solution of this problem.
*
* @author Hendrik Wördehoff, sd&m AG
*/
public class ColumnVisibilitySupport
implements PropertyChangeListener
{
/** Property name from {@link JTable}. */
private static final String TABLE_PROPERTY_COLUMN_MODEL = "columnModel";
/** Property name from {@link JTable}. */
private static final String TABLE_PROPERTY_MODEL = "model";
/** Property name from {@link JTable}. */
private static final String TABLE_PROPERTY_AUTO_CREATE_COLUMNS_FROM_MODEL = "autoCreateColumnsFromModel";
/** Table to operate on. */
protected JTable mTable;
/**
* List of currently invisible table columns.
* The removed {@link TableColumn} objects are stored in this list.
*/
protected List mInvisibleColumns = new ArrayList<>();
/**
* List of {@link ColumnVisibilityChangeListener}s.
*/
protected EventListenerList mListenerList = new EventListenerList();
/**
* Constructor.
* We install ourselves as property change listener on the table.
*
* @param pTable table to operate on (must not be null
)
*/
public ColumnVisibilitySupport(final JTable pTable)
{
if ( pTable == null )
throw new IllegalArgumentException("table must not be null");
this.mTable = pTable;
pTable.addPropertyChangeListener(this);
}
/**
* Access the table we work on.
*
* @return table this instance operates on
*/
public JTable getTable()
{
return mTable;
}
/**
* Hide a column of our table.
* Listeners are notified after the column is hidden.
*
* @param pModelIndex column index in the table model
*/
protected void hideColumn(final int pModelIndex)
{
int viewIndex = mTable.convertColumnIndexToView(pModelIndex);
if (viewIndex >= 0)
{
// make table column invisible
TableColumnModel tcm = mTable.getColumnModel();
TableColumn col = tcm.getColumn(viewIndex);
tcm.removeColumn(col);
mInvisibleColumns.add(col);
// notify listeners
fireColumnVisibilityEventHidden(col);
}
}
/**
* Show a column of this table.
* Listeners are notified after the column is shown.
*
* @param pModelIndex column index in the table model
*/
protected void showColumn(final int pModelIndex)
{
for (Iterator i = mInvisibleColumns.iterator(); i.hasNext();)
{
TableColumn col = i.next();
if (col.getModelIndex() == pModelIndex)
{
// make table column visible
mTable.getColumnModel().addColumn(col);
i.remove();
// notify listeners
fireColumnVisibilityEventShown(col);
// finished
return;
}
}
}
/**
* Test whether a table column is currently visible.
*
* @param pModelIndex column index in the table model
* @return true
if table column is visible
*/
public boolean isColumnVisible(final int pModelIndex)
{
return mTable.convertColumnIndexToView(pModelIndex) >= 0;
}
/**
* Show or hide a table column.
*
* @param pModelIndex column index in the table model
* @param pVisible true
to make the column visible
*/
public void setColumnVisible(final int pModelIndex, final boolean pVisible)
{
if (pVisible)
showColumn(pModelIndex);
else
hideColumn(pModelIndex);
// will resize one or more of the columns in the table
// so that the total width of all of the JTable's columns will be equal to the width of the table
mTable.sizeColumnsToFit(-1);
}
/**
* Get the visibility of all table columns at once.
* The visibility is modeled as a boolean[]
. There is an
* entry in this array for each table model column. true
* means the respective column is visible. false
makes the
* column hidden.
*
* @return visiblity information for each column in the table model
*/
public boolean[] getColumnsVisible()
{
boolean[] visible = new boolean[mTable.getModel().getColumnCount()];
for (int i = 0; i < visible.length; i++)
{
visible[i] = isColumnVisible(i);
}
return visible;
}
/**
* Set the visibility of all table columns at once.
* The visibility is modeled as a boolean[]
. There must be an
* entry in this array for each table model column. true
* means the respective column is visible. false
makes the
* column hidden.
*
* @param pVisible visiblity information for each column in the table model
* @throws IllegalArgumentException if visible is null
or has a different number of entries than the table model
*/
public void setColumnsVisible(final boolean[] pVisible)
{
int columnCount = mTable.getModel().getColumnCount();
if (pVisible == null || pVisible.length != columnCount)
throw new IllegalArgumentException("visible must not be null and must contain the same number of columns as the table model");
for (int i = 0; i < columnCount; i++)
{
if (isColumnVisible(i) != pVisible[i])
{
setColumnVisible(i, pVisible[i]);
}
}
}
/**
* Make all table columns visible.
*/
public void setAllColumnsVisible()
{
int columnCount = mTable.getModel().getColumnCount();
for (int i = 0; i < columnCount; i++)
{
if (!isColumnVisible(i))
{
setColumnVisible(i, true);
}
}
}
/**
* Test if all columns are visible.
*
* @return true
if there are currently no invisible columns
*/
public boolean isAllColumnsVisible()
{
return mInvisibleColumns.isEmpty();
}
/**
* Fetch the hidden table column for a model index.
* If the table column with the index is not hidden this method will return null
.
* @param pModelIndex the model index
* @return table column object with the fitting model index or null
*/
public TableColumn getInvisibleColumn(final int pModelIndex)
{
for (final Object invisibleColumn : mInvisibleColumns)
{
TableColumn t = (TableColumn) invisibleColumn;
if (t.getModelIndex() == pModelIndex)
return t;
}
return null;
}
/**
* Clear the list of invisible columns.
*
* This method must be called whenever the table column model is changed completely.
* We do our best to catch this in {@link #propertyChange(java.beans.PropertyChangeEvent)}. But we cannot notice all
* changes. So you have to call this e.g. whenever {@link JTable#createDefaultColumnsFromModel}
* is called explicitely.
*/
public void clearInvisibleColumns()
{
mInvisibleColumns.clear();
}
/*** Property change listener *********************************************/
/**
* Forget all invisible columns if the column model of the table is changed.
* This may happen either by an explicitly {@link JTable#setColumnModel(TableColumnModel)} or
* implicitly by any action that calls {@link JTable#createDefaultColumnsFromModel}.
*
* @param pEvent information about the changed property
*/
public void propertyChange(final PropertyChangeEvent pEvent)
{
String propertyName = pEvent.getPropertyName();
if ( TABLE_PROPERTY_COLUMN_MODEL.equals(propertyName) ) // setColumnModel
{
//noinspection ObjectEquality
if ( pEvent.getNewValue() != pEvent.getOldValue() )
{
clearInvisibleColumns();
}
}
else if ( TABLE_PROPERTY_AUTO_CREATE_COLUMNS_FROM_MODEL.equals(propertyName) || // setAutoCreateColumnsFromModel
TABLE_PROPERTY_MODEL .equals(propertyName) ) // setModel
{
if ( mTable.getAutoCreateColumnsFromModel() )
{
// setAutoCreateColumnsFromModel and setModel call createDefaultColumnsFromModel if autoCreateColumnsFromModel is true
clearInvisibleColumns();
}
}
}
/*** Listener support *****************************************************/
/**
* Add a listener for showing/hiding of columns.
*
* @param pListener listener to add
*/
public void addColumnVisibilityChangeListener(final ColumnVisibilityChangeListener pListener)
{
mListenerList.add(ColumnVisibilityChangeListener.class, pListener);
}
/**
* Remove a listener for showing/hiding of columns.
*
* @param pListener listener to remove
*/
public void removeColumnVisibilityChangeListener(final ColumnVisibilityChangeListener pListener)
{
mListenerList.remove(ColumnVisibilityChangeListener.class, pListener);
}
/**
* Notify our listeners that a table column was shown.
*
* @param pColumn column that was shown
*/
protected void fireColumnVisibilityEventShown(final TableColumn pColumn)
{
ColumnVisibilityChangeEvent event = new ColumnVisibilityChangeEvent(this, mTable, pColumn);
// Process the listeners last to first, notifying those that are interested in this event
Object[] listeners = mListenerList.getListenerList(); // could be improved with getListeners() under 1.3
for ( int i = listeners.length-2; i>=0; i-=2 )
{
if ( listeners[i] == ColumnVisibilityChangeListener.class )
{
((ColumnVisibilityChangeListener)listeners[i+1]).columnShown(event);
}
}
}
/**
* Notify our listeners that a table column was hidden.
*
* @param pColumn column that was hidden
*/
protected void fireColumnVisibilityEventHidden(final TableColumn pColumn)
{
ColumnVisibilityChangeEvent event = new ColumnVisibilityChangeEvent(this, mTable, pColumn);
// Process the listeners last to first, notifying those that are interested in this event
Object[] listeners = mListenerList.getListenerList(); // could be improved with getListeners() under 1.3
for ( int i = listeners.length-2; i>=0; i-=2 )
{
if ( listeners[i] == ColumnVisibilityChangeListener.class )
{
((ColumnVisibilityChangeListener)listeners[i+1]).columnHidden(event);
}
}
}
}