diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form index 000469cc2a44..1eaf1286e09e 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form @@ -45,29 +45,7 @@ - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java index 67c6cff9c257..a44a5aee83f7 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java @@ -39,9 +39,18 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.platform.ProjectTemplate; -import com.intellij.ui.components.JBLabel; +import com.intellij.platform.ProjectTemplateEP; +import com.intellij.platform.templates.LocalArchivedTemplate; +import com.intellij.ui.CollectionListModel; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBList; +import com.intellij.ui.tabs.TabInfo; +import com.intellij.ui.tabs.TabsListener; +import com.intellij.ui.tabs.impl.JBTabsImpl; +import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FactoryMap; import com.intellij.util.containers.MultiMap; @@ -53,6 +62,7 @@ import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; +import java.net.URL; import java.util.*; import java.util.List; @@ -60,7 +70,7 @@ import java.util.List; * @author Dmitry Avdeev * Date: 04.09.13 */ -public class ProjectTypeStep extends ModuleWizardStep { +public class ProjectTypeStep extends ModuleWizardStep implements Disposable { private static final String FRAMEWORKS_CARD = "frameworks card"; private static final String GROUP_CARD = "group description card"; @@ -69,9 +79,12 @@ public class ProjectTypeStep extends ModuleWizardStep { private final ModulesProvider myModulesProvider; private JPanel myPanel; private JPanel myOptionsPanel; - private JBLabel myGroupDescriptionLabel; private JBList myProjectTypeList; - private final ProjectTypesList myList; + + private final ProjectTypesList myProjectTypesList; + private final JBList myTemplatesList; + private final TabInfo myFrameworksTab; + private final TabInfo myTemplatesTab; @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private final FactoryMap myBuilders = new FactoryMap() { @@ -86,6 +99,7 @@ public class ProjectTypeStep extends ModuleWizardStep { private final AddSupportForFrameworksPanel myFrameworksPanel; private final ModuleBuilder.ModuleConfigurationUpdater myConfigurationUpdater; private boolean myCommitted; + private final JBTabsImpl myTabs; public ProjectTypeStep(WizardContext context, NewProjectWizard wizard, ModulesProvider modulesProvider) { myContext = context; @@ -129,19 +143,12 @@ public class ProjectTypeStep extends ModuleWizardStep { } } - myList = new ProjectTypesList(myProjectTypeList, map); + myProjectTypesList = new ProjectTypesList(myProjectTypeList, map); myProjectTypeList.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override public void valueChanged(ListSelectionEvent e) { - ModuleBuilder builder = getSelectedBuilder(); - if (builder != null) { - myContext.setProjectBuilder(builder); - myWizard.getSequence().setType(builder.getBuilderId()); - builder.addModuleConfigurationUpdater(myConfigurationUpdater); - } - updateOptionsPanel(getSelectedObject()); + projectTypeChanged(true); } }); @@ -152,17 +159,59 @@ public class ProjectTypeStep extends ModuleWizardStep { myFrameworksPanel = new AddSupportForFrameworksPanel(Collections.emptyList(), model, true); Disposer.register(wizard.getDisposable(), myFrameworksPanel); - myOptionsPanel.add(myFrameworksPanel.getMainPanel(), FRAMEWORKS_CARD); + myTemplatesList = new JBList(); + myTemplatesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + myTemplatesList.setCellRenderer(new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(JList list, ProjectCategory value, int index, boolean selected, boolean hasFocus) { + append(value.getDisplayName()).setIcon(value.getIcon()); + } + }); + myTemplatesList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + projectTypeChanged(false); + } + }); + + myTabs = new JBTabsImpl(null, IdeFocusManager.findInstance(), this); + myTabs.addListener(new TabsListener.Adapter() { + @Override + public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) { + projectTypeChanged(false); + } + }); + myFrameworksTab = new TabInfo(myFrameworksPanel.getMainPanel()).setText(" Frameworks "); + myTabs.addTab(myFrameworksTab); + myTemplatesTab = new TabInfo(ScrollPaneFactory.createScrollPane(myTemplatesList)).setText(" Templates "); + myTabs.addTab(myTemplatesTab); + myOptionsPanel.add(myTabs.getComponent(), FRAMEWORKS_CARD); + } + + public void projectTypeChanged(boolean updatePanel) { + ModuleBuilder builder = getSelectedBuilder(); + if (builder != null) { + myContext.setProjectBuilder(builder); + myWizard.getSequence().setType(builder.getBuilderId()); + if (myFrameworksTab == myTabs.getSelectedInfo()) { + builder.addModuleConfigurationUpdater(myConfigurationUpdater); + } + } + if (updatePanel) { + updateOptionsPanel(getSelectedProjectType()); + } } @Nullable - public ProjectCategory getSelectedObject() { - return myList.getSelectedTemplate(); + public ProjectCategory getSelectedProjectType() { + return myTabs.getSelectedInfo() == myFrameworksTab ? + myProjectTypesList.getSelectedTemplate() : + (ProjectCategory)myTemplatesList.getSelectedValue(); } @Nullable private ModuleBuilder getSelectedBuilder() { - ProjectCategory object = getSelectedObject(); + ProjectCategory object = getSelectedProjectType(); return object == null ? null : myBuilders.get(object); } @@ -197,11 +246,17 @@ public class ProjectTypeStep extends ModuleWizardStep { myFrameworksPanel.setProviders(matched, new HashSet(Arrays.asList(projectCategory.getAssociatedFrameworkIds())), new HashSet(Arrays.asList(projectCategory.getPreselectedFrameworkIds()))); + List templates = getTemplates(projectCategory.getId()); + myFrameworksTab.setHidden(matched.isEmpty() && !templates.isEmpty()); + + //noinspection unchecked + myTemplatesList.setModel(new CollectionListModel(templates)); + myTemplatesTab.setHidden(templates.isEmpty()); + if (!templates.isEmpty()) { + myTemplatesList.setSelectedIndex(0); + } } } - else if (object instanceof TemplatesGroup) { - myGroupDescriptionLabel.setText(((TemplatesGroup)object).getDescription()); - } ((CardLayout)myOptionsPanel.getLayout()).show(myOptionsPanel, card); } @@ -230,17 +285,17 @@ public class ProjectTypeStep extends ModuleWizardStep { @Override public void updateStep() { - myList.resetSelection(); + myProjectTypesList.resetSelection(); } @Override public void onStepLeaving() { - myList.saveSelection(); + myProjectTypesList.saveSelection(); } @Override public void onWizardFinished() throws CommitStepException { - if (!myCommitted) { + if (!myCommitted && myTabs.getSelectedInfo() == myFrameworksTab) { boolean ok = myFrameworksPanel.downloadLibraries(); if (!ok) { int answer = Messages.showYesNoDialog(getComponent(), @@ -266,6 +321,28 @@ public class ProjectTypeStep extends ModuleWizardStep { @TestOnly public boolean setSelectedProjectType(String group, String name) { - return myList.setSelectedTemplate(group, name); + return myProjectTypesList.setSelectedTemplate(group, name); } + + @Override + public void dispose() { + } + + private static List getTemplates(final String projectType) { + ProjectTemplateEP[] extensions = ProjectTemplateEP.EP_NAME.getExtensions(); + return ContainerUtil.mapNotNull(extensions, new NullableFunction() { + @Nullable + @Override + public ProjectCategory fun(ProjectTemplateEP ep) { + + if (!projectType.equals(ep.projectType)) { + return null; + } + ClassLoader classLoader = ep.getLoaderForClass(); + URL url = classLoader.getResource(ep.templatePath); + return url == null ? null : new TemplateBasedProjectType(new LocalArchivedTemplate(url, classLoader)); + } + }); + } + } diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/TemplateBasedProjectType.java b/java/idea-ui/src/com/intellij/ide/projectWizard/TemplateBasedProjectType.java index 8f462aea22aa..e9eefe505881 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/TemplateBasedProjectType.java +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/TemplateBasedProjectType.java @@ -17,12 +17,10 @@ package com.intellij.ide.projectWizard; import com.intellij.ide.util.projectWizard.ModuleBuilder; import com.intellij.platform.ProjectTemplate; -import com.intellij.platform.templates.ArchivedTemplatesFactory; import com.intellij.platform.templates.LocalArchivedTemplate; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.io.File; import java.net.URL; /** @@ -36,8 +34,7 @@ public class TemplateBasedProjectType extends ProjectCategory { public TemplateBasedProjectType(String templatePath) { URL resource = getClass().getResource(templatePath); assert resource != null : templatePath; - String name = ArchivedTemplatesFactory.getTemplateName(new File(templatePath).getName()); - myTemplate = new LocalArchivedTemplate(name, resource, getClass().getClassLoader()); + myTemplate = new LocalArchivedTemplate(resource, getClass().getClassLoader()); } public TemplateBasedProjectType(ProjectTemplate template) { diff --git a/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java b/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java index 8ea988e72a95..b2ea5025efd2 100644 --- a/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java +++ b/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java @@ -41,7 +41,7 @@ import java.util.*; */ public class ArchivedTemplatesFactory extends ProjectTemplatesFactory { - private static final String ZIP = ".zip"; + static final String ZIP = ".zip"; private final ClearableLazyValue>> myGroups = new ClearableLazyValue>>() { @NotNull @@ -83,7 +83,8 @@ public class ArchivedTemplatesFactory extends ProjectTemplatesFactory { if (index != -1) { child = child.substring(0, index); } - map.putValue(child.replace('_', ' '), Pair.create(new URL(url.getKey().toExternalForm() + "/" + child), url.getValue())); + String name = child.replace('_', ' '); + map.putValue(name, Pair.create(new URL(url.getKey().toExternalForm() + "/" + child), url.getValue())); } } catch (IOException e) { @@ -132,8 +133,7 @@ public class ArchivedTemplatesFactory extends ProjectTemplatesFactory { for (String child : children) { if (child.endsWith(ZIP)) { URL templateUrl = new URL(url.first.toExternalForm() + "/" + child); - String name = getTemplateName(child); - templates.add(new LocalArchivedTemplate(name, templateUrl, url.second)); + templates.add(new LocalArchivedTemplate(templateUrl, url.second)); } } } @@ -144,10 +144,6 @@ public class ArchivedTemplatesFactory extends ProjectTemplatesFactory { return templates.toArray(new ProjectTemplate[templates.size()]); } - public static String getTemplateName(String child) { - return child.substring(0, child.length() - ZIP.length()).replace('_', ' '); - } - @Override public int getGroupWeight(String group) { return CUSTOM_GROUP.equals(group) ? -2 : 0; diff --git a/java/idea-ui/src/com/intellij/platform/templates/LocalArchivedTemplate.java b/java/idea-ui/src/com/intellij/platform/templates/LocalArchivedTemplate.java index f671335e6d7e..dd29a6986922 100644 --- a/java/idea-ui/src/com/intellij/platform/templates/LocalArchivedTemplate.java +++ b/java/idea-ui/src/com/intellij/platform/templates/LocalArchivedTemplate.java @@ -31,6 +31,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Collections; @@ -52,10 +53,9 @@ public class LocalArchivedTemplate extends ArchivedProjectTemplate { private List myInputFields = Collections.emptyList(); private Icon myIcon; - public LocalArchivedTemplate(@NotNull String displayName, - @NotNull URL archivePath, + public LocalArchivedTemplate(@NotNull URL archivePath, @NotNull ClassLoader classLoader) { - super(displayName); + super(getTemplateName(archivePath)); myArchivePath = archivePath; myModuleType = computeModuleType(this); @@ -80,6 +80,11 @@ public class LocalArchivedTemplate extends ArchivedProjectTemplate { } } + private static String getTemplateName(URL url) { + String fileName = new File(url.getPath()).getName(); + return fileName.substring(0, fileName.length() - ArchivedTemplatesFactory.ZIP.length()).replace('_', ' '); + } + @Override public String getDescription() { return readEntry(new Condition() { diff --git a/platform/platform-impl/src/com/intellij/platform/ProjectTemplateEP.java b/platform/platform-impl/src/com/intellij/platform/ProjectTemplateEP.java new file mode 100644 index 000000000000..b91a09bf336f --- /dev/null +++ b/platform/platform-impl/src/com/intellij/platform/ProjectTemplateEP.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.platform; + +import com.intellij.openapi.extensions.AbstractExtensionPointBean; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.xmlb.annotations.Attribute; + +/** + * @author Dmitry Avdeev + * Date: 07.11.13 + */ +public class ProjectTemplateEP extends AbstractExtensionPointBean { + + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.projectTemplate"); + + @Attribute("projectType") + public String projectType; + + @Attribute("templatePath") + public String templatePath; + +} diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 6ac266db5c15..091006a1d0ca 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -98,6 +98,8 @@ +