/* * Copyright 2000-2014 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.profile.codeInspection.ui; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; import com.intellij.codeInsight.daemon.impl.SeverityUtil; import com.intellij.codeInsight.hint.HintUtil; import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ModifiableModel; import com.intellij.codeInspection.ex.*; import com.intellij.icons.AllIcons; import com.intellij.ide.CommonActionsManager; import com.intellij.ide.DefaultTreeExpander; import com.intellij.ide.TreeExpander; import com.intellij.ide.ui.search.SearchUtil; import com.intellij.ide.ui.search.SearchableOptionsRegistrar; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.profile.ApplicationProfileManager; import com.intellij.profile.DefaultProjectProfileManager; import com.intellij.profile.ProfileManager; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.profile.codeInspection.SeverityProvider; import com.intellij.profile.codeInspection.ui.filter.InspectionFilterAction; import com.intellij.profile.codeInspection.ui.filter.InspectionsFilter; import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionConfigTreeNode; import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionsConfigTreeComparator; import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionsConfigTreeRenderer; import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionsConfigTreeTable; import com.intellij.profile.codeInspection.ui.table.ScopesAndSeveritiesTable; import com.intellij.psi.search.scope.packageSet.NamedScope; import com.intellij.ui.*; import com.intellij.ui.components.JBLabel; import com.intellij.util.Alarm; import com.intellij.util.Function; import com.intellij.util.config.StorageAccessors; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.FocusManager; import javax.swing.*; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.io.StringReader; import java.util.*; import java.util.List; /** * User: anna * Date: 31-May-2006 */ public class SingleInspectionProfilePanel extends JPanel { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.InspectionToolsPanel"); @NonNls private static final String INSPECTION_FILTER_HISTORY = "INSPECTION_FILTER_HISTORY"; private static final String UNDER_CONSTRUCTION = InspectionsBundle.message("inspection.tool.description.under.construction.text"); @NonNls private static final String EMPTY_HTML = "<html><body></body></html>"; @NonNls private static final String VERTICAL_DIVIDER_PROPORTION = "VERTICAL_DIVIDER_PROPORTION"; @NonNls private static final String HORIZONTAL_DIVIDER_PROPORTION = "HORIZONTAL_DIVIDER_PROPORTION"; private final List<ToolDescriptors> myInitialToolDescriptors = new ArrayList<ToolDescriptors>(); private final InspectionConfigTreeNode myRoot = new InspectionConfigTreeNode(InspectionsBundle.message("inspection.root.node.title")); private final Alarm myAlarm = new Alarm(); private final StorageAccessors myProperties = StorageAccessors.createGlobal("SingleInspectionProfilePanel"); private final InspectionProjectProfileManager myProjectProfileManager; private InspectionProfileImpl mySelectedProfile; private JEditorPane myBrowser; private JPanel myOptionsPanel; private JPanel myInspectionProfilePanel = null; private FilterComponent myProfileFilter; private final InspectionsFilter myInspectionsFilter = new InspectionsFilter() { @Override protected void filterChanged() { // dont change if we dont visible if(myProfileFilter == null) { return; } filterTree(myProfileFilter.getFilter()); } }; private boolean myModified = false; private InspectionsConfigTreeTable myTreeTable; private TreeExpander myTreeExpander; @NotNull private String myCurrentProfileName; private boolean myIsInRestore = false; private boolean myShareProfile; private Splitter myRightSplitter; private Splitter myMainSplitter; private String[] myInitialScopesOrder; private Disposable myDisposable = new Disposable() { @Override public void dispose() {} }; public SingleInspectionProfilePanel(@NotNull InspectionProjectProfileManager projectProfileManager, @NotNull String inspectionProfileName, @NotNull ModifiableModel profile) { super(new BorderLayout()); myProjectProfileManager = projectProfileManager; mySelectedProfile = (InspectionProfileImpl)profile; myCurrentProfileName = inspectionProfileName; myShareProfile = profile.getProfileManager() == projectProfileManager; } private static VisibleTreeState getExpandedNodes(InspectionProfileImpl profile) { if (profile.getProfileManager() instanceof ApplicationProfileManager) { return AppInspectionProfilesVisibleTreeState.getInstance().getVisibleTreeState(profile); } else { DefaultProjectProfileManager projectProfileManager = (DefaultProjectProfileManager)profile.getProfileManager(); return ProjectInspectionProfilesVisibleTreeState.getInstance(projectProfileManager.getProject()).getVisibleTreeState(profile); } } @Nullable public static ModifiableModel createNewProfile(final int initValue, ModifiableModel selectedProfile, JPanel parent, String profileName, Set<String> existingProfileNames, @NotNull Project project) { profileName = Messages.showInputDialog(parent, profileName, "Create New Inspection Profile", Messages.getQuestionIcon()); if (profileName == null) return null; final ProfileManager profileManager = selectedProfile.getProfileManager(); if (existingProfileNames.contains(profileName)) { Messages.showErrorDialog(InspectionsBundle.message("inspection.unable.to.create.profile.message", profileName), InspectionsBundle.message("inspection.unable.to.create.profile.dialog.title")); return null; } InspectionProfileImpl inspectionProfile = new InspectionProfileImpl(profileName, InspectionToolRegistrar.getInstance(), profileManager); if (initValue == -1) { inspectionProfile.initInspectionTools(project); ModifiableModel profileModifiableModel = inspectionProfile.getModifiableModel(); final InspectionToolWrapper[] profileEntries = profileModifiableModel.getInspectionTools(null); for (InspectionToolWrapper toolWrapper : profileEntries) { profileModifiableModel.disableTool(toolWrapper.getShortName(), null, project); } profileModifiableModel.setProjectLevel(false); profileModifiableModel.setModified(true); return profileModifiableModel; } else if (initValue == 0) { inspectionProfile.copyFrom(selectedProfile); inspectionProfile.setName(profileName); inspectionProfile.initInspectionTools(project); inspectionProfile.setModified(true); return inspectionProfile; } return null; } @Nullable private static InspectionConfigTreeNode findNodeByKey(String name, InspectionConfigTreeNode root) { for (int i = 0; i < root.getChildCount(); i++) { final InspectionConfigTreeNode child = (InspectionConfigTreeNode)root.getChildAt(i); final Descriptor descriptor = child.getDefaultDescriptor(); if (descriptor != null) { if (descriptor.getKey().toString().equals(name)) { return child; } } else { final InspectionConfigTreeNode node = findNodeByKey(name, child); if (node != null) return node; } } return null; } public static String renderSeverity(HighlightSeverity severity) { return StringUtil.capitalizeWords(severity.getName().toLowerCase(), true); } private static void updateUpHierarchy(final InspectionConfigTreeNode parent) { if (parent != null) { parent.dropCache(); updateUpHierarchy((InspectionConfigTreeNode)parent.getParent()); } } private static boolean isDescriptorAccepted(Descriptor descriptor, @NonNls String filter, final boolean forceInclude, final List<Set<String>> keySetList, final Set<String> quoted) { filter = filter.toLowerCase(); if (StringUtil.containsIgnoreCase(descriptor.getText(), filter)) { return true; } final String[] groupPath = descriptor.getGroup(); for (String group : groupPath) { if (StringUtil.containsIgnoreCase(group, filter)) { return true; } } for (String stripped : quoted) { if (StringUtil.containsIgnoreCase(descriptor.getText(),stripped)) { return true; } for (String group : groupPath) { if (StringUtil.containsIgnoreCase(group,stripped)) { return true; } } final String description = descriptor.getToolWrapper().loadDescription(); if (description != null && StringUtil.containsIgnoreCase(description.toLowerCase(), stripped)) { if (!forceInclude) return true; } else if (forceInclude) return false; } for (Set<String> keySet : keySetList) { if (keySet.contains(descriptor.getKey().toString())) { if (!forceInclude) { return true; } } else { if (forceInclude) { return false; } } } return forceInclude; } private static void setConfigPanel(final JPanel configPanelAnchor, final ScopeToolState state) { configPanelAnchor.removeAll(); final JComponent additionalConfigPanel = state.getAdditionalConfigPanel(); if (additionalConfigPanel != null) { final JScrollPane pane = ScrollPaneFactory.createScrollPane(additionalConfigPanel, SideBorder.NONE); FocusManager.getCurrentManager().addPropertyChangeListener("focusOwner", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (!(evt.getNewValue() instanceof JComponent)) { return; } final JComponent component = (JComponent)evt.getNewValue(); if (component.isAncestorOf(pane)) { pane.scrollRectToVisible(component.getBounds()); } } }); configPanelAnchor.add(pane); } UIUtil.setEnabled(configPanelAnchor, state.isEnabled(), true); } private static InspectionConfigTreeNode getGroupNode(InspectionConfigTreeNode root, String[] groupPath) { InspectionConfigTreeNode currentRoot = root; for (final String group : groupPath) { currentRoot = getGroupNode(currentRoot, group); } return currentRoot; } private static InspectionConfigTreeNode getGroupNode(InspectionConfigTreeNode root, String group) { final int childCount = root.getChildCount(); for (int i = 0; i < childCount; i++) { InspectionConfigTreeNode child = (InspectionConfigTreeNode)root.getChildAt(i); if (group.equals(child.getUserObject())) { return child; } } InspectionConfigTreeNode child = new InspectionConfigTreeNode(group); root.add(child); return child; } private static void copyUsedSeveritiesIfUndefined(final ModifiableModel selectedProfile, final ProfileManager profileManager) { final SeverityRegistrar registrar = ((SeverityProvider)profileManager).getSeverityRegistrar(); final Set<HighlightSeverity> severities = ((InspectionProfileImpl)selectedProfile).getUsedSeverities(); for (Iterator<HighlightSeverity> iterator = severities.iterator(); iterator.hasNext();) { HighlightSeverity severity = iterator.next(); if (registrar.isSeverityValid(severity.getName())) { iterator.remove(); } } if (!severities.isEmpty()) { final SeverityRegistrar oppositeRegister = ((SeverityProvider)selectedProfile.getProfileManager()).getSeverityRegistrar(); for (HighlightSeverity severity : severities) { final TextAttributesKey attributesKey = TextAttributesKey.find(severity.getName()); final TextAttributes textAttributes = oppositeRegister.getTextAttributesBySeverity(severity); if (textAttributes == null) { continue; } HighlightInfoType.HighlightInfoTypeImpl info = new HighlightInfoType.HighlightInfoTypeImpl(severity, attributesKey); registrar.registerSeverity(new SeverityRegistrar.SeverityBasedTextAttributes(textAttributes.clone(), info), textAttributes.getErrorStripeColor()); } } } private void initUI() { myInspectionProfilePanel = createInspectionProfileSettingsPanel(); add(myInspectionProfilePanel, BorderLayout.CENTER); UserActivityWatcher userActivityWatcher = new UserActivityWatcher(); userActivityWatcher.addUserActivityListener(new UserActivityListener() { @Override public void stateChanged() { //invoke after all other listeners SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (mySelectedProfile == null) return; //panel was disposed updateProperSettingsForSelection(); wereToolSettingsModified(); } }); } }); userActivityWatcher.register(myOptionsPanel); updateSelectedProfileState(); reset(); } private void updateSelectedProfileState() { if (mySelectedProfile == null) return; restoreTreeState(); repaintTableData(); updateSelection(); } public void updateSelection() { if (myTreeTable != null) { final TreePath selectionPath = myTreeTable.getTree().getSelectionPath(); if (selectionPath != null) { TreeUtil.selectNode(myTreeTable.getTree(), (TreeNode) selectionPath.getLastPathComponent()); final int rowForPath = myTreeTable.getTree().getRowForPath(selectionPath); TableUtil.selectRows(myTreeTable, new int[]{rowForPath}); scrollToCenter(); } } } private void loadDescriptorsConfigs(boolean onlyModified) { for (ToolDescriptors toolDescriptors : myInitialToolDescriptors) { loadDescriptorConfig(toolDescriptors.getDefaultDescriptor(), onlyModified); for (Descriptor descriptor : toolDescriptors.getNonDefaultDescriptors()) { loadDescriptorConfig(descriptor, onlyModified); } } } private void loadDescriptorConfig(Descriptor descriptor, boolean ifModifier) { if (!ifModifier || mySelectedProfile.isProperSetting(descriptor.getKey().toString())) { descriptor.loadConfig(); } } private void wereToolSettingsModified() { for (final ToolDescriptors toolDescriptor : myInitialToolDescriptors) { Descriptor desc = toolDescriptor.getDefaultDescriptor(); if (wereToolSettingsModified(desc, true)) return; List<Descriptor> descriptors = toolDescriptor.getNonDefaultDescriptors(); for (Descriptor descriptor : descriptors) { if (wereToolSettingsModified(descriptor, false)) return; } } myModified = false; } private boolean wereToolSettingsModified(Descriptor descriptor, boolean isDefault) { if (!mySelectedProfile.isToolEnabled(descriptor.getKey(), descriptor.getScope(), myProjectProfileManager.getProject())) { return false; } Element oldConfig = descriptor.getConfig(); if (oldConfig == null) return false; ScopeToolState state = null; if (isDefault) { state = mySelectedProfile.getToolDefaultState(descriptor.getKey().toString(), myProjectProfileManager.getProject()); } else { for (ScopeToolState candidate : mySelectedProfile .getNonDefaultTools(descriptor.getKey().toString(), myProjectProfileManager.getProject())) { final String scope = descriptor.getScopeName(); if (Comparing.equal(candidate.getScopeName(), scope)) { state = candidate; break; } } } if (state == null) { return true; } Element newConfig = Descriptor.createConfigElement(state.getTool()); if (!JDOMUtil.areElementsEqual(oldConfig, newConfig)) { myAlarm.cancelAllRequests(); myAlarm.addRequest(new Runnable() { @Override public void run() { myTreeTable.repaint(); } }, 300); myModified = true; return true; } return false; } private void updateProperSettingsForSelection() { final TreePath selectionPath = myTreeTable.getTree().getSelectionPath(); if (selectionPath != null) { InspectionConfigTreeNode node = (InspectionConfigTreeNode)selectionPath.getLastPathComponent(); final Descriptor descriptor = node.getDefaultDescriptor(); if (descriptor != null) { final boolean properSetting = mySelectedProfile.isProperSetting(descriptor.getKey().toString()); if (node.isProperSetting() != properSetting) { myAlarm.cancelAllRequests(); myAlarm.addRequest(new Runnable() { @Override public void run() { myTreeTable.repaint(); } }, 300); node.dropCache(); updateUpHierarchy((InspectionConfigTreeNode)node.getParent()); } } } } private void initToolStates() { final InspectionProfileImpl profile = mySelectedProfile; if (profile == null) return; myInitialToolDescriptors.clear(); final Project project = myProjectProfileManager.getProject(); for (final ScopeToolState state : profile.getDefaultStates(myProjectProfileManager.getProject())) { if (!accept(state.getTool())) continue; myInitialToolDescriptors.add(ToolDescriptors.fromScopeToolState(state, profile, project)); } myInitialScopesOrder = mySelectedProfile.getScopesOrder(); } protected boolean accept(InspectionToolWrapper entry) { return entry.getDefaultLevel() != HighlightDisplayLevel.NON_SWITCHABLE_ERROR; } private void postProcessModification() { wereToolSettingsModified(); //resetup configs for (ScopeToolState state : mySelectedProfile.getAllTools(myProjectProfileManager.getProject())) { state.resetConfigPanel(); } fillTreeData(myProfileFilter.getFilter(), true); repaintTableData(); updateOptionsAndDescriptionPanel(myTreeTable.getTree().getSelectionPaths()); } public void setFilter(String filter) { myProfileFilter.setFilter(filter); } private void filterTree(@Nullable String filter) { if (myTreeTable != null) { getExpandedNodes(mySelectedProfile).saveVisibleState(myTreeTable.getTree()); fillTreeData(filter, true); reloadModel(); restoreTreeState(); if (myTreeTable.getTree().getSelectionPath() == null) { TreeUtil.selectFirstNode(myTreeTable.getTree()); } } } private void filterTree() { filterTree(myProfileFilter != null ? myProfileFilter.getFilter() : null); } private void reloadModel() { try { myIsInRestore = true; ((DefaultTreeModel)myTreeTable.getTree().getModel()).reload(); } finally { myIsInRestore = false; } } private void restoreTreeState() { try { myIsInRestore = true; getExpandedNodes(mySelectedProfile).restoreVisibleState(myTreeTable.getTree()); } finally { myIsInRestore = false; } } private ActionToolbar createTreeToolbarPanel() { final CommonActionsManager actionManager = CommonActionsManager.getInstance(); DefaultActionGroup actions = new DefaultActionGroup(); actions.add(new InspectionFilterAction(mySelectedProfile, myInspectionsFilter, myProjectProfileManager.getProject())); actions.addSeparator(); actions.add(actionManager.createExpandAllAction(myTreeExpander, myTreeTable)); actions.add(actionManager.createCollapseAllAction(myTreeExpander, myTreeTable)); actions.add(new DumbAwareAction("Reset to Empty", "Reset to empty", AllIcons.Actions.Reset_to_empty){ @Override public void update(@NotNull AnActionEvent e) { e.getPresentation().setEnabled(mySelectedProfile != null && mySelectedProfile.isExecutable(myProjectProfileManager.getProject())); } @Override public void actionPerformed(@NotNull AnActionEvent e) { mySelectedProfile.resetToEmpty(e.getProject()); loadDescriptorsConfigs(false); postProcessModification(); } }); actions.add(new AdvancedSettingsAction(myProjectProfileManager.getProject(), myRoot) { @Override protected InspectionProfileImpl getInspectionProfile() { return mySelectedProfile; } @Override protected void postProcessModification() { loadDescriptorsConfigs(true); SingleInspectionProfilePanel.this.postProcessModification(); } }); final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actions, true); actionToolbar.setTargetComponent(this); return actionToolbar; } private void repaintTableData() { if (myTreeTable != null) { getExpandedNodes(mySelectedProfile).saveVisibleState(myTreeTable.getTree()); reloadModel(); restoreTreeState(); } } public void selectInspectionTool(String name) { final InspectionConfigTreeNode node = findNodeByKey(name, myRoot); if (node != null) { TreeUtil.selectNode(myTreeTable.getTree(), node); final int rowForPath = myTreeTable.getTree().getRowForPath(new TreePath(node.getPath())); TableUtil.selectRows(myTreeTable, new int[]{rowForPath}); scrollToCenter(); } } private void scrollToCenter() { ListSelectionModel selectionModel = myTreeTable.getSelectionModel(); int maxSelectionIndex = selectionModel.getMaxSelectionIndex(); final int maxColumnSelectionIndex = Math.max(0, myTreeTable.getColumnModel().getSelectionModel().getMinSelectionIndex()); Rectangle maxCellRect = myTreeTable.getCellRect(maxSelectionIndex, maxColumnSelectionIndex, false); final Point selectPoint = maxCellRect.getLocation(); final int allHeight = myTreeTable.getVisibleRect().height; myTreeTable.scrollRectToVisible(new Rectangle(new Point(0, Math.max(0, selectPoint.y - allHeight / 2)), new Dimension(0, allHeight))); } private JScrollPane initTreeScrollPane() { fillTreeData(null, true); final InspectionsConfigTreeRenderer renderer = new InspectionsConfigTreeRenderer(){ @Override protected String getFilter() { return myProfileFilter != null ? myProfileFilter.getFilter() : null; } }; myTreeTable = InspectionsConfigTreeTable.create(new InspectionsConfigTreeTable.InspectionsConfigTreeTableSettings(myRoot, myProjectProfileManager.getProject()) { @Override protected void onChanged(final InspectionConfigTreeNode node) { updateUpHierarchy((InspectionConfigTreeNode)node.getParent()); } @Override public void updateRightPanel() { updateOptionsAndDescriptionPanel(); } @Override public InspectionProfileImpl getInspectionProfile() { return mySelectedProfile; } }, myDisposable); myTreeTable.setTreeCellRenderer(renderer); myTreeTable.setRootVisible(false); UIUtil.setLineStyleAngled(myTreeTable.getTree()); TreeUtil.installActions(myTreeTable.getTree()); myTreeTable.getTree().addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { if (myTreeTable.getTree().getSelectionPaths() != null) { updateOptionsAndDescriptionPanel(myTreeTable.getTree().getSelectionPaths()); } else { initOptionsAndDescriptionPanel(); } if (!myIsInRestore) { InspectionProfileImpl selected = mySelectedProfile; if (selected != null) { InspectionProfileImpl baseProfile = (InspectionProfileImpl)selected.getParentProfile(); if (baseProfile != null) { getExpandedNodes(baseProfile).setSelectionPaths(myTreeTable.getTree().getSelectionPaths()); } getExpandedNodes(selected).setSelectionPaths(myTreeTable.getTree().getSelectionPaths()); } } } }); myTreeTable.addMouseListener(new PopupHandler() { @Override public void invokePopup(Component comp, int x, int y) { final int[] selectionRows = myTreeTable.getTree().getSelectionRows(); if (selectionRows != null && myTreeTable.getTree().getPathForLocation(x, y) != null && Arrays.binarySearch(selectionRows, myTreeTable.getTree().getRowForLocation(x, y)) > -1) { compoundPopup().show(comp, x, y); } } }); new TreeSpeedSearch(myTreeTable.getTree(), new Convertor<TreePath, String>() { @Override public String convert(TreePath o) { final InspectionConfigTreeNode node = (InspectionConfigTreeNode)o.getLastPathComponent(); final Descriptor descriptor = node.getDefaultDescriptor(); return descriptor != null ? InspectionsConfigTreeComparator.getDisplayTextToSort(descriptor.getText()) : InspectionsConfigTreeComparator .getDisplayTextToSort(node.getGroupName()); } }); final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTreeTable); myTreeTable.getTree().setShowsRootHandles(true); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); TreeUtil.collapseAll(myTreeTable.getTree(), 1); myTreeTable.getTree().addTreeExpansionListener(new TreeExpansionListener() { @Override public void treeCollapsed(TreeExpansionEvent event) { InspectionProfileImpl selected = mySelectedProfile; final InspectionConfigTreeNode node = (InspectionConfigTreeNode)event.getPath().getLastPathComponent(); final InspectionProfileImpl parentProfile = (InspectionProfileImpl)selected.getParentProfile(); if (parentProfile != null) { getExpandedNodes(parentProfile).saveVisibleState(myTreeTable.getTree()); } getExpandedNodes(selected).saveVisibleState(myTreeTable.getTree()); } @Override public void treeExpanded(TreeExpansionEvent event) { InspectionProfileImpl selected = mySelectedProfile; if (selected != null) { final InspectionConfigTreeNode node = (InspectionConfigTreeNode)event.getPath().getLastPathComponent(); final InspectionProfileImpl parentProfile = (InspectionProfileImpl)selected.getParentProfile(); if (parentProfile != null) { getExpandedNodes(parentProfile).expandNode(node); } getExpandedNodes(selected).expandNode(node); } } }); myTreeExpander = new DefaultTreeExpander(myTreeTable.getTree()) { @Override public boolean canExpand() { return myTreeTable.isShowing(); } @Override public boolean canCollapse() { return myTreeTable.isShowing(); } }; myProfileFilter = new MyFilterComponent(); return scrollPane; } private JPopupMenu compoundPopup() { final DefaultActionGroup group = new DefaultActionGroup(); final SeverityRegistrar severityRegistrar = ((SeverityProvider)mySelectedProfile.getProfileManager()).getOwnSeverityRegistrar(); TreeSet<HighlightSeverity> severities = new TreeSet<HighlightSeverity>(severityRegistrar); severities.add(HighlightSeverity.ERROR); severities.add(HighlightSeverity.WARNING); severities.add(HighlightSeverity.WEAK_WARNING); final Collection<SeverityRegistrar.SeverityBasedTextAttributes> infoTypes = SeverityUtil.getRegisteredHighlightingInfoTypes(severityRegistrar); for (SeverityRegistrar.SeverityBasedTextAttributes info : infoTypes) { severities.add(info.getSeverity()); } for (HighlightSeverity severity : severities) { final HighlightDisplayLevel level = HighlightDisplayLevel.find(severity); group.add(new AnAction(renderSeverity(severity), renderSeverity(severity), level.getIcon()) { @Override public void actionPerformed(@NotNull AnActionEvent e) { setNewHighlightingLevel(level); } @Override public boolean isDumbAware() { return true; } }); } group.add(AnSeparator.getInstance()); ActionPopupMenu menu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group); return menu.getComponent(); } @NotNull public InspectionsFilter getInspectionsFilter() { return myInspectionsFilter; } private void fillTreeData(@Nullable String filter, boolean forceInclude) { if (mySelectedProfile == null) return; myRoot.removeAllChildren(); myRoot.dropCache(); List<Set<String>> keySetList = new ArrayList<Set<String>>(); final Set<String> quoted = new HashSet<String>(); if (filter != null && !filter.isEmpty()) { keySetList.addAll(SearchUtil.findKeys(filter, quoted)); } Project project = myProjectProfileManager.getProject(); final boolean emptyFilter = myInspectionsFilter.isEmptyFilter(); for (ToolDescriptors toolDescriptors : myInitialToolDescriptors) { final Descriptor descriptor = toolDescriptors.getDefaultDescriptor(); if (filter != null && !filter.isEmpty() && !isDescriptorAccepted(descriptor, filter, forceInclude, keySetList, quoted)) { continue; } if (!emptyFilter && !myInspectionsFilter.matches(mySelectedProfile.getTools(toolDescriptors.getDefaultDescriptor().getKey().toString(), project))) { continue; } final InspectionConfigTreeNode node = new InspectionConfigTreeNode(toolDescriptors); getGroupNode(myRoot, toolDescriptors.getDefaultDescriptor().getGroup()).add(node); myRoot.dropCache(); } if (filter != null && forceInclude && myRoot.getChildCount() == 0) { final Set<String> filters = SearchableOptionsRegistrar.getInstance().getProcessedWords(filter); if (filters.size() > 1 || !quoted.isEmpty()) { fillTreeData(filter, false); } } TreeUtil.sort(myRoot, new InspectionsConfigTreeComparator()); } // TODO 134099: see IntentionDescriptionPanel#readHTML private boolean readHTML(String text) { try { myBrowser.read(new StringReader(text), null); return true; } catch (IOException ignored) { return false; } } // TODO 134099: see IntentionDescriptionPanel#toHTML private String toHTML(String text) { final HintHint hintHint = new HintHint(myBrowser, new Point(0, 0)); hintHint.setFont(UIUtil.getLabelFont()); return HintUtil.prepareHintText(text, hintHint); } private void updateOptionsAndDescriptionPanel(final TreePath... paths) { if (mySelectedProfile == null || paths == null || paths.length == 0) { return; } final TreePath path = paths[0]; if (path == null) return; final List<InspectionConfigTreeNode> nodes = InspectionsAggregationUtil.getInspectionsNodes(paths); if (!nodes.isEmpty()) { final InspectionConfigTreeNode singleNode = paths.length == 1 && ((InspectionConfigTreeNode)paths[0].getLastPathComponent()).getDefaultDescriptor() != null ? ContainerUtil.getFirstItem(nodes) : null; if (singleNode != null) { if (singleNode.getDefaultDescriptor().loadDescription() != null) { // need this in order to correctly load plugin-supplied descriptions final Descriptor defaultDescriptor = singleNode.getDefaultDescriptor(); final String description = defaultDescriptor.loadDescription(); try { if (!readHTML(SearchUtil.markup(toHTML(description), myProfileFilter.getFilter()))) { readHTML(toHTML("<b>" + UNDER_CONSTRUCTION + "</b>")); } } catch (Throwable t) { LOG.error("Failed to load description for: " + defaultDescriptor.getToolWrapper().getTool().getClass() + "; description: " + description, t); } } else { readHTML(toHTML("Can't find inspection description.")); } } else { readHTML(toHTML("Multiple inspections are selected. You can edit them as a single inspection.")); } myOptionsPanel.removeAll(); final Project project = myProjectProfileManager.getProject(); final JPanel severityPanel = new JPanel(new GridBagLayout()); final double severityPanelWeightY; final JPanel configPanelAnchor = new JPanel(new GridLayout()); final Set<String> scopesNames = new THashSet<String>(); for (final InspectionConfigTreeNode node : nodes) { final List<ScopeToolState> nonDefaultTools = mySelectedProfile.getNonDefaultTools(node.getDefaultDescriptor().getKey().toString(), project); for (final ScopeToolState tool : nonDefaultTools) { scopesNames.add(tool.getScopeName()); } } if (scopesNames.isEmpty()) { final LevelChooserAction severityLevelChooser = new LevelChooserAction(mySelectedProfile) { @Override protected void onChosen(final HighlightSeverity severity) { final HighlightDisplayLevel level = HighlightDisplayLevel.find(severity); for (final InspectionConfigTreeNode node : nodes) { final HighlightDisplayKey key = node.getDefaultDescriptor().getKey(); final NamedScope scope = node.getDefaultDescriptor().getScope(); final boolean toUpdate = mySelectedProfile.getErrorLevel(key, scope, project) != level; mySelectedProfile.setErrorLevel(key, level, null, project); if (toUpdate) node.dropCache(); } myTreeTable.updateUI(); } }; final HighlightSeverity severity = ScopesAndSeveritiesTable.getSeverity(ContainerUtil.map(nodes, new Function<InspectionConfigTreeNode, ScopeToolState>() { @Override public ScopeToolState fun(InspectionConfigTreeNode node) { return node.getDefaultDescriptor().getState(); } })); severityLevelChooser.setChosen(severity); final ScopesChooser scopesChooser = new ScopesChooser(ContainerUtil.map(nodes, new Function<InspectionConfigTreeNode, Descriptor>() { @Override public Descriptor fun(final InspectionConfigTreeNode node) { return node.getDefaultDescriptor(); } }), mySelectedProfile, project, null) { @Override protected void onScopesOrderChanged() { myTreeTable.updateUI(); updateOptionsAndDescriptionPanel(); } @Override protected void onScopeAdded() { myTreeTable.updateUI(); updateOptionsAndDescriptionPanel(); } }; severityPanel.add(new JLabel(InspectionsBundle.message("inspection.severity")), new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(10, 0, 10, 0), 0, 0)); final JComponent severityLevelChooserComponent = severityLevelChooser.createCustomComponent(severityLevelChooser.getTemplatePresentation()); severityPanel.add(severityLevelChooserComponent, new GridBagConstraints(1, 0, 1, 1, 0, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(10, 0, 10, 0), 0, 0)); final JComponent scopesChooserComponent = scopesChooser.createCustomComponent(scopesChooser.getTemplatePresentation()); severityPanel.add(scopesChooserComponent, new GridBagConstraints(2, 0, 1, 1, 0, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(10, 0, 10, 0), 0, 0)); final JLabel label = new JLabel("", SwingConstants.RIGHT); severityPanel.add(label, new GridBagConstraints(3, 0, 1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets(2, 0, 2, 0), 0, 0)); severityPanelWeightY = 0.0; if (singleNode != null) { setConfigPanel(configPanelAnchor, mySelectedProfile.getToolDefaultState(singleNode.getDefaultDescriptor().getKey().toString(), project)); } } else { if (singleNode != null) { for (final Descriptor descriptor : singleNode.getDescriptors().getNonDefaultDescriptors()) { descriptor.loadConfig(); } } final JTable scopesAndScopesAndSeveritiesTable = new ScopesAndSeveritiesTable(new ScopesAndSeveritiesTable.TableSettings(nodes, mySelectedProfile, project) { @Override protected void onScopeChosen(@NotNull final ScopeToolState state) { setConfigPanel(configPanelAnchor, state); configPanelAnchor.revalidate(); configPanelAnchor.repaint(); } @Override protected void onSettingsChanged() { myTreeTable.updateUI(); } @Override protected void onScopeAdded() { myTreeTable.updateUI(); updateOptionsAndDescriptionPanel(); } @Override protected void onScopesOrderChanged() { myTreeTable.updateUI(); updateOptionsAndDescriptionPanel(); } @Override protected void onScopeRemoved(final int scopesCount) { myTreeTable.updateUI(); if (scopesCount == 1) { updateOptionsAndDescriptionPanel(); } } }); final ToolbarDecorator wrappedTable = ToolbarDecorator.createDecorator(scopesAndScopesAndSeveritiesTable).disableUpDownActions().setRemoveActionUpdater( new AnActionButtonUpdater() { @Override public boolean isEnabled(AnActionEvent e) { final int selectedRow = scopesAndScopesAndSeveritiesTable.getSelectedRow(); final int rowCount = scopesAndScopesAndSeveritiesTable.getRowCount(); return rowCount - 1 != selectedRow; } }); final JPanel panel = wrappedTable.createPanel(); panel.setMinimumSize(new Dimension(getMinimumSize().width, 3 * scopesAndScopesAndSeveritiesTable.getRowHeight())); severityPanel.add(new JBLabel("Severity by Scope"), new GridBagConstraints(0, 0, 1, 1, 1.0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(5, 0, 2, 10), 0, 0)); severityPanel.add(panel, new GridBagConstraints(0, 1, 1, 1, 0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); severityPanelWeightY = 0.3; } myOptionsPanel.add(severityPanel, new GridBagConstraints(0, 0, 1, 1, 1.0, severityPanelWeightY, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); if (configPanelAnchor.getComponentCount() != 0) { configPanelAnchor.setBorder(IdeBorderFactory.createTitledBorder("Options", false, new Insets(7, 0, 0, 0))); } GuiUtils.enableChildren(myOptionsPanel, isThoughOneNodeEnabled(nodes)); if (configPanelAnchor.getComponentCount() != 0 || scopesNames.isEmpty()) { myOptionsPanel.add(configPanelAnchor, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); } myOptionsPanel.revalidate(); } else { initOptionsAndDescriptionPanel(); } myOptionsPanel.repaint(); } private boolean isThoughOneNodeEnabled(final List<InspectionConfigTreeNode> nodes) { final Project project = myProjectProfileManager.getProject(); for (final InspectionConfigTreeNode node : nodes) { final String toolId = node.getDefaultDescriptor().getKey().toString(); if (mySelectedProfile.getTools(toolId, project).isEnabled()) { return true; } } return false; } private void updateOptionsAndDescriptionPanel() { final TreePath[] paths = myTreeTable.getTree().getSelectionPaths(); if (paths != null) { updateOptionsAndDescriptionPanel(paths); } else { initOptionsAndDescriptionPanel(); } } private void initOptionsAndDescriptionPanel() { myOptionsPanel.removeAll(); readHTML(EMPTY_HTML); myOptionsPanel.validate(); myOptionsPanel.repaint(); } public boolean setSelectedProfileModified(boolean modified) { mySelectedProfile.setModified(modified); return modified; } public ModifiableModel getSelectedProfile() { return mySelectedProfile; } private void setSelectedProfile(final ModifiableModel modifiableModel) { if (mySelectedProfile == modifiableModel) return; mySelectedProfile = (InspectionProfileImpl)modifiableModel; if (mySelectedProfile != null) { myCurrentProfileName = mySelectedProfile.getName(); } initToolStates(); filterTree(); } @Override public Dimension getPreferredSize() { return new Dimension(700, 500); } public void disposeUI() { if (myInspectionProfilePanel == null) { return; } myProperties.setFloat(VERTICAL_DIVIDER_PROPORTION, myMainSplitter.getProportion()); myProperties.setFloat(HORIZONTAL_DIVIDER_PROPORTION, myRightSplitter.getProportion()); myAlarm.cancelAllRequests(); myProfileFilter.dispose(); if (mySelectedProfile != null) { for (ScopeToolState state : mySelectedProfile.getAllTools(myProjectProfileManager.getProject())) { state.resetConfigPanel(); } } mySelectedProfile = null; Disposer.dispose(myDisposable); myDisposable = null; } private JPanel createInspectionProfileSettingsPanel() { myBrowser = new JEditorPane(UIUtil.HTML_MIME, EMPTY_HTML); myBrowser.setEditable(false); myBrowser.setBorder(IdeBorderFactory.createEmptyBorder(5, 5, 5, 5)); myBrowser.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); initToolStates(); fillTreeData(myProfileFilter != null ? myProfileFilter.getFilter() : null, true); JPanel descriptionPanel = new JPanel(new BorderLayout()); descriptionPanel.setBorder(IdeBorderFactory.createTitledBorder(InspectionsBundle.message("inspection.description.title"), false, new Insets(2, 0, 0, 0))); descriptionPanel.add(ScrollPaneFactory.createScrollPane(myBrowser), BorderLayout.CENTER); myRightSplitter = new Splitter(true); myRightSplitter.setFirstComponent(descriptionPanel); myRightSplitter.setProportion(myProperties.getFloat(HORIZONTAL_DIVIDER_PROPORTION, 0.5f)); myOptionsPanel = new JPanel(new GridBagLayout()); initOptionsAndDescriptionPanel(); myRightSplitter.setSecondComponent(myOptionsPanel); myRightSplitter.setHonorComponentsMinimumSize(true); final JScrollPane tree = initTreeScrollPane(); final JPanel northPanel = new JPanel(new GridBagLayout()); northPanel.setBorder(IdeBorderFactory.createEmptyBorder(2, 0, 2, 0)); myProfileFilter.setPreferredSize(new Dimension(20, myProfileFilter.getPreferredSize().height)); northPanel.add(myProfileFilter, new GridBagConstraints(0, 0, 1, 1, 0.5, 1, GridBagConstraints.BASELINE_TRAILING, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); northPanel.add(createTreeToolbarPanel().getComponent(), new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.BASELINE_LEADING, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); myMainSplitter = new Splitter(false, myProperties.getFloat(VERTICAL_DIVIDER_PROPORTION, 0.5f), 0.01f, 0.99f); myMainSplitter.setFirstComponent(tree); myMainSplitter.setSecondComponent(myRightSplitter); myMainSplitter.setHonorComponentsMinimumSize(false); final JPanel panel = new JPanel(new BorderLayout()); panel.add(northPanel, BorderLayout.NORTH); panel.add(myMainSplitter, BorderLayout.CENTER); return panel; } public boolean isModified() { if (myModified) return true; if (mySelectedProfile.isChanged()) return true; if (myShareProfile != (mySelectedProfile.getProfileManager() == myProjectProfileManager)) return true; if (!Comparing.strEqual(myCurrentProfileName, mySelectedProfile.getName())) return true; if (!Comparing.equal(myInitialScopesOrder, mySelectedProfile.getScopesOrder())) return true; if (descriptorsAreChanged()) { return true; } return false; } public void reset() { myModified = false; setSelectedProfile(mySelectedProfile); final String filter = myProfileFilter.getFilter(); myProfileFilter.reset(); myProfileFilter.setSelectedItem(filter); myShareProfile = mySelectedProfile.getProfileManager() == myProjectProfileManager; } public void apply() throws ConfigurationException { final boolean modified = isModified(); if (!modified) { return; } final ModifiableModel selectedProfile = getSelectedProfile(); if (!Comparing.equal(myCurrentProfileName, selectedProfile.getName())) { selectedProfile.getProfileManager().deleteProfile(selectedProfile.getName()); selectedProfile.setName(myCurrentProfileName); selectedProfile.getProfileManager().updateProfile(selectedProfile); } ProfileManager profileManager = myShareProfile ? myProjectProfileManager : InspectionProfileManager.getInstance(); selectedProfile.setProjectLevel(myShareProfile); if (selectedProfile.getProfileManager() != profileManager) { if (selectedProfile.getProfileManager().getProfile(selectedProfile.getName(), false) != null) { selectedProfile.getProfileManager().deleteProfile(selectedProfile.getName()); } copyUsedSeveritiesIfUndefined(selectedProfile, profileManager); selectedProfile.setProfileManager(profileManager); } final InspectionProfile parentProfile = selectedProfile.getParentProfile(); try { selectedProfile.commit(); } catch (IOException e) { throw new ConfigurationException(e.getMessage()); } setSelectedProfile(parentProfile.getModifiableModel()); setSelectedProfileModified(false); myModified = false; } private boolean descriptorsAreChanged() { for (ToolDescriptors toolDescriptors : myInitialToolDescriptors) { Descriptor desc = toolDescriptors.getDefaultDescriptor(); Project project = myProjectProfileManager.getProject(); if (mySelectedProfile.isToolEnabled(desc.getKey(), null, project) != desc.isEnabled()){ return true; } if (mySelectedProfile.getErrorLevel(desc.getKey(), desc.getScope(), project) != desc.getLevel()) { return true; } final List<Descriptor> descriptors = toolDescriptors.getNonDefaultDescriptors(); for (Descriptor descriptor : descriptors) { if (mySelectedProfile.isToolEnabled(descriptor.getKey(), descriptor.getScope(), project) != descriptor.isEnabled()) { return true; } if (mySelectedProfile.getErrorLevel(descriptor.getKey(), descriptor.getScope(), project) != descriptor.getLevel()) { return true; } } final List<ScopeToolState> tools = mySelectedProfile.getNonDefaultTools(desc.getKey().toString(), project); if (tools.size() != descriptors.size()) { return true; } for (int i = 0; i < tools.size(); i++) { final ScopeToolState pair = tools.get(i); if (!Comparing.equal(pair.getScope(project), descriptors.get(i).getScope())) { return true; } } } return false; } public boolean isProfileShared() { return myShareProfile; } public void setProfileShared(boolean profileShared) { myShareProfile = profileShared; } @NotNull public String getCurrentProfileName() { return myCurrentProfileName; } public void setCurrentProfileName(@NotNull String currentProfileName) { myCurrentProfileName = currentProfileName; } @Override public void setVisible(boolean aFlag) { if (aFlag && myInspectionProfilePanel == null) { initUI(); } super.setVisible(aFlag); } private void setNewHighlightingLevel(@NotNull HighlightDisplayLevel level) { final int[] rows = myTreeTable.getTree().getSelectionRows(); final boolean showOptionsAndDescriptorPanels = rows != null && rows.length == 1; for (int i = 0; rows != null && i < rows.length; i++) { final InspectionConfigTreeNode node = (InspectionConfigTreeNode)myTreeTable.getTree().getPathForRow(rows[i]).getLastPathComponent(); final InspectionConfigTreeNode parent = (InspectionConfigTreeNode)node.getParent(); final Object userObject = node.getUserObject(); if (userObject instanceof ToolDescriptors && (node.getScopeName() != null || node.isLeaf())) { updateErrorLevel(node, showOptionsAndDescriptorPanels, level); updateUpHierarchy(parent); } else { updateErrorLevelUpInHierarchy(level, showOptionsAndDescriptorPanels, node); updateUpHierarchy(parent); } } if (rows != null) { updateOptionsAndDescriptionPanel(myTreeTable.getTree().getSelectionPaths()); } else { initOptionsAndDescriptionPanel(); } repaintTableData(); } private void updateErrorLevelUpInHierarchy(@NotNull HighlightDisplayLevel level, boolean showOptionsAndDescriptorPanels, InspectionConfigTreeNode node) { node.dropCache(); for (int j = 0; j < node.getChildCount(); j++) { final InspectionConfigTreeNode child = (InspectionConfigTreeNode)node.getChildAt(j); final Object userObject = child.getUserObject(); if (userObject instanceof ToolDescriptors && (child.getScopeName() != null || child.isLeaf())) { updateErrorLevel(child, showOptionsAndDescriptorPanels, level); } else { updateErrorLevelUpInHierarchy(level, showOptionsAndDescriptorPanels, child); } } } private void updateErrorLevel(final InspectionConfigTreeNode child, final boolean showOptionsAndDescriptorPanels, @NotNull HighlightDisplayLevel level) { final HighlightDisplayKey key = child.getDefaultDescriptor().getKey(); mySelectedProfile.setErrorLevel(key, level, null, myProjectProfileManager.getProject()); child.dropCache(); if (showOptionsAndDescriptorPanels) { updateOptionsAndDescriptionPanel(new TreePath(child.getPath())); } } public JComponent getPreferredFocusedComponent() { return myTreeTable; } private class MyFilterComponent extends FilterComponent { private MyFilterComponent() { super(INSPECTION_FILTER_HISTORY, 10); setHistory(Arrays.asList("\"New in 13\"")); } @Override public void filter() { filterTree(getFilter()); } @Override protected void onlineFilter() { if (mySelectedProfile == null) return; final String filter = getFilter(); getExpandedNodes(mySelectedProfile).saveVisibleState(myTreeTable.getTree()); fillTreeData(filter, true); reloadModel(); if (filter == null || filter.isEmpty()) { restoreTreeState(); } else { TreeUtil.expandAll(myTreeTable.getTree()); } } } }