001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.binding.value.swing;
017
018 import java.awt.event.FocusEvent;
019 import java.awt.event.FocusListener;
020 import java.beans.PropertyChangeEvent;
021 import java.beans.PropertyChangeListener;
022 import java.text.ParseException;
023
024 import javax.swing.JFormattedTextField;
025 import javax.swing.event.DocumentEvent;
026 import javax.swing.event.DocumentListener;
027
028 import org.springframework.binding.value.ValueModel;
029 import org.springframework.binding.value.support.AbstractValueModelAdapter;
030
031 /**
032 * Sets the value of the value model associated with a formatted text field when the text field changes according to the
033 * value commit policy.
034 *
035 * This setter will also update the formatted text field value when the underlying value model value changes.
036 *
037 * @author Oliver Hutchison
038 * @author Keith Donald
039 */
040 public class FormattedTextFieldAdapter extends AbstractValueModelAdapter implements PropertyChangeListener,
041 DocumentListener, FocusListener {
042
043 private final JFormattedTextField component;
044
045 private boolean settingValue;
046
047 private boolean ignoreValue;
048
049 public FormattedTextFieldAdapter(JFormattedTextField component, ValueModel valueModel,
050 ValueCommitPolicy commitPolicy) {
051 super(valueModel);
052 this.component = component;
053 this.component.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT);
054 this.component.addPropertyChangeListener("value", this);
055 if (commitPolicy == ValueCommitPolicy.AS_YOU_TYPE) {
056 component.getDocument().addDocumentListener(this);
057 }
058
059 // mathiasbr: register focus listener to avoid a race condition.
060 // If the formatted text field lost its focus the value will be replaced.
061 // This results into two events: the first one sets the value to null
062 // the second one sets the value to its value before it was set to null.
063 // If the focus is moved to the button which is bound to the validation
064 // and the field has a constraint (like required) the button will be disabled
065 // while it is pressed. Although the button will be enabled when the second
066 // event with the previous value is set again but the button will lost its
067 // armed and pressed state when it is disabled.
068 // The result is that a user has to click the button twice
069 // before the button fires the actionPerformed event
070 component.addFocusListener(this);
071
072 initalizeAdaptedValue();
073 }
074
075 protected void valueModelValueChanged(Object value) {
076 settingValue = true;
077 try {
078 component.setValue(value);
079 } finally {
080 settingValue = false;
081 }
082 }
083
084 public void propertyChange(PropertyChangeEvent e) {
085 if (logger.isDebugEnabled()) {
086 Class valueClass = (e.getNewValue() != null ? e.getNewValue().getClass() : null);
087 logger.debug("Formatted text field property '" + e.getPropertyName() + "' changed; new value is '"
088 + e.getNewValue() + "', valueClass=" + valueClass);
089 }
090 adaptedValueChanged(component.getValue());
091 }
092
093 public void insertUpdate(DocumentEvent e) {
094 tryToCommitEdit();
095 }
096
097 public void removeUpdate(DocumentEvent e) {
098 if (!ignoreValue)
099 tryToCommitEdit();
100 else
101 ignoreValue = false;
102 }
103
104 public void changedUpdate(DocumentEvent e) {
105 tryToCommitEdit();
106 }
107
108 private void tryToCommitEdit() {
109 if (!settingValue) {
110 try {
111 component.commitEdit();
112 } catch (ParseException e) {
113 // ignore
114 }
115 }
116 }
117
118 public void focusGained(FocusEvent e) {
119 ignoreValue = false;
120 }
121
122 public void focusLost(FocusEvent e) {
123 ignoreValue = true;
124 }
125 }