/** * Copyright (C) 2015 Valkyrie RCP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.valkyriercp.form.binding.swing; import net.miginfocom.swing.MigLayout; import org.valkyriercp.binding.form.FormModel; import org.valkyriercp.component.BigDecimalTextField; import org.valkyriercp.component.UserInputListener; import org.valkyriercp.form.binding.support.CustomBinding; import javax.swing.*; import java.math.BigDecimal; /** * Binding to handle Numbers. Can be configured in different with shiftFactor/shiftScale and * decorations. * * @author jh * */ public class NumberBinding extends CustomBinding implements UserInputListener { protected final BigDecimalTextField numberField; protected final boolean readOnly; private final String leftDecoration; private final String rightDecoration; private final BigDecimal shiftFactor; private int shiftScale; private boolean isSettingValue = false; /** * Creates a NumberBinding. * * @param requiredClass * Required class for this binding. * @param component * The BigDecimalTextField to use. * @param readOnly * Force readonly at all times. * @param leftDecoration * Decorating label with string at left side. * @param rightDecoration * Decorating label with string at right side. * @param shiftFactor * Shifting factor to use when setting/getting number in * inputfield. Can eg be used to display percentages as ###.## * instead of #.####. * @param shiftScale * Scale to set on BigDecimal. * @param formModel * FormModel. * @param formPropertyPath * PropertyPath. */ public NumberBinding(Class requiredClass, BigDecimalTextField component, boolean readOnly, String leftDecoration, String rightDecoration, BigDecimal shiftFactor, int shiftScale, FormModel formModel, String formPropertyPath) { super(formModel, formPropertyPath, requiredClass); this.numberField = component; this.readOnly = readOnly; this.leftDecoration = leftDecoration; this.rightDecoration = rightDecoration; this.shiftFactor = shiftFactor; this.shiftScale = shiftScale; } /** * @inheritDoc */ protected void valueModelChanged(Object newValue) { this.isSettingValue = true; if ((this.shiftFactor != null) && (newValue != null)) // if shifting, class is BigDecimal this.numberField.setValue(((BigDecimal) newValue).multiply(this.shiftFactor)); else this.numberField.setValue((Number) newValue); readOnlyChanged(); this.isSettingValue = false; } /** * @inheritDoc */ protected JComponent doBindControl() { valueModelChanged(getValue()); this.numberField.addUserInputListener(this); if ((this.leftDecoration == null) && (this.rightDecoration == null)) return this.numberField; return createPanelWithDecoration(); } /** * @inheritDoc */ public void update(JComponent component) { if (!this.isSettingValue && NumberBinding.this.numberField.isEditable()) { Number value = NumberBinding.this.numberField.getValue(); if ((value != null) && (NumberBinding.this.shiftFactor != null)) NumberBinding.this.controlValueChanged(((BigDecimal) value).divide(NumberBinding.this.shiftFactor, NumberBinding.this.shiftScale, BigDecimal.ROUND_UP)); else NumberBinding.this.controlValueChanged(value); } } /** * Create a panel with (possibly) decorations on both sides. * * TODO This leaves one problem: when validating and eg coloring/adding overlay the * panel is used instead of the inputfield. There could be an interface that * returns the correct component to be handled while validating. * * @return a decorated component which contains the inputfield. */ private JComponent createPanelWithDecoration() { StringBuffer columnLayout = new StringBuffer(); if (this.leftDecoration != null) columnLayout.insert(0, "[]"); columnLayout.append("[grow]"); if (this.rightDecoration != null) columnLayout.append("[]"); JPanel panel = new JPanel(new MigLayout("insets 0", columnLayout.toString(), "")) { public void requestFocus() { NumberBinding.this.numberField.requestFocus(); } }; if (this.leftDecoration != null) { panel.add(new JLabel(this.leftDecoration)); } panel.add(this.numberField, "grow"); if (this.rightDecoration != null) { panel.add(new JLabel(this.rightDecoration)); } return panel; } /** * @inheritDoc */ protected void readOnlyChanged() { numberField.setEditable(isEnabled() && !this.readOnly && !isReadOnly()); } /** * @inheritDoc */ protected void enabledChanged() { this.numberField.setEnabled(isEnabled()); readOnlyChanged(); } }