diff --git a/src/App.java b/src/App.java index 254f12a..e792983 100644 --- a/src/App.java +++ b/src/App.java @@ -1,4 +1,5 @@ // GUI imports +import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.UIManager; import java.awt.BorderLayout; @@ -18,6 +19,7 @@ public class App extends JFrame { // Data fields private Settings s; + private SudokuChecker sc; /** * Create a new App object, initializing the GUI and internal logic. @@ -27,8 +29,12 @@ public class App extends JFrame { s = new Settings(); initSetup(); nav = new Nav(s, false); - board = new Board(s, nav.getLoadedGrid()); + sc = new SudokuChecker(nav.getLoadedGrid()); + board = new Board(s, nav.getLoadedGrid(), sc); + nav.setBoard(board); + nav.setChecker(sc); + add(createApp()); } @@ -69,6 +75,7 @@ public class App extends JFrame { */ private Root createApp() { Root root = new Root(s); + root.setLayout(new BoxLayout(root, BoxLayout.Y_AXIS)); root.add(nav); root.add(board); diff --git a/src/gui/Board.java b/src/gui/Board.java index ee1b030..23ce8e0 100644 --- a/src/gui/Board.java +++ b/src/gui/Board.java @@ -4,9 +4,12 @@ import java.awt.GridLayout; import java.awt.LayoutManager; import javax.swing.JPanel; +import javax.swing.border.LineBorder; import javax.swing.JLabel; import gui.backend.Settings; +import gui.backend.SudokuChecker; +import gui.backend.Theme; /** * The Board class represents the Sudoku board in the GUI. @@ -22,6 +25,7 @@ import gui.backend.Settings; public class Board extends JPanel { private Settings s; private Cell[][] grid; + private SudokuChecker sc; private Cell selected; /** @@ -29,10 +33,11 @@ public class Board extends JPanel { * * @param s */ - public Board(Settings s, Cell[][] grid) { + public Board(Settings s, Cell[][] grid, SudokuChecker sc) { super(new GridLayout(9, 9)); this.s = s; this.grid = grid; + this.sc = sc; style(); createBoard(); } @@ -65,13 +70,19 @@ public class Board extends JPanel { * Set up the Board Panel with the appropriate styling. */ private void style() { - // TODO: Style the Board Panel. + setBackground(s.getTheme().getPrimaryBackground()); + setForeground(s.getTheme().getPrimaryText()); } + /** + * Create the Board Panel with the appropriate cells. + */ private void createBoard() { + if(s.getAutoFillNotes()) grid = sc.getPossibleValues(grid); + for(int i = 0; i < 9; i++) { for(int j = 0; j < 9; j++) { - CellGUI cell = new CellGUI(grid[i][j], s.getCellGUIStartMode()); + CellGUI cell = new CellGUI(grid[i][j], s.getCellGUIStartMode() || s.getAutoFillNotes(), s.getTheme()); add(cell); } } @@ -81,14 +92,15 @@ public class Board extends JPanel { * GUI representation of a single cell in the Sudoku grid. */ private class CellGUI extends JPanel { - private static GridLayout noteLayout = new GridLayout(3, 3); - private static GridLayout valueLayout = new GridLayout(1, 1); + private GridLayout noteLayout = new GridLayout(3, 3); + private GridLayout valueLayout = new GridLayout(1, 1); + private Theme theme; private Cell cell; private JLabel valueLabel; - private JLabel[] notesLabels; + private Note[] notesLabels = new Note[9]; private boolean notes; // true for notes, false for value - private boolean startInNotesOrValueMode; + private boolean cellGUIStartMode; /** * Create a new CellGUI object with the given single cell. @@ -98,24 +110,18 @@ public class Board extends JPanel { * @param cell * @param startInNotesOrValueMode */ - public CellGUI(Cell cell, boolean startInNotesOrValueMode) { + public CellGUI(Cell cell, boolean notes, Theme theme) { super(); this.cell = cell; - this.startInNotesOrValueMode = startInNotesOrValueMode; + this.notes = notes; + this.theme = theme; + style(); // Populate the cell with the appropriate value or notes. if(cell.getValue() == 0) { - generateNotes(); - - if(startInNotesOrValueMode) { - setLayout(noteLayout); - notes = true; - } else { - setLayout(valueLayout); - notes = false; - valueLabel = new JLabel(""); - add(valueLabel); - } + notes = !notes; + valueLabel = new JLabel(""); + setNoteMode(); } else { valueLabel = new JLabel(Integer.toString(cell.getValue())); add(valueLabel); @@ -131,6 +137,25 @@ public class Board extends JPanel { return cell.getValue(); } + /** + * Set the value of the underlying Cell object. + * + * @param value + */ + public void setValue(int value) { + cell.setValue(value); + valueLabel.setText(Integer.toString(value)); + } + + /** + * Set the possible values of the underlying Cell object. + * + * @param value + */ + public void setPossibleValues(int[] values) { + cell.setPossibleValues(values); + } + /** * Add a possible value to the cell. * @@ -163,44 +188,54 @@ public class Board extends JPanel { * layout of the cell. */ public void setNoteMode() { + removeAll(); if(notes) { - removeAll(); setLayout(valueLayout); - repaint(); - revalidate(); add(valueLabel); } else { - removeAll(); setLayout(noteLayout); - repaint(); - revalidate(); generateNotes(); + for(int i = 0; i < 9; i++) + add(notesLabels[i]); } + repaint(); + revalidate(); notes = !notes; } + /** + * Style the cell with the appropriate colors from the theme. + */ + private void style() { + setBackground(theme.getPrimaryBackground()); + setForeground(theme.getPrimaryText()); + setBorder(new LineBorder(theme.getPrimaryBorder(), 2)); + } + /** * Create the GUI components for the cell. */ private void generateNotes() { if(cell.getPossibleValues().length == 0) { - notesLabels = new JLabel[9]; + for(int i = 0; i < 9; i++) { + notesLabels[i] = new Note("", theme); + add(notesLabels[i]); + } return; } + // Add the noted possible values to the cell. - int index = 0; for(int i = 1; i <= 9; i++) { - if(cell.getPossibleValues()[index] == i) { - notesLabels[index] = new JLabel(Integer.toString(i)); - index++; - } else { - notesLabels[index] = new JLabel(""); - } - add(notesLabels[index]); + if(cell.getPossibleValues()[i - 1] == i) + notesLabels[i - 1] = new Note(Integer.toString(i), theme); + else + notesLabels[i - 1] = new Note("", theme); + + add(notesLabels[i - 1]); } } } diff --git a/src/gui/Cell.java b/src/gui/Cell.java index 5f72672..d8c5539 100644 --- a/src/gui/Cell.java +++ b/src/gui/Cell.java @@ -11,6 +11,8 @@ public class Cell { private int col; private int box; private int value; + private boolean initValue; + private List possibleValues; /** @@ -41,6 +43,8 @@ public class Cell { this.row = row; this.col = col; this.value = value; + initValue = value != 0; + setBox(); possibleValues = new List(); } @@ -69,6 +73,8 @@ public class Cell { * @param value */ public void setValue(int value) { + if(initValue) return; + possibleValues.clear(); this.value = value; } @@ -99,6 +105,8 @@ public class Cell { * @param value */ public void addPossibleValue(int value) { + if(initValue) return; + if(possibleValues.contains(value)) { return; } else if(value < 1 || value > 9) { @@ -114,6 +122,8 @@ public class Cell { * @param possibleValues */ public void setPossibleValues(int[] possibleValues) { + if(initValue) return; + this.possibleValues = new List(possibleValues); } @@ -140,7 +150,7 @@ public class Cell { public int[] getPossibleValues() { return possibleValues.toArray(); } - + /** * Given the cell's row and column, set the box number for the cell. */ diff --git a/src/gui/Nav.java b/src/gui/Nav.java index 8d8f63f..f15d0d4 100644 --- a/src/gui/Nav.java +++ b/src/gui/Nav.java @@ -6,6 +6,7 @@ import java.io.FileNotFoundException; import javax.swing.JPanel; import gui.backend.Settings; +import gui.backend.SudokuChecker; import javax.swing.JLabel; import javax.swing.JButton; @@ -26,6 +27,7 @@ import javax.swing.JButton; */ public class Nav extends JPanel { private Settings s; + private SudokuChecker sc; private File f; private Board b; private Cell[][] grid; @@ -111,6 +113,22 @@ public class Nav extends JPanel { this.b = b; } + /** + * Set the SudokuChecker object for the Nav bar. + * + * This is necessary for the Nav bar to be able to interact with the + * SudokuChecker object for the solve button. This shouldn't be necessary + * to change once the program is running. + */ + public void setChecker(SudokuChecker sc) { + this.sc = sc; + } + + private void style() { + setBackground(s.getTheme().getPrimaryBackground()); + setForeground(s.getTheme().getPrimaryText()); + } + /** * Create the GUI for the Nav bar. * @@ -119,6 +137,7 @@ public class Nav extends JPanel { * debugging purposes. */ private void createGUI() { + style(); JLabel elapsedTime = new JLabel("Elapsed Time: 00:00:00"); elapsedTime.setFont(s.getFont()); // TODO: Make custom Label class. JButton solve = new JButton("Solve"); diff --git a/src/gui/Note.java b/src/gui/Note.java new file mode 100644 index 0000000..79f057b --- /dev/null +++ b/src/gui/Note.java @@ -0,0 +1,35 @@ +package gui; + +import javax.swing.JLabel; + +import gui.backend.Theme; + +/** + * The Note class represents a single note of a cell in the Sudoku board. + * + * This is the GUI abstraction for the underlying Cell object, and is used to + * display and manage the note for a cell. + */ +public class Note extends JLabel { + private Theme t; + + /** + * Create a new Note object with the given text and theme. + * + * @param text + * @param t + */ + public Note(String text, Theme t) { + super(text); + this.t = t; + style(); + } + + /** + * Style the Note component with the current theme. + */ + private void style() { + setBackground(t.getPrimaryBackground()); + setForeground(t.getPrimaryText()); + } +} diff --git a/src/gui/Root.java b/src/gui/Root.java index 376a45d..c24104d 100644 --- a/src/gui/Root.java +++ b/src/gui/Root.java @@ -1,5 +1,6 @@ package gui; +import java.awt.LayoutManager; import javax.swing.JPanel; import gui.backend.Settings; diff --git a/src/gui/backend/Settings.java b/src/gui/backend/Settings.java index 764213e..778b620 100644 --- a/src/gui/backend/Settings.java +++ b/src/gui/backend/Settings.java @@ -50,6 +50,12 @@ public class Settings { // Default open state for the application. private int defaultOpenState; + // Theme object storing the colors for the GUI components. + private Theme theme; + + // Auto-fill notes + private boolean autoFillNotes; + /** * Create a new Settings object, initializing the default settings * or reading in the settings from settings file if it exists. @@ -244,6 +250,65 @@ public class Settings { updateSettingsFile(); } + /** + * The Theme object stores the colors for the GUI components. + * + * The available colors for customization are: + * - primaryBackground + * - secondaryBackground + * - primaryText + * - secondaryText + * - primaryHighlight + * - secondaryHighlight + * - primaryBorder + * - secondaryBorder + * - primaryButton + * - secondaryButton + * - primaryButtonHighlight + * - secondaryButtonHighlight + * - primaryButtonBorder + * - secondaryButtonBorder + * + * @see Theme + * @return + */ + public Theme getTheme() { + return theme; + } + + /** + * Set the theme and write it to the settings file. + * + * @param theme + */ + public void setTheme(Theme theme) { + this.theme = theme; + updateSettingsFile(); + } + + /** + * The Auto-fill notes setting is a configurable setting, where the user + * can specify whether notes should be automatically filled in when a + * value is placed in a cell. + * + * By default, the auto-fill notes setting is set to false. + * + * @return boolean + */ + public boolean getAutoFillNotes() { + return autoFillNotes; + } + + /** + * Set the auto-fill notes setting and write it to the settings file. + * + * @param autoFillNotes + */ + public void setAutoFillNotes(boolean autoFillNotes) { + this.autoFillNotes = autoFillNotes; + updateSettingsFile(); + } + /** * Open and write the settings to the settings file. * @@ -279,6 +344,8 @@ public class Settings { resizable = false; cellGUIStartMode = false; defaultOpenState = 0; + theme = new Theme(new File(appDirectory + "default.theme")); + autoFillNotes = true; updateSettingsFile(); } diff --git a/src/gui/backend/SudokuChecker.java b/src/gui/backend/SudokuChecker.java index 10af2a6..7cf8c86 100644 --- a/src/gui/backend/SudokuChecker.java +++ b/src/gui/backend/SudokuChecker.java @@ -28,7 +28,44 @@ public class SudokuChecker { */ public SudokuChecker(Cell[][] grid) { this.grid = grid; - solve(); + } + + /** + * Get the possible values for each cell in the Sudoku puzzle. + * + * Intended to be used for auto-filling in possible values in the GUI. + * + * @return + */ + public Cell[][] getPossibleValues(Cell[][] grid) { + for(int row = 0; row < grid.length; row++) { + for(int col = 0; col < grid[row].length; col++) { + if(grid[row][col].getValue() == 0) + continue; + + ArrayList intersection = intersection( + getRowRemainingNumbers(row), + getColRemainingNumbers(col) + ); + + intersection = intersection( + intersection, + getBoxRemainingNumbers(row, col) + ); + + // Join the arrays and find the intersection of the three arrays + ArrayList availableNumbers = new ArrayList(); + for(int i = 0; i < intersection.size(); i++) { + availableNumbers.add(intersection.get(i)); + } + + grid[row][col].setPossibleValues( + arrayListToArray(availableNumbers) + ); + } + } + + return grid; } /** @@ -37,6 +74,7 @@ public class SudokuChecker { * @return Cell[][] */ public Cell[][] getSolution() { + solve(); return grid; } diff --git a/src/gui/backend/Theme.java b/src/gui/backend/Theme.java index c848bd7..7dad6d4 100644 --- a/src/gui/backend/Theme.java +++ b/src/gui/backend/Theme.java @@ -71,10 +71,11 @@ public class Theme { * Sets the theme for the GUI components. */ private void setTheme() { - primaryBackground = Color.WHITE; + primaryBackground = Color.LIGHT_GRAY; secondaryBackground = Color.LIGHT_GRAY; - primaryText = Color.BLACK; + primaryText = Color.DARK_GRAY; secondaryText = Color.DARK_GRAY; + primaryBorder = Color.BLACK; } /**