/* Copyright (C) 2009 Mobile Sorcery AB
This program is free software; you can redistribute it and/or modify it
under the terms of the Eclipse Public License v1.0.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for
more details.
You should have received a copy of the Eclipse Public License v1.0 along
with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html
*/
package com.mobilesorcery.sdk.ui.internal.launch;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Text;
import com.mobilesorcery.sdk.core.CoreMoSyncPlugin;
import com.mobilesorcery.sdk.core.IBuildConfiguration;
import com.mobilesorcery.sdk.core.ILaunchConstants;
import com.mobilesorcery.sdk.core.MoSyncNature;
import com.mobilesorcery.sdk.core.MoSyncProject;
import com.mobilesorcery.sdk.core.launch.AutomaticEmulatorLauncher;
import com.mobilesorcery.sdk.core.launch.IEmulatorLauncher;
import com.mobilesorcery.sdk.core.launch.MoReLauncher;
import com.mobilesorcery.sdk.internal.launch.EmulatorLaunchConfigurationDelegate;
import com.mobilesorcery.sdk.ui.BuildConfigurationsContentProvider;
import com.mobilesorcery.sdk.ui.BuildConfigurationsLabelProvider;
import com.mobilesorcery.sdk.ui.DefaultMessageProvider;
import com.mobilesorcery.sdk.ui.MoSyncProjectSelectionDialog;
import com.mobilesorcery.sdk.ui.MosyncUIPlugin;
import com.mobilesorcery.sdk.ui.UpdateListener.IUpdatableControl;
import com.mobilesorcery.sdk.ui.launch.IEmulatorLaunchConfigurationPart;
public class MoSyncLaunchParamsTab extends AbstractLaunchConfigurationTab implements IUpdatableControl {
private class TabListener implements ModifyListener, SelectionListener {
@Override
public void modifyText(ModifyEvent e) {
updateLaunchConfigurationDialog();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {/* do nothing */
}
@Override
public void widgetSelected(SelectionEvent e) {
Object source = e.getSource();
if (source == projectButton) {
handleProjectButtonSelected();
} else if (source == projectText) {
updateLaunchConfigurationDialog();
} else {
updateLaunchConfigurationDialog();
}
}
}
private class UpdateConfigurationListener implements ModifyListener {
@Override
public void modifyText(ModifyEvent e) {
updateConfigurations();
if (e.getSource() == projectText) {
updateLaunchDelegateList();
}
}
}
private Text projectText;
private Button projectButton;
private final TabListener listener = new TabListener();
private IProject project;
private String mode;
private Button changeConfiguration;
private ComboViewer configurations;
private Group configurationGroup;
private String[] debugBuildConfigurationTypes = new String[] { IBuildConfiguration.DEBUG_TYPE };
private String[] buildConfigurationTypes = new String[] { IBuildConfiguration.RELEASE_TYPE };
private ComboViewer launchDelegateList;
private Composite launchDelegateHolder;
private StackLayout launchDelegateHolderLayout;
private final HashMap<String, Composite> delegateComposites = new HashMap<String, Composite>();
private ILaunchConfiguration config;
private final HashMap<String, IEmulatorLaunchConfigurationPart> launcherParts = new HashMap<String, IEmulatorLaunchConfigurationPart>();
private final HashSet<IEmulatorLaunchConfigurationPart> initedParts = new HashSet<IEmulatorLaunchConfigurationPart>();
private Composite launchDelegateHolderParent;
private boolean allowsExternalEmulators;
private String currentLaunchDelegateId;
@Override
public void createControl(Composite parent) {
Composite control = new Composite(parent, SWT.NONE);
control.setLayout(new GridLayout(1, false));
createProjectEditor(control);
if (allowsExternalEmulators) {
createLaunchDelegateEditor(control);
}
createConfigurationEditor(control);
setControl(control);
}
private void createLaunchDelegateEditor(Composite control) {
Set<String> ids = sortLaunchers(CoreMoSyncPlugin.getDefault().getEmulatorLauncherIds());
launchDelegateHolderParent = control;
Group launchDelegateGroup = new Group(control, SWT.NONE);
launchDelegateGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
launchDelegateGroup.setText("&Emulator");
launchDelegateGroup.setLayout(new GridLayout(1, false));
launchDelegateList = new ComboViewer(launchDelegateGroup);
launchDelegateList.setContentProvider(new ArrayContentProvider());
launchDelegateList.setInput(filterLaunchDelegateIds(ids).toArray());
launchDelegateList.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
return CoreMoSyncPlugin.getDefault()
.getEmulatorLauncher((String) element).getName();
}
});
launchDelegateList
.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) event
.getSelection();
String id = (String) selection.getFirstElement();
switchDelegate(id, false);
}
});
launchDelegateList.getCombo().addModifyListener(listener);
launchDelegateHolderParent = launchDelegateGroup;
launchDelegateHolder = new Composite(launchDelegateHolderParent, SWT.NONE);
launchDelegateHolder.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
launchDelegateHolderLayout = new StackLayout();
launchDelegateHolder.setLayout(launchDelegateHolderLayout);
switchDelegate(MoReLauncher.ID, true);
}
private Set<String> sortLaunchers(Set<String> emulatorLauncherIds) {
TreeSet<String> sortedLaunchers = new TreeSet<String>(new Comparator<String>() {
@Override
public int compare(String id1, String id2) {
if (id1.equals(id2)) {
return 0;
}
int score1 = getLauncherScore(id1);
int score2 = getLauncherScore(id2);
if (score1 != score2) {
return score2 - score1;
}
return id1.compareTo(id2);
}
private int getLauncherScore(String id) {
if (AutomaticEmulatorLauncher.ID.equals(id)) {
return 2;
} else if (MoReLauncher.ID.equals(id)) {
return 1;
}
return 0;
}
});
sortedLaunchers.addAll(emulatorLauncherIds);
return sortedLaunchers;
}
protected void switchDelegate(String id, boolean updateCombo) {
Composite delegateComposite = delegateComposites.get(id);
if (delegateComposite == null) {
delegateComposite = createDelegateComposite(id,
launchDelegateHolder);
delegateComposites.put(id, delegateComposite);
}
initLauncherPart(id);
if (updateCombo) {
this.launchDelegateList.setSelection(new StructuredSelection(id));
}
launchDelegateHolderLayout.topControl = delegateComposite;
currentLaunchDelegateId = id;
launchDelegateHolder.layout();
validateCurrentLaunchDelegate();
updateLaunchConfigurationDialog();
}
private void initLauncherPart(String id) {
IEmulatorLaunchConfigurationPart launcherPart = launcherParts.get(id);
if (config != null && launcherPart != null && !initedParts.contains(launcherPart)) {
try {
launcherPart.init(config);
} catch (CoreException e) {
CoreMoSyncPlugin.getDefault().log(e);
} finally {
if (launcherPart != null) {
initedParts.add(launcherPart);
}
}
}
}
private Composite createDelegateComposite(String id, Composite parent) {
IEmulatorLaunchConfigurationPart launcherPart = MosyncUIPlugin
.getDefault().getEmulatorLauncherPart(id);
if (launcherPart == null) {
return new Composite(parent, SWT.NONE);
}
launcherParts.put(id, launcherPart);
Composite control = launcherPart.createControl(parent, this);
control.setLayoutData(new GridData(GridData.FILL_BOTH));
return control;
}
private IProject selectProject() {
MoSyncProjectSelectionDialog dialog = new MoSyncProjectSelectionDialog(
getShell());
dialog.setInitialProject(getProject());
return dialog.selectProject();
}
private void createConfigurationEditor(Composite control) {
configurationGroup = new Group(control, SWT.NONE);
configurationGroup
.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
configurationGroup.setText("&Configuration");
configurationGroup.setLayout(new GridLayout(1, false));
changeConfiguration = new Button(configurationGroup, SWT.CHECK);
changeConfiguration
.setText(isDebugMode() ? "&Automatically switch to this configuration before debugging"
: "&Automatically switch to this configuration before launching");
changeConfiguration.addSelectionListener(listener);
configurations = new ComboViewer(configurationGroup, SWT.READ_ONLY
| SWT.BORDER);
configurations.getCombo().addSelectionListener(listener);
}
private void updateConfigurations() {
MoSyncProject project = selectedProject();
if (project != null) {
configurations
.setContentProvider(new BuildConfigurationsContentProvider(
project,
isDebugMode() ? debugBuildConfigurationTypes
: buildConfigurationTypes));
configurations
.setLabelProvider(new BuildConfigurationsLabelProvider(
project));
configurations.setInput(project);
}
}
private void updateLaunchDelegateList() {
if (launchDelegateList != null) {
Set<String> ids = filterLaunchDelegateIds(CoreMoSyncPlugin.getDefault().getEmulatorLauncherIds());
launchDelegateList.setInput(ids.toArray());
updateLaunchDelegateListSelection(this.config);
}
}
/**
* Updates the selection in the launchDelegate list according to the
* current launch configuration.
*
* @param config The current launch configuration, if config is null the
* default config is used.
*/
private void updateLaunchDelegateListSelection(ILaunchConfiguration config) {
if (launchDelegateList == null) {
return;
}
if(config == null) {
launchDelegateList.setSelection(new StructuredSelection(MoReLauncher.ID));
return;
}
try {
String launchConfigId = config.getAttribute(ILaunchConstants.LAUNCH_DELEGATE_ID, MoReLauncher.ID);
launchDelegateList.setSelection(new StructuredSelection(launchConfigId));
}
catch(CoreException ce) {
// If we cannot find the id, use the default.
launchDelegateList.setSelection(new StructuredSelection(MoReLauncher.ID));
}
}
private Set<String> filterLaunchDelegateIds(Set<String> emulatorLauncherIds) {
Set<String> result = new HashSet<String>();
for (String id : emulatorLauncherIds) {
IEmulatorLauncher launcher = CoreMoSyncPlugin.getDefault().getEmulatorLauncher(id);
if (launcher != null && config != null) {
/*int availability = launcher.isAvailable(config, mode);
if (availability == IEmulatorLauncher.AVAILABLE || availability == IEmulatorLauncher.REQUIRES_CONFIGURATION) {
result.add(id);
}*/
if (launcher.supportsLaunchMode(mode) || MoReLauncher.ID.equals(id)) {
result.add(id);
}
}
}
return sortLaunchers(result);
}
public void setBuildConfigurationTypes(boolean isDebug, String... types) {
if (isDebug) {
debugBuildConfigurationTypes = types;
} else {
buildConfigurationTypes = types;
}
}
protected void handleProjectButtonSelected() {
IProject project = selectProject();
if (project == null) {
return;
}
String projectName = project.getName();
projectText.setText(projectName);
}
private IWorkspaceRoot getWorkspaceRoot() {
return ResourcesPlugin.getWorkspace().getRoot();
}
protected void createProjectEditor(Composite parent) {
Font font = parent.getFont();
Group group = new Group(parent, SWT.NONE);
group.setText("Project");
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
group.setLayoutData(gd);
GridLayout layout = new GridLayout();
layout.numColumns = 2;
group.setLayout(layout);
group.setFont(font);
projectText = new Text(group, SWT.SINGLE | SWT.BORDER);
gd = new GridData(GridData.FILL_HORIZONTAL);
projectText.setLayoutData(gd);
projectText.setFont(font);
projectText.addModifyListener(listener);
projectText.addModifyListener(new UpdateConfigurationListener());
projectButton = createPushButton(group, "Browse...", null);
projectButton.addSelectionListener(listener);
}
@Override
public String getName() {
return "Main";
}
@Override
public void initializeFrom(ILaunchConfiguration config) {
try {
this.config = config;
projectText.setText(config.getAttribute(ILaunchConstants.PROJECT,
""));
initializeBuildConfigurationOptions(config);
updateLaunchConfigurationDialog();
updateLaunchDelegateListSelection(config);
} catch (Exception e) {
e.printStackTrace();
// Ignore.
}
}
public void initializeBuildConfigurationOptions(ILaunchConfiguration config)
throws CoreException {
String defaultBuildConfig = EmulatorLaunchConfigurationDelegate
.getDefaultBuildConfiguration(
MoSyncProject.create(getProject()), mode);
changeConfiguration.setSelection(config.getAttribute(
getAutoChangeConfigKey(), false));
String buildConfiguration = config.getAttribute(getBuildConfigKey(),
defaultBuildConfig);
configurations
.setSelection(new StructuredSelection(buildConfiguration));
}
private boolean isDebugMode() {
return "debug".equals(mode);
}
private String getAutoChangeConfigKey() {
return isDebugMode() ? ILaunchConstants.AUTO_CHANGE_CONFIG_DEBUG
: ILaunchConstants.AUTO_CHANGE_CONFIG;
}
private String getBuildConfigKey() {
return isDebugMode() ? ILaunchConstants.BUILD_CONFIG_DEBUG
: ILaunchConstants.BUILD_CONFIG;
}
private String getCurrentLauncherDelegateId() {
String delegateId = MoReLauncher.ID;
if (launchDelegateList != null) {
IStructuredSelection selection = (IStructuredSelection) launchDelegateList.getSelection();
delegateId = (String) selection.getFirstElement();
}
return delegateId;
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy copy) {
for (IEmulatorLaunchConfigurationPart launcherPart : launcherParts
.values()) {
if (initedParts.contains(launcherPart)) {
launcherPart.apply(copy);
}
}
copy.setAttribute(ILaunchConstants.PROJECT, projectText.getText()
.trim());
copy.setAttribute(getAutoChangeConfigKey(),
changeConfiguration.getSelection());
copy.setAttribute(getBuildConfigKey(), getSelectedBuildConfiguration());
copy.setAttribute(ILaunchConstants.LAUNCH_DELEGATE_ID, getCurrentLauncherDelegateId());
}
public String getSelectedBuildConfiguration() {
String result = (String) ((IStructuredSelection) configurations
.getSelection()).getFirstElement();
return result == null ? "" : result;
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy wc) {
EmulatorLaunchConfigurationDelegate
.configureLaunchConfigForSourceLookup(wc);
}
private IProject getProject() {
return project;
}
public void setMode(String mode) {
this.mode = mode;
}
@Override
public void updateLaunchConfigurationDialog() {
if (changeConfiguration == null) {
return;
}
MoSyncProject project = selectedProject();
boolean configurationsVisible = project != null
&& project.areBuildConfigurationsSupported();
boolean comboEnabled = changeConfiguration.getSelection()
&& configurationsVisible;
configurations.getControl().setEnabled(comboEnabled);
configurationGroup.setVisible(configurationsVisible);
if (launchDelegateHolderParent != null) {
launchDelegateHolderParent.setVisible(project != null);
}
super.updateLaunchConfigurationDialog();
}
private MoSyncProject selectedProject() {
MoSyncProject newProject = null;
try {
project = getWorkspaceRoot().getProject(
projectText.getText().trim());
if (project != null && project.exists() && project.isOpen()
&& project.hasNature(MoSyncNature.ID)) {
newProject = MoSyncProject.create(project);
}
} catch (Exception e) {
// Ignore.
}
return newProject;
}
@Override
public void updateUI() {
updateConfigurations();
validateCurrentLaunchDelegate();
updateLaunchConfigurationDialog();
}
private void validateCurrentLaunchDelegate() {
IEmulatorLaunchConfigurationPart launcherPart = launcherParts.get(currentLaunchDelegateId);
if (launcherPart != null) {
IMessageProvider validationResult = launcherPart.validate();
if (DefaultMessageProvider.isEmpty(validationResult)) {
setErrorMessage(null);
} else {
setErrorMessage(validationResult.getMessage());
}
}
}
public void setAllowExternalEmulators(boolean allowsExternalEmulators) {
this.allowsExternalEmulators = allowsExternalEmulators;
}
}