blob: c616e76f7dadf037e760ffb17c830ce3c2dff646 [file] [log] [blame]
package com.intellij.coverage.view;
import com.intellij.CommonBundle;
import com.intellij.coverage.CoverageDataManager;
import com.intellij.coverage.CoverageSuitesBundle;
import com.intellij.execution.RunManagerEx;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.configurations.RunConfigurationBase;
import com.intellij.execution.impl.RunDialog;
import com.intellij.icons.AllIcons;
import com.intellij.ide.actions.CloseTabToolbarAction;
import com.intellij.ide.actions.ContextHelpAction;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.ui.table.JBTable;
import com.intellij.util.ui.StatusText;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.awt.event.*;
/**
* User: anna
* Date: 1/2/12
*/
public class CoverageView extends JPanel implements DataProvider, Disposable {
@NonNls private static final String ACTION_DRILL_DOWN = "DrillDown";
@NonNls private static final String ACTION_GO_UP = "GoUp";
@NonNls private static final String HELP_ID = "reference.toolWindows.Coverage";
private CoverageTableModel myModel;
private JBTable myTable;
private CoverageViewBuilder myBuilder;
private final Project myProject;
private final CoverageViewManager.StateBean myStateBean;
public CoverageView(final Project project, final CoverageDataManager dataManager, CoverageViewManager.StateBean stateBean) {
super(new BorderLayout());
myProject = project;
myStateBean = stateBean;
final JLabel titleLabel = new JLabel();
final CoverageSuitesBundle suitesBundle = dataManager.getCurrentSuitesBundle();
myModel = new CoverageTableModel(suitesBundle, stateBean, project);
myTable = new JBTable(myModel);
final StatusText emptyText = myTable.getEmptyText();
emptyText.setText("No coverage results.");
final RunConfigurationBase configuration = suitesBundle.getRunConfiguration();
if (configuration != null) {
emptyText.appendText(" Click ");
emptyText.appendText("Edit", SimpleTextAttributes.LINK_ATTRIBUTES, new ActionListener() {
public void actionPerformed(final ActionEvent e) {
final String configurationName = configuration.getName();
final RunnerAndConfigurationSettings configurationSettings = RunManagerEx.getInstanceEx(project).findConfigurationByName(configurationName);
if (configurationSettings != null) {
RunDialog.editConfiguration(project, configurationSettings, "Edit Run Configuration");
} else {
Messages.showErrorDialog(project, "Configuration \'" + configurationName + "\' was not found", CommonBundle.getErrorTitle());
}
}
});
emptyText.appendText(" to fix configuration settings.");
}
myTable.getColumnModel().getColumn(0).setCellRenderer(new NodeDescriptorTableCellRenderer());
myTable.getTableHeader().setReorderingAllowed(false);
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.add(ScrollPaneFactory.createScrollPane(myTable), BorderLayout.CENTER);
centerPanel.add(titleLabel, BorderLayout.NORTH);
add(centerPanel, BorderLayout.CENTER);
final CoverageViewTreeStructure structure = new CoverageViewTreeStructure(project, suitesBundle, stateBean);
myBuilder = new CoverageViewBuilder(project, new JBList(), myModel, structure, myTable);
myBuilder.setParentTitle(titleLabel);
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
drillDown(structure);
return true;
}
}.installOn(myTable);
final TableSpeedSearch speedSearch = new TableSpeedSearch(myTable);
speedSearch.setClearSearchOnNavigateNoMatch(true);
PopupHandler.installUnknownPopupHandler(myTable, createPopupGroup(), ActionManager.getInstance());
TableScrollingUtil.installActions(myTable);
myTable.registerKeyboardAction(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
if (myBuilder == null) return;
myBuilder.buildRoot();
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SLASH, SystemInfo.isMac ? InputEvent.META_MASK : InputEvent.CTRL_MASK), JComponent.WHEN_FOCUSED);
myTable.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ACTION_DRILL_DOWN);
myTable.getInputMap(WHEN_FOCUSED).put(
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, SystemInfo.isMac ? InputEvent.META_MASK : InputEvent.CTRL_MASK), ACTION_DRILL_DOWN);
myTable.getActionMap().put(ACTION_DRILL_DOWN, new AbstractAction() {
public void actionPerformed(final ActionEvent e) {
drillDown(structure);
}
});
myTable.getInputMap(WHEN_FOCUSED).put(
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, SystemInfo.isMac ? InputEvent.META_MASK : InputEvent.CTRL_MASK), ACTION_GO_UP);
myTable.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), ACTION_GO_UP);
myTable.getActionMap().put(ACTION_GO_UP, new AbstractAction() {
public void actionPerformed(final ActionEvent e) {
goUp();
}
});
final JComponent component =
ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, createToolbarActions(structure), false).getComponent();
add(component, BorderLayout.WEST);
}
@Override
public void dispose() {
}
private static ActionGroup createPopupGroup() {
final DefaultActionGroup actionGroup = new DefaultActionGroup();
actionGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE));
return actionGroup;
}
private ActionGroup createToolbarActions(final CoverageViewTreeStructure treeStructure) {
final DefaultActionGroup actionGroup = new DefaultActionGroup();
actionGroup.add(new GoUpAction(treeStructure));
if (treeStructure.supportFlattenPackages()) {
actionGroup.add(new FlattenPackagesAction());
}
installAutoScrollToSource(actionGroup);
installAutoScrollFromSource(actionGroup);
actionGroup.add(ActionManager.getInstance().getAction("GenerateCoverageReport"));
actionGroup.add(new CloseTabToolbarAction() {
@Override
public void actionPerformed(AnActionEvent e) {
CoverageDataManager.getInstance(myProject).chooseSuitesBundle(null);
}
});
actionGroup.add(new ContextHelpAction(HELP_ID));
return actionGroup;
}
private void installAutoScrollFromSource(DefaultActionGroup actionGroup) {
final MyAutoScrollFromSourceHandler handler = new MyAutoScrollFromSourceHandler();
handler.install();
actionGroup.add(handler.createToggleAction());
}
private void installAutoScrollToSource(DefaultActionGroup actionGroup) {
AutoScrollToSourceHandler autoScrollToSourceHandler = new AutoScrollToSourceHandler(){
@Override
protected boolean isAutoScrollMode() {
return myStateBean.myAutoScrollToSource;
}
@Override
protected void setAutoScrollMode(boolean state) {
myStateBean.myAutoScrollToSource = state;
}
};
autoScrollToSourceHandler.install(myTable);
actionGroup.add(autoScrollToSourceHandler.createToggleAction());
}
public void goUp() {
if (myBuilder == null) {
return;
}
myBuilder.goUp();
}
private void drillDown(CoverageViewTreeStructure treeStructure) {
final AbstractTreeNode element = getSelectedValue();
if (element == null) return;
if (treeStructure.getChildElements(element).length == 0) {
if (element.canNavigate()) {
element.navigate(true);
}
return;
}
myBuilder.drillDown();
}
public void updateParentTitle() {
myBuilder.updateParentTitle();
}
private AbstractTreeNode getSelectedValue() {
return (AbstractTreeNode)myBuilder.getSelectedValue();
}
private boolean topElementIsSelected(final CoverageViewTreeStructure treeStructure) {
if (myTable == null) return false;
if (myModel.getSize() >= 1) {
final AbstractTreeNode rootElement = (AbstractTreeNode)treeStructure.getRootElement();
final AbstractTreeNode node = (AbstractTreeNode)myModel.getElementAt(0);
if (node.getParent() == rootElement) {
return true;
}
}
return false;
}
public boolean canSelect(VirtualFile file) {
return myBuilder.canSelect(file);
}
public void select(VirtualFile file) {
myBuilder.select(file);
}
public Object getData(@NonNls String dataId) {
if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
return getSelectedValue();
}
if (PlatformDataKeys.HELP_ID.is(dataId)) {
return HELP_ID;
}
return null;
}
private static class NodeDescriptorTableCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
final Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value instanceof NodeDescriptor) {
NodeDescriptor descriptor = (NodeDescriptor)value;
setIcon(descriptor.getIcon());
setText(descriptor.toString());
if (!isSelected) setForeground(((CoverageListNode)descriptor).getFileStatus().getColor());
}
return component;
}
}
private class FlattenPackagesAction extends ToggleAction {
private FlattenPackagesAction() {
super("Flatten Packages", "Flatten Packages", AllIcons.ObjectBrowser.FlattenPackages);
}
@Override
public boolean isSelected(AnActionEvent e) {
return myStateBean.myFlattenPackages;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
myStateBean.myFlattenPackages = state;
final Object selectedValue = myBuilder.getSelectedValue();
myBuilder.buildRoot();
if (selectedValue != null) {
myBuilder.select(((CoverageListNode)selectedValue).getValue());
}
myBuilder.ensureSelectionExist();
myBuilder.updateParentTitle();
}
}
private class GoUpAction extends AnAction {
private final CoverageViewTreeStructure myTreeStructure;
public GoUpAction(CoverageViewTreeStructure treeStructure) {
super("Go Up", "Go to Upper Level", AllIcons.Nodes.UpLevel);
myTreeStructure = treeStructure;
registerCustomShortcutSet(KeyEvent.VK_BACK_SPACE, 0, myTable);
}
@Override
public void actionPerformed(AnActionEvent e) {
goUp();
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(!topElementIsSelected(myTreeStructure));
}
}
private class MyAutoScrollFromSourceHandler extends AutoScrollFromSourceHandler {
public MyAutoScrollFromSourceHandler() {
super(CoverageView.this.myProject, CoverageView.this, CoverageView.this);
}
@Override
protected boolean isAutoScrollEnabled() {
return myStateBean.myAutoScrollFromSource;
}
@Override
protected void setAutoScrollEnabled(boolean state) {
myStateBean.myAutoScrollFromSource = state;
}
@Override
protected void selectElementFromEditor(@NotNull FileEditor editor) {
if (myProject.isDisposed() || !CoverageView.this.isShowing()) return;
if (myStateBean.myAutoScrollFromSource) {
final VirtualFile file = FileEditorManagerEx.getInstanceEx(myProject).getFile(editor);
if (file != null) {
if (canSelect(file)) {
PsiElement e = null;
if (editor instanceof TextEditor) {
final int offset = ((TextEditor)editor).getEditor().getCaretModel().getOffset();
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
if (psiFile != null) {
e = psiFile.findElementAt(offset);
}
}
myBuilder.select(e != null ? e : file);
}
}
}
}
}
}