* improved support for move/rename
This commit is contained in:
parent
0a2d323ac4
commit
f61b084769
|
@ -49,7 +49,6 @@ class ExpressionFormatDocument extends PlainDocument {
|
|||
RoundBrackets("()"),
|
||||
SquareBrackets("[]"),
|
||||
CurlyBrackets("{}"),
|
||||
RegexLiteral("//"),
|
||||
SingleQuoteStringLiteral("''"),
|
||||
DoubleQuoteStringLiteral("\"\"");
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ import net.sourceforge.tuned.ui.TunedUtilities;
|
|||
|
||||
class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
|
||||
|
||||
private final JTextComponent textComponent = new JTextField();
|
||||
|
||||
private Pattern pattern;
|
||||
private Highlighter.HighlightPainter highlightPainter;
|
||||
protected final JTextComponent textComponent = new JTextField();
|
||||
|
||||
protected final Pattern pattern;
|
||||
protected final Highlighter.HighlightPainter highlightPainter;
|
||||
|
||||
|
||||
public HighlightListCellRenderer(Pattern pattern, Highlighter.HighlightPainter highlightPainter, int padding) {
|
||||
super(new Insets(0, 0, 0, 0));
|
||||
|
||||
|
@ -84,7 +84,7 @@ class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class HighlightUpdateListener implements DocumentListener {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -531,8 +531,14 @@ class HistoryDialog extends JDialog {
|
|||
File dir = directory != null ? directory : element.dir();
|
||||
|
||||
// reverse
|
||||
File from = new File(dir, element.to());
|
||||
File to = new File(dir, element.from());
|
||||
File from = new File(element.to());
|
||||
File to = new File(element.from());
|
||||
|
||||
// resolve against given directory or against the original base directory if the path is not absolute
|
||||
if (!from.isAbsolute())
|
||||
from = new File(dir, directory == null ? from.getPath() : from.getName());
|
||||
if (!to.isAbsolute())
|
||||
to = new File(dir, directory == null ? to.getPath() : to.getName());
|
||||
|
||||
renameMap.put(from, to);
|
||||
}
|
||||
|
@ -847,7 +853,12 @@ class HistoryDialog extends JDialog {
|
|||
|
||||
public boolean isBroken(int row) {
|
||||
Element element = data.get(row);
|
||||
File file = new File(element.dir(), element.to());
|
||||
|
||||
File file = new File(element.to());
|
||||
|
||||
// resolve relative path
|
||||
if (!file.isAbsolute())
|
||||
file = new File(element.dir(), file.getPath());
|
||||
|
||||
return !file.exists();
|
||||
}
|
||||
|
|
|
@ -91,10 +91,15 @@ class RenameAction extends AbstractAction {
|
|||
|
||||
|
||||
private File rename(File file, String path) throws IOException {
|
||||
// same folder, different name
|
||||
File destination = new File(file.getParentFile(), path);
|
||||
File destination = new File(path);
|
||||
|
||||
// name may be a relative path, so we can't use file.getParentFile()
|
||||
// resolve destination
|
||||
if (!destination.isAbsolute()) {
|
||||
// same folder, different name
|
||||
destination = new File(file.getParentFile(), path);
|
||||
}
|
||||
|
||||
// make sure we that we can create the destination folder structure
|
||||
File destinationFolder = destination.getParentFile();
|
||||
|
||||
// create parent folder if necessary
|
||||
|
|
|
@ -10,11 +10,15 @@ import java.awt.Color;
|
|||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
|
@ -25,6 +29,7 @@ import javax.swing.JLabel;
|
|||
import javax.swing.JList;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
|
@ -47,13 +52,37 @@ class ValidateDialog extends JDialog {
|
|||
list = new JList(model);
|
||||
list.setEnabled(false);
|
||||
|
||||
list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4));
|
||||
list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4) {
|
||||
|
||||
@Override
|
||||
protected void updateHighlighter() {
|
||||
textComponent.getHighlighter().removeAllHighlights();
|
||||
|
||||
Matcher matcher = pattern.matcher(textComponent.getText());
|
||||
File file = new File(textComponent.getText());
|
||||
|
||||
// highlight path components separately to ignore "illegal characters" that are either path separators or part of the drive letter (e.g. ':' in 'E:')
|
||||
for (File element : listPath(file)) {
|
||||
int limit = element.getPath().length();
|
||||
matcher.region(limit - element.getName().length(), limit);
|
||||
|
||||
while (matcher.find()) {
|
||||
try {
|
||||
textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter);
|
||||
} catch (BadLocationException e) {
|
||||
//should not happen
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JLabel label = new JLabel("Some names contain invalid characters:");
|
||||
|
||||
JComponent content = (JComponent) getContentPane();
|
||||
|
||||
content.setLayout(new MigLayout("insets dialog, nogrid, fill"));
|
||||
content.setLayout(new MigLayout("insets dialog, nogrid, fill", "", "[pref!][fill][pref!]"));
|
||||
|
||||
content.add(label, "wrap");
|
||||
content.add(new JScrollPane(list), "grow, wrap 2mm");
|
||||
|
@ -94,7 +123,8 @@ class ValidateDialog extends JDialog {
|
|||
public void actionPerformed(ActionEvent e) {
|
||||
// validate names
|
||||
for (int i = 0; i < model.length; i++) {
|
||||
model[i] = validateFileName(model[i]);
|
||||
// remove illegal characters
|
||||
model[i] = validateFilePath(new File(model[i])).getPath();
|
||||
}
|
||||
|
||||
// update view
|
||||
|
@ -126,22 +156,23 @@ class ValidateDialog extends JDialog {
|
|||
|
||||
|
||||
public static boolean validate(Component parent, List<String> source) {
|
||||
IndexView<String> invalid = new IndexView<String>(source);
|
||||
IndexView<String> invalidFilePaths = new IndexView<String>(source);
|
||||
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
String name = source.get(i);
|
||||
String path = source.get(i);
|
||||
|
||||
if (isInvalidFileName(name)) {
|
||||
invalid.addIndex(i);
|
||||
// invalid file names are also invalid file paths
|
||||
if (isInvalidFilePath(new File(path))) {
|
||||
invalidFilePaths.addIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid.isEmpty()) {
|
||||
// nothing to do
|
||||
// check if there is anything to do in the first place
|
||||
if (invalidFilePaths.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalid);
|
||||
ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalidFilePaths);
|
||||
|
||||
// show and block
|
||||
dialog.setVisible(true);
|
||||
|
@ -151,11 +182,11 @@ class ValidateDialog extends JDialog {
|
|||
return false;
|
||||
}
|
||||
|
||||
List<String> valid = dialog.getModel();
|
||||
List<String> validatedFilePaths = dialog.getModel();
|
||||
|
||||
// validate source list via index view
|
||||
for (int i = 0; i < invalid.size(); i++) {
|
||||
invalid.set(i, valid.get(i));
|
||||
for (int i = 0; i < invalidFilePaths.size(); i++) {
|
||||
invalidFilePaths.set(i, validatedFilePaths.get(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -9,6 +9,8 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -140,6 +142,17 @@ public final class FileUtilities {
|
|||
}
|
||||
|
||||
|
||||
public static List<File> listPath(File file) {
|
||||
LinkedList<File> nodes = new LinkedList<File>();
|
||||
|
||||
for (File node = file; node != null; node = node.getParentFile()) {
|
||||
nodes.addFirst(node);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
||||
public static List<File> listFiles(Iterable<File> folders, int maxDepth) {
|
||||
List<File> files = new ArrayList<File>();
|
||||
|
||||
|
@ -167,28 +180,65 @@ public final class FileUtilities {
|
|||
|
||||
|
||||
/**
|
||||
* Invalid filename characters: \, /, :, *, ?, ", <, >, |, \r and \n
|
||||
* Invalid file name characters: \, /, :, *, ?, ", <, >, |, \r and \n
|
||||
*/
|
||||
public static final Pattern ILLEGAL_CHARACTERS = Pattern.compile("[\\\\/:*?\"<>|\\r\\n]");
|
||||
|
||||
|
||||
/**
|
||||
* Strip filename of invalid characters
|
||||
* Strip file name of invalid characters
|
||||
*
|
||||
* @param filename original filename
|
||||
* @return valid filename stripped of invalid characters
|
||||
* @return valid file name stripped of invalid characters
|
||||
*/
|
||||
public static String validateFileName(CharSequence filename) {
|
||||
// strip invalid characters from filename
|
||||
// strip invalid characters from file name
|
||||
return ILLEGAL_CHARACTERS.matcher(filename).replaceAll("");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isInvalidFileName(CharSequence filename) {
|
||||
// check if file name contains any illegal characters
|
||||
return ILLEGAL_CHARACTERS.matcher(filename).find();
|
||||
}
|
||||
|
||||
|
||||
public static File validateFileName(File file) {
|
||||
// windows drives (e.g. c:, d:, etc.) are never invalid because name will be an empty string
|
||||
if (!isInvalidFileName(file.getName()))
|
||||
return file;
|
||||
|
||||
// validate file name only
|
||||
return new File(file.getParentFile(), validateFileName(file.getName()));
|
||||
}
|
||||
|
||||
|
||||
public static File validateFilePath(File path) {
|
||||
Iterator<File> nodes = listPath(path).iterator();
|
||||
|
||||
// initialize with root node, keep original root object if possible (so we don't loose the drive on windows)
|
||||
File validatedPath = validateFileName(nodes.next());
|
||||
|
||||
// validate the rest of the path
|
||||
while (nodes.hasNext()) {
|
||||
validatedPath = new File(validatedPath, validateFileName(nodes.next().getName()));
|
||||
}
|
||||
|
||||
return validatedPath;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isInvalidFilePath(File path) {
|
||||
// check if file name contains any illegal characters
|
||||
for (File node = path; node != null; node = node.getParentFile()) {
|
||||
if (isInvalidFileName(node.getName()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static final long KILO = 1024;
|
||||
public static final long MEGA = KILO * 1024;
|
||||
public static final long GIGA = MEGA * 1024;
|
||||
|
|
Loading…
Reference in New Issue