// This file is part of Penn TotalRecall <http://memory.psych.upenn.edu/TotalRecall>.
//
// TotalRecall is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 only.
//
// TotalRecall is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TotalRecall. If not, see <http://www.gnu.org/licenses/>.
package components.preferences;
import info.UserPrefs;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import javax.swing.BoxLayout;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
* An <code>AbstractPreferenceDisplay</code> for choosing the range of frequencies to bandpass filter.
*
* Reasonable inputs (i.e. non-negative integer frequencies with max >= min) are enforced.
*
* @author Yuvi Masory
*/
public class BandPassFilterPreference extends AbstractPreferenceDisplay {
private static final int DEFAULT_MIN = UserPrefs.defaultMinBandPass;
private static final int DEFAULT_MAX = UserPrefs.defaultMaxBandPass;
private int lastMinVal;
private int lastMaxVal;
//by the time the input verifier is made in the constructor,
//minField's getText() must return a string parseable to
//an integer >= maxField's
private JTextField minField;
private JTextField maxField;
private String prefTitle;
/**
* Creates a new <code>BandPasFilterPreference</code> with the provided title.
*
* @param prefTitle The title of the preference, will be displayed graphically for the user
*/
protected BandPassFilterPreference(String prefTitle) {
super(prefTitle);
this.prefTitle = prefTitle;
JPanel panel = new JPanel(new GridLayout(0, 1));
JPanel minPanel = new JPanel();
minPanel.setLayout(new BoxLayout(minPanel, BoxLayout.X_AXIS));
minPanel.add(new JLabel("Min (Hz):"));
int storedMin = UserPrefs.prefs.getInt(UserPrefs.minBandPass, DEFAULT_MIN);
UserPrefs.prefs.putInt(UserPrefs.minBandPass, storedMin);
lastMinVal = storedMin;
minField = new JTextField(Integer.toString(storedMin));
minField.setMaximumSize(new Dimension(75, Integer.MAX_VALUE));
minPanel.add(minField);
JPanel maxPanel = new JPanel();
maxPanel.setLayout(new BoxLayout(maxPanel, BoxLayout.X_AXIS));
maxPanel.add(new JLabel("Max (Hz):"));
int storedMax = UserPrefs.prefs.getInt(UserPrefs.maxBandPass, DEFAULT_MAX);
UserPrefs.prefs.putInt(UserPrefs.maxBandPass, storedMax);
lastMaxVal = storedMax;
maxField = new JTextField(Integer.toString(storedMax));
//set the input verifier, which will correct bad inputs when the user tries to move focus away
NondecreasingPositiveIntegerVerifier verifier = new NondecreasingPositiveIntegerVerifier(minField, maxField);
minField.setInputVerifier(verifier);
maxField.setInputVerifier(verifier);
maxField.setMaximumSize(new Dimension(75, Integer.MAX_VALUE));
maxPanel.add(maxField);
panel.add(minPanel);
panel.add(maxPanel);
add(panel);
}
/**
* Rejects invalid inputs (see class-level docs) by throwing <code>BadPreferenceException</code>.
* This check is redundant to the input verifier's job.
*
* {@inheritDoc}
*/
@Override
protected boolean save() throws BadPreferenceException {
int minVal = 0;
int maxVal = 0;
try {
minVal = Integer.parseInt(minField.getText());
maxVal = Integer.parseInt(maxField.getText());
}
catch(NumberFormatException e) {
throw new BadPreferenceException(prefTitle, "Input must be an integer.");
}
if(minVal > maxVal) {
throw new BadPreferenceException(prefTitle, "Minimum value must be less than maximum value.");
}
else {
lastMinVal = minVal;
minField.setText(Integer.toString(minVal));
UserPrefs.prefs.putInt(UserPrefs.minBandPass, minVal);
lastMaxVal = maxVal;
maxField.setText(Integer.toString(maxVal));
UserPrefs.prefs.putInt(UserPrefs.maxBandPass, maxVal);
return true;
}
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isChanged() {
try {
if(Integer.parseInt(minField.getText()) != lastMinVal){
return true;
}
if(Integer.parseInt(maxField.getText()) != lastMaxVal) {
return true;
}
}
catch(NumberFormatException e) {
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected void graphicallyRevert() {
maxField.setText(Integer.toString(lastMaxVal));
minField.setText(Integer.toString(lastMinVal));
}
/**
* {@inheritDoc}
*/
@Override
protected void restoreDefault() {
UserPrefs.prefs.putInt(UserPrefs.minBandPass, DEFAULT_MIN);
minField.setText(Integer.toString(DEFAULT_MIN));
lastMinVal = DEFAULT_MIN;
UserPrefs.prefs.putInt(UserPrefs.maxBandPass, DEFAULT_MAX);
maxField.setText(Integer.toString(DEFAULT_MAX));
lastMaxVal = DEFAULT_MAX;
}
/**
* Custom <code>InputVerifier</code> that guarantees one <code>JTextField</code> displays a positive integer >= than the other one.
*
*/
private class NondecreasingPositiveIntegerVerifier extends InputVerifier {
JTextField minComp;
JTextField maxComp;
/**
* Creates a new <code>NondecreasingPositiveIntegerVerifier</code> with the provided <code>JTextFields</code>.
*
* @param minComp The <code>JTextField</code> displaying the smaller (or equal) integer
* @param maxComp The <code>JTextField</code> displaying the larger (or equal) integer
* @throws IllegalArgumentException If the inputs' texts do not currently meet the verifier's standards
*/
private NondecreasingPositiveIntegerVerifier(JTextField minComp, JTextField maxComp) {
this.minComp = minComp;
this.maxComp = maxComp;
if(verify(minComp) == false || verify(maxComp) == false) {
throw new IllegalArgumentException("provided components not in verifiable state");
}
}
/**
* In this implementation we give a beep and return the value to the previous one if the field's text is found invalid by {{@link #verify(JComponent)}.
* {@inheritDoc}
*/
@Override
public boolean shouldYieldFocus(JComponent input) {
if(input != minComp && input != maxComp) {
throw new IllegalArgumentException("unrecognized component: not min or max component given to constructor");
}
boolean goodInput = verify(input);
if(goodInput == false) {
if(input == minComp) {
minComp.setText(Integer.toString(lastMinVal));
if(verify(maxComp) == false) {
System.err.println("input verifier internal assumption failed");
}
}
else {
maxComp.setText(Integer.toString(lastMaxVal));
if(verify(minComp) == false) {
System.err.println("input verifier internal assumption failed");
}
}
Toolkit.getDefaultToolkit().beep();
}
return goodInput;
}
/**
* {@inheritDoc}
*/
@Override
public boolean verify(JComponent input) {
if(input != minComp && input != maxComp) {
throw new IllegalArgumentException("unrecognized component: not min or max component given to constructor");
}
int minNum = 0;
int maxNum = 0;
try {
minNum = Integer.parseInt(minComp.getText());
maxNum = Integer.parseInt(maxComp.getText());
}
catch(NumberFormatException e) {
return false;
}
if(minNum < 0 || maxNum < 0) {
return false;
}
if(minNum > maxNum) {
return false;
}
return true;
}
}
}