001 package org.springframework.richclient.form.binding.swing;
002
003 import com.jgoodies.forms.layout.CellConstraints;
004 import com.jgoodies.forms.layout.FormLayout;
005 import org.springframework.binding.form.FormModel;
006 import org.springframework.richclient.components.BigDecimalTextField;
007 import org.springframework.richclient.components.UserInputListener;
008 import org.springframework.richclient.form.binding.support.CustomBinding;
009
010 import javax.swing.*;
011 import java.math.BigDecimal;
012
013 /**
014 * Binding to handle Numbers. Can be configured in different with shiftFactor/shiftScale and
015 * decorations.
016 *
017 * @author jh
018 * @see org.springframework.richclient.form.binding.swing.NumberBinder
019 *
020 */
021 public class NumberBinding extends CustomBinding implements UserInputListener
022 {
023
024 protected final BigDecimalTextField numberField;
025
026 protected final boolean readOnly;
027
028 private final String leftDecoration;
029
030 private final String rightDecoration;
031
032 private final BigDecimal shiftFactor;
033
034 private int shiftScale;
035
036 private boolean isSettingValue = false;
037
038 /**
039 * Creates a NumberBinding.
040 *
041 * @param requiredClass
042 * Required class for this binding.
043 * @param component
044 * The BigDecimalTextField to use.
045 * @param readOnly
046 * Force readonly at all times.
047 * @param leftDecoration
048 * Decorating label with string at left side.
049 * @param rightDecoration
050 * Decorating label with string at right side.
051 * @param shiftFactor
052 * Shifting factor to use when setting/getting number in
053 * inputfield. Can eg be used to display percentages as ###.##
054 * instead of #.####.
055 * @param shiftScale
056 * Scale to set on BigDecimal.
057 * @param formModel
058 * FormModel.
059 * @param formPropertyPath
060 * PropertyPath.
061 */
062 public NumberBinding(Class requiredClass, BigDecimalTextField component, boolean readOnly,
063 String leftDecoration, String rightDecoration, BigDecimal shiftFactor, int shiftScale,
064 FormModel formModel, String formPropertyPath)
065 {
066 super(formModel, formPropertyPath, requiredClass);
067 this.numberField = component;
068 this.readOnly = readOnly;
069 this.leftDecoration = leftDecoration;
070 this.rightDecoration = rightDecoration;
071 this.shiftFactor = shiftFactor;
072 this.shiftScale = shiftScale;
073 }
074
075 /**
076 * @inheritDoc
077 */
078 protected void valueModelChanged(Object newValue)
079 {
080 this.isSettingValue = true;
081 if ((this.shiftFactor != null) && (newValue != null)) // if shifting, class is BigDecimal
082 this.numberField.setValue(((BigDecimal) newValue).multiply(this.shiftFactor));
083 else
084 this.numberField.setValue((Number) newValue);
085 readOnlyChanged();
086 this.isSettingValue = false;
087 }
088
089 /**
090 * @inheritDoc
091 */
092 protected JComponent doBindControl()
093 {
094 valueModelChanged(getValue());
095 this.numberField.addUserInputListener(this);
096 if ((this.leftDecoration == null) && (this.rightDecoration == null))
097 return this.numberField;
098
099 return createPanelWithDecoration();
100 }
101
102 /**
103 * @inheritDoc
104 */
105 public void update(JComponent component)
106 {
107 if (!this.isSettingValue && NumberBinding.this.numberField.isEditable())
108 {
109 Number value = NumberBinding.this.numberField.getValue();
110 if ((value != null) && (NumberBinding.this.shiftFactor != null))
111 NumberBinding.this.controlValueChanged(((BigDecimal) value).divide(NumberBinding.this.shiftFactor,
112 NumberBinding.this.shiftScale, BigDecimal.ROUND_UP));
113 else
114 NumberBinding.this.controlValueChanged(value);
115 }
116 }
117
118 /**
119 * Create a panel with (possibly) decorations on both sides.
120 *
121 * TODO This leaves one problem: when validating and eg coloring/adding overlay the
122 * panel is used instead of the inputfield. There could be an interface that
123 * returns the correct component to be handled while validating.
124 *
125 * @return a decorated component which contains the inputfield.
126 */
127 private JComponent createPanelWithDecoration()
128 {
129 StringBuffer columnLayout = new StringBuffer();
130 if (this.leftDecoration != null)
131 columnLayout.append("pref, 3dlu, ");
132 columnLayout.append("fill:pref:grow");
133 if (this.rightDecoration != null)
134 columnLayout.append(", 3dlu, pref");
135
136 JPanel panel = new JPanel(new FormLayout(columnLayout.toString(),
137 "fill:pref:grow")) {
138 public void requestFocus() {
139 NumberBinding.this.numberField.requestFocus();
140 }
141
142 };
143 CellConstraints cc = new CellConstraints();
144 int columnIndex = 1;
145 if (this.leftDecoration != null)
146 {
147 panel.add(new JLabel(this.leftDecoration), cc.xy(columnIndex, 1));
148 columnIndex += 2;
149 }
150 panel.add(this.numberField, cc.xy(columnIndex, 1));
151 if (this.rightDecoration != null)
152 {
153 columnIndex += 2;
154 panel.add(new JLabel(this.rightDecoration), cc.xy(columnIndex, 1));
155 }
156 return panel;
157 }
158
159 /**
160 * @inheritDoc
161 */
162 protected void readOnlyChanged()
163 {
164 numberField.setEditable(isEnabled() && !this.readOnly && !isReadOnly());
165 }
166
167 /**
168 * @inheritDoc
169 */
170 protected void enabledChanged()
171 {
172 this.numberField.setEnabled(isEnabled());
173 readOnlyChanged();
174 }
175 }