1

My application's JFrame logic looks like:

public Table() {
    super("Chess");
    thisFrame = this;
    tableMenuBar = new JMenuBar();
    populateMenuBar();
    setJMenuBar(tableMenuBar);
    getContentPane().setLayout(new BorderLayout());
    chessBoard = new Board(new StandardBoardConfigurator());
    gamePanel = new GameHistoryPanel();
    chatPanel = new ChatPanel();
    takenPiecesPanel = new TakenPiecesPanel();
    boardPanel = new BoardPanel(chessBoard);
    gameProgress = 1;
    highlightLegalMoves = true;
    moveLog = new ArrayList<Move>();
    gameOver = false;
    getContentPane().add(takenPiecesPanel, BorderLayout.WEST);
    getContentPane().add(boardPanel, BorderLayout.CENTER);
    getContentPane().add(gamePanel, BorderLayout.EAST);
    getContentPane().add(chatPanel, BorderLayout.SOUTH);
    // Make sure we have nice window decorations.
    setDefaultLookAndFeelDecorated(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(OUTER_FRAME_DIMENSION);
    pack();
    setVisible(true);
}

and the JPanel containing the JTable is 'GameHistoryPanel', which has this as it's constructor logic:

    public GameHistoryPanel() {
        this.setLayout(new BorderLayout());
        this.model = new DataModel();
        this.table = new JTable(model);
        this.table.setRowHeight(15);
        final JScrollPane scrollPane = new JScrollPane(this.table);
        scrollPane.setColumnHeaderView(table.getTableHeader());
        scrollPane.setPreferredSize(HISTORY_PANEL_DIMENSION);
        this.add(scrollPane, BorderLayout.CENTER);
        this.currentRow = 0;
        this.currentColumn = 0;
        this.setVisible(true);
    }

The GamePanel has the following update routine which calls setValueAt whenever a move is made:

    public void increment(final Board board,
                          final Move move) {
        this.model.setValueAt(move, currentRow, currentColumn);
        if(board.currentPlayer().getAlliance() == Alliance.WHITE) {
            currentColumn++;
        } else if (board.currentPlayer().getAlliance() == Alliance.BLACK) {
            currentRow++;
            currentColumn = 0;
        }
    }

Upon launching the game, the GamePanel is grayed out. When I resize it vertically, it suddenly appears with all of the correct values. I don't understand why. I did notice that resizing causes getValueAt to be invoked a bunch of times. Can someone help me understand this?

EDIT 2: If I add this line:

        this.model.fireTableDataChanged();

to increment, it seems to work fine. I'm wholly confused...

EDIT: Here is my TableModel class:

    private static class DataModel extends AbstractTableModel {

        private static final String[] names = {"White", "Black"};
        private final List<Row> values;

        public DataModel() {
            values = new ArrayList<Row>();
        }

        @Override
        public int getRowCount() {
            return values.size();
        }

        @Override
        public int getColumnCount() {
            return names.length;
        }

        @Override
        public Object getValueAt(int row, int col) {
            final Row currentRow = values.get(row);
            if(col == 0) {
                return currentRow.getWhiteMove();
            } else if (col == 1) {
                return currentRow.getBlackMove();
            }
            return null;
        }

        @Override
        public void setValueAt(Object aValue, int row, int col) {
            final Row currentRow;
            if(values.size() <= row) {
                currentRow = new Row();
                values.add(currentRow);
            } else {
                currentRow = values.get(row);
            }
            if(col == 0) {
                currentRow.setWhiteMove((Move) aValue);
            } else  if(col == 1) {
                currentRow.setBlackMove((Move)aValue);
            }
            this.fireTableCellUpdated(row, col);
        }

        @Override
        public Class<?> getColumnClass(int col) {
            return Move.class;
        }

        @Override
        public String getColumnName(int col) {
            return names[col];
        }
    }
}
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124

2 Answers2

2

If I add this line, this.model.fireTableDataChanged(), to increment(), it seems to work fine.

Your implementation of setValueAt() in DataMdel is flawed in that it may add instances of Row to the model, while only invoking fireTableCellUpdated() for a single row and col. You need to fire the event appropriate to the actual modification.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for the answer. I suspected something like this. I haven't figured out what needs to be modified yet. I'll mark this as the correct answer as soon as I can sort out how to fix the impl. – Amir Afghani Dec 14 '12 at 16:29
  • @AmirAfghani: Ping me if you update your question. I've found that creating an [sscce](http://sscce.org/) makes it easier to study such problems in isolation. You may find it helpful to examine my previous answers in the [tag:JTable] tag, many of which contain relevant comments from user kleopatra. – trashgod Dec 14 '12 at 20:41
1

Try to call

.invalidate();
.repaint();

on either JPanel, JScrollPane, JTable

My guess is that adding as last lines of increment()

JTable.invalidate();
JTable.repaint();

should be sufficient. If not, please, check for each component.

Nikolay Kuznetsov
  • 9,467
  • 12
  • 55
  • 101