/** * Copyright (c) 2005-2013 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.ui.wizards.project; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard; import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.log.Log; import org.python.pydev.navigator.ui.PydevPackageExplorer; import org.python.pydev.navigator.ui.PydevPackageExplorer.PydevCommonViewer; import org.python.pydev.plugin.PyStructureConfigHelpers; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.shared_core.callbacks.ICallback; import org.python.pydev.ui.dialogs.PyDialogHelpers; import org.python.pydev.ui.wizards.gettingstarted.AbstractNewProjectWizard; /** * Python Project creation wizard * * <ul> * <li>Asks users information about Python project * <li>Launches another thread to create Python project. A progress monitor is shown in UI thread * </ul> * * TODO: Add a checkbox asking should a skeleton of a Python program generated * * @author Mikko Ohtamaa * @author Fabio Zadrozny */ public class PythonProjectWizard extends AbstractNewProjectWizard implements IExecutableExtension { /** * The current selection. */ protected IStructuredSelection selection; public static final String WIZARD_ID = "org.python.pydev.ui.wizards.project.PythonProjectWizard"; protected IWizardNewProjectNameAndLocationPage projectPage; protected IWizardNewProjectExistingSourcesPage sourcesPage; Shell shell; /** Target project created by this wizard */ IProject generatedProject; /** Exception throw by generator thread */ Exception creationThreadException; private IProject createdProject; private IConfigurationElement fConfigElement; protected IWorkbench workbench; @Override public void init(IWorkbench workbench, IStructuredSelection currentSelection) { this.selection = currentSelection; this.workbench = workbench; initializeDefaultPageImageDescriptor(); projectPage = createProjectPage(); sourcesPage = createSourcesPage(); PyDialogHelpers.enableAskInterpreterStep(false); } @Override public void dispose() { super.dispose(); PyDialogHelpers.enableAskInterpreterStep(true); } /** * Creates the project page. */ protected IWizardNewProjectNameAndLocationPage createProjectPage() { return new NewProjectNameAndLocationWizardPage("Setting project properties"); } /** * Creates the sources page. */ protected IWizardNewProjectExistingSourcesPage createSourcesPage() { return new NewProjectExistingSourcesWizardPage("Setting project sources"); } /** * Returns the sources page. */ protected IWizardNewProjectExistingSourcesPage getSourcesPage() { return sourcesPage; } /** * Returns the page that should appear after the sources page, or null if no page comes after it. */ protected WizardPage getPageAfterSourcesPage() { return referencePage; } /** * Add wizard pages to the instance * * @see org.eclipse.jface.wizard.IWizard#addPages() */ @Override public void addPages() { addPage(projectPage); addPage(sourcesPage); addProjectReferencePage(); } /** * Creates a new project resource with the entered name. * * @return the created project resource, or <code>null</code> if the project was not created */ protected IProject createNewProject(final Object... additionalArgsToConfigProject) { // get a project handle final IProject newProjectHandle = projectPage.getProjectHandle(); // get a project descriptor IPath defaultPath = Platform.getLocation(); IPath newPath = projectPage.getLocationPath(); if (defaultPath.equals(newPath)) { newPath = null; } else { //The user entered the path and it's the same as it'd be if he chose the default path. IPath withName = defaultPath.append(newProjectHandle.getName()); if (newPath.toFile().equals(withName.toFile())) { newPath = null; } } IWorkspace workspace = ResourcesPlugin.getWorkspace(); final IProjectDescription description = workspace.newProjectDescription(newProjectHandle.getName()); description.setLocation(newPath); // update the referenced project if provided if (referencePage != null) { IProject[] refProjects = referencePage.getReferencedProjects(); if (refProjects.length > 0) { description.setReferencedProjects(refProjects); } } final String projectType = projectPage.getProjectType(); String projectInterpreter = projectPage.getProjectInterpreter(); if (projectInterpreter == null || projectInterpreter.trim().isEmpty()) { projectInterpreter = IPythonNature.DEFAULT_INTERPRETER; } final String projectInterpreterFinal = projectInterpreter; // define the operation to create a new project WorkspaceModifyOperation op = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor monitor) throws CoreException { createAndConfigProject(newProjectHandle, description, projectType, projectInterpreterFinal, monitor, additionalArgsToConfigProject); } }; // run the operation to create a new project try { getContainer().run(true, true, op); } catch (InterruptedException e) { return null; } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof CoreException) { if (((CoreException) t).getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) { MessageDialog.openError(getShell(), "Unable to create project", "Another project with the same name (and different case) already exists."); } else { ErrorDialog .openError(getShell(), "Unable to create project", null, ((CoreException) t).getStatus()); } } else { // Unexpected runtime exceptions and errors may still occur. Log.log(IStatus.ERROR, t.toString(), t); MessageDialog.openError(getShell(), "Unable to create project", t.getMessage()); } return null; } return newProjectHandle; } protected ICallback<List<IContainer>, IProject> getSourceFolderHandlesCallback = new ICallback<List<IContainer>, IProject>() { @Override public List<IContainer> call(IProject projectHandle) { final int sourceFolderConfigurationStyle = projectPage.getSourceFolderConfigurationStyle(); List<IContainer> ret = new ArrayList<IContainer>(); switch (sourceFolderConfigurationStyle) { case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_CREATE_PROJECT_AS_SRC_FOLDER: //if the user hasn't selected to create a source folder, use the project itself for that. ret = new ArrayList<IContainer>(); ret.add(projectHandle); return ret; case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_EXISTING_SOURCES: return new ArrayList<IContainer>(); case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_NO_PYTHONPATH: return new ArrayList<IContainer>(); default: IContainer folder = projectHandle.getFolder("src"); ret = new ArrayList<IContainer>(); ret.add(folder); return ret; } } }; protected ICallback<List<IPath>, IProject> getExistingSourceFolderHandlesCallback = new ICallback<List<IPath>, IProject>() { @Override public List<IPath> call(IProject projectHandle) { if (projectPage.getSourceFolderConfigurationStyle() == IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_EXISTING_SOURCES) { List<IPath> eSources = sourcesPage.getExistingSourceFolders(); if (eSources.size() > 0) { return eSources; } } return null; } }; /** * This method can be overridden to provide a custom creation of the project. * * It should create the project, configure the folders in the pythonpath (source folders and external folders * if applicable), set the project type and project interpreter. */ protected void createAndConfigProject(final IProject newProjectHandle, final IProjectDescription description, final String projectType, final String projectInterpreter, IProgressMonitor monitor, Object... additionalArgsToConfigProject) throws CoreException { ICallback<List<IContainer>, IProject> getSourceFolderHandlesCallback = this.getSourceFolderHandlesCallback; ICallback<List<IPath>, IProject> getExistingSourceFolderHandlesCallback = this.getExistingSourceFolderHandlesCallback; PyStructureConfigHelpers.createPydevProject(description, newProjectHandle, monitor, projectType, projectInterpreter, getSourceFolderHandlesCallback, null, getExistingSourceFolderHandlesCallback); } /** * The user clicked Finish button * * Launches another thread to create Python project. A progress monitor is shown in the UI thread. */ @Override public boolean performFinish() { createdProject = createNewProject(); IWorkingSet[] workingSets = projectPage.getWorkingSets(); if (workingSets.length > 0) { PlatformUI.getWorkbench().getWorkingSetManager().addToWorkingSets(createdProject, workingSets); //Workaround to properly show project in Package Explorer: if Top Level Elements are //working sets, and the destination working set of the new project is selected, that set //must be reselected in order to display the project. PydevPackageExplorer pView = (PydevPackageExplorer) PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getActivePage() .findView("org.python.pydev.navigator.view"); if (pView != null) { IWorkingSet[] inputSets = ((PydevCommonViewer) pView.getCommonViewer()).getSelectedWorkingSets(); if (inputSets != null && inputSets.length == 1) { IWorkingSet inputSet = inputSets[0]; if (inputSet != null) { for (IWorkingSet destinationSet : workingSets) { if (inputSet.equals(destinationSet)) { pView.getCommonViewer().setInput(inputSet); break; } } } } } } // Switch to default perspective (will ask before changing) BasicNewProjectResourceWizard.updatePerspective(fConfigElement); BasicNewResourceWizard.selectAndReveal(createdProject, workbench.getActiveWorkbenchWindow()); return true; } public IProject getCreatedProject() { return createdProject; } /** * Set Python logo to top bar */ protected void initializeDefaultPageImageDescriptor() { ImageDescriptor desc = PydevPlugin .imageDescriptorFromPlugin(PydevPlugin.getPluginID(), "icons/python_logo.png");//$NON-NLS-1$ setDefaultPageImageDescriptor(desc); } @Override public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { this.fConfigElement = config; } }