/* * This file is part of BBCT. * * Copyright 2012-14 codeguru <codeguru@users.sourceforge.net> * * BBCT 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, either version 3 of the License, or * (at your option) any later version. * * BBCT 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package bbct.swing.gui; import bbct.common.data.BaseballCard; import bbct.common.exceptions.InputException; import bbct.swing.BBCTStringResources; import bbct.swing.BBCTStringResources.TitleResources; import bbct.swing.FontResources; import bbct.swing.gui.event.UpdateInstructionsFocusListener; import bbct.swing.gui.inputverifiers.CurrencyInputVerifier; import bbct.swing.gui.inputverifiers.NotEmptyInputVerifier; import bbct.swing.gui.inputverifiers.PositiveIntegerInputVerifier; import bbct.swing.gui.inputverifiers.YearInputVerifier; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.ParseException; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.InputVerifier; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; /** * {@link CardDetailsPanel} contains labels and text fields for the data stored * in a {@link bbct.data.BaseballCard} model object. This panel can be used in * two modes. The first mode, which is set by using the default constructor * {@link #CardDetailsPanel()} or {@link #CardDetailsPanel(boolean)} with a * value of {@code true}, allows editing of all of the text fields. The second * mode, set with {@link #CardDetailsPanel(boolean)} or * {@link #CardDetailsPanel(bbct.data.BaseballCard, boolean)} with a value of * {@code false} for the {@code boolean } parameter, only allows editing of the * value and count text fields. * * TODO: Instructions should change depending on value of allEditable field. * * TODO: Add $ at the beginning of valueTextField's text if it is not present? */ @SuppressWarnings("serial") public class CardDetailsPanel extends JPanel { /** * Creates a new {@link CardDetailsPanel}. * * @param allEditable * Whether or not all text fields are editable. The count and * value text fields will always be editable regardless of the * value of this flag. */ public CardDetailsPanel(boolean allEditable) { this.allEditable = allEditable; this.initComponents(); } /** * Creates a new {@link CardDetailsPanel}. * * @param card * The {@link bbct.data.BaseballCard} object used to initialize * the values of the text fields in this panel. * @param allEditable * Whether or not all text fields are editable. The count and * value text fields will always be editable regardless of the * value of this flag. */ public CardDetailsPanel(BaseballCard card, boolean allEditable) { this.allEditable = allEditable; this.initComponents(); this.brandTextField.setText(card.getBrand()); this.yearTextField.setValue(card.getYear()); this.numberTextField.setValue(card.getNumber()); // TODO: This works, but the logic should be part of the JFormattedTextField, not the panel int value = card.getValue(); int dollars = value / 100; int cents = value % 100; String centsStr = cents < 10 ? "0" + cents : "" + cents; String valueStr = "$" + dollars + "." + centsStr; this.valueTextField.setText(valueStr); this.countTextField.setValue(card.getCount()); this.playerNameTextField.setText(card.getPlayerName()); this.playerPositionComboBox.setSelectedItem(card.getPlayerPosition()); } /** * Creates a {@link bbct.data.BaseballCard} from the data in the text fields * in this panel. * * @return A {@link bbct.data.BaseballCard} initialized with data from the * text fields in this panel. * @throws InputException * If any text field is blank or contains text with invalid * formatting. */ public BaseballCard getBaseballCard() throws InputException { // TODO: Thoroughly test all error handling code. // Validate card brand this.brandTextField.selectAll(); this.brandTextField.requestFocusInWindow(); if (!this.notEmptyVerifier.verify(this.brandTextField)) { throw new InputException(BBCTStringResources.ErrorResources.CARD_BRAND_ERROR); } String brand = this.brandTextField.getText(); // Validate year, number, value, and count int year = ((Number) this.verifyTextField(this.yearTextField, this.yearVerifier, BBCTStringResources.ErrorResources.CARD_YEAR_ERROR)).intValue(); int number = ((Number) this.verifyTextField(this.numberTextField, this.numVerifier, BBCTStringResources.ErrorResources.CARD_NUMBER_ERROR)).intValue(); double valueDbl = ((Number) this.verifyTextField(this.valueTextField, this.currencyVerifier, BBCTStringResources.ErrorResources.CARD_VALUE_ERROR)).doubleValue(); int value = (int) (valueDbl * 100); int count = ((Number) this.verifyTextField(this.countTextField, this.numVerifier, BBCTStringResources.ErrorResources.CARD_COUNT_ERROR)).intValue(); // Validate player name this.playerNameTextField.selectAll(); this.playerNameTextField.requestFocusInWindow(); if (!this.notEmptyVerifier.verify(this.playerNameTextField)) { throw new InputException(BBCTStringResources.ErrorResources.PLAYER_NAME_ERROR); } String playerName = this.playerNameTextField.getText(); // Validate player position this.playerPositionComboBox.requestFocusInWindow(); if (this.playerPositionComboBox.getSelectedIndex() == -1) { throw new InputException(BBCTStringResources.ErrorResources.PLAYER_POSITION_ERROR); } String playerPosition = (String) this.playerPositionComboBox.getSelectedItem(); return new BaseballCard(brand, year, number, value, count, playerName, playerPosition); } /** * Reset all text fields to blank strings and set focus to the card brand * text field. */ public void reset() { this.brandTextField.setText(""); this.countTextField.setText(""); this.numberTextField.setText(""); this.valueTextField.setText(""); this.yearTextField.setText(""); this.playerNameTextField.setText(""); this.playerPositionComboBox.setSelectedIndex(-1); this.brandTextField.requestFocusInWindow(); } private void initComponents() { final int topOutside = 5; final int bottomOutside = 5; final int rightOutside = 10; final int leftOutside = 10; final int inside = 10; Insets topLeftInsets = new Insets(topOutside, leftOutside, inside, inside); Insets topRightInsets = new Insets(topOutside, inside, inside, rightOutside); Insets leftInsets = new Insets(inside, leftOutside, inside, inside); Insets rightInsets = new Insets(inside, inside, inside, rightOutside); Insets bottomLeftInsets = new Insets(inside, leftOutside, bottomOutside, inside); Insets bottomRightInsets = new Insets(inside, inside, bottomOutside, rightOutside); JPanel cardDetailsPanel = new JPanel(new BorderLayout()); Border emptyBorder = BorderFactory.createEmptyBorder(5, 10, 5, 10); TitledBorder titledCardDetailsBorder = BorderFactory.createTitledBorder(TitleResources.CARD_DETAILS_BORDER_TITLE); titledCardDetailsBorder.setTitleFont(FontResources.TITLED_BORDER_FONT); Border cardDetailsBorder = BorderFactory.createCompoundBorder(emptyBorder, titledCardDetailsBorder); cardDetailsPanel.setBorder(cardDetailsBorder); JPanel cardDetailsInputPanel = new JPanel(new GridBagLayout()); JLabel brandLabel = new JLabel(BBCTStringResources.LabelResources.CARD_BRAND_LABEL); brandLabel.setFont(FontResources.DEFAULT_FONT); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.insets = topLeftInsets; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; cardDetailsInputPanel.add(brandLabel, gbc); this.brandTextField = new JTextField(); this.brandTextField.setEditable(this.allEditable); this.brandTextField.setFont(FontResources.DEFAULT_FONT); this.brandTextField.setColumns(CardDetailsPanel.TEXT_FIELD_COLUMNS); this.brandTextField.addFocusListener(new UpdateInstructionsFocusListener(BBCTStringResources.InstructionResources.CARD_BRAND_INSTRUCTIONS)); gbc.gridx = 1; gbc.gridy = 0; gbc.weightx = 2; gbc.insets = topRightInsets; cardDetailsInputPanel.add(this.brandTextField, gbc); JLabel yearLabel = new JLabel(BBCTStringResources.LabelResources.CARD_YEAR_LABEL); yearLabel.setFont(FontResources.DEFAULT_FONT); gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 1; gbc.insets = leftInsets; cardDetailsInputPanel.add(yearLabel, gbc); this.yearTextField = new JFormattedTextField(); this.yearTextField.setEditable(this.allEditable); this.yearTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat("#0")))); this.yearTextField.setFocusLostBehavior(JFormattedTextField.PERSIST); this.yearTextField.setFont(FontResources.DEFAULT_FONT); this.yearTextField.setColumns(CardDetailsPanel.TEXT_FIELD_COLUMNS); this.yearTextField.addFocusListener(new UpdateInstructionsFocusListener(BBCTStringResources.InstructionResources.CARD_YEAR_INSTRUCTIONS)); gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 2; gbc.insets = rightInsets; cardDetailsInputPanel.add(this.yearTextField, gbc); JLabel numberLabel = new JLabel(BBCTStringResources.LabelResources.CARD_NUMBER_LABEL); numberLabel.setFont(FontResources.DEFAULT_FONT); gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 1; gbc.insets = leftInsets; cardDetailsInputPanel.add(numberLabel, gbc); this.numberTextField = new JFormattedTextField(); this.numberTextField.setEditable(this.allEditable); this.numberTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat("#0")))); this.numberTextField.setFocusLostBehavior(JFormattedTextField.PERSIST); this.numberTextField.setFont(FontResources.DEFAULT_FONT); this.numberTextField.setColumns(CardDetailsPanel.TEXT_FIELD_COLUMNS); this.numberTextField.addFocusListener(new UpdateInstructionsFocusListener(BBCTStringResources.InstructionResources.CARD_NUMBER_INSTRUCTIONS)); gbc.gridx = 1; gbc.gridy = 2; gbc.weightx = 2; gbc.insets = rightInsets; cardDetailsInputPanel.add(this.numberTextField, gbc); JLabel valueLabel = new JLabel(BBCTStringResources.LabelResources.CARD_VALUE_LABEL); valueLabel.setFont(FontResources.DEFAULT_FONT); gbc.gridx = 0; gbc.gridy = 3; gbc.weightx = 1; gbc.insets = leftInsets; cardDetailsInputPanel.add(valueLabel, gbc); this.valueTextField = new JFormattedTextField(); this.valueTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(java.text.NumberFormat.getCurrencyInstance()))); this.valueTextField.setFocusLostBehavior(JFormattedTextField.PERSIST); this.valueTextField.setFont(FontResources.DEFAULT_FONT); this.valueTextField.setColumns(CardDetailsPanel.TEXT_FIELD_COLUMNS); this.valueTextField.addFocusListener(new UpdateInstructionsFocusListener(BBCTStringResources.InstructionResources.CARD_VALUE_INSTRUCTIONS)); gbc.gridx = 1; gbc.gridy = 3; gbc.weightx = 2; gbc.insets = rightInsets; cardDetailsInputPanel.add(this.valueTextField, gbc); JLabel countLabel = new JLabel(BBCTStringResources.LabelResources.CARD_COUNT_LABEL); countLabel.setFont(FontResources.DEFAULT_FONT); gbc.gridx = 0; gbc.gridy = 4; gbc.weightx = 1; gbc.insets = leftInsets; cardDetailsInputPanel.add(countLabel, gbc); this.countTextField = new JFormattedTextField(); this.countTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat("#0")))); this.countTextField.setFocusLostBehavior(JFormattedTextField.PERSIST); this.countTextField.setFont(FontResources.DEFAULT_FONT); this.countTextField.setColumns(CardDetailsPanel.TEXT_FIELD_COLUMNS); this.countTextField.addFocusListener(new UpdateInstructionsFocusListener(BBCTStringResources.InstructionResources.CARD_COUNT_INSTRUCTIONS)); gbc.gridx = 1; gbc.gridy = 4; gbc.weightx = 2; gbc.insets = rightInsets; cardDetailsInputPanel.add(this.countTextField, gbc); cardDetailsPanel.add(cardDetailsInputPanel, BorderLayout.PAGE_START); JPanel deleteOptionPanel = new JPanel(new GridBagLayout()); this.deleteOption = new JCheckBox(BBCTStringResources.LabelResources.DELETE_CARD_LABEL); this.deleteOption.setFont(FontResources.BUTTON_FONT); this.deleteOption.setEnabled(!this.allEditable); gbc.gridx = 0; gbc.gridy = 0; gbc.insets = leftInsets; deleteOptionPanel.add(this.deleteOption, gbc); JPanel playerDetailsPanel = new JPanel(new BorderLayout()); TitledBorder titledPlayerDetailsBorder = BorderFactory.createTitledBorder(TitleResources.PLAYER_DETAILS_BORDER_TITLE); titledPlayerDetailsBorder.setTitleFont(FontResources.TITLED_BORDER_FONT); Border playerDetailsBorder = BorderFactory.createCompoundBorder(emptyBorder, titledPlayerDetailsBorder); playerDetailsPanel.setBorder(playerDetailsBorder); JPanel playerDetailsInputPanel = new JPanel(new GridBagLayout()); JLabel playerNameLabel = new JLabel(BBCTStringResources.LabelResources.PLAYER_NAME_LABEL); playerNameLabel.setFont(FontResources.DEFAULT_FONT); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.insets = topLeftInsets; playerDetailsInputPanel.add(playerNameLabel, gbc); this.playerNameTextField = new JTextField(); this.playerNameTextField.setEditable(this.allEditable); this.playerNameTextField.setColumns(CardDetailsPanel.TEXT_FIELD_COLUMNS); this.playerNameTextField.setFont(FontResources.DEFAULT_FONT); this.playerNameTextField.addFocusListener(new UpdateInstructionsFocusListener(BBCTStringResources.InstructionResources.PLAYER_NAME_INSTRUCTIONS)); gbc.gridx = 1; gbc.gridy = 0; gbc.weightx = 2; gbc.insets = topRightInsets; playerDetailsInputPanel.add(this.playerNameTextField, gbc); JLabel playerPositionLabel = new JLabel(BBCTStringResources.LabelResources.PLAYER_POSITION_LABEL); playerPositionLabel.setFont(FontResources.DEFAULT_FONT); gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 1; gbc.insets = bottomLeftInsets; playerDetailsInputPanel.add(playerPositionLabel, gbc); this.playerPositionComboBox = new JComboBox<String>(BBCTStringResources.ComboBoxResources.POSITIONS); this.playerPositionComboBox.setEditable(this.allEditable); this.playerPositionComboBox.setFont(FontResources.DEFAULT_FONT); this.playerPositionComboBox.addFocusListener(new UpdateInstructionsFocusListener(BBCTStringResources.InstructionResources.PLAYER_POSITION_INSTRUCTIONS)); gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 2; gbc.insets = bottomRightInsets; playerDetailsInputPanel.add(this.playerPositionComboBox, gbc); playerDetailsPanel.add(playerDetailsInputPanel, BorderLayout.PAGE_START); this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.add(cardDetailsPanel); this.add(playerDetailsPanel); this.add(deleteOptionPanel); addAncestorListener(new AncestorListener() { @Override public void ancestorMoved(AncestorEvent evt) { } @Override public void ancestorAdded(AncestorEvent evt) { if (CardDetailsPanel.this.allEditable) { CardDetailsPanel.this.brandTextField.requestFocusInWindow(); } else { CardDetailsPanel.this.valueTextField.requestFocusInWindow(); } } @Override public void ancestorRemoved(AncestorEvent evt) { } }); } private Object verifyTextField(JFormattedTextField tf, InputVerifier v, String errorMessage) throws InputException { tf.selectAll(); tf.requestFocusInWindow(); try { tf.commitEdit(); } catch (ParseException ex) { throw new InputException(errorMessage, ex); } if (!v.verify(tf)) { throw new InputException(errorMessage); } return tf.getValue(); } public boolean deleteCard() { return this.deleteOption.isSelected(); } private JTextField brandTextField; private JFormattedTextField countTextField; private JFormattedTextField numberTextField; private JTextField playerNameTextField; private JComboBox<String> playerPositionComboBox; private JFormattedTextField valueTextField; private JFormattedTextField yearTextField; private JCheckBox deleteOption; private boolean allEditable = true; private final InputVerifier notEmptyVerifier = new NotEmptyInputVerifier(); private final InputVerifier numVerifier = new PositiveIntegerInputVerifier(); private final InputVerifier yearVerifier = new YearInputVerifier(); private final InputVerifier currencyVerifier = new CurrencyInputVerifier(); private static final int TEXT_FIELD_COLUMNS = 15; private static BaseballCard createBaseballCard() { String brand = "Topps"; int year = 1991; int num = 278; int val = 10000; int count = 1; String name = "Alex Fernandez"; String pos = "Pitcher"; return new BaseballCard(brand, year, num, val, count, name, pos); } /** * This is a test function for {@link CardDetailsPanel}. It simply creates a * {@link javax.swing.JFrame} in which to display the panel. * * @param args * The command-line arguments (ignored). */ public static void main(String[] args) { // TODO: Add a way to test getBaseballCard() final JFrame frame = new JFrame("CardDetailsPanel Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JPanel cardPanel = new JPanel(); final CardLayout cl = new CardLayout(); cardPanel.setLayout(cl); final CardDetailsPanel editablePanel = new CardDetailsPanel(true); cardPanel.add(editablePanel, "editablePanel"); cardPanel.add(new CardDetailsPanel(false), "uneditablePanel"); cardPanel.add(new CardDetailsPanel(createBaseballCard(), false)); JPanel buttonPanel = new JPanel(); JButton nextButton = new JButton("Next"); nextButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { cl.next(cardPanel); } }); buttonPanel.add(nextButton); JButton getCardButton = new JButton("Get Card"); getCardButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { try { // TODO: How do I get the currently visible CardDetailsPanel? // CardDetailsPanel panel = (CardDetailsPanel)cl. BaseballCard card = editablePanel.getBaseballCard(); JOptionPane.showMessageDialog(frame, card, "Baseball Card", JOptionPane.INFORMATION_MESSAGE); } catch (InputException ex) { JOptionPane.showMessageDialog(frame, ex, "Input Error", JOptionPane.ERROR_MESSAGE); } } }); buttonPanel.add(getCardButton); frame.setLayout(new BorderLayout()); frame.add(cardPanel, BorderLayout.CENTER); frame.add(buttonPanel, BorderLayout.SOUTH); frame.pack(); frame.setVisible(true); } }