blob: 74cfdd9f434e01f64867ba64c82e176c76fa01e3 [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.codeInsight.daemon.impl;
import com.intellij.codeInsight.daemon.DaemonBundle;
import com.intellij.ide.PowerSaveMode;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.ui.HintHint;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.components.panels.VerticalBox;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.ui.AwtVisitor;
import com.intellij.xml.util.XmlStringUtil;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Map;
/**
* User: cdr
*/
public class TrafficProgressPanel extends JPanel {
private static final int MAX = 100;
private static final String MAX_TEXT = "100%";
private static final String MIN_TEXT = "0%";
private final JLabel statistics = new JLabel();
private final Map<ProgressableTextEditorHighlightingPass, Pair<JProgressBar, JLabel>> passes = new LinkedHashMap<ProgressableTextEditorHighlightingPass, Pair<JProgressBar, JLabel>>();
private final Map<JProgressBar, JLabel> myProgressToText = new HashMap<JProgressBar, JLabel>();
private final JLabel statusLabel = new JLabel();
private final JLabel dumbLabel = new JLabel("Complete results will be available after indexing");
@NotNull private final TrafficLightRenderer myTrafficLightRenderer;
private final JPanel myPassStatuses = new JPanel();
private final JPanel myEmptyPassStatuses = new NonOpaquePanel();
private final Wrapper myPassStatusesContainer = new Wrapper();
@NotNull private final HintHint myHintHint;
public TrafficProgressPanel(@NotNull TrafficLightRenderer trafficLightRenderer, @NotNull Editor editor, @NotNull HintHint hintHint) {
myHintHint = hintHint;
myTrafficLightRenderer = trafficLightRenderer;
setLayout(new BorderLayout());
VerticalBox center = new VerticalBox();
add(center, BorderLayout.NORTH);
center.add(statusLabel);
center.add(dumbLabel);
center.add(new Separator());
center.add(Box.createVerticalStrut(6));
TrafficLightRenderer.DaemonCodeAnalyzerStatus fakeStatusLargeEnough = new TrafficLightRenderer.DaemonCodeAnalyzerStatus();
fakeStatusLargeEnough.errorCount = new int[]{1, 1, 1, 1};
Project project = trafficLightRenderer.getProject();
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
fakeStatusLargeEnough.passStati = new ArrayList<ProgressableTextEditorHighlightingPass>();
for (int i = 0; i < 3; i++) {
fakeStatusLargeEnough.passStati
.add(new ProgressableTextEditorHighlightingPass(project, null, DaemonBundle.message("pass.wolf"), psiFile, editor, TextRange.EMPTY_RANGE, false,
HighlightInfoProcessor.getEmpty()) {
@Override
protected void collectInformationWithProgress(@NotNull ProgressIndicator progress) {
}
@Override
protected void applyInformationWithProgress() {
}
});
}
rebuildPassesPanel(fakeStatusLargeEnough);
for (Pair<JProgressBar, JLabel> pair : passes.values()) {
JProgressBar bar = pair.first;
bar.setMaximum(MAX);
JLabel label = pair.second;
label.setText(MAX_TEXT);
}
center.add(myPassStatusesContainer);
add(statistics, BorderLayout.SOUTH);
updatePanel(fakeStatusLargeEnough, true);
hintHint.initStyle(this, true);
}
private class Separator extends NonOpaquePanel {
@Override
protected void paintComponent(@NotNull Graphics g) {
Insets insets = getInsets();
if (insets == null) {
insets = new Insets(0, 0, 0, 0);
}
g.setColor(myHintHint.getTextForeground());
g.drawLine(insets.left, insets.top, getWidth() - insets.left - insets.right, insets.top);
}
@NotNull
@Override
public Dimension getPreferredSize() {
return new Dimension(1, 1);
}
@NotNull
@Override
public Dimension getMinimumSize() {
return new Dimension(1, 1);
}
}
private void rebuildPassesPanel(@Nullable TrafficLightRenderer.DaemonCodeAnalyzerStatus status) {
myPassStatuses.removeAll();
myPassStatuses.setLayout(new GridBagLayout());
passes.clear();
GridBagConstraints c = new GridBagConstraints();
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
if (status != null) {
for (ProgressableTextEditorHighlightingPass pass : status.passStati) {
JLabel label = new JLabel(pass.getPresentableName() + ": ");
label.setHorizontalTextPosition(SwingConstants.RIGHT);
JProgressBar progressBar = new JProgressBar(0, MAX);
progressBar.putClientProperty("JComponent.sizeVariant", "mini");
JLabel percLabel = new JLabel();
passes.put(pass, Pair.create(progressBar, percLabel));
myProgressToText.put(progressBar, percLabel);
c.gridx = 0;
myPassStatuses.add(label, c);
c.gridx = 1;
myPassStatuses.add(progressBar, c);
c.gridx = 2;
c.weightx = 1;
myPassStatuses.add(percLabel, c);
c.gridy++;
}
}
myHintHint.initStyle(myPassStatuses, true);
statusLabel.setFont(statusLabel.getFont().deriveFont(Font.BOLD));
}
public void updatePanel(@Nullable TrafficLightRenderer.DaemonCodeAnalyzerStatus status, boolean isFake) {
boolean isDumb = DumbService.isDumb(myTrafficLightRenderer.getProject());
dumbLabel.setVisible(isDumb);
try {
if (PowerSaveMode.isEnabled()) {
statusLabel.setText("Code analysis is disabled in power save mode");
myPassStatuses.setVisible(false);
statistics.setText("");
}
else if (status == null || status.noHighlightingRoots != null && status.noHighlightingRoots.length == status.rootsNumber) {
statusLabel.setText(DaemonBundle.message("analysis.hasnot.been.run"));
myPassStatuses.setVisible(true);
setPassesEnabled(false, Boolean.FALSE);
statistics.setText("");
}
else if (status.errorAnalyzingFinished) {
if (isDumb) {
statusLabel.setText("Shallow analysis completed");
}
else {
statusLabel.setText(DaemonBundle.message("analysis.completed"));
}
myPassStatuses.setVisible(true);
setPassesEnabled(false, Boolean.TRUE);
}
else if (!status.enabled) {
statusLabel.setText("Code analysis has been suspended");
myPassStatuses.setVisible(true);
setPassesEnabled(false, Boolean.FALSE);
statistics.setText("");
}
else {
statusLabel.setText(DaemonBundle.message("performing.code.analysis"));
myPassStatuses.setVisible(true);
setPassesEnabled(true, null);
}
if (status == null ||
!status.passStati.equals(new ArrayList<ProgressableTextEditorHighlightingPass>(passes.keySet()))) {
// passes set has changed
rebuildPassesPanel(status);
}
if (status != null) {
for (ProgressableTextEditorHighlightingPass pass : status.passStati) {
double progress = pass.getProgress();
Pair<JProgressBar, JLabel> pair = passes.get(pass);
JProgressBar progressBar = pair.first;
int percent = (int)Math.round(progress * MAX);
progressBar.setValue(percent);
JLabel percentage = pair.second;
percentage.setText(percent + "%");
}
int currentSeverityErrors = 0;
@Language("HTML")
String text = "";
for (int i = status.errorCount.length - 1; i >= 0; i--) {
if (status.errorCount[i] > 0) {
final HighlightSeverity severity = SeverityRegistrar.getSeverityRegistrar(myTrafficLightRenderer.getProject()).getSeverityByIndex(i);
String name =
status.errorCount[i] > 1 ? StringUtil.pluralize(severity.getName().toLowerCase()) : severity.getName().toLowerCase();
text += status.errorAnalyzingFinished
? DaemonBundle.message("errors.found", status.errorCount[i], name)
: DaemonBundle.message("errors.found.so.far", status.errorCount[i], name);
text += "<br>";
currentSeverityErrors += status.errorCount[i];
}
}
if (currentSeverityErrors == 0) {
text += status.errorAnalyzingFinished
? DaemonBundle.message("no.errors.or.warnings.found")
: DaemonBundle.message("no.errors.or.warnings.found.so.far") + "<br>";
}
statistics.setText(XmlStringUtil.wrapInHtml(text));
}
}
finally {
if (isFake) {
myEmptyPassStatuses.setPreferredSize(myPassStatuses.getPreferredSize());
myPassStatusesContainer.setContent(myEmptyPassStatuses);
}
else {
myPassStatusesContainer.setContent(myPassStatuses);
}
}
}
private void setPassesEnabled(final boolean enabled, @Nullable final Boolean completed) {
new AwtVisitor(myPassStatuses) {
@Override
public boolean visit(Component component) {
if (component instanceof JProgressBar) {
JProgressBar progress = (JProgressBar)component;
progress.setEnabled(enabled);
if (completed != null) {
if (completed) {
progress.setValue(MAX);
myProgressToText.get(progress).setText(MAX_TEXT);
}
else {
progress.setValue(0);
myProgressToText.get(progress).setText(MIN_TEXT);
}
}
}
return false;
}
};
}
}