3

I have a JTable displaying rows from an SQL database. The table is relatively small (only 4 columns and up to 1000 rows).

I would like to give the user the opportunity to edit any cells in the table but want to avoid restricting it so much so that they must use an edit dialog box (this makes for far easier error checking and validation but is less intuitive)

I have tried a few different ways of controlling edit selections using the valueChanged method of my JTable but haven't had much luck.

I would like each row to be edited and written to the database at the conclusion of editing. I would like that once a cell has been clicked to start the editing of that row, no other rows can be selected until the user has finished editing the row (other rows are grayed out). After editing each cell and pressing enter, the edit selection should jump to the next column in the same row.

Can anyone give pointers on how I can achieve this?

//  Create table with database data
   table = new JTable(new DefaultTableModel(data, columnNames)) {
        public Class getColumnClass(int column) {
            for (int row = 0; row < getRowCount(); row++) {
                Object o = getValueAt(row, column);
                if (o != null){
                    return o.getClass();
                }
            }
            return Object.class;
        }

        @Override
        public boolean isCellEditable(int row, int col){
            return true; 
        }

        @Override
        public boolean editCellAt(int row, int column) {
            boolean ans = super.editCellAt(row, column);
            if (ans) { 
                Component editor = table.getEditorComponent();
                editor.requestFocusInWindow();
            }
            return ans;
        }

        @Override
        public void valueChanged(ListSelectionEvent source) {
            super.valueChanged(source);
            if (table!=null)
                table.changeSelection(getSelectedRow(), getSelectedColumn()+1, false, false);
        }

    };

Edit - custom cell editor with table pointer seems to be a start

public class ExchangeTableCellEditor  extends AbstractCellEditor implements TableCellEditor {

private JTable table;

JComponent component = new JTextField();

public ExchangeTableCellEditor(JTable table) {
    this.table = table;
}

public boolean stopCellEditing() {
    boolean ans = super.stopCellEditing();
    //now we want to increment the cell count
    table.editCellAt(table.getSelectedRow(), table.getSelectedColumn()+1);
    return ans;
}

@Override
public void cancelCellEditing() {
    //do nothing... must accept cell changes 
}

@Override
public Object getCellEditorValue() {
    return ((JTextField)component).getText();
}

@Override
public Component getTableCellEditorComponent(JTable arg0, Object value,
        boolean arg2, int arg3, int arg4) {
    ((JTextField)component).setText((String)value);
    return component;
}

}

jmc
  • 620
  • 14
  • 24
  • 1
    Please edit your question to include an [sscce](http://sscce.org/) that exhibits any problem(s) you encounter. – trashgod Feb 06 '12 at 04:49
  • Edited with an example of how I am trying to change the selection when editing. Thanks – jmc Feb 06 '12 at 05:20
  • don't make a CellEditor go clever - never-ever! Here specifically it means to _not_ restart editing, that'll confuse all internal updating... – kleopatra Feb 06 '12 at 09:18
  • btw, the implementation is invalid - it must notify its listeners when editing is terminated for editor-internal reasons (f.i. when the user commits the value) See DefaultCellEditor source code for examples – kleopatra Feb 06 '12 at 09:20

2 Answers2

4

The default renderer and editor is typically adequate for most data types, but you can define custom renderers and editors as needed.

Addendum: I'm unfamiliar with the approach shown in your fragment. Instead, register a TableModelListener with your model, as shown below, and update the database with whatever granularity is warranted. See also How to Use Tables: Listening for Data Changes.

Addendum: @kleopatra is correct about your TableCellEditor. One convenient way to notify listeners is to invoke the super implementation, as shown here. Note that the delegate invokes fireEditingStopped().

/** @see https://stackoverflow.com/questions/9155596 */
public class NewJavaGUI extends JPanel {

    private final JTable table;

    public NewJavaGUI() {
        String[] colNames = {"C1", "C2", "C3"};
        DefaultTableModel model = new DefaultTableModel(colNames, 0) {

            @Override
            public boolean isCellEditable(int row, int col) {
                // return your actual criteria
                return true;
            }

            @Override
            public Class getColumnClass(int col) {
                // return your actual type tokens
                return getValueAt(0, col).getClass();
            }
        };
        // Add data; note auto-boxing
        model.addRow(new Object[]{"A1", "A2", 42});
        model.addRow(new Object[]{"B1", "B2", 42d});
        model.addTableModelListener(new TableModelListener() {

            @Override
            public void tableChanged(TableModelEvent e) {
                // DML as indicated
            }
        });
        table = new JTable(model);
        this.add(table);
    }

    private void display() {
        JFrame f = new JFrame("NewJavaGUI");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new NewJavaGUI().display();
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for the answer - link and code was enough to get in the right direction. have edited my question with custom cell editor that seems to start the solution. – jmc Feb 06 '12 at 08:42
  • Monday is nitpicking day :-) The Model Rules - don't re/implement any of its in methods in the view, especially not unconditionally allow editing - not even in an example, newbies tend to remember those ... – kleopatra Feb 06 '12 at 09:47
  • @kleopatra: Excellent nit to pick; carelessness on my part; updated. Thanks! – trashgod Feb 06 '12 at 14:07
  • insistive me still not satisfied: the Rule holds for _all_ methods of the model. The view must not second-guess the model's intentions (what if the model intentionally returned the widest possible type because it allows and in fact contains a variety of icons, strings, numbers...)? While this is an example only - which we would never code in in any real-world environment - the mis-placement is spreading, see f.i http://stackoverflow.com/questions/9239270/how-to-change-jtable-image-from-one-column-to-another-column-using-mouse-click-e – kleopatra Feb 11 '12 at 11:07
  • @kleopatra: D'oh, _more_ carelessness on my part; thank you for taking the time to review and comment. – trashgod Feb 11 '12 at 12:06
-1

The behaviour you mention can be achieved by forcing your table to start editing again.

First make sure you now yourRow and Column and that you add your own tablecelleditor that extands from the AbstractCellEditor then add this to your stopCellEditing method:

EventQueue.invokeLater(new Runnable()
    {

      public void run()
      {
        yourTable.editCellAt( yourRow, yourColumn+1);
      }
    });
Peter
  • 5,728
  • 20
  • 23
  • 1
    -1 it's wrong to let the editor interfere with the table's state - it's sole job is to allow editing a value and report back to its listeners when it is ready. Then the _listener_ (mostly the table itself) will grab the new value and do with it what is needed – kleopatra Feb 06 '12 at 09:41
  • the editor is not affecting the state. Its just saying to the table to restart editing. – Peter Feb 06 '12 at 11:07
  • 1
    which _is_ changing its state ;-) That's _not_ an editor's job. – kleopatra Feb 06 '12 at 12:17