/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.debug.ui.blocks; import java.io.File; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.swt.SWT; 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.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 org.eclipse.swt.widgets.Widget; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.docutils.StringSubstitution; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.core.log.Log; import org.python.pydev.debug.core.Constants; import org.python.pydev.plugin.nature.PythonNature; import org.python.pydev.ui.dialogs.PythonModulePickerDialog; /** * A control for selecting a python module. */ public class MainModuleBlock extends AbstractLaunchConfigurationTab { private Text fMainModuleText; private Button fMainModuleBrowseButton; private String fProjectName; private ModifyListener fProjectModifyListener; /* (non-Javadoc) * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) */ public void createControl(Composite parent) { Font font = parent.getFont(); Group group = new Group(parent, SWT.NONE); setControl(group); GridLayout topLayout = new GridLayout(); topLayout.numColumns = 2; group.setLayout(topLayout); GridData gd = new GridData(GridData.FILL_HORIZONTAL); group.setLayoutData(gd); group.setFont(font); group.setText("Main Module"); fMainModuleText = new Text(group, SWT.SINGLE | SWT.BORDER); gd = new GridData(GridData.FILL_HORIZONTAL); fMainModuleText.setLayoutData(gd); fMainModuleText.setFont(font); fMainModuleText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent evt) { updateLaunchConfigurationDialog(); } }); final Composite lParent = parent; fMainModuleBrowseButton = createPushButton(group, "Browse...", null); fMainModuleBrowseButton.setText("Browse"); // On button click, this displays the python module picker dialog. fMainModuleBrowseButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IFile currentFile = getMainModuleFile(); IResource resource = workspace.getRoot().findMember(fProjectName); if (resource instanceof IProject) { IProject project = (IProject) resource; PythonModulePickerDialog dialog = new PythonModulePickerDialog(lParent.getShell(), "Main Module", "Choose Python module which starts execution", project); // Fixed request 1407469: main module browse button forgets path dialog.setInitialSelection(currentFile); int result = dialog.open(); if (result == PythonModulePickerDialog.OK) { Object results[] = dialog.getResult(); if ((results != null) && (results.length > 0) && (results[0] instanceof IFile)) { IFile file = (IFile) results[0]; IPath path = file.getFullPath(); String containerName = path.makeRelative().toString(); fMainModuleText.setText("${workspace_loc:" + containerName + "}"); } } } } }); // Create a ModifyListener, used to listen for project modifications in the ProjectBlock. // This assumes that the Project is in a Text control... fProjectModifyListener = new ModifyListener() { public void modifyText(ModifyEvent e) { Widget widget = e.widget; if (widget instanceof Text) { Text text = (Text) widget; fProjectName = text.getText(); IWorkspace workspace = ResourcesPlugin.getWorkspace(); IResource resource = workspace.getRoot().findMember(fProjectName); boolean enabled = false; if ((resource != null) && (resource instanceof IProject)) { IProject project = (IProject) resource; PythonNature nature = PythonNature.getPythonNature(project); enabled = (nature != null); } fMainModuleBrowseButton.setEnabled(enabled); } } }; } /* * (non-Javadoc) * * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() */ public String getName() { return "Main module"; } /* * (non-Javadoc) * * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) */ public void initializeFrom(ILaunchConfiguration configuration) { // Initialize the location field String location = ""; try { location = configuration.getAttribute(Constants.ATTR_LOCATION, ""); } catch (CoreException e) { } fMainModuleText.setText(location); // Obtain a copy of the project name (not displayed) String projectName = ""; try { projectName = configuration.getAttribute(Constants.ATTR_PROJECT, ""); } catch (CoreException e) { } fProjectName = projectName; } /* * (non-Javadoc) * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) */ public void performApply(ILaunchConfigurationWorkingCopy configuration) { String value = fMainModuleText.getText().trim(); setAttribute(configuration, Constants.ATTR_LOCATION, value); configuration.setMappedResources(new IResource[] { getMainModuleFile() }); } /* * (non-Javadoc) * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) */ public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { //no defaults to set } /** * Obtains an IFile that targets the current main module. * * This is used for initializing the module selection dialog. * * @return The main module file. */ private IFile getMainModuleFile() { String path = fMainModuleText.getText(); IFile file = null; if (path.length() > 0) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); StringSubstitution stringSubstitution = getStringSubstitution(root); try { path = stringSubstitution.performStringSubstitution(path, false); IFile[] files = root.findFilesForLocationURI(new File(path).toURI()); if (files.length > 0) { file = files[0]; } } catch (CoreException e) { Log.log(e); } } return file; } /** * @param root the workspace root. * @return an object capable on making string substitutions based on variables in the project and in the workspace. */ public StringSubstitution getStringSubstitution(IWorkspaceRoot root) { IResource resource = root.findMember(fProjectName); IPythonNature nature = null; if (resource instanceof IProject) { nature = PythonNature.getPythonNature(resource); } StringSubstitution stringSubstitution = new StringSubstitution(nature); return stringSubstitution; } /** * Sets attributes in the working copy * * @param configuration The configuration to set the attribute in * @param name Name of the attribute to set * @param value Value to set */ private void setAttribute(ILaunchConfigurationWorkingCopy configuration, String name, String value) { if (value == null || value.length() == 0) { configuration.setAttribute(name, (String) null); } else { configuration.setAttribute(name, value); } } /* * (non-Javadoc) * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public boolean isValid(ILaunchConfiguration launchConfig) { boolean result = super.isValid(launchConfig); if (result) { setMessage(null); setErrorMessage(null); IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); StringSubstitution stringSubstitution = getStringSubstitution(root); String location = fMainModuleText.getText(); try { String identifier = launchConfig.getType().getIdentifier(); if (identifier.equals(Constants.ID_PYTHON_UNITTEST_LAUNCH_CONFIGURATION_TYPE) || identifier.equals(Constants.ID_JYTHON_UNITTEST_LAUNCH_CONFIGURATION_TYPE) || identifier.equals(Constants.ID_IRONPYTHON_UNITTEST_LAUNCH_CONFIGURATION_TYPE) || identifier.equals(Constants.ID_PYTHON_COVERAGE_LAUNCH_CONFIGURATION_TYPE)) { //may have multiple files selected for the run for unitest and code-coverage for (String loc : StringUtils.splitAndRemoveEmptyTrimmed(location, '|')) { String expandedLocation = stringSubstitution.performStringSubstitution(loc); File file = new File(expandedLocation); if (!file.exists()) { setErrorMessage(com.aptana.shared_core.string.StringUtils.format("The file \"%s\" does not exist.", file)); result = false; break; } } } else { String expandedLocation = stringSubstitution.performStringSubstitution(location); File file = new File(expandedLocation); if (!file.exists()) { setErrorMessage(com.aptana.shared_core.string.StringUtils.format("The file \"%s\" does not exist.", file)); result = false; } else if (!file.isFile()) { setErrorMessage(com.aptana.shared_core.string.StringUtils.format("The file \"%s\" does not actually map to a file.", file)); result = false; } } } catch (CoreException e) { setErrorMessage("Unable to resolve location"); result = false; } } return result; } /** * Obtain a listener, used to detect changes of the currently selected project * This updates the browse button, and allos the appropriate selection of the main module. * * @return a ModifyListener that updates the block controls. */ public ModifyListener getProjectModifyListener() { return fProjectModifyListener; } }