All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.starlink.table.gui.TableRowHeader Maven / Gradle / Ivy

There is a newer version: 4.3
Show newest version
package uk.ac.starlink.table.gui;

import java.awt.Dimension;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

/**
 * Provides a component suitable for use as a rowHeader component in
 * the same JScrollPane as is being used to house a 
 * JTable.  It displays the row indices starting at 1 and increasing.
 * If you want some other number to be displayed, override the
 * {@link #rowNumber} method.
 * 

* You would normally use this class as follows: *

 *     JTable jtab = ...
 *     JScrollPane scrollpane = new JScrollPane( jtab );
 *     scrollpane.setRowHeaderView( new TableRowHeader( jtab ) );
 * 
* This header will register itself as a listener on the master table's * model so that it can respond to changes. * In the event that the master JTable's model changes during the lifetime of * this header table, then {@link #modelChanged} should be called. * At construction time the master table's selection model will be * installed in this header too, so that you can make row selections by * clicking on the header or the master. * * @author Mark Taylor (Starlink) * @see javax.swing.JScrollPane */ public class TableRowHeader extends JTable { private JTable masterTable; private AbstractTableModel rowModel; private TableModel masterModel; private TableModelListener listener; /** * Construct a new TableRowHeader. */ public TableRowHeader( JTable table ) { this.masterTable = table; this.masterModel = masterTable.getModel(); /* Set the model. */ rowModel = new AbstractTableModel() { public int getRowCount() { return masterTable.getRowCount(); } public int getColumnCount() { return 1; } public Object getValueAt( int irow, int icol ) { return new Long( rowNumber( irow ) ) + " "; } }; setModel( rowModel ); /* Use the same selection model as the master table, so that you * can select rows by clicking on the row header or the master. */ setSelectionModel( masterTable.getSelectionModel() ); /* Set up a listener on the master table which will trigger change * events on this table when the master model changes. */ listener = new TableModelListener() { public void tableChanged( TableModelEvent evt ) { /* Probably one could be more conservative about how the * TableModelEvents are propagated here, since some of them * will not affect the content of the row model. * However, TableModelEvent seems to be a bit underdocumented, * so play it safe for now. I don't *think* this is causing * performance problems anywhere. */ rowModel.fireTableDataChanged(); } }; masterModel.addTableModelListener( listener ); /* Configure to be uninteresting as a JTable. */ setTableHeader( null ); setAutoResizeMode( AUTO_RESIZE_OFF ); setPreferredScrollableViewportSize( masterTable.getPreferredSize() ); setColumnSelectionAllowed( false ); setRowSelectionAllowed( false ); /* Create a suitable renderer. */ DefaultTableCellRenderer rend = (DefaultTableCellRenderer) new JTableHeader().getDefaultRenderer(); rend.setFont( UIManager.getFont( "TableHeader.font" ) ); rend.setBackground( UIManager.getColor( "TableHeader.background" ) ); rend.setForeground( UIManager.getColor( "TableHeader.foreground" ) ); rend.setHorizontalAlignment( SwingConstants.RIGHT ); /* Set up the sole column. */ TableColumn col = new TableColumn( 0, 64, rend, null ) { Integer prefWidth_; public void setPreferredWidth( int width ) { prefWidth_ = new Integer( width ); } public int getPreferredWidth() { if ( prefWidth_ != null ) { return prefWidth_.intValue(); } JTable tab = TableRowHeader.this; int nrow = masterTable.getRowCount(); if ( nrow > 0 ) { int first = StarJTable.getCellWidth( tab, 0, 0 ); int last = StarJTable.getCellWidth( tab, nrow - 1, 0); int guess = tab.getCellRenderer( 0, 0 ) .getTableCellRendererComponent( tab, new Long( nrow + 1 ) + " ", false, false, 0, 0 ) .getPreferredSize().width; return 8 + Math.max( Math.max( first, last ), guess ); } else { return 64; // no rows - make something up } } }; /* Configure it into the column model. */ TableColumnModel tcm = new DefaultTableColumnModel(); tcm.addColumn( col ); setColumnModel( tcm ); } /** * Sets the longest value that will be used in the row header field. * This is used to configure the size of the column in pixels, * so the value used should be the one that generates the longest text; * typically the largest number, but for instance "-1" is longer than "1". * *

If no value is set, a guess will be made based on the number of rows. * * @param num longest number */ public void setLongestNumber( long num ) { int width = getCellRenderer( 0, 0 ) .getTableCellRendererComponent( this, new Long( num ) + " ", false, false, 0, 0 ) .getPreferredSize().width + 8; getColumnModel().getColumn( 0 ).setPreferredWidth( width ); } public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } /** * This method should be called to notify this header that the * master table's TableModel has been changed. */ public void modelChanged() { TableModel mmodel = masterTable.getModel(); if ( mmodel != masterModel ) { masterModel.removeTableModelListener( listener ); masterModel = mmodel; masterModel.addTableModelListener( listener ); } rowModel.fireTableDataChanged(); } /** * Determines the numeric index to be displayed for a given row * number into the table. The default implementation returns * irow+1 so that the first row is labelled 1, the second * one 2 etc, but this method may be overridden for more specialised * behaviour. * * @param irow the row index of the displayed row (starts at zero) * @return the number of the row it should be labelled */ public long rowNumber( int irow ) { return irow + 1; } /** * Installs this row header on a scroll pane. * * @param scroller scroll pane for which this component will be the * row header */ public void installOnScroller( final JScrollPane scroller ) { /* Do the standard installation. */ scroller.setRowHeaderView( this ); /* Do additional work to ensure that when the row header scrolls, * the main table will scroll as well. This really ought to be * taken care of automatically (the opposite job is), but it is * not - see the java bug database bug ID #4242632. */ final JViewport rhView = scroller.getRowHeader(); rhView.addChangeListener( new ChangeListener() { public void stateChanged( ChangeEvent evt ) { int iExtent = rhView.getExtentSize().height; int iMax = rhView.getViewSize().height; int iPos = rhView.getViewPosition().y; int iValue = Math.max( 0, Math.min( iPos, iMax - iExtent ) ); scroller.getVerticalScrollBar() .setValues( iValue, iExtent, 0, iMax ); } } ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy