/*
* Rapid Beans Framework: EditorPropertyQuantitySwing.java
*
* Copyright (C) 2009 Martin Bluemel
*
* Creation Date: 12/12/2006
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copies of the GNU Lesser General Public License and the
* GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
package org.rapidbeans.presentation.swing;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.Locale;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.rapidbeans.core.basic.Property;
import org.rapidbeans.core.basic.PropertyQuantity;
import org.rapidbeans.core.basic.RapidQuantity;
import org.rapidbeans.core.exception.RapidBeansRuntimeException;
import org.rapidbeans.core.exception.ValidationException;
import org.rapidbeans.core.type.TypePropertyQuantity;
import org.rapidbeans.core.type.TypeRapidEnum;
import org.rapidbeans.core.type.TypeRapidQuantityConversionTable;
import org.rapidbeans.presentation.Application;
import org.rapidbeans.presentation.EditorBean;
import org.rapidbeans.presentation.config.ConfigPropEditorBean;
/**
* a special property editor for RapidQuantity properties. Combines a text field
* for editing the magnitude with a combo box for selecting the unit.
*
* @author Martin Bluemel
*/
public class EditorPropertyQuantitySwing extends EditorPropertySwing {
/**
* the text field.
*/
private JTextField text = new JTextField();
/**
* the combo box.
*/
private JComboBox combobox = new JComboBox();
/**
* the panel.
*/
private BBEditorPropertyQuantitySwingPanel panel = null;
/**
* the panel's layout manager.
*/
private LayoutManager layout = new GridBagLayout();
/**
* @return the editor's widget
*/
public Object getWidget() {
return this.panel;
}
/**
* @return the editor's widget
*/
public JTextField getWidgetTextField() {
return this.text;
}
/**
* @return the editor's widget
*/
public JComboBox getWidgetComboBox() {
return this.combobox;
}
/**
* setter.
*
* @param prop
* the property to set.
*/
@Override
protected void setProperty(final Property prop) {
super.setProperty(prop);
}
/**
* constructor.
*
* @param prop
* the bean property to edit
* @param propBak
* the bean property backup
* @param bizBeanEditor
* the parent bean editor
* @param client
* the client
*/
public EditorPropertyQuantitySwing(final Application client, final EditorBean bizBeanEditor, final Property prop,
final Property propBak) {
super(client, bizBeanEditor, prop, propBak);
this.panel = new BBEditorPropertyQuantitySwingPanel(this);
super.initColors();
if (!(prop instanceof PropertyQuantity)) {
throw new RapidBeansRuntimeException("invalid propperty for a quantity editor");
}
if (prop.getType().isKeyCandidate()) {
if (this.getBeanEditor().getParentBean() == null) {
this.text.setEditable(false);
this.combobox.setEnabled(false);
this.combobox.setBackground(COLOR_KEY);
}
}
this.text.addKeyListener(new KeyListener() {
public void keyTyped(final KeyEvent e) {
}
public void keyPressed(final KeyEvent e) {
}
public void keyReleased(final KeyEvent e) {
fireInputFieldChanged();
}
});
this.combobox.setModel(new ModelComboBoxEnum((TypePropertyQuantity) prop.getType()));
this.combobox.setRenderer(new RendererListEnum(client.getCurrentLocale(), this));
this.combobox.addItemListener(new ItemListener() {
public void itemStateChanged(final ItemEvent e) {
convertToNewUnit();
fireInputFieldChanged();
}
});
this.panel.setLayout(this.layout);
this.panel.add(this.text, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
GridBagConstraints.HORIZONTAL, new Insets(5, 0, 5, 5), 0, 0));
this.panel.add(this.combobox, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
this.updateUI();
final ConfigPropEditorBean cfg = getConfig();
if (prop.getReadonly() || (cfg != null && !cfg.getEnabled())) {
this.panel.setEnabled(false);
}
}
private RapidQuantity lastQuant = null;
/**
* convert the value shown to the new unit
*/
private void convertToNewUnit() {
try {
this.setUIEventLock();
RapidQuantity value = getInputFieldValue();
if (value != null && this.lastQuant != null) {
value = lastQuant.convert(value.getUnit());
this.text.setText(value.getMagnitude().toString());
}
this.lastQuant = value;
} finally {
this.releaseUIEventLock();
}
}
/**
* update the string presented in the editor.
*/
public void updateUI() {
try {
this.setUIEventLock();
final RapidQuantity value = (RapidQuantity) this.getProperty().getValue();
if (value == null) {
this.text.setText("");
this.combobox.setSelectedItem(((TypePropertyQuantity) (this.getProperty().getType())).getDefaultUnit());
} else {
if (value.getMagnitude() == null) {
this.text.setText("");
} else {
this.text.setText(value.getMagnitude().toString());
}
if (value.getUnit() == null) {
this.combobox.setSelectedItem(((TypePropertyQuantity) (this.getProperty().getType()))
.getDefaultUnit());
} else {
this.combobox.setSelectedItem(value.getUnit());
}
}
if (this.combobox.getSelectedItem() == null) {
preSelectUnit();
}
} finally {
this.releaseUIEventLock();
}
}
/**
* @return the Text field's content
*/
public RapidQuantity getInputFieldValue() {
RapidQuantity ifValue = null;
if (this.text.getText().trim().length() > 0) {
if (this.combobox.getSelectedItem() == null) {
throw new ValidationException("invalid.quantity.unit.missing", ifValue,
"no unit specified for quantity");
}
try {
final String qtypename = ((TypePropertyQuantity) this.getProperty().getType()).getQuantitytype()
.getName();
final String ifValueString = getInputFieldValueString();
ifValue = RapidQuantity.createInstance(qtypename, ifValueString);
} catch (RapidBeansRuntimeException e) {
final Throwable c1 = e.getCause();
if (c1 == null) {
throw e;
} else if (c1 instanceof ValidationException) {
throw (ValidationException) c1;
} else if (c1 instanceof InvocationTargetException) {
final Throwable c2 = c1.getCause();
if (c2 == null) {
throw e;
} else if (c2 instanceof ValidationException) {
throw (ValidationException) c2;
} else {
throw e;
}
} else {
throw e;
}
}
}
return ifValue;
}
/**
* validate an input field.
*
* @return if the string in the input field is valid or at least could at
* least get after appending additional characters.
*
* @param ex
* the validation exception
*/
protected boolean hasPotentiallyValidInputField(final ValidationException ex) {
boolean potentiallyValid = false;
if (ex.getSignature().endsWith("incomplete")) {
potentiallyValid = this.checkLocalNumber(false);
} else {
final String s = this.text.getText().trim();
final int slen = s.length();
char c;
potentiallyValid = true;
for (int i = 0; potentiallyValid == true && i < slen; i++) {
c = s.charAt(i);
if ((c < '0' || c > '9') && c != 'E' && c != 'e') {
potentiallyValid = false;
}
}
}
return potentiallyValid;
}
/**
* check the localized number.
*
* @param completenessRequired
* if completeness is required
*
* @return if the local number is ok
*/
protected boolean checkLocalNumber(final boolean completenessRequired) {
boolean ok = true;
try {
parseLocalNumber(completenessRequired);
} catch (ValidationException e) {
ok = false;
}
return ok;
}
/**
* pre select an appropriate unit
*/
protected void preSelectUnit() {
final TypePropertyQuantity proptype = (TypePropertyQuantity) this.getProperty().getType();
if (proptype.getDefaultUnit() != null) {
// select the property's default unit
this.combobox.setSelectedItem(proptype.getDefaultUnit());
} else {
final TypeRapidQuantityConversionTable ct = proptype.getQuantitytype().getConversionTable();
if (ct.getNormUnit() != null) {
// select the quantity's norm unit
this.combobox.setSelectedItem(ct.getNormUnit());
} else {
// select the quantity's first unit
final TypeRapidEnum et = proptype.getQuantitytype().getUnitInfo();
this.combobox.setSelectedItem(et.elementOf(0));
}
}
}
/**
* A helper class for number parsing.
*
* @author Martin Bluemel
*/
private class ParsedNumber {
// /**
// * the number.
// */
// private BigDecimal number = null;
// /**
// * the ok flag.
// */
// private boolean ok = false;
/**
* constructor.
*
* @param s
* the number string
* @param l
* the locale
*/
ParsedNumber(final String s, final Locale l) {
if (s != null && s.length() > 0) {
try {
new BigDecimal(s);
} catch (NumberFormatException e) {
throw new ValidationException("invalid.quantity.magnitude.only", s, "\"" + s
+ "\" is not a valid number.", new Object[] { s });
}
}
}
// /**
// * @param s the day String to set
// */
// public void setNumber(final String s) {
// this.number = new BigDecimal(s);
// }
// /**
// * @param o the ok to set
// */
// public void setOk(final boolean o) {
// this.ok = o;
// }
// /**
// * @return the number
// */
// public BigDecimal getNumber() {
// return number;
// }
// /**
// * @return if the property is O.K.
// */
// public boolean isOk() {
// return ok;
// }
}
/**
* validate the date field.
*
* @return if the string in the date field is valid or at least could at
* least get after appending additional characters.
*
* @param completenessRequired
* if the input fields must be completeS
*/
private ParsedNumber parseLocalNumber(final boolean completenessRequired) {
ParsedNumber number = new ParsedNumber(this.text.getText(), this.getLocale().getLocale());
return number;
}
/**
* @return the input field value as string.
*/
public String getInputFieldValueString() {
return this.text.getText().trim() + ' ' + this.combobox.getSelectedItem();
}
/**
* Set the background.
*
* @param bg
* the new background color
*/
protected void setBackground(final Color bg) {
this.text.setBackground(bg);
this.combobox.setBackground(bg);
}
/**
* Set enabled.
*
* @param enabled
* if enabled or not
*/
protected void setEnabled(final boolean enabled) {
this.text.setEnabled(enabled);
this.combobox.setEnabled(enabled);
}
/**
* special panel that forwards set background call to its components.
*
* @author Martin Bluemel
*/
private final class BBEditorPropertyQuantitySwingPanel extends JPanel {
/**
* serialization id.
*/
private static final long serialVersionUID = 1L;
/**
* the parent property editor for the panel.
*/
private final EditorPropertyQuantitySwing parentEditor;
/**
* constructor with components.
*
* @param parent
* the parent editor
*/
public BBEditorPropertyQuantitySwingPanel(final EditorPropertyQuantitySwing parent) {
super();
this.parentEditor = parent;
}
/**
* forwards set background call to its component widgets.
*
* @param bg
* the new background color
*/
public void setBackground(final Color bg) {
if (this.parentEditor != null) {
this.parentEditor.setBackground(bg);
} else {
super.setBackground(bg);
}
}
/**
* forwards set enabled call to its component widgets.
*
* @param enabled
* if enabled or not
*/
public void setEnabled(final boolean enabled) {
if (this.parentEditor != null) {
this.parentEditor.setEnabled(enabled);
} else {
super.setEnabled(enabled);
}
}
}
}