package no.ntnu.fp.gui; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.FocusListener; import java.awt.event.FocusEvent; import java.util.Date; import java.awt.Component; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.JFormattedTextField; import javax.swing.BorderFactory; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import no.ntnu.fp.model.Person; import no.ntnu.fp.swingutil.EmailFormatter; /** * A panel for viewing and editing {@link no.ntnu.fp.model.Person} objects. The various * interfaces are implemented to react to sub-components and model changes. * * @author Hallvard Tr�tteberg * @author Thomas Østerlie * * @version $Revision: 1.4 $ - $Date: 2005/02/22 07:54:45 $ */ @SuppressWarnings("serial") public class PersonPanel extends JPanel implements PropertyChangeListener, ActionListener, ItemListener, FocusListener { /** * The underlying data model is the {@link no.ntnu.fp.model.Person} class. */ private Person model; /** * Text field for displaying and editing the person's name. */ private JFormattedTextField nameTextField; /** * Text field for displaying and editing the person's email address. */ private JFormattedTextField emailTextField; /** * Text field for displaying and editing the person's date of birth. */ private JFormattedTextField dateOfBirthTextField; /** * Used by {@link #propertyChanged(String, String, JTextField)} and * {@link #sourceChanged(Object)}. */ private Object eventSource = null; /** * Constructor for objects of class PersonPanel */ public PersonPanel() { setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(5, 5, 5, 5) )); setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); Insets insets = new Insets(2, 2, 2, 2); constraints.insets = insets; constraints.anchor = GridBagConstraints.LINE_START; nameTextField = new JFormattedTextField(); nameTextField.addPropertyChangeListener(this); nameTextField.setColumns(20); addGridBagLabel("Name: ", 0, constraints); addGridBagComponent(nameTextField, 0, constraints); emailTextField = new JFormattedTextField(new EmailFormatter()); emailTextField.addPropertyChangeListener(this); emailTextField.setColumns(20); addGridBagLabel("Email: ", 1, constraints); addGridBagComponent(emailTextField, 1, constraints); dateOfBirthTextField = new JFormattedTextField(PersonCellRenderer.dateFormatter); dateOfBirthTextField.addPropertyChangeListener(this); dateOfBirthTextField.setColumns(15); addGridBagLabel("Birthday: ", 2, constraints); addGridBagComponent(dateOfBirthTextField, 2, constraints); setEditable(false); } /** * Controls whether the components are editable or not. * * @param editable whether to turn on or off the editable property */ public void setEditable(boolean editable) { nameTextField.setEditable(editable); emailTextField.setEditable(editable); dateOfBirthTextField.setEditable(editable); } /** * This method is defined in {@link java.beans.PropertyChangeListener} and * is called when a property of an object listened to is changed.<P> * * Note that <code>PersonPanel</code> listens to both the underlying model object, * i.e. {@link no.ntnu.fp.model.Person}, and to the value property of the * {@link javax.swing.JFormattedTextField}, i.e. nameTextField, emailTextField, and * dateOfBirthTextField. * * @see java.beans.PropertyChangeListener <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/PropertyChangeListener.html">java.beans.PropertyChangeListener</a> * @see javax.swing.JFormattedTextField <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFormattedTextField.html">javax.swing.JFormattedTextField</a> */ public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource() == dateOfBirthTextField) { sourceChanged(dateOfBirthTextField); } else if (evt.getSource() == emailTextField) { sourceChanged(emailTextField); } else if (evt.getSource() == nameTextField) { sourceChanged(nameTextField); } else { updatePanel(evt.getPropertyName()); } } /** * Sets the <code>PersonPanel</code>'s underlying data model. * * @param p The underlying data model. */ public void setModel(Person p) { if (p != null) { if (model != null) model.removePropertyChangeListener(this); model = p; model.addPropertyChangeListener(this); updatePanel(null); } } /** * Utility method for adding a label to the GridBagLayout. * Labels are placed in column 0, occupy only one cell and do not stretch. * * @param s the label * @param row the row * @param constraints the GridBagConstraints */ private void addGridBagLabel(String s, int row, GridBagConstraints constraints) { constraints.gridx = 0; constraints.gridy = row; constraints.gridheight = 1; constraints.gridwidth = 1; constraints.fill = GridBagConstraints.NONE; constraints.weightx = 0.0; add(new JLabel(s), constraints); } /** * Utility method for adding a component to the GridBagLayout. * Components are placed in column 1, occupy only one cell and stretches. * * @param s the label * @param row the row * @param constraints the GridBagConstraints */ private void addGridBagComponent(Component c, int row, GridBagConstraints constraints) { constraints.gridx = 1; constraints.gridy = row; constraints.gridheight = 1; constraints.gridwidth = GridBagConstraints.REMAINDER; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1.0; add(c, constraints); } private boolean propertyChanged(String changed, String prop, JTextField tf) { return (changed == null || (changed.equals(prop) && eventSource != tf && eventSource != tf.getDocument())); } /** * Updates the sub-components according to the underlying model. * * @param property the name of the property that triggered the update (may be null) */ private void updatePanel(String property) { if (model == null) { setEditable(false); } if (propertyChanged(property, Person.NAME_PROPERTY_NAME, nameTextField)) { String name = (model != null ? model.getName() : ""); nameTextField.setText(name != null ? name : ""); } if (propertyChanged(property, Person.EMAIL_PROPERTY_NAME, emailTextField)) { String email = (model != null ? model.getEmail() : ""); emailTextField.setValue(email); } if (propertyChanged(property, Person.DATEOFBIRTH_PROPERTY_NAME, dateOfBirthTextField)) { Date birthday = (model != null ? model.getDateOfBirth() : null); dateOfBirthTextField.setValue(birthday); } } /** * Handles changes in sub-components, * that should be propagated to the underlying Person model object. * * @param source the source of the change, typicall a sub-component */ private void sourceChanged(Object source) { if (model == null) { return; } eventSource = source; if (source == nameTextField) { model.setName(nameTextField.getText()); } else if (source == emailTextField) { model.setEmail((String)emailTextField.getValue()); } else if (source == dateOfBirthTextField) { model.setDateOfBirth((Date)dateOfBirthTextField.getValue()); } eventSource = null; } /** * This method is defined in ActionListener and * is called when ENTER is typed in a JTextField * (or a JButton is hit or JMenuItem is selected, but that's not relevant for PersonPanel) * * @param event the ActionEvent describing the action */ public void actionPerformed(ActionEvent event) { sourceChanged(event.getSource()); } /** * This method is defined in ItemListener and * is called when an item is selected in a JComboBox * (or a JSlider or JSpinner, but that's not relevant for PersonPanel) * * @param event the ItemEvent describing the selection */ public void itemStateChanged(ItemEvent event) { sourceChanged(event.getSource()); } /** * This method is defined in FocusListener and * is called when a JTextField (or in fact any component) loses the keyboard focus. * This normally happens when the user TABs out of the text field or clicks outside it. * * @param event the FocusEvent describing what happened */ public void focusLost(FocusEvent event) { sourceChanged(event.getSource()); } public void focusGained(FocusEvent event) {} }