OSDN Git Service

3734a42ef1da556c257e7809f41441f118c52a82
[android-x86/sdk.git] / sdkmanager / libs / sdkuilib / src / com / android / sdkuilib / internal / widgets / AvdCreationDialog.java
1 /*
2  * Copyright (C) 2009 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.sdkuilib.internal.widgets;
18
19 import com.android.prefs.AndroidLocation;
20 import com.android.prefs.AndroidLocation.AndroidLocationException;
21 import com.android.sdklib.IAndroidTarget;
22 import com.android.sdklib.ISdkLog;
23 import com.android.sdklib.SdkConstants;
24 import com.android.sdklib.SdkManager;
25 import com.android.sdklib.internal.avd.AvdManager;
26 import com.android.sdklib.internal.avd.HardwareProperties;
27 import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
28 import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty;
29 import com.android.sdkuilib.internal.repository.icons.ImageFactory;
30 import com.android.sdkuilib.ui.GridDialog;
31
32 import org.eclipse.jface.dialogs.IDialogConstants;
33 import org.eclipse.jface.viewers.CellEditor;
34 import org.eclipse.jface.viewers.CellLabelProvider;
35 import org.eclipse.jface.viewers.ComboBoxCellEditor;
36 import org.eclipse.jface.viewers.EditingSupport;
37 import org.eclipse.jface.viewers.ISelection;
38 import org.eclipse.jface.viewers.ISelectionChangedListener;
39 import org.eclipse.jface.viewers.IStructuredContentProvider;
40 import org.eclipse.jface.viewers.IStructuredSelection;
41 import org.eclipse.jface.viewers.SelectionChangedEvent;
42 import org.eclipse.jface.viewers.TableViewer;
43 import org.eclipse.jface.viewers.TableViewerColumn;
44 import org.eclipse.jface.viewers.TextCellEditor;
45 import org.eclipse.jface.viewers.Viewer;
46 import org.eclipse.jface.viewers.ViewerCell;
47 import org.eclipse.jface.window.Window;
48 import org.eclipse.swt.SWT;
49 import org.eclipse.swt.events.ModifyEvent;
50 import org.eclipse.swt.events.ModifyListener;
51 import org.eclipse.swt.events.SelectionAdapter;
52 import org.eclipse.swt.events.SelectionEvent;
53 import org.eclipse.swt.events.VerifyEvent;
54 import org.eclipse.swt.events.VerifyListener;
55 import org.eclipse.swt.graphics.Point;
56 import org.eclipse.swt.layout.GridData;
57 import org.eclipse.swt.layout.GridLayout;
58 import org.eclipse.swt.widgets.Button;
59 import org.eclipse.swt.widgets.Combo;
60 import org.eclipse.swt.widgets.Composite;
61 import org.eclipse.swt.widgets.Control;
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.Shell;
66 import org.eclipse.swt.widgets.Table;
67 import org.eclipse.swt.widgets.TableColumn;
68 import org.eclipse.swt.widgets.Text;
69
70 import java.io.File;
71 import java.util.ArrayList;
72 import java.util.HashMap;
73 import java.util.Map;
74 import java.util.TreeMap;
75 import java.util.Map.Entry;
76
77 /**
78  * AVD creator dialog.
79  *
80  * TODO:
81  * - use SdkTargetSelector instead of Combo
82  * - tooltips on widgets.
83  *
84  */
85 final class AvdCreationDialog extends GridDialog {
86
87     private final AvdManager mAvdManager;
88     private final TreeMap<String, IAndroidTarget> mCurrentTargets =
89         new TreeMap<String, IAndroidTarget>();
90
91     private final Map<String, HardwareProperty> mHardwareMap;
92     private final Map<String, String> mProperties = new HashMap<String, String>();
93     // a list of user-edited properties.
94     private final ArrayList<String> mEditedProperties = new ArrayList<String>();
95     private final ImageFactory mImageFactory;
96     private final ISdkLog mSdkLog;
97
98     private Text mAvdName;
99     private Combo mTargetCombo;
100
101     private Button mSdCardSizeRadio;
102     private Text mSdCardSize;
103     private Combo mSdCardSizeCombo;
104
105     private Text mSdCardFile;
106     private Button mBrowseSdCard;
107     private Button mSdCardFileRadio;
108
109     private Button mSkinListRadio;
110     private Combo mSkinCombo;
111
112     private Button mSkinSizeRadio;
113     private Text mSkinSizeWidth;
114     private Text mSkinSizeHeight;
115
116     private TableViewer mHardwareViewer;
117     private Button mDeleteHardwareProp;
118
119     private Button mForceCreation;
120     private Button mOkButton;
121     private Label mStatusIcon;
122     private Label mStatusLabel;
123     private Composite mStatusComposite;
124
125     /**
126      * {@link VerifyListener} for {@link Text} widgets that should only contains numbers.
127      */
128     private final VerifyListener mDigitVerifier = new VerifyListener() {
129         public void verifyText(VerifyEvent event) {
130             int count = event.text.length();
131             for (int i = 0 ; i < count ; i++) {
132                 char c = event.text.charAt(i);
133                 if (c < '0' || c > '9') {
134                     event.doit = false;
135                     return;
136                 }
137             }
138         }
139     };
140
141     /**
142      * Callback when the AVD name is changed.
143      * Enables the force checkbox if the name is a duplicate.
144      */
145     private class CreateNameModifyListener implements ModifyListener {
146         public void modifyText(ModifyEvent e) {
147             String name = mAvdName.getText().trim();
148             AvdInfo avdMatch = mAvdManager.getAvd(name, false /*validAvdOnly*/);
149             if (avdMatch != null) {
150                 mForceCreation.setEnabled(true);
151             } else {
152                 mForceCreation.setEnabled(false);
153                 mForceCreation.setSelection(false);
154             }
155
156             validatePage();
157         }
158     }
159
160     /**
161      * {@link ModifyListener} used for live-validation of the fields content.
162      */
163     private class ValidateListener extends SelectionAdapter implements ModifyListener {
164         public void modifyText(ModifyEvent e) {
165             validatePage();
166         }
167
168         @Override
169         public void widgetSelected(SelectionEvent e) {
170             super.widgetSelected(e);
171             validatePage();
172         }
173     }
174
175     protected AvdCreationDialog(Shell parentShell,
176             AvdManager avdManager,
177             ImageFactory imageFactory,
178             ISdkLog log) {
179         super(parentShell, 2, false);
180         mAvdManager = avdManager;
181         mImageFactory = imageFactory;
182         mSdkLog = log;
183
184         File hardwareDefs = null;
185
186         SdkManager sdkMan = avdManager.getSdkManager();
187         if (sdkMan != null) {
188             String sdkPath = sdkMan.getLocation();
189             if (sdkPath != null) {
190                 hardwareDefs = new File (sdkPath + File.separator +
191                         SdkConstants.OS_SDK_TOOLS_LIB_FOLDER, SdkConstants.FN_HARDWARE_INI);
192             }
193         }
194
195         if (hardwareDefs == null) {
196             log.error(null, "Failed to load file %s from SDK", SdkConstants.FN_HARDWARE_INI);
197             mHardwareMap = new HashMap<String, HardwareProperty>();
198         } else {
199             mHardwareMap = HardwareProperties.parseHardwareDefinitions(
200                 hardwareDefs, null /*sdkLog*/);
201         }
202     }
203
204     @Override
205     public void create() {
206         super.create();
207
208         Point p = getShell().getSize();
209         if (p.x < 400) {
210             p.x = 400;
211         }
212         getShell().setSize(p);
213     }
214
215     @Override
216     protected Control createContents(Composite parent) {
217         Control control = super.createContents(parent);
218         getShell().setText("Create new AVD");
219
220         mOkButton = getButton(IDialogConstants.OK_ID);
221         validatePage();
222
223         return control;
224     }
225
226     @Override
227     public void createDialogContent(final Composite parent) {
228         GridData gd;
229         GridLayout gl;
230
231         Label label = new Label(parent, SWT.NONE);
232         label.setText("Name:");
233
234         mAvdName = new Text(parent, SWT.BORDER);
235         mAvdName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
236         mAvdName.addModifyListener(new CreateNameModifyListener());
237
238         label = new Label(parent, SWT.NONE);
239         label.setText("Target:");
240
241         mTargetCombo = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
242         mTargetCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
243         mTargetCombo.addSelectionListener(new SelectionAdapter() {
244             @Override
245             public void widgetSelected(SelectionEvent e) {
246                 super.widgetSelected(e);
247                 reloadSkinCombo();
248                 validatePage();
249             }
250         });
251
252         // --- sd card group
253         label = new Label(parent, SWT.NONE);
254         label.setText("SD Card:");
255         label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING,
256                 false, false));
257
258         final Group sdCardGroup = new Group(parent, SWT.NONE);
259         sdCardGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
260         sdCardGroup.setLayout(new GridLayout(3, false));
261
262         mSdCardSizeRadio = new Button(sdCardGroup, SWT.RADIO);
263         mSdCardSizeRadio.setText("Size:");
264         mSdCardSizeRadio.addSelectionListener(new SelectionAdapter() {
265             @Override
266             public void widgetSelected(SelectionEvent arg0) {
267                 boolean sizeMode = mSdCardSizeRadio.getSelection();
268                 enableSdCardWidgets(sizeMode);
269                 validatePage();
270             }
271         });
272
273         ValidateListener validateListener = new ValidateListener();
274
275         mSdCardSize = new Text(sdCardGroup, SWT.BORDER);
276         mSdCardSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
277         mSdCardSize.addVerifyListener(mDigitVerifier);
278         mSdCardSize.addModifyListener(validateListener);
279
280         mSdCardSizeCombo = new Combo(sdCardGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
281         mSdCardSizeCombo.add("KiB");
282         mSdCardSizeCombo.add("MiB");
283         mSdCardSizeCombo.select(1);
284         mSdCardSizeCombo.addSelectionListener(validateListener);
285
286         mSdCardFileRadio = new Button(sdCardGroup, SWT.RADIO);
287         mSdCardFileRadio.setText("File:");
288
289         mSdCardFile = new Text(sdCardGroup, SWT.BORDER);
290         mSdCardFile.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
291         mSdCardFile.addModifyListener(validateListener);
292
293         mBrowseSdCard = new Button(sdCardGroup, SWT.PUSH);
294         mBrowseSdCard.setText("Browse...");
295         mBrowseSdCard.addSelectionListener(new SelectionAdapter() {
296            @Override
297             public void widgetSelected(SelectionEvent arg0) {
298                onBrowseSdCard();
299                validatePage();
300             }
301         });
302
303         mSdCardSizeRadio.setSelection(true);
304         enableSdCardWidgets(true);
305
306         // --- skin group
307         label = new Label(parent, SWT.NONE);
308         label.setText("Skin:");
309         label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING,
310                 false, false));
311
312         final Group skinGroup = new Group(parent, SWT.NONE);
313         skinGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
314         skinGroup.setLayout(new GridLayout(4, false));
315
316         mSkinListRadio = new Button(skinGroup, SWT.RADIO);
317         mSkinListRadio.setText("Built-in:");
318         mSkinListRadio.addSelectionListener(new SelectionAdapter() {
319             @Override
320             public void widgetSelected(SelectionEvent arg0) {
321                 boolean listMode = mSkinListRadio.getSelection();
322                 enableSkinWidgets(listMode);
323                 validatePage();
324             }
325         });
326
327         mSkinCombo = new Combo(skinGroup, SWT.READ_ONLY | SWT.DROP_DOWN);
328         mSkinCombo.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
329         gd.horizontalSpan = 3;
330         mSkinCombo.addSelectionListener(new SelectionAdapter() {
331             @Override
332             public void widgetSelected(SelectionEvent arg0) {
333                 // get the skin info
334                 loadSkin();
335             }
336         });
337
338         mSkinSizeRadio = new Button(skinGroup, SWT.RADIO);
339         mSkinSizeRadio.setText("Resolution:");
340
341         mSkinSizeWidth = new Text(skinGroup, SWT.BORDER);
342         mSkinSizeWidth.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
343         mSkinSizeWidth.addVerifyListener(mDigitVerifier);
344         mSkinSizeWidth.addModifyListener(validateListener);
345
346         new Label(skinGroup, SWT.NONE).setText("x");
347
348         mSkinSizeHeight = new Text(skinGroup, SWT.BORDER);
349         mSkinSizeHeight.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
350         mSkinSizeHeight.addVerifyListener(mDigitVerifier);
351         mSkinSizeHeight.addModifyListener(validateListener);
352
353         mSkinListRadio.setSelection(true);
354         enableSkinWidgets(true);
355
356         // --- hardware group
357         label = new Label(parent, SWT.NONE);
358         label.setText("Hardware:");
359         label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING,
360                 false, false));
361
362         final Group hwGroup = new Group(parent, SWT.NONE);
363         hwGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
364         hwGroup.setLayout(new GridLayout(2, false));
365
366         createHardwareTable(hwGroup);
367
368         // composite for the side buttons
369         Composite hwButtons = new Composite(hwGroup, SWT.NONE);
370         hwButtons.setLayoutData(new GridData(GridData.FILL_VERTICAL));
371         hwButtons.setLayout(gl = new GridLayout(1, false));
372         gl.marginHeight = gl.marginWidth = 0;
373
374         Button b = new Button(hwButtons, SWT.PUSH | SWT.FLAT);
375         b.setText("New...");
376         b.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
377         b.addSelectionListener(new SelectionAdapter() {
378             @Override
379             public void widgetSelected(SelectionEvent event) {
380                 HardwarePropertyChooser dialog = new HardwarePropertyChooser(parent.getShell(),
381                         mHardwareMap, mProperties.keySet());
382                 if (dialog.open() == Window.OK) {
383                     HardwareProperty choice = dialog.getProperty();
384                     if (choice != null) {
385                         mProperties.put(choice.getName(), choice.getDefault());
386                         mHardwareViewer.refresh();
387                     }
388                 }
389             }
390         });
391         mDeleteHardwareProp = new Button(hwButtons, SWT.PUSH | SWT.FLAT);
392         mDeleteHardwareProp.setText("Delete");
393         mDeleteHardwareProp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
394         mDeleteHardwareProp.addSelectionListener(new SelectionAdapter() {
395             @Override
396             public void widgetSelected(SelectionEvent arg0) {
397                 ISelection selection = mHardwareViewer.getSelection();
398                 if (selection instanceof IStructuredSelection) {
399                     String hwName = (String)((IStructuredSelection)selection).getFirstElement();
400                     mProperties.remove(hwName);
401                     mHardwareViewer.refresh();
402                 }
403             }
404         });
405         mDeleteHardwareProp.setEnabled(false);
406
407         // --- end hardware group
408
409         mForceCreation = new Button(parent, SWT.CHECK);
410         mForceCreation.setText("Force create");
411         mForceCreation.setToolTipText("Select this to override any existing AVD");
412         mForceCreation.setLayoutData(new GridData(GridData.END, GridData.CENTER,
413                 true, false, 2, 1));
414         mForceCreation.setEnabled(false);
415         mForceCreation.addSelectionListener(validateListener);
416
417         // add a separator to separate from the ok/cancel button
418         label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
419         label.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1));
420
421         // add stuff for the error display
422         mStatusComposite = new Composite(parent, SWT.NONE);
423         mStatusComposite.setLayoutData(new GridData(GridData.FILL, GridData.CENTER,
424                 true, false, 3, 1));
425         mStatusComposite.setLayout(gl = new GridLayout(2, false));
426         gl.marginHeight = gl.marginWidth = 0;
427
428         mStatusIcon = new Label(mStatusComposite, SWT.NONE);
429         mStatusIcon.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING,
430                 false, false));
431         mStatusLabel = new Label(mStatusComposite, SWT.NONE);
432         mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
433         mStatusLabel.setText(" \n "); //$NON-NLS-1$
434
435         reloadTargetCombo();
436     }
437
438     /**
439      * Creates the UI for the hardware properties table.
440      * This creates the {@link Table}, and several viewers ({@link TableViewer},
441      * {@link TableViewerColumn}) and adds edit support for the 2nd column
442      */
443     private void createHardwareTable(Composite parent) {
444         final Table hardwareTable = new Table(parent, SWT.SINGLE | SWT.FULL_SELECTION);
445         GridData gd = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
446         gd.widthHint = 200;
447         gd.heightHint = 100;
448         hardwareTable.setLayoutData(gd);
449         hardwareTable.setHeaderVisible(true);
450         hardwareTable.setLinesVisible(true);
451
452         // -- Table viewer
453         mHardwareViewer = new TableViewer(hardwareTable);
454         mHardwareViewer.addSelectionChangedListener(new ISelectionChangedListener() {
455             public void selectionChanged(SelectionChangedEvent event) {
456                 // it's a single selection mode, we can just access the selection index
457                 // from the table directly.
458                 mDeleteHardwareProp.setEnabled(hardwareTable.getSelectionIndex() != -1);
459             }
460         });
461
462         // only a content provider. Use viewers per column below (for editing support)
463         mHardwareViewer.setContentProvider(new IStructuredContentProvider() {
464             public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
465                 // we can just ignore this. we just use mProperties directly.
466             }
467
468             public Object[] getElements(Object arg0) {
469                 return mProperties.keySet().toArray();
470             }
471
472             public void dispose() {
473                 // pass
474             }
475         });
476
477         // -- column 1: prop abstract name
478         TableColumn col1 = new TableColumn(hardwareTable, SWT.LEFT);
479         col1.setText("Property");
480         col1.setWidth(150);
481         TableViewerColumn tvc1 = new TableViewerColumn(mHardwareViewer, col1);
482         tvc1.setLabelProvider(new CellLabelProvider() {
483             @Override
484             public void update(ViewerCell cell) {
485                 HardwareProperty prop = mHardwareMap.get(cell.getElement());
486                 cell.setText(prop != null ? prop.getAbstract() : "");
487             }
488         });
489
490         // -- column 2: prop value
491         TableColumn col2 = new TableColumn(hardwareTable, SWT.LEFT);
492         col2.setText("Value");
493         col2.setWidth(50);
494         TableViewerColumn tvc2 = new TableViewerColumn(mHardwareViewer, col2);
495         tvc2.setLabelProvider(new CellLabelProvider() {
496             @Override
497             public void update(ViewerCell cell) {
498                 String value = mProperties.get(cell.getElement());
499                 cell.setText(value != null ? value : "");
500             }
501         });
502
503         // add editing support to the 2nd column
504         tvc2.setEditingSupport(new EditingSupport(mHardwareViewer) {
505             @Override
506             protected void setValue(Object element, Object value) {
507                 String hardwareName = (String)element;
508                 HardwareProperty property = mHardwareMap.get(hardwareName);
509                 switch (property.getType()) {
510                     case INTEGER:
511                         mProperties.put((String)element, (String)value);
512                         break;
513                     case DISKSIZE:
514                         if (HardwareProperties.DISKSIZE_PATTERN.matcher((String)value).matches()) {
515                             mProperties.put((String)element, (String)value);
516                         }
517                         break;
518                     case BOOLEAN:
519                         int index = (Integer)value;
520                         mProperties.put((String)element, HardwareProperties.BOOLEAN_VALUES[index]);
521                         break;
522                 }
523                 mHardwareViewer.refresh(element);
524             }
525
526             @Override
527             protected Object getValue(Object element) {
528                 String hardwareName = (String)element;
529                 HardwareProperty property = mHardwareMap.get(hardwareName);
530                 String value = mProperties.get(hardwareName);
531                 switch (property.getType()) {
532                     case INTEGER:
533                         // intended fall-through.
534                     case DISKSIZE:
535                         return value;
536                     case BOOLEAN:
537                         return HardwareProperties.getBooleanValueIndex(value);
538                 }
539
540                 return null;
541             }
542
543             @Override
544             protected CellEditor getCellEditor(Object element) {
545                 String hardwareName = (String)element;
546                 HardwareProperty property = mHardwareMap.get(hardwareName);
547                 switch (property.getType()) {
548                     // TODO: custom TextCellEditor that restrict input.
549                     case INTEGER:
550                         // intended fall-through.
551                     case DISKSIZE:
552                         return new TextCellEditor(hardwareTable);
553                     case BOOLEAN:
554                         return new ComboBoxCellEditor(hardwareTable,
555                                 HardwareProperties.BOOLEAN_VALUES,
556                                 SWT.READ_ONLY | SWT.DROP_DOWN);
557                 }
558                 return null;
559             }
560
561             @Override
562             protected boolean canEdit(Object element) {
563                 String hardwareName = (String)element;
564                 HardwareProperty property = mHardwareMap.get(hardwareName);
565                 return property != null;
566             }
567         });
568
569
570         mHardwareViewer.setInput(mProperties);
571     }
572
573     @Override
574     protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
575         if (id == IDialogConstants.OK_ID) {
576             label = "Create AVD";
577         }
578
579         return super.createButton(parent, id, label, defaultButton);
580     }
581
582     @Override
583     protected void okPressed() {
584         if (createAvd()) {
585             super.okPressed();
586         }
587     }
588
589     /**
590      * Enable or disable the sd card widgets.
591      * @param sizeMode if true the size-based widgets are to be enabled, and the file-based ones
592      * disabled.
593      */
594     private void enableSdCardWidgets(boolean sizeMode) {
595         mSdCardSize.setEnabled(sizeMode);
596         mSdCardSizeCombo.setEnabled(sizeMode);
597
598         mSdCardFile.setEnabled(!sizeMode);
599         mBrowseSdCard.setEnabled(!sizeMode);
600     }
601
602     /**
603      * Enable or disable the skin widgets.
604      * @param listMode if true the list-based widgets are to be enabled, and the size-based ones
605      * disabled.
606      */
607     private void enableSkinWidgets(boolean listMode) {
608         mSkinCombo.setEnabled(listMode);
609
610         mSkinSizeWidth.setEnabled(!listMode);
611         mSkinSizeHeight.setEnabled(!listMode);
612     }
613
614
615     private void onBrowseSdCard() {
616         FileDialog dlg = new FileDialog(getContents().getShell(), SWT.OPEN);
617         dlg.setText("Choose SD Card image file.");
618
619         String fileName = dlg.open();
620         if (fileName != null) {
621             mSdCardFile.setText(fileName);
622         }
623     }
624
625     private void reloadTargetCombo() {
626         String selected = null;
627         int index = mTargetCombo.getSelectionIndex();
628         if (index >= 0) {
629             selected = mTargetCombo.getItem(index);
630         }
631
632         mCurrentTargets.clear();
633         mTargetCombo.removeAll();
634
635         boolean found = false;
636         index = -1;
637
638         SdkManager sdkManager = mAvdManager.getSdkManager();
639         if (sdkManager != null) {
640             for (IAndroidTarget target : sdkManager.getTargets()) {
641                 String name;
642                 if (target.isPlatform()) {
643                     name = String.format("%s - API Level %s",
644                             target.getName(),
645                             target.getVersion().getApiString());
646                 } else {
647                     name = String.format("%s (%s) - API Level %s",
648                             target.getName(),
649                             target.getVendor(),
650                             target.getVersion().getApiString());
651                 }
652                 mCurrentTargets.put(name, target);
653                 mTargetCombo.add(name);
654                 if (!found) {
655                     index++;
656                     found = name.equals(selected);
657                 }
658             }
659         }
660
661         mTargetCombo.setEnabled(mCurrentTargets.size() > 0);
662
663         if (found) {
664             mTargetCombo.select(index);
665         }
666
667         reloadSkinCombo();
668     }
669
670     private void reloadSkinCombo() {
671         String selected = null;
672         int index = mSkinCombo.getSelectionIndex();
673         if (index >= 0) {
674             selected = mSkinCombo.getItem(index);
675         }
676
677         mSkinCombo.removeAll();
678         mSkinCombo.setEnabled(false);
679
680         index = mTargetCombo.getSelectionIndex();
681         if (index >= 0) {
682
683             String targetName = mTargetCombo.getItem(index);
684
685             boolean found = false;
686             IAndroidTarget target = mCurrentTargets.get(targetName);
687             if (target != null) {
688                 mSkinCombo.add(String.format("Default (%s)", target.getDefaultSkin()));
689
690                 index = -1;
691                 for (String skin : target.getSkins()) {
692                     mSkinCombo.add(skin);
693                     if (!found) {
694                         index++;
695                         found = skin.equals(selected);
696                     }
697                 }
698
699                 mSkinCombo.setEnabled(true);
700
701                 if (found) {
702                     mSkinCombo.select(index);
703                 } else {
704                     mSkinCombo.select(0);  // default
705                     loadSkin();
706                 }
707             }
708         }
709     }
710
711     /**
712      * Validates the fields, displays errors and warnings.
713      * Enables the finish button if there are no errors.
714      */
715     private void validatePage() {
716         String error = null;
717
718         // Validate AVD name
719         String avdName = mAvdName.getText().trim();
720         boolean hasAvdName = avdName.length() > 0;
721         if (hasAvdName && !AvdManager.RE_AVD_NAME.matcher(avdName).matches()) {
722             error = String.format(
723                 "AVD name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
724                 avdName, AvdManager.CHARS_AVD_NAME);
725         }
726
727         // Validate target
728         if (hasAvdName && error == null && mTargetCombo.getSelectionIndex() < 0) {
729             error = "A target must be selected in order to create an AVD.";
730         }
731
732         // Validate SDCard path or value
733         if (error == null) {
734             // get the mode. We only need to check the file since the
735             // verifier on the size Text will prevent invalid input
736             boolean sdcardFileMode = mSdCardFileRadio.getSelection();
737             if (sdcardFileMode) {
738                 String sdName = mSdCardFile.getText().trim();
739                 if (sdName.length() > 0 && !new File(sdName).isFile()) {
740                     error = "SD Card path isn't valid.";
741                 }
742             } else {
743                 String valueString = mSdCardSize.getText();
744                 if (valueString.length() > 0) {
745                     int value = Integer.parseInt(valueString); // verifier makes this
746                                                                // unlikely to fail.
747                     switch (mSdCardSizeCombo.getSelectionIndex()) {
748                         case 0:
749                             value *= 1024;
750                             break;
751                         case 1:
752                             value *= 1024 * 1024;
753                             break;
754                     }
755
756                     if (value < 9 * 1024 * 1024) {
757                         error = "SD Card size must be at least 9MB";
758                     }
759                 }
760             }
761         }
762
763         // validate the skin
764         if (error == null) {
765             // get the mode, we should only check if it's in size mode since
766             // the built-in list mode is always valid.
767             if (mSkinSizeRadio.getSelection()) {
768                 // need both with and heigh to be non null
769                 String width = mSkinSizeWidth.getText();   // no need for trim, since the verifier
770                 String height = mSkinSizeHeight.getText(); // rejects non digit.
771
772                 if (width.length() == 0 || height.length() == 0) {
773                     error = "Skin size is incorrect.\nBoth dimensions must be > 0";
774                 }
775             }
776         }
777
778         // Check for duplicate AVD name
779         if (hasAvdName && error == null) {
780             AvdInfo avdMatch = mAvdManager.getAvd(avdName, false /*validAvdOnly*/);
781             if (avdMatch != null && !mForceCreation.getSelection()) {
782                 error = String.format(
783                         "The AVD name '%s' is already used.\n" +
784                         "Check \"Force create\" to override existing AVD.",
785                         avdName);
786             }
787         }
788
789         // Validate the create button
790         boolean can_create = hasAvdName && error == null;
791         if (can_create) {
792             can_create &= mTargetCombo.getSelectionIndex() >= 0;
793         }
794         mOkButton.setEnabled(can_create);
795
796         // -- update UI
797         if (error != null) {
798             mStatusIcon.setImage(mImageFactory.getImageByName("reject_icon16.png"));
799             mStatusLabel.setText(error);
800         } else {
801             mStatusIcon.setImage(null);
802             mStatusLabel.setText(" \n "); //$NON-NLS-1$
803         }
804
805         mStatusComposite.pack(true);
806     }
807
808     private void loadSkin() {
809         int targetIndex = mTargetCombo.getSelectionIndex();
810         if (targetIndex < 0) {
811             return;
812         }
813
814         // resolve the target.
815         String targetName = mTargetCombo.getItem(targetIndex);
816         IAndroidTarget target = mCurrentTargets.get(targetName);
817         if (target == null) {
818             return;
819         }
820
821         // get the skin name
822         String skinName = null;
823         int skinIndex = mSkinCombo.getSelectionIndex();
824         if (skinIndex < 0) {
825             return;
826         } else if (skinIndex == 0) { // default skin for the target
827             skinName = target.getDefaultSkin();
828         } else {
829             skinName = mSkinCombo.getItem(skinIndex);
830         }
831
832         // load the skin properties
833         String path = target.getPath(IAndroidTarget.SKINS);
834         File skin = new File(path, skinName);
835         if (skin.isDirectory() == false && target.isPlatform() == false) {
836             // it's possible the skin is in the parent target
837             path = target.getParent().getPath(IAndroidTarget.SKINS);
838             skin = new File(path, skinName);
839         }
840
841         if (skin.isDirectory() == false) {
842             return;
843         }
844
845         // now get the hardware.ini from the add-on (if applicable) and from the skin
846         // (if applicable)
847         HashMap<String, String> hardwareValues = new HashMap<String, String>();
848         if (target.isPlatform() == false) {
849             File targetHardwareFile = new File(target.getLocation(), AvdManager.HARDWARE_INI);
850             if (targetHardwareFile.isFile()) {
851                 Map<String, String> targetHardwareConfig = SdkManager.parsePropertyFile(
852                         targetHardwareFile, null /*log*/);
853                 if (targetHardwareConfig != null) {
854                     hardwareValues.putAll(targetHardwareConfig);
855                 }
856             }
857         }
858
859         // from the skin
860         File skinHardwareFile = new File(skin, AvdManager.HARDWARE_INI);
861         if (skinHardwareFile.isFile()) {
862             Map<String, String> skinHardwareConfig = SdkManager.parsePropertyFile(
863                     skinHardwareFile, null /*log*/);
864             if (skinHardwareConfig != null) {
865                 hardwareValues.putAll(skinHardwareConfig);
866             }
867         }
868
869         // now set those values in the list of properties for the AVD.
870         // We just check that none of those properties have been edited by the user yet.
871         for (Entry<String, String> entry : hardwareValues.entrySet()) {
872             if (mEditedProperties.contains(entry.getKey()) == false) {
873                 mProperties.put(entry.getKey(), entry.getValue());
874             }
875         }
876
877         mHardwareViewer.refresh();
878     }
879
880     /**
881      * Creates an AVD from the values in the UI. Called when the user presses the OK button.
882      */
883     private boolean createAvd() {
884         String avdName = mAvdName.getText().trim();
885         int targetIndex = mTargetCombo.getSelectionIndex();
886
887         // quick check on the name and the target selection
888         if (avdName.length() == 0 || targetIndex < 0) {
889             return false;
890         }
891
892         // resolve the target.
893         String targetName = mTargetCombo.getItem(targetIndex);
894         IAndroidTarget target = mCurrentTargets.get(targetName);
895         if (target == null) {
896             return false;
897         }
898
899         // get the SD card data from the UI.
900         String sdName = null;
901         if (mSdCardSizeRadio.getSelection()) {
902             // size mode
903             String value = mSdCardSize.getText().trim();
904             if (value.length() > 0) {
905                 sdName = value;
906                 // add the unit
907                 switch (mSdCardSizeCombo.getSelectionIndex()) {
908                     case 0:
909                         sdName += "K";
910                         break;
911                     case 1:
912                         sdName += "M";
913                         break;
914                     default:
915                         // shouldn't be here
916                         assert false;
917                 }
918             }
919         } else {
920             // file mode.
921             sdName = mSdCardFile.getText().trim();
922         }
923
924         // get the Skin data from the UI
925         String skinName = null;
926         if (mSkinListRadio.getSelection()) {
927             // built-in list provides the skin
928             int skinIndex = mSkinCombo.getSelectionIndex();
929             if (skinIndex > 0) {
930                 // index 0 is the default, we don't use it
931                 skinName = mSkinCombo.getItem(skinIndex);
932             }
933         } else {
934             // size mode. get both size and writes it as a skin name
935             // thanks to validatePage() we know the content of the fields is correct
936             skinName = mSkinSizeWidth.getText() + "x" + mSkinSizeHeight.getText(); //$NON-NLS-1$
937         }
938
939         ISdkLog log = mSdkLog;
940         if (log == null || log instanceof MessageBoxLog) {
941             // If the current logger is a message box, we use our own (to make sure
942             // to display errors right away and customize the title).
943             log = new MessageBoxLog(
944                     String.format("Result of creating AVD '%s':", avdName),
945                     getContents().getDisplay(),
946                     false /*logErrorsOnly*/);
947         }
948
949         File avdFolder = null;
950         try {
951             avdFolder = new File(
952                     AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
953                     avdName + AvdManager.AVD_FOLDER_EXTENSION);
954         } catch (AndroidLocationException e) {
955             return false;
956         }
957
958         boolean force = mForceCreation.getSelection();
959
960         boolean success = false;
961         AvdInfo avdInfo = mAvdManager.createAvd(
962                 avdFolder,
963                 avdName,
964                 target,
965                 skinName,
966                 sdName,
967                 mProperties,
968                 force,
969                 log);
970
971         success = avdInfo != null;
972
973         if (log instanceof MessageBoxLog) {
974             ((MessageBoxLog) log).displayResult(success);
975         }
976         return success;
977     }
978 }