/******************************************************************************* * Copyright 2013 Geoscience Australia * * 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 au.gov.ga.earthsci.application.widgets; import gov.nasa.worldwind.geom.Position; import java.text.NumberFormat; import java.util.EventListener; import java.util.EventObject; import java.util.Vector; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; /** * A widget that allows for the viewing / editing of a {@link Double} value. * <p/> * If style specifies {@code READ_ONLY}, the widget will present the * {@link Double} value as a read-only label. Otherwise the editor will present * an editable text field. * * <dl> * <dt><b>Styles:</b></dt> * <dd>READ_ONLY</dd> * <dt><b>Events:</b></dt> * <dd>SWT.Modify ({@code data} is a {@code String})</dd> * </dl> * * @author Michael de Hoog */ public class DoubleEditor extends Composite { private final Text text; private int numDecimalPlaces = 8; private final AtomicBoolean quiet = new AtomicBoolean(false); private final Vector<DoubleEditorListener> listeners = new Vector<DoubleEditorListener>(); /** * Create a new {@link DoubleEditor} */ public DoubleEditor(Composite parent, int style) { super(parent, style); GridLayout layout = new GridLayout(3, false); layout.horizontalSpacing = 2; setLayout(layout); int fieldStyle = style | SWT.BORDER; GridData fieldLayoutData = new GridData(GridData.FILL_HORIZONTAL); ModifyListener modifyListener = new ModifyListener() { @Override public void modifyText(ModifyEvent e) { notifyModify(); } }; addLabel(style, Messages.DoubleEditor_Label); text = addText(fieldStyle, fieldLayoutData, modifyListener); } private Label addLabel(int style, String text) { Label result = new Label(this, style); result.setText(text); return result; } private Text addText(int style, Object layoutData, ModifyListener listener) { Text result = new Text(this, style); result.setLayoutData(layoutData); result.addModifyListener(listener); return result; } /** * Returns the current {@link Position} value this editor reflects, or * <code>null</code> if it is invalid. */ public Double getDoubleValue() { try { return Double.parseDouble(text.getText().trim()); } catch (Exception e) { return null; } } /** * Set the current {@link Position} value on this editor. * <p/> * This will reset the fields on the editor to reflect those of the provided * {@link Position}. * <p/> * If <code>null</code> is provided, the fields of this editor will be * cleared. */ public void setDoubleValue(Double d) { quiet.set(true); if (d != null) { NumberFormat numberFormat = getNumberFormat(); text.setText(numberFormat.format(d)); } else { text.setText(""); //$NON-NLS-1$ } quiet.set(false); notifyModify(); } /** * Set the number of decimal places to use when representing position values */ public void setNumDecimalPlaces(int numDecimalPlaces) { this.numDecimalPlaces = numDecimalPlaces; } private NumberFormat getNumberFormat() { NumberFormat numberFormat = NumberFormat.getNumberInstance(); numberFormat.setMaximumFractionDigits(numDecimalPlaces); numberFormat.setMinimumFractionDigits(numDecimalPlaces); numberFormat.setGroupingUsed(false); return numberFormat; } /** * Register a {@link DoubleEditorListener} on this editor */ public void addDoubleEditorListener(DoubleEditorListener l) { if (l == null) { return; } listeners.add(l); } /** * Remove the {@link DoubleEditorListener} from this editor */ public void removeDoubleEditorListener(DoubleEditorListener l) { if (l == null) { return; } listeners.remove(l); } private void notifyModify() { if (quiet.get()) { return; } // Send a low-level event Event e = new Event(); e.item = this; e.widget = this; e.type = SWT.Modify; e.display = Display.getCurrent(); e.data = text.getText(); notifyListeners(SWT.Modify, e); // Then a high-level event if (listeners.isEmpty()) { return; } DoubleChangedEvent dce = new DoubleChangedEvent(this, getDoubleValue(), text.getText()); for (DoubleEditorListener l : listeners) { l.doubleChanged(dce); } } /** * An event that is fired when a user's text change alters the value of a * {@link Double} within a {@link DoubleEditor} */ public final static class DoubleChangedEvent extends EventObject { private final boolean valid; private final double d; private final String val; private DoubleChangedEvent(Object source, Double d, String val) { super(source); this.d = d; this.valid = d != null; this.val = val; } public boolean isValid() { return valid; } public Double getDouble() { return d; } public String getDoubleText() { return val; } } /** * An interface for classes that wish to be notified of changes to the * {@link Double} value backing a {@link DoubleEditor}. */ public static interface DoubleEditorListener extends EventListener { /** * Invoked when changes to the editor have resulted in a changed * {@link Double} */ void doubleChanged(DoubleChangedEvent e); } }