am beb3214: ADT #1823896: Ability to add an "extra" action to the AVD Se

Merge commit 'beb3214b46e18bb461760c47f04c5b909557d060'

* commit 'beb3214b46e18bb461760c47f04c5b909557d060':
  ADT #1823896: Ability to add an "extra" action to the AVD Selector.
This commit is contained in:
Raphael
2009-05-01 14:08:07 -07:00
committed by The Android Open Source Project
9 changed files with 315 additions and 106 deletions

View File

@@ -65,7 +65,6 @@ import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Shell;
@@ -484,26 +483,22 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
AdtPlugin.printToConsole(project, String.format( AdtPlugin.printToConsole(project, String.format(
"Failed to find an AVD compatible with target '%1$s'.", "Failed to find an AVD compatible with target '%1$s'.",
projectTarget.getName())); projectTarget.getName()));
final Display display = AdtPlugin.getDisplay(); final Display display = AdtPlugin.getDisplay();
final int[] result = new int[] { Window.CANCEL }; final boolean[] searchAgain = new boolean[] { false };
// ask the user to create a new one. // ask the user to create a new one.
display.syncExec(new Runnable() { display.syncExec(new Runnable() {
public void run() { public void run() {
Shell shell = display.getActiveShell(); Shell shell = display.getActiveShell();
if (MessageDialog.openQuestion(shell, "AVD Error", if (MessageDialog.openQuestion(shell, "Android AVD Error",
"No Compatible targets were found. Do you wish to create one?")) { "No compatible targets were found. Do you wish to a add new Android Virtual Device?")) {
AvdManagerAction action = new AvdManagerAction(); AvdManagerAction action = new AvdManagerAction();
action.run(null /*action*/); action.run(null /*action*/);
result[0] = action.getDialogResult(); searchAgain[0] = true;
} }
} }
}); });
if (result[0] == Window.CANCEL) { if (searchAgain[0]) {
AdtPlugin.printErrorToConsole(project, String.format("Launch aborted."));
stopLaunch(launchInfo);
return;
} else {
// attempt to reload the AVDs and find one compatible. // attempt to reload the AVDs and find one compatible.
defaultAvd = findMatchingAvd(avdManager, projectTarget); defaultAvd = findMatchingAvd(avdManager, projectTarget);
@@ -588,9 +583,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
} }
/** /**
* @param avdManager * Find a matching AVD.
* @param projectTarget
* @return
*/ */
private AvdInfo findMatchingAvd(AvdManager avdManager, final IAndroidTarget projectTarget) { private AvdInfo findMatchingAvd(AvdManager avdManager, final IAndroidTarget projectTarget) {
AvdInfo[] avds = avdManager.getValidAvds(); AvdInfo[] avds = avdManager.getValidAvds();

View File

@@ -33,6 +33,7 @@ import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.avd.AvdManager; import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.AvdManager.AvdInfo; import com.android.sdklib.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.AvdSelector; import com.android.sdkuilib.AvdSelector;
import com.android.sdkuilib.AvdSelector.SelectionMode;
import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogConstants;
@@ -327,7 +328,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
if (deviceMode) { if (deviceMode) {
handleDeviceSelection(); handleDeviceSelection();
} else { } else {
mResponse.setAvdToLaunch(mPreferredAvdSelector.getFirstSelected()); mResponse.setAvdToLaunch(mPreferredAvdSelector.getSelected());
} }
enableOkButton(); enableOkButton();
@@ -417,13 +418,22 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
mPreferredAvdSelector = new AvdSelector(offsetComp, mPreferredAvdSelector = new AvdSelector(offsetComp,
getNonRunningAvds(false /*reloadAvds*/), getNonRunningAvds(false /*reloadAvds*/),
mProjectTarget, mProjectTarget,
new Runnable() { new AvdSelector.IExtraAction() {
public void run() { public void run() {
AvdManagerAction action = new AvdManagerAction(); AvdManagerAction action = new AvdManagerAction();
action.run(null); action.run(null);
refillAvdList(true /*reloadAvds*/); refillAvdList(true /*reloadAvds*/);
} }
});
public boolean isEnabled() {
return true;
}
public String label() {
return "AVD Manager...";
}
},
SelectionMode.CHECK);
mPreferredAvdSelector.setTableHeightHint(100); mPreferredAvdSelector.setTableHeightHint(100);
mPreferredAvdSelector.setEnabled(false); mPreferredAvdSelector.setEnabled(false);
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
@@ -434,7 +444,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
@Override @Override
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
if (mDisableAvdSelectionChange == false) { if (mDisableAvdSelectionChange == false) {
mResponse.setAvdToLaunch(mPreferredAvdSelector.getFirstSelected()); mResponse.setAvdToLaunch(mPreferredAvdSelector.getSelected());
enableOkButton(); enableOkButton();
} }
} }
@@ -735,7 +745,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
AvdInfo[] array = getNonRunningAvds(reloadAvds); AvdInfo[] array = getNonRunningAvds(reloadAvds);
// save the current selection // save the current selection
AvdInfo selected = mPreferredAvdSelector.getFirstSelected(); AvdInfo selected = mPreferredAvdSelector.getSelected();
// disable selection change. // disable selection change.
mDisableAvdSelectionChange = true; mDisableAvdSelectionChange = true;

View File

@@ -27,6 +27,7 @@ import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.avd.AvdManager; import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.AvdManager.AvdInfo; import com.android.sdklib.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.AvdSelector; import com.android.sdkuilib.AvdSelector;
import com.android.sdkuilib.AvdSelector.SelectionMode;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
@@ -192,13 +193,22 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
mPreferredAvdLabel.setText("Select a preferred Android Virtual Device for deployment:"); mPreferredAvdLabel.setText("Select a preferred Android Virtual Device for deployment:");
mPreferredAvdSelector = new AvdSelector(offsetComp, mPreferredAvdSelector = new AvdSelector(offsetComp,
null /*avds*/, null /*avds*/,
new Runnable() { new AvdSelector.IExtraAction() {
public void run() { public void run() {
AvdManagerAction action = new AvdManagerAction(); AvdManagerAction action = new AvdManagerAction();
action.run(null); action.run(null);
updateAvdList(null); updateAvdList(null);
} }
});
public boolean isEnabled() {
return true;
}
public String label() {
return "AVD Manager...";
}
},
SelectionMode.CHECK);
mPreferredAvdSelector.setTableHeightHint(100); mPreferredAvdSelector.setTableHeightHint(100);
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
@Override @Override
@@ -445,7 +455,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
public void performApply(ILaunchConfigurationWorkingCopy configuration) { public void performApply(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
mAutoTargetButton.getSelection()); mAutoTargetButton.getSelection());
AvdInfo avd = mPreferredAvdSelector.getFirstSelected(); AvdInfo avd = mPreferredAvdSelector.getSelected();
if (avd != null) { if (avd != null) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, avd.getName()); configuration.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, avd.getName());
} else { } else {

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.ui;
import org.eclipse.jface.wizard.WizardDialog;
/**
* An interface that enables a client to update {@link WizardDialog} after its creation.
*/
public interface IUpdateWizardDialog {
/**
* Invoked after {@link WizardDialog#create()} to let the caller update the dialog.
*/
public void updateWizardDialog(WizardDialogEx dialog);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.ui;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Shell;
/**
* A {@link WizardDialog} that gives access to some inner controls.
*/
public final class WizardDialogEx extends WizardDialog {
/**
* @see WizardDialog#WizardDialog(Shell, IWizard)
*/
public WizardDialogEx(Shell parentShell, IWizard newWizard) {
super(parentShell, newWizard);
}
/**
* Returns the cancel button.
* <p/>
* Note: there is already a protected, deprecated method that does the same thing.
* To avoid overriding a deprecated method, the name as be changed to ...Ex.
*/
public Button getCancelButtonEx() {
return getButton(IDialogConstants.CANCEL_ID);
}
}

View File

@@ -16,12 +16,14 @@
package com.android.ide.eclipse.adt.wizards.actions; package com.android.ide.eclipse.adt.wizards.actions;
import com.android.ide.eclipse.adt.ui.IUpdateWizardDialog;
import com.android.ide.eclipse.adt.ui.WizardDialogEx;
import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorInput;
@@ -124,9 +126,13 @@ import org.eclipse.ui.internal.util.Util;
// It's not visible yet until a dialog is created and opened // It's not visible yet until a dialog is created and opened
Shell parent = window.getShell(); Shell parent = window.getShell();
WizardDialog dialog = new WizardDialog(parent, mWizard); WizardDialogEx dialog = new WizardDialogEx(parent, mWizard);
dialog.create(); dialog.create();
if (mWizard instanceof IUpdateWizardDialog) {
((IUpdateWizardDialog) mWizard).updateWizardDialog(dialog);
}
// This code comes straight from NewWizardShortcutAction#run() // This code comes straight from NewWizardShortcutAction#run()
Point defaultSize = dialog.getShell().getSize(); Point defaultSize = dialog.getShell().getSize();
dialog.getShell().setSize( dialog.getShell().setSize(

View File

@@ -27,6 +27,7 @@ import com.android.sdklib.ISdkLog;
import com.android.sdklib.avd.AvdManager; import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.AvdManager.AvdInfo; import com.android.sdklib.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.AvdSelector; import com.android.sdkuilib.AvdSelector;
import com.android.sdkuilib.AvdSelector.SelectionMode;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.jface.wizard.WizardPage; import org.eclipse.jface.wizard.WizardPage;
@@ -56,7 +57,6 @@ import java.util.TreeMap;
class AvdManagerListPage extends WizardPage { class AvdManagerListPage extends WizardPage {
private AvdSelector mAvdSelector; private AvdSelector mAvdSelector;
private Button mDeleteButton;
private Button mRefreshButton; private Button mRefreshButton;
private Text mCreateName; private Text mCreateName;
private Combo mCreateTargetCombo; private Combo mCreateTargetCombo;
@@ -162,32 +162,21 @@ class AvdManagerListPage extends WizardPage {
} }
}); });
mAvdSelector = new AvdSelector(parent, null); mAvdSelector = new AvdSelector(parent,
mAvdSelector.setSelectionListener(new SelectionAdapter() { SelectionMode.SELECT,
@Override new AvdSelector.IExtraAction() {
public void widgetSelected(SelectionEvent e) { public String label() {
super.widgetSelected(e); return "Delete AVD...";
updateDeleteButton(); }
}
});
mDeleteButton = new Button(parent, SWT.PUSH); public boolean isEnabled() {
mDeleteButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false)); return mAvdSelector != null && mAvdSelector.getSelected() != null;
mDeleteButton.addSelectionListener(new SelectionAdapter() { }
@Override
public void widgetSelected(SelectionEvent e) { public void run() {
onDelete(); onDelete();
} }
}); });
updateDeleteButton();
}
private void updateDeleteButton() {
AvdInfo selected = mAvdSelector.getFirstSelected();
mDeleteButton.setText(String.format("Delete %s...",
selected == null ? "" : selected.getName())); //$NON-NLS-1$
mDeleteButton.pack();
mDeleteButton.setEnabled(selected != null);
} }
/** /**
@@ -441,19 +430,24 @@ class AvdManagerListPage extends WizardPage {
* Tries to preserve the selection. * Tries to preserve the selection.
*/ */
private void reloadAvdList() { private void reloadAvdList() {
AvdInfo selected = mAvdSelector.getFirstSelected(); AvdInfo selected = mAvdSelector.getSelected();
AvdManager avdm = getAvdManager(); AvdManager avdm = getAvdManager();
AvdInfo[] avds = null;
// For the AVD manager to reload the list, in case AVDs where created using the // For the AVD manager to reload the list, in case AVDs where created using the
// command line tool. // command line tool.
try { // The AVD manager may not exist yet, typically when loading the SDK.
avdm.reloadAvds(); if (avdm != null) {
} catch (AndroidLocationException e) { try {
AdtPlugin.log(e, "AVD Manager reload failed"); //$NON-NLS-1$ avdm.reloadAvds();
} catch (AndroidLocationException e) {
AdtPlugin.log(e, "AVD Manager reload failed"); //$NON-NLS-1$
}
avds = avdm.getValidAvds();
} }
AvdInfo[] avds = avdm == null ? null : avdm.getValidAvds();
mAvdSelector.setAvds(avds, null /*filter*/); mAvdSelector.setAvds(avds, null /*filter*/);
// Keep the list of known AVD names to check if they exist quickly. however // Keep the list of known AVD names to check if they exist quickly. however
@@ -473,11 +467,11 @@ class AvdManagerListPage extends WizardPage {
} }
/** /**
* Triggered when the user selects the "delete" button. * Triggered when the user selects the "delete" button (the extra action in the selector)
* Deletes the currently selected AVD, if any. * Deletes the currently selected AVD, if any.
*/ */
private void onDelete() { private void onDelete() {
AvdInfo avdInfo = mAvdSelector.getFirstSelected(); AvdInfo avdInfo = mAvdSelector.getSelected();
AvdManager avdm = getAvdManager(); AvdManager avdm = getAvdManager();
if (avdInfo == null || avdm == null) { if (avdInfo == null || avdm == null) {
return; return;
@@ -496,7 +490,6 @@ class AvdManagerListPage extends WizardPage {
log.display(success); log.display(success);
reloadAvdList(); reloadAvdList();
updateDeleteButton();
} }
/** /**

View File

@@ -18,11 +18,14 @@
package com.android.ide.eclipse.adt.wizards.avdmanager; package com.android.ide.eclipse.adt.wizards.avdmanager;
import com.android.ide.eclipse.adt.ui.IUpdateWizardDialog;
import com.android.ide.eclipse.adt.ui.WizardDialogEx;
import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.IconFactory;
import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard; import org.eclipse.jface.wizard.Wizard;
import org.eclipse.swt.widgets.Button;
import org.eclipse.ui.INewWizard; import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbench;
@@ -34,7 +37,7 @@ import org.eclipse.ui.IWorkbench;
* to get something quick out of the door. We'll need to revisit this when we implement * to get something quick out of the door. We'll need to revisit this when we implement
* the final standalone AVD Manager UI and this Wizard will go away. * the final standalone AVD Manager UI and this Wizard will go away.
*/ */
public class AvdManagerWizard extends Wizard implements INewWizard { public class AvdManagerWizard extends Wizard implements INewWizard, IUpdateWizardDialog {
private static final String PROJECT_LOGO_LARGE = "android_large"; //$NON-NLS-1$ private static final String PROJECT_LOGO_LARGE = "android_large"; //$NON-NLS-1$
@@ -97,4 +100,15 @@ public class AvdManagerWizard extends Wizard implements INewWizard {
setDefaultPageImageDescriptor(desc); setDefaultPageImageDescriptor(desc);
} }
/**
* Invoked once the dialog frame as been created.
* We use it to hide the cancel button, which looks odd here.
*/
public void updateWizardDialog(WizardDialogEx dialog) {
Button cancel = dialog.getCancelButtonEx();
if (cancel != null) {
cancel.setVisible(false);
}
}
} }

View File

@@ -42,9 +42,9 @@ import org.eclipse.swt.widgets.TableItem;
/** /**
* The AVD selector is a table that is added to the given parent composite. * The AVD selector is a table that is added to the given parent composite.
* <p/> * <p/>
* To use, create it using {@link #AvdSelector(Composite, AvdInfo[])} then * To use, create it using {@link #AvdSelector(Composite, SelectionMode, IExtraAction)} then
* call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)} * call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)}
* and finally use {@link #getFirstSelected()} to retrieve the selection. * and finally use {@link #getSelected()} to retrieve the selection.
*/ */
public final class AvdSelector { public final class AvdSelector {
@@ -54,6 +54,51 @@ public final class AvdSelector {
private Label mDescription; private Label mDescription;
private static int NUM_COL = 2; private static int NUM_COL = 2;
private final SelectionMode mSelectionMode;
private final IExtraAction mExtraAction;
private Button mExtraActionButton;
/** The selection mode, either {@link #SELECT} or {@link #CHECK} */
public enum SelectionMode {
/**
* In the "check" selection mode, checkboxes are displayed on each line
* and {@link AvdSelector#getSelected()} returns the line that is checked
* even if it is not the currently selected line. Only one line can
* be checked at once.
*/
CHECK,
/**
* In the "select" selection mode, there are no checkboxes and
* {@link AvdSelector#getSelected()} returns the line currently selected.
* Only one line can be selected at once.
*/
SELECT
}
/**
* Defines an "extra action" button that can be shown under the AVD Selector.
*/
public interface IExtraAction {
/**
* Label of the button that will be created.
* This is invoked once when the button is created and cannot be changed later.
*/
public String label();
/**
* This is invoked just after the selection has changed to update the "enabled"
* state of the action. Implementation should use {@link AvdSelector#getSelected()}.
*/
public boolean isEnabled();
/**
* Run the action, invoked when the button is clicked.
*
* The caller's action is responsible for reloading the AVD list
* using {@link AvdSelector#setAvds(AvdInfo[], IAndroidTarget)}.
*/
public void run();
}
/** /**
* Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}, filtered * Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}, filtered
@@ -65,23 +110,29 @@ public final class AvdSelector {
* @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify. * @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify.
* It can be null. * It can be null.
* @param filter When non-null, will display only the AVDs matching this target. * @param filter When non-null, will display only the AVDs matching this target.
* @param avdManagerAction A runnable to associate with an "AVD Manager" button. This button * @param extraAction When non-null, displays an extra action button.
* is hidden if null is passed. The caller's action is responsible for reloading * @param selectionMode One of {@link SelectionMode#SELECT} or {@link SelectionMode#CHECK}
* the AVD list using {@link #setAvds(AvdInfo[], IAndroidTarget)}.
*/ */
public AvdSelector(Composite parent, public AvdSelector(Composite parent,
AvdInfo[] avds, AvdInfo[] avds,
IAndroidTarget filter, IAndroidTarget filter,
final Runnable avdManagerAction) { IExtraAction extraAction,
SelectionMode selectionMode) {
mAvds = avds; mAvds = avds;
mExtraAction = extraAction;
mSelectionMode = selectionMode;
// Layout has 2 columns // Layout has 2 columns
Composite group = new Composite(parent, SWT.NONE); Composite group = new Composite(parent, SWT.NONE);
group.setLayout(new GridLayout(NUM_COL, false /*makeColumnsEqualWidth*/)); group.setLayout(new GridLayout(NUM_COL, false /*makeColumnsEqualWidth*/));
group.setLayoutData(new GridData(GridData.FILL_BOTH)); group.setLayoutData(new GridData(GridData.FILL_BOTH));
group.setFont(parent.getFont()); group.setFont(parent.getFont());
mTable = new Table(group, SWT.CHECK | SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER); int style = SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER;
if (selectionMode == SelectionMode.CHECK) {
style |= SWT.CHECK;
}
mTable = new Table(group, style);
mTable.setHeaderVisible(true); mTable.setHeaderVisible(true);
mTable.setLinesVisible(false); mTable.setLinesVisible(false);
setTableHeightHint(0); setTableHeightHint(0);
@@ -89,14 +140,15 @@ public final class AvdSelector {
mDescription = new Label(group, SWT.WRAP); mDescription = new Label(group, SWT.WRAP);
mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
if (avdManagerAction != null) { if (extraAction != null) {
Button avdManagerButton = new Button(group, SWT.PUSH); mExtraActionButton = new Button(group, SWT.PUSH);
avdManagerButton.setText("AVD Manager..."); mExtraActionButton.setText(extraAction.label());
avdManagerButton.addSelectionListener(new SelectionAdapter() { mExtraActionButton.setEnabled(extraAction.isEnabled());
mExtraActionButton.addSelectionListener(new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e); super.widgetSelected(e);
avdManagerAction.run(); mExtraAction.run();
} }
}); });
} }
@@ -123,29 +175,33 @@ public final class AvdSelector {
* @param parent The parent composite where the selector will be added. * @param parent The parent composite where the selector will be added.
* @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify. * @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify.
* It can be null. * It can be null.
* @param extraAction When non-null, displays an extra action button.
* @param selectionMode One of {@link SelectionMode#SELECT} or {@link SelectionMode#CHECK}
*/ */
public AvdSelector(Composite parent, AvdInfo[] avds) { public AvdSelector(Composite parent,
this(parent, avds, null /* filter */, null /* avdManagerAction */); AvdInfo[] avds,
IExtraAction extraAction,
SelectionMode selectionMode) {
this(parent, avds, null /* filter */, extraAction, selectionMode);
} }
/** /**
* Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}. * Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}.
* *
* @param parent The parent composite where the selector will be added. * @param parent The parent composite where the selector will be added.
* @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify. * @param extraAction When non-null, displays an extra action button.
* It can be null. * @param selectionMode One of {@link SelectionMode#SELECT} or {@link SelectionMode#CHECK}
* @param avdManagerAction A runnable to associate with an "AVD Manager" button. This button
* is hidden if null is passed. The caller's action is responsible for reloading
* the AVD list using {@link #setAvds(AvdInfo[], IAndroidTarget)}.
*/ */
public AvdSelector(Composite parent, AvdInfo[] avds, Runnable avdManagerAction) { public AvdSelector(Composite parent,
this(parent, avds, null /* filter */, avdManagerAction); SelectionMode selectionMode,
IExtraAction extraAction) {
this(parent, null /*avds*/, null /* filter */, extraAction, selectionMode);
} }
/** /**
* Sets the table grid layout data. * Sets the table grid layout data.
* *
* @param heightHint If > 0, tthe height hint is set to the requested value. * @param heightHint If > 0, the height hint is set to the requested value.
*/ */
public void setTableHeightHint(int heightHint) { public void setTableHeightHint(int heightHint) {
GridData data = new GridData(); GridData data = new GridData();
@@ -162,7 +218,10 @@ public final class AvdSelector {
/** /**
* Sets a new set of AVD, with an optional filter. * Sets a new set of AVD, with an optional filter.
* <p/>This must be called from the UI thread. * Tries to keep the selection.
* <p/>
* This must be called from the UI thread.
*
* *
* @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify. * @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify.
* It can be null. * It can be null.
@@ -170,8 +229,13 @@ public final class AvdSelector {
* filter target will displayed an available for selection. * filter target will displayed an available for selection.
*/ */
public void setAvds(AvdInfo[] avds, IAndroidTarget filter) { public void setAvds(AvdInfo[] avds, IAndroidTarget filter) {
AvdInfo selected = getSelected();
mAvds = avds; mAvds = avds;
fillTable(mTable, filter); fillTable(mTable, filter);
setSelection(selected);
} }
/** /**
@@ -191,7 +255,7 @@ public final class AvdSelector {
* The event's item contains a {@link TableItem}. * The event's item contains a {@link TableItem}.
* The {@link TableItem#getData()} contains an {@link IAndroidTarget}. * The {@link TableItem#getData()} contains an {@link IAndroidTarget}.
* <p/> * <p/>
* It is recommended that the caller uses the {@link #getFirstSelected()} method instead. * It is recommended that the caller uses the {@link #getSelected()} method instead.
* *
* @param selectionListener The new listener or null to remove it. * @param selectionListener The new listener or null to remove it.
*/ */
@@ -211,16 +275,33 @@ public final class AvdSelector {
public boolean setSelection(AvdInfo target) { public boolean setSelection(AvdInfo target) {
boolean found = false; boolean found = false;
boolean modified = false; boolean modified = false;
int selIndex = mTable.getSelectionIndex();
int index = 0;
for (TableItem i : mTable.getItems()) { for (TableItem i : mTable.getItems()) {
if ((AvdInfo) i.getData() == target) { if (mSelectionMode == SelectionMode.SELECT) {
found = true; if ((AvdInfo) i.getData() == target) {
if (!i.getChecked()) { found = true;
modified = true; if (index != selIndex) {
i.setChecked(true); mTable.setSelection(index);
modified = true;
}
break;
}
index++;
} else if (mSelectionMode == SelectionMode.CHECK){
if ((AvdInfo) i.getData() == target) {
found = true;
if (!i.getChecked()) {
modified = true;
i.setChecked(true);
}
} else if (i.getChecked()) {
modified = true;
i.setChecked(false);
} }
} else if (i.getChecked()) {
modified = true;
i.setChecked(false);
} }
} }
@@ -228,19 +309,30 @@ public final class AvdSelector {
mSelectionListener.widgetSelected(null); mSelectionListener.widgetSelected(null);
} }
if (mExtraAction != null && mExtraActionButton != null) {
mExtraActionButton.setEnabled(mExtraAction.isEnabled());
}
return found; return found;
} }
/** /**
* Returns the first selected item. * Returns the currently selected item.
* This is useful when the table is in single-selection mode.
* *
* @return The first selected item or null. * @return The currently selected item or null.
*/ */
public AvdInfo getFirstSelected() { public AvdInfo getSelected() {
for (TableItem i : mTable.getItems()) { if (mSelectionMode == SelectionMode.SELECT) {
if (i.getChecked()) { int selIndex = mTable.getSelectionIndex();
return (AvdInfo) i.getData(); if (selIndex >= 0) {
return (AvdInfo) mTable.getItem(selIndex).getData();
}
} else if (mSelectionMode == SelectionMode.CHECK) {
for (TableItem i : mTable.getItems()) {
if (i.getChecked()) {
return (AvdInfo) i.getData();
}
} }
} }
return null; return null;
@@ -305,6 +397,10 @@ public final class AvdSelector {
if (mSelectionListener != null) { if (mSelectionListener != null) {
mSelectionListener.widgetSelected(e); mSelectionListener.widgetSelected(e);
} }
if (mExtraAction != null && mExtraActionButton != null) {
mExtraActionButton.setEnabled(mExtraAction.isEnabled());
}
} }
/** /**
@@ -318,7 +414,9 @@ public final class AvdSelector {
public void widgetDefaultSelected(SelectionEvent e) { public void widgetDefaultSelected(SelectionEvent e) {
if (e.item instanceof TableItem) { if (e.item instanceof TableItem) {
TableItem i = (TableItem) e.item; TableItem i = (TableItem) e.item;
i.setChecked(true); if (mSelectionMode == SelectionMode.CHECK) {
i.setChecked(true);
}
enforceSingleSelection(i); enforceSingleSelection(i);
updateDescription(i); updateDescription(i);
} }
@@ -326,6 +424,10 @@ public final class AvdSelector {
if (mSelectionListener != null) { if (mSelectionListener != null) {
mSelectionListener.widgetDefaultSelected(e); mSelectionListener.widgetDefaultSelected(e);
} }
if (mExtraAction != null && mExtraActionButton != null) {
mExtraActionButton.setEnabled(mExtraAction.isEnabled());
}
} }
/** /**
@@ -333,11 +435,16 @@ public final class AvdSelector {
* This makes the chekboxes act as radio buttons. * This makes the chekboxes act as radio buttons.
*/ */
private void enforceSingleSelection(TableItem item) { private void enforceSingleSelection(TableItem item) {
if (item.getChecked()) { if (mSelectionMode == SelectionMode.SELECT) {
Table parentTable = item.getParent(); // pass
for (TableItem i2 : parentTable.getItems()) {
if (i2 != item && i2.getChecked()) { } else if (mSelectionMode == SelectionMode.CHECK) {
i2.setChecked(false); if (item.getChecked()) {
Table parentTable = item.getParent();
for (TableItem i2 : parentTable.getItems()) {
if (i2 != item && i2.getChecked()) {
i2.setChecked(false);
}
} }
} }
} }