Tue, 28 Jul 2020 14:27:14 +0200
bugfix: modified state is reset even when saving fails + more tests
1 /*
2 * Copyright 2013 Mike Becker. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
27 package de.uapcore.sudoku;
29 import javax.swing.*;
30 import java.awt.*;
31 import java.awt.event.FocusAdapter;
32 import java.awt.event.FocusEvent;
33 import java.awt.event.KeyAdapter;
34 import java.awt.event.KeyEvent;
36 /**
37 * A custom text field specifically for Sudoku number fields.
38 */
39 public final class SudokuTextField extends JTextField {
41 private boolean modified;
43 public SudokuTextField() {
44 setBorder(null);
45 setBackground(Color.WHITE);
47 setFont(new Font("Dialog", Font.PLAIN, 18));
48 setHorizontalAlignment(JTextField.CENTER);
50 Dimension dim = new Dimension(40,40);
51 setPreferredSize(dim);
52 setMinimumSize(dim);
53 setMaximumSize(dim);
55 addKeyListener(new KeyAdapter() {
56 private void handle(KeyEvent e) {
57 char c = e.getKeyChar();
58 if (!e.isAltDown() && !e.isControlDown() &&
59 !Character.isISOControl(c)) {
60 // Perform clean input check
61 if (getText().length() > 0 && getSelectedText() == null) {
62 if (c != KeyEvent.CHAR_UNDEFINED) {
63 e.consume();
64 }
65 } else {
66 if (c < '1' || c > '9') {
67 e.consume();
68 } else {
69 setModified(true);
70 }
71 }
72 } else {
73 // We can still be tricked by hotkeys, so do it the hard way
74 if (!getText().matches("^[1-9]$")) {
75 setText("");
76 }
77 }
78 }
80 @Override
81 public void keyPressed(KeyEvent e) {
82 handle(e);
83 }
85 @Override
86 public void keyTyped(KeyEvent e) {
87 handle(e);
88 }
90 @Override
91 public void keyReleased(KeyEvent e) {
92 handle(e);
93 }
95 });
96 addFocusListener(new FocusAdapter() {
97 @Override
98 public void focusGained(FocusEvent e) {
99 selectAll();
100 }
101 @Override
102 public void focusLost(FocusEvent e) {
103 select(0, 0);
104 }
105 });
106 }
108 /**
109 * Returns the current value in the field or zero if the field is empty.
110 *
111 * @return the number from 1 to 9 or zero if empty
112 */
113 public int getValue() {
114 if (getText().isEmpty()) {
115 return 0;
116 } else {
117 return Integer.parseInt(getText());
118 }
119 }
121 /**
122 * Sets the field's value.
123 *
124 * @param v the number from 1 to 9 or zero to clear the field
125 * @throws IllegalArgumentException if v is not between 0 and 9
126 */
127 public void setValue(int v) {
128 if (v == 0) {
129 setText("");
130 } else if (v > 0 && v < 10) {
131 setText(String.valueOf(v));
132 } else {
133 throw new IllegalArgumentException(
134 "Sudoku numbers must be in range 0-9 (0 means 'not set')");
135 }
136 }
138 /**
139 * Sets the modified state of this field.
140 *
141 * Modified fields are displayed in blue color.
142 *
143 * @param modified a flag indicating whether this field is modified
144 */
145 public void setModified(boolean modified) {
146 this.modified = modified;
147 setForeground(modified?Color.BLUE:Color.BLACK);
148 }
150 /**
151 * Checks whether this field is in modified state.
152 *
153 * @return true if this field is modified
154 */
155 public boolean isModified() {
156 return modified;
157 }
158 }