ProjectTemplateList

This commit is contained in:
Dmitry Avdeev
2013-12-24 12:07:13 +04:00
parent c3ca08be47
commit 4f93b7d314
6 changed files with 245 additions and 330 deletions
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.ide.projectWizard.ProjectTemplateList">
<grid id="27dc6" binding="myPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<scrollpane id="99b52" class="com.intellij.ui.components.JBScrollPane">
<constraints border-constraint="Center"/>
<properties/>
<border type="none"/>
<children>
<component id="f677e" class="com.intellij.ui.components.JBList" binding="myList">
<constraints/>
<properties>
<selectionMode value="0"/>
</properties>
</component>
</children>
</scrollpane>
<grid id="2bdbb" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints border-constraint="South"/>
<properties/>
<border type="none"/>
<children>
<scrollpane id="1765e" class="com.intellij.ui.components.JBScrollPane">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="-1" height="100"/>
</grid>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="124ce" class="javax.swing.JTextPane" binding="myDescriptionPane">
<constraints/>
<properties/>
</component>
</children>
</scrollpane>
<vspacer id="c930f">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="1" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="-1" height="3"/>
</grid>
</constraints>
</vspacer>
</children>
</grid>
</children>
</grid>
</form>
@@ -0,0 +1,149 @@
/*
* 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.ide.projectWizard;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.ListItemDescriptor;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.ProjectTemplate;
import com.intellij.platform.templates.ArchivedProjectTemplate;
import com.intellij.ui.CollectionListModel;
import com.intellij.ui.components.JBList;
import com.intellij.ui.popup.list.GroupedItemsListRenderer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* @author Dmitry Avdeev
* Date: 12/24/13
*/
public class ProjectTemplateList extends JPanel {
private JBList myList;
private JPanel myPanel;
private JTextPane myDescriptionPane;
private ProjectTemplate myFirstArchivedTemplate;
public ProjectTemplateList() {
super(new BorderLayout());
add(myPanel, BorderLayout.CENTER);
myList.setCellRenderer(new GroupedItemsListRenderer(new ListItemDescriptor<ProjectTemplate>() {
@Nullable
@Override
public String getTextFor(ProjectTemplate value) {
return value.getName();
}
@Nullable
@Override
public String getTooltipFor(ProjectTemplate value) {
return null;
}
@Nullable
@Override
public Icon getIconFor(ProjectTemplate value) {
return value.getIcon();
}
@Override
public boolean hasSeparatorAboveOf(ProjectTemplate value) {
return value == myFirstArchivedTemplate;
}
@Nullable
@Override
public String getCaptionAboveOf(ProjectTemplate value) {
return "Project Templates";
}
}));
myList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
myDescriptionPane.setText("");
ProjectTemplate template = getSelectedTemplate();
if (template != null) {
String description = template.getDescription();
if (StringUtil.isNotEmpty(description)) {
description = "<html><body><font " +
(SystemInfo.isMac ? "" : "face=\"Verdana\" size=\"-1\"") + '>' + description +
"</font></body></html>";
myDescriptionPane.setText(description);
}
}
}
});
Messages.installHyperlinkSupport(myDescriptionPane);
}
public void setTemplates(List<ProjectTemplate> list) {
Collections.sort(list, new Comparator<ProjectTemplate>() {
@Override
public int compare(ProjectTemplate o1, ProjectTemplate o2) {
return Comparing.compare(o1 instanceof ArchivedProjectTemplate, o2 instanceof ArchivedProjectTemplate);
}
});
myFirstArchivedTemplate = ContainerUtil.find(list, new Condition<ProjectTemplate>() {
@Override
public boolean value(ProjectTemplate template) {
return template instanceof ArchivedProjectTemplate;
}
});
int index = myList.getSelectedIndex();
//noinspection unchecked
myList.setModel(new CollectionListModel(list));
myList.setSelectedIndex(index == -1 ? 0 : index);
}
public ProjectTemplate getSelectedTemplate() {
return (ProjectTemplate)myList.getSelectedValue();
}
public void addListSelectionListener(ListSelectionListener listener) {
myList.addListSelectionListener(listener);
}
public boolean setSelectedTemplate(String name) {
ListModel model1 = myList.getModel();
for (int j = 0; j < model1.getSize(); j++) {
if (name.equals(((ProjectTemplate)model1.getElementAt(j)).getName())) {
myList.setSelectedIndex(j);
return true;
}
}
return false;
}
public void setPaintBusy(boolean b) {
myList.setPaintBusy(b);
}
}
@@ -55,43 +55,9 @@
<properties/>
<border type="none"/>
<children>
<grid id="46e74" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints border-constraint="South"/>
<properties/>
<border type="none"/>
<children>
<scrollpane id="31a22" class="com.intellij.ui.components.JBScrollPane">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<minimum-size width="-1" height="100"/>
<preferred-size width="-1" height="100"/>
</grid>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="9cddd" class="javax.swing.JTextPane" binding="myDescription">
<constraints/>
<properties/>
</component>
</children>
</scrollpane>
</children>
</grid>
<scrollpane id="4f20c" class="com.intellij.ui.components.JBScrollPane">
<nested-form id="b063c" form-file="com/intellij/ide/projectWizard/ProjectTemplateList.form" binding="myTemplatesList">
<constraints border-constraint="Center"/>
<properties/>
<border type="none"/>
<children>
<component id="bb891" class="com.intellij.ui.components.JBList" binding="myTemplatesList">
<constraints/>
<properties>
<selectionMode value="0"/>
</properties>
</component>
</children>
</scrollpane>
</nested-form>
</children>
</grid>
</children>
@@ -25,11 +25,8 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.ProjectTemplate;
import com.intellij.platform.ProjectTemplateEP;
import com.intellij.platform.templates.ArchivedProjectTemplate;
@@ -71,8 +68,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
private JPanel myPanel;
private JPanel myOptionsPanel;
private JBList myProjectTypeList;
private JTextPane myDescription;
private JBList myTemplatesList;
private ProjectTemplateList myTemplatesList;
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private final FactoryMap<ProjectTemplate, ModuleBuilder> myBuilders = new FactoryMap<ProjectTemplate, ModuleBuilder>() {
@@ -83,8 +79,8 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
}
};
private final Set<String> myCards = new HashSet<String>();
private final MultiMap<TemplatesGroup,ProjectTemplate> myTemplatesMap;
private boolean myRemoteTemplatesLoaded;
public ProjectTypeStep(WizardContext context, NewProjectWizard wizard, ModulesProvider modulesProvider) {
myContext = context;
@@ -154,23 +150,17 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
}
});
myTemplatesList.setCellRenderer(new ColoredListCellRenderer<ProjectTemplate>() {
@Override
protected void customizeCellRenderer(JList list, ProjectTemplate value, int index, boolean selected, boolean hasFocus) {
append(value.getName()).setIcon(value.getIcon());
}
});
myTemplatesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
myTemplatesList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
updateSelection();
}
});
for (ProjectTemplate category : myTemplatesMap.values()) {
myWizard.getSequence().addStepsForBuilder(myBuilders.get(category), context, modulesProvider);
}
Messages.installHyperlinkSupport(myDescription);
myProjectTypeList.setSelectedIndex(0);
}
@@ -180,21 +170,39 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
TemplatesGroup group = getSelectedGroup();
if (group == null) return;
Collection<ProjectTemplate> templates = myTemplatesMap.get(group);
String card = DEFAULT_CARD;
if (!selectCustomOptions(templates)) {
setTemplatesList(group, templates);
((CardLayout)myOptionsPanel.getLayout()).show(myOptionsPanel, DEFAULT_CARD);
}
updateSelection();
}
private void setTemplatesList(TemplatesGroup group, Collection<ProjectTemplate> templates) {
ArrayList<ProjectTemplate> list = new ArrayList<ProjectTemplate>(templates);
if (group.getParentGroup() == null) {
for (TemplatesGroup templatesGroup : myTemplatesMap.keySet()) {
if (group.getName().equals(templatesGroup.getParentGroup())) {
list.addAll(myTemplatesMap.get(templatesGroup));
}
}
}
myTemplatesList.setTemplates(list);
}
private boolean selectCustomOptions(Collection<ProjectTemplate> templates) {
if (templates.size() == 1) {
ModuleBuilder builder = myBuilders.get(templates.iterator().next());
JComponent panel = builder.getCustomOptionsPanel(this);
if (panel != null) {
card = builder.getBuilderId();
String card = builder.getBuilderId();
if (myCards.add(card)) {
myOptionsPanel.add(panel, card);
}
((CardLayout)myOptionsPanel.getLayout()).show(myOptionsPanel, card);
return true;
}
}
myTemplatesList.setModel(new CollectionListModel(templates));
myTemplatesList.setSelectedIndex(0);
((CardLayout)myOptionsPanel.getLayout()).show(myOptionsPanel, card);
updateSelection();
return false;
}
private TemplatesGroup getSelectedGroup() {
@@ -212,7 +220,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
return template;
}
}
return (ProjectTemplate)myTemplatesList.getSelectedValue();
return myTemplatesList.getSelectedTemplate();
}
@Nullable
@@ -236,7 +244,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
@Override
public void updateStep() {
if (myContext.isCreatingNewProject()) {
if (myContext.isCreatingNewProject() && !myRemoteTemplatesLoaded) {
loadRemoteTemplates();
}
}
@@ -256,13 +264,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
for (int i = 0; i < model.getSize(); i++) {
if (group.equals(((TemplatesGroup)model.getElementAt(i)).getName())) {
myProjectTypeList.setSelectedIndex(i);
ListModel model1 = myTemplatesList.getModel();
for (int j = 0; j < model1.getSize(); j++) {
if (name.equals(((ProjectTemplate)model1.getElementAt(j)).getName())) {
myTemplatesList.setSelectedIndex(j);
return true;
}
}
return myTemplatesList.setSelectedTemplate(name);
}
}
return false;
@@ -315,14 +317,13 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
TemplatesGroup group = getSelectedGroup();
if (group == null) return;
Collection<ProjectTemplate> templates = myTemplatesMap.get(group);
int index = myTemplatesList.getSelectedIndex();
myTemplatesList.setModel(new CollectionListModel(templates));
myTemplatesList.setSelectedIndex(index);
setTemplatesList(group, templates);
}
});
}
finally {
myTemplatesList.setPaintBusy(false);
myRemoteTemplatesLoaded = true;
}
}
});
@@ -334,19 +335,6 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable, Act
}
private void updateSelection() {
ProjectTemplate template = getSelectedTemplate();
if (template != null) {
String description = template.getDescription();
if (StringUtil.isNotEmpty(description)) {
description = "<html><body><font " +
(SystemInfo.isMac ? "" : "face=\"Verdana\" size=\"-1\"") + '>' + description +
"</font></body></html>";
myDescription.setText(description);
}
}
else {
myDescription.setText("");
}
ModuleBuilder builder = getSelectedBuilder();
if (builder != null) {
myContext.setProjectBuilder(builder);
@@ -1,243 +0,0 @@
/*
* 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.ide.projectWizard;
import com.intellij.ide.util.newProjectWizard.SelectTemplateSettings;
import com.intellij.ide.util.newProjectWizard.TemplatesGroup;
import com.intellij.ide.util.projectWizard.ModuleBuilder;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.ui.popup.ListItemDescriptor;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.platform.ProjectTemplatesFactory;
import com.intellij.ui.CollectionListModel;
import com.intellij.ui.ListSpeedSearch;
import com.intellij.ui.SpeedSearchComparator;
import com.intellij.ui.components.JBList;
import com.intellij.ui.popup.list.GroupedItemsListRenderer;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.*;
/**
* @author Dmitry Avdeev
* Date: 11/21/12
*/
public class ProjectTypesList implements Disposable {
private final JBList myList;
private final CollectionListModel<TemplateItem> myModel;
private Pair<TemplateItem, Integer> myBestMatch;
public ProjectTypesList(JBList list, MultiMap<TemplatesGroup, ProjectCategory> map, FactoryMap<ProjectCategory, ModuleBuilder> builders) {
myList = list;
myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
new ListSpeedSearch(myList) {
@Override
protected String getElementText(Object element) {
return super.getElementText(element);
}
}.setComparator(new SpeedSearchComparator(false));
List<TemplateItem> items = buildItems(map, builders);
myModel = new CollectionListModel<TemplateItem>(items);
myList.setCellRenderer(new GroupedItemsListRenderer(new ListItemDescriptor() {
@Nullable
@Override
public String getTextFor(Object value) {
return ((TemplateItem)value).getName();
}
@Nullable
@Override
public String getTooltipFor(Object value) {
return null;
}
@Nullable
@Override
public Icon getIconFor(Object value) {
return ((TemplateItem)value).getIcon();
}
@Override
public boolean hasSeparatorAboveOf(Object value) {
TemplateItem item = (TemplateItem)value;
int index = myModel.getElementIndex(item);
return index == 0 || !myModel.getElementAt(index - 1).getGroupName().equals(item.getGroupName());
}
@Nullable
@Override
public String getCaptionAboveOf(Object value) {
return ((TemplateItem)value).getGroupName();
}
}));
myList.setModel(myModel);
}
void installKeyAction(JComponent component) {
new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
InputEvent event = e.getInputEvent();
if (event instanceof KeyEvent) {
int row = myList.getSelectedIndex();
int toSelect;
switch (((KeyEvent)event).getKeyCode()) {
case KeyEvent.VK_UP:
toSelect = row == 0 ? myList.getItemsCount() - 1 : row - 1;
myList.setSelectedIndex(toSelect);
myList.ensureIndexIsVisible(toSelect);
break;
case KeyEvent.VK_DOWN:
toSelect = row < myList.getItemsCount() - 1 ? row + 1 : 0;
myList.setSelectedIndex(toSelect);
myList.ensureIndexIsVisible(toSelect);
break;
}
}
}
}.registerCustomShortcutSet(new CustomShortcutSet(KeyEvent.VK_UP, KeyEvent.VK_DOWN), component);
}
void resetSelection() {
if (myList.getSelectedIndex() != -1) return;
SelectTemplateSettings settings = SelectTemplateSettings.getInstance();
if (settings.getLastGroup() == null || !setSelectedType(settings.getLastGroup(), settings.getLastTemplate())) {
myList.setSelectedIndex(0);
}
}
void saveSelection() {
TemplateItem item = (TemplateItem)myList.getSelectedValue();
if (item != null) {
SelectTemplateSettings.getInstance().setLastTemplate(item.getGroupName(), item.getName());
}
}
private static List<TemplateItem> buildItems(final MultiMap<TemplatesGroup, ProjectCategory> map, final FactoryMap<ProjectCategory, ModuleBuilder> builders) {
List<TemplateItem> items = new ArrayList<TemplateItem>();
List<TemplatesGroup> groups = new ArrayList<TemplatesGroup>(map.keySet());
final Map<ModuleType, Integer> moduleTypeMap = new HashMap<ModuleType, Integer>();
for (ProjectCategory category : map.values()) {
ModuleType type = builders.get(category).getModuleType();
Integer integer = moduleTypeMap.get(type);
moduleTypeMap.put(type, integer == null ? 0 : integer + 1);
}
Collections.sort(groups, new Comparator<TemplatesGroup>() {
@Override
public int compare(TemplatesGroup o1, TemplatesGroup o2) {
int weight = Comparing.compare(o1.getName().equals(ProjectTemplatesFactory.OTHER_GROUP), o2.getName().equals(ProjectTemplatesFactory.OTHER_GROUP));
if (weight != 0) return weight;
int byModuleType = getModuleTypePopularity(o2, map, builders, moduleTypeMap) - getModuleTypePopularity(o1, map, builders, moduleTypeMap);
if (byModuleType != 0) return byModuleType;
int i = map.get(o2).size() - map.get(o1).size(); // compare group size
return i == 0 ? o1.compareTo(o2) : i;
}
});
for (TemplatesGroup group : groups) {
for (ProjectCategory template : map.get(group)) {
TemplateItem templateItem = new TemplateItem(template, group.getName());
items.add(templateItem);
}
}
return items;
}
private static int getModuleTypePopularity(TemplatesGroup group,
MultiMap<TemplatesGroup, ProjectCategory> map,
FactoryMap<ProjectCategory, ModuleBuilder> builders,
Map<ModuleType, Integer> moduleTypeMap) {
int moduleTypePopularity = 0;
for (ProjectCategory category : map.get(group)) {
ModuleType type = builders.get(category).getModuleType();
moduleTypePopularity = Math.max(moduleTypePopularity, moduleTypeMap.get(type));
}
return moduleTypePopularity;
}
@Nullable
public ProjectCategory getSelectedType() {
Object value = myList.getSelectedValue();
return value instanceof TemplateItem ? ((TemplateItem)value).myTemplate : null;
}
public boolean setSelectedType(@Nullable String group, @Nullable String name) {
for (int i = 0; i < myList.getModel().getSize(); i++) {
Object o = myList.getModel().getElementAt(i);
if (o instanceof TemplateItem && ((TemplateItem)o).myGroup.equals(group) && ((TemplateItem)o).getName().equals(name)) {
myList.setSelectedIndex(i);
myList.ensureIndexIsVisible(i);
return true;
}
}
return false;
}
@Override
public void dispose() {
}
static class TemplateItem {
private final ProjectCategory myTemplate;
private final String myGroup;
TemplateItem(ProjectCategory template, String group) {
myTemplate = template;
myGroup = group;
}
String getName() {
return myTemplate.getDisplayName();
}
public String getGroupName() {
return myGroup;
}
Icon getIcon() {
return myTemplate.createModuleBuilder().getNodeIcon();
}
@Nullable
String getDescription() {
return myTemplate.getDescription();
}
@Override
public String toString() {
return getName() + " " + getGroupName();
}
}
}
@@ -22,18 +22,18 @@ import javax.swing.*;
/**
* @author kir
*/
public interface ListItemDescriptor {
public interface ListItemDescriptor<T> {
@Nullable
String getTextFor(Object value);
String getTextFor(T value);
@Nullable
String getTooltipFor(Object value);
String getTooltipFor(T value);
@Nullable
Icon getIconFor(Object value);
Icon getIconFor(T value);
boolean hasSeparatorAboveOf(Object value);
boolean hasSeparatorAboveOf(T value);
@Nullable
String getCaptionAboveOf(Object value);
String getCaptionAboveOf(T value);
}