/*
* Copyright (C) 2008 The Android Open Source Project
*
* 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 com.android.ddmuilib.location;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
/**
* Encapsulation of controls handling a location coordinate in decimal and sexagesimal.
* <p/>This handle the conversion between both modes automatically by using a {@link ModifyListener}
* on all the {@link Text} widgets.
* <p/>To get/set the coordinate, use {@link #setValue(double)} and {@link #getValue()} (preceded by
* a call to {@link #isValueValid()})
*/
public final class CoordinateControls {
private double mValue;
private boolean mValueValidity = false;
private Text mDecimalText;
private Text mSexagesimalDegreeText;
private Text mSexagesimalMinuteText;
private Text mSexagesimalSecondText;
/** Internal flag to prevent {@link ModifyEvent} to be sent when {@link Text#setText(String)}
* is called. This is an int instead of a boolean to act as a counter. */
private int mManualTextChange = 0;
/**
* ModifyListener for the 3 {@link Text} controls of the sexagesimal mode.
*/
private ModifyListener mSexagesimalListener = new ModifyListener() {
public void modifyText(ModifyEvent event) {
if (mManualTextChange > 0) {
return;
}
try {
mValue = getValueFromSexagesimalControls();
setValueIntoDecimalControl(mValue);
mValueValidity = true;
} catch (NumberFormatException e) {
// wrong format empty the decimal controls.
mValueValidity = false;
resetDecimalControls();
}
}
};
/**
* Creates the {@link Text} control for the decimal display of the coordinate.
* <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
* @param parent The {@link Composite} parent of the control.
*/
public void createDecimalText(Composite parent) {
mDecimalText = createTextControl(parent, "-199.999999", new ModifyListener() {
public void modifyText(ModifyEvent event) {
if (mManualTextChange > 0) {
return;
}
try {
mValue = Double.parseDouble(mDecimalText.getText());
setValueIntoSexagesimalControl(mValue);
mValueValidity = true;
} catch (NumberFormatException e) {
// wrong format empty the sexagesimal controls.
mValueValidity = false;
resetSexagesimalControls();
}
}
});
}
/**
* Creates the {@link Text} control for the "degree" display of the coordinate in sexagesimal
* mode.
* <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
* @param parent The {@link Composite} parent of the control.
*/
public void createSexagesimalDegreeText(Composite parent) {
mSexagesimalDegreeText = createTextControl(parent, "-199", mSexagesimalListener); //$NON-NLS-1$
}
/**
* Creates the {@link Text} control for the "minute" display of the coordinate in sexagesimal
* mode.
* <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
* @param parent The {@link Composite} parent of the control.
*/
public void createSexagesimalMinuteText(Composite parent) {
mSexagesimalMinuteText = createTextControl(parent, "99", mSexagesimalListener); //$NON-NLS-1$
}
/**
* Creates the {@link Text} control for the "second" display of the coordinate in sexagesimal
* mode.
* <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
* @param parent The {@link Composite} parent of the control.
*/
public void createSexagesimalSecondText(Composite parent) {
mSexagesimalSecondText = createTextControl(parent, "99.999", mSexagesimalListener); //$NON-NLS-1$
}
/**
* Sets the coordinate into the {@link Text} controls.
* @param value the coordinate value to set.
*/
public void setValue(double value) {
mValue = value;
mValueValidity = true;
setValueIntoDecimalControl(value);
setValueIntoSexagesimalControl(value);
}
/**
* Returns whether the value in the control(s) is valid.
*/
public boolean isValueValid() {
return mValueValidity;
}
/**
* Returns the current value set in the control(s).
* <p/>This value can be erroneous, and a check with {@link #isValueValid()} should be performed
* before any call to this method.
*/
public double getValue() {
return mValue;
}
/**
* Enables or disables all the {@link Text} controls.
* @param enabled the enabled state.
*/
public void setEnabled(boolean enabled) {
mDecimalText.setEnabled(enabled);
mSexagesimalDegreeText.setEnabled(enabled);
mSexagesimalMinuteText.setEnabled(enabled);
mSexagesimalSecondText.setEnabled(enabled);
}
private void resetDecimalControls() {
mManualTextChange++;
mDecimalText.setText(""); //$NON-NLS-1$
mManualTextChange--;
}
private void resetSexagesimalControls() {
mManualTextChange++;
mSexagesimalDegreeText.setText(""); //$NON-NLS-1$
mSexagesimalMinuteText.setText(""); //$NON-NLS-1$
mSexagesimalSecondText.setText(""); //$NON-NLS-1$
mManualTextChange--;
}
/**
* Creates a {@link Text} with a given parent, default string and a {@link ModifyListener}
* @param parent the parent {@link Composite}.
* @param defaultString the default string to be used to compute the {@link Text} control
* size hint.
* @param listener the {@link ModifyListener} to be called when the {@link Text} control is
* modified.
*/
private Text createTextControl(Composite parent, String defaultString,
ModifyListener listener) {
// create the control
Text text = new Text(parent, SWT.BORDER | SWT.LEFT | SWT.SINGLE);
// add the standard listener to it.
text.addModifyListener(listener);
// compute its size/
mManualTextChange++;
text.setText(defaultString);
text.pack();
Point size = text.computeSize(SWT.DEFAULT, SWT.DEFAULT);
text.setText(""); //$NON-NLS-1$
mManualTextChange--;
GridData gridData = new GridData();
gridData.widthHint = size.x;
text.setLayoutData(gridData);
return text;
}
private double getValueFromSexagesimalControls() throws NumberFormatException {
double degrees = Double.parseDouble(mSexagesimalDegreeText.getText());
double minutes = Double.parseDouble(mSexagesimalMinuteText.getText());
double seconds = Double.parseDouble(mSexagesimalSecondText.getText());
boolean isPositive = (degrees >= 0.);
degrees = Math.abs(degrees);
double value = degrees + minutes / 60. + seconds / 3600.;
return isPositive ? value : - value;
}
private void setValueIntoDecimalControl(double value) {
mManualTextChange++;
mDecimalText.setText(String.format("%.6f", value));
mManualTextChange--;
}
private void setValueIntoSexagesimalControl(double value) {
// get the sign and make the number positive no matter what.
boolean isPositive = (value >= 0.);
value = Math.abs(value);
// get the degree
double degrees = Math.floor(value);
// get the minutes
double minutes = Math.floor((value - degrees) * 60.);
// get the seconds.
double seconds = (value - degrees) * 3600. - minutes * 60.;
mManualTextChange++;
mSexagesimalDegreeText.setText(
Integer.toString(isPositive ? (int)degrees : (int)- degrees));
mSexagesimalMinuteText.setText(Integer.toString((int)minutes));
mSexagesimalSecondText.setText(String.format("%.3f", seconds)); //$NON-NLS-1$
mManualTextChange--;
}
}
|