/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.fib.view.widget; import java.awt.ComponentOrientation; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.util.logging.Logger; import javax.swing.BorderFactory; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JSpinner.DefaultEditor; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openflexo.antar.binding.TypeUtils; import org.openflexo.fib.controller.FIBController; import org.openflexo.fib.model.FIBNumber; import org.openflexo.fib.view.FIBWidgetView; import org.openflexo.localization.FlexoLocalization; import org.openflexo.toolbox.ToolBox; /** * Represents a widget able to edit an Long or an Integer object * * @author sguerin */ public abstract class FIBNumberWidget<T extends Number> extends FIBWidgetView<FIBNumber, JSpinner, T> { static final Logger logger = Logger.getLogger(FIBNumberWidget.class.getPackage().getName()); boolean validateOnReturn; protected boolean ignoreTextfieldChanges = false; private JPanel container; private JCheckBox checkBox; JSpinner valueChooser; /** * @param model */ public FIBNumberWidget(FIBNumber model, FIBController controller) { super(model, controller); validateOnReturn = model.getValidateOnReturn(); Number min = model.getMinValue(); Number max = model.getMaxValue(); Number inc = model.getIncrement(); container = new JPanel(new GridBagLayout()); container.setOpaque(false); checkBox = new JCheckBox(); checkBox.setToolTipText(FlexoLocalization.localizedForKey("undefined_value", checkBox)); checkBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { valueChooser.setEnabled(!checkBox.isSelected()); updateModelFromWidget(); } }); SpinnerNumberModel valueModel = makeSpinnerModel(); valueChooser = new JSpinner(valueModel); valueChooser.setEditor(new JSpinner.NumberEditor(valueChooser /* , "#.##" */)); valueChooser.setValue(getDefaultValue()); valueChooser.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (e.getSource() == valueChooser && !ignoreTextfieldChanges) { updateModelFromWidget(); } } }); valueChooser.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); JComponent editor = valueChooser.getEditor(); if (editor instanceof DefaultEditor) { ((DefaultEditor) editor).getTextField().setHorizontalAlignment(SwingConstants.LEFT); if (!ToolBox.isMacOSLaf()) { ((DefaultEditor) editor).getTextField().setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); } } if (model.getColumns() != null) { getTextField().setColumns(model.getColumns()); } else { getTextField().setColumns(getDefaultColumns()); } if (isReadOnly()) { valueChooser.setEnabled(false); } getJComponent().addFocusListener(this); getTextField().addFocusListener(this); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1.0; container.add(valueChooser, gbc); gbc.fill = GridBagConstraints.NONE; gbc.weightx = 0; gbc.anchor = GridBagConstraints.LINE_START; container.add(checkBox, gbc); updateCheckboxVisibility(); updateFont(); } @Override public void focusGained(FocusEvent event) { super.focusGained(event); if (event.getSource() == getTextField()) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { getTextField().selectAll(); } }); } } protected abstract SpinnerNumberModel makeSpinnerModel(); public abstract T getDefaultValue(); @Override public synchronized boolean updateWidgetFromModel() { // logger.info("updateWidgetFromModel() with "+getValue()); T editedValue = null; if (!checkBox.isSelected()) { editedValue = getEditedValue(); } if (notEquals(getValue(), editedValue)) { widgetUpdating = true; valueChooser.setEnabled((getValue() != null || !getWidget().getAllowsNull()) && isEnabled()); checkBox.setSelected(getValue() == null); T currentValue = null; if (getValue() == null) { // setValue(getDefaultValue()); currentValue = getDefaultValue(); } else { try { currentValue = getValue(); } catch (ClassCastException e) { logger.warning("ClassCastException: " + e.getMessage()); logger.warning("Data: " + getWidget().getData() + " returned " + getValue()); } } ignoreTextfieldChanges = true; try { valueChooser.setValue(currentValue); } catch (IllegalArgumentException e) { logger.warning("IllegalArgumentException: " + e.getMessage()); } ignoreTextfieldChanges = false; widgetUpdating = false; return true; } return false; } protected abstract T getEditedValue(); /** * Update the model given the actual state of the widget */ @Override public synchronized boolean updateModelFromWidget() { T editedValue = null; if (!checkBox.isSelected()) { editedValue = getEditedValue(); } if (notEquals(getValue(), editedValue)) { if (isReadOnly()) { return false; } modelUpdating = true; setValue(editedValue); modelUpdating = false; return true; } return false; } @Override public JPanel getJComponent() { return container; } @Override public JSpinner getDynamicJComponent() { return valueChooser; } public JFormattedTextField getTextField() { JComponent editor = valueChooser.getEditor(); if (editor instanceof JSpinner.DefaultEditor) { return ((JSpinner.DefaultEditor) editor).getTextField(); } return null; } public abstract int getDefaultColumns(); public static class FIBByteWidget extends FIBNumberWidget<Byte> { public FIBByteWidget(FIBNumber model, FIBController controller) { super(model, controller); } @Override protected SpinnerNumberModel makeSpinnerModel() { Byte min = getWidget().retrieveMinValue().byteValue(); Byte max = getWidget().retrieveMaxValue().byteValue(); Byte inc = getWidget().retrieveIncrement().byteValue(); try { return new SpinnerNumberModel(getDefaultValue(), min, max, inc); } catch (IllegalArgumentException e) { return new SpinnerNumberModel(min, min, max, inc); } } @Override public Byte getDefaultValue() { return new Byte((byte) 0); } @Override protected Byte getEditedValue() { return ((Number) valueChooser.getValue()).byteValue(); } @Override public int getDefaultColumns() { return 4; } } public static class FIBShortWidget extends FIBNumberWidget<Short> { public FIBShortWidget(FIBNumber model, FIBController controller) { super(model, controller); } @Override protected SpinnerNumberModel makeSpinnerModel() { Short min = getWidget().retrieveMinValue().shortValue(); Short max = getWidget().retrieveMaxValue().shortValue(); Short inc = getWidget().retrieveIncrement().shortValue(); try { return new SpinnerNumberModel(getDefaultValue(), min, max, inc); } catch (IllegalArgumentException e) { return new SpinnerNumberModel(min, min, max, inc); } } @Override public Short getDefaultValue() { return new Short((short) 0); } @Override protected Short getEditedValue() { return ((Number) valueChooser.getValue()).shortValue(); } @Override public int getDefaultColumns() { return 6; } } public static class FIBIntegerWidget extends FIBNumberWidget<Integer> { public FIBIntegerWidget(FIBNumber model, FIBController controller) { super(model, controller); } @Override protected SpinnerNumberModel makeSpinnerModel() { Integer min = getWidget().retrieveMinValue().intValue(); Integer max = getWidget().retrieveMaxValue().intValue(); Integer inc = getWidget().retrieveIncrement().intValue(); try { return new SpinnerNumberModel(getDefaultValue(), min, max, inc); } catch (IllegalArgumentException e) { return new SpinnerNumberModel(min, min, max, inc); } } @Override public Integer getDefaultValue() { if (getWidget().getMinValue() != null && getWidget().getMinValue().intValue() > 0) { return getWidget().getMinValue().intValue(); } return Integer.valueOf(0); } @Override protected Integer getEditedValue() { return ((Number) valueChooser.getValue()).intValue(); } @Override public int getDefaultColumns() { return 8; } } public static class FIBLongWidget extends FIBNumberWidget<Long> { public FIBLongWidget(FIBNumber model, FIBController controller) { super(model, controller); } @Override protected SpinnerNumberModel makeSpinnerModel() { Long min = getWidget().retrieveMinValue().longValue(); Long max = getWidget().retrieveMaxValue().longValue(); Long inc = getWidget().retrieveIncrement().longValue(); try { return new SpinnerNumberModel(getDefaultValue(), min, max, inc); } catch (IllegalArgumentException e) { return new SpinnerNumberModel(min, min, max, inc); } } @Override public Long getDefaultValue() { return Long.valueOf(0); } @Override protected Long getEditedValue() { return ((Number) valueChooser.getValue()).longValue(); } @Override public int getDefaultColumns() { return 10; } } public static class FIBFloatWidget extends FIBNumberWidget<Float> { public FIBFloatWidget(FIBNumber model, FIBController controller) { super(model, controller); } @Override protected SpinnerNumberModel makeSpinnerModel() { float min = getWidget().retrieveMinValue().floatValue(); float max = getWidget().retrieveMaxValue().floatValue(); float inc = getWidget().retrieveIncrement().floatValue(); try { return new SpinnerNumberModel((Number) getDefaultValue(), min, max, inc); } catch (IllegalArgumentException e) { return new SpinnerNumberModel(min, min, max, inc); } } @Override public Float getDefaultValue() { return Float.valueOf(0.0f); } @Override protected Float getEditedValue() { return ((Number) valueChooser.getValue()).floatValue(); } @Override public int getDefaultColumns() { return 10; } } public static class FIBDoubleWidget extends FIBNumberWidget<Double> { public FIBDoubleWidget(FIBNumber model, FIBController controller) { super(model, controller); } @Override protected SpinnerNumberModel makeSpinnerModel() { Double min = getWidget().retrieveMinValue().doubleValue(); Double max = getWidget().retrieveMaxValue().doubleValue(); Double inc = getWidget().retrieveIncrement().doubleValue(); try { return new SpinnerNumberModel(getDefaultValue(), min, max, inc); } catch (IllegalArgumentException e) { return new SpinnerNumberModel(min, min, max, inc); } } @Override public Double getDefaultValue() { return Double.valueOf(0.0); } @Override protected Double getEditedValue() { return ((Number) valueChooser.getValue()).doubleValue(); } @Override public int getDefaultColumns() { return 10; } } public void updateCheckboxVisibility() { checkBox.setVisible(getWidget().getAllowsNull() && !TypeUtils.isPrimitive(getComponent().getDataType())); } public void updateColumns() { if (getComponent().getColumns() != null) { getTextField().setColumns(getComponent().getColumns()); } else { getTextField().setColumns(0); } container.revalidate(); } }