/* Useful imports */ import edu.neu.ccs.*; import edu.neu.ccs.gui.*; import edu.neu.ccs.codec.*; import edu.neu.ccs.console.*; import edu.neu.ccs.filter.*; import edu.neu.ccs.jpf.*; import edu.neu.ccs.parser.*; import edu.neu.ccs.pedagogy.*; import edu.neu.ccs.quick.*; import edu.neu.ccs.util.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.font.*; import java.awt.image.*; import javax.swing.*; import javax.swing.border.*; import java.io.*; import java.util.*; import java.math.*; import java.beans.*; import java.lang.reflect.*; import java.net.URL; import java.util.regex.*; import java.text.ParseException; public class SudokuBlock extends BufferedPanel { // Static definitions // /** The normal background color. */ private static final Color backcolor = Colors.bisque; /** The background color when the block is frozen */ private static final Color frozencolor = Colors.lightblue; /** The border color for a selected block. */ private static final Color bordercolor = Colors.red; /** The size of the box for the digits or hints. */ private static final int boxsize = 48; /** The stroke thickness for the selection rectangle. */ private static final int gapsize = 2; /** The size of the full cell. */ private static final int cellsize = boxsize + 4 * gapsize; /** The large font size for the regular digits. */ private static final int largefontsize = boxsize; /** The large font for the regular digits. */ private static final Font largefont = new Font("serif", Font.BOLD, largefontsize); /** The 9 regular digits as paintables. */ private static TextPaintable[] digits = new TextPaintable[10]; /** Initialization of the 9 regular digits as paintables. */ static { // find the height of a typical digit in the large font TextPaintable p = new TextPaintable ("0", largefont, Colors.black, TextBounds.TIGHT, TextAnchor.CENTER_BASELINE, 0, 0); Rectangle2D textbounds = p.getBounds2D(); int digitheight = (int) textbounds.getHeight(); // find the location of the center baseline for all digits int x = cellsize / 2; int y = (cellsize + digitheight) / 2; // create digits 1-9 // omit digit 0 since it will never be used for (int i = 1; i <= 9; i++) { digits[i] = new TextPaintable (i + "", largefont, Colors.black, TextBounds.TIGHT, TextAnchor.CENTER_BASELINE, x, y); } } /** The small font size for the hint digits. */ private static final int smallfontsize = (boxsize / 3); /** The small font for the hint digits. */ private static final Font smallfont = new Font("serif", Font.BOLD, smallfontsize); /** The 9 hint digits as paintables. */ private static TextPaintable[] hintdigits = new TextPaintable[10]; /** Initialization of the 9 hint digits as paintables. */ static { // find the height of a typical digit in the small font TextPaintable p = new TextPaintable ("0", smallfont, Colors.black, TextBounds.TIGHT, TextAnchor.CENTER_BASELINE, 0, 0); Rectangle2D textbounds = p.getBounds2D(); int digitheight = (int) textbounds.getHeight(); int[] xpos = { cellsize / 5, cellsize / 2, cellsize - (cellsize / 5) }; int[] ypos = { 3 * gapsize + digitheight, (cellsize + digitheight) / 2, cellsize - (3 * gapsize) }; // create digits 1-9 // omit digit 0 since it will never be used for (int i = 1; i <= 9; i++) { int row = (i - 1) / 3; int col = (i - 1) % 3; int x = xpos[col]; int y = ypos[row]; hintdigits[i] = new TextPaintable (i + "", smallfont, Colors.black, TextBounds.TIGHT, TextAnchor.CENTER_BASELINE, x, y); } } /** The rectangle for selection of a cell. */ private static Rectangle2D border = null; /** The stroke for selection of a cell. */ private static Stroke stroke = null; /** Initialization for selection objects. */ static { int a = gapsize / 2; int b = gapsize; border = new Rectangle2D.Double(a, a, cellsize - b, cellsize - b); stroke = new BasicStroke(b); } // Member definitions // /** The Sudoku GUI object. */ private SudokuBase GUI = null; /** The Sudoku model object. */ private SudokuModel model = null; /** The Sudoku table object. */ private SudokuTable table = null; /** The row of this block in the table. */ private int row = 0; /** The col of this block in the table. */ private int col = 0; /** The cell position. */ private CellPosition position = null; /** Is this block the selected block for user key input? */ private boolean selected = false; /** Is this block active or frozen? */ private boolean active = true; /** The inner panel that can do key input listening. */ private DisplayPanel inner = null; /** The key listener adapter. */ private KeyActionAdapter keyadapter = null; /** The mouse listener adapter. */ private MouseActionAdapter mouseadapter = null; /** The key action object for key pressed events. */ private KeyAction keyPressed = new KeyAction() { public void keyActionPerformed(KeyEvent kevt) { handleKeyPressed(kevt); } }; /** The mouse action object for mouse pressed events. */ private MouseAction mousePressed = new MouseAction() { public void mouseActionPerformed(MouseEvent mevt) { handleMousePressed(mevt); } }; // Constructors // /** The default constructor useful only for simple tests. */ public SudokuBlock() { super(cellsize, cellsize, backcolor); initializeSudokuBlock(); } /** * The constructor that provides the table and the * row and col position of this block in the table. */ public SudokuBlock (SudokuTable table, int row, int col) { super(cellsize, cellsize, backcolor); this.table = table; if (table != null) model = table.getSudokuModel(); if (model != null) GUI = model.getSoduku(); this.row = row; this.col = col; initializeSudokuBlock(); } // Methods // /** Complete the initialization for this block. */ private void initializeSudokuBlock() { position = new CellPosition(row, col); inner = getInnerPanel(); keyadapter = getKeyActionAdapter(); initializeKeys(); mouseadapter = getMouseActionAdapter(); initializeMouse(); } /** * The key action method for key pressed events. * * Keys 1 to 9 enter that digit in the cell and * inform the model and the GUI. * * Keys 0 or spacebar clear the cell and inform * the model and the GUI. * * Arrow keys shift the selection from this block * to the nearest block in that direction that is * active. If no such block exists then nothing * is changed. */ private void handleKeyPressed(KeyEvent kevt) { if (! (selected && active)) return; int code = kevt.getKeyCode(); // first check for digits or space bar int digit = -1; switch(code) { case KeyEvent.VK_1: case KeyEvent.VK_2: case KeyEvent.VK_3: case KeyEvent.VK_4: case KeyEvent.VK_5: case KeyEvent.VK_6: case KeyEvent.VK_7: case KeyEvent.VK_8: case KeyEvent.VK_9: { digit = code - KeyEvent.VK_0; break; } case KeyEvent.VK_0: case KeyEvent.VK_SPACE: digit = 0; break; default: break; } if (digit >= 0) { paintDigit(digit); if (model != null) model.setCellData(row, col, digit); if (GUI != null) GUI.updateHints(); return; } // now check for arrow keys if (table == null) return; int delta_r = 0; int delta_c = 0; switch(code) { case KeyEvent.VK_LEFT: delta_c = -1; break; case KeyEvent.VK_RIGHT: delta_c = 1; break; case KeyEvent.VK_UP: delta_r = -1; break; case KeyEvent.VK_DOWN: delta_r = 1; break; default: return; } int r = row + delta_r; int c = col + delta_c; while ((1 <= r) && (r <= 9) && (1 <= c) && (c <= 9)) { SudokuBlock block = table.getSudokuBlock(r, c); if (block.isActive()) { this.unselect(); block.select(); return; } r += delta_r; c += delta_c; } } /** Initialize key listening for the inner panel. */ private void initializeKeys() { // enable the inner panel to gain the focus of attention inner.setFocusable(true); // attach the pressed action object to the key adapter keyadapter.addKeyPressedAction(keyPressed); } /** Set or reset key listening to the inner panel. */ public void resetKeyFocus() { inner.requestFocusInWindow(); } /** Initialize mouse listening. */ private void initializeMouse() { mouseadapter.addMousePressedAction(mousePressed); } /** * The mouse action method for mouse pressed events. * * If this block is active then a mouse press makes * this block the selected block. */ private void handleMousePressed(MouseEvent mevt) { if (! active) return; if (table != null) table.unselectBlocks(); select(); } /** Returns the row of the block. */ public int getRow() { return row; } /** Returns the col of the block. */ public int getCol() { return col; } /** * Returns the cell position of the block. * * The cell position returned should not be modified. */ public CellPosition getCellPosition() { return position; } /** Select or unselect this block for key input. */ public void setSelect(boolean select) { if (select) resetKeyFocus(); if (selected != select) { selected = select; repaintBlock(); } } /** Select this block for key input. */ public void select() { setSelect(true); } /** Unselect this block for key input. */ public void unselect() { setSelect(false); } /** Is this block selected for key input? */ public boolean isSelected() { return selected; } /** Set this block to active or frozen. */ public void setActive(boolean active) { if (this.active == active) return; this.active = active; Color color = active ? backcolor : frozencolor; this.setBufferBackground(color); if (!active) { unselect(); } repaintBlock(); } /** Set this block to frozen. */ public void freeze() { setActive(false); } /** Set this block to active. */ public void thaw() { setActive(true); } /** Is this block active? */ public boolean isActive() { return active; } /** Is this block frozen? */ public boolean isFrozen() { return !active; } /** Clear the block of regular digits and hint digits. */ public void clearBlock() { clearSequence(); repaintBlock(); } /** * Paint a regular digit between 1 and 9. * * If the given digit is not between 1 and 9 then clear * the block. */ public void paintDigit(int digit) { clearSequence(); if ((digit >= 1) && (digit <= 9)) addPaintable(digits[digit]); repaintBlock(); } /** * Paint the small hint digits corresponding to the data * items in the given hint that are true. */ public void paintHint(SudokuHint hint) { clearSequence(); if (hint != null) for (int digit = 1; digit <= 9; digit++) if (hint.getHint(digit)) addPaintable(hintdigits[digit]); repaintBlock(); } /** * After refreshing the selection state border, * repaint the block. */ public void repaintBlock() { paintSelectState(); repaint(); } /** Refresh the selection state border. */ private void paintSelectState() { clearPanel(); if (selected) { Graphics2D g = getBufferGraphics(); g.setPaint(bordercolor); g.setStroke(stroke); g.draw(border); } } /** Trivial test code. */ public static void main(String[] args) { SudokuBlock block1 = new SudokuBlock(); SudokuBlock block2 = new SudokuBlock(); SudokuHint hint = new SudokuHint(); hint.setAll(true); block1.paintHint(hint); block2.paintHint(hint); Object[] stuff = { block1, block2 }; TablePanel panel = new TablePanel(stuff, HORIZONTAL, 5, 5, CENTER); panel.setBackground(Colors.black); panel.frame("Two Sudoku Blocks"); } }