Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ddmuilib.location;
     18 
     19 import org.eclipse.swt.SWT;
     20 import org.eclipse.swt.events.ModifyEvent;
     21 import org.eclipse.swt.events.ModifyListener;
     22 import org.eclipse.swt.graphics.Point;
     23 import org.eclipse.swt.layout.GridData;
     24 import org.eclipse.swt.layout.GridLayout;
     25 import org.eclipse.swt.widgets.Composite;
     26 import org.eclipse.swt.widgets.Text;
     27 
     28 import java.text.DecimalFormat;
     29 import java.text.ParseException;
     30 
     31 /**
     32  * Encapsulation of controls handling a location coordinate in decimal and sexagesimal.
     33  * <p/>This handle the conversion between both modes automatically by using a {@link ModifyListener}
     34  * on all the {@link Text} widgets.
     35  * <p/>To get/set the coordinate, use {@link #setValue(double)} and {@link #getValue()} (preceded by
     36  * a call to {@link #isValueValid()})
     37  */
     38 public final class CoordinateControls {
     39     private double mValue;
     40     private boolean mValueValidity = false;
     41     private Text mDecimalText;
     42     private Text mSexagesimalDegreeText;
     43     private Text mSexagesimalMinuteText;
     44     private Text mSexagesimalSecondText;
     45     private final DecimalFormat mDecimalFormat = new DecimalFormat();
     46 
     47     /** Internal flag to prevent {@link ModifyEvent} to be sent when {@link Text#setText(String)}
     48      * is called. This is an int instead of a boolean to act as a counter. */
     49     private int mManualTextChange = 0;
     50 
     51     /**
     52      * ModifyListener for the 3 {@link Text} controls of the sexagesimal mode.
     53      */
     54     private ModifyListener mSexagesimalListener = new ModifyListener() {
     55         public void modifyText(ModifyEvent event) {
     56             if (mManualTextChange > 0) {
     57                 return;
     58             }
     59             try {
     60                 mValue = getValueFromSexagesimalControls();
     61                 setValueIntoDecimalControl(mValue);
     62                 mValueValidity = true;
     63             } catch (ParseException e) {
     64                 // wrong format empty the decimal controls.
     65                 mValueValidity = false;
     66                 resetDecimalControls();
     67             }
     68         }
     69     };
     70 
     71     /**
     72      * Creates the {@link Text} control for the decimal display of the coordinate.
     73      * <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
     74      * @param parent The {@link Composite} parent of the control.
     75      */
     76     public void createDecimalText(Composite parent) {
     77         mDecimalText = createTextControl(parent, "-199.999999", new ModifyListener() {
     78             public void modifyText(ModifyEvent event) {
     79                 if (mManualTextChange > 0) {
     80                     return;
     81                 }
     82                 try {
     83                     mValue = mDecimalFormat.parse(mDecimalText.getText()).doubleValue();
     84                     setValueIntoSexagesimalControl(mValue);
     85                     mValueValidity = true;
     86                 } catch (ParseException e) {
     87                     // wrong format empty the sexagesimal controls.
     88                     mValueValidity = false;
     89                     resetSexagesimalControls();
     90                 }
     91             }
     92         });
     93     }
     94 
     95     /**
     96      * Creates the {@link Text} control for the "degree" display of the coordinate in sexagesimal
     97      * mode.
     98      * <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
     99      * @param parent The {@link Composite} parent of the control.
    100      */
    101     public void createSexagesimalDegreeText(Composite parent) {
    102         mSexagesimalDegreeText = createTextControl(parent, "-199", mSexagesimalListener); //$NON-NLS-1$
    103     }
    104 
    105     /**
    106      * Creates the {@link Text} control for the "minute" display of the coordinate in sexagesimal
    107      * mode.
    108      * <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
    109      * @param parent The {@link Composite} parent of the control.
    110      */
    111     public void createSexagesimalMinuteText(Composite parent) {
    112         mSexagesimalMinuteText = createTextControl(parent, "99", mSexagesimalListener); //$NON-NLS-1$
    113     }
    114 
    115     /**
    116      * Creates the {@link Text} control for the "second" display of the coordinate in sexagesimal
    117      * mode.
    118      * <p/>The control is expected to be placed in a Composite using a {@link GridLayout}.
    119      * @param parent The {@link Composite} parent of the control.
    120      */
    121     public void createSexagesimalSecondText(Composite parent) {
    122         mSexagesimalSecondText = createTextControl(parent, "99.999", mSexagesimalListener); //$NON-NLS-1$
    123     }
    124 
    125     /**
    126      * Sets the coordinate into the {@link Text} controls.
    127      * @param value the coordinate value to set.
    128      */
    129     public void setValue(double value) {
    130         mValue = value;
    131         mValueValidity = true;
    132         setValueIntoDecimalControl(value);
    133         setValueIntoSexagesimalControl(value);
    134     }
    135 
    136     /**
    137      * Returns whether the value in the control(s) is valid.
    138      */
    139     public boolean isValueValid() {
    140         return mValueValidity;
    141     }
    142 
    143     /**
    144      * Returns the current value set in the control(s).
    145      * <p/>This value can be erroneous, and a check with {@link #isValueValid()} should be performed
    146      * before any call to this method.
    147      */
    148     public double getValue() {
    149         return mValue;
    150     }
    151 
    152     /**
    153      * Enables or disables all the {@link Text} controls.
    154      * @param enabled the enabled state.
    155      */
    156     public void setEnabled(boolean enabled) {
    157         mDecimalText.setEnabled(enabled);
    158         mSexagesimalDegreeText.setEnabled(enabled);
    159         mSexagesimalMinuteText.setEnabled(enabled);
    160         mSexagesimalSecondText.setEnabled(enabled);
    161     }
    162 
    163     private void resetDecimalControls() {
    164         mManualTextChange++;
    165         mDecimalText.setText(""); //$NON-NLS-1$
    166         mManualTextChange--;
    167     }
    168 
    169     private void resetSexagesimalControls() {
    170         mManualTextChange++;
    171         mSexagesimalDegreeText.setText(""); //$NON-NLS-1$
    172         mSexagesimalMinuteText.setText(""); //$NON-NLS-1$
    173         mSexagesimalSecondText.setText(""); //$NON-NLS-1$
    174         mManualTextChange--;
    175     }
    176 
    177     /**
    178      * Creates a {@link Text} with a given parent, default string and a {@link ModifyListener}
    179      * @param parent the parent {@link Composite}.
    180      * @param defaultString the default string to be used to compute the {@link Text} control
    181      * size hint.
    182      * @param listener the {@link ModifyListener} to be called when the {@link Text} control is
    183      * modified.
    184      */
    185     private Text createTextControl(Composite parent, String defaultString,
    186             ModifyListener listener) {
    187         // create the control
    188         Text text = new Text(parent, SWT.BORDER | SWT.LEFT | SWT.SINGLE);
    189 
    190         // add the standard listener to it.
    191         text.addModifyListener(listener);
    192 
    193         // compute its size/
    194         mManualTextChange++;
    195         text.setText(defaultString);
    196         text.pack();
    197         Point size = text.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    198         text.setText(""); //$NON-NLS-1$
    199         mManualTextChange--;
    200 
    201         GridData gridData = new GridData();
    202         gridData.widthHint = size.x;
    203         text.setLayoutData(gridData);
    204 
    205         return text;
    206     }
    207 
    208     private double getValueFromSexagesimalControls() throws ParseException {
    209         double degrees = mDecimalFormat.parse(mSexagesimalDegreeText.getText()).doubleValue();
    210         double minutes = mDecimalFormat.parse(mSexagesimalMinuteText.getText()).doubleValue();
    211         double seconds = mDecimalFormat.parse(mSexagesimalSecondText.getText()).doubleValue();
    212 
    213         boolean isPositive = (degrees >= 0.);
    214         degrees = Math.abs(degrees);
    215 
    216         double value = degrees + minutes / 60. + seconds / 3600.;
    217         return isPositive ? value : - value;
    218     }
    219 
    220     private void setValueIntoDecimalControl(double value) {
    221         mManualTextChange++;
    222         mDecimalText.setText(String.format("%.6f", value));
    223         mManualTextChange--;
    224     }
    225 
    226     private void setValueIntoSexagesimalControl(double value) {
    227         // get the sign and make the number positive no matter what.
    228         boolean isPositive = (value >= 0.);
    229         value = Math.abs(value);
    230 
    231         // get the degree
    232         double degrees = Math.floor(value);
    233 
    234         // get the minutes
    235         double minutes = Math.floor((value - degrees) * 60.);
    236 
    237         // get the seconds.
    238         double seconds = (value - degrees) * 3600. - minutes * 60.;
    239 
    240         mManualTextChange++;
    241         mSexagesimalDegreeText.setText(
    242                 Integer.toString(isPositive ? (int)degrees : (int)- degrees));
    243         mSexagesimalMinuteText.setText(Integer.toString((int)minutes));
    244         mSexagesimalSecondText.setText(String.format("%.3f", seconds)); //$NON-NLS-1$
    245         mManualTextChange--;
    246     }
    247 }
    248