| /* |
| * Copyright 2000-2009 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. |
| */ |
| |
| /** |
| * @author cdr |
| */ |
| package com.intellij.codeInsight.intention.impl.config; |
| |
| import com.intellij.codeInsight.intention.IntentionManager; |
| import com.intellij.ide.CommonActionsManager; |
| import com.intellij.ide.DefaultTreeExpander; |
| import com.intellij.ide.ui.search.SearchUtil; |
| import com.intellij.openapi.actionSystem.ActionManager; |
| import com.intellij.openapi.actionSystem.ActionPlaces; |
| import com.intellij.openapi.actionSystem.DefaultActionGroup; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.packageDependencies.ui.TreeExpansionMonitor; |
| import com.intellij.ui.*; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.ui.UIUtil; |
| import com.intellij.util.ui.tree.TreeUtil; |
| |
| import javax.swing.*; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| import javax.swing.tree.DefaultTreeModel; |
| import javax.swing.tree.TreePath; |
| import java.awt.*; |
| import java.util.*; |
| import java.util.List; |
| |
| public abstract class IntentionSettingsTree { |
| private JComponent myComponent; |
| private CheckboxTree myTree; |
| private FilterComponent myFilter; |
| |
| private final Map<IntentionActionMetaData, Boolean> myIntentionToCheckStatus = new HashMap<IntentionActionMetaData, Boolean>(); |
| private JPanel myNorthPanel; |
| |
| protected IntentionSettingsTree() { |
| initTree(); |
| } |
| |
| public JTree getTree(){ |
| return myTree; |
| } |
| |
| public JComponent getComponent() { |
| return myComponent; |
| } |
| |
| private void initTree() { |
| myTree = new CheckboxTree(new CheckboxTree.CheckboxTreeCellRenderer(true) { |
| @Override |
| public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { |
| if (!(value instanceof CheckedTreeNode)) return; |
| CheckedTreeNode node = (CheckedTreeNode)value; |
| SimpleTextAttributes attributes = node.getUserObject() instanceof IntentionActionMetaData ? SimpleTextAttributes.REGULAR_ATTRIBUTES : SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES; |
| final String text = getNodeText(node); |
| final Color background = selected ? UIUtil.getTreeSelectionBackground() : UIUtil.getTreeTextBackground(); |
| UIUtil.changeBackGround(this, background); |
| if (text != null) { |
| SearchUtil.appendFragments(myFilter != null ? myFilter.getFilter() : null, |
| text, |
| attributes.getStyle(), |
| attributes.getFgColor(), |
| background, |
| getTextRenderer()); |
| } |
| } |
| }, new CheckedTreeNode(null)); |
| |
| myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| TreePath path = e.getPath(); |
| Object userObject = ((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObject(); |
| selectionChanged(userObject); |
| } |
| }); |
| |
| myFilter = new MyFilterComponent(); |
| myComponent = new JPanel(new BorderLayout()); |
| JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree); |
| myNorthPanel = new JPanel(new BorderLayout()); |
| myNorthPanel.add(myFilter, BorderLayout.CENTER); |
| |
| final DefaultActionGroup group = new DefaultActionGroup(); |
| final CommonActionsManager actionManager = CommonActionsManager.getInstance(); |
| |
| final DefaultTreeExpander treeExpander = new DefaultTreeExpander(myTree); |
| group.add(actionManager.createExpandAllAction(treeExpander, myTree)); |
| group.add(actionManager.createCollapseAllAction(treeExpander, myTree)); |
| |
| myNorthPanel.add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent(), BorderLayout.WEST); |
| |
| myComponent.add(myNorthPanel, BorderLayout.NORTH); |
| myComponent.add(scrollPane, BorderLayout.CENTER); |
| |
| myFilter.reset(); |
| } |
| |
| protected abstract void selectionChanged(Object selected); |
| protected abstract List<IntentionActionMetaData> filterModel(String filter, final boolean force); |
| |
| public void filter(List<IntentionActionMetaData> intentionsToShow) { |
| refreshCheckStatus((CheckedTreeNode)myTree.getModel().getRoot()); |
| reset(intentionsToShow); |
| } |
| |
| public void reset(){ |
| while (((IntentionManagerImpl)IntentionManager.getInstance()).hasActiveRequests()) { |
| try { |
| Thread.sleep(100); |
| } |
| catch (InterruptedException ignored) { |
| } |
| } |
| resetCheckStatus(); |
| reset(IntentionManagerSettings.getInstance().getMetaData()); |
| } |
| |
| private void resetCheckStatus() { |
| myIntentionToCheckStatus.clear(); |
| IntentionManagerSettings manager = IntentionManagerSettings.getInstance(); |
| for (IntentionActionMetaData metaData : manager.getMetaData()) { |
| myIntentionToCheckStatus.put(metaData, manager.isEnabled(metaData)); |
| } |
| } |
| |
| private void reset(List<IntentionActionMetaData> intentionsToShow) { |
| CheckedTreeNode root = new CheckedTreeNode(null); |
| final DefaultTreeModel treeModel = (DefaultTreeModel)myTree.getModel(); |
| intentionsToShow = sort(intentionsToShow); |
| |
| for (final IntentionActionMetaData metaData : intentionsToShow) { |
| String[] category = metaData.myCategory; |
| CheckedTreeNode node = root; |
| for (final String name : category) { |
| CheckedTreeNode child = findChild(node, name); |
| if (child == null) { |
| CheckedTreeNode newChild = new CheckedTreeNode(name); |
| treeModel.insertNodeInto(newChild, node, node.getChildCount()); |
| child = newChild; |
| } |
| node = child; |
| } |
| CheckedTreeNode newChild = new CheckedTreeNode(metaData); |
| treeModel.insertNodeInto(newChild, node, node.getChildCount()); |
| } |
| resetCheckMark(root); |
| treeModel.setRoot(root); |
| treeModel.nodeChanged(root); |
| TreeUtil.expandAll(myTree); |
| myTree.setSelectionRow(0); |
| } |
| |
| public void selectIntention(String familyName) { |
| final CheckedTreeNode child = findChildRecursively(getRoot(), familyName); |
| if (child != null) { |
| final TreePath path = new TreePath(child.getPath()); |
| TreeUtil.selectPath(myTree, path); |
| } |
| } |
| |
| private static List<IntentionActionMetaData> sort(final List<IntentionActionMetaData> intentionsToShow) { |
| List<IntentionActionMetaData> copy = new ArrayList<IntentionActionMetaData>(intentionsToShow); |
| Collections.sort(copy, new Comparator<IntentionActionMetaData>() { |
| @Override |
| public int compare(final IntentionActionMetaData data1, final IntentionActionMetaData data2) { |
| String[] category1 = data1.myCategory; |
| String[] category2 = data2.myCategory; |
| int result = ArrayUtil.lexicographicCompare(category1, category2); |
| if (result!= 0) { |
| return result; |
| } |
| return data1.getFamily().compareTo(data2.getFamily()); |
| } |
| }); |
| return copy; |
| } |
| |
| private CheckedTreeNode getRoot() { |
| return (CheckedTreeNode)myTree.getModel().getRoot(); |
| } |
| |
| private boolean resetCheckMark(final CheckedTreeNode root) { |
| Object userObject = root.getUserObject(); |
| if (userObject instanceof IntentionActionMetaData) { |
| IntentionActionMetaData metaData = (IntentionActionMetaData)userObject; |
| Boolean b = myIntentionToCheckStatus.get(metaData); |
| boolean enabled = b == null ? false : b.booleanValue(); |
| root.setChecked(enabled); |
| return enabled; |
| } |
| else { |
| root.setChecked(false); |
| visitChildren(root, new CheckedNodeVisitor() { |
| @Override |
| public void visit(CheckedTreeNode node) { |
| if (resetCheckMark(node)) { |
| root.setChecked(true); |
| } |
| } |
| }); |
| return root.isChecked(); |
| } |
| } |
| |
| private static CheckedTreeNode findChild(CheckedTreeNode node, final String name) { |
| final Ref<CheckedTreeNode> found = new Ref<CheckedTreeNode>(); |
| visitChildren(node, new CheckedNodeVisitor() { |
| @Override |
| public void visit(CheckedTreeNode node) { |
| String text = getNodeText(node); |
| if (name.equals(text)) { |
| found.set(node); |
| } |
| } |
| }); |
| return found.get(); |
| } |
| |
| private static CheckedTreeNode findChildRecursively(CheckedTreeNode node, final String name) { |
| final Ref<CheckedTreeNode> found = new Ref<CheckedTreeNode>(); |
| visitChildren(node, new CheckedNodeVisitor() { |
| @Override |
| public void visit(CheckedTreeNode node) { |
| if (found.get() != null) return; |
| final Object userObject = node.getUserObject(); |
| if (userObject instanceof IntentionActionMetaData) { |
| String text = getNodeText(node); |
| if (name.equals(text)) { |
| found.set(node); |
| } |
| } else { |
| final CheckedTreeNode child = findChildRecursively(node, name); |
| if (child != null) { |
| found.set(child); |
| } |
| } |
| } |
| }); |
| return found.get(); |
| } |
| |
| private static String getNodeText(CheckedTreeNode node) { |
| final Object userObject = node.getUserObject(); |
| String text; |
| if (userObject instanceof String) { |
| text = (String)userObject; |
| } |
| else if (userObject instanceof IntentionActionMetaData) { |
| text = ((IntentionActionMetaData)userObject).getFamily(); |
| } |
| else { |
| text = "???"; |
| } |
| return text; |
| } |
| |
| public void apply() { |
| CheckedTreeNode root = getRoot(); |
| apply(root); |
| } |
| |
| private void refreshCheckStatus(final CheckedTreeNode root) { |
| Object userObject = root.getUserObject(); |
| if (userObject instanceof IntentionActionMetaData) { |
| IntentionActionMetaData actionMetaData = (IntentionActionMetaData)userObject; |
| myIntentionToCheckStatus.put(actionMetaData, root.isChecked()); |
| } |
| else { |
| visitChildren(root, new CheckedNodeVisitor() { |
| @Override |
| public void visit(CheckedTreeNode node) { |
| refreshCheckStatus(node); |
| } |
| }); |
| } |
| |
| } |
| |
| private static void apply(CheckedTreeNode root) { |
| Object userObject = root.getUserObject(); |
| if (userObject instanceof IntentionActionMetaData) { |
| IntentionActionMetaData actionMetaData = (IntentionActionMetaData)userObject; |
| IntentionManagerSettings.getInstance().setEnabled(actionMetaData, root.isChecked()); |
| } |
| else { |
| visitChildren(root, new CheckedNodeVisitor() { |
| @Override |
| public void visit(CheckedTreeNode node) { |
| apply(node); |
| } |
| }); |
| } |
| } |
| |
| public boolean isModified() { |
| return isModified(getRoot()); |
| } |
| |
| private static boolean isModified(CheckedTreeNode root) { |
| Object userObject = root.getUserObject(); |
| if (userObject instanceof IntentionActionMetaData) { |
| IntentionActionMetaData actionMetaData = (IntentionActionMetaData)userObject; |
| boolean enabled = IntentionManagerSettings.getInstance().isEnabled(actionMetaData); |
| return enabled != root.isChecked(); |
| } |
| else { |
| final boolean[] modified = new boolean[] { false }; |
| visitChildren(root, new CheckedNodeVisitor() { |
| @Override |
| public void visit(CheckedTreeNode node) { |
| modified[0] |= isModified(node); |
| } |
| }); |
| return modified[0]; |
| } |
| } |
| |
| public void dispose() { |
| myFilter.dispose(); |
| } |
| |
| public void setFilter(String filter){ |
| myFilter.setFilter(filter); |
| } |
| |
| public String getFilter() { |
| return myFilter.getFilter(); |
| } |
| |
| interface CheckedNodeVisitor { |
| void visit(CheckedTreeNode node); |
| } |
| private static void visitChildren(CheckedTreeNode node, CheckedNodeVisitor visitor) { |
| Enumeration children = node.children(); |
| while (children.hasMoreElements()) { |
| final CheckedTreeNode child = (CheckedTreeNode)children.nextElement(); |
| visitor.visit(child); |
| } |
| } |
| |
| private class MyFilterComponent extends FilterComponent { |
| private final TreeExpansionMonitor<DefaultMutableTreeNode> myExpansionMonitor = TreeExpansionMonitor.install(myTree); |
| |
| public MyFilterComponent() { |
| super("INTENTION_FILTER_HISTORY", 10); |
| } |
| |
| @Override |
| public void filter() { |
| final String filter = getFilter(); |
| if (filter != null && filter.length() > 0) { |
| if (!myExpansionMonitor.isFreeze()) { |
| myExpansionMonitor.freeze(); |
| } |
| } |
| IntentionSettingsTree.this.filter(filterModel(filter, true)); |
| if (myTree != null) { |
| List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(myTree); |
| ((DefaultTreeModel)myTree.getModel()).reload(); |
| TreeUtil.restoreExpandedPaths(myTree, expandedPaths); |
| } |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| myTree.setSelectionRow(0); |
| myTree.requestFocus(); |
| } |
| }); |
| TreeUtil.expandAll(myTree); |
| if (filter == null || filter.length() == 0) { |
| TreeUtil.collapseAll(myTree, 0); |
| myExpansionMonitor.restore(); |
| } |
| } |
| |
| @Override |
| protected void onlineFilter() { |
| final String filter = getFilter(); |
| if (filter != null && filter.length() > 0) { |
| if (!myExpansionMonitor.isFreeze()) { |
| myExpansionMonitor.freeze(); |
| } |
| } |
| IntentionSettingsTree.this.filter(filterModel(filter, true)); |
| TreeUtil.expandAll(myTree); |
| if (filter == null || filter.length() == 0) { |
| TreeUtil.collapseAll(myTree, 0); |
| myExpansionMonitor.restore(); |
| } |
| } |
| } |
| |
| public JPanel getToolbarPanel() { |
| return myNorthPanel; |
| } |
| } |