/* * Copyright 2000-2012 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. */ /* * Created by IntelliJ IDEA. * User: max * Date: Nov 4, 2001 * Time: 5:19:35 PM */ package com.intellij.codeInspection.ui; import com.intellij.codeInspection.CommonProblemDescriptor; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ex.*; import com.intellij.codeInspection.reference.RefElement; import com.intellij.codeInspection.reference.RefEntity; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FileStatus; import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionsConfigTreeComparator; import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.TreeSpeedSearch; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.containers.Convertor; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeWillExpandListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.util.*; public class InspectionTree extends Tree { private final HashSet<Object> myExpandedUserObjects; @NotNull private final GlobalInspectionContextImpl myContext; private SelectionPath mySelectionPath; public InspectionTree(@NotNull Project project, @NotNull GlobalInspectionContextImpl context) { super(new InspectionRootNode(project)); myContext = context; setCellRenderer(new CellRenderer()); setShowsRootHandles(true); UIUtil.setLineStyleAngled(this); addTreeWillExpandListener(new ExpandListener()); myExpandedUserObjects = new HashSet<Object>(); myExpandedUserObjects.add(project); TreeUtil.installActions(this); new TreeSpeedSearch(this, new Convertor<TreePath, String>() { @Override public String convert(TreePath o) { return InspectionsConfigTreeComparator.getDisplayTextToSort(o.getLastPathComponent().toString()); } }); addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { TreePath newSelection = e.getNewLeadSelectionPath(); if (newSelection != null) { mySelectionPath = new SelectionPath(newSelection); } } }); } public void removeAllNodes() { getRoot().removeAllChildren(); nodeStructureChanged(getRoot()); } public InspectionTreeNode getRoot() { return (InspectionTreeNode)getModel().getRoot(); } @Nullable public InspectionToolWrapper getSelectedToolWrapper() { final TreePath[] paths = getSelectionPaths(); if (paths == null) return null; InspectionToolWrapper toolWrapper = null; for (TreePath path : paths) { Object[] nodes = path.getPath(); for (int j = nodes.length - 1; j >= 0; j--) { Object node = nodes[j]; if (node instanceof InspectionNode) { InspectionToolWrapper wrapper = ((InspectionNode)node).getToolWrapper(); if (toolWrapper == null) { toolWrapper = wrapper; } else if (toolWrapper != wrapper) { return null; } break; } } } return toolWrapper; } @NotNull public RefEntity[] getSelectedElements() { TreePath[] selectionPaths = getSelectionPaths(); if (selectionPaths != null) { InspectionToolWrapper toolWrapper = getSelectedToolWrapper(); if (toolWrapper == null) return RefEntity.EMPTY_ELEMENTS_ARRAY; List<RefEntity> result = new ArrayList<RefEntity>(); for (TreePath selectionPath : selectionPaths) { final InspectionTreeNode node = (InspectionTreeNode)selectionPath.getLastPathComponent(); addElementsInNode(node, result); } return result.toArray(new RefEntity[result.size()]); } return RefEntity.EMPTY_ELEMENTS_ARRAY; } private static void addElementsInNode(InspectionTreeNode node, List<RefEntity> out) { if (!node.isValid()) return; if (node instanceof RefElementNode) { final RefEntity element = ((RefElementNode)node).getElement(); if (!out.contains(element)) { out.add(0, element); } } if (node instanceof ProblemDescriptionNode) { final RefEntity element = ((ProblemDescriptionNode)node).getElement(); if (!out.contains(element)) { out.add(0, element); } } final Enumeration children = node.children(); while (children.hasMoreElements()) { InspectionTreeNode child = (InspectionTreeNode)children.nextElement(); addElementsInNode(child, out); } } public CommonProblemDescriptor[] getSelectedDescriptors() { final InspectionToolWrapper toolWrapper = getSelectedToolWrapper(); if (getSelectionCount() == 0) return ProblemDescriptor.EMPTY_ARRAY; final TreePath[] paths = getSelectionPaths(); final LinkedHashSet<CommonProblemDescriptor> descriptors = new LinkedHashSet<CommonProblemDescriptor>(); for (TreePath path : paths) { Object node = path.getLastPathComponent(); traverseDescriptors((InspectionTreeNode)node, descriptors); } return descriptors.toArray(new CommonProblemDescriptor[descriptors.size()]); } private static void traverseDescriptors(InspectionTreeNode node, LinkedHashSet<CommonProblemDescriptor> descriptors){ if (node instanceof ProblemDescriptionNode) { descriptors.add(((ProblemDescriptionNode)node).getDescriptor()); } for(int i = node.getChildCount() - 1; i >= 0; i--){ traverseDescriptors((InspectionTreeNode)node.getChildAt(i), descriptors); } } private void nodeStructureChanged(InspectionTreeNode node) { ((DefaultTreeModel)getModel()).nodeStructureChanged(node); } private class ExpandListener implements TreeWillExpandListener { @Override public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { final InspectionTreeNode node = (InspectionTreeNode)event.getPath().getLastPathComponent(); final Object userObject = node.getUserObject(); //TODO: never re-sort if (node.isValid() && !myExpandedUserObjects.contains(userObject)) { sortChildren(node); nodeStructureChanged(node); } myExpandedUserObjects.add(userObject); // Smart expand if (node.getChildCount() == 1) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { expandPath(new TreePath(node.getPath())); } }); } } @Override public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { InspectionTreeNode node = (InspectionTreeNode)event.getPath().getLastPathComponent(); myExpandedUserObjects.remove(node.getUserObject()); } } public void restoreExpansionAndSelection() { restoreExpansionStatus((InspectionTreeNode)getModel().getRoot()); if (mySelectionPath != null) { mySelectionPath.restore(); } } private void restoreExpansionStatus(InspectionTreeNode node) { if (myExpandedUserObjects.contains(node.getUserObject())) { sortChildren(node); TreeNode[] pathToNode = node.getPath(); expandPath(new TreePath(pathToNode)); Enumeration children = node.children(); while (children.hasMoreElements()) { InspectionTreeNode childNode = (InspectionTreeNode)children.nextElement(); restoreExpansionStatus(childNode); } } } private static class CellRenderer extends ColoredTreeCellRenderer { /* private Project myProject; InspectionManagerEx myManager; public CellRenderer(Project project) { myProject = project; myManager = (InspectionManagerEx)InspectionManager.getInstance(myProject); }*/ @Override public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { InspectionTreeNode node = (InspectionTreeNode)value; append(node.toString(), patchAttr(node, appearsBold(node) ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : getMainForegroundAttributes(node))); int problemCount = node.getProblemCount(); if (!leaf) { append(" " + InspectionsBundle.message("inspection.problem.descriptor.count", problemCount), patchAttr(node, SimpleTextAttributes.GRAYED_ATTRIBUTES)); } if (!node.isValid()) { append(" " + InspectionsBundle.message("inspection.invalid.node.text"), patchAttr(node, SimpleTextAttributes.ERROR_ATTRIBUTES)); } else { setIcon(node.getIcon(expanded)); } } public static SimpleTextAttributes patchAttr(InspectionTreeNode node, SimpleTextAttributes attributes) { if (node.isResolved()) { return new SimpleTextAttributes(attributes.getBgColor(), attributes.getFgColor(), attributes.getWaveColor(), attributes.getStyle() | SimpleTextAttributes.STYLE_STRIKEOUT); } return attributes; } private static SimpleTextAttributes getMainForegroundAttributes(InspectionTreeNode node) { SimpleTextAttributes foreground = SimpleTextAttributes.REGULAR_ATTRIBUTES; if (node instanceof RefElementNode) { RefEntity refElement = ((RefElementNode)node).getElement(); if (refElement instanceof RefElement) { refElement = ((RefElement)refElement).getContainingEntry(); if (((RefElement)refElement).isEntry() && ((RefElement)refElement).isPermanentEntry()) { foreground = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.blue); } } } final FileStatus nodeStatus = node.getNodeStatus(); if (nodeStatus != FileStatus.NOT_CHANGED){ foreground = new SimpleTextAttributes(foreground.getBgColor(), nodeStatus.getColor(), foreground.getWaveColor(), foreground.getStyle()); } return foreground; } private static boolean appearsBold(Object node) { return ((InspectionTreeNode)node).appearsBold(); } } private void sortChildren(InspectionTreeNode node) { final List<TreeNode> children = TreeUtil.childrenToArray(node); Collections.sort(children, InspectionResultsViewComparator.getInstance()); node.removeAllChildren(); TreeUtil.addChildrenTo(node, children); ((DefaultTreeModel)getModel()).reload(node); } private class SelectionPath { private final Object[] myPath; private final int[] myIndicies; public SelectionPath(TreePath path) { myPath = path.getPath(); myIndicies = new int[myPath.length]; for (int i = 0; i < myPath.length - 1; i++) { InspectionTreeNode node = (InspectionTreeNode)myPath[i]; myIndicies[i + 1] = getChildIndex(node, (InspectionTreeNode)myPath[i + 1]); } } private int getChildIndex(InspectionTreeNode node, InspectionTreeNode child) { int idx = 0; Enumeration children = node.children(); while (children.hasMoreElements()) { InspectionTreeNode ch = (InspectionTreeNode)children.nextElement(); if (ch == child) break; idx++; } return idx; } public void restore() { getSelectionModel().removeSelectionPaths(getSelectionModel().getSelectionPaths()); TreeUtil.selectPath(InspectionTree.this, restorePath()); } private TreePath restorePath() { ArrayList<Object> newPath = new ArrayList<Object>(); newPath.add(getModel().getRoot()); restorePath(newPath, 1); return new TreePath(newPath.toArray(new InspectionTreeNode[newPath.size()])); } private void restorePath(ArrayList<Object> newPath, int idx) { if (idx >= myPath.length) return; InspectionTreeNode oldNode = (InspectionTreeNode)myPath[idx]; InspectionTreeNode newRoot = (InspectionTreeNode)newPath.get(idx - 1); InspectionResultsViewComparator comparator = InspectionResultsViewComparator.getInstance(); Enumeration children = newRoot.children(); while (children.hasMoreElements()) { InspectionTreeNode child = (InspectionTreeNode)children.nextElement(); if (comparator.compare(child, oldNode) == 0) { newPath.add(child); restorePath(newPath, idx + 1); return; } } // Exactly same element not found. Trying to select somewhat near. int count = newRoot.getChildCount(); if (count > 0) { if (myIndicies[idx] < count) { newPath.add(newRoot.getChildAt(myIndicies[idx])); } else { newPath.add(newRoot.getChildAt(count - 1)); } } } } @NotNull public GlobalInspectionContextImpl getContext() { return myContext; } }