filebot/source/net/filebot/ui/sfv/SfvPanel.java

300 lines
8.8 KiB
Java

package net.filebot.ui.sfv;
import static net.filebot.ui.sfv.ChecksumTableModel.*;
import static net.filebot.ui.transfer.BackgroundFileTransferablePolicy.*;
import static net.filebot.util.FileUtilities.*;
import static net.filebot.util.ui.SwingUI.*;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import javax.swing.AbstractAction;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import com.google.common.eventbus.Subscribe;
import net.filebot.ResourceManager;
import net.filebot.hash.HashType;
import net.filebot.ui.SelectDialog;
import net.filebot.ui.transfer.DefaultTransferHandler;
import net.filebot.ui.transfer.LoadAction;
import net.filebot.ui.transfer.SaveAction;
import net.filebot.ui.transfer.TransferablePolicy;
import net.filebot.ui.transfer.TransferablePolicy.TransferAction;
import net.filebot.util.FileUtilities;
import net.miginfocom.swing.MigLayout;
public class SfvPanel extends JComponent {
private final ChecksumComputationService computationService = new ChecksumComputationService();
private final ChecksumTable table = new ChecksumTable();
private final ChecksumTableTransferablePolicy transferablePolicy = new ChecksumTableTransferablePolicy(table, computationService);
private final ChecksumTableExportHandler exportHandler = new ChecksumTableExportHandler(table.getModel());
public SfvPanel() {
table.setTransferHandler(new DefaultTransferHandler(transferablePolicy, exportHandler));
JPanel contentPane = new JPanel(new MigLayout("insets 0, nogrid, fill", "", "[fill]10px[bottom, pref!]4px"));
contentPane.setBorder(new TitledBorder("SFV"));
setLayout(new MigLayout("insets dialog, fill"));
add(contentPane, "grow");
contentPane.add(new JScrollPane(table), "grow, wrap");
contentPane.add(new JButton(loadAction), "gap left 15px");
contentPane.add(new JButton(saveAction));
contentPane.add(new JButton(clearAction), "gap right 40px");
// hash function toggle button group
ButtonGroup group = new ButtonGroup();
for (HashType hash : HashType.values()) {
JToggleButton button = new ChecksumButton(new ChangeHashTypeAction(hash));
group.add(button);
contentPane.add(button);
}
contentPane.add(new TotalProgressPanel(computationService), "gap left 35px:push, gap right 7px, hidemode 1");
// cancel and restart computations whenever the hash function has been changed
table.getModel().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (HASH_TYPE_PROPERTY.equals(evt.getPropertyName())) {
restartComputation((HashType) evt.getNewValue());
}
}
});
// Shortcut DELETE
installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), removeAction);
}
public TransferablePolicy getTransferablePolicy() {
return transferablePolicy;
}
protected void restartComputation(HashType hash) {
// cancel all running computations
computationService.reset();
ChecksumTableModel model = table.getModel();
// calculate new hashes, one executor for each checksum column
Map<File, ExecutorService> executors = new HashMap<File, ExecutorService>(4);
for (ChecksumRow row : model.rows()) {
for (ChecksumCell cell : row.values()) {
if (cell.getChecksum(hash) == null && cell.getRoot().isDirectory()) {
cell.putTask(new ChecksumComputationTask(new File(cell.getRoot(), cell.getName()), hash));
ExecutorService executor = executors.get(cell.getRoot());
if (executor == null) {
executor = computationService.newExecutor();
executors.put(cell.getRoot(), executor);
}
// start computation
executor.execute(cell.getTask());
}
}
}
// start shutdown sequence for all created executors
for (ExecutorService executor : executors.values()) {
executor.shutdown();
}
}
@Subscribe
public void handle(Transferable transferable) throws Exception {
TransferablePolicy handler = getTransferablePolicy();
if (handler != null && handler.accept(transferable)) {
handler.handleTransferable(transferable, TransferAction.PUT);
}
}
private final SaveAction saveAction = new ChecksumTableSaveAction();
private final LoadAction loadAction = new LoadAction(this::getTransferablePolicy);
private final AbstractAction clearAction = new AbstractAction("Clear", ResourceManager.getIcon("action.clear")) {
@Override
public void actionPerformed(ActionEvent e) {
transferablePolicy.reset();
computationService.reset();
table.getModel().clear();
}
};
private final AbstractAction removeAction = new AbstractAction("Remove") {
@Override
public void actionPerformed(ActionEvent e) {
if (table.getSelectedRowCount() < 1)
return;
int[] rows = table.getSelectedRows();
if (rows.length <= 0) {
// no rows selected
return;
}
// first selected row
int selectedRow = table.getSelectedRow();
// convert view index to model index
for (int i = 0; i < rows.length; i++) {
rows[i] = table.getRowSorter().convertRowIndexToModel(rows[i]);
}
// remove selected rows
table.getModel().remove(rows);
// update computation service task count
computationService.purge();
// auto select next row
selectedRow = Math.min(selectedRow, table.getRowCount() - 1);
table.getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
}
};
protected class ChangeHashTypeAction extends AbstractAction implements PropertyChangeListener {
private ChangeHashTypeAction(HashType hash) {
super(hash.toString());
putValue(HASH_TYPE_PROPERTY, hash);
// initialize selected state
propertyChange(new PropertyChangeEvent(this, HASH_TYPE_PROPERTY, null, table.getModel().getHashType()));
transferablePolicy.addPropertyChangeListener(this);
table.getModel().addPropertyChangeListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
table.getModel().setHashType((HashType) getValue(HASH_TYPE_PROPERTY));
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (LOADING_PROPERTY.equals(evt.getPropertyName())) {
// update enabled state
setEnabled(!(Boolean) evt.getNewValue());
} else if (HASH_TYPE_PROPERTY.equals(evt.getPropertyName())) {
// update selected state
putValue(SELECTED_KEY, evt.getNewValue() == getValue(HASH_TYPE_PROPERTY));
}
}
}
protected class ChecksumTableSaveAction extends SaveAction {
private File selectedColumn = null;
public ChecksumTableSaveAction() {
super(exportHandler);
}
@Override
public ChecksumTableExportHandler getExportHandler() {
return (ChecksumTableExportHandler) super.getExportHandler();
}
@Override
protected boolean canExport() {
return selectedColumn != null && super.canExport();
}
@Override
protected void export(File file) throws IOException {
getExportHandler().export(file, selectedColumn);
}
@Override
protected File getDefaultFile() {
// use the column root as default folder in the file dialog
return new File(selectedColumn, validateFileName(getExportHandler().getDefaultFileName(selectedColumn)));
}
@Override
public void actionPerformed(ActionEvent e) {
List<File> options = new ArrayList<File>();
// filter out verification file columns
for (File file : table.getModel().getChecksumColumns()) {
if (file.isDirectory())
options.add(file);
}
// can't export anything
if (options.isEmpty()) {
return;
}
try {
if (options.size() == 1) {
// auto-select option if there is only one option
this.selectedColumn = options.get(0);
} else if (options.size() > 1) {
// user must select one option
SelectDialog<File> selectDialog = new SelectDialog<File>(SwingUtilities.getWindowAncestor(SfvPanel.this), options) {
@Override
protected String convertValueToString(Object value) {
return FileUtilities.getFolderName((File) value);
}
};
selectDialog.getHeaderLabel().setText("Select checksum column:");
selectDialog.setLocationRelativeTo(SfvPanel.this);
selectDialog.setVisible(true);
this.selectedColumn = selectDialog.getSelectedValue();
}
if (this.selectedColumn != null) {
// continue if a column was selected
super.actionPerformed(e);
}
} finally {
// reset selected column
this.selectedColumn = null;
}
}
}
}