1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-08-13 17:03:45 -04:00

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

View File

@ -53,6 +53,7 @@ import net.sourceforge.filebot.WebServices;
import net.sourceforge.filebot.similarity.Match; import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.ui.Language; import net.sourceforge.filebot.ui.Language;
import net.sourceforge.filebot.ui.rename.RenameModel.FormattedFuture; 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.AudioTrack;
import net.sourceforge.filebot.web.AudioTrackFormat; import net.sourceforge.filebot.web.AudioTrackFormat;
import net.sourceforge.filebot.web.Episode; import net.sourceforge.filebot.web.Episode;
@ -183,7 +184,12 @@ public class RenamePanel extends JComponent {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
// show popup on actionPerformed only when names list is empty // 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); fetchPopupAction.actionPerformed(e);
} }
} }

View File

@ -103,7 +103,7 @@
<div> <div>
<ul> <ul>
<li><a href="http://www.filebot.net/forums/">Forums</a></li> <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> <li><a href="http://www.filebot.net/#download">Download</a></li>
</ul> </ul>
</div> </div>