blob: fe36408cf61a2a01465542b19c8ddc596511f7b9 [file] [log] [blame]
/*
* 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.inspectionsTree;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.ScopeToolState;
import com.intellij.ide.IdeTooltip;
import com.intellij.ide.IdeTooltipManager;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.profile.codeInspection.ui.InspectionsAggregationUtil;
import com.intellij.profile.codeInspection.ui.table.ScopesAndSeveritiesTable;
import com.intellij.profile.codeInspection.ui.table.ThreeStateCheckBoxRenderer;
import com.intellij.ui.DoubleClickListener;
import com.intellij.ui.treeStructure.treetable.TreeTable;
import com.intellij.ui.treeStructure.treetable.TreeTableModel;
import com.intellij.ui.treeStructure.treetable.TreeTableTree;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
/**
* @author Dmitry Batkovich
*/
public class InspectionsConfigTreeTable extends TreeTable {
private final static Logger LOG = Logger.getInstance(InspectionsConfigTreeTable.class);
private final static int TREE_COLUMN = 0;
private final static int SEVERITIES_COLUMN = 1;
private final static int IS_ENABLED_COLUMN = 2;
public InspectionsConfigTreeTable(final InspectionsConfigTreeTableSettings settings) {
super(new InspectionsConfigTreeTableModel(settings));
final TableColumn severitiesColumn = getColumnModel().getColumn(SEVERITIES_COLUMN);
severitiesColumn.setMaxWidth(20);
final TableColumn isEnabledColumn = getColumnModel().getColumn(IS_ENABLED_COLUMN);
isEnabledColumn.setMaxWidth(20);
isEnabledColumn.setCellRenderer(new ThreeStateCheckBoxRenderer());
isEnabledColumn.setCellEditor(new ThreeStateCheckBoxRenderer());
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(final MouseEvent e) {
final Point point = e.getPoint();
final int column = columnAtPoint(point);
if (column != SEVERITIES_COLUMN) {
return;
}
final int row = rowAtPoint(point);
final Object maybeIcon = getModel().getValueAt(row, column);
if (maybeIcon instanceof MultiScopeSeverityIcon) {
final LinkedHashMap<String, HighlightSeverity> scopeToAverageSeverityMap =
((MultiScopeSeverityIcon)maybeIcon).getScopeToAverageSeverityMap();
IdeTooltipManager.getInstance().show(
new IdeTooltip(InspectionsConfigTreeTable.this, point, new ScopesAndSeveritiesHintTable(scopeToAverageSeverityMap)), false);
}
}
});
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent event) {
final TreePath path = getTree().getPathForRow(getTree().getLeadSelectionRow());
if (path != null) {
final InspectionConfigTreeNode node = (InspectionConfigTreeNode)path.getLastPathComponent();
if (node.isLeaf()) {
swapInspectionEnableState();
}
}
return true;
}
}.installOn(this);
registerKeyboardAction(new ActionListener() {
public void actionPerformed(ActionEvent e) {
swapInspectionEnableState();
updateUI();
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), JComponent.WHEN_FOCUSED);
getEmptyText().setText("No enabled inspections available");
}
private void swapInspectionEnableState() {
for (int selectedRow : getSelectedRows()) {
final Object value = getValueAt(selectedRow, IS_ENABLED_COLUMN);
final boolean newValue = !Boolean.TRUE.equals(value);
setValueAt(newValue, selectedRow, IS_ENABLED_COLUMN);
}
}
public abstract static class InspectionsConfigTreeTableSettings {
private final TreeNode myRoot;
private final Project myProject;
public InspectionsConfigTreeTableSettings(final TreeNode root, final Project project) {
myRoot = root;
myProject = project;
}
public TreeNode getRoot() {
return myRoot;
}
public Project getProject() {
return myProject;
}
protected abstract InspectionProfileImpl getInspectionProfile();
protected abstract void onChanged(InspectionConfigTreeNode node);
}
private static class InspectionsConfigTreeTableModel extends DefaultTreeModel implements TreeTableModel {
private final InspectionsConfigTreeTableSettings mySettings;
private TreeTable myTreeTable;
public InspectionsConfigTreeTableModel(final InspectionsConfigTreeTableSettings settings) {
super(settings.getRoot());
mySettings = settings;
}
@Override
public int getColumnCount() {
return 3;
}
@Nullable
@Override
public String getColumnName(final int column) {
return null;
}
@Override
public Class getColumnClass(final int column) {
switch (column) {
case TREE_COLUMN:
return TreeTableModel.class;
case SEVERITIES_COLUMN:
return Icon.class;
case IS_ENABLED_COLUMN:
return Boolean.class;
}
throw new IllegalArgumentException();
}
@Nullable
@Override
public Object getValueAt(final Object node, final int column) {
if (column == TREE_COLUMN) {
return null;
}
final InspectionConfigTreeNode treeNode = (InspectionConfigTreeNode)node;
final List<HighlightDisplayKey> inspectionsKeys = InspectionsAggregationUtil.getInspectionsKeys(treeNode);
if (column == SEVERITIES_COLUMN) {
final MultiColoredHighlightSeverityIconSink sink = new MultiColoredHighlightSeverityIconSink();
for (final HighlightDisplayKey selectedInspectionsNode : inspectionsKeys) {
final String toolId = selectedInspectionsNode.toString();
if (mySettings.getInspectionProfile().getTools(toolId, mySettings.getProject()).isEnabled()) {
sink.put(mySettings.getInspectionProfile().getToolDefaultState(toolId, mySettings.getProject()),
mySettings.getInspectionProfile().getNonDefaultTools(toolId, mySettings.getProject()));
}
}
return sink.constructIcon(mySettings.getInspectionProfile());
} else if (column == IS_ENABLED_COLUMN) {
return isEnabled(inspectionsKeys);
}
throw new IllegalArgumentException();
}
@Nullable
private Boolean isEnabled(final List<HighlightDisplayKey> selectedInspectionsNodes) {
Boolean isPreviousEnabled = null;
for (final HighlightDisplayKey key : selectedInspectionsNodes) {
final boolean enabled = mySettings.getInspectionProfile().getTools(key.toString(), mySettings.getProject()).isEnabled();
if (isPreviousEnabled == null) {
isPreviousEnabled = enabled;
} else if (!isPreviousEnabled.equals(enabled)) {
return null;
}
}
return isPreviousEnabled;
}
@Override
public boolean isCellEditable(final Object node, final int column) {
return column == IS_ENABLED_COLUMN;
}
@Override
public void setValueAt(final Object aValue, final Object node, final int column) {
LOG.assertTrue(column == IS_ENABLED_COLUMN);
LOG.assertTrue(aValue != null, "node = " + node);
final boolean doEnable = (Boolean) aValue;
for (final InspectionConfigTreeNode aNode : InspectionsAggregationUtil.getInspectionsNodes((InspectionConfigTreeNode) node)) {
final String toolId = aNode.getKey().toString();
if (doEnable) {
mySettings.getInspectionProfile().enableTool(toolId, mySettings.getProject());
} else {
mySettings.getInspectionProfile().disableTool(toolId, mySettings.getProject());
}
aNode.dropCache();
mySettings.onChanged(aNode);
}
if (myTreeTable != null) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
((AbstractTableModel)myTreeTable.getModel()).fireTableDataChanged();
}
});
}
}
@Override
public void setTree(final JTree tree) {
myTreeTable = ((TreeTableTree)tree).getTreeTable();
}
}
private static class MultiColoredHighlightSeverityIconSink {
private final Map<String, HighlightSeverity> myScopeToAverageSeverityMap = new HashMap<String, HighlightSeverity>();
private String myDefaultScopeName;
private boolean myIsFirst = true;
public Icon constructIcon(final InspectionProfileImpl inspectionProfile) {
if (myScopeToAverageSeverityMap.isEmpty()) {
return null;
}
return !allScopesHasMixedSeverity()
? new MultiScopeSeverityIcon(myScopeToAverageSeverityMap, myDefaultScopeName, inspectionProfile)
: ScopesAndSeveritiesTable.MIXED_FAKE_LEVEL.getIcon();
}
private boolean allScopesHasMixedSeverity() {
for (final Map.Entry<String, HighlightSeverity> e : myScopeToAverageSeverityMap.entrySet()) {
if (!ScopesAndSeveritiesTable.MIXED_FAKE_SEVERITY.equals(e.getValue())) {
return false;
}
}
return true;
}
public void put(final ScopeToolState defaultState, final Collection<ScopeToolState> nonDefault) {
putOne(defaultState);
if (myDefaultScopeName == null) {
myDefaultScopeName = defaultState.getScopeName();
}
for (final ScopeToolState scopeToolState : nonDefault) {
putOne(scopeToolState);
}
if (myIsFirst) {
myIsFirst = false;
}
}
private void putOne(final ScopeToolState state) {
final Icon icon = state.getLevel().getIcon();
final String scopeName = state.getScopeName();
if (icon instanceof HighlightDisplayLevel.SingleColorIconWithMask) {
if (myIsFirst) {
myScopeToAverageSeverityMap.put(scopeName, state.getLevel().getSeverity());
} else {
final HighlightSeverity severity = myScopeToAverageSeverityMap.get(scopeName);
if (!ScopesAndSeveritiesTable.MIXED_FAKE_SEVERITY.equals(severity) && !Comparing.equal(severity, state.getLevel().getSeverity())) {
myScopeToAverageSeverityMap.put(scopeName, ScopesAndSeveritiesTable.MIXED_FAKE_SEVERITY);
}
}
} else if (!ScopesAndSeveritiesTable.MIXED_FAKE_SEVERITY.equals(myScopeToAverageSeverityMap.get(scopeName))) {
myScopeToAverageSeverityMap.put(scopeName, ScopesAndSeveritiesTable.MIXED_FAKE_SEVERITY);
}
}
}
}