Home | History | Annotate | Download | only in ddmuilib
      1 /*
      2  * Copyright (C) 2007 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;
     18 
     19 import com.android.ddmlib.IDevice;
     20 import com.android.ddmlib.EmulatorConsole;
     21 import com.android.ddmlib.EmulatorConsole.GsmMode;
     22 import com.android.ddmlib.EmulatorConsole.GsmStatus;
     23 import com.android.ddmlib.EmulatorConsole.NetworkStatus;
     24 import com.android.ddmuilib.location.CoordinateControls;
     25 import com.android.ddmuilib.location.GpxParser;
     26 import com.android.ddmuilib.location.KmlParser;
     27 import com.android.ddmuilib.location.TrackContentProvider;
     28 import com.android.ddmuilib.location.TrackLabelProvider;
     29 import com.android.ddmuilib.location.TrackPoint;
     30 import com.android.ddmuilib.location.WayPoint;
     31 import com.android.ddmuilib.location.WayPointContentProvider;
     32 import com.android.ddmuilib.location.WayPointLabelProvider;
     33 import com.android.ddmuilib.location.GpxParser.Track;
     34 
     35 import org.eclipse.jface.dialogs.MessageDialog;
     36 import org.eclipse.jface.preference.IPreferenceStore;
     37 import org.eclipse.jface.viewers.ISelection;
     38 import org.eclipse.jface.viewers.ISelectionChangedListener;
     39 import org.eclipse.jface.viewers.IStructuredSelection;
     40 import org.eclipse.jface.viewers.SelectionChangedEvent;
     41 import org.eclipse.jface.viewers.TableViewer;
     42 import org.eclipse.swt.SWT;
     43 import org.eclipse.swt.SWTException;
     44 import org.eclipse.swt.custom.ScrolledComposite;
     45 import org.eclipse.swt.custom.StackLayout;
     46 import org.eclipse.swt.events.ControlAdapter;
     47 import org.eclipse.swt.events.ControlEvent;
     48 import org.eclipse.swt.events.ModifyEvent;
     49 import org.eclipse.swt.events.ModifyListener;
     50 import org.eclipse.swt.events.SelectionAdapter;
     51 import org.eclipse.swt.events.SelectionEvent;
     52 import org.eclipse.swt.graphics.Image;
     53 import org.eclipse.swt.graphics.Rectangle;
     54 import org.eclipse.swt.layout.FillLayout;
     55 import org.eclipse.swt.layout.GridData;
     56 import org.eclipse.swt.layout.GridLayout;
     57 import org.eclipse.swt.widgets.Button;
     58 import org.eclipse.swt.widgets.Combo;
     59 import org.eclipse.swt.widgets.Composite;
     60 import org.eclipse.swt.widgets.Control;
     61 import org.eclipse.swt.widgets.Display;
     62 import org.eclipse.swt.widgets.FileDialog;
     63 import org.eclipse.swt.widgets.Group;
     64 import org.eclipse.swt.widgets.Label;
     65 import org.eclipse.swt.widgets.TabFolder;
     66 import org.eclipse.swt.widgets.TabItem;
     67 import org.eclipse.swt.widgets.Table;
     68 import org.eclipse.swt.widgets.Text;
     69 
     70 /**
     71  * Panel to control the emulator using EmulatorConsole objects.
     72  */
     73 public class EmulatorControlPanel extends SelectionDependentPanel {
     74 
     75     // default location: Patio outside Charlie's
     76     private final static double DEFAULT_LONGITUDE = -122.084095;
     77     private final static double DEFAULT_LATITUDE = 37.422006;
     78 
     79     private final static String SPEED_FORMAT = "Speed: %1$dX";
     80 
     81 
     82     /**
     83      * Map between the display gsm mode and the internal tag used by the display.
     84      */
     85     private final static String[][] GSM_MODES = new String[][] {
     86         { "unregistered", GsmMode.UNREGISTERED.getTag() },
     87         { "home", GsmMode.HOME.getTag() },
     88         { "roaming", GsmMode.ROAMING.getTag() },
     89         { "searching", GsmMode.SEARCHING.getTag() },
     90         { "denied", GsmMode.DENIED.getTag() },
     91     };
     92 
     93     private final static String[] NETWORK_SPEEDS = new String[] {
     94         "Full",
     95         "GSM",
     96         "HSCSD",
     97         "GPRS",
     98         "EDGE",
     99         "UMTS",
    100         "HSDPA",
    101     };
    102 
    103     private final static String[] NETWORK_LATENCIES = new String[] {
    104         "None",
    105         "GPRS",
    106         "EDGE",
    107         "UMTS",
    108     };
    109 
    110     private final static int[] PLAY_SPEEDS = new int[] { 1, 2, 5, 10, 20, 50 };
    111 
    112     private final static String RE_PHONE_NUMBER = "^[+#0-9]+$"; //$NON-NLS-1$
    113     private final static String PREFS_WAYPOINT_COL_NAME = "emulatorControl.waypoint.name"; //$NON-NLS-1$
    114     private final static String PREFS_WAYPOINT_COL_LONGITUDE = "emulatorControl.waypoint.longitude"; //$NON-NLS-1$
    115     private final static String PREFS_WAYPOINT_COL_LATITUDE = "emulatorControl.waypoint.latitude"; //$NON-NLS-1$
    116     private final static String PREFS_WAYPOINT_COL_ELEVATION = "emulatorControl.waypoint.elevation"; //$NON-NLS-1$
    117     private final static String PREFS_WAYPOINT_COL_DESCRIPTION = "emulatorControl.waypoint.desc"; //$NON-NLS-1$
    118     private final static String PREFS_TRACK_COL_NAME = "emulatorControl.track.name"; //$NON-NLS-1$
    119     private final static String PREFS_TRACK_COL_COUNT = "emulatorControl.track.count"; //$NON-NLS-1$
    120     private final static String PREFS_TRACK_COL_FIRST = "emulatorControl.track.first"; //$NON-NLS-1$
    121     private final static String PREFS_TRACK_COL_LAST = "emulatorControl.track.last"; //$NON-NLS-1$
    122     private final static String PREFS_TRACK_COL_COMMENT = "emulatorControl.track.comment"; //$NON-NLS-1$
    123 
    124     private EmulatorConsole mEmulatorConsole;
    125 
    126     private Composite mParent;
    127 
    128     private Label mVoiceLabel;
    129     private Combo mVoiceMode;
    130     private Label mDataLabel;
    131     private Combo mDataMode;
    132     private Label mSpeedLabel;
    133     private Combo mNetworkSpeed;
    134     private Label mLatencyLabel;
    135     private Combo mNetworkLatency;
    136 
    137     private Label mNumberLabel;
    138     private Text mPhoneNumber;
    139 
    140     private Button mVoiceButton;
    141     private Button mSmsButton;
    142 
    143     private Label mMessageLabel;
    144     private Text mSmsMessage;
    145 
    146     private Button mCallButton;
    147     private Button mCancelButton;
    148 
    149     private TabFolder mLocationFolders;
    150 
    151     private Button mDecimalButton;
    152     private Button mSexagesimalButton;
    153     private CoordinateControls mLongitudeControls;
    154     private CoordinateControls mLatitudeControls;
    155     private Button mGpxUploadButton;
    156     private Table mGpxWayPointTable;
    157     private Table mGpxTrackTable;
    158     private Button mKmlUploadButton;
    159     private Table mKmlWayPointTable;
    160 
    161     private Button mPlayGpxButton;
    162     private Button mGpxBackwardButton;
    163     private Button mGpxForwardButton;
    164     private Button mGpxSpeedButton;
    165     private Button mPlayKmlButton;
    166     private Button mKmlBackwardButton;
    167     private Button mKmlForwardButton;
    168     private Button mKmlSpeedButton;
    169 
    170     private Image mPlayImage;
    171     private Image mPauseImage;
    172 
    173     private Thread mPlayingThread;
    174     private boolean mPlayingTrack;
    175     private int mPlayDirection = 1;
    176     private int mSpeed;
    177     private int mSpeedIndex;
    178 
    179     private final SelectionAdapter mDirectionButtonAdapter = new SelectionAdapter() {
    180         @Override
    181         public void widgetSelected(SelectionEvent e) {
    182             Button b = (Button)e.getSource();
    183             if (b.getSelection() == false) {
    184                 // basically the button was unselected, which we don't allow.
    185                 // so we reselect it.
    186                 b.setSelection(true);
    187                 return;
    188             }
    189 
    190             // now handle selection change.
    191             if (b == mGpxForwardButton || b == mKmlForwardButton) {
    192                 mGpxBackwardButton.setSelection(false);
    193                 mGpxForwardButton.setSelection(true);
    194                 mKmlBackwardButton.setSelection(false);
    195                 mKmlForwardButton.setSelection(true);
    196                 mPlayDirection = 1;
    197 
    198             } else {
    199                 mGpxBackwardButton.setSelection(true);
    200                 mGpxForwardButton.setSelection(false);
    201                 mKmlBackwardButton.setSelection(true);
    202                 mKmlForwardButton.setSelection(false);
    203                 mPlayDirection = -1;
    204             }
    205         }
    206     };
    207 
    208     private final SelectionAdapter mSpeedButtonAdapter = new SelectionAdapter() {
    209         @Override
    210         public void widgetSelected(SelectionEvent e) {
    211             mSpeedIndex = (mSpeedIndex+1) % PLAY_SPEEDS.length;
    212             mSpeed = PLAY_SPEEDS[mSpeedIndex];
    213 
    214             mGpxSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed));
    215             mGpxPlayControls.pack();
    216             mKmlSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed));
    217             mKmlPlayControls.pack();
    218 
    219             if (mPlayingThread != null) {
    220                 mPlayingThread.interrupt();
    221             }
    222         }
    223      };
    224     private Composite mKmlPlayControls;
    225     private Composite mGpxPlayControls;
    226 
    227 
    228     public EmulatorControlPanel() {
    229     }
    230 
    231     /**
    232      * Sent when a new device is selected. The new device can be accessed
    233      * with {@link #getCurrentDevice()}
    234      */
    235     @Override
    236     public void deviceSelected() {
    237         handleNewDevice(getCurrentDevice());
    238     }
    239 
    240     /**
    241      * Sent when a new client is selected. The new client can be accessed
    242      * with {@link #getCurrentClient()}
    243      */
    244     @Override
    245     public void clientSelected() {
    246         // pass
    247     }
    248 
    249     /**
    250      * Creates a control capable of displaying some information.  This is
    251      * called once, when the application is initializing, from the UI thread.
    252      */
    253     @Override
    254     protected Control createControl(Composite parent) {
    255         mParent = parent;
    256 
    257         final ScrolledComposite scollingParent = new ScrolledComposite(parent, SWT.V_SCROLL);
    258         scollingParent.setExpandVertical(true);
    259         scollingParent.setExpandHorizontal(true);
    260         scollingParent.setLayoutData(new GridData(GridData.FILL_BOTH));
    261 
    262         final Composite top = new Composite(scollingParent, SWT.NONE);
    263         scollingParent.setContent(top);
    264         top.setLayout(new GridLayout(1, false));
    265 
    266         // set the resize for the scrolling to work (why isn't that done automatically?!?)
    267         scollingParent.addControlListener(new ControlAdapter() {
    268             @Override
    269             public void controlResized(ControlEvent e) {
    270                 Rectangle r = scollingParent.getClientArea();
    271                 scollingParent.setMinSize(top.computeSize(r.width, SWT.DEFAULT));
    272             }
    273         });
    274 
    275         createRadioControls(top);
    276 
    277         createCallControls(top);
    278 
    279         createLocationControls(top);
    280 
    281         doEnable(false);
    282 
    283         top.layout();
    284         Rectangle r = scollingParent.getClientArea();
    285         scollingParent.setMinSize(top.computeSize(r.width, SWT.DEFAULT));
    286 
    287         return scollingParent;
    288     }
    289 
    290     /**
    291      * Create Radio (on/off/roaming, for voice/data) controls.
    292      * @param top
    293      */
    294     private void createRadioControls(final Composite top) {
    295         Group g1 = new Group(top, SWT.NONE);
    296         g1.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    297         g1.setLayout(new GridLayout(2, false));
    298         g1.setText("Telephony Status");
    299 
    300         // the inside of the group is 2 composite so that all the column of the controls (mainly
    301         // combos) have the same width, while not taking the whole screen width
    302         Composite insideGroup = new Composite(g1, SWT.NONE);
    303         GridLayout gl = new GridLayout(4, false);
    304         gl.marginBottom = gl.marginHeight = gl.marginLeft = gl.marginRight = 0;
    305         insideGroup.setLayout(gl);
    306 
    307         mVoiceLabel = new Label(insideGroup, SWT.NONE);
    308         mVoiceLabel.setText("Voice:");
    309         mVoiceLabel.setAlignment(SWT.RIGHT);
    310 
    311         mVoiceMode = new Combo(insideGroup, SWT.READ_ONLY);
    312         mVoiceMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    313         for (String[] mode : GSM_MODES) {
    314             mVoiceMode.add(mode[0]);
    315         }
    316         mVoiceMode.addSelectionListener(new SelectionAdapter() {
    317             // called when selection changes
    318             @Override
    319             public void widgetSelected(SelectionEvent e) {
    320                 setVoiceMode(mVoiceMode.getSelectionIndex());
    321             }
    322         });
    323 
    324         mSpeedLabel = new Label(insideGroup, SWT.NONE);
    325         mSpeedLabel.setText("Speed:");
    326         mSpeedLabel.setAlignment(SWT.RIGHT);
    327 
    328         mNetworkSpeed = new Combo(insideGroup, SWT.READ_ONLY);
    329         mNetworkSpeed.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    330         for (String mode : NETWORK_SPEEDS) {
    331             mNetworkSpeed.add(mode);
    332         }
    333         mNetworkSpeed.addSelectionListener(new SelectionAdapter() {
    334             // called when selection changes
    335             @Override
    336             public void widgetSelected(SelectionEvent e) {
    337                 setNetworkSpeed(mNetworkSpeed.getSelectionIndex());
    338             }
    339         });
    340 
    341         mDataLabel = new Label(insideGroup, SWT.NONE);
    342         mDataLabel.setText("Data:");
    343         mDataLabel.setAlignment(SWT.RIGHT);
    344 
    345         mDataMode = new Combo(insideGroup, SWT.READ_ONLY);
    346         mDataMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    347         for (String[] mode : GSM_MODES) {
    348             mDataMode.add(mode[0]);
    349         }
    350         mDataMode.addSelectionListener(new SelectionAdapter() {
    351             // called when selection changes
    352             @Override
    353             public void widgetSelected(SelectionEvent e) {
    354                 setDataMode(mDataMode.getSelectionIndex());
    355             }
    356         });
    357 
    358         mLatencyLabel = new Label(insideGroup, SWT.NONE);
    359         mLatencyLabel.setText("Latency:");
    360         mLatencyLabel.setAlignment(SWT.RIGHT);
    361 
    362         mNetworkLatency = new Combo(insideGroup, SWT.READ_ONLY);
    363         mNetworkLatency.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    364         for (String mode : NETWORK_LATENCIES) {
    365             mNetworkLatency.add(mode);
    366         }
    367         mNetworkLatency.addSelectionListener(new SelectionAdapter() {
    368             // called when selection changes
    369             @Override
    370             public void widgetSelected(SelectionEvent e) {
    371                 setNetworkLatency(mNetworkLatency.getSelectionIndex());
    372             }
    373         });
    374 
    375         // now an empty label to take the rest of the width of the group
    376         Label l = new Label(g1, SWT.NONE);
    377         l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    378     }
    379 
    380     /**
    381      * Create Voice/SMS call/hang up controls
    382      * @param top
    383      */
    384     private void createCallControls(final Composite top) {
    385         GridLayout gl;
    386         Group g2 = new Group(top, SWT.NONE);
    387         g2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    388         g2.setLayout(new GridLayout(1, false));
    389         g2.setText("Telephony Actions");
    390 
    391         // horizontal composite for label + text field
    392         Composite phoneComp = new Composite(g2, SWT.NONE);
    393         phoneComp.setLayoutData(new GridData(GridData.FILL_BOTH));
    394         gl = new GridLayout(2, false);
    395         gl.marginBottom = gl.marginHeight = gl.marginLeft = gl.marginRight = 0;
    396         phoneComp.setLayout(gl);
    397 
    398         mNumberLabel = new Label(phoneComp, SWT.NONE);
    399         mNumberLabel.setText("Incoming number:");
    400 
    401         mPhoneNumber = new Text(phoneComp, SWT.BORDER | SWT.LEFT | SWT.SINGLE);
    402         mPhoneNumber.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    403         mPhoneNumber.addModifyListener(new ModifyListener() {
    404             public void modifyText(ModifyEvent e) {
    405                 // Reenable the widgets based on the content of the text.
    406                 // doEnable checks the validity of the phone number to enable/disable some
    407                 // widgets.
    408                 // Looks like we're getting a callback at creation time, so we can't
    409                 // suppose that we are enabled when the text is modified...
    410                 doEnable(mEmulatorConsole != null);
    411             }
    412         });
    413 
    414         mVoiceButton = new Button(phoneComp, SWT.RADIO);
    415         GridData gd = new GridData();
    416         gd.horizontalSpan = 2;
    417         mVoiceButton.setText("Voice");
    418         mVoiceButton.setLayoutData(gd);
    419         mVoiceButton.setEnabled(false);
    420         mVoiceButton.setSelection(true);
    421         mVoiceButton.addSelectionListener(new SelectionAdapter() {
    422             // called when selection changes
    423             @Override
    424             public void widgetSelected(SelectionEvent e) {
    425                 doEnable(true);
    426 
    427                 if (mVoiceButton.getSelection()) {
    428                     mCallButton.setText("Call");
    429                 } else {
    430                     mCallButton.setText("Send");
    431                 }
    432             }
    433         });
    434 
    435         mSmsButton = new Button(phoneComp, SWT.RADIO);
    436         mSmsButton.setText("SMS");
    437         gd = new GridData();
    438         gd.horizontalSpan = 2;
    439         mSmsButton.setLayoutData(gd);
    440         mSmsButton.setEnabled(false);
    441         // Since there are only 2 radio buttons, we can put a listener on only one (they
    442         // are both called on select and unselect event.
    443 
    444         mMessageLabel = new Label(phoneComp, SWT.NONE);
    445         gd = new GridData();
    446         gd.verticalAlignment = SWT.TOP;
    447         mMessageLabel.setLayoutData(gd);
    448         mMessageLabel.setText("Message:");
    449         mMessageLabel.setEnabled(false);
    450 
    451         mSmsMessage = new Text(phoneComp, SWT.BORDER | SWT.LEFT | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
    452         mSmsMessage.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
    453         gd.heightHint = 70;
    454         mSmsMessage.setEnabled(false);
    455 
    456         // composite to put the 2 buttons horizontally
    457         Composite g2ButtonComp = new Composite(g2, SWT.NONE);
    458         g2ButtonComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    459         gl = new GridLayout(2, false);
    460         gl.marginWidth = gl.marginHeight = 0;
    461         g2ButtonComp.setLayout(gl);
    462 
    463         // now a button below the phone number
    464         mCallButton = new Button(g2ButtonComp, SWT.PUSH);
    465         mCallButton.setText("Call");
    466         mCallButton.setEnabled(false);
    467         mCallButton.addSelectionListener(new SelectionAdapter() {
    468             @Override
    469             public void widgetSelected(SelectionEvent e) {
    470                 if (mEmulatorConsole != null) {
    471                     if (mVoiceButton.getSelection()) {
    472                         processCommandResult(mEmulatorConsole.call(mPhoneNumber.getText().trim()));
    473                     } else {
    474                         // we need to encode the message. We need to replace the carriage return
    475                         // character by the 2 character string \n.
    476                         // Because of this the \ character needs to be escaped as well.
    477                         // ReplaceAll() expects regexp so \ char are escaped twice.
    478                         String message = mSmsMessage.getText();
    479                         message = message.replaceAll("\\\\", //$NON-NLS-1$
    480                                 "\\\\\\\\"); //$NON-NLS-1$
    481 
    482                         // While the normal line delimiter is returned by Text.getLineDelimiter()
    483                         // it seems copy pasting text coming from somewhere else could have another
    484                         // delimited. For this reason, we'll replace is several steps
    485 
    486                         // replace the dual CR-LF
    487                         message = message.replaceAll("\r\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-1$
    488 
    489                         // replace remaining stand alone \n
    490                         message = message.replaceAll("\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-1$
    491 
    492                         // replace remaining stand alone \r
    493                         message = message.replaceAll("\r", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-1$
    494 
    495                         processCommandResult(mEmulatorConsole.sendSms(mPhoneNumber.getText().trim(),
    496                                 message));
    497                     }
    498                 }
    499             }
    500         });
    501 
    502         mCancelButton = new Button(g2ButtonComp, SWT.PUSH);
    503         mCancelButton.setText("Hang Up");
    504         mCancelButton.setEnabled(false);
    505         mCancelButton.addSelectionListener(new SelectionAdapter() {
    506             @Override
    507             public void widgetSelected(SelectionEvent e) {
    508                 if (mEmulatorConsole != null) {
    509                     if (mVoiceButton.getSelection()) {
    510                         processCommandResult(mEmulatorConsole.cancelCall(
    511                                 mPhoneNumber.getText().trim()));
    512                     }
    513                 }
    514             }
    515         });
    516     }
    517 
    518     /**
    519      * Create Location controls.
    520      * @param top
    521      */
    522     private void createLocationControls(final Composite top) {
    523         Label l = new Label(top, SWT.NONE);
    524         l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    525         l.setText("Location Controls");
    526 
    527         mLocationFolders = new TabFolder(top, SWT.NONE);
    528         mLocationFolders.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    529 
    530         Composite manualLocationComp = new Composite(mLocationFolders, SWT.NONE);
    531         TabItem item = new TabItem(mLocationFolders, SWT.NONE);
    532         item.setText("Manual");
    533         item.setControl(manualLocationComp);
    534 
    535         createManualLocationControl(manualLocationComp);
    536 
    537         ImageLoader loader = ImageLoader.getDdmUiLibLoader();
    538         mPlayImage = loader.loadImage("play.png", mParent.getDisplay()); //$NON-NLS-1$
    539         mPauseImage = loader.loadImage("pause.png", mParent.getDisplay()); //$NON-NLS-1$
    540 
    541         Composite gpxLocationComp = new Composite(mLocationFolders, SWT.NONE);
    542         item = new TabItem(mLocationFolders, SWT.NONE);
    543         item.setText("GPX");
    544         item.setControl(gpxLocationComp);
    545 
    546         createGpxLocationControl(gpxLocationComp);
    547 
    548         Composite kmlLocationComp = new Composite(mLocationFolders, SWT.NONE);
    549         kmlLocationComp.setLayout(new FillLayout());
    550         item = new TabItem(mLocationFolders, SWT.NONE);
    551         item.setText("KML");
    552         item.setControl(kmlLocationComp);
    553 
    554         createKmlLocationControl(kmlLocationComp);
    555     }
    556 
    557     private void createManualLocationControl(Composite manualLocationComp) {
    558         final StackLayout sl;
    559         GridLayout gl;
    560         Label label;
    561 
    562         manualLocationComp.setLayout(new GridLayout(1, false));
    563         mDecimalButton = new Button(manualLocationComp, SWT.RADIO);
    564         mDecimalButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    565         mDecimalButton.setText("Decimal");
    566         mSexagesimalButton = new Button(manualLocationComp, SWT.RADIO);
    567         mSexagesimalButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    568         mSexagesimalButton.setText("Sexagesimal");
    569 
    570         // composite to hold and switching between the 2 modes.
    571         final Composite content = new Composite(manualLocationComp, SWT.NONE);
    572         content.setLayout(sl = new StackLayout());
    573 
    574         // decimal display
    575         final Composite decimalContent = new Composite(content, SWT.NONE);
    576         decimalContent.setLayout(gl = new GridLayout(2, false));
    577         gl.marginHeight = gl.marginWidth = 0;
    578 
    579         mLongitudeControls = new CoordinateControls();
    580         mLatitudeControls = new CoordinateControls();
    581 
    582         label = new Label(decimalContent, SWT.NONE);
    583         label.setText("Longitude");
    584 
    585         mLongitudeControls.createDecimalText(decimalContent);
    586 
    587         label = new Label(decimalContent, SWT.NONE);
    588         label.setText("Latitude");
    589 
    590         mLatitudeControls.createDecimalText(decimalContent);
    591 
    592         // sexagesimal content
    593         final Composite sexagesimalContent = new Composite(content, SWT.NONE);
    594         sexagesimalContent.setLayout(gl = new GridLayout(7, false));
    595         gl.marginHeight = gl.marginWidth = 0;
    596 
    597         label = new Label(sexagesimalContent, SWT.NONE);
    598         label.setText("Longitude");
    599 
    600         mLongitudeControls.createSexagesimalDegreeText(sexagesimalContent);
    601 
    602         label = new Label(sexagesimalContent, SWT.NONE);
    603         label.setText("\u00B0"); // degree character
    604 
    605         mLongitudeControls.createSexagesimalMinuteText(sexagesimalContent);
    606 
    607         label = new Label(sexagesimalContent, SWT.NONE);
    608         label.setText("'");
    609 
    610         mLongitudeControls.createSexagesimalSecondText(sexagesimalContent);
    611 
    612         label = new Label(sexagesimalContent, SWT.NONE);
    613         label.setText("\"");
    614 
    615         label = new Label(sexagesimalContent, SWT.NONE);
    616         label.setText("Latitude");
    617 
    618         mLatitudeControls.createSexagesimalDegreeText(sexagesimalContent);
    619 
    620         label = new Label(sexagesimalContent, SWT.NONE);
    621         label.setText("\u00B0");
    622 
    623         mLatitudeControls.createSexagesimalMinuteText(sexagesimalContent);
    624 
    625         label = new Label(sexagesimalContent, SWT.NONE);
    626         label.setText("'");
    627 
    628         mLatitudeControls.createSexagesimalSecondText(sexagesimalContent);
    629 
    630         label = new Label(sexagesimalContent, SWT.NONE);
    631         label.setText("\"");
    632 
    633         // set the default display to decimal
    634         sl.topControl = decimalContent;
    635         mDecimalButton.setSelection(true);
    636 
    637         mDecimalButton.addSelectionListener(new SelectionAdapter() {
    638             @Override
    639             public void widgetSelected(SelectionEvent e) {
    640                 if (mDecimalButton.getSelection()) {
    641                     sl.topControl = decimalContent;
    642                 } else {
    643                     sl.topControl = sexagesimalContent;
    644                 }
    645                 content.layout();
    646             }
    647         });
    648 
    649         Button sendButton = new Button(manualLocationComp, SWT.PUSH);
    650         sendButton.setText("Send");
    651         sendButton.addSelectionListener(new SelectionAdapter() {
    652             @Override
    653             public void widgetSelected(SelectionEvent e) {
    654                 if (mEmulatorConsole != null) {
    655                     processCommandResult(mEmulatorConsole.sendLocation(
    656                             mLongitudeControls.getValue(), mLatitudeControls.getValue(), 0));
    657                 }
    658             }
    659         });
    660 
    661         mLongitudeControls.setValue(DEFAULT_LONGITUDE);
    662         mLatitudeControls.setValue(DEFAULT_LATITUDE);
    663     }
    664 
    665     private void createGpxLocationControl(Composite gpxLocationComp) {
    666         GridData gd;
    667 
    668         IPreferenceStore store = DdmUiPreferences.getStore();
    669 
    670         gpxLocationComp.setLayout(new GridLayout(1, false));
    671 
    672         mGpxUploadButton = new Button(gpxLocationComp, SWT.PUSH);
    673         mGpxUploadButton.setText("Load GPX...");
    674 
    675         // Table for way point
    676         mGpxWayPointTable = new Table(gpxLocationComp,
    677                 SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
    678         mGpxWayPointTable.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
    679         gd.heightHint = 100;
    680         mGpxWayPointTable.setHeaderVisible(true);
    681         mGpxWayPointTable.setLinesVisible(true);
    682 
    683         TableHelper.createTableColumn(mGpxWayPointTable, "Name", SWT.LEFT,
    684                 "Some Name",
    685                 PREFS_WAYPOINT_COL_NAME, store);
    686         TableHelper.createTableColumn(mGpxWayPointTable, "Longitude", SWT.LEFT,
    687                 "-199.999999",
    688                 PREFS_WAYPOINT_COL_LONGITUDE, store);
    689         TableHelper.createTableColumn(mGpxWayPointTable, "Latitude", SWT.LEFT,
    690                 "-199.999999",
    691                 PREFS_WAYPOINT_COL_LATITUDE, store);
    692         TableHelper.createTableColumn(mGpxWayPointTable, "Elevation", SWT.LEFT,
    693                 "99999.9",
    694                 PREFS_WAYPOINT_COL_ELEVATION, store);
    695         TableHelper.createTableColumn(mGpxWayPointTable, "Description", SWT.LEFT,
    696                 "Some Description",
    697                 PREFS_WAYPOINT_COL_DESCRIPTION, store);
    698 
    699         final TableViewer gpxWayPointViewer = new TableViewer(mGpxWayPointTable);
    700         gpxWayPointViewer.setContentProvider(new WayPointContentProvider());
    701         gpxWayPointViewer.setLabelProvider(new WayPointLabelProvider());
    702 
    703         gpxWayPointViewer.addSelectionChangedListener(new ISelectionChangedListener() {
    704             public void selectionChanged(SelectionChangedEvent event) {
    705                 ISelection selection = event.getSelection();
    706                 if (selection instanceof IStructuredSelection) {
    707                     IStructuredSelection structuredSelection = (IStructuredSelection)selection;
    708                     Object selectedObject = structuredSelection.getFirstElement();
    709                     if (selectedObject instanceof WayPoint) {
    710                         WayPoint wayPoint = (WayPoint)selectedObject;
    711 
    712                         if (mEmulatorConsole != null && mPlayingTrack == false) {
    713                             processCommandResult(mEmulatorConsole.sendLocation(
    714                                     wayPoint.getLongitude(), wayPoint.getLatitude(),
    715                                     wayPoint.getElevation()));
    716                         }
    717                     }
    718                 }
    719             }
    720         });
    721 
    722         // table for tracks.
    723         mGpxTrackTable = new Table(gpxLocationComp,
    724                 SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
    725         mGpxTrackTable.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
    726         gd.heightHint = 100;
    727         mGpxTrackTable.setHeaderVisible(true);
    728         mGpxTrackTable.setLinesVisible(true);
    729 
    730         TableHelper.createTableColumn(mGpxTrackTable, "Name", SWT.LEFT,
    731                 "Some very long name",
    732                 PREFS_TRACK_COL_NAME, store);
    733         TableHelper.createTableColumn(mGpxTrackTable, "Point Count", SWT.RIGHT,
    734                 "9999",
    735                 PREFS_TRACK_COL_COUNT, store);
    736         TableHelper.createTableColumn(mGpxTrackTable, "First Point Time", SWT.LEFT,
    737                 "999-99-99T99:99:99Z",
    738                 PREFS_TRACK_COL_FIRST, store);
    739         TableHelper.createTableColumn(mGpxTrackTable, "Last Point Time", SWT.LEFT,
    740                 "999-99-99T99:99:99Z",
    741                 PREFS_TRACK_COL_LAST, store);
    742         TableHelper.createTableColumn(mGpxTrackTable, "Comment", SWT.LEFT,
    743                 "-199.999999",
    744                 PREFS_TRACK_COL_COMMENT, store);
    745 
    746         final TableViewer gpxTrackViewer = new TableViewer(mGpxTrackTable);
    747         gpxTrackViewer.setContentProvider(new TrackContentProvider());
    748         gpxTrackViewer.setLabelProvider(new TrackLabelProvider());
    749 
    750         gpxTrackViewer.addSelectionChangedListener(new ISelectionChangedListener() {
    751             public void selectionChanged(SelectionChangedEvent event) {
    752                 ISelection selection = event.getSelection();
    753                 if (selection instanceof IStructuredSelection) {
    754                     IStructuredSelection structuredSelection = (IStructuredSelection)selection;
    755                     Object selectedObject = structuredSelection.getFirstElement();
    756                     if (selectedObject instanceof Track) {
    757                         Track track = (Track)selectedObject;
    758 
    759                         if (mEmulatorConsole != null && mPlayingTrack == false) {
    760                             TrackPoint[] points = track.getPoints();
    761                             processCommandResult(mEmulatorConsole.sendLocation(
    762                                     points[0].getLongitude(), points[0].getLatitude(),
    763                                     points[0].getElevation()));
    764                         }
    765 
    766                         mPlayGpxButton.setEnabled(true);
    767                         mGpxBackwardButton.setEnabled(true);
    768                         mGpxForwardButton.setEnabled(true);
    769                         mGpxSpeedButton.setEnabled(true);
    770 
    771                         return;
    772                     }
    773                 }
    774 
    775                 mPlayGpxButton.setEnabled(false);
    776                 mGpxBackwardButton.setEnabled(false);
    777                 mGpxForwardButton.setEnabled(false);
    778                 mGpxSpeedButton.setEnabled(false);
    779             }
    780         });
    781 
    782         mGpxUploadButton.addSelectionListener(new SelectionAdapter() {
    783             @Override
    784             public void widgetSelected(SelectionEvent e) {
    785                 FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.OPEN);
    786 
    787                 fileDialog.setText("Load GPX File");
    788                 fileDialog.setFilterExtensions(new String[] { "*.gpx" } );
    789 
    790                 String fileName = fileDialog.open();
    791                 if (fileName != null) {
    792                     GpxParser parser = new GpxParser(fileName);
    793                     if (parser.parse()) {
    794                         gpxWayPointViewer.setInput(parser.getWayPoints());
    795                         gpxTrackViewer.setInput(parser.getTracks());
    796                     }
    797                 }
    798             }
    799         });
    800 
    801         mGpxPlayControls = new Composite(gpxLocationComp, SWT.NONE);
    802         GridLayout gl;
    803         mGpxPlayControls.setLayout(gl = new GridLayout(5, false));
    804         gl.marginHeight = gl.marginWidth = 0;
    805         mGpxPlayControls.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    806 
    807         mPlayGpxButton = new Button(mGpxPlayControls, SWT.PUSH | SWT.FLAT);
    808         mPlayGpxButton.setImage(mPlayImage);
    809         mPlayGpxButton.addSelectionListener(new SelectionAdapter() {
    810            @Override
    811             public void widgetSelected(SelectionEvent e) {
    812                if (mPlayingTrack == false) {
    813                    ISelection selection = gpxTrackViewer.getSelection();
    814                    if (selection.isEmpty() == false && selection instanceof IStructuredSelection) {
    815                        IStructuredSelection structuredSelection = (IStructuredSelection)selection;
    816                        Object selectedObject = structuredSelection.getFirstElement();
    817                        if (selectedObject instanceof Track) {
    818                            Track track = (Track)selectedObject;
    819                            playTrack(track);
    820                        }
    821                    }
    822                } else {
    823                    // if we're playing, then we pause
    824                    mPlayingTrack = false;
    825                    if (mPlayingThread != null) {
    826                        mPlayingThread.interrupt();
    827                    }
    828                }
    829             }
    830         });
    831 
    832         Label separator = new Label(mGpxPlayControls, SWT.SEPARATOR | SWT.VERTICAL);
    833         separator.setLayoutData(gd = new GridData(
    834                 GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
    835         gd.heightHint = 0;
    836 
    837         ImageLoader loader = ImageLoader.getDdmUiLibLoader();
    838         mGpxBackwardButton = new Button(mGpxPlayControls, SWT.TOGGLE | SWT.FLAT);
    839         mGpxBackwardButton.setImage(loader.loadImage("backward.png", mParent.getDisplay())); //$NON-NLS-1$
    840         mGpxBackwardButton.setSelection(false);
    841         mGpxBackwardButton.addSelectionListener(mDirectionButtonAdapter);
    842         mGpxForwardButton = new Button(mGpxPlayControls, SWT.TOGGLE | SWT.FLAT);
    843         mGpxForwardButton.setImage(loader.loadImage("forward.png", mParent.getDisplay())); //$NON-NLS-1$
    844         mGpxForwardButton.setSelection(true);
    845         mGpxForwardButton.addSelectionListener(mDirectionButtonAdapter);
    846 
    847         mGpxSpeedButton = new Button(mGpxPlayControls, SWT.PUSH | SWT.FLAT);
    848 
    849         mSpeedIndex = 0;
    850         mSpeed = PLAY_SPEEDS[mSpeedIndex];
    851 
    852         mGpxSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed));
    853         mGpxSpeedButton.addSelectionListener(mSpeedButtonAdapter);
    854 
    855         mPlayGpxButton.setEnabled(false);
    856         mGpxBackwardButton.setEnabled(false);
    857         mGpxForwardButton.setEnabled(false);
    858         mGpxSpeedButton.setEnabled(false);
    859 
    860     }
    861 
    862     private void createKmlLocationControl(Composite kmlLocationComp) {
    863         GridData gd;
    864 
    865         IPreferenceStore store = DdmUiPreferences.getStore();
    866 
    867         kmlLocationComp.setLayout(new GridLayout(1, false));
    868 
    869         mKmlUploadButton = new Button(kmlLocationComp, SWT.PUSH);
    870         mKmlUploadButton.setText("Load KML...");
    871 
    872         // Table for way point
    873         mKmlWayPointTable = new Table(kmlLocationComp,
    874                 SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
    875         mKmlWayPointTable.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
    876         gd.heightHint = 200;
    877         mKmlWayPointTable.setHeaderVisible(true);
    878         mKmlWayPointTable.setLinesVisible(true);
    879 
    880         TableHelper.createTableColumn(mKmlWayPointTable, "Name", SWT.LEFT,
    881                 "Some Name",
    882                 PREFS_WAYPOINT_COL_NAME, store);
    883         TableHelper.createTableColumn(mKmlWayPointTable, "Longitude", SWT.LEFT,
    884                 "-199.999999",
    885                 PREFS_WAYPOINT_COL_LONGITUDE, store);
    886         TableHelper.createTableColumn(mKmlWayPointTable, "Latitude", SWT.LEFT,
    887                 "-199.999999",
    888                 PREFS_WAYPOINT_COL_LATITUDE, store);
    889         TableHelper.createTableColumn(mKmlWayPointTable, "Elevation", SWT.LEFT,
    890                 "99999.9",
    891                 PREFS_WAYPOINT_COL_ELEVATION, store);
    892         TableHelper.createTableColumn(mKmlWayPointTable, "Description", SWT.LEFT,
    893                 "Some Description",
    894                 PREFS_WAYPOINT_COL_DESCRIPTION, store);
    895 
    896         final TableViewer kmlWayPointViewer = new TableViewer(mKmlWayPointTable);
    897         kmlWayPointViewer.setContentProvider(new WayPointContentProvider());
    898         kmlWayPointViewer.setLabelProvider(new WayPointLabelProvider());
    899 
    900         mKmlUploadButton.addSelectionListener(new SelectionAdapter() {
    901             @Override
    902             public void widgetSelected(SelectionEvent e) {
    903                 FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.OPEN);
    904 
    905                 fileDialog.setText("Load KML File");
    906                 fileDialog.setFilterExtensions(new String[] { "*.kml" } );
    907 
    908                 String fileName = fileDialog.open();
    909                 if (fileName != null) {
    910                     KmlParser parser = new KmlParser(fileName);
    911                     if (parser.parse()) {
    912                         kmlWayPointViewer.setInput(parser.getWayPoints());
    913 
    914                         mPlayKmlButton.setEnabled(true);
    915                         mKmlBackwardButton.setEnabled(true);
    916                         mKmlForwardButton.setEnabled(true);
    917                         mKmlSpeedButton.setEnabled(true);
    918                     }
    919                 }
    920             }
    921         });
    922 
    923         kmlWayPointViewer.addSelectionChangedListener(new ISelectionChangedListener() {
    924             public void selectionChanged(SelectionChangedEvent event) {
    925                 ISelection selection = event.getSelection();
    926                 if (selection instanceof IStructuredSelection) {
    927                     IStructuredSelection structuredSelection = (IStructuredSelection)selection;
    928                     Object selectedObject = structuredSelection.getFirstElement();
    929                     if (selectedObject instanceof WayPoint) {
    930                         WayPoint wayPoint = (WayPoint)selectedObject;
    931 
    932                         if (mEmulatorConsole != null && mPlayingTrack == false) {
    933                             processCommandResult(mEmulatorConsole.sendLocation(
    934                                     wayPoint.getLongitude(), wayPoint.getLatitude(),
    935                                     wayPoint.getElevation()));
    936                         }
    937                     }
    938                 }
    939             }
    940         });
    941 
    942 
    943 
    944         mKmlPlayControls = new Composite(kmlLocationComp, SWT.NONE);
    945         GridLayout gl;
    946         mKmlPlayControls.setLayout(gl = new GridLayout(5, false));
    947         gl.marginHeight = gl.marginWidth = 0;
    948         mKmlPlayControls.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    949 
    950         mPlayKmlButton = new Button(mKmlPlayControls, SWT.PUSH | SWT.FLAT);
    951         mPlayKmlButton.setImage(mPlayImage);
    952         mPlayKmlButton.addSelectionListener(new SelectionAdapter() {
    953            @Override
    954             public void widgetSelected(SelectionEvent e) {
    955                if (mPlayingTrack == false) {
    956                    Object input = kmlWayPointViewer.getInput();
    957                    if (input instanceof WayPoint[]) {
    958                        playKml((WayPoint[])input);
    959                    }
    960                } else {
    961                    // if we're playing, then we pause
    962                    mPlayingTrack = false;
    963                    if (mPlayingThread != null) {
    964                        mPlayingThread.interrupt();
    965                    }
    966                }
    967             }
    968         });
    969 
    970         Label separator = new Label(mKmlPlayControls, SWT.SEPARATOR | SWT.VERTICAL);
    971         separator.setLayoutData(gd = new GridData(
    972                 GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
    973         gd.heightHint = 0;
    974 
    975         ImageLoader loader = ImageLoader.getDdmUiLibLoader();
    976         mKmlBackwardButton = new Button(mKmlPlayControls, SWT.TOGGLE | SWT.FLAT);
    977         mKmlBackwardButton.setImage(loader.loadImage("backward.png", mParent.getDisplay())); //$NON-NLS-1$
    978         mKmlBackwardButton.setSelection(false);
    979         mKmlBackwardButton.addSelectionListener(mDirectionButtonAdapter);
    980         mKmlForwardButton = new Button(mKmlPlayControls, SWT.TOGGLE | SWT.FLAT);
    981         mKmlForwardButton.setImage(loader.loadImage("forward.png", mParent.getDisplay())); //$NON-NLS-1$
    982         mKmlForwardButton.setSelection(true);
    983         mKmlForwardButton.addSelectionListener(mDirectionButtonAdapter);
    984 
    985         mKmlSpeedButton = new Button(mKmlPlayControls, SWT.PUSH | SWT.FLAT);
    986 
    987         mSpeedIndex = 0;
    988         mSpeed = PLAY_SPEEDS[mSpeedIndex];
    989 
    990         mKmlSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed));
    991         mKmlSpeedButton.addSelectionListener(mSpeedButtonAdapter);
    992 
    993         mPlayKmlButton.setEnabled(false);
    994         mKmlBackwardButton.setEnabled(false);
    995         mKmlForwardButton.setEnabled(false);
    996         mKmlSpeedButton.setEnabled(false);
    997     }
    998 
    999     /**
   1000      * Sets the focus to the proper control inside the panel.
   1001      */
   1002     @Override
   1003     public void setFocus() {
   1004     }
   1005 
   1006     @Override
   1007     protected void postCreation() {
   1008         // pass
   1009     }
   1010 
   1011     private synchronized void setDataMode(int selectionIndex) {
   1012         if (mEmulatorConsole != null) {
   1013             processCommandResult(mEmulatorConsole.setGsmDataMode(
   1014                     GsmMode.getEnum(GSM_MODES[selectionIndex][1])));
   1015         }
   1016     }
   1017 
   1018     private synchronized void setVoiceMode(int selectionIndex) {
   1019         if (mEmulatorConsole != null) {
   1020             processCommandResult(mEmulatorConsole.setGsmVoiceMode(
   1021                     GsmMode.getEnum(GSM_MODES[selectionIndex][1])));
   1022         }
   1023     }
   1024 
   1025     private synchronized void setNetworkLatency(int selectionIndex) {
   1026         if (mEmulatorConsole != null) {
   1027             processCommandResult(mEmulatorConsole.setNetworkLatency(selectionIndex));
   1028         }
   1029     }
   1030 
   1031     private synchronized void setNetworkSpeed(int selectionIndex) {
   1032         if (mEmulatorConsole != null) {
   1033             processCommandResult(mEmulatorConsole.setNetworkSpeed(selectionIndex));
   1034         }
   1035     }
   1036 
   1037 
   1038     /**
   1039      * Callback on device selection change.
   1040      * @param device the new selected device
   1041      */
   1042     public void handleNewDevice(IDevice device) {
   1043         if (mParent.isDisposed()) {
   1044             return;
   1045         }
   1046         // unlink to previous console.
   1047         synchronized (this) {
   1048             mEmulatorConsole = null;
   1049         }
   1050 
   1051         try {
   1052             // get the emulator console for this device
   1053             // First we need the device itself
   1054             if (device != null) {
   1055                 GsmStatus gsm = null;
   1056                 NetworkStatus netstatus = null;
   1057 
   1058                 synchronized (this) {
   1059                     mEmulatorConsole = EmulatorConsole.getConsole(device);
   1060                     if (mEmulatorConsole != null) {
   1061                         // get the gsm status
   1062                         gsm = mEmulatorConsole.getGsmStatus();
   1063                         netstatus = mEmulatorConsole.getNetworkStatus();
   1064 
   1065                         if (gsm == null || netstatus == null) {
   1066                             mEmulatorConsole = null;
   1067                         }
   1068                     }
   1069                 }
   1070 
   1071                 if (gsm != null && netstatus != null) {
   1072                     Display d = mParent.getDisplay();
   1073                     if (d.isDisposed() == false) {
   1074                         final GsmStatus f_gsm = gsm;
   1075                         final NetworkStatus f_netstatus = netstatus;
   1076 
   1077                         d.asyncExec(new Runnable() {
   1078                             public void run() {
   1079                                 if (f_gsm.voice != GsmMode.UNKNOWN) {
   1080                                     mVoiceMode.select(getGsmComboIndex(f_gsm.voice));
   1081                                 } else {
   1082                                     mVoiceMode.clearSelection();
   1083                                 }
   1084                                 if (f_gsm.data != GsmMode.UNKNOWN) {
   1085                                     mDataMode.select(getGsmComboIndex(f_gsm.data));
   1086                                 } else {
   1087                                     mDataMode.clearSelection();
   1088                                 }
   1089 
   1090                                 if (f_netstatus.speed != -1) {
   1091                                     mNetworkSpeed.select(f_netstatus.speed);
   1092                                 } else {
   1093                                     mNetworkSpeed.clearSelection();
   1094                                 }
   1095 
   1096                                 if (f_netstatus.latency != -1) {
   1097                                     mNetworkLatency.select(f_netstatus.latency);
   1098                                 } else {
   1099                                     mNetworkLatency.clearSelection();
   1100                                 }
   1101                             }
   1102                         });
   1103                     }
   1104                 }
   1105             }
   1106         } finally {
   1107             // enable/disable the ui
   1108             boolean enable = false;
   1109             synchronized (this) {
   1110                 enable = mEmulatorConsole != null;
   1111             }
   1112 
   1113             enable(enable);
   1114         }
   1115     }
   1116 
   1117     /**
   1118      * Enable or disable the ui. Can be called from non ui threads.
   1119      * @param enabled
   1120      */
   1121     private void enable(final boolean enabled) {
   1122         try {
   1123             Display d = mParent.getDisplay();
   1124             d.asyncExec(new Runnable() {
   1125                 public void run() {
   1126                     if (mParent.isDisposed() == false) {
   1127                         doEnable(enabled);
   1128                     }
   1129                 }
   1130             });
   1131         } catch (SWTException e) {
   1132             // disposed. do nothing
   1133         }
   1134     }
   1135 
   1136     private boolean isValidPhoneNumber() {
   1137         String number = mPhoneNumber.getText().trim();
   1138 
   1139         return number.matches(RE_PHONE_NUMBER);
   1140     }
   1141 
   1142     /**
   1143      * Enable or disable the ui. Cannot be called from non ui threads.
   1144      * @param enabled
   1145      */
   1146     protected void doEnable(boolean enabled) {
   1147         mVoiceLabel.setEnabled(enabled);
   1148         mVoiceMode.setEnabled(enabled);
   1149 
   1150         mDataLabel.setEnabled(enabled);
   1151         mDataMode.setEnabled(enabled);
   1152 
   1153         mSpeedLabel.setEnabled(enabled);
   1154         mNetworkSpeed.setEnabled(enabled);
   1155 
   1156         mLatencyLabel.setEnabled(enabled);
   1157         mNetworkLatency.setEnabled(enabled);
   1158 
   1159         // Calling setEnabled on a text field will trigger a modifyText event, so we don't do it
   1160         // if we don't need to.
   1161         if (mPhoneNumber.isEnabled() != enabled) {
   1162             mNumberLabel.setEnabled(enabled);
   1163             mPhoneNumber.setEnabled(enabled);
   1164         }
   1165 
   1166         boolean valid = isValidPhoneNumber();
   1167 
   1168         mVoiceButton.setEnabled(enabled && valid);
   1169         mSmsButton.setEnabled(enabled && valid);
   1170 
   1171         boolean smsValid = enabled && valid && mSmsButton.getSelection();
   1172 
   1173         // Calling setEnabled on a text field will trigger a modifyText event, so we don't do it
   1174         // if we don't need to.
   1175         if (mSmsMessage.isEnabled() != smsValid) {
   1176             mMessageLabel.setEnabled(smsValid);
   1177             mSmsMessage.setEnabled(smsValid);
   1178         }
   1179         if (enabled == false) {
   1180             mSmsMessage.setText(""); //$NON-NLs-1$
   1181         }
   1182 
   1183         mCallButton.setEnabled(enabled && valid);
   1184         mCancelButton.setEnabled(enabled && valid && mVoiceButton.getSelection());
   1185 
   1186         if (enabled == false) {
   1187             mVoiceMode.clearSelection();
   1188             mDataMode.clearSelection();
   1189             mNetworkSpeed.clearSelection();
   1190             mNetworkLatency.clearSelection();
   1191             if (mPhoneNumber.getText().length() > 0) {
   1192                 mPhoneNumber.setText(""); //$NON-NLS-1$
   1193             }
   1194         }
   1195 
   1196         // location controls
   1197         mLocationFolders.setEnabled(enabled);
   1198 
   1199         mDecimalButton.setEnabled(enabled);
   1200         mSexagesimalButton.setEnabled(enabled);
   1201         mLongitudeControls.setEnabled(enabled);
   1202         mLatitudeControls.setEnabled(enabled);
   1203 
   1204         mGpxUploadButton.setEnabled(enabled);
   1205         mGpxWayPointTable.setEnabled(enabled);
   1206         mGpxTrackTable.setEnabled(enabled);
   1207         mKmlUploadButton.setEnabled(enabled);
   1208         mKmlWayPointTable.setEnabled(enabled);
   1209     }
   1210 
   1211     /**
   1212      * Returns the index of the combo item matching a specific GsmMode.
   1213      * @param mode
   1214      */
   1215     private int getGsmComboIndex(GsmMode mode) {
   1216         for (int i = 0 ; i < GSM_MODES.length; i++) {
   1217             String[] modes = GSM_MODES[i];
   1218             if (mode.getTag().equals(modes[1])) {
   1219                 return i;
   1220             }
   1221         }
   1222         return -1;
   1223     }
   1224 
   1225     /**
   1226      * Processes the result of a command sent to the console.
   1227      * @param result the result of the command.
   1228      */
   1229     private boolean processCommandResult(final String result) {
   1230         if (result != EmulatorConsole.RESULT_OK) {
   1231             try {
   1232                 mParent.getDisplay().asyncExec(new Runnable() {
   1233                     public void run() {
   1234                         if (mParent.isDisposed() == false) {
   1235                             MessageDialog.openError(mParent.getShell(), "Emulator Console",
   1236                                     result);
   1237                         }
   1238                     }
   1239                 });
   1240             } catch (SWTException e) {
   1241                 // we're quitting, just ignore
   1242             }
   1243 
   1244             return false;
   1245         }
   1246 
   1247         return true;
   1248     }
   1249 
   1250     /**
   1251      * @param track
   1252      */
   1253     private void playTrack(final Track track) {
   1254         // no need to synchronize this check, the worst that can happen, is we start the thread
   1255         // for nothing.
   1256         if (mEmulatorConsole != null) {
   1257             mPlayGpxButton.setImage(mPauseImage);
   1258             mPlayKmlButton.setImage(mPauseImage);
   1259             mPlayingTrack = true;
   1260 
   1261             mPlayingThread = new Thread() {
   1262                 @Override
   1263                 public void run() {
   1264                     try {
   1265                         TrackPoint[] trackPoints = track.getPoints();
   1266                         int count = trackPoints.length;
   1267 
   1268                         // get the start index.
   1269                         int start = 0;
   1270                         if (mPlayDirection == -1) {
   1271                             start = count - 1;
   1272                         }
   1273 
   1274                         for (int p = start; p >= 0 && p < count; p += mPlayDirection) {
   1275                             if (mPlayingTrack == false) {
   1276                                 return;
   1277                             }
   1278 
   1279                             // get the current point and send its location to
   1280                             // the emulator.
   1281                             final TrackPoint trackPoint = trackPoints[p];
   1282 
   1283                             synchronized (EmulatorControlPanel.this) {
   1284                                 if (mEmulatorConsole == null ||
   1285                                         processCommandResult(mEmulatorConsole.sendLocation(
   1286                                                 trackPoint.getLongitude(), trackPoint.getLatitude(),
   1287                                                 trackPoint.getElevation())) == false) {
   1288                                     return;
   1289                                 }
   1290                             }
   1291 
   1292                             // if this is not the final point, then get the next one and
   1293                             // compute the delta time
   1294                             int nextIndex = p + mPlayDirection;
   1295                             if (nextIndex >=0 && nextIndex < count) {
   1296                                 TrackPoint nextPoint = trackPoints[nextIndex];
   1297 
   1298                                 long delta = nextPoint.getTime() - trackPoint.getTime();
   1299                                 if (delta < 0) {
   1300                                     delta = -delta;
   1301                                 }
   1302 
   1303                                 long startTime = System.currentTimeMillis();
   1304 
   1305                                 try {
   1306                                     sleep(delta / mSpeed);
   1307                                 } catch (InterruptedException e) {
   1308                                     if (mPlayingTrack == false) {
   1309                                         return;
   1310                                     }
   1311 
   1312                                     // we got interrupted, lets make sure we can play
   1313                                     do {
   1314                                         long waited = System.currentTimeMillis() - startTime;
   1315                                         long needToWait = delta / mSpeed;
   1316                                         if (waited < needToWait) {
   1317                                             try {
   1318                                                 sleep(needToWait - waited);
   1319                                             } catch (InterruptedException e1) {
   1320                                                 // we'll just loop and wait again if needed.
   1321                                                 // unless we're supposed to stop
   1322                                                 if (mPlayingTrack == false) {
   1323                                                     return;
   1324                                                 }
   1325                                             }
   1326                                         } else {
   1327                                             break;
   1328                                         }
   1329                                     } while (true);
   1330                                 }
   1331                             }
   1332                         }
   1333                     } finally {
   1334                         mPlayingTrack = false;
   1335                         try {
   1336                             mParent.getDisplay().asyncExec(new Runnable() {
   1337                                 public void run() {
   1338                                     if (mPlayGpxButton.isDisposed() == false) {
   1339                                         mPlayGpxButton.setImage(mPlayImage);
   1340                                         mPlayKmlButton.setImage(mPlayImage);
   1341                                     }
   1342                                 }
   1343                             });
   1344                         } catch (SWTException e) {
   1345                             // we're quitting, just ignore
   1346                         }
   1347                     }
   1348                 }
   1349             };
   1350 
   1351             mPlayingThread.start();
   1352         }
   1353     }
   1354 
   1355     private void playKml(final WayPoint[] trackPoints) {
   1356         // no need to synchronize this check, the worst that can happen, is we start the thread
   1357         // for nothing.
   1358         if (mEmulatorConsole != null) {
   1359             mPlayGpxButton.setImage(mPauseImage);
   1360             mPlayKmlButton.setImage(mPauseImage);
   1361             mPlayingTrack = true;
   1362 
   1363             mPlayingThread = new Thread() {
   1364                 @Override
   1365                 public void run() {
   1366                     try {
   1367                         int count = trackPoints.length;
   1368 
   1369                         // get the start index.
   1370                         int start = 0;
   1371                         if (mPlayDirection == -1) {
   1372                             start = count - 1;
   1373                         }
   1374 
   1375                         for (int p = start; p >= 0 && p < count; p += mPlayDirection) {
   1376                             if (mPlayingTrack == false) {
   1377                                 return;
   1378                             }
   1379 
   1380                             // get the current point and send its location to
   1381                             // the emulator.
   1382                             WayPoint trackPoint = trackPoints[p];
   1383 
   1384                             synchronized (EmulatorControlPanel.this) {
   1385                                 if (mEmulatorConsole == null ||
   1386                                         processCommandResult(mEmulatorConsole.sendLocation(
   1387                                                 trackPoint.getLongitude(), trackPoint.getLatitude(),
   1388                                                 trackPoint.getElevation())) == false) {
   1389                                     return;
   1390                                 }
   1391                             }
   1392 
   1393                             // if this is not the final point, then get the next one and
   1394                             // compute the delta time
   1395                             int nextIndex = p + mPlayDirection;
   1396                             if (nextIndex >=0 && nextIndex < count) {
   1397 
   1398                                 long delta = 1000; // 1 second
   1399                                 if (delta < 0) {
   1400                                     delta = -delta;
   1401                                 }
   1402 
   1403                                 long startTime = System.currentTimeMillis();
   1404 
   1405                                 try {
   1406                                     sleep(delta / mSpeed);
   1407                                 } catch (InterruptedException e) {
   1408                                     if (mPlayingTrack == false) {
   1409                                         return;
   1410                                     }
   1411 
   1412                                     // we got interrupted, lets make sure we can play
   1413                                     do {
   1414                                         long waited = System.currentTimeMillis() - startTime;
   1415                                         long needToWait = delta / mSpeed;
   1416                                         if (waited < needToWait) {
   1417                                             try {
   1418                                                 sleep(needToWait - waited);
   1419                                             } catch (InterruptedException e1) {
   1420                                                 // we'll just loop and wait again if needed.
   1421                                                 // unless we're supposed to stop
   1422                                                 if (mPlayingTrack == false) {
   1423                                                     return;
   1424                                                 }
   1425                                             }
   1426                                         } else {
   1427                                             break;
   1428                                         }
   1429                                     } while (true);
   1430                                 }
   1431                             }
   1432                         }
   1433                     } finally {
   1434                         mPlayingTrack = false;
   1435                         try {
   1436                             mParent.getDisplay().asyncExec(new Runnable() {
   1437                                 public void run() {
   1438                                     if (mPlayGpxButton.isDisposed() == false) {
   1439                                         mPlayGpxButton.setImage(mPlayImage);
   1440                                         mPlayKmlButton.setImage(mPlayImage);
   1441                                     }
   1442                                 }
   1443                             });
   1444                         } catch (SWTException e) {
   1445                             // we're quitting, just ignore
   1446                         }
   1447                     }
   1448                 }
   1449             };
   1450 
   1451             mPlayingThread.start();
   1452         }
   1453     }
   1454 }
   1455