|
|
Entire Source Listing of the TableTest Application
The previous portion of this section presented the functional components of the TableTest application. For completeness, the entire source listing is given in Listing 12.10. This includes the renderers, editors, and the data model. The code is contained in a single source file. To allow the renderers or editors to be used in multiple tables, they need to be moved into their own source file.
Listing 12.10 The TableTest Application
package com.foley.test;
import java.awt.*;
import java.io.Serializable;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import com.foley.utility.ApplicationFrame;
/**
* An application that displays a JTable instance
* in its frame.
*
* @author Mike Foley
**/
public class TableTest extends Object {
/**
* Application entry point.
* Create a frame and the table and display them.
*
* @param args Command line parameter. Not used.
**/
public static void main( String args[] ) {
JFrame frame = new ApplicationFrame( TableTest );
JTable table = new JTable( new TableTestModel() );
//
// Create and register a Color class renderer.
//
DefaultTableCellRenderer colorRenderer =
new DefaultTableCellRenderer() {
public void setValue( Object value ) {
setBackground( ( Color )value );
}
};
colorRenderer.setOpaque( true );
table.setDefaultRenderer( Color.class, colorRenderer );
//
// Create a combo box with colors available in the table.
// The combo box will be used for the Color cell editor.
// The combo box itself has a custom list renderer.
//
JComboBox comboBox = new JComboBox();
comboBox.addItem( Color.black );
comboBox.addItem( Color.gray );
comboBox.addItem( Color.red );
comboBox.addItem( Color.pink );
comboBox.addItem( Color.orange );
comboBox.addItem( Color.yellow );
comboBox.addItem( Color.green );
comboBox.addItem( Color.blue );
comboBox.addItem( Color.cyan );
comboBox.addItem( Color.white );
comboBox.addItem( Color.lightGray );
comboBox.setRenderer( new ColorCellRenderer() );
table.setDefaultEditor( Color.class,
new DefaultCellEditor( comboBox ) );
table.setDefaultRenderer( Boolean.class,
new SelectedCheckBoxRenderer() );
//
// Add an editor for the Number class that aborts the edit
// without changing the data model when the escape key is
// entered during the edit.
//
JTextField textField = new JTextField();
final DefaultCellEditor cancelCellEditor =
new DefaultCellEditor( textField );
textField.setHorizontalAlignment( SwingConstants.RIGHT );
textField.addKeyListener( new KeyAdapter() {
public void keyReleased( KeyEvent event ) {
if( event.getKeyCode() == KeyEvent.VK_ESCAPE )
cancelCellEditor.cancelCellEditing();
}
} );
table.setDefaultEditor( Number.class, cancelCellEditor );
//
// A single multiline renderer may be shared between all
// columns in the table. Set the renderer for each column.
//
MutliLineHeaderRenderer headerRenderer =
new MutliLineHeaderRenderer();
TableColumnModel columnModel = table.getColumnModel();
for( Enumeration e = columnModel.getColumns();
e.hasMoreElements(); ) {
TableColumn column = ( TableColumn )e.nextElement();
column.setHeaderRenderer( headerRenderer );
}
//
// Only allow row selection.
//
table.setRowSelectionAllowed( true );
table.setColumnSelectionAllowed( false );
//
// Add the table to a scroll pane and add to the content
// pane of the frame.
//
frame.getContentPane().add( new JScrollPane( table ),
BorderLayout.CENTER );
frame.pack();
frame.setVisible( true );
} // main
} // TableTest
/**
* An example of a multiline table header.
* To be used effectively, known header value types
* would need to be specified in an interface that
* could be tested for here. This would allow useful
* data to be queried for the additional rows in the header.
* This could be done by a specialized table model that
* returns column headers with breaks in the returned
* String from the getColumnName method that could be
* interpreted here.
**/
class MutliLineHeaderRenderer extends JTextArea
implements TableCellRenderer, Serializable {
/**
* MutliLineHeaderRenderer, default Constructor.
* Set the number of rows to two. For a production
* header renderer, the number of rows would probably
* be determined dynamically depending on the header value.
**/
public MutliLineHeaderRenderer() {
super( 2, 20 );
setOpaque( true );
}
/**
* Configure in the standard table header colors and border.
* The multiline string is a bit contrived here. Would need
* to get a real multiline header from the table model, or
* values of known types, i.e. interfaces.
**/
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
if (table != null) {
JTableHeader header = table.getTableHeader();
if (header != null) {
setForeground(header.getForeground());
setBackground(header.getBackground());
setFont(header.getFont());
}
}
//
// Add a second line that is the column number.
//
setText( (value == null) ? Column + column :
Column + column + \n + value.toString() );
setBorder( UIManager.getBorder(TableHeader.cellBorder) );
return this;
}
}
/**
* ColorCellRenderer is a rendering class that expects
* Color values. When this is the case, it paints the color
* as the background of the label. If not, show the String
* returned from the toString method of the value Object.
*
* @author Mike Foley
**/
class ColorCellRenderer extends JLabel
implements ListCellRenderer, Serializable {
Border selectedWhiteBorder;
Border selectedBlackBorder;
/**
* ColorCellRenderer, default constructor.
* We must be opaque so the background is painted.
**/
public ColorCellRenderer() {
setOpaque( true );
selectedWhiteBorder = BorderFactory.createMatteBorder(
3, 5, 3, 5, Color.white );
selectedBlackBorder = BorderFactory.createMatteBorder(
3, 5, 3, 5, Color.black );
}
/**
* Configure yourself for the state passed.
* If the value is a Color, set the background to that
* color. If not, use the toString method of the value
*
**/
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
if( value instanceof Color ) {
setText( );
setBackground( ( Color )value );
if( isSelected ) {
if( value.equals( Color.white ) )
setBorder( selectedBlackBorder );
else
setBorder( selectedWhiteBorder );
} else {
setBorder( null );
}
} else {
setText( value.toString() );
if( isSelected ) {
setBackground( list.getSelectionBackground() );
setForeground( list.getSelectionForeground() );
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
}
return this;
}
} // ColorCellRenderer
/**
* The SelectedCheckBoxRenderer class is almost the same as the
* default check box renderer in the JTable class. However, it
* paints the cell in the normal table selected colors when it is
* selected. This avoids the white areas in the table for Boolean
* columns. Also, if all columns in the table are Boolean, you
* wouldnt be able to see the selected row using the default renderer!
*
* @author Mike Foley
**/
class SelectedCheckBoxRenderer extends JCheckBox
implements TableCellRenderer, Serializable
private Border noFocusBorder;
private Color unselectedForeground;
private Color unselectedBackground;
/**
* SelectedCheckBoxRenderer, default constructor.
*
* We need to be opaque so our background color is painted.
* Create our border. Keep a reference around so it can be
* reset when we are not selected.
* Center the check box to match the position of the Boolean editor.
**/
public SelectedCheckBoxRenderer() {
super();
setOpaque( true );
noFocusBorder = BorderFactory.createEmptyBorder( 1, 2, 1, 2 );
setBorder( noFocusBorder );
setHorizontalAlignment( JLabel.CENTER );
}
/**
* Set the foreground color. Remember the color, so
* we can reset it when we are not selected.
*
* @param c The new foreground color.
**/
public void setForeground(Color c) {
super.setForeground(c);
unselectedForeground = c;
}
/**
* Set the background color. Remember the color, so
* we can reset it when we are not selected.
*
* @param c The new background color.
**/
public void setBackground(Color c) {
super.setBackground(c);
unselectedBackground = c;
}
/**
* Clear the foreground and background colors after
* updating the UI. This will cause the colors to be
* read from the table property portion of the UI.
**/
public void updateUI() {
super.updateUI();
setForeground( null );
setBackground( null );
}
/**
* getTableCellRendererComponent, from TableCellRenderer
*
* Configure the check box for the given state.
*
**/
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
//
// Check or uncheck the JCheckBox.
//
setSelected((value != null && ((Boolean)value).booleanValue()));
//
// If we are selected, paint in the tables selection colors.
//
if( isSelected ) {
super.setForeground( table.getSelectionForeground() );
super.setBackground( table.getSelectionBackground() );
} else {
super.setForeground( ( unselectedForeground != null ) ?
unselectedForeground : table.getForeground()
);
super.setBackground( ( unselectedBackground != null ) ?
unselectedBackground
unselectedBackground :
table.getBackground() );
} // else
//
// If we have the focus, paint in the focus color for the table.
// and set the focus border.
// If not, set the no focus border.
//
if( hasFocus ) {
setBorder( UIManager.getBorder(
Table.focusCellHighlightBorder ) );
if (table.isCellEditable(row, column)) {
super.setForeground( UIManager.getColor(
Table.focusCellForeground ) );
super.setBackground( UIManager.getColor(
Table.focusCellBackground ) );
}
} else {
setBorder( noFocusBorder );
}
return( this );
}
} // SelectedCheckBoxRenderer
/**
* Table model that returns the Class of the data in
* a particular column. This allows the table to set
* renderers for the data.
**/
class TableTestModel extends AbstractTableModel
implements Serializable {
/**
* The data in the model.
**/
Object[][] data = {
{ Mike, Color.blue, new Boolean( true ), new Integer(9) },
{ Mari, Color.red, new Boolean( true ), new Integer(6) },
{ Molly, Color.yellow, new Boolean( false ), new Integer(1) },
{ Betsy, Color.orange, new Boolean( false ), new Integer(8) },
{ Bobby, Color.lightGray, new Boolean( true ), new Integer(7) }
};
/**
* The column names for the model.
**/
String[] columnNames = { Name\nFirst, Color, Enabled, Size };
/**
* TableTestModel, constructor.
**/
TableTestModel() {
super();
}
/**
* getRowCount, from TableModel.
*
* @return The number of rows in the data.
**/
public int getRowCount() {
return( data.length );
}
/**
* getColumnCount, from TableModel.
*
* @return The number of columns in the data.
**/
public int getColumnCount() {
return( data[0].length );
}
/**
* getValueAt, from TableModel.
*
* @param row The row of the requested data object.
* @param column The column of the requested data object.
* @return The object at the specified cell.
**/
public Object getValueAt( int row, int column ) {
return( data[ row ][ column ] );
}
/**
* setValueAt, from TableModel.
*
* Set the data value in the given location to the
* given object.
*
* @param value The new data object.
* @param row The row of the new data object.
* @param column The column of the new data object.
**/
public void setValueAt( Object value, int row, int column ) {
if( column == 0 )
throw new RuntimeException(
The Name column is not editable );
data[ row ][ column ] = value;
fireTableCellUpdated( row, column );
}
/**
* getColumnName, from TableModel.
*
* @param column The column index whose name is desired.
* @return The name of the column with the given index.
**/
public String getColumnName( int column ) {
return( columnNames[ column ] );
}
/**
* getColumnClass, from TableModel.
*
* @param column The column index whose Class is desired.
* @return The Class of the data for the column with the given index.
**/
public Class getColumnClass( int column ) {
return( data[0][ column ].getClass() );
}
/**
* isCellEditable, from TableModel.
*
* All columns except the first are editable in this model.
*
* @param row The row of the cell whose editability is requested.
* @param column The column of the cell whose editability is
* requested.
* @return true if the cell is editable, false otherwise.
**/
public boolean isCellEditable( int row, int column ) {
if( column == 0 ) {
return( false );
} else {
return( true );
}
}
}
|