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 }