diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.form b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.form
index 9d55be75fa91..6c58d0011214 100644
--- a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.form
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.form
@@ -24,11 +24,12 @@
-
+
-
-
-
+
+
+
+
@@ -68,7 +69,7 @@
-
+
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.java
index 99b710e74f4f..5c611dc7d6e0 100644
--- a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.java
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.java
@@ -18,27 +18,37 @@ package com.intellij.ide.util.newProjectWizard;
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
import com.intellij.ide.util.projectWizard.WizardContext;
import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.ProjectTemplate;
import com.intellij.platform.ProjectTemplatesFactory;
import com.intellij.psi.codeStyle.MinusculeMatcher;
import com.intellij.psi.codeStyle.NameUtil;
-import com.intellij.ui.CollectionListModel;
-import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.SearchTextField;
-import com.intellij.ui.components.JBList;
+import com.intellij.ui.speedSearch.ElementFilter;
+import com.intellij.ui.treeStructure.*;
+import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder;
+import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure;
+import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
/**
* @author Dmitry Avdeev
@@ -47,34 +57,76 @@ import java.util.List;
public class SelectTemplateStep extends ModuleWizardStep {
private JPanel myPanel;
- private JBList myTemplatesList;
+ private SimpleTree myTemplatesTree;
private JPanel mySettingsPanel;
private SearchTextField mySearchField;
private JTextPane myDescriptionPane;
private JPanel myDescriptionPanel;
+ private final WizardContext myContext;
+ private final ElementFilter.Active.Impl myFilter;
+ private final FilteringTreeBuilder myBuilder;
+ private MinusculeMatcher myMatcher;
+
public SelectTemplateStep(WizardContext context) {
- final List templates = new ArrayList();
- ProjectTemplatesFactory[] factories = ProjectTemplatesFactory.EP_NAME.getExtensions();
- for (ProjectTemplatesFactory factory : factories) {
- templates.addAll(Arrays.asList(factory.createTemplates(context)));
- }
-
+ myContext = context;
Messages.installHyperlinkSupport(myDescriptionPane);
- myTemplatesList.setModel(new CollectionListModel(templates));
- myTemplatesList.setCellRenderer(new ColoredListCellRenderer() {
+ ProjectTemplatesFactory[] factories = ProjectTemplatesFactory.EP_NAME.getExtensions();
+ final MultiMap groups = new MultiMap();
+ for (ProjectTemplatesFactory factory : factories) {
+ for (String string : factory.getGroups()) {
+ groups.putValue(string, factory);
+ }
+ }
+
+ SimpleTreeStructure.Impl structure = new SimpleTreeStructure.Impl(new SimpleNode() {
@Override
- protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
- ProjectTemplate template = (ProjectTemplate)value;
- append(template.getName());
+ public SimpleNode[] getChildren() {
+ return ContainerUtil.map2Array(groups.entrySet(), NO_CHILDREN, new Function>, SimpleNode>() {
+ @Override
+ public SimpleNode fun(Map.Entry> entry) {
+ return new GroupNode(entry.getKey(), entry.getValue());
+ }
+ });
}
});
- myTemplatesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ buildMatcher();
+ myFilter = new ElementFilter.Active.Impl() {
@Override
- public void valueChanged(ListSelectionEvent e) {
+ public boolean shouldBeShowing(SimpleNode template) {
+ return matches(template);
+ }
+ };
+ myBuilder = new FilteringTreeBuilder(myTemplatesTree, myFilter, structure, null);
+
+ myTemplatesTree.setRootVisible(false);
+ myTemplatesTree.setShowsRootHandles(false);
+ myTemplatesTree.setCellRenderer(new ColoredTreeCellRenderer() {
+ @Override
+ public void customizeCellRenderer(JTree tree,
+ Object value,
+ boolean selected,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus) {
+// setIpad(new Insets());
+ SimpleNode node = getSimpleNode(value);
+ if (node != null) {
+ append(node.getName());
+ }
+ if (node instanceof GroupNode) {
+ setIcon(UIUtil.getTreeIcon(expanded));
+ }
+ }
+ });
+
+ myTemplatesTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
if (mySettingsPanel.getComponentCount() > 0) {
mySettingsPanel.remove(0);
}
@@ -89,44 +141,83 @@ public class SelectTemplateStep extends ModuleWizardStep {
myDescriptionPane.setText(description);
myDescriptionPanel.setVisible(StringUtil.isNotEmpty(description));
}
+ else {
+ mySettingsPanel.setVisible(false);
+ myDescriptionPanel.setVisible(false);
+ }
mySettingsPanel.revalidate();
mySettingsPanel.repaint();
}
});
- if (myTemplatesList.getModel().getSize() > 0) {
- myTemplatesList.setSelectedIndex(0);
- }
+
+ //if (myTemplatesTree.getModel().getSize() > 0) {
+ // myTemplatesTree.setSelectedIndex(0);
+ //}
mySearchField.addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
- final MinusculeMatcher matcher = NameUtil.buildMatcher(mySearchField.getText(), NameUtil.MatchingCaseSensitivity.NONE);
- ProjectTemplate selectedTemplate = getSelectedTemplate();
- List list = ContainerUtil.filter(templates, new Condition() {
- @Override
- public boolean value(ProjectTemplate template) {
- String name = template.getName();
- String[] words = NameUtil.nameToWords(name);
- for (String word : words) {
- if (matcher.matches(word)) return true;
- }
- return false;
- }
- });
- myTemplatesList.setModel(new CollectionListModel(list));
- if (!list.isEmpty()) {
- if (list.contains(selectedTemplate)) {
- myTemplatesList.setSelectedValue(selectedTemplate, true);
- }
- else {
- myTemplatesList.setSelectedIndex(0);
- }
- }
+ doFilter();
}
});
+ doFilter();
}
+ private void doFilter() {
+ buildMatcher();
+ SimpleNode selectedNode = getSelectedNode();
+ final Ref node = new Ref();
+ if (!(selectedNode instanceof TemplateNode) || !matches(selectedNode)) {
+ myTemplatesTree.accept(myBuilder, new SimpleNodeVisitor() {
+ @Override
+ public boolean accept(SimpleNode simpleNode) {
+ FilteringTreeStructure.FilteringNode wrapper = (FilteringTreeStructure.FilteringNode)simpleNode;
+ Object delegate = wrapper.getDelegate();
+ if (delegate instanceof TemplateNode && matches((SimpleNode)delegate)) {
+ node.set((SimpleNode)delegate);
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ myFilter.fireUpdate(node.get(), true, false);
+ }
+
+ private boolean matches(SimpleNode template) {
+ String name = template.getName();
+ if (name == null) return false;
+ String[] words = NameUtil.nameToWords(name);
+ for (String word : words) {
+ if (myMatcher.matches(word)) return true;
+ }
+ return false;
+ }
+
+ private void buildMatcher() {
+ myMatcher = NameUtil.buildMatcher(mySearchField.getText(), NameUtil.MatchingCaseSensitivity.NONE);
+ }
+
+ @Nullable
public ProjectTemplate getSelectedTemplate() {
- return (ProjectTemplate)myTemplatesList.getSelectedValue();
+ SimpleNode delegate = getSelectedNode();
+ return delegate instanceof TemplateNode ? ((TemplateNode)delegate).myTemplate : null;
+ }
+
+ @Nullable
+ private SimpleNode getSelectedNode() {
+ TreePath path = myTemplatesTree.getSelectionPath();
+ if (path == null) return null;
+ return getSimpleNode(path.getLastPathComponent());
+ }
+
+ @Nullable
+ private SimpleNode getSimpleNode(Object component) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode)component;
+ Object userObject = node.getUserObject();
+ if (!(userObject instanceof FilteringTreeStructure.FilteringNode)) return null;
+ FilteringTreeStructure.FilteringNode object = (FilteringTreeStructure.FilteringNode)userObject;
+ return (SimpleNode)object.getDelegate();
}
@Override
@@ -142,4 +233,51 @@ public class SelectTemplateStep extends ModuleWizardStep {
@Override
public void updateDataModel() {
}
+
+ @Override
+ public void disposeUIResources() {
+ Disposer.dispose(myBuilder);
+ }
+
+ private class GroupNode extends SimpleNode {
+ private final String myGroup;
+ private final Collection myFactories;
+
+ public GroupNode(String group, Collection factories) {
+ myGroup = group;
+ myFactories = factories;
+ }
+
+ @Override
+ public SimpleNode[] getChildren() {
+ List children = new ArrayList();
+ for (ProjectTemplatesFactory factory : myFactories) {
+ ProjectTemplate[] templates = factory.createTemplates(myGroup, myContext);
+ for (ProjectTemplate template : templates) {
+ children.add(new TemplateNode(template));
+ }
+ }
+ return children.toArray(new SimpleNode[children.size()]);
+ }
+
+
+ @Override
+ public String getName() {
+ return myGroup;
+ }
+ }
+
+ private static class TemplateNode extends NullNode {
+
+ private final ProjectTemplate myTemplate;
+
+ public TemplateNode(ProjectTemplate template) {
+ myTemplate = template;
+ }
+
+ @Override
+ public String getName() {
+ return myTemplate.getName();
+ }
+ }
}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetsTreeCellRenderer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetsTreeCellRenderer.java
index 67b89765c300..d89fe3797478 100644
--- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetsTreeCellRenderer.java
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetsTreeCellRenderer.java
@@ -21,13 +21,12 @@ import com.intellij.ui.ErrorLabel;
import com.intellij.ui.GroupedElementsRenderer;
import javax.swing.*;
-import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
/**
* @author nik
*/
-public class FacetsTreeCellRenderer extends GroupedElementsRenderer.Tree implements TreeCellRenderer {
+public class FacetsTreeCellRenderer extends GroupedElementsRenderer.Tree {
@Override
protected JComponent createItemComponent() {
myTextLabel = new ErrorLabel();
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 0cbca9eb0086..9ce6014d93c3 100644
--- a/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java
+++ b/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java
@@ -38,7 +38,13 @@ public class ArchivedTemplatesFactory implements ProjectTemplatesFactory {
@NotNull
@Override
- public ProjectTemplate[] createTemplates(WizardContext context) {
+ public String[] getGroups() {
+ return new String[] {"Standard"};
+ }
+
+ @NotNull
+ @Override
+ public ProjectTemplate[] createTemplates(String group, WizardContext context) {
IdeaPluginDescriptor[] plugins = PluginManager.getPlugins();
Set urls = new HashSet();
diff --git a/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java b/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java
index 7223a36514af..314dfbbc0fc1 100644
--- a/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java
+++ b/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java
@@ -21,6 +21,7 @@ import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
+import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
public abstract class GroupedElementsRenderer {
@@ -135,7 +136,7 @@ public abstract class GroupedElementsRenderer {
}
}
- public static abstract class Tree extends GroupedElementsRenderer {
+ public static abstract class Tree extends GroupedElementsRenderer implements TreeCellRenderer {
protected void layout() {
myRendererComponent.add(mySeparatorComponent, BorderLayout.NORTH);
diff --git a/platform/platform-api/src/com/intellij/ui/speedSearch/ElementFilter.java b/platform/platform-api/src/com/intellij/ui/speedSearch/ElementFilter.java
index 503c9c91eb60..1d568967b6e5 100644
--- a/platform/platform-api/src/com/intellij/ui/speedSearch/ElementFilter.java
+++ b/platform/platform-api/src/com/intellij/ui/speedSearch/ElementFilter.java
@@ -41,7 +41,7 @@ public interface ElementFilter {
abstract class Impl implements Active {
Set> myListeners = new CopyOnWriteArraySet>();
- public ActionCallback fireUpdate(final T preferredSelection, final boolean adjustSelection, final boolean now) {
+ public ActionCallback fireUpdate(@Nullable final T preferredSelection, final boolean adjustSelection, final boolean now) {
final ActionCallback result = new ActionCallback(myListeners.size());
for (final Listener myListener : myListeners) {
myListener.update(preferredSelection, adjustSelection, now).doWhenProcessed(result.createSetDoneRunnable());
diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeBuilder.java b/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeBuilder.java
index b84b12d258db..d6048bfbbe35 100644
--- a/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeBuilder.java
+++ b/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeBuilder.java
@@ -48,7 +48,7 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
public FilteringTreeBuilder(Tree tree,
ElementFilter filter,
AbstractTreeStructure structure,
- Comparator comparator) {
+ @Nullable Comparator comparator) {
super(tree,
(DefaultTreeModel)tree.getModel(),
structure instanceof FilteringTreeStructure ? structure
diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java
index 4006716e9f48..be3330db1bf5 100644
--- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java
+++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java
@@ -45,7 +45,6 @@ import javax.swing.event.TreeSelectionListener;
import javax.swing.plaf.TreeUI;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
@@ -283,7 +282,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl
return null;
}
- class Renderer extends GroupedElementsRenderer.Tree implements TreeCellRenderer {
+ class Renderer extends GroupedElementsRenderer.Tree {
private JLabel myHandle;
diff --git a/platform/platform-impl/src/com/intellij/platform/ProjectTemplatesFactory.java b/platform/platform-impl/src/com/intellij/platform/ProjectTemplatesFactory.java
index f1e7f041e8bc..e64e4ae47800 100644
--- a/platform/platform-impl/src/com/intellij/platform/ProjectTemplatesFactory.java
+++ b/platform/platform-impl/src/com/intellij/platform/ProjectTemplatesFactory.java
@@ -28,5 +28,8 @@ public interface ProjectTemplatesFactory {
ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.projectTemplatesFactory");
@NotNull
- ProjectTemplate[] createTemplates(WizardContext context);
+ String[] getGroups();
+
+ @NotNull
+ ProjectTemplate[] createTemplates(String group, WizardContext context);
}
diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java
index fbe23b9c0155..25236800c7b0 100644
--- a/platform/util/src/com/intellij/util/ui/UIUtil.java
+++ b/platform/util/src/com/intellij/util/ui/UIUtil.java
@@ -840,6 +840,10 @@ public class UIUtil {
return UIManager.getIcon("Tree.expandedIcon");
}
+ public static Icon getTreeIcon(boolean expanded) {
+ return expanded ? getTreeExpandedIcon() : getTreeCollapsedIcon();
+ }
+
public static Icon getTreeSelectedCollapsedIcon() {
return isUnderAquaBasedLookAndFeel() || isUnderNimbusLookAndFeel() || isUnderGTKLookAndFeel()
? AllIcons.Mac.Tree_white_right_arrow : getTreeCollapsedIcon();