/*******************************************************************************
* 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.Vec4;
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 can be used to edit the fields of a {@link Vec4} instance
* <p/>
* If style specifies {@code READ_ONLY}, the widget will present the
* {@link Vec4} fields as read-only labels. Otherwise the editor will present
* editable text fields.
*
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>READ_ONLY</dd>
* <dt><b>Events:</b></dt>
* <dd>SWT.Modify ({@code data} is a 4 element array of {@code String} with
* [x,y,z,w])</dd>
* </dl>
*
* A high-level event/listener API is also provided. See
* {@link #addVec4EditorListener()}.
*
* @author James Navin (james.navin@ga.gov.au)
*/
public class Vec4Editor extends Composite
{
private final Text xText;
private final Text yText;
private final Text zText;
private final Text wText;
private final AtomicBoolean quiet = new AtomicBoolean(false);
private final Vector<Vec4EditorListener> listeners = new Vector<Vec4EditorListener>();
private int numDecimalPlaces = 8;
/**
* Create a new editor instance
*/
public Vec4Editor(Composite parent, int style)
{
super(parent, style);
GridLayout layout = new GridLayout(2, 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.Vec4Editor_XLabel);
xText = addText(fieldStyle, fieldLayoutData, modifyListener);
addLabel(style, Messages.Vec4Editor_YLabel);
yText = addText(fieldStyle, fieldLayoutData, modifyListener);
addLabel(style, Messages.Vec4Editor_ZLabel);
zText = addText(fieldStyle, fieldLayoutData, modifyListener);
addLabel(style, Messages.Vec4Editor_WLabel);
wText = 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;
}
/**
* Set the {@link Vec4} value on this editor.
* <p/>
* This will update the fields of the editor to reflect those contained in
* the provided {@link Vec4}. If the provided {@link Vec4} is
* <code>null</code>, the fields of the editor will be cleared.
*/
public void setVec4Value(Vec4 vec)
{
quiet.set(true);
if (vec == null)
{
xText.setText(""); //$NON-NLS-1$
yText.setText(""); //$NON-NLS-1$
zText.setText(""); //$NON-NLS-1$
wText.setText(""); //$NON-NLS-1$
}
else
{
NumberFormat f = getNumberFormat();
xText.setText(f.format(vec.x));
yText.setText(f.format(vec.y));
zText.setText(f.format(vec.z));
wText.setText(f.format(vec.w));
}
quiet.set(false);
notifyModify();
}
/**
* Return the {@link Vec4} value represented by this editor, or
* <code>null</code> if it is invalid.
*/
public Vec4 getVec4Value()
{
try
{
double x = Double.parseDouble(xText.getText().trim());
double y = Double.parseDouble(yText.getText().trim());
double z = Double.parseDouble(zText.getText().trim());
double w = Double.parseDouble(wText.getText().trim());
return new Vec4(x, y, z, w);
}
catch (Exception e)
{
return null;
}
}
/**
* Set the number of decimal places to use when representing vector 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 Vec4EditorListener} on this editor.
*/
public void addVec4EditorListener(Vec4EditorListener l)
{
if (l == null)
{
return;
}
listeners.add(l);
}
/**
* Remove the provided {@link Vec4EditorListener} from this editor.
*/
public void removeVec4EditorListener(Vec4EditorListener l)
{
if (l == null)
{
return;
}
listeners.remove(l);
}
private void notifyModify()
{
if (quiet.get())
{
return;
}
Event e = new Event();
e.widget = this;
e.item = this;
e.type = SWT.Modify;
e.display = Display.getCurrent();
e.data = new String[] { xText.getText(), yText.getText(), zText.getText(), wText.getText() };
notifyListeners(SWT.Modify, e);
if (listeners.isEmpty())
{
return;
}
Vec4ChangedEvent vce =
new Vec4ChangedEvent(this, getVec4Value(), xText.getText(), yText.getText(), zText.getText(),
wText.getText());
for (Vec4EditorListener l : listeners)
{
l.vec4Changed(vce);
}
}
/**
* An event that is fired when the {@link Vec4} value is changed within a
* {@link Vec4Editor}
*/
public static class Vec4ChangedEvent extends EventObject
{
private final Vec4 vec;
private final String xText;
private final String yText;
private final String zText;
private final String wText;
private Vec4ChangedEvent(Object source, Vec4 vec, String xText, String yText, String zText, String wText)
{
super(source);
this.vec = vec;
this.xText = xText;
this.yText = yText;
this.zText = zText;
this.wText = wText;
}
public boolean isValid()
{
return vec != null;
}
public Vec4 getVec4()
{
return vec;
}
public String getXText()
{
return xText;
}
public String getYText()
{
return yText;
}
public String getZText()
{
return zText;
}
public String getWText()
{
return wText;
}
}
/**
* A listener interface for classes that wish to be notified of changes to
* the fields of a {@link Vec4Editor}
*/
public static interface Vec4EditorListener extends EventListener
{
/**
* Invoked when changes are made to the fields of the source
* {@link Vec4Editor}
*/
void vec4Changed(Vec4ChangedEvent e);
}
}