4f9f1074df68e32c14bd695f9e4adc080d42bba8
[idea/community.git] / python / ide / src / com / jetbrains / python / newProject / actions / PyCharmNewProjectStep.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.newProject.actions;
17
18 import com.google.common.collect.Lists;
19 import com.intellij.ide.GeneralSettings;
20 import com.intellij.ide.util.projectWizard.WebProjectTemplate;
21 import com.intellij.internal.statistic.UsageTrigger;
22 import com.intellij.internal.statistic.beans.ConvertUsagesUtil;
23 import com.intellij.openapi.actionSystem.DefaultActionGroup;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.extensions.Extensions;
27 import com.intellij.openapi.module.Module;
28 import com.intellij.openapi.options.ConfigurationException;
29 import com.intellij.openapi.project.DumbAware;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.project.ProjectManager;
32 import com.intellij.openapi.projectRoots.ProjectJdkTable;
33 import com.intellij.openapi.projectRoots.Sdk;
34 import com.intellij.openapi.projectRoots.SdkAdditionalData;
35 import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
36 import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
37 import com.intellij.openapi.ui.Messages;
38 import com.intellij.openapi.util.Computable;
39 import com.intellij.openapi.vfs.LocalFileSystem;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.platform.DirectoryProjectGenerator;
42 import com.intellij.platform.PlatformProjectOpenProcessor;
43 import com.intellij.projectImport.ProjectOpenedCallback;
44 import com.intellij.util.NullableConsumer;
45 import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
46 import com.jetbrains.python.newProject.PyFrameworkProjectGenerator;
47 import com.jetbrains.python.newProject.PyNewProjectSettings;
48 import com.jetbrains.python.newProject.PythonBaseProjectGenerator;
49 import com.jetbrains.python.newProject.PythonProjectGenerator;
50 import com.jetbrains.python.sdk.PyDetectedSdk;
51 import com.jetbrains.python.sdk.PySdkService;
52 import com.jetbrains.python.sdk.PythonSdkAdditionalData;
53 import com.jetbrains.python.sdk.PythonSdkType;
54 import org.jetbrains.annotations.NotNull;
55 import org.jetbrains.annotations.Nullable;
56
57 import java.io.File;
58 import java.util.Arrays;
59 import java.util.Comparator;
60 import java.util.List;
61
62 public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAware {
63   private static final Logger LOG = Logger.getInstance(PyCharmNewProjectStep.class);
64
65   public PyCharmNewProjectStep(@NotNull final String name, @Nullable final Runnable runnable) {
66     super(name, true);
67
68     final NullableConsumer<AbstractProjectSettingsStep> callback = new NullableConsumer<AbstractProjectSettingsStep>() {
69       @Override
70       public void consume(@Nullable AbstractProjectSettingsStep settingsStep) {
71         if (runnable != null)
72           runnable.run();
73         if (settingsStep == null) return;
74
75         Sdk sdk = settingsStep.getSdk();
76         final Project project = ProjectManager.getInstance().getDefaultProject();
77         final ProjectSdksModel model = PyConfigurableInterpreterList.getInstance(project).getModel();
78         if (sdk instanceof PyDetectedSdk) {
79           final String name = sdk.getName();
80           VirtualFile sdkHome = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
81             @Override
82             public VirtualFile compute() {
83               return LocalFileSystem.getInstance().refreshAndFindFileByPath(name);
84             }
85           });
86           PySdkService.getInstance().solidifySdk(sdk);
87           sdk = SdkConfigurationUtil.setupSdk(ProjectJdkTable.getInstance().getAllJdks(), sdkHome, PythonSdkType.getInstance(), true, null,
88                                               null);
89           model.addSdk(sdk);
90           settingsStep.setSdk(sdk);
91           try {
92             model.apply();
93           }
94           catch (ConfigurationException exception) {
95             LOG.error("Error adding detected python interpreter " + exception.getMessage());
96           }
97         }
98         Project newProject = generateProject(project, settingsStep);
99         if (newProject != null) {
100           SdkConfigurationUtil.setDirectoryProjectSdk(newProject, sdk);
101           final List<Sdk> sdks = PythonSdkType.getAllSdks();
102           for (Sdk s : sdks) {
103             final SdkAdditionalData additionalData = s.getSdkAdditionalData();
104             if (additionalData instanceof PythonSdkAdditionalData) {
105               ((PythonSdkAdditionalData)additionalData).reassociateWithCreatedProject(newProject);
106             }
107           }
108         }
109       }
110
111       @Nullable
112       private Project generateProject(@NotNull final Project project, @NotNull final AbstractProjectSettingsStep settings) {
113         final DirectoryProjectGenerator generator = settings.getProjectGenerator();
114         final File location = new File(settings.getProjectLocation());
115         if (!location.exists() && !location.mkdirs()) {
116           Messages.showErrorDialog(project, "Cannot create directory '" + location + "'", "Create Project");
117           return null;
118         }
119
120         final VirtualFile baseDir = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
121           public VirtualFile compute() {
122             return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(location);
123           }
124         });
125         LOG.assertTrue(baseDir != null, "Couldn't find '" + location + "' in VFS");
126         baseDir.refresh(false, true);
127
128         if (baseDir.getChildren().length > 0) {
129           int rc = Messages.showYesNoDialog(project,
130                                             "The directory '" + location +
131                                             "' is not empty. Would you like to create a project from existing sources instead?",
132                                             "Create New Project", Messages.getQuestionIcon());
133           if (rc == Messages.YES) {
134             return PlatformProjectOpenProcessor.getInstance().doOpenProject(baseDir, null, false);
135           }
136         }
137
138         String generatorName = generator == null ? "empty" : ConvertUsagesUtil.ensureProperKey(generator.getName());
139         UsageTrigger.trigger("NewDirectoryProjectAction." + generatorName);
140
141         GeneralSettings.getInstance().setLastProjectCreationLocation(location.getParent());
142
143         return PlatformProjectOpenProcessor.doOpenProject(baseDir, null, false, -1, new ProjectOpenedCallback() {
144           @Override
145           public void projectOpened(Project project, Module module) {
146             if (generator != null) {
147               Object projectSettings = null;
148               if (generator instanceof PythonProjectGenerator)
149                 projectSettings = ((PythonProjectGenerator)generator).getProjectSettings();
150               else if (generator instanceof WebProjectTemplate) {
151                 projectSettings = ((WebProjectTemplate)generator).getPeer().getSettings();
152               }
153               if (projectSettings instanceof PyNewProjectSettings) {
154                 ((PyNewProjectSettings)projectSettings).setSdk(settings.getSdk());
155                 ((PyNewProjectSettings)projectSettings).setInstallFramework(settings.installFramework());
156               }
157               //noinspection unchecked
158               generator.generateProject(project, baseDir, projectSettings, module);
159             }
160           }
161         }, false);
162       }
163     };
164
165     final ProjectSpecificAction action = new ProjectSpecificAction(callback, new PythonBaseProjectGenerator());
166     add(action);
167
168     final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME);
169     if (generators.length == 0) {
170       action.setPopup(false);
171     }
172     Arrays.sort(generators, new Comparator<DirectoryProjectGenerator>() {
173       @Override
174       public int compare(DirectoryProjectGenerator o1, DirectoryProjectGenerator o2) {
175         if (o1 instanceof PyFrameworkProjectGenerator && !(o2 instanceof PyFrameworkProjectGenerator)) return -1;
176         if (!(o1 instanceof PyFrameworkProjectGenerator) && o2 instanceof PyFrameworkProjectGenerator) return 1;
177         return o1.getName().compareTo(o2.getName());
178       }
179     });
180
181     List<DirectoryProjectGenerator> pluginSpecificGenerators = Lists.newArrayList();
182     for (DirectoryProjectGenerator generator : generators) {
183       if (generator instanceof PythonProjectGenerator)
184         add(new ProjectSpecificAction(callback, generator));
185       else
186         pluginSpecificGenerators.add(generator);
187     }
188
189     if (!pluginSpecificGenerators.isEmpty()) {
190       add(new PluginSpecificProjectsStep(callback, pluginSpecificGenerators));
191     }
192   }
193
194   public PyCharmNewProjectStep() {
195     this("Select Project Type", null);
196
197   }
198
199 }