JTable bug?

My program has a JTable in a TabbedPane, a menu bar, and a toolbar with icons representing menu bar shortcuts.
One menu/toolbar item is to clear all cells. When this is selected, my program first stops editing on the current cell, so that any cell currently being edited gets cleared too. I do it this way:
int row = myJTable.getEditingRow();
int col = myJTable.getEditingColumn();
if (row != -1 && col != -1)
myJTable.getCellEditor(row,col).stopCellEditing();
I then clear all table cells. There are similar menu/toolbar items for setting all cells to 0, filling them with random values, etc.
The problem is this: When I use the a Menu item to change table cells, the next cell I click in will not allow me to edit it! It will become selected, and have a blinking cursor, and look exactly like a cell normally looks when you can edit it, but when you type, nothing happens! I instead have to click in a different cell to be able to edit again. This happens every time I use a menu item to change all table cells.
HOWEVER, if I use a toolbar button to change all table cells, this does NOT happen! I'm baffled... Both the menu listener and toolbar listener call the exact same method to change all table cells. The code is identical.
Any ideas? If no one has any ideas, I can try to put together a small program to demonstrate the problem, but figured I would see if anyone has thoughts before taking the time to do that.
-Vern

Okay, here is source code. Sorry it's so long -- I tried to trim it down to the bare essentials necessary to reproduce the problem. [On a side note: anyone know how to post source code wihout all leading tabs/spaces getting trimmed?]
The problem may have something to do with my funky table cell editor -- MyCellEditor. I did not write this editor -- it was part of an existing project I have been assigned to modify. But if you remove the editor, it works correctly. I'm not sure if it's this particular editor causing the problem somehow, or just the fact that a custom editor is being used that helps trigger the problem.
In any case, run this program, type a number into a field, then choose "Clear table" from the menu. Then click once in a new field, and type in a number. It won't! Click in another field, and you can type again. Any ideas about this will be greatly appreciated!
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
import java.awt.event.*;
import java.awt.*;
public class JTableBugDemo
     protected JMenuItem clearMenuItem;
     protected MyTableModel tableModel;
     protected JTable table;
     public JTableBugDemo()
          JFrame frame = new JFrame("JTable Bug Demo");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          JMenuBar menuBar = new JMenuBar();
          JMenu fileMenu = new JMenu();
          clearMenuItem = new JMenuItem();
          fileMenu.setText( "File" );
          clearMenuItem.setText( "Clear table" );
          fileMenu.add(clearMenuItem);
          menuBar.add(fileMenu);
          frame.setJMenuBar(menuBar);
          clearMenuItem.addActionListener(new MyActionListener());
          JPanel newContentPane = createTable();
          newContentPane.setOpaque(true);
          frame.setContentPane(newContentPane);
          frame.pack();
          frame.setVisible(true);
     public JPanel createTable()
          JPanel myPanel = new JPanel( new GridLayout(1,0) );
          tableModel = new MyTableModel(5,5);
          table = new JTable();
          table.setPreferredScrollableViewportSize(new Dimension(500, 150) );
          table.setDefaultEditor(String.class, new MyCellEditor());
          table.setModel( tableModel );
          JScrollPane scrollPane = new JScrollPane(table);
          myPanel.add(scrollPane);
          return myPanel;
     public void finishTablesEditing()
          int row = table.getEditingRow();
          int col = table.getEditingColumn();
          if (row != -1 && col != -1) // if anything is being editing
               table.getCellEditor(row,col).stopCellEditing();
     public static void main(String[] args)
          new JTableBugDemo();
     class MyActionListener implements ActionListener
public void actionPerformed( ActionEvent e )
               if ( e.getSource().equals( clearMenuItem ) )
                    finishTablesEditing();
                    clearTable( );
                    return;
          // Put empty string in all table cells
     public void clearTable()
          for (int row = 0; row < tableModel.getRowCount(); row++)
               for (int col = 0; col < tableModel.getColumnCount(); col++)
                    tableModel.setValueAt("", row, col);
     class MyTableModel extends AbstractTableModel
          protected Object[][] data;
          public MyTableModel(int numRows, int numCols)
               data = new Object[numRows][numCols];
               for (int row = 0; row < numRows; row++)
                    for (int col = 0; col < numCols; col++)
                         data[row][col] = "" + (row * col);
          public int getColumnCount()
               return data[0].length;
          public int getRowCount()
               return data.length;
          public Object getValueAt(int row, int col)
               return data[row][col];
          public void setValueAt(Object value, int row, int col)
               data[row][col] = (Object)value;
               fireTableCellUpdated(row, col);
          public boolean isCellEditable(int row, int col)
               return true;
          public Class getColumnClass(int col)
               return getValueAt(0, col).getClass();
     class MyCellEditor extends DefaultCellEditor
          public MyCellEditor()
               super( new MyRealNumberOrBlankField(5) );
               setClickCountToStart(1);
     class MyRealNumberOrBlankField extends JTextField
          public MyRealNumberOrBlankField(int columns)
               super(columns);
          protected Document createDefaultModel()
               return new MyRealNumberOrBlankDocument();
          protected class MyRealNumberOrBlankDocument extends PlainDocument
               public void insertString(int offs, String str, AttributeSet a) throws BadLocationException
                    if(str == "")
                         super.insertString(offs, str, a);
                         return;
                    char[] source = str.toCharArray();
                    char[] result = new char[source.length];
                    int j = 0;
                    for (int i = 0; i < result.length; i++)
                         if (Character.isDigit(source) || source[i] == '.' || source[i] == 'E' || source[i] == '-' )
                              result[j++] = source[i];
                    super.insertString(offs, new String(result, 0, j), a);

Similar Messages

  • Weird jtable bug

    So I tried to add a scrollpane to my JTextArea when I edited a certain box. That turned out to be a bad idea, so I put everything back where it went before, but now it won't edit the cells. I even used svn to get the exact code that was working before and it isn't working.
    class StringEditor extends AbstractCellEditor implements TableCellEditor{
        JComponent component= new JTextArea();
        public Object getCellEditorValue() {
            return ((JTextArea)component).getText();
        public Component getTableCellEditorComponent(JTable jtable, Object value,
                boolean isSelected, int row, int col) {
            if(isSelected){
                //action when selected
            ((JTextArea)component).setText((String)value);
            return component;
    }Edited by: user958508 on Mar 8, 2011 5:37 PM

    user958508 wrote:
    So I tried to add a scrollpane to my JTextArea when I edited a certain box. That turned out to be a bad idea, so I put everything back where it went before, but now it won't edit the cells. I even used svn to get the exact code that was working before and it isn't working.That's not possible. Make sure your whole working copy (not just the class where you think the problem is) is reverted to the revision that used to work.
    >
    class StringEditor extends AbstractCellEditor implements TableCellEditor{...Nothing obvious, but it's hard to infer anything from such a partial extract. For better help sooner, please post an SSCCE instead(http://sscce.org).
    Well, at least, thanks for using tags properly to provide readable code (you'd be surprised by the number of people who do not even care to try).
    Much luck,
        J.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   

  • Bug in binding a Jtable to a Temporary Vo

    I've a JClient app in vich I create a Temporary VO wich is not based upon a table.
    I try to bind it to a JTable I get an error :
    (oracle.jbo.DMLException) JBO-26044: Error while getting estimated row count for view object ProvaVo, statement SELECT count(1) FROM () ESTCOUNT.
    When I bind the same Vo to a form it works.
    If I remember right this was a bug already fixed (I'm using 9.0.4 production on Windows).
    Does anyone know how to avoid this ?
    Tks
    Tullio

    Yes using the WorkAround You suggested I don't have the JBO Error anymore.
    I didn't reproduce with the tester because the tester doesn't use JTable for a single Vo (or, at least, I didn't find the way).
    I suspect the problem is in the JTable binding because I debbugged Your code and I found a place in wich a test isVisible (applied to a rowset or to a VO, I don't remember) fails and then it "return 0".
    The flag tested by that method was setted, in my analysis, only by the execute() wich is not called for a temporary vo. (this is correct because otherwise the Vo would result empty).
    Tks
    Tullio

  • Bug when using JComboBox as JTable CellEditor

    Hello! I have a JTable that displays database information, and one of the columns is editable using a JComboBox. When the user selects an item from the JComboBox, the database (and consequently the JTable) is updated with the new value.
    Everything works fine except for a serious and subtle bug. To explain what happens, here is an example. If Row 1 has the value "ABC" and the user selects the editable column on that row, the JComboBox drops down with the existing value "ABC" already selected (this is the default behavior, not something I implemented). Now, if the user does not select a new value from the JComboBox, and instead selects the editable column on Row 2 that contains "XYZ", Row 1 gets updated to contain "XYZ"!
    The reason that is happening is because I'm updating the database by responding to the ActionListener.actionPerformed event in the JComboBox, and when a new row is selected, the actionPerformed event gets fired BEFORE the JTable's selected row index gets updated. So the old row gets updated with the new row's information, even though the user never actually selected a new item in the JComboBox.
    If I use ItemListener.itemStateChanged instead, I get the same results. If I use MouseListener.mousePressed/Released I get no events at all for the JComboBox list selection. If anyone else has encountered this problem and found a workaround, I would very much appreciate knowing what you did. Here are the relavent code snippets:
    // In the dialog containing JTable:
    JComboBox cboRouteType = new JComboBox(new String[]{"ABC", "XYZ"));
    cboRouteType.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ev) {
              doCboRouteTypeSelect((String)cboRouteType.getSelectedItem());
    private void doCboRouteTypeSelect(String selItem) {
         final RouteEntry selRoute = tblRoutes.getSelectedRoute();
         if (selRoute == null) { return; }
         RouteType newType = RouteType.fromString(selItem);
         // Only update the database if the value was actually changed.
         if (selRoute.getRouteType() == newType) { return; }
         RouteType oldType = selRoute.getRouteType();
         selRoute.setRouteType(newType);
         // (update the db below)
    // In the JTable:
    public RouteEntry getSelectedRoute() {
         int selIndx = getSelectedRow();
         if (selIndx < 0) return null;
         return model.getRouteAt(selIndx);
    // In the TableModel:
    private Vector<RouteEntry> vRoutes = new Vector<RouteEntry>();
    public RouteEntry getRouteAt(int indx) { return vRoutes.get(indx); }

    Update: I was able to resolve this issue. In case anyone is curious, the problem was caused by using the actionPerformed method in the first place. Apparently when the JComboBox is used as a table cell editor, it calls setValueAt on my table model and that's where I'm supposed to respond to the selection.
    Obviously the table itself shouldn't be responsible for database updates, so I had to create a CellEditListener interface and implement it from the class that actually does the DB update in a thread and is capable of reporting SQL exceptions to the user. All that work just to let the user update the table/database with a dropdown. Sheesh!

  • JTable Java 1.6 Bug Progress ??? Work Around ?

    I was just wondering if there was a work around for the following bug on java 1.6 ???
    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6568959
    This has been really annoying me because it's a requirement to use JInternalFrame and of course they want to treat the JTable like a spread sheet and edit cells using the keyboard and mouse. Is there some way to fix this really soon ? I was shocked why my program worked so well until I tried java 1.6 JRE and then the focus problems came.

    I was just wondering if there was a work around for the following bug on java 1.6 ???
    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6568959
    This has been really annoying me because it's a requirement to use JInternalFrame and of course they want to treat the JTable like a spread sheet and edit cells using the keyboard and mouse. Is there some way to fix this really soon ? I was shocked why my program worked so well until I tried java 1.6 JRE and then the focus problems came.

  • Is there a bug in the JTable under 1.2.2 ?

    Hi I use the VM 1.2.2.
    My JTable is disabled by:
    referencesTable.setEnabled(false);
    But this method has no effect under 1.2.
    If I run the app. under 1.4 the table is disabled.
    How can I disable a JTable under 1.2.
    Thanx
    Stephan

    Thanks M, you're right, I do have 13.0.1, it's just for some reason ive noticed some strange behavior in photoshop, can't figure out why. I still can't get my panels to collapse when double clicking the bar and the curves adjustment layer doesn't allow me to place a point using the eyedropper tool when clicking on the image, this has just recently started after reinstalling photoshop on my imac using the creative cloud application manager

  • OLD BUG is back: Heavy JClient Bug regarding to scrolling within a JTable.....

    Hi,
    I'm just evaluating the new features of JDev 9.0.3.988 (preview) and found an old bug back again!
    see: Re: Fetching data from database for login
    The current behaviour is slightly different:
    You have a generated JClient form displaying a table. There are more rows than the table can show at once. You scroll down with the cursor keys until some new rows are fetched. Now scroll up again. Make sure you only use cursor keys. If you reach the upper area of the table you can see that the StatusBar isn't refreshing it's current row counter and all possibly connected fields to the same binding don't update themself either.
    You can see this behavior by using only wizard generated source code based on the SCOTT/TIGER schema.
    This bug should have been definitively fixed in a previous beta version...
    Can anyone give a comment on this?
    Thanks in advance,
    Torsten.

    This bugfix doesn't work!!!!!!
    It makes the table scrollable via cursor keys, but now scrolling with the mouse isn't possible anymore!!!!I see the bug when you apply the above mentioned workaround that leads to scrolling due to double handlers for valueChanged event
    Lets try to fix this in the JUTableBinding source. From my test runs on the when the same fix is applied on JUTableBinding source, the scroll works fine in both key/mouse modes.
    Let us know if you find otherwise.
    The fix in JUTableBinding is:
    Look up source for JUTableBinding in your setup. (It'd be in (JDEVHOME)\bc4j\src\bc4juisrc.zip
    Save a copy in your current project in the project's source path maintaining the class's package structure - save the file into sourcepath\oracle\jbo\uicli\jui\JUTableBinding.java
    Replace the existing valueChanged method with the following:
          public void valueChanged(ListSelectionEvent event)
             try
                ListSelectionModel listSelModel = (ListSelectionModel) event.getSource();
                if (mSelRowInProg || listSelModel.getValueIsAdjusting())
                   return;
                RowIterator iter = getRowIterator();
                if (!listSelModel.isSelectionEmpty())
                   if (iter == null)
                      return;
                   int selectTabRowIndex = listSelModel.getMinSelectionIndex();
                   if (getFormBinding() != null)
                      ((JUPanelBinding)getFormBinding()).callBeforeRowNavigated(getIteratorBinding());
                   int idx = selectTabRowIndex - iter.getRangeStart();
                   if (idx >= iter.getRangeSize())
                      idx = idx - iter.getRangeSize() + 1;
                      iter.scrollRange(idx);
                      idx = selectTabRowIndex - iter.getRangeStart();
                   else if (idx < 0)
                      iter.scrollRange(idx);
                      idx = 0;
                   iter.setCurrentRowAtRangeIndex(idx);
             catch(Exception ex)
                reportException(ex);
                refresh(true /*selectRow*/);
    This is a really heavy bug making the whole JUTableBinding useless... any comments on this? :((

  • Problem printing the JTable

    Hi all,
    I have a problem with printing JTable.
    My application has a submit button,JTable and Print Button.
    As I click on the Submit button, the data is retrieved from the Database(MS Access) and displayed on the JTable.
    Now when I click on the Print button, the printer properties dialog box is displayed. But when I click on the print button in the dialog box, nothing is printed on the paper.
    I checked the printers and faxes in the control panel, It showed Java printing under the Document name and Printing under the Status. It is displayed for sometime and then disappeared after some time. But nothing is printed on the paper(not even the blank paper).
    I tried a lot but couldn't understand the problem.
    I have used the following files:
    PrintJTable.java
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.print.*;
    import java.awt.geom.*;
    import java.awt.Dimension;
    import java.applet.*;
    import java.sql.*;
    import java.util.*;
    import java.net.*;
    import java.lang.*;
    import javax.swing.table.*;
    import javax.swing.event.*;
    import javax.swing.border.*;
    class PrintJTable implements ActionListener,Printable
    Connection connect;
    ResultSet rs;
    JTable table;
    JScrollPane tableAggregate;
    DisplayTable displayTable;
    JButton print,submitButton;
    public PrintJTable()
    JFrame frame = new JFrame("Table");
    frame.addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {System.exit(0);}});
    connect();
    displayTable= new DisplayTable();
    JButton submitButton= new JButton("SUBMIT");
    submitButton.addActionListener(this);
    JButton printButton= new JButton("PRINT!");
    // for faster printing turn double buffering off
    RepaintManager.currentManager( frame).setDoubleBufferingEnabled(false);
    printButton.addActionListener( new ActionListener(){
    public void actionPerformed(ActionEvent evt) {
    PrinterJob pj=PrinterJob.getPrinterJob();
    pj.setPrintable(PrintJTable.this);
    pj.printDialog();
    try{
    pj.print();
    }catch (Exception PrintException) {}
    tableAggregate = createTable();
    tableAggregate.setBorder(new BevelBorder(BevelBorder.LOWERED));
    frame.getContentPane().setLayout(new BorderLayout());
    frame.getContentPane().add(submitButton,BorderLayout.NORTH);
    frame.getContentPane().add(tableAggregate,BorderLayout.CENTER);
    frame.getContentPane().add(printButton,BorderLayout.SOUTH);
    frame.pack();
    frame.setVisible(true);
    } // end of constructor
    public void connect()
    try {
    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    System.out.println("Opening db connection");
    connect = DriverManager.getConnection("jdbc:odbc:Driver={MicroSoft Access Driver (*.mdb)};DBQ=C:/db1.mdb","","");
    catch (ClassNotFoundException ex) {
    System.err.println("Cannot find the database driver classes.");
    System.err.println(ex);
    catch (SQLException ex) {
    System.err.println("Cannot connect to this database.");
    System.err.println(ex);
    public JScrollPane createTable() {
    displayTable= new DisplayTable();
    JTable table = new JTable(displayTable);
         table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
         table.getTableHeader().setReorderingAllowed(false);
    JScrollPane scrollpane = new JScrollPane(table,
    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
    JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    return scrollpane;
    } // end of createTable()
    public void actionPerformed(ActionEvent ie)
    try
    Statement statement6=connect.createStatement();
    ResultSet rs6=statement6.executeQuery("select * from benf_details");
    displayTable.executeQuery(rs6);
    statement6.close();
    catch(SQLException sqle)
    JOptionPane.showMessageDialog(null,"error2"+sqle);
    }// end of actionPerformed
    public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
    Graphics2D g2 = (Graphics2D)g;
    g2.setColor(Color.black);
    int fontHeight=g2.getFontMetrics().getHeight();
    int fontDesent=g2.getFontMetrics().getDescent();
    double pageHeight = pageFormat.getImageableHeight()-fontHeight; //leave room for page number
    double pageWidth = pageFormat.getImageableWidth();
    System.out.println("page width = " + pageWidth );
    double tableWidth = (double) table.getColumnModel().getTotalColumnWidth();
    System.out.println("table width = " + tableWidth );
    double scale = 1;
    if (tableWidth >= pageWidth) {
    scale = pageWidth / tableWidth;
    System.out.println("scale = " + scale );
    double headerHeightOnPage = table.getTableHeader().getHeight() * scale;
    double tableWidthOnPage = tableWidth * scale;
    double oneRowHeight = (table.getRowHeight() + table.getRowMargin()) * scale;
    int numRowsOnAPage = (int)((pageHeight - headerHeightOnPage) / oneRowHeight);
    double pageHeightForTable = oneRowHeight * numRowsOnAPage;
    int totalNumPages = (int)Math.ceil(((double)table.getRowCount())/numRowsOnAPage);
    if(pageIndex >= totalNumPages) {
    return NO_SUCH_PAGE;
    g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
    g2.drawString("Page: "+ (pageIndex + 1),(int)pageWidth / 2 - 35,
    (int)( pageHeight + fontHeight - fontDesent ));//bottom center
    g2.translate( 0f, headerHeightOnPage );
    g2.translate( 0f, -pageIndex * pageHeightForTable );
    //If this piece of the table is smaller than the size available,
    //clip to the appropriate bounds.
    if (pageIndex + 1 == totalNumPages) {
    int lastRowPrinted = numRowsOnAPage * pageIndex;
    int numRowsLeft = table.getRowCount() - lastRowPrinted;
    g2.setClip(0, (int)(pageHeightForTable * pageIndex),
    (int) Math.ceil(tableWidthOnPage),
    (int) Math.ceil(oneRowHeight * numRowsLeft));
    //else clip to the entire area available.
    else{
    g2.setClip(0, (int)(pageHeightForTable * pageIndex),
    (int) Math.ceil(tableWidthOnPage),
    (int) Math.ceil(pageHeightForTable));
    g2.scale(scale,scale);
    table.paint(g2);
    g2.scale(1/scale,1/scale);
    g2.translate( 0f, pageIndex * pageHeightForTable);
    g2.translate( 0f, -headerHeightOnPage);
    g2.setClip(0, 0,(int) Math.ceil(tableWidthOnPage), (int)Math.ceil(headerHeightOnPage));
    g2.scale(scale,scale);
    table.getTableHeader().paint(g2);//paint header at top
    return Printable.PAGE_EXISTS;
    } // end of print()
    public static void main(String[] args) {
    new PrintJTable();
    }// end of PrintJTable
    DisplayTable.java
    import java.util.Vector;
    import java.sql.*;
    import javax.swing.*;
    import javax.swing.table.AbstractTableModel;
    import javax.swing.event.TableModelEvent;
    public class DisplayTable extends AbstractTableModel {
    Connection connection;
    Statement statement;
    ResultSet resultSet;
    String[] columnNames = {};
    Vector          rows = new Vector();
    ResultSetMetaData metaData;
    String db_uname,db_passwd;
    public DisplayTable() {
    public void executeQuery(ResultSet resultSet) {
    try {
    metaData = resultSet.getMetaData();
    int numberOfColumns = metaData.getColumnCount();
    columnNames = new String[numberOfColumns];
    // Get the column names and cache them.
    // Then we can close the connection.
    for(int column = 0; column < numberOfColumns; column++) {
    columnNames[column] = metaData.getColumnLabel(column+1);
    // Get all rows.
    rows = new Vector();
    while (resultSet.next()) {
    Vector newRow = new Vector();
    for (int i = 1; i <= getColumnCount(); i++) {
         newRow.addElement(resultSet.getObject(i));
    rows.addElement(newRow);
    // close(); Need to copy the metaData, bug in jdbc:odbc driver.
    fireTableChanged(null); // Tell the listeners a new table has arrived.
    catch (SQLException ex) {
    System.err.println(ex);
    public void close() throws SQLException {
    System.out.println("Closing db connection");
    resultSet.close();
    statement.close();
    connection.close();
    protected void finalize() throws Throwable {
    close();
    super.finalize();
    // Implementation of the TableModel Interface
    // MetaData
    public String getColumnName(int column) {
    if (columnNames[column] != null) {
    return columnNames[column];
    } else {
    return "";
    public Class getColumnClass(int column) {
    int type;
    try {
    type = metaData.getColumnType(column+1);
    catch (SQLException e) {
    return super.getColumnClass(column);
    switch(type) {
    case Types.CHAR:
    case Types.VARCHAR:
    case Types.LONGVARCHAR:
    return String.class;
    case Types.BIT:
    return Boolean.class;
    case Types.TINYINT:
    case Types.SMALLINT:
    case Types.INTEGER:
    return Integer.class;
    case Types.BIGINT:
    return Long.class;
    case Types.FLOAT:
    case Types.DOUBLE:
    return Double.class;
    case Types.DATE:
    return java.sql.Date.class;
    default:
    return Object.class;
    // to make the cells editable
    public boolean isCellEditable(int row, int column) {
    try {
    return metaData.isWritable(column+1);
    catch (SQLException e) {
    return false;
    public int getColumnCount() {
    return columnNames.length;
    // Data methods
    public int getRowCount() {
    return rows.size();
    public Object getValueAt(int aRow, int aColumn) {
    Vector row = (Vector)rows.elementAt(aRow);
    return row.elementAt(aColumn);
    public String dbRepresentation(int column, Object value) {
    int type;
    if (value == null) {
    return "null";
    try {
    type = metaData.getColumnType(column+1);
    catch (SQLException e) {
    return value.toString();
    switch(type) {
    case Types.INTEGER:
    case Types.DOUBLE:
    case Types.FLOAT:
    return value.toString();
    case Types.BIT:
    return ((Boolean)value).booleanValue() ? "1" : "0";
    case Types.DATE:
    return value.toString(); // This will need some conversion.
    default:
    return "\""+value.toString()+"\"";
    public void setValueAt(Object value, int row, int column) {
    try {
    String tableName = metaData.getTableName(column+1);
    // Some of the drivers seem buggy, tableName should not be null.
    if (tableName == null) {
    System.out.println("Table name returned null.");
    String columnName = getColumnName(column);
    String query =
    "update "+tableName+
    " set "+columnName+" = "+dbRepresentation(column, value)+
    " where ";
    // We don't have a model of the schema so we don't know the
    // primary keys or which columns to lock on. To demonstrate
    // that editing is possible, we'll just lock on everything.
    for(int col = 0; col<getColumnCount(); col++) {
    String colName = getColumnName(col);
    if (colName.equals("")) {
    continue;
    if (col != 0) {
    query = query + " and ";
    query = query + colName +" = "+
    dbRepresentation(col, getValueAt(row, col));
    System.out.println(query);
    System.out.println("Not sending update to database");
    // statement.executeQuery(query);
    catch (SQLException e) {
    // e.printStackTrace();
    System.err.println("Update failed");
    Vector dataRow = (Vector)rows.elementAt(row);
    dataRow.setElementAt(value, column);
    }

    Java 1.5 incorporates a very simple way to print from a JTable. I am using a mysql DB but it is the same concept. Review my code and let me know if you have any questions.
    package emsmain;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.*;
    import java.sql.*;
    import java.text.MessageFormat;
    import java.util.*;
    import javax.print.*;
    import javax.print.attribute.HashPrintRequestAttributeSet;
    import javax.print.attribute.PrintRequestAttributeSet;
    import javax.swing.*;
    import javax.swing.JTable;
    import javax.swing.table.*;
    import java.awt.print.*;
    public class TableFromDatabase extends JFrame implements ActionListener{
        JButton jbtprint = new JButton("Print Report");
        JTable Reporttable;
        Connection conn = Connect_Database.getConnection();
         public TableFromDatabase()
    Vector columnNames = new Vector();
    Vector data = new Vector();     
              try{  
                String query = null;
                Statement ps = null;
                ResultSet rs = null;
                  //Class Master List
                  if (Report_Menu.jrbMaster_list.isSelected()){
                      query =("Select * from students") ;
                  //Classes taken by student
                  if (Report_Menu.jrbClass_taken.isSelected()){
                      String taken = Report_Menu.jtfStudent.getText();
                      query = ("select program.course_num, course_name, course_start_date, course_stat_desc from registration, program where student_id = '"+taken+"' and program.course_num = registration.course_num");
                  //Birthday report
                  if (Report_Menu.jrbBirthday.isSelected()){
                      String birthday = (String) Report_Menu.jcb_birthday.getSelectedItem();
                      query = ("SELECT student_id, fname, lname, address, city, state_name, zipcode, dob FROM students where substring(dob, 6,2) = '"+birthday+"'"); 
                  //Course Catologue
                  if (Report_Menu.jrbCourseCatologue.isSelected()){
                      String course = (String) Report_Menu.jcbChooseCourse.getSelectedItem();
                      query = ("select  course_name, course_length, course_cost, course_credits from course where course_name = '"+course+"'");
                  //Certification expiration report
                  if (Report_Menu.jrbExpiration.isSelected()){
                      String month = (String) Report_Menu.jcbMonth.getSelectedItem();
                      String year = (String) Report_Menu.jcbYear.getSelectedItem();
                      query = ("SELECT FNAME, LNAME FROM STUDENTS, REGISTRATION WHERE substring(expiration_date, 6,2) = '"+month+"' and substring(expiration_date, 1,4) = '"+year+"' and registration.student_id = students.student_id") ;
                  //Class Roster
                  if (Report_Menu.jrbRoster.isSelected()){
                      String c_number = Report_Menu.jtfClassNumber.getText();
                      query = ("Select course_name, course_start_date, fname, lname from program, registration, students where program.course_num = '"+c_number+"' and registration.student_id = students.student_id;");
                  //Squad list and counts
                  if (Report_Menu.jrbSquadCount.isSelected()){
                      query = ("SELECT Squad_Name, count(student_id) from students group by Squad_Name");
                  //Student List
                  if (Report_Menu.jrbStudent_list.isSelected()){
                      String Choose_month = (String) Report_Menu.jcbcourse_month.getSelectedItem();
                      String Choose_Course = (String) Report_Menu.jcbcourse_name.getSelectedItem();
                      query = ("select count(student_id) from (registration, program) where program .course_name = '"+Choose_Course+"' and substring(course_start_date, 6,2) = '"+Choose_month+"'and registration.course_num = program.course_num;");
                ps = conn.createStatement();
                //Run Selected Report
                ps.execute(query);
                rs = ps.executeQuery(query);
                ResultSetMetaData md = rs.getMetaData();
                int columns = md.getColumnCount();
                //  Get column names
                for (int i = 1; i <= columns; i++)
                    columnNames.addElement( md.getColumnName(i) );
                //  Get row data
                while (rs.next())
                    Vector row = new Vector(columns);
                    for (int i = 1; i <= columns; i++)
                        row.addElement( rs.getObject(i) );
                    //add row data to JTable
                    data.addElement( row );
                rs.close();
                ps.close();
            catch(Exception e)
                JOptionPane.showMessageDialog(null,e.getMessage());
            //  Create Jtable with database data 
            Container c = getContentPane();
            c.setLayout(new BorderLayout());
            Reporttable = new JTable(data, columnNames);
            JScrollPane scrollPane = new JScrollPane( Reporttable );
            c.add( scrollPane );
            JPanel buttonPanel = new JPanel();
            buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
            buttonPanel.add(jbtprint);
            c.add(buttonPanel,BorderLayout.SOUTH);
            jbtprint.addActionListener(this);
        public void actionPerformed(ActionEvent e){
             if(e.getActionCommand().equals("Print Report")){
               PrintForm();
    public void PrintForm(){ 
         try {
             String name = null;
             // Will display correct heading for print job
             if (Report_Menu.jrbMaster_list.isSelected()){
             name = "Master List";
             if (Report_Menu.jrbBirthday.isSelected()){
             name = "Birthday List";
             if (Report_Menu.jrbClass_taken.isSelected()){
             name = "Classes taken by Student";
             if (Report_Menu.jrbCourseCatologue.isSelected()){
             name = "Course Catalogue";
             if (Report_Menu.jrbExpiration.isSelected()){
             name = "Certification Expiration Report";
             if (Report_Menu.jrbRoster.isSelected()){
             name = "Class Roster";
             if (Report_Menu.jrbSquadCount.isSelected()){
             name = "Squad list with Student Counts";
             if (Report_Menu.jrbStudent_list.isSelected()){
             name = "Student count by Course";
             // fetch the printable
             Printable printable = Reporttable.getPrintable(JTable.PrintMode.FIT_WIDTH,
                                                      new MessageFormat(name),
                                                      new MessageFormat("Page - {0}"));
             // fetch a PrinterJob
             PrinterJob job = PrinterJob.getPrinterJob();
             // set the Printable on the PrinterJob
             job.setPrintable(printable);
             // create an attribute set to store attributes from the print dialog
             PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
             // display a print dialog and record whether or not the user cancels it
             boolean printAccepted = job.printDialog(attr);
             // if the user didn't cancel the dialog
             if (printAccepted) {
                    try {
                          // do the printing (may need to handle PrinterException)
                        job.print(attr);
                    } catch (PrinterException ex) {
                        ex.printStackTrace();
         } finally {
             // restore the original table state here (for example, restore selection)
    }

  • Print JTable with Multiple pages and rows

    I took the printing example at http://java.sun.com/developer/onlineTraining/Programming/JDCBook/advprint.html#pe and modified it a bit to include the following:
    1) To Print Multiple pages
    2) To wrap lines that is too long for the column
    3) To print with a more proffesional style, so that it doesn't look like a screen capture is printed
    4) To align the numbers to the right and center column headings
    import javax.swing.*;
    import javax.swing.table.*;
    import java.awt.print.*;
    import java.util.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.geom.*;
    import java.awt.Dimension;
    import javax.print.*;
    import javax.print.attribute.*;
    import javax.print.attribute.standard.*;
    import java.text.*;
    public class Report implements Printable
         private final int LEFT_ALIGN = -1;
         private final int CENTER_ALIGN = 0;
         private final int RIGHT_ALIGN = 1;
         private JFrame frame;
         private JTable tableView;
         private String lastPrintDate;
         private Font defaultFont;
         private Font headerFont;
         private Font footerFont;
         private int headerHeight;
         private int footerHeight;
         private int cellBuffer = 5;
         private boolean first_pass;
         private ArrayList pages;
         public Report()
              frame = new JFrame("Sales Report");
              frame.addWindowListener(new WindowAdapter()
                   public void windowClosing(WindowEvent e)
                        System.exit(0);
              final String[] headers =
                   "ID",
                   "Description",
                   "open price",
                   "latest price",
                   "End Date",
                   "Quantity"
              int count = 0;
              final Object[][] data =
                   {new Integer(count++), "Box of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of BirosBox of Biros ppppppppppppppp", "1.00", "4.99", new Date(), new Integer(200000)},
                   {new Integer(count++), "Blue Biro", "0.10", "0.14", new Date(), new Integer(1)},
                   {new Integer(count++), "legal pad", "1.00", "2.49", new Date(), new Integer(1)},
                   {new Integer(count++), "tape", "1.00", "1.49", new Date(), new Integer(1)},
                   {new Integer(count++), "stapler", "4.00", "4.49", new Date(), new Integer(1)},
                   {new Integer(count++), "Box of Biros", "1.00", "4.99", new Date(), new Integer(2)},
                   {new Integer(count++), "Blue Biro", "0.10", "0.14", new Date(), new Integer(1)},
                   {new Integer(count++), "legal pad", "1.00", "2.49", new Date(), new Integer(1)},
                   {new Integer(count++), "tape", "1.00", "1.49", new Date(), new Integer(1)},
                   {new Integer(count++), "stapler", "4.00", "4.49", new Date(), new Integer(1)},
                   {new Integer(count++), "Box of Biros", "1.00", "4.99", new Date(), new Integer(2)},
                   {new Integer(count++), "Blue Biro", "0.10", "0.14", new Date(), new Integer(1)},
                   {new Integer(count++), "legal pad", "1.00", "2.49", new Date(), new Integer(1)},
                   {new Integer(count++), "tape", "1.00", "1.49", new Date(), new Integer(1)},
                   {new Integer(count++), "stapler", "4.00", "4.49", new Date(), new Integer(1)},
                   {new Integer(count++), "Box of Biros", "1.00", "4.99", new Date(), new Integer(2)},
                   {new Integer(count++), "Blue Biro", "0.10", "0.14", new Date(), new Integer(1)},
                   {new Integer(count++), "legal pad", "1.00", "2.49", new Date(), new Integer(1)},
                   {new Integer(count++), "tape", "1.00", "1.49", new Date(), new Integer(1)},
                   {new Integer(count++), "stapler", "4.00", "4.49", new Date(), new Integer(1)},
                   {new Integer(count++),  "Box of Biros", "1.00", "4.99", new Date(), new Integer(2)},
                   {new Integer(count++),  "Blue Biro", "0.10", "0.14", new Date(), new Integer(1)},
                   {new Integer(count++),  "legal pad", "1.00", "2.49", new Date(), new Integer(1)},
                   {new Integer(count++),  "tape", "1.00", "1.49", new Date(), new Integer(1)},
                   {new Integer(count++),  "stapler", "4.00", "4.49", new Date(), new Integer(1)},
                   {new Integer(count++),  "Box of Biros", "1.00", "4.99", new Date(), new Integer(2)},
                   {new Integer(count++),  "Blue Biro", "0.10", "0.14", new Date(), new Integer(1)},
                   {new Integer(count++),  "legal pad", "1.00", "2.49", new Date(), new Integer(1)},
                   {new Integer(count++),  "tape", "1.00", "1.49", new Date(), new Integer(1)},
                   {new Integer(count++),  "stapler", "4.00", "4.49", new Date(), new Integer(1)},
                   {new Integer(count++),  "Box of Biros", "1.00", "4.99", new Date(), new Integer(2)}
              TableModel dataModel = new AbstractTableModel()
                   public int getColumnCount() { return headers.length; }
                   public int getRowCount() { return data.length;}
                   public Object getValueAt(int row, int col)
                        return data[row][col];
                   public String getColumnName(int column)
                        return headers[column];
                   public Class getColumnClass(int col)
                        return getValueAt(0,col).getClass();
                   public boolean isCellEditable(int row, int col)
                        return (col==1);
                   public void setValueAt(Object aValue, int row, int column)
                        data[row][column] = aValue;
              tableView = new JTable(dataModel);
              JScrollPane scrollpane = new JScrollPane(tableView);
              scrollpane.setPreferredSize(new Dimension(500, 80));
              frame.getContentPane().setLayout(new BorderLayout());
              frame.getContentPane().add(BorderLayout.CENTER,scrollpane);
              frame.pack();
              JButton printButton= new JButton();
              printButton.setText("print me!");
              frame.getContentPane().add(BorderLayout.SOUTH,printButton);
              // for faster printing turn double buffering off
              RepaintManager.currentManager(frame).setDoubleBufferingEnabled(false);
              printButton.addActionListener( new ActionListener()
                   public void actionPerformed(ActionEvent evt)
                        doPrint();
              frame.setVisible(true);
          * Reset variables before printing
         private void prepareForPrint()
              pages = new ArrayList();
              first_pass = true;
          * Display a print dialog with some hardcoded defaults
          * The print fonts are also hardcoded
         public void doPrint()
              try
                   String jobName = "Java Report";
                   defaultFont = new Font("Arial", Font.PLAIN, 8);
                   footerFont = new Font("Arial", Font.PLAIN, 6);
                   headerFont = new Font("Arial", Font.BOLD, 10);
                   PrinterJob prnJob = PrinterJob.getPrinterJob();
                   prnJob.setPrintable(this);
                   PrintRequestAttributeSet prnSet = new HashPrintRequestAttributeSet();
                   prnSet.add(new Copies(1));
                   prnSet.add(new JobName(jobName, null));
                   prnSet.add(MediaSizeName.ISO_A4);
                   PageFormat pf = prnJob.defaultPage();
                   pf.setOrientation(java.awt.print.PageFormat.PORTRAIT);
                   prnJob.setJobName(jobName);
                   PrintService[] services = PrinterJob.lookupPrintServices();
                   if (services.length > 0)
                        if (prnJob.printDialog(prnSet))
                              * Get print date
                             String dateFormat = "dd/MM/yyyy HH:mm:ss";
                             DateFormat m_DateFormat = new SimpleDateFormat(dateFormat);
                             lastPrintDate = m_DateFormat.format(new Date()).toString();
                             prepareForPrint();
                             prnJob.print(prnSet);
                   else
                        JOptionPane.showMessageDialog(frame, "No Printer was found!!", "Printer Error", JOptionPane.ERROR_MESSAGE);
                        return;
              catch (PrinterException e)
                   e.printStackTrace();
         public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException
               * Check if this is the first time the print method is called for this print action.
               * It is not guaranteed that the print will be called with synchronous pageIndex'es,
               * so we need to calculate the number of pages and which rows appear on which pages.
               * Then the correct page will be printed regardless of which pageIndex is sent through.
              if (first_pass)
                   calcPages(g, pageFormat);
              first_pass = false;
              // Stop printing if the pageIndex is out of range
              if (pageIndex >= pages.size())
                   return NO_SUCH_PAGE;
              Graphics2D     g2 = (Graphics2D) g;
              g2.setColor(Color.black);
              // The footer will be one line at the bottom of the page, cater for this.
              g2.setFont(footerFont);
              footerHeight = g2.getFontMetrics().getHeight() + g2.getFontMetrics().getDescent();
              g2.setFont(defaultFont);
              FontMetrics fontMetrics = g2.getFontMetrics();
              int fontHeight = fontMetrics.getHeight();
              int fontDescent = fontMetrics.getDescent();
              double pageHeight = pageFormat.getImageableHeight() + pageFormat.getImageableY();
              double pageWidth = pageFormat.getImageableWidth();
              double tableWidth = (double) tableView.getColumnModel().getTotalColumnWidth();
              // Shrink or expand the table to fit the page width
              double scale = pageWidth / (tableWidth+ (cellBuffer * tableView.getColumnCount()));
              // Calculate the width in pixels for each column
              double[] columnWidths = new double[tableView.getColumnCount()];
              for(int i = 0; i < tableView.getColumnCount(); i++)
                   columnWidths[i] = (double)tableView.getColumnModel().getColumn(i).getWidth() * scale;
              // Reset the view to the start of the page
              g2.translate(0, 0);
              // Draw a rectangle to see the printable area
              g2.draw3DRect((int)pageFormat.getImageableX(),
                        (int)pageFormat.getImageableY(),
                        (int)pageFormat.getImageableWidth(),
                        (int)pageFormat.getImageableHeight(),
                        false);
              // Calculate the header height
              g2.setFont(headerFont);
              fontMetrics = g2.getFontMetrics();
              // Print the headers and retreive the starting position for the data
              int next_row = printLine(g2, pageFormat, fontMetrics, -1, (int)pageFormat.getImageableY() + fontHeight, columnWidths);
              g2.setFont(defaultFont);
              fontMetrics = g2.getFontMetrics();
              // Start printing the detail
              ArrayList page = (ArrayList)pages.get(pageIndex);
              int start = ((Integer)page.get(0)).intValue();
              int end = ((Integer)page.get(1)).intValue();
              for (int i = start; i <= end; i++)
                   next_row = printLine(g2, pageFormat, fontMetrics, i, next_row, columnWidths);
              // Print the footer
              g2.setFont(footerFont);
              String pageFooter = "Page " + (pageIndex + 1) + " - " + lastPrintDate;
              g2.drawString(pageFooter,
                             (int)pageFormat.getWidth() / 2 - (fontMetrics.stringWidth(pageFooter) / 2),
                             (int)(pageHeight - fontDescent));
              return PAGE_EXISTS;
          * We can't guarantee that the same amount of rows will be displayed on each page,
          * the row heights are dynamic and may wrap onto 2 or more lines.
          * Thus we need to calculate the height of each row and then test how may rows
          * fit on a specific page. eg. Page 1 contains rows 1 to 10, Page 2 contains rows 11 to 15 etc.
         public void calcPages(Graphics g, PageFormat pageFormat) throws PrinterException
              Graphics2D     g2 = (Graphics2D) g;
              g2.setColor(Color.black);
              // The footer will be one line at the bottom of the page, cater for this.
              g2.setFont(footerFont);
              footerHeight = g2.getFontMetrics().getHeight() + g2.getFontMetrics().getDescent();
              g2.setFont(defaultFont);
              FontMetrics fontMetrics = g2.getFontMetrics();
              int fontHeight = fontMetrics.getHeight();
              int fontDescent = fontMetrics.getDescent();
              double pageHeight = pageFormat.getImageableHeight() - fontHeight;
              double pageWidth = pageFormat.getImageableWidth();
              double tableWidth = (double) tableView.getColumnModel().getTotalColumnWidth();
              // Shrink or expand the table to fit the page width
              double scale = pageWidth / (tableWidth+ (cellBuffer * tableView.getColumnCount()));
              // Calculate the width in pixels for each column
              double[] columnWidths = new double[tableView.getColumnCount()];
              for(int i = 0; i < tableView.getColumnCount(); i++)
                   columnWidths[i] = (double)tableView.getColumnModel().getColumn(i).getWidth() * scale;
              // Calculate the header height
              int maxHeight = 0;
              g2.setFont(headerFont);
              fontMetrics = g2.getFontMetrics();
              for (int j = 0; j < tableView.getColumnCount(); j++)
                   String value = tableView.getColumnName(j).toString();
                   int numLines = (int)Math.ceil(fontMetrics.stringWidth(value) / columnWidths[j]);
                   if (numLines > maxHeight)
                        maxHeight = numLines;
              headerHeight = g2.getFontMetrics().getHeight() * maxHeight;
              g2.setFont(defaultFont);
              fontMetrics = g2.getFontMetrics();
              int pageNum = 0;
              int bottom_of_page = (int)(pageFormat.getImageableHeight() + pageFormat.getImageableY()) - footerHeight;
              int prev_row = 0;
              int next_row = (int)pageFormat.getImageableY() + fontHeight + headerHeight;
              int i = 0;
              ArrayList page = new ArrayList();
              page.add(new Integer(0));
              for (i = 0; i < tableView.getRowCount(); i++)
                   maxHeight = 0;
                   for (int j = 0; j < tableView.getColumnCount(); j++)
                        String value = tableView.getValueAt(i, j).toString();
                        int numLines = (int)Math.ceil(fontMetrics.stringWidth(value) / columnWidths[j]);
                        if (numLines > maxHeight)
                             maxHeight = numLines;
                   prev_row = next_row;
                   next_row += (fontHeight * maxHeight);
                   // If we've reached the bottom of the page then set the current page's end row
                   if (next_row > bottom_of_page)
                        page.add(new Integer(i - 1));
                        pages.add(page);
                        page = new ArrayList();
                        page.add(new Integer(i));
                        pageNum++;
                        next_row = (int)pageFormat.getImageableY()
                                       + fontHeight
                                       + ((int)pageFormat.getHeight() * pageNum)
                                       + headerHeight;
                        bottom_of_page = (int)(pageFormat.getImageableHeight()
                                            + pageFormat.getImageableY())
                                            + ((int)pageFormat.getHeight() * pageNum)
                                            - footerHeight;
                        //Include the current row on the next page, because there is no space on this page
                        i--;
              page.add(new Integer(i - 1));
              pages.add(page);
          * Print the headers or a row from the table to the graphics context
          * Return the position of the row following this one
         public int printLine(Graphics2D g2,
                                       PageFormat pageFormat,
                                       FontMetrics fontMetrics,
                                       int rowNum,
                                       int next_row,
                                       double[] columnWidths)
                   throws PrinterException
              int lead = 0;
              int maxHeight = 0;
              for (int j = 0; j < tableView.getColumnCount(); j++)
                   String value = null;
                   int align = LEFT_ALIGN;
                   if (rowNum > -1)
                        Object obj = tableView.getValueAt(rowNum, j);
                        if (obj instanceof Number)
                             align = RIGHT_ALIGN;
                        value = obj.toString();
                   else
                        align = CENTER_ALIGN;
                        value = tableView.getColumnName(j);
                   int numLines = (int)Math.ceil(fontMetrics.stringWidth(value) / columnWidths[j]);
                   if (numLines > maxHeight)
                        maxHeight = numLines;
                   if (fontMetrics.stringWidth(value) < columnWidths[j])
                        // Single line
                        int offset = 0;
                        // Work out the offset from the start of the column to display alignment correctly
                        switch (align)
                             case RIGHT_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value)); break;
                             case CENTER_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value)) / 2; break;
                             default: offset = 0; break;
                        g2.drawString(value,
                                       lead + (int)(pageFormat.getImageableX() + offset),
                                       next_row);
                   else
                        for(int a = 0; a < numLines; a++)
                             //Multi-Line
                             int x = 0;
                             int width = 0;
                             for(x = 0; x < value.length(); x++)
                                  width += fontMetrics.charWidth(value.charAt(x));
                                  if (width > columnWidths[j])
                                       break;
                             int offset = 0;
                             // Work out the offset from the start of the column to display alignment correctly
                             switch (align)
                                  case RIGHT_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value)); break;
                                  case CENTER_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value)) / 2; break;
                                  default: offset = 0; break;
                             g2.drawString(value.substring(0, x),
                                            lead + (int)(pageFormat.getImageableX() + offset),
                                            next_row + (fontMetrics.getHeight() * a));                    
                             value = value.substring(x);
                   lead += columnWidths[j] + cellBuffer;
              // Draw a solid line below the row
              g2.draw(new Line2D.Double(pageFormat.getImageableX(),
                             next_row + (fontMetrics.getHeight() * (maxHeight - 1)) + fontMetrics.getDescent(),
                             pageFormat.getImageableY() + pageFormat.getImageableWidth(),
                             next_row + (fontMetrics.getHeight() * (maxHeight - 1)) + fontMetrics.getDescent()));
              // Return the position of the row following this one
              return next_row + (fontMetrics.getHeight() * maxHeight);
         public static void main(String[] args)
              new Report();
    }

    Fixed some bugs and added a title. Just pass in a JTable and the class will do the rest.
    import javax.swing.*;
    import javax.swing.table.*;
    import java.awt.print.*;
    import java.util.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.geom.*;
    import javax.print.*;
    import javax.print.attribute.*;
    import javax.print.attribute.standard.*;
    import java.text.*;
    import java.math.*;
    public class PrintJTable implements Printable
         private final int LEFT_ALIGN = -1;
         private final int CENTER_ALIGN = 0;
         private final int RIGHT_ALIGN = 1;
         private JFrame m_parent;
         private String m_title;
         private JTable tableView;
         private String lastPrintDate;
         private Font defaultFont;
         private Font headerFont;
         private Font footerFont;
         private int headerHeight;
         private int footerHeight;
         private int cellBuffer = 5;
         private boolean first_pass;
         private ArrayList pages;
         public PrintJTable(JFrame parent, JTable table)
              m_parent = parent;
              tableView = table;
              doPrint();
         public PrintJTable(JFrame parent, String title, JTable table)
              m_parent = parent;
              m_title = title;
              tableView = table;
              doPrint();
          * Reset variables before printing
         private void prepareForPrint()
              pages = new ArrayList();
              first_pass = true;
          * Display a print dialog with some hardcoded defaults
          * The print fonts are also hardcoded
         public void doPrint()
              try
                   String jobName = "Java Report";
                   defaultFont = new Font("Arial", Font.PLAIN, 8);
                   footerFont = new Font("Arial", Font.PLAIN, 6);
                   headerFont = new Font("Arial", Font.BOLD, 8);
                   PrinterJob prnJob = PrinterJob.getPrinterJob();
                   prnJob.setPrintable(this);
                   PrintRequestAttributeSet prnSet = new HashPrintRequestAttributeSet();
                   prnSet.add(new Copies(1));
                   prnSet.add(new JobName(jobName, null));
                   prnSet.add(MediaSizeName.ISO_A4);
                   PageFormat pf = prnJob.defaultPage();
                   pf.setOrientation(java.awt.print.PageFormat.PORTRAIT);
                   prnJob.setJobName(jobName);
                   PrintService[] services = PrinterJob.lookupPrintServices();
                   if (services.length > 0)
                        if (prnJob.printDialog(prnSet))
                              * Get print date
                             String dateFormat = "dd/MM/yyyy HH:mm:ss";
                             DateFormat m_DateFormat = new SimpleDateFormat(dateFormat);
                             lastPrintDate = m_DateFormat.format(new Date()).toString();
                             prepareForPrint();
                             prnJob.print(prnSet);
                   else
                        JOptionPane.showMessageDialog(m_parent, "No Printer was found!!", "Printer Error", JOptionPane.ERROR_MESSAGE);
                        return;
              catch (PrinterException e)
                   e.printStackTrace();
         public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException
               * Check if this is the first time the print method is called for this print action.
               * It is not guaranteed that the print will be called with synchronous pageIndex'es,
               * so we need to calculate the number of pages and which rows appear on which pages.
               * Then the correct page will be printed regardless of which pageIndex is sent through.
              if (first_pass)
                   calcPages(g, pageFormat);
              first_pass = false;
              // Stop printing if the pageIndex is out of range
              if (pageIndex >= pages.size())
                   return NO_SUCH_PAGE;
              Graphics2D     g2 = (Graphics2D) g;
              g2.setColor(Color.black);
              // The footer will be one line at the bottom of the page, cater for this.
              g2.setFont(footerFont);
              footerHeight = g2.getFontMetrics().getHeight() + g2.getFontMetrics().getDescent();
              g2.setFont(defaultFont);
              FontMetrics fontMetrics = g2.getFontMetrics();
              int fontHeight = fontMetrics.getHeight();
              int fontDescent = fontMetrics.getDescent();
              double pageHeight = pageFormat.getImageableHeight() + pageFormat.getImageableY();
              double pageWidth = pageFormat.getImageableWidth();
              double tableWidth = (double) tableView.getColumnModel().getTotalColumnWidth();
              // Shrink or expand the table to fit the page width
              double scale = (pageWidth - (cellBuffer * tableView.getColumnCount())) / tableWidth;
              // Calculate the width in pixels for each column
              double[] columnWidths = new double[tableView.getColumnCount()];
              double test = 0;
              for(int i = 0; i < tableView.getColumnCount(); i++)
                   columnWidths[i] = (double)Math.floor(tableView.getColumnModel().getColumn(i).getWidth() * scale);
                   test += columnWidths;
              // Reset the view to the start of the page
              g2.translate(0, 0);
              // Draw a rectangle to see the printable area
              g2.draw3DRect((int)pageFormat.getImageableX(),
                        (int)pageFormat.getImageableY(),
                        (int)pageFormat.getImageableWidth(),
                        (int)pageFormat.getImageableHeight(),
                        false);
              // Calculate the header height
              g2.setFont(headerFont);
              fontMetrics = g2.getFontMetrics();
              // Print the headers and retreive the starting position for the data
              int next_row = (int)pageFormat.getImageableY() + fontMetrics.getHeight();
              if ((m_title != null) && (!m_title.equalsIgnoreCase("")))
                   g2.drawString(m_title,
                                       (int)(pageFormat.getImageableX()),
                                       next_row);
                   Color current_color = g2.getColor();
                   g2.setColor(Color.lightGray);
                   int y = next_row + fontMetrics.getDescent();
                   g2.draw(new Line2D.Double(pageFormat.getImageableX(),
                                  y,
                                  (pageFormat.getImageableY() + pageFormat.getImageableWidth()),
                                  y));
                   g2.setColor(current_color);
                   next_row += fontMetrics.getHeight();
              next_row = printLine(g2, pageFormat, fontMetrics, -1, next_row, columnWidths);
              g2.setFont(defaultFont);
              fontMetrics = g2.getFontMetrics();
              // Start printing the detail
              ArrayList page = (ArrayList)pages.get(pageIndex);
              int start = ((Integer)page.get(0)).intValue();
              int end = ((Integer)page.get(1)).intValue();
              for (int i = start; i <= end; i++)
                   next_row = printLine(g2, pageFormat, fontMetrics, i, next_row, columnWidths);
              // Print the footer
              g2.setFont(footerFont);
              String pageFooter = "Page " + (pageIndex + 1) + " - " + lastPrintDate;
              g2.drawString(pageFooter,
                             (int)pageFormat.getWidth() / 2 - (fontMetrics.stringWidth(pageFooter) / 2),
                             (int)(pageHeight - fontDescent));
              return PAGE_EXISTS;
         * We can't guarantee that the same amount of rows will be displayed on each page,
         * the row heights are dynamic and may wrap onto 2 or more lines.
         * Thus we need to calculate the height of each row and then test how may rows
         * fit on a specific page. eg. Page 1 contains rows 1 to 10, Page 2 contains rows 11 to 15 etc.
         public void calcPages(Graphics g, PageFormat pageFormat) throws PrinterException
              Graphics2D     g2 = (Graphics2D) g;
              g2.setColor(Color.black);
              // The footer will be one line at the bottom of the page, cater for this.
              g2.setFont(footerFont);
              footerHeight = g2.getFontMetrics().getHeight() + g2.getFontMetrics().getDescent();
              g2.setFont(defaultFont);
              FontMetrics fontMetrics = g2.getFontMetrics();
              int fontHeight = fontMetrics.getHeight();
              int fontDescent = fontMetrics.getDescent();
              double pageHeight = pageFormat.getImageableHeight() - fontHeight;
              double pageWidth = pageFormat.getImageableWidth();
              double tableWidth = (double) tableView.getColumnModel().getTotalColumnWidth();
              // Shrink or expand the table to fit the page width
              double scale = (pageWidth - (cellBuffer * tableView.getColumnCount())) / tableWidth;
              // Calculate the width in pixels for each column
              double[] columnWidths = new double[tableView.getColumnCount()];
              for(int i = 0; i < tableView.getColumnCount(); i++)
                   columnWidths[i] = (double)Math.floor(tableView.getColumnModel().getColumn(i).getWidth() * scale);
              // Calculate the header height
              int maxHeight = 0;
              g2.setFont(headerFont);
              fontMetrics = g2.getFontMetrics();
              headerHeight = 0;
              if ((m_title != null) && (!m_title.equalsIgnoreCase("")))
                   headerHeight = fontMetrics.getHeight();
              for (int j = 0; j < tableView.getColumnCount(); j++)
                   String value = tableView.getColumnName(j).toString();
                   int numLines = (int)Math.ceil(fontMetrics.stringWidth(value) / columnWidths[j]);
                   if (numLines > maxHeight)
                        maxHeight = numLines;
              headerHeight += g2.getFontMetrics().getHeight() * maxHeight;
              g2.setFont(defaultFont);
              fontMetrics = g2.getFontMetrics();
              int pageNum = 0;
              int bottom_of_page = (int)(pageFormat.getImageableHeight() + pageFormat.getImageableY()) - footerHeight;
              int prev_row = 0;
              int next_row = (int)pageFormat.getImageableY() + fontHeight + headerHeight;
              int i = 0;
              ArrayList page = new ArrayList();
              page.add(new Integer(0));
              for (i = 0; i < tableView.getRowCount(); i++)
                   maxHeight = 0;
                   for (int j = 0; j < tableView.getColumnCount(); j++)
                        String value = formatObject(tableView.getValueAt(i, j));
                        int numLines = (int)Math.ceil(fontMetrics.stringWidth(value) / columnWidths[j]);
                        if (numLines > maxHeight)
                             maxHeight = numLines;
                   prev_row = next_row;
                   next_row += (fontHeight * maxHeight);
                   // If we've reached the bottom of the page then set the current page's end row
                   if (next_row > bottom_of_page)
                        page.add(new Integer(i - 1));
                        pages.add(page);
                        page = new ArrayList();
                        page.add(new Integer(i));
                        pageNum++;
                        next_row = (int)pageFormat.getImageableY()
                                       + fontHeight
                                       + ((int)pageFormat.getHeight() * pageNum)
                                       + headerHeight;
                        bottom_of_page = (int)(pageFormat.getImageableHeight()
                                            + pageFormat.getImageableY())
                                            + ((int)pageFormat.getHeight() * pageNum)
                                            - footerHeight;
                        //Include the current row on the next page, because there is no space on this page
                        i--;
              page.add(new Integer(i - 1));
              pages.add(page);
         * Print the headers or a row from the table to the graphics context
         * Return the position of the row following this one
         public int printLine(Graphics2D g2,
                                       PageFormat pageFormat,
                                       FontMetrics fontMetrics,
                                       int rowNum,
                                       int next_row,
                                       double[] columnWidths)
                   throws PrinterException
              int lead = 0;
              int maxHeight = 0;
              for (int j = 0; j < tableView.getColumnCount(); j++)
                   String value = null;
                   int align = LEFT_ALIGN;
                   if (rowNum > -1)
                        Object obj = tableView.getValueAt(rowNum, j);
                        if (obj instanceof Number)
                             align = RIGHT_ALIGN;
                        value = formatObject(obj);
                   else
                        //align = CENTER_ALIGN;
                        value = tableView.getColumnName(j);
                   int numLines = (int)Math.ceil(fontMetrics.stringWidth(value) / columnWidths[j]);
                   if (numLines > maxHeight)
                        maxHeight = numLines;
                   if (fontMetrics.stringWidth(value) < columnWidths[j])
                        // Single line
                        int offset = 0;
                        // Work out the offset from the start of the column to display alignment correctly
                        switch (align)
                             case RIGHT_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value)); break;
                             case CENTER_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value)) / 2; break;
                             default: offset = 0; break;
                        g2.drawString(value,
                                       lead + (int)(pageFormat.getImageableX() + offset),
                                       next_row);
                   else
                        for(int a = 0; a < numLines; a++)
                             //Multi-Line
                             int x = 0;
                             int width = 0;
                             for(x = 0; x < value.length(); x++)
                                  width += fontMetrics.charWidth(value.charAt(x));
                                  if (width > columnWidths[j])
                                       break;
                             int offset = 0;
                             // Work out the offset from the start of the column to display alignment correctly
                             switch (align)
                                  case RIGHT_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value.substring(0, x))); break;
                                  case CENTER_ALIGN: offset = (int)(columnWidths[j] - fontMetrics.stringWidth(value.substring(0, x))) / 2; break;
                                  default: offset = 0; break;
                             g2.drawString(value.substring(0, x),
                                            lead + (int)(pageFormat.getImageableX() + offset),
                                            next_row + (fontMetrics.getHeight() * a));                    
                             value = value.substring(x);
                   lead += columnWidths[j] + cellBuffer;
              // Draw a solid line below the row
              Color current_color = g2.getColor();
              g2.setColor(Color.lightGray);
              int y = next_row + (fontMetrics.getHeight() * (maxHeight - 1)) + fontMetrics.getDescent();
              g2.draw(new Line2D.Double(pageFormat.getImageableX(),
                             y,
                             (pageFormat.getImageableY() + pageFormat.getImageableWidth()),
                             y));
              g2.setColor(current_color);
              // Return the position of the row following this one
              return next_row + (fontMetrics.getHeight() * maxHeight);
         public String formatObject(Object obj)
              String value = (obj == null) ? "" : obj.toString();
              return value;

  • Changing a JTable's model

    Hello everyone,
    I dont have a whole lot of experience w/ JTables and this problem has been troubling me for a while now. I have a JTable that significantly changes when the user hits the calculate button, so instead of going through the trouble of completely reformatting the table by adding/removing columns and table headers, I simple make a new model and set the table's model to the new one, and then call table.updateUI(). The problem is that my custom cell renderer that displays a down or up arrow depending on how the given column is sorted stops working. The sorter works properly and the table displays properly, but the arrows do not change from up to down or vise versa when a column is resorted. The whole process works just fine the first time, but the more times the user pushes the calculate button the more off it gets. I have added a flag in the actionPerformed method in my cell renderer that tells me when the table header is clicked. Every time the calculate button is hit, more flags appear when the table header is clicked. Therefore the arrows change position on odd numbers and do not on evens (because the evens cancel eachother out and the arrow appears to sit still), however the sorter still works properly. It seems as though somehow a new mouse listener or cell renderer is added after each time the calculate button is pressed. Either that or the entire table is not being reset somehow and something is still invisible in the background.
    Here is some of my code.
    // The initial table with no data
    String[] str = {"class 1", "class 2", "class 3", "class 4", "class 5", "= GPA"};
    String[][]  data= {{"", "", "", "", "", ""},
    pmModel = new ProjectionModel(str, data);
    mainTable = new JTable(pmModel);
    mainTable.getTableHeader().setDefaultRenderer(new SortButtonRenderer());
    // After the calculate button has been pushed:
    // The table is reset here. TableSorter extends TableMap
        ProjectionModel newModel = new ProjectionModel(courseList, charList);
        TableSorter sorter = new TableSorter(newModel);
        // this method is shown below
        sorter.addMouseListenerToHeaderInTable(mainTable);
        sorter.sortedUp = false;
        mainTable.addNotify();
        mainTable.setModel(sorter);
        // SortButtonRenderer extends TableCellRenderer
        SortButtonRenderer renderer = new SortButtonRenderer();
        mainTable.getTableHeader().setDefaultRenderer(renderer);
        // set the arrow to DOWN on the first column
        renderer.setSelectedColumn(0);
        renderer.setSelectedColumn(0);
        mainTable.updateUI();
    // This is the addMouseListenerToTable(JTable table) found in the TableSorter class
        public void addMouseListenerToHeaderInTable(JTable table) {
            final TableSorter sorter = this;
            final JTable tableView = table;
            tableView.setColumnSelectionAllowed(false);
            MouseAdapter listMouseListener = new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    TableColumnModel columnModel = tableView.getColumnModel();
                    int viewColumn = columnModel.getColumnIndexAtX(e.getX());
                    int column = tableView.convertColumnIndexToModel(viewColumn);
                    if (e.getClickCount() == 1 && column != -1) {
                      SortButtonRenderer renderer =
                            (SortButtonRenderer)tableView.getTableHeader().getDefaultRenderer();
                        //int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK;
                        //boolean ascending = (shiftPressed == 0);
                        boolean ascending = (renderer.getLastColumn() == column && !sortedUp);
                        renderer.setLastColumn(column);
                        sorter.sortByColumn(column, ascending);
                        if (ascending) sortedUp = true;
                        else sortedUp = false;
                        renderer.setSelectedColumn(column);
                        // the flag that prints the column that is pressed
                        System.out.println(column + " " + sortedUp);
            JTableHeader th = tableView.getTableHeader();
            th.addMouseListener(listMouseListener); // it seems like i need a way to remove any
    // previous mouseListeners here, i think this would solve the problem
        }The flag's output looks something like this:
    < first time calculate button is clicked >
    < column 0 is clicked> 0 false
    < column 0 is clicked again > 0 true
    < second time calculate button is clicked >
    < column 0 is clicked >
    0 false
    0 true
    notice they cancel eachother out so the arrow stays in the same position
    < third time calculate button is clicked >
    < column 0 is clicked >
    0 false
    0 true
    0 false
    notice they dont cancel eachother out, so the arrow changes positions
    This problem is really bugging me. I appreciate any help. Thanks.
    -kc

    Well I fixed my problem but I kind of cheated...
    I used a static variable in the class that inits the GUI to keep track of the MouseListeners that are added to the table headers by the TableSorter. In the TableSorter class's addMouseListenerToTableHeader(JTable table) class I added a line of code before the "th.addMouseListener(listListener)" line: "th.removeMouseListener(Projector.lastListener))"
    It works but it kind of sucks. If there is a better way to do it that would be great. I'm sorry if you read through all of that stuff and then I ended up fixing my own problem anyway :(
    Thanks anyway.
    -kc

  • How to select rows in the inner JTable rendered in an outer JTable cell

    I have wrriten the following code for creating cell specific renderer - JTable rendered in a cell of a JTable.
    table=new JTable(data,columnNames)
    public TableCellRenderer getCellRenderer(int row, int column)
    if ((row == 0) && (column == 0))
    return new ColorRenderer();
    else if((row == 1) && (column == 0))
    return new ColorRenderer1();
    else
    return super.getCellRenderer(row, column);
    ColorRenderer and ColorRenderer1 are two inner classes, which implement TableCellRenderer to draw inner JTable on the outer JTable cell, having 2 rows and 1 column each.
    Now what is happening the above code keeps executing continously, that is the classes are being initialised continously, inner JTables are rendered (drawn) continously, and this makes the application slow after some time. It throws java.lang.OutOfMemoryException.
    WHY IS IT SO??? I can't understand where's the bug..
    Any advice please???
    Moreover i want selections in inner tables and not on outer table, how can this be possible.
    I am working on this since a long time but have not yet found a way out...

    With your help i have overcome the problem of continous repeatition.
    The major problem which I am facing is, in selecting rows in the inner rendered JTables.
    I have added listener on outer JTable which select rows on the outer JTable, hence the complete inner JTable which being treated as a row, gets selected.
    The thing is i need to select the rows of inner rendered JTables,not the outer JTable.
    How to go about it??
    I have even added listener to inner rendered JTables, but only first row of every table gets selected.
    Please help....
    Thanks in advance.

  • Is this a bug in Java!

    int i=0;
    i=i++;
    System.out.println(i);
    What should this code print.
    I guess 1.
    But it prints 0.
    Can anyone explain the logic behind this, or is this a bug in Java.
    Thanks for your time in advance.

    What did the mackerel do to deserve that?it wrote Sun's search function !!!!!! :pPlease, a mackerel would of done a better job.
    All I can say is "JTable project", smiles.
    (Gah, Please kill me)mlk BANG BANG*
    *Post decease operator.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               

  • L&F mapping JTable

    The look and feel values chosen for the JTable under windows do not feel right to me (albeit that I cannot find many examples of tables under windows).
    I am trying to provide some workarounds that follow what most people expect to happen but am a little unclear on what should happen on some key presses for the JTable in windows look and feel
    Space handling
    From one of the MS windows books it suggests that the space key is a selection key, thus when you press space a row in a table should be selected (and when you press again it should deselect the row?)
    The JList has functionality for the space key
    q1) JTable should have space bar handling out of the box?
    (space, CTRL+space, shift+space)
    I have not seen this raised as a bug.
    Enter handling
    The enter key press is supposed to activate the default button
    or commit a text field under edit.
    F2 puts a field under edit
    q2) Should the enter key put an editable cell under edit (same mouse action as single or double click to edit)?
    q3) if not should the first enter key stop cell editing and a second press activate the default button, with only a single enter to activate default button if a cell is not under edit
    Escape handling
    The escape key will cancel editing of a cell that is under edit.
    q4) should the event not be passed on when editing has been cancelled so that dialogs with a canel button can be dismissed
    or
    q5) should the first escape cancel editing while the second escape will action the escape action of the window.
    Any links to "highly regarded" useability/look and feel sites would be helpful - sun does not appear to provide much on this subject
    I've read both their l&f and advanced l&f books and they just scratch the surface of look and feel without much realworld appliance to real gui issues.
    Might be useful to others: Some of the code that I was trying to create goes like....
    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Event;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Window;
    import java.awt.event.ActionEvent;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.awt.event.KeyEvent;
    import java.math.BigDecimal;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.CellEditor;
    import javax.swing.InputMap;
    import javax.swing.JComponent;
    import javax.swing.JLabel;
    import javax.swing.JTable;
    import javax.swing.JViewport;
    import javax.swing.KeyStroke;
    import javax.swing.ListSelectionModel;
    import javax.swing.RootPaneContainer;
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.border.Border;
    import javax.swing.border.LineBorder;
    import javax.swing.table.TableColumn;
    import javax.swing.table.TableColumnModel;
    import javax.swing.table.TableModel;
    /** Provides utility methods for the JTable and JCSortableTable classes
    * @author  */
    public final class JTableUtil
          * Constructor for JCTableUtil.
         private JTableUtil()
              super();
         /** will resize the last column of a table to fill the gap
          * if the width of the columns is narrower than the width of the
          * table
          * @param table to act upon
         static void resizeLastColumn(final JTable table)
              TableColumn lastColumn = getLastVisibleColumn(table);
              if (lastColumn != null)
                   int gap = getLastColumnGap(table);
                   if (gap > 0)
                        int newLastColumnWidth = gap + lastColumn.getWidth();
                        lastColumn.setWidth(newLastColumnWidth);
                        lastColumn.setPreferredWidth(newLastColumnWidth);
                        redrawTable(table);
          * Determines if there is a gap between the last column in a table
          * and the border of the table.
          * Works if the table is drawn on the page or is in the
          * viewport of a scroll pane.
          * @param table to act upon
          * @return the gap in pixels between last column and table edge.
         static int getLastColumnGap(final JTable table)
              int widthTable = getTableWidth(table);
              int lastVisColIndex = getLastVisibleColumnIndex(table);
              TableColumnModel columnModel = table.getColumnModel();
              int widthColumns = columnModel.getTotalColumnWidth();
              final TableColumn lastColumn = columnModel.getColumn(lastVisColIndex);
              // gap is number of pixels from right hand edge of last column
              // to right hand edge of the table
              int gap = widthTable - widthColumns;
              return gap;
          * Determines the width of a table returning the table width if
          * table is painted onto a panel, but if the table is painted
          * into a scroll pane then it is the scroll pane viewport width that is returned
          * This is to capture that the width of the table may be less than the
          * width of the viewport - ie if there is a gap after the last column in a table.
          * @param table to act upon
          * @return the width of the table in pixels
         static int getTableWidth(final JTable table)
              int widthTable = table.getWidth();
              Object tableParent = table.getParent();
              JViewport tableViewPort = null;
              if (tableParent instanceof JViewport)
                   tableViewPort = (JViewport) tableParent;
                   widthTable = tableViewPort.getWidth();
              return widthTable;
         /** Cause the table to redraw wether table is painted on a panel
          * or in a scroll pane
          * @param table to act upon
         static void redrawTable(final JTable table)
              Component tableRegion = table;
              Component tableParent = table.getParent();
              if (tableParent instanceof JViewport)
                   tableRegion = tableParent;
              tableRegion.invalidate();
              tableRegion.validate();
         /** Determines the last (right most) column in a table with a width
          * greater than 0
          * @param table to act upon
          * @return index (0 based) of the last visible column.
         static int getLastVisibleColumnIndex(JTable table)
              TableColumnModel columnModel = table.getColumnModel();
              boolean found = false;
              int columnIndex = columnModel.getColumnCount();
              while (!found && columnIndex > 0)
                   columnIndex--;
                   if (columnModel.getColumn(columnIndex).getWidth() != 0)
                        found = true;
              return columnIndex;
         /** Determines the last (right most) column in a table with a width
          * greater than 0
          * @param table to act upon
          * @return TableColumn - the last visible column.
         static TableColumn getLastVisibleColumn(JTable table)
              TableColumnModel columnModel = table.getColumnModel();
              return columnModel.getColumn(getLastVisibleColumnIndex(table));
          * Add the currency symbol to a JCTable/JCListTable column header
          * e.g. 'Salary' becomes 'Salary (�)'
         public static void addCurrencySymbol(JTable table, Object columnKey)
              //@todo - update to work with JTable
              TableColumnModel columnModel = table.getTableHeader().getColumnModel();
              int columnIndex = columnModel.getColumnIndex(columnKey);
              TableColumn column = columnModel.getColumn(columnIndex);
              Object object = column.getHeaderValue();
              if (object instanceof JLabel)
                   JLabel columnLabel = (JLabel) object;
                   SlacCurrency.getInstance().addCurrencySymbol(columnLabel);
              else
                   // @todo log here ???
              // is above correct - need to get the JLabel rendered from the table
              // in the scroll pane?          
         /** Provides a sum of a column of BigDecimals
          * @param tableModel model containing column that you wish to sum
          * @param column the column that you wish to be summed, must contain numeric values and not strings
         public static BigDecimal calculateTotalForColumn(final TableModel tableModel, final int column)
              int numRows = tableModel.getRowCount();
              BigDecimal total = new BigDecimal("0");
              for (int row = 0; row < numRows; row++)
                   Object value = tableModel.getValueAt(row, column);
                   if (value instanceof BigDecimal)
                        BigDecimal decimalValue = (BigDecimal) value;
                        total = total.add(decimalValue);
                   else
                        //Logger.logGeneralFailure("", ErrorCodes.GUI_ERROR, this);
              return total;
          * Provides the swing setting JTable.AUTO_RESIZE_OFF.
          * <p>
          * In this situation if the width of the columns is smaller than the width
          * of the table then there will be a gap after the last column.
          * <p>
          * This method will add a ComponentListener to the table and or
          * scroll pane viewport so that there is no gap after the last column.
          * @param table to act upon
         public static void configureAutoResizeOff(final JTable table)
              table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
              Component tableParent = table.getParent();
              JViewport tableViewPort = null;
              ComponentAdapter gapFiller = new ComponentAdapter()
                   public void componentResized(ComponentEvent e)
                        super.componentResized(e);
                        JTableUtil.resizeLastColumn(table);
              table.addComponentListener(gapFiller);
              if (tableParent instanceof JViewport)
                   tableViewPort = (JViewport) tableParent;
                   tableViewPort.addComponentListener(gapFiller);
         /** Method provides fixes to provide standard handling for keyboard and mouse usage
          * <ul>
          *    <li> spacebar - selection handling of rows that have focus emphasis see fixSpacebarHandling()
          *    <li> initial focus handling - table should give visual feedback when gaining focus
          *    <li>
          * </ul>
          * @param table - the table to add standards handling to
         public static void setupKeyboardMouseHandling(JTable table)
              fixTableGainsFocusOnFirstEntry(table);
              fixSpacebarHandling(table);
              fixCtrlSpacebarHandling(table);
              fixShiftSpacebarHandling(table);
              fixCtrlUpDownHandling(table);
              fixFocusHighlight();
              fixEscEnter(table);
         /** Add fixes to the look and feel handling for a JTable
          * Enter on a table will do different things depending on the mode of the table
          * <p>
          * if a cell is being edited then
          * Enter will do what normally happens to a field under edit - ie stop the editing and commit
          * Escape will cancel the editing
          * <p>
          * if a cell is not under edit then
          * Enter will activate the default button
          * Escape will activate the default cancel button (see FrameUtil.addEscapeKeyAction())
         static void fixEscEnter(final JTable table)
              final RootPaneContainer root = (RootPaneContainer)SwingUtilities.windowForComponent(table);
              final String escapeKey = "escapeAction";
              Action escapeAction = new AbstractAction(escapeKey)
                   public void actionPerformed(ActionEvent actionEvent)
                        if(table.isEditing())
                             CellEditor editor = table.getCellEditor(table.getEditingRow(), table.getEditingColumn());
                             editor.cancelCellEditing();
                        else
                             Window parentWindow = SwingUtilities.windowForComponent(table);
                             Action windowEscapeAction = FrameUtil.getEscapeKeyAction(root);
                             windowEscapeAction.actionPerformed(actionEvent);
              final String enterKey = "enterAction";
              Action enterAction = new AbstractAction(enterKey)
                   public void actionPerformed(ActionEvent actionEvent)
                        if(table.isEditing())
                             CellEditor editor = table.getCellEditor(table.getEditingRow(), table.getEditingColumn());
                             editor.stopCellEditing();
                        else
                             root.getRootPane().getDefaultButton().doClick();
              InputMap inputMap = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
              table.getActionMap().put(escapeKey, escapeAction);
              table.getActionMap().put(enterKey, enterAction);
              inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), escapeKey);          
              inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), enterKey);          
         static void fixFocusHighlight()
              Border focusCellHighlightBorder = new DashedLineBorder(Color.BLACK);
              UIManager.put("Table.focusCellHighlightBorder", focusCellHighlightBorder);
         /** If you do not setup a table to have a row selected then there is a bug where for example
          * you use an accelerator key to put focus in the table and you get no visual feedback that
          * the table has focus - ie no focus emphasis - dashed box
          * @param table - the table to fix
         static void fixTableGainsFocusOnFirstEntry(JTable table)
              // for first time tabbing to table make sure that first cell has focus
              // don't seem to need this fix under windows XP
              table.getSelectionModel().removeSelectionInterval(0, 0);
         /** fix spacebar handling
          * java standards does not mention spacebar handling on a JTable but this is a windows
          * standard feature and is also a feature on JList.
          * Without spacebar handling on a JTable there would be no other keyboard handling that
          * would allow you to select the current row.
          * <p>
          * Trying to follow windows standards since java does not list space bar handling for JTable.
          * The following bahaviour can be seen in IBM code in project Java build path - tab libraries
          * and in microsoft code in the administrative tools control panel dialog in windows XP
          * <p>
          * spacebar - select the current row without deselecting any others
          * <p>
          * handling should be fixed in merlin release 1.5
          * see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
         static void fixSpacebarHandling(JTable table)
              KeyStroke ksSpace = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true);
              final String ACTION_SPACE = "SPACE";
              Action spaceAction = new AbstractAction(ACTION_SPACE)
                   public void actionPerformed(ActionEvent actionEvent)
                        ListSelectionModel selectModel =
                             ((JTable) actionEvent.getSource()).getSelectionModel();
                        int currentRowIndex = selectModel.getAnchorSelectionIndex();
                        selectModel.addSelectionInterval(currentRowIndex, currentRowIndex);
              table.getInputMap().put(ksSpace, ACTION_SPACE);
              table.getActionMap().put(ACTION_SPACE, spaceAction);
         /** fix ctrl + spacebar handling
          * java standards does not mention spacebar handling on a JTable but this is a windows
          * standard feature and is also a feature on JList.
          * Without spacebar handling on a JTable there would be no other keyboard handling that
          * would allow you to select the current row.
          * <p>
          * Trying to follow windows standards since java does not list space bar handling for JTable.
          * The following bahaviour can be seen in IBM code in project Java build path - tab libraries
          * and in microsoft code in the administrative tools control panel dialog in windows XP
          * <p>
          * ctrl + spacebar - toggle selection on the current row without deselecting any others
          * <p>
          * handling should be fixed in merlin release 1.5
          * see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
         static void fixCtrlSpacebarHandling(JTable table)
              KeyStroke ksCtrlSpace = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, Event.CTRL_MASK, true);
              final String ACTION_CTRL_SPACE = "CTRLSPACE";
              Action ctrlSpaceAction = new AbstractAction(ACTION_CTRL_SPACE)
                   public void actionPerformed(ActionEvent actionEvent)
                        ListSelectionModel selectModel =
                             ((JTable) actionEvent.getSource()).getSelectionModel();
                        int currentRowIndex = selectModel.getAnchorSelectionIndex();
                        boolean isCurrentRowSelected = selectModel.isSelectedIndex(currentRowIndex);
                        if (isCurrentRowSelected)
                             selectModel.removeSelectionInterval(currentRowIndex, currentRowIndex);
                        else
                             selectModel.addSelectionInterval(currentRowIndex, currentRowIndex);
              table.getInputMap().put(ksCtrlSpace, ACTION_CTRL_SPACE);
              table.getActionMap().put(ACTION_CTRL_SPACE, ctrlSpaceAction);
         /** fix shift + spacebar handling
          * java standards does not mention spacebar handling on a JTable but this is a windows
          * standard feature and is also a feature on JList.
          * Without spacebar handling on a JTable there would be no other keyboard handling that
          * would allow you to select the current row.
          * <p>
          * Trying to follow windows standards since java does not list space bar handling for JTable.
          * The following bahaviour can be seen in IBM code in project Java build path - tab libraries
          * and in microsoft code in the administrative tools control panel dialog in windows XP
          * <p>
          * shift + spacebar - extend the selection from the anchor to the lead index.
          * this might still be a bit funny in java 1.4.2 and code may need to be changed slightly
          * <p>
          * handling should be fixed in merlin release 1.5
          * see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
         static void fixShiftSpacebarHandling(JTable table)
              KeyStroke ksShiftSpace = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, Event.SHIFT_MASK, true);
              final String ACTION_SHIFT_SPACE = "SHIFTSPACE";
              Action shiftSpaceAction = new AbstractAction(ACTION_SHIFT_SPACE)
                   public void actionPerformed(ActionEvent actionEvent)
                        ListSelectionModel selectModel =
                             ((JTable) actionEvent.getSource()).getSelectionModel();
                        int currentRowIndex = selectModel.getAnchorSelectionIndex();
                        int startRowIndex = selectModel.getLeadSelectionIndex();
                        selectModel.setSelectionInterval(startRowIndex, currentRowIndex);
              table.getInputMap().put(ksShiftSpace, ACTION_SHIFT_SPACE);
              table.getActionMap().put(ACTION_SHIFT_SPACE, shiftSpaceAction);
         /** fix ctrl + down or up handling - move focus emphasis up or down accordingly
          * <p>
          * handling should be fixed in merlin release 1.5
          * see bug report http://developer.java.sun.com/developer/bugParade/bugs/4303294.html
         static void fixCtrlUpDownHandling(JTable table)
              final String ACTION_UP = "CTRLUP";
              KeyStroke ksCtrlUp = KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.CTRL_MASK, true);
              KeyStroke ksCtrlDown = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.CTRL_MASK, true);
              Action ctrlUpAction = new CtrlUpDownAction(ACTION_UP, true);
              final String ACTION_DOWN = "CTRLDOWN";
              Action ctrlDownAction = new CtrlUpDownAction(ACTION_DOWN, false);
              table.getInputMap().put(ksCtrlUp, ACTION_UP);
              table.getActionMap().put(ACTION_UP, ctrlUpAction);
              table.getInputMap().put(ksCtrlDown, ACTION_DOWN);
              table.getActionMap().put(ACTION_DOWN, ctrlDownAction);
    * Action for moving focus emphasis up or down
    class CtrlUpDownAction extends AbstractAction
         private boolean m_isUp = true;
         /** Ctor controlling direction of focus emphasis
          * @param name of the action
          * @param isUp - true if dirction of action is up, else false
         public CtrlUpDownAction(String name, boolean isUp)
              super(name);
              m_isUp = isUp;
         /** Moves the focus emphasis
          * @param actionEvent - see javax.swing.AbstractAction class for details
         public void actionPerformed(ActionEvent actionEvent)
              JTable table = (JTable) actionEvent.getSource();
              ListSelectionModel selectModel = table.getSelectionModel();
              int nextRowIndex =
                   getNextRowIndex(table.getRowCount(), selectModel.getAnchorSelectionIndex());
              if (selectModel.isSelectedIndex(nextRowIndex))
                   selectModel.addSelectionInterval(nextRowIndex, nextRowIndex);
              else
                   selectModel.removeSelectionInterval(nextRowIndex, nextRowIndex);
         /** Gets the index of the next row depending on direction up or down
          * @param rowCount - number of rows in the table
          * @param currentRowIndex - index of the row that has focus emphasis
          * @return the index of the next row
         private int getNextRowIndex(int rowCount, int currentRowIndex)
              int nextRowIndex = -1;
              if (m_isUp && currentRowIndex > 0)
                   // emphasis moving up one row
                   nextRowIndex = currentRowIndex - 1;
              else if (!m_isUp && currentRowIndex < (rowCount - 1))
                   // emphasis moving down one row
                   nextRowIndex = currentRowIndex + 1;
              return nextRowIndex;
    /** Draws a dashed line (well more of a dotted line)
    * Used for eg drawing focus emphasis rectangle
    * Class draws a rectangle of chosen colour overlaid with a dashed white line.
    class DashedLineBorder extends LineBorder
         private BasicStroke m_lineStroke = new BasicStroke();
         private BasicStroke m_dashStroke =
              new BasicStroke(
                   m_lineStroke.getLineWidth(),
                   BasicStroke.CAP_BUTT,//m_lineStroke.getEndCap(),
                   m_lineStroke.getLineJoin(),
                   m_lineStroke.getMiterLimit(),
                   new float[] { 1,1,1 },
                   0);
          * Constructor for DashedLineBorder.
          * @param color
         public DashedLineBorder(Color color)
              super(color);
          * Constructor for DashedLineBorder.
          * @param color
          * @param thickness
         public DashedLineBorder(Color color, int thickness)
              super(color, thickness);
          * Constructor for DashedLineBorder.
          * @param color
          * @param thickness
          * @param roundedCorners
         public DashedLineBorder(Color color, int thickness, boolean roundedCorners)
              super(color, thickness, roundedCorners);
         /** Similar to LineBorder's method but draws a line in the chosen colour
          * before drawing a white dashed line on top of it
          * @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int)
         public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
              Color oldColor = g.getColor();
              int i;
              Graphics2D g2 = (Graphics2D) g;
              g2.setStroke(m_lineStroke);
              g.setColor(lineColor);
              drawRect(g, x, y, width, height);
              g2.setStroke(m_dashStroke);
              g.setColor(Color.WHITE);
              drawRect(g, x, y, width, height);
              g.setColor(oldColor);
          * @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int)
         private void drawRect(Graphics g, int x, int y, int width, int height)
              // extracted from Java LineBorder.paintBorder
              int i;
              /// PENDING(klobad) How/should do we support Roundtangles?
              for (i = 0; i < thickness; i++)
                   if (!roundedCorners)
                        g.drawRect(x + i, y + i, width - i - i - 1, height - i - i - 1);
                   else
                        g.drawRoundRect(
                             x + i,
                             y + i,
                             width - i - i - 1,
                             height - i - i - 1,
                             thickness,
                             thickness);
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.Frame;
    import java.awt.Image;
    import java.awt.Insets;
    import java.awt.Rectangle;
    import java.awt.Toolkit;
    import java.awt.Window;
    import java.awt.event.ActionEvent;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.awt.image.ImageProducer;
    import java.io.IOException;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.ActionMap;
    import javax.swing.InputMap;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JDialog;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.KeyStroke;
    import javax.swing.RootPaneContainer;
    * Provides static utility methods for JFrames.
    public final class FrameUtil {
        * No instances allowed. Just use the static methods.
       private FrameUtil() {
          super();
        * Sets the button that is "activated" when the escape key is used.
        * A bit like
        * SwingUtilities.getRootPane(this).setDefaultButton(myButton);
        * @param jFrame The JFrame or JDialog for which the escape key button should be set.
        * @param button The button which should be clicked automatically when the escape key is pressed.
       public static void setEscapeKeyButton(final RootPaneContainer jFrame, final JButton button) {
          addEscapeKeyAction(jFrame, new AbstractAction() {
             public void actionPerformed(final ActionEvent evt) {
                button.doClick();
        * Adds an action to the jframe's  action map that is triggered by the Escape key.
        * (If you just want to simulate a button click when the escape key is pressed then
        * use setEscapeKeyButton.)
        * A bit like
        * SwingUtilities.getRootPane(this).setDefaultButton(myButton);
        * @param jFrame The JFrame or JDialog for which the escape key action should be set.
        * @param action The action that is executred when the escape key us pressed,
       public static void addEscapeKeyAction(final RootPaneContainer jFrame, final Action action) {
          String actionKey = "escapeAction";
          KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
          // using getRootPane rather than getContentPane, content pane doesn't work in all
          // scenarios
          InputMap inputMap = (jFrame.getRootPane()).getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
          inputMap.put(escape, actionKey);
          ActionMap actionMap = (jFrame.getRootPane()).getActionMap();
          actionMap.put(actionKey, action);
        * Finds the escape action for the given frame or dialog if one was added
        * @param jFrame The JFrame or JDialog for which the escape key action should be set.
       public static Action getEscapeKeyAction(final RootPaneContainer jFrame)
          InputMap inputMap = (jFrame.getRootPane()).getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
          KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
          ActionMap actionMap = (jFrame.getRootPane()).getActionMap();
          Object actionKey = inputMap.get(escapeKeyStroke);
          Action escapeAction = null;
          if(actionKey != null)
             escapeAction = actionMap.get(actionKey);
          return escapeAction;

    From one of the MS windows books.....Going by the books, eh?
    Well, apparently MS doesn't do so with Excel... Pressing space will simply trigger an edit!
    q1) JTable should have space bar handling out of the box?
    (space, CTRL+space, shift+space)
    I have not seen this raised as a bug.A bug, no... a feature, maybe. Anyway, JTable provides most of the features needed for creating a table -- it's far from perfect..... adding support for CTRL+SPACE and SHIFT+SPACE is not all that hard to do.
    q2) Should the enter key put an editable cell under edit and
    q3) if not should the first enter key stop cell editing and a second press activate the default buttonIMHO, a good answer to your question can be found by running MS Excel and see how it's done.
    The escape key will cancel editing of a cell that is under edit.and q4, q5
    It should, as in Excel. But then, in Lotus 1-2-3, first escape will clear the edit buffer (unless it's empty) and the second escape will cancel editing. Who'is more programmatically correct (I don't know)?
    ;o)
    V.V.

  • JTable - isEditing does not work in Java 1.4

    I just upgraded jdk1.1.8 with swing 1.1.1 to Java1.4. The isEditing method doesn't work on Java1.4. When I edit a cell in the JTable and click a button , the value of the last edited cell is lost. I was able to catch the edited value by calling isEditing in jdk1.1.8 but Java 1.4 does not detect that the cell was edited. Is this a bug? Has anyone experience this problem? Is there a work around solution? Please help!!!

    Hi,
    Below is the code that I modified from a sample code in Java Tutorial. I added an OK button. The isEditing method does not trap the edit the first time but does on the second edit in JDK1.4. However, it works in JDK1.1.8. Am I doing something wrong or is this a bug? I desperately have to solve this problem to upgrade to JDK1.4. Thank-you for your help! I really appreciate it!
    import javax.swing.JTable;
    import javax.swing.table.AbstractTableModel;
    import javax.swing.JScrollPane;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    import javax.swing.JOptionPane;
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.event.*;
    import javax.swing.table.*;
    public class TableDemo extends JFrame {
    private boolean DEBUG = true;
    private JPanel buttonPanel;
    private JTable table;
    public TableDemo() {
    super("TableDemo");
    MyTableModel myModel = new MyTableModel();
    table = new JTable(myModel);
    table.setPreferredScrollableViewportSize(new Dimension(500, 70));
    //Create the scroll pane and add the table to it.
    JScrollPane scrollPane = new JScrollPane(table);
    //Add the scroll pane to this window.
    getContentPane().add(scrollPane, BorderLayout.CENTER);
    // create OK, Reset, and Cancel buttons
    createButtons();
    getContentPane().add(buttonPanel, BorderLayout.SOUTH);
    addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {
    System.exit(0);
    class MyTableModel extends AbstractTableModel {
    final String[] columnNames = {"First Name",
    "Last Name",
    "Sport",
    "# of Years",
    "Vegetarian"};
    final Object[][] data = {
    {"Mary", "Campione",
    "Snowboarding", new Integer(5), new Boolean(false)},
    {"Alison", "Huml",
    "Rowing", new Integer(3), new Boolean(true)},
    {"Kathy", "Walrath",
    "Chasing toddlers", new Integer(2), new Boolean(false)},
    {"Mark", "Andrews",
    "Speed reading", new Integer(20), new Boolean(true)},
    {"Angela", "Lih",
    "Teaching high school", new Integer(4), new Boolean(false)}
    public int getColumnCount() {
    return columnNames.length;
    public int getRowCount() {
    return data.length;
    public String getColumnName(int col) {
    return columnNames[col];
    public Object getValueAt(int row, int col) {
    return data[row][col];
    * JTable uses this method to determine the default renderer/
    * editor for each cell. If we didn't implement this method,
    * then the last column would contain text ("true"/"false"),
    * rather than a check box.
    public Class getColumnClass(int c) {
    return getValueAt(0, c).getClass();
    * Don't need to implement this method unless your table's
    * editable.
    public boolean isCellEditable(int row, int col) {
    //Note that the data/cell address is constant,
    //no matter where the cell appears onscreen.
    return true;
    * Don't need to implement this method unless your table's
    * data can change.
    public void setValueAt(Object value, int row, int col) {
    if (DEBUG) {
    System.out.println("Setting value at " + row + "," + col
    + " to " + value
    + " (an instance of "
    + value.getClass() + ")");
    if (data[0][col] instanceof Integer
    && !(value instanceof Integer)) {                 
    //With JFC/Swing 1.1 and JDK 1.2, we need to create
    //an Integer from the value; otherwise, the column
    //switches to contain Strings. Starting with v 1.3,
    //the table automatically converts value to an Integer,
    //so you only need the code in the 'else' part of this
    //'if' block.
    //XXX: See TableEditDemo.java for a better solution!!!
    try {
    data[row][col] = new Integer(value.toString());
    fireTableCellUpdated(row, col);
    } catch (NumberFormatException e) {
    JOptionPane.showMessageDialog(TableDemo.this,
    "The \"" + getColumnName(col)
    + "\" column accepts only integer values.");
    } else {
    data[row][col] = value;
    fireTableCellUpdated(row, col);
    if (DEBUG) {
    System.out.println("New value of data:");
    printDebugData();
    private void printDebugData() {
    int numRows = getRowCount();
    int numCols = getColumnCount();
    for (int i=0; i < numRows; i++) {
    System.out.print(" row " + i + ":");
    for (int j=0; j < numCols; j++) {
    System.out.print(" " + data[i][j]);
    System.out.println();
    System.out.println("--------------------------");
    private void createButtons() {
    // create panel to hold buttons
    buttonPanel = new JPanel();
    JButton buttonOK = new JButton("OK");
    buttonOK.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
    // detect if a value was edited when OK was pressed without entering
    // return or changing focus
    if (table.isEditing()) {
    System.out.println("****** Table is edited ******");
    // value was edited when OK was pressed without entering return
    // or changing focus
    int row = table.getEditingRow();
    int column = table.getEditingColumn();
    System.out.println("row: " + row + ", column: " + column + " is edited");
    buttonPanel.add(buttonOK);
    public static void main(String[] args) {
    TableDemo frame = new TableDemo();
    frame.pack();
    frame.setVisible(true);

  • JComboBox Cell Editor in JTable

    I've scouered the forums for an answer to my question, and while
    finding other valuable advice, I have yet to find an answer to my
    question. But first, a little description:
    I have a JTable consisting of 5 columns:
    col1= standard Object cell editor
    col2= JComboBox cell editor
    col3= JComboBox cell editor, values dependent on col2
    col4= JComboBox cell editor, values dependent on col3
    col5= JComboBox cell editor, values dependent on col4
    Data structure looks like this:
    col1= company object, containing vector of values for col2
    col2= lease object, containing vector of values for col3
    col3= well object, containing vector of values for col4
    col4= pump object, containing vector of values for col5
    col5= simply displayed.
    I have a JButton that adds a new row to the table via dialog, then menu
    options to add entries to the comboboxes/vectors. The kicker here is
    that everything is fine up until I've added a pump, and click the cell
    to view the entry. In my cellEditor class, I have a 'getSelected()'
    method that returns 'combobox.getSelectedIndex()'. When 'edittingStopped()'
    is thrown for any cell in this column, I get a null pointer in my
    getSelectedIndex() method of the lease combobox - only in this pump
    column. Even the part column works correctly. Code snips:
    public class MyApplication ... {
      private TableColumn leaseColumn;
      private TableColumn wellColumn;
      private TableColumn pumpColumn;
      private TableColumn partColumn;
      private LeaseDropDown leaseDropDown;
      private WellDropDown wellDropDown;
      private PumpDropDown pumpDropDown;
      private PartDropDown partDropDown;
      private int currentLease = 0;
      private int currentWell = 0;
      private int currentPump = 0;
      public MyApplication() {
        leaseColumn = pumpshopTable.getColumnModel().getColumn(1);
        leaseDropDown = new LeaseDropDown(companies);
        leaseColumn.setCellEditor(leaseDropDown);
        DefaultTableCellRenderer leaseRenderer =
          new DefaultTableCellRenderer();
        leaseRenderer.setToolTipText("Click for leases");
        leaseColumn.setCellRenderer(leaseRenderer);
        //... same for lease, well, pump, part ...
        leaseDropDown.addCellEditorListener(new CellEditorListener() {
          public void editingCanceled(ChangeEvent e) {
          } // end editingCanceled method
          public void editingStopped(ChangeEvent e) {
            updateCells();
          } // end editingStopped method
        }); // end addCellEditorListener inner class
        //.... same inner class for well, pump, part ...
      } // end MyApplication constructor
      public void updateCells() {
        currentLease = leaseDropDown.getSelectedLease();
        //... get current well, pump, part ...
        leaseDropDown = new LeaseDropDown(companies); // companies=Vector,col1
        leaseColumn.setCellEditor(leaseDropDown);
        //... same for lease, well, pump and part columns ...
      } // end updateCells method
    } // end MyApplication class
    public class LeaseDropDown extends AbstractCellEditor
        implements TableCellEditor {
      private Vector companiesVector;
      private JComboBox leaseList;
      public LeaseDropDown(Vector cVector) {
        companiesVector = cVector;     
      } // end LeaseDropDown constructor
      public Component getTableCellEditorComponent(JTable table,
          Object value, boolean isSelected, int rowIndex, int vColIndex) {
        Company thisCompany = (Company) companiesVector.get(rowIndex);
        Vector leasesVector = (Vector) thisCompany.getLeases();
        leaseList = new JComboBox(leasesVector);
        return leaseList;
      } // end getTableCellEditorComponent method
      public Object getCellEditorValue() {
        return leaseList.getSelectedItem();
      } // end getCellEditorValue method
      public int getSelectedLease() {
        JOptionPane.showInputDialog("Selected lease is: " +
          leaseList.getSelectedIndex());
        return leaseList.getSelectedIndex();          
      } // end getSelectedLease method
    } // end LeaseDropDown class... LeaseDropDown can be extrapolated to well, pump, and part,
    handing well the selected lease, handing pump the selected
    lease and well, handing part the selected lease, well and pump.
    I guess my question is how do I get the selected comboboxitem (I'd
    settle for the entire combobox if there's no other way) to fill in the
    next column? Why does the way I have it now work for the first 2 combobox
    columns and not the third?

    I'll try to provide more details.
    I use a JComboBox implementation as a cell in a JTable. The CombBox is editable . This is what I get when I try to type in something.
    java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location
    at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:1507)
    at java.awt.Component.getLocationOnScreen(Component.java:1481)
    at javax.swing.JPopupMenu.show(JPopupMenu.java:921)
    at javax.swing.plaf.basic.BasicComboPopup.show(BasicComboPopup.java:177)
    at javax.swing.plaf.basic.BasicComboBoxUI.setPopupVisible(BasicComboBoxUI.java:927)
    at javax.swing.JComboBox.setPopupVisible(JComboBox.java:790)
    at javax.swing.JComboBox.showPopup(JComboBox.java:775)
    I read some related bugs on the sun site but I am not sure if this is a bug and if it is then has it been fixed or work-around provided.
    any insights ??

Maybe you are looking for

  • How to open a new window on click of a button, for printing stuffs in adf

    Hi ADF Experts, Jdeveloper 11.1.1.7.0 version. I am having a Print Button. On click of this I want to open a new window? how is it possible. Note it should not be a popup or panelwindow. Please suggest. Thanks, Roy

  • Question in Mapping- (N:1 Mapping)

    Dear friends, Hope everybody is doing great. I am doing file to Idoc scenario. I want to map N elements from source side to target one element under particular node. When I duplicate the node on the target side and do mapping, it pops up below error

  • A page that looked fine in the last version does not look correct at all in 3.6.3, although it looks fine in both IE6 and IE8

    This is a log-in page, which points to our email registration software's server (Tikit's Emerge Reaction Server). If you open in it in the last version of FF, it looks as expected. The page renders fine in IE6 and IE8, and all our other website pages

  • Distributed objects

    Hi, I have a doubt while going through mastering ejb book. can any one clarify me abt this of exactly what happening internally. In the implicit middleware: " when a client call a distributed object, the client calls a stub which is a client-side pro

  • Dead power supply fan

    The power supply fan on my 2002 Quicksilver Dual 1gig G4 just quit and I am obviously concerned about the ramifications. I've read a few posts in different places about fan replacements but not sure I am up to that. Does anyone have any advice / caut