project templates shown as tree

This commit is contained in:
Dmitry Avdeev
2012-10-03 19:02:22 +04:00
parent 167d1a3299
commit 40c59725c0
10 changed files with 212 additions and 61 deletions
@@ -24,11 +24,12 @@
<properties/>
<border type="none"/>
<children>
<component id="9dad" class="com.intellij.ui.components.JBList" binding="myTemplatesList">
<component id="a0023" class="com.intellij.ui.treeStructure.SimpleTree" binding="myTemplatesTree">
<constraints/>
<properties>
<fixedCellWidth value="30"/>
</properties>
<properties/>
<clientProperties>
<JTree.lineStyle class="java.lang.String" value="Angled"/>
</clientProperties>
</component>
</children>
</scrollpane>
@@ -68,7 +69,7 @@
<properties>
<contentType value="text/html"/>
<editable value="false"/>
<text value="&lt;html&gt;&#13;&#10; &lt;head&gt;&#13;&#10;&#13;&#10; &lt;/head&gt;&#13;&#10; &lt;body&gt;&#13;&#10; &lt;p style=&quot;margin-top: 0&quot;&gt;&#13;&#10; &#13;&#10; &lt;/p&gt;&#13;&#10; &lt;/body&gt;&#13;&#10;&lt;/html&gt;&#13;&#10;"/>
<text value="&lt;html&gt;&#13;&#10; &lt;head&gt;&#13;&#10; &#13;&#10; &lt;/head&gt;&#13;&#10; &lt;body&gt;&#13;&#10; &lt;/body&gt;&#13;&#10;&lt;/html&gt;&#13;&#10;"/>
</properties>
</component>
</children>
@@ -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<SimpleNode> myFilter;
private final FilteringTreeBuilder myBuilder;
private MinusculeMatcher myMatcher;
public SelectTemplateStep(WizardContext context) {
final List<ProjectTemplate> templates = new ArrayList<ProjectTemplate>();
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<ProjectTemplate>(templates));
myTemplatesList.setCellRenderer(new ColoredListCellRenderer() {
ProjectTemplatesFactory[] factories = ProjectTemplatesFactory.EP_NAME.getExtensions();
final MultiMap<String, ProjectTemplatesFactory> groups = new MultiMap<String, ProjectTemplatesFactory>();
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<Map.Entry<String, Collection<ProjectTemplatesFactory>>, SimpleNode>() {
@Override
public SimpleNode fun(Map.Entry<String, Collection<ProjectTemplatesFactory>> entry) {
return new GroupNode(entry.getKey(), entry.getValue());
}
});
}
});
myTemplatesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
buildMatcher();
myFilter = new ElementFilter.Active.Impl<SimpleNode>() {
@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<ProjectTemplate> list = ContainerUtil.filter(templates, new Condition<ProjectTemplate>() {
@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<ProjectTemplate>(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<SimpleNode> node = new Ref<SimpleNode>();
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<ProjectTemplatesFactory> myFactories;
public GroupNode(String group, Collection<ProjectTemplatesFactory> factories) {
myGroup = group;
myFactories = factories;
}
@Override
public SimpleNode[] getChildren() {
List<SimpleNode> children = new ArrayList<SimpleNode>();
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();
}
}
}
@@ -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();
@@ -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<URL> urls = new HashSet<URL>();
@@ -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);
@@ -41,7 +41,7 @@ public interface ElementFilter<T> {
abstract class Impl<T> implements Active<T> {
Set<Listener<T>> myListeners = new CopyOnWriteArraySet<Listener<T>>();
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<T> myListener : myListeners) {
myListener.update(preferredSelection, adjustSelection, now).doWhenProcessed(result.createSetDoneRunnable());
@@ -48,7 +48,7 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
public FilteringTreeBuilder(Tree tree,
ElementFilter filter,
AbstractTreeStructure structure,
Comparator<NodeDescriptor> comparator) {
@Nullable Comparator<NodeDescriptor> comparator) {
super(tree,
(DefaultTreeModel)tree.getModel(),
structure instanceof FilteringTreeStructure ? structure
@@ -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;
@@ -28,5 +28,8 @@ public interface ProjectTemplatesFactory {
ExtensionPointName<ProjectTemplatesFactory> EP_NAME = ExtensionPointName.create("com.intellij.projectTemplatesFactory");
@NotNull
ProjectTemplate[] createTemplates(WizardContext context);
String[] getGroups();
@NotNull
ProjectTemplate[] createTemplates(String group, WizardContext context);
}
@@ -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();