* make things more idiot-proof

This commit is contained in:
Reinhard Pointner 2013-09-30 04:46:33 +00:00
parent 20aef4e385
commit e29b07c186
3 changed files with 73 additions and 87 deletions

View File

@ -1,7 +1,5 @@
package net.sourceforge.filebot.ui.rename;
import static java.util.Collections.*;
import static javax.swing.JOptionPane.*;
import static net.sourceforge.filebot.Settings.*;
@ -52,44 +50,41 @@ import net.sourceforge.tuned.ui.ProgressDialog;
import net.sourceforge.tuned.ui.ProgressDialog.Cancellable;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
class RenameAction extends AbstractAction {
public static final String RENAME_ACTION = "RENAME_ACTION";
private final RenameModel model;
public RenameAction(RenameModel model) {
this.model = model;
resetValues();
}
public void resetValues() {
putValue(RENAME_ACTION, StandardRenameAction.MOVE);
putValue(NAME, "Rename");
putValue(SMALL_ICON, ResourceManager.getIcon("action.rename"));
}
@Override
public void actionPerformed(ActionEvent evt) {
Window window = getWindow(evt.getSource());
try {
if (model.getRenameMap().isEmpty()) {
UILogger.info("Nothing to rename. Please add some files and create matches first.");
return;
}
Map<File, File> renameMap = checkRenamePlan(validate(model.getRenameMap(), window), window);
StandardRenameAction action = (StandardRenameAction) getValue(RENAME_ACTION);
if (renameMap.isEmpty()) {
return;
}
// start processing
window.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// first write all the metadata if xattr is enabled
if (useExtendedFileAttributes()) {
try {
@ -102,7 +97,7 @@ class RenameAction extends AbstractAction {
Logger.getLogger(RenameAction.class.getName()).warning("Failed to write xattr: " + e.getMessage());
}
}
if (useNativeShell() && isNativeActionSupported(action)) {
RenameJob renameJob = new NativeRenameJob(renameMap, NativeRenameAction.valueOf(action.name()));
renameJob.execute();
@ -114,7 +109,7 @@ class RenameAction extends AbstractAction {
} else {
RenameJob renameJob = new RenameJob(renameMap, action);
renameJob.execute();
try {
// wait a for little while (renaming might finish in less than a second)
renameJob.get(2, TimeUnit.SECONDS);
@ -123,7 +118,7 @@ class RenameAction extends AbstractAction {
ProgressDialog dialog = createProgressDialog(window, renameJob);
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
dialog.setIndeterminate(true);
// display progress dialog and stop blocking EDT
window.setCursor(Cursor.getDefaultCursor());
dialog.setVisible(true);
@ -134,11 +129,10 @@ class RenameAction extends AbstractAction {
} catch (Throwable e) {
UILogger.log(Level.WARNING, e.getMessage(), e);
}
window.setCursor(Cursor.getDefaultCursor());
}
public boolean isNativeActionSupported(StandardRenameAction action) {
try {
return NativeRenameAction.isSupported() && NativeRenameAction.valueOf(action.name()) != null;
@ -146,34 +140,33 @@ class RenameAction extends AbstractAction {
return false;
}
}
private Map<File, File> checkRenamePlan(List<Entry<File, File>> renamePlan, Window parent) {
// build rename map and perform some sanity checks
Map<File, File> renameMap = new HashMap<File, File>();
Set<File> destinationSet = new HashSet<File>();
List<String> issues = new ArrayList<String>();
for (Entry<File, File> mapping : renamePlan) {
File source = mapping.getKey();
File destination = mapping.getValue();
// resolve destination
if (!destination.isAbsolute()) {
// same folder, different name
destination = new File(source.getParentFile(), destination.getPath());
}
try {
if (renameMap.containsKey(source))
throw new IllegalArgumentException("Duplicate source file: " + source.getPath());
if (destinationSet.contains(destination))
throw new IllegalArgumentException("Conflict detected: " + mapping.getValue().getPath());
if (destination.exists() && !resolveDestination(mapping.getKey(), mapping.getValue(), false).equals(mapping.getKey()))
throw new IllegalArgumentException("File already exists: " + mapping.getValue().getPath());
// use original mapping values
renameMap.put(mapping.getKey(), mapping.getValue());
destinationSet.add(destination);
@ -181,11 +174,11 @@ class RenameAction extends AbstractAction {
issues.add(e.getMessage());
}
}
if (issues.size() > 0) {
String text = "Some files cannot be renamed:";
JList issuesComponent = new JList(issues.toArray()) {
@Override
public Dimension getPreferredScrollableViewportSize() {
// adjust component size
@ -195,66 +188,62 @@ class RenameAction extends AbstractAction {
Object[] message = new Object[] { text, new JScrollPane(issuesComponent) };
String[] actions = new String[] { "Continue", "Cancel" };
JOptionPane pane = new JOptionPane(message, PLAIN_MESSAGE, YES_NO_OPTION, null, actions, actions[1]);
// display option dialog
pane.createDialog(getWindow(parent), "Conflicting Files").setVisible(true);
if (pane.getValue() != actions[0]) {
return emptyMap();
}
}
return renameMap;
}
private List<Entry<File, File>> validate(Map<File, String> renameMap, Window parent) {
final List<Entry<File, File>> source = new ArrayList<Entry<File, File>>(renameMap.size());
for (Entry<File, String> entry : renameMap.entrySet()) {
source.add(new SimpleEntry<File, File>(entry.getKey(), new File(entry.getValue())));
}
List<File> destinationFileNameView = new AbstractList<File>() {
@Override
public File get(int index) {
return source.get(index).getValue();
}
@Override
public File set(int index, File name) {
return source.get(index).setValue(name);
}
@Override
public int size() {
return source.size();
}
};
if (ValidateDialog.validate(parent, destinationFileNameView)) {
// names have been validated via view
return source;
}
// return empty list if validation was cancelled
return emptyList();
}
protected ProgressDialog createProgressDialog(Window parent, final RenameJob job) {
final ProgressDialog dialog = new ProgressDialog(parent, job);
// configure dialog
dialog.setTitle("Moving files...");
dialog.setIcon((Icon) getValue(SMALL_ICON));
// close progress dialog when worker is finished
job.addPropertyChangeListener(new SwingWorkerPropertyChangeAdapter() {
@Override
protected void event(String name, Object oldValue, Object newValue) {
if (name.equals("currentFile")) {
@ -266,63 +255,58 @@ class RenameAction extends AbstractAction {
}
}
}
@Override
protected void done(PropertyChangeEvent evt) {
dialog.close();
}
});
return dialog;
}
protected class RenameJob extends SwingWorker<Map<File, File>, Void> implements Cancellable {
protected final net.sourceforge.filebot.RenameAction action;
protected final Map<File, File> renameMap;
protected final Map<File, File> renameLog;
protected final Semaphore postprocess = new Semaphore(0);
public RenameJob(Map<File, File> renameMap, net.sourceforge.filebot.RenameAction action) {
this.action = action;
this.renameMap = synchronizedMap(renameMap);
this.renameLog = synchronizedMap(new LinkedHashMap<File, File>());
}
@Override
protected Map<File, File> doInBackground() throws Exception {
try {
for (Entry<File, File> mapping : renameMap.entrySet()) {
if (isCancelled())
return renameLog;
// update progress dialog
firePropertyChange("currentFile", mapping.getKey(), mapping.getValue());
// rename file, throw exception on failure
File source = mapping.getKey();
File destination = resolveDestination(mapping.getKey(), mapping.getValue(), false);
if (!source.equals(destination)) {
action.rename(source, destination);
}
// remember successfully renamed matches for history entry and possible revert
renameLog.put(mapping.getKey(), mapping.getValue());
}
} finally {
postprocess.release();
}
return renameLog;
}
@Override
protected void done() {
try {
@ -335,50 +319,47 @@ class RenameAction extends AbstractAction {
Logger.getLogger(RenameAction.class.getName()).log(Level.SEVERE, e.getMessage(), e);
}
}
// collect renamed types
final List<Class<?>> types = new ArrayList<Class<?>>();
// remove renamed matches
for (File source : renameLog.keySet()) {
// find index of source file
int index = model.files().indexOf(source);
types.add(model.values().get(index).getClass());
// remove complete match
model.matches().remove(index);
}
if (renameLog.size() > 0) {
UILogger.info(String.format("%d files renamed.", renameLog.size()));
HistorySpooler.getInstance().append(renameLog.entrySet());
// count global statistics
for (Class<?> it : new HashSet<Class<?>>(types)) {
Analytics.trackEvent("GUI", "Rename", it.getSimpleName(), frequency(types, it));
}
}
}
@Override
public boolean cancel() {
return cancel(true);
}
}
protected class NativeRenameJob extends RenameJob implements Cancellable {
public NativeRenameJob(Map<File, File> renameMap, NativeRenameAction action) {
super(renameMap, action);
}
@Override
protected Map<File, File> doInBackground() throws Exception {
NativeRenameAction shell = (NativeRenameAction) action;
// prepare delta, ignore files already named as desired
Map<File, File> todo = new LinkedHashMap<File, File>();
for (Entry<File, File> mapping : renameMap.entrySet()) {
@ -388,7 +369,7 @@ class RenameAction extends AbstractAction {
todo.put(source, destination);
}
}
// call native shell move/copy
try {
shell.rename(todo);
@ -405,15 +386,14 @@ class RenameAction extends AbstractAction {
}
postprocess.release();
}
return renameLog;
}
@Override
public boolean cancel() {
throw new UnsupportedOperationException();
}
}
}

View File

@ -53,6 +53,7 @@ import net.sourceforge.filebot.WebServices;
import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.ui.Language;
import net.sourceforge.filebot.ui.rename.RenameModel.FormattedFuture;
import net.sourceforge.filebot.ui.transfer.LoadAction;
import net.sourceforge.filebot.web.AudioTrack;
import net.sourceforge.filebot.web.AudioTrackFormat;
import net.sourceforge.filebot.web.Episode;
@ -183,7 +184,12 @@ public class RenamePanel extends JComponent {
@Override
public void actionPerformed(ActionEvent e) {
// show popup on actionPerformed only when names list is empty
if (renameModel.size() > 0 && !renameModel.hasComplement(0)) {
if (renameModel.size() == 0) {
new LoadAction(filesList.getTransferablePolicy()).actionPerformed(e);
if (renameModel.size() > 0) {
fetchPopupAction.actionPerformed(e);
}
} else if (renameModel.size() > 0 && !renameModel.hasComplement(0)) {
fetchPopupAction.actionPerformed(e);
}
}

View File

@ -103,7 +103,7 @@
<div>
<ul>
<li><a href="http://www.filebot.net/forums/">Forums</a></li>
<li><a href="http://www.filebot.net/forums/viewtopic.php?f=3&amp;t=7">FAQ</a></li>
<li><a href="http://www.filebot.net/forums/viewtopic.php?f=3&amp;t=7#p7">Manual</a></li>
<li><a href="http://www.filebot.net/#download">Download</a></li>
</ul>
</div>