//============================================================================= // Copyright 2006-2010 Daniel W. Dyer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //============================================================================= package org.uncommons.watchmaker.examples.sudoku; import java.awt.Color; import java.awt.Component; import java.awt.Font; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.border.Border; import javax.swing.table.DefaultTableCellRenderer; import org.uncommons.swing.ConfigurableLineBorder; /** * Renders Sudoku cells in a JTable. As well as displaying the cell value, * grid lines are drawn to conform to normal Sudoku conventions and incorrect * solutions have their invalid cells colour-coded. * @author Daniel Dyer */ class SudokuCellRenderer extends DefaultTableCellRenderer { private static final Font VARIABLE_CELL_FONT = new Font("Monospaced", Font.PLAIN, 32); private static final Font FIXED_CELL_FONT = new Font("Monospaced", Font.BOLD, 34); private static final Color VARIABLE_TEXT_COLOUR = Color.DARK_GRAY; private static final Color FIXED_TEXT_COLOUR = Color.BLACK; private static final Color[] CONFLICT_COLOURS = {Color.WHITE, Color.YELLOW, Color.ORANGE, Color.RED}; private static final Border TOP_BORDER = new ConfigurableLineBorder(true, false, false, false, 1); private static final Border BOTTOM_BORDER = new ConfigurableLineBorder(false, false, true, false, 1); private static final Border LEFT_BORDER = new ConfigurableLineBorder(false, true, false, false, 1); private static final Border RIGHT_BORDER = new ConfigurableLineBorder(false, false, false, true, 1); private static final Border TOP_LEFT_BORDER = new ConfigurableLineBorder(true, true, false, false, 1); private static final Border TOP_RIGHT_BORDER = new ConfigurableLineBorder(true, false, false, true, 1); private static final Border BOTTOM_LEFT_BORDER = new ConfigurableLineBorder(false, true, true, false, 1); private static final Border BOTTOM_RIGHT_BORDER = new ConfigurableLineBorder(false, false, true, true, 1); SudokuCellRenderer() { setHorizontalAlignment(JLabel.CENTER); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component component = super.getTableCellRendererComponent(table, value, false, false, row, column); SudokuTableModel model = (SudokuTableModel) table.getModel(); Sudoku sudoku = model.getSudoku(); if (sudoku != null) { if (sudoku.isFixed(row, column)) { component.setFont(FIXED_CELL_FONT); component.setForeground(FIXED_TEXT_COLOUR); } else { component.setFont(VARIABLE_CELL_FONT); component.setForeground(VARIABLE_TEXT_COLOUR); } int conflicts = 0; // Calculate conflicts in columns (there should be no conflicts in rows // because of the constraints enforced by the evolutionary operators). for (int i = 0; i < Sudoku.SIZE; i++) { if (i != row && model.getValueAt(i, column).equals(value)) { ++conflicts; } } // Calculate conflicts in sub-grid. int band = row / 3; int bandStart = band * 3; int stack = column / 3; int stackStart = stack * 3; for (int i = bandStart; i < bandStart + 3; i++) { for (int j = stackStart; j < stackStart + 3; j++) { if (i != row && j != column && model.getValueAt(i, j).equals(value)) { ++conflicts; } } } // Color the cell based on how "wrong" it is. component.setBackground(CONFLICT_COLOURS[Math.min(conflicts, CONFLICT_COLOURS.length - 1)]); } else { // If the model is in puzzle mode, then all non-null cells are 'givens'. component.setFont(FIXED_CELL_FONT); component.setForeground(FIXED_TEXT_COLOUR); } ((JComponent) component).setBorder(getBorder(row, column)); return component; } /** * Get appropriate border for cell based on its position in the grid. */ private Border getBorder(int row, int column) { if (row % 3 == 2) { switch (column % 3) { case 2: return BOTTOM_RIGHT_BORDER; case 0: return BOTTOM_LEFT_BORDER; default: return BOTTOM_BORDER; } } else if (row % 3 == 0) { switch (column % 3) { case 2: return TOP_RIGHT_BORDER; case 0: return TOP_LEFT_BORDER; default: return TOP_BORDER; } } switch (column % 3) { case 2: return RIGHT_BORDER; case 0: return LEFT_BORDER; default: return null; } } }