package com.google.jstestdriver.idea.execution.settings.ui;
import com.google.common.collect.Lists;
import com.google.jstestdriver.idea.execution.settings.JstdRunSettings;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.table.JBTable;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/**
* @author Sergey Simonchik
*/
public class JstdCoverageSection extends AbstractRunSettingsSection {
private final ExcludedTableModel myExcludedTableModel;
private final JComponent myComponent;
public JstdCoverageSection(@NotNull final Project project) {
myExcludedTableModel = new ExcludedTableModel();
final JBTable table = new JBTable(myExcludedTableModel);
table.getEmptyText().setText("No files excluded from coverage");
table.setRowHeight((int)(table.getRowHeight() * 1.2));
Dimension preferredScrollableViewportSize = new Dimension(
table.getPreferredScrollableViewportSize().width,
table.getRowHeight() * 6
);
table.setPreferredScrollableViewportSize(preferredScrollableViewportSize);
table.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setMinimumSize(preferredScrollableViewportSize);
JTableHeader tableHeader = table.getTableHeader();
tableHeader.setResizingAllowed(false);
tableHeader.setReorderingAllowed(false);
tableHeader.setPreferredSize(new Dimension(
tableHeader.getPreferredSize().width,
(int) (tableHeader.getPreferredSize().height * 1.2)
));
JScrollPane scrollPane = new JBScrollPane(
table,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
);
Dimension scrollDimension = new Dimension(
preferredScrollableViewportSize.width,
tableHeader.getPreferredSize().height + preferredScrollableViewportSize.height + 4
);
scrollPane.setMinimumSize(scrollDimension);
myComponent = ToolbarDecorator.createDecorator(table)
.disableUpAction()
.disableDownAction()
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
addPath(project, table);
}
}).setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
removePaths(table);
}
}).createPanel();
}
private static void addPath(@NotNull Project project, @NotNull JBTable table) {
ExcludedTableModel tableModel = (ExcludedTableModel) table.getModel();
int selectedIndex = table.getSelectedRow() + 1;
if (selectedIndex < 0) {
selectedIndex = tableModel.getRowCount();
}
int savedSelected = selectedIndex;
VirtualFile[] chosen = FileChooser.chooseFiles(FileChooserDescriptorFactory.createAllButJarContentsDescriptor(), project, null);
for (final VirtualFile chosenFile : chosen) {
String path = FileUtil.toSystemDependentName(chosenFile.getPath());
if (tableModel.isFileExcluded(path)) {
continue;
}
tableModel.addPath(path, selectedIndex);
selectedIndex++;
}
if (selectedIndex > savedSelected) {
tableModel.fireTableRowsInserted(savedSelected, selectedIndex - 1);
table.setRowSelectionInterval(savedSelected, selectedIndex - 1);
}
}
private static void removePaths(@NotNull JBTable table) {
int[] selected = table.getSelectedRows();
if (selected == null || selected.length <= 0) {
return;
}
if (table.isEditing()) {
TableCellEditor editor = table.getCellEditor();
if (editor != null) {
editor.stopCellEditing();
}
}
ExcludedTableModel model = (ExcludedTableModel) table.getModel();
Arrays.sort(selected);
int removedCount = 0;
for (int indexToRemove : selected) {
final int row = indexToRemove - removedCount;
model.removeRow(row);
model.fireTableRowsDeleted(row, row);
removedCount++;
}
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> {
IdeFocusManager.getGlobalInstance().requestFocus(table, true);
});
}
@NotNull
@Override
protected JComponent createComponent(@NotNull CreationContext creationContext) {
return myComponent;
}
@Override
public void resetFrom(@NotNull JstdRunSettings runSettings) {
List<String> excludedFiles = runSettings.getFilesExcludedFromCoverage();
myExcludedTableModel.setExcludedFiles(excludedFiles);
}
@Override
public void applyTo(@NotNull JstdRunSettings.Builder runSettingsBuilder) {
runSettingsBuilder.setFilesExcludedFromCoverage(myExcludedTableModel.myExcludedFiles);
}
private static class ExcludedTableModel extends AbstractTableModel {
private final List<String> myExcludedFiles = Lists.newArrayList();
private void setExcludedFiles(List<String> excludedFiles) {
myExcludedFiles.clear();
myExcludedFiles.addAll(excludedFiles);
fireTableDataChanged();
}
@Override
public int getRowCount() {
return myExcludedFiles.size();
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public String getColumnName(int columnIndex) {
if (columnIndex == 0) {
return "Excluded file path";
}
throw unexpectedColumn(columnIndex);
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex != 0) {
throw unexpectedColumn(columnIndex);
}
checkRowIndex(rowIndex);
myExcludedFiles.set(rowIndex, String.valueOf(aValue));
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex != 0) {
throw unexpectedColumn(columnIndex);
}
checkRowIndex(rowIndex);
return myExcludedFiles.get(rowIndex);
}
public void removeRow(int rowIndex) {
checkRowIndex(rowIndex);
myExcludedFiles.remove(rowIndex);
}
private void checkRowIndex(int rowIndex) {
if (rowIndex < 0 || rowIndex >= myExcludedFiles.size()) {
throw new IllegalStateException("Requested excluded file is out of bound: (rowIndex: "
+ rowIndex + ", size: " + myExcludedFiles.size() + ")");
}
}
public boolean isFileExcluded(@NotNull String path) {
return myExcludedFiles.contains(path);
}
public void addPath(@NotNull String path, int selectedIndex) {
myExcludedFiles.add(selectedIndex, path);
}
private static IllegalStateException unexpectedColumn(int column) {
return new IllegalStateException("Unexpected column: " + column);
}
}
}