debuggercdi/com.nokia.cdt.debug.launch/src/com/nokia/cdt/internal/debug/launch/newwizard/DebugRunProcessDialog.java
/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.cdt.internal.debug.launch.newwizard;
import java.io.File;
import java.text.MessageFormat;
import java.util.List;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.JFaceResources;
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.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.PreferencesUtil;
import com.nokia.carbide.cdt.builder.CarbideBuilderPlugin;
import com.nokia.carbide.cdt.builder.project.ICarbideBuildConfiguration;
import com.nokia.carbide.cdt.builder.project.ICarbideProjectInfo;
import com.nokia.carbide.cdt.builder.project.ISISBuilderInfo;
import com.nokia.cdt.internal.debug.launch.newwizard.LaunchWizardData.EExeSelection;
import com.nokia.cpp.internal.api.utils.core.PathUtils;
import com.nokia.cpp.internal.api.utils.ui.BrowseDialogUtils;
/**
* This dialog allows in-depth configuration of the debug/run process options.
*/
public class DebugRunProcessDialog extends AbstractLaunchSettingsDialog implements ICProjectDescriptionListener {
private ComboViewer projectExecutableViewer;
private Text remoteProgramEntry;
private Button projectExecutableRadioButton;
private Button remoteExecutableRadioButton;
private Button attachToProcessRadioButton;
private Label packageInfoLabel;
private Button installPackageCheckbox;
private Combo sisFile;
private Text sisEdit;
private Button sisBrowse;
private Composite installPackageUI;
protected DebugRunProcessDialog(Shell shell, LaunchWizardData data) {
super(shell, data);
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = initDialogArea(parent,
MessageFormat.format("Change {0} Process", data.getModeLabel()),
data.isDebug() ? LaunchWizardHelpIds.WIZARD_DIALOG_CHANGE_DEBUG_PROCESS :
LaunchWizardHelpIds.WIZARD_DIALOG_CHANGE_RUN_PROCESS);
createProcessSelector(composite);
Label sep = new Label(composite, SWT.NONE);
GridDataFactory.fillDefaults().applyTo(sep);
createPackageConfiguration(composite);
initUI();
validate();
return composite;
}
private void createProcessSelector(Composite composite) {
Label label;
label = new Label(composite, SWT.WRAP);
label.setText(MessageFormat.format("{0} method:", data.getModeLabel()));
label.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
GridDataFactory.fillDefaults().grab(true, false).applyTo(composite);
Composite radioGroup = new Composite(composite, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, false).applyTo(radioGroup);
GridLayoutFactory.fillDefaults().extendedMargins(INDENT, 0, 0, 0).numColumns(2).applyTo(radioGroup);
createProjectExecutableRadioButton(radioGroup);
createRemoteExecutableRadioButton(radioGroup);
label = new Label(radioGroup, SWT.SEPARATOR | SWT.HORIZONTAL);
GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(label);
createAttachToProcessRadioButton(radioGroup);
String msg;
if (data.isDebug())
msg = "Configure how to debug the program. The initial settings reflect the debug capabilities of the selected device and the SIS builder settings.";
else
msg = "Configure how to run the program.";
setMessage(msg);
switch (data.getExeSelection()) {
case USE_PROJECT_EXECUTABLE:
projectExecutableRadioButton.setSelection(true);
break;
case USE_REMOTE_EXECUTABLE:
remoteExecutableRadioButton.setSelection(true);
break;
case ATTACH_TO_PROCESS:
attachToProcessRadioButton.setSelection(true);
break;
}
}
private void createPackageConfiguration(Composite composite) {
Label label;
label = new Label(composite, SWT.WRAP);
label.setText(MessageFormat.format("Deploy method:", data.getModeLabel()));
label.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
packageInfoLabel = new Label(composite, SWT.WRAP);
GridDataFactory.fillDefaults().grab(true, false).applyTo(packageInfoLabel);
composite.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
packageInfoLabel.pack();
}
});
installPackageCheckbox = new Button(composite, SWT.CHECK);
GridDataFactory.fillDefaults().applyTo(installPackageCheckbox);
installPackageCheckbox.setText("Install package before launch");
installPackageUI = new Composite(composite, SWT.NONE);
GridDataFactory.fillDefaults().indent(INDENT, 0).applyTo(installPackageUI);
createSISContents(installPackageUI);
installPackageCheckbox.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
data.setInstallPackage(installPackageCheckbox.getSelection());
updatePackageUI();
}
});
if (data.isInstallPackage()) {
installPackageCheckbox.setSelection(true);
updatePackageUI();
}
updateSisFile();
updatePackageUI();
}
protected void createSISContents(Composite composite) {
GridLayoutFactory.fillDefaults().margins(6, 6).numColumns(2).applyTo(composite);
final IProject project = data.getProject();
ICarbideProjectInfo cpi = CarbideBuilderPlugin.getBuildManager().getProjectInfo(project);
if (cpi != null) {
final Label sisLabel = new Label(composite, SWT.NONE);
sisLabel.setText("SIS File to Install:");
GridDataFactory.fillDefaults().align(SWT.LEFT, SWT.CENTER).span(1, 1).applyTo(sisLabel);
sisLabel.setToolTipText("Specify which SIS file to install on the phone prior to launching");
sisLabel.setData(UID, "DebugRunProcessDialog.sisLabel");
sisFile = new Combo(composite, SWT.READ_ONLY);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(1, 1).grab(true, false).applyTo(sisLabel);
sisFile.setToolTipText("Specify which SIS file to install on the phone prior to launching");
sisFile.add("None"); //$NON-NLS-1$
sisFile.setData(UID, "DebugRunProcessDialog.sisFile");
sisFile.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateSisFile();
}
});
ICarbideBuildConfiguration config = cpi.getDefaultConfiguration();
for (ISISBuilderInfo info : config.getSISBuilderInfoList()) {
IPath sisPath = info.getSigningType() == ISISBuilderInfo.DONT_SIGN ? info.getUnsignedSISFullPath() : info.getSignedSISFullPath();
sisFile.add(sisPath.toOSString());
}
// select the first sis file if any, otherwise select none
if (sisFile.getItemCount() > 1) {
sisFile.select(1);
} else {
sisFile.select(0);
}
// listen for events so we can detect if they click on the link below and add new sis info.
CoreModel.getDefault().getProjectDescriptionManager().addCProjectDescriptionListener(this, CProjectDescriptionEvent.APPLIED);
composite.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
CoreModel.getDefault().getProjectDescriptionManager().removeCProjectDescriptionListener(DebugRunProcessDialog.this);
}
});
Link link = new Link(composite, SWT.NONE);
link.setText("<a>" + "Modify SIS builder settings for build configuration" + "...</a>");
GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1).grab(true, false).applyTo(link);
link.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
PreferencesUtil.createPropertyDialogOn(getShell(), project, "com.nokia.carbide.cdt.internal.builder.ui.CarbideBuildConfigurationsPage", null, null).open();
}
});
link.setData(UID, "DebugRunProcessDialog.link");
} else {
// not a Carbide project, just an executable. show a browse/edit combo
// to let them select a sis file if they want to.
final Label sisLabel = new Label(composite, SWT.NONE);
sisLabel.setText("SIS File to Install:"); //$NON-NLS-1$
GridDataFactory.swtDefaults().span(2, 1).applyTo(sisLabel);
sisLabel.setToolTipText("Specify which SIS file to install on the phone prior to launching");
sisLabel.setData(UID, "DebugRunProcessDialog.sisLabel");
sisEdit = new Text(composite, SWT.BORDER);
GridDataFactory.fillDefaults().span(1, 1).align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(sisEdit);
sisEdit.setToolTipText("Specify which SIS file to install on the phone prior to launching");
sisEdit.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
updateSisFile();
validate();
}
});
sisEdit.setData(UID, "DebugRunProcessDialog.sisEdit");
sisBrowse = new Button(composite, SWT.NONE);
sisBrowse.setText("Browse...");
sisBrowse.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
sisBrowse.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent evt) {
FileDialog dialog = new FileDialog(getShell(), SWT.NONE);
dialog.setText("Select installation file");
dialog.setFilterExtensions(new String[] {"*.sis*", "*.*"});
dialog.setFilterNames(new String[] {"Installation Files", "All Files"});
BrowseDialogUtils.initializeFrom(dialog, sisEdit);
String result = dialog.open();
if (result != null && new File(result).exists()) {
sisEdit.setText(result);
updateSisFile();
validate();
}
}
});
sisBrowse.setData(UID, "DebugRunProcessDialog.sisBrowse");
}
}
/**
*
*/
protected void updateSisFile() {
String sisPath;
if (sisFile != null) {
sisPath = sisFile.getSelectionIndex() == 0 ? null : sisFile.getText(); //$NON-NLS-1$
data.setSisPath(sisPath);
} else if (sisEdit != null) {
sisPath = sisEdit.getText();
data.setSisPath(sisPath);
}
}
private void updatePackageUI() {
installPackageUI.setEnabled(data.isInstallPackage());
for (Control kid : installPackageUI.getChildren())
kid.setEnabled(data.isInstallPackage());
}
public void handleEvent(CProjectDescriptionEvent event) {
Shell shell = getShell();
if (shell == null || shell.isDisposed()) {
return;
}
IProject project = event.getProject() ;
if (project != data.getProject()) {
return;
}
if (sisFile != null) {
ICarbideProjectInfo cpi = CarbideBuilderPlugin.getBuildManager().getProjectInfo(project);
if (cpi != null) {
sisFile.removeAll();
sisFile.add("None");
ICarbideBuildConfiguration config = cpi.getDefaultConfiguration();
for (ISISBuilderInfo info : config.getSISBuilderInfoList()) {
IPath sisPath = info.getSigningType() == ISISBuilderInfo.DONT_SIGN ? info.getUnsignedSISFullPath() : info.getSignedSISFullPath();
sisFile.add(sisPath.toOSString());
}
// select the first sis file if any, otherwise select none
if (sisFile.getItemCount() > 1) {
sisFile.select(1);
} else {
sisFile.select(0);
}
}
}
}
protected void initUI() {
List<IPath> exes = data.getExes();
projectExecutableViewer.setInput(exes);
IPath exeSelectionPath = data.getExeSelectionPath();
if (exeSelectionPath.equals(Path.EMPTY) && !exes.isEmpty())
exeSelectionPath = exes.get(0);
projectExecutableViewer.setSelection(new StructuredSelection(exeSelectionPath));
IPath remotePath = createSuggestedRemotePath(exeSelectionPath);
remoteProgramEntry.setText(PathUtils.convertPathToWindows(remotePath));
if (data.getExeSelection() == EExeSelection.USE_PROJECT_EXECUTABLE && exeSelectionPath != null) {
projectExecutableRadioButton.forceFocus();
}
if (data.getExeSelection() == EExeSelection.USE_REMOTE_EXECUTABLE && exeSelectionPath != null) {
remoteExecutableRadioButton.forceFocus();
}
if (data.getExeSelection() == EExeSelection.ATTACH_TO_PROCESS) {
attachToProcessRadioButton.forceFocus();
}
handleProjectExecutableRadioSelected();
handleRemoteExecutableRadioSelected();
handleAttachToProcessRadioSelected();
}
private IPath createSuggestedRemotePath(IPath exeSelectionPath) {
String filename = exeSelectionPath.lastSegment();
return PathUtils.createPath("C:/sys/bin").append(filename);
}
/**
* Allow selecting an executable detected to be built by the program.
* @param radioGroup
*/
private void createProjectExecutableRadioButton(Composite radioGroup) {
projectExecutableRadioButton = new Button(radioGroup, SWT.RADIO);
GridDataFactory.fillDefaults().grab(false, false).applyTo(projectExecutableRadioButton);
projectExecutableRadioButton.setText("Launch project &executable:");
projectExecutableRadioButton.setData(UID, "radio_project_executable");
projectExecutableViewer = new ComboViewer(radioGroup, SWT.READ_ONLY);
GridDataFactory.fillDefaults().grab(true, false).applyTo(projectExecutableViewer.getControl());
projectExecutableViewer.getControl().setData(UID, "combo_project_executable");
projectExecutableViewer.setContentProvider(new ArrayContentProvider());
projectExecutableViewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof IPath)
return ((IPath) element).lastSegment();
return super.getText(element);
}
});
projectExecutableRadioButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
handleProjectExecutableRadioSelected();
}
});
projectExecutableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
Object sel = ((IStructuredSelection) event.getSelection()).getFirstElement();
if (sel instanceof IPath) {
data.setExeSelectionPath((IPath) sel);
// track the default remote program from the executable, for easy editing
if (remoteProgramEntry != null) {
IPath exeSelectionPath = createSuggestedRemotePath(data.getExeSelectionPath());
remoteProgramEntry.setText(PathUtils.convertPathToWindows(exeSelectionPath));
}
validate();
}
}
});
}
private void handleProjectExecutableRadioSelected() {
if (projectExecutableRadioButton.getSelection()) {
projectExecutableViewer.getControl().setEnabled(true);
data.setExeSelection(EExeSelection.USE_PROJECT_EXECUTABLE);
IPath selectedPath = (IPath) ((IStructuredSelection) projectExecutableViewer.getSelection()).getFirstElement();
if (selectedPath != null) {
String symbianPath = PathUtils.convertPathToWindows(selectedPath);
data.setExeSelectionPath(new Path(symbianPath));
}
validate();
} else {
projectExecutableViewer.getControl().setEnabled(false);
// another button becomes active and sets the new launch process
}
}
/**
* Allow user to enter an executable path.
* @param radioGroup
*/
private void createRemoteExecutableRadioButton(Composite radioGroup) {
remoteExecutableRadioButton = new Button(radioGroup, SWT.RADIO);
GridDataFactory.fillDefaults().grab(false, false).applyTo(remoteExecutableRadioButton);
remoteExecutableRadioButton.setText("Launch &remote program:");
remoteExecutableRadioButton.setData(UID, "radio_remote_program");
remoteProgramEntry = new Text(radioGroup, SWT.BORDER);
GridDataFactory.fillDefaults().grab(true, false).applyTo(remoteProgramEntry);
remoteProgramEntry.setData(UID, "text_remote_program");
remoteExecutableRadioButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
handleRemoteExecutableRadioSelected();
}
});
remoteProgramEntry.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
data.setExeSelectionPath(new Path(remoteProgramEntry.getText().trim()));
validate();
}
});
}
private void handleRemoteExecutableRadioSelected() {
if (remoteExecutableRadioButton.getSelection()) {
remoteProgramEntry.setEnabled(true);
data.setExeSelection(EExeSelection.USE_REMOTE_EXECUTABLE);
String symbianPath = PathUtils.convertPathToWindows(remoteProgramEntry.getText());
data.setExeSelectionPath(new Path(symbianPath));
validate();
} else {
remoteProgramEntry.setEnabled(false);
// another button becomes active and sets the new launch process
}
}
/**
* Allow user to attach to a process.
* @param radioGroup
*/
private void createAttachToProcessRadioButton(Composite radioGroup) {
attachToProcessRadioButton = new Button(radioGroup, SWT.RADIO);
GridDataFactory.fillDefaults().grab(false, false).applyTo(attachToProcessRadioButton);
attachToProcessRadioButton.setText("&Attach to process:");
attachToProcessRadioButton.setData(UID, "radio_attach_to_process");
Label label = new Label(radioGroup, SWT.WRAP);
GridDataFactory.fillDefaults().grab(false, false).align(SWT.LEFT, SWT.CENTER).applyTo(label);
label.setText("(selected at launch time)");
attachToProcessRadioButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
handleAttachToProcessRadioSelected();
}
});
}
private void handleAttachToProcessRadioSelected() {
if (attachToProcessRadioButton.getSelection()) {
data.setExeSelection(EExeSelection.ATTACH_TO_PROCESS);
data.setExeSelectionPath(null);
validate();
} else {
// another button becomes active and sets the new launch process
}
}
@Override
protected void validate() {
IStatus status = Status.OK_STATUS;
// check launch method
IPath exePath = data.getExeSelectionPath();
switch (data.getExeSelection()) {
case USE_PROJECT_EXECUTABLE:
if (exePath.isEmpty()) {
status = error("The project builds no executables.");
}
break;
case USE_REMOTE_EXECUTABLE:
if (exePath.isEmpty()) {
status = error("Enter a non-empty executable path.");
} else {
String exePathString = exePath.toString();
char drive = exePathString.charAt(0);
char colon = exePathString.length() < 2 ? 0x0 : exePathString.charAt(1);
char root = exePathString.length() < 3 ? 0x0 : exePathString.charAt(2);
char lastChar = exePathString.charAt(exePathString.length() - 1);
if (!Character.isLetter(drive) || colon != ':' || (root != '\\' && root != '/') ||
lastChar == '\\' || lastChar == '/' || lastChar == ':') {
status = error("The executable path must be absolute.");
} else if (exePath.getFileExtension() == null) {
status = warning("The executable path should end in a filename.");
}
}
break;
case ATTACH_TO_PROCESS:
break;
}
// check SIS selection
if (data.isInstallPackage()) {
if (sisEdit != null) {
String text = sisEdit.getText().trim();
if (text.length() > 0) {
// empty is allowed, but if they specify something, make sure
// it exists
File file = new File(text);
if (!file.exists()) {
status = error("The SIS file ''{0}'' does not exist.", text);
}
}
}
}
updateStatus(status);
}
}