* better handling of move/rename operations (display validation dialog, create folders if necessary, working revert)

This commit is contained in:
Reinhard Pointner 2009-07-20 11:03:24 +00:00
parent 472ed8aac0
commit 46764f7d63
5 changed files with 68 additions and 55 deletions

View File

@ -11,7 +11,6 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map.Entry;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
@ -93,8 +92,15 @@ class History {
private String to; private String to;
private Element() { public Element() {
// hide constructor // used by JAXB
}
public Element(String from, String to, File dir) {
this.from = from;
this.to = to;
this.dir = dir;
} }
@ -136,29 +142,10 @@ class History {
} }
public void add(Iterable<Entry<File, File>> elements) { public void add(Collection<Element> elements) {
Sequence sequence = new Sequence(); Sequence sequence = new Sequence();
sequence.date = new Date(); sequence.date = new Date();
sequence.elements = new ArrayList<Element>(); sequence.elements = new ArrayList<Element>(elements);
for (Entry<File, File> entry : elements) {
File from = entry.getKey();
File to = entry.getValue();
// sanity check, parent folder must be the same for both files
if (!from.getParentFile().equals(to.getParentFile())) {
throw new IllegalArgumentException(String.format("Illegal entry: ", entry));
}
Element element = new Element();
element.dir = from.getParentFile();
element.from = from.getName();
element.to = to.getName();
sequence.elements.add(element);
}
add(sequence); add(sequence);
} }

View File

@ -6,10 +6,14 @@ import static net.sourceforge.filebot.ui.panel.rename.History.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sourceforge.filebot.ui.panel.rename.History.Element;
final class HistorySpooler { final class HistorySpooler {
@ -45,9 +49,15 @@ final class HistorySpooler {
} }
public synchronized void append(Iterable<Entry<File, File>> elements) { public synchronized void append(Iterable<Entry<File, String>> elements) {
List<Element> sequence = new ArrayList<Element>();
for (Entry<File, String> element : elements) {
sequence.add(new Element(element.getKey().getName(), element.getValue(), element.getKey().getParentFile()));
}
// append to session history // append to session history
sessionHistory.add(elements); sessionHistory.add(sequence);
} }

View File

@ -37,13 +37,12 @@ class RenameAction extends AbstractAction {
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
List<Entry<File, File>> renameLog = new ArrayList<Entry<File, File>>(); List<Entry<File, String>> renameLog = new ArrayList<Entry<File, String>>();
try { try {
for (Entry<File, File> mapping : validate(model.getRenameMap(), getWindow(evt.getSource()))) { for (Entry<File, String> mapping : validate(model.getRenameMap(), getWindow(evt.getSource()))) {
// rename file // rename file, throw exception on failure
if (!mapping.getKey().renameTo(mapping.getValue())) rename(mapping.getKey(), mapping.getValue());
throw new IOException(String.format("Failed to rename file: \"%s\".", mapping.getKey().getName()));
// remember successfully renamed matches for history entry and possible revert // remember successfully renamed matches for history entry and possible revert
renameLog.add(mapping); renameLog.add(mapping);
@ -58,21 +57,27 @@ class RenameAction extends AbstractAction {
Logger.getLogger("ui").warning(e.getMessage()); Logger.getLogger("ui").warning(e.getMessage());
// revert rename operations in reverse order // revert rename operations in reverse order
for (ListIterator<Entry<File, File>> iterator = renameLog.listIterator(renameLog.size()); iterator.hasPrevious();) { for (ListIterator<Entry<File, String>> iterator = renameLog.listIterator(renameLog.size()); iterator.hasPrevious();) {
Entry<File, File> mapping = iterator.previous(); Entry<File, String> mapping = iterator.previous();
try {
File source = mapping.getKey();
File destination = new File(source.getParentFile(), mapping.getValue());
// revert rename
rename(destination, source.getName());
if (mapping.getValue().renameTo(mapping.getKey())) {
// remove reverted rename operation from log // remove reverted rename operation from log
iterator.remove(); iterator.remove();
} else { } catch (IOException ioe) {
// failed to revert rename operation // failed to revert rename operation
Logger.getLogger("ui").severe(String.format("Failed to revert file: \"%s\".", mapping.getValue().getName())); Logger.getLogger("ui").severe(String.format("Failed to revert file: \"%s\".", mapping.getValue()));
} }
} }
} }
// remove renamed matches // remove renamed matches
for (Entry<File, File> entry : renameLog) { for (Entry<File, ?> entry : renameLog) {
// find index of source file // find index of source file
int index = model.files().indexOf(entry.getKey()); int index = model.files().indexOf(entry.getKey());
@ -87,26 +92,40 @@ class RenameAction extends AbstractAction {
} }
private Iterable<Entry<File, File>> validate(Map<File, File> renameMap, Window parent) { private File rename(File file, String name) throws IOException {
final List<Entry<File, File>> source = new ArrayList<Entry<File, File>>(renameMap.entrySet()); // same folder, different name
File destination = new File(file.getParentFile(), name);
File destinationFolder = destination.getParentFile();
// create parent folder if necessary
if (!destinationFolder.isDirectory()) {
if (!destinationFolder.mkdirs()) {
throw new IOException("Failed to create folder: " + destinationFolder);
}
}
if (!file.renameTo(destination)) {
throw new IOException("Failed to rename file: " + file.getName());
}
return destination;
}
private Iterable<Entry<File, String>> validate(Map<File, String> renameMap, Window parent) {
final List<Entry<File, String>> source = new ArrayList<Entry<File, String>>(renameMap.entrySet());
List<String> destinationFileNameView = new AbstractList<String>() { List<String> destinationFileNameView = new AbstractList<String>() {
@Override @Override
public String get(int index) { public String get(int index) {
return source.get(index).getValue().getName(); return source.get(index).getValue();
} }
@Override @Override
public String set(int index, String name) { public String set(int index, String name) {
Entry<File, File> entry = source.get(index); return source.get(index).setValue(name);
File old = entry.getValue();
// update name
entry.setValue(new File(old.getParent(), name));
return old.getName();
} }

View File

@ -78,8 +78,8 @@ public class RenameModel extends MatchModel<Object, File> {
} }
public Map<File, File> getRenameMap() { public Map<File, String> getRenameMap() {
Map<File, File> map = new LinkedHashMap<File, File>(); Map<File, String> map = new LinkedHashMap<File, String>();
for (int i = 0; i < names.size(); i++) { for (int i = 0; i < names.size(); i++) {
if (hasComplement(i)) { if (hasComplement(i)) {
@ -108,11 +108,8 @@ public class RenameModel extends MatchModel<Object, File> {
} }
} }
// same parent, different name
File newFile = new File(originalFile.getParentFile(), nameBuilder.toString());
// insert mapping // insert mapping
if (map.put(originalFile, newFile) != null) { if (map.put(originalFile, nameBuilder.toString()) != null) {
throw new IllegalStateException(String.format("Duplicate file entry: \"%s\"", originalFile.getName())); throw new IllegalStateException(String.format("Duplicate file entry: \"%s\"", originalFile.getName()));
} }
} }

View File

@ -91,7 +91,7 @@ public class RenamePanel extends JComponent {
filesList.getListComponent().setCellRenderer(cellrenderer); filesList.getListComponent().setCellRenderer(cellrenderer);
EventSelectionModel<Match<Object, File>> selectionModel = new EventSelectionModel<Match<Object, File>>(renameModel.matches()); EventSelectionModel<Match<Object, File>> selectionModel = new EventSelectionModel<Match<Object, File>>(renameModel.matches());
selectionModel.setSelectionMode(ListSelection.MULTIPLE_INTERVAL_SELECTION_DEFENSIVE); selectionModel.setSelectionMode(ListSelection.SINGLE_SELECTION);
// use the same selection model for both lists to synchronize selection // use the same selection model for both lists to synchronize selection
namesList.getListComponent().setSelectionModel(selectionModel); namesList.getListComponent().setSelectionModel(selectionModel);