From 3ed58bda08dea6be3f72e98d9283dc3892e1f801 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Fri, 17 Jan 2014 14:07:38 +0000 Subject: [PATCH] * [Windows] allow renaming of files where just the upper/lower case is different --- .../filebot/ui/rename/RenameAction.java | 2 +- .../ui/rename/RenameListCellRenderer.java | 136 ++++++++---------- .../net/sourceforge/tuned/FileUtilities.java | 3 +- 3 files changed, 62 insertions(+), 79 deletions(-) diff --git a/source/net/sourceforge/filebot/ui/rename/RenameAction.java b/source/net/sourceforge/filebot/ui/rename/RenameAction.java index 688a4f65..10eee95c 100644 --- a/source/net/sourceforge/filebot/ui/rename/RenameAction.java +++ b/source/net/sourceforge/filebot/ui/rename/RenameAction.java @@ -303,7 +303,7 @@ class RenameAction extends AbstractAction { // rename file, throw exception on failure File source = mapping.getKey(); File destination = resolveDestination(mapping.getKey(), mapping.getValue(), false); - if (!source.equals(destination)) { + if (!source.getAbsolutePath().equals(destination.getAbsolutePath())) { action.rename(source, destination); } diff --git a/source/net/sourceforge/filebot/ui/rename/RenameListCellRenderer.java b/source/net/sourceforge/filebot/ui/rename/RenameListCellRenderer.java index b8df0a1d..524fb2ac 100644 --- a/source/net/sourceforge/filebot/ui/rename/RenameListCellRenderer.java +++ b/source/net/sourceforge/filebot/ui/rename/RenameListCellRenderer.java @@ -1,7 +1,5 @@ - package net.sourceforge.filebot.ui.rename; - import static net.sourceforge.filebot.similarity.EpisodeMetrics.*; import static net.sourceforge.tuned.FileUtilities.*; import static net.sourceforge.tuned.ui.TunedUtilities.*; @@ -34,44 +32,41 @@ import net.sourceforge.tuned.FileUtilities; import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer; import net.sourceforge.tuned.ui.GradientStyle; - class RenameListCellRenderer extends DefaultFancyListCellRenderer { - + private RenameModel renameModel; - + private TypeRenderer typeRenderer = new TypeRenderer(); - + private Color noMatchGradientBeginColor = new Color(0xB7B7B7); private Color noMatchGradientEndColor = new Color(0x9A9A9A); - + private Color warningGradientBeginColor = Color.RED; private Color warningGradientEndColor = new Color(0xDC143C); - + private Color pathRainbowBeginColor = new Color(0xCC3300); private Color pathRainbowEndColor = new Color(0x008080); - - + public RenameListCellRenderer(RenameModel renameModel) { super(new Insets(4, 7, 4, 7)); - + this.renameModel = renameModel; setHighlightingEnabled(false); - + setLayout(new MigLayout("insets 0, fill", "align left", "align center")); this.add(typeRenderer, "gap rel:push, hidemode 3"); } - - + @Override public void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - + // reset decoration / highlighting setOpaque(false); setIcon(null); typeRenderer.setVisible(false); typeRenderer.setAlpha(1.0f); - + // render unmatched values differently if (!renameModel.hasComplement(index)) { if (isSelected) { @@ -81,16 +76,16 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer { typeRenderer.setAlpha(0.5f); } } - + if (renameModel.preserveExtension() && index < renameModel.size() && renameModel.getMatch(index).getCandidate() != null) { typeRenderer.setText(getType(renameModel.getMatch(index).getCandidate())); typeRenderer.setVisible(true); } - + if (value instanceof File) { // display file extension File file = (File) value; - + if (renameModel.preserveExtension()) { setText(FileUtilities.getName(file)); } else { @@ -100,14 +95,14 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer { // display progress icon FormattedFuture formattedFuture = (FormattedFuture) value; float matchProbablity = renameModel.hasComplement(index) ? getMatchProbablity(formattedFuture.getMatch()) : 1; - + if (formattedFuture.isDone() && !formattedFuture.isCancelled()) { if (!renameModel.preserveExtension() && renameModel.hasComplement(index)) { // absolute path mode File file = renameModel.getMatch(index).getCandidate(); File path = resolveAbsolutePath(file.getParentFile(), formattedFuture.toString(), null); setText(isSelected || matchProbablity < 1 ? formatPath(path) : colorizePath(path, true)); - + String ext = getExtension(path); typeRenderer.setText(ext != null ? ext.toLowerCase() : "MISSING EXTENSION"); if (file.isDirectory()) { @@ -122,25 +117,25 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer { } else { setText(formattedFuture.preview()); // default text } - + switch (formattedFuture.getState()) { - case PENDING: - setIcon(ResourceManager.getIcon("worker.pending")); - break; - case STARTED: - setIcon(ResourceManager.getIcon("worker.started")); - break; + case PENDING: + setIcon(ResourceManager.getIcon("worker.pending")); + break; + case STARTED: + setIcon(ResourceManager.getIcon("worker.started")); + break; } - + if (renameModel.hasComplement(index)) { setOpaque(true); // enable paint background setBackground(derive(warningGradientBeginColor, (1 - matchProbablity) * 0.5f)); // alpha indicates match probability - + if (matchProbablity < 1) { if (isSelected) { setGradientColors(warningGradientBeginColor, warningGradientEndColor); setIcon(ResourceManager.getIcon("status.warning")); - + if (formattedFuture.isComplexFormat()) { typeRenderer.setVisible(true); typeRenderer.setAlpha(1.0f); @@ -148,40 +143,38 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer { } } } - + // check if files already exist FormattedFuture pathFuture = (FormattedFuture) value; if (pathFuture.isDone() && !pathFuture.isCancelled()) { File from = renameModel.getMatch(index).getCandidate(); File to = resolveAbsolutePath(from.getParentFile(), pathFuture.toString(), renameModel.preserveExtension() ? getExtension(from) : null); - if (from.equals(to)) { + if (from.getAbsolutePath().equals(to.getAbsolutePath())) { setIcon(ResourceManager.getIcon("dialog.continue")); - } else if (to.exists()) { - setIcon(ResourceManager.getIcon("dialog.cancel")); + } else if (to.exists() && !to.equals(from)) { + setIcon(ResourceManager.getIcon("dialog.cancel")); // take into account that on Windows equals/exists is case-insensitive which we have to work around } } } } } - - + protected String formatPath(File file) { return normalizePathSeparators(file.getPath()); } - - + protected String colorizePath(File file, boolean hasExtension) { List path = listPath(file); StringBuilder html = new StringBuilder(512); html.append(""); - + // colorize parent path for (int i = 0; i < path.size() - 1; i++) { float f = (path.size() <= 2) ? 1 : (float) i / (path.size() - 2); Color c = interpolateHSB(pathRainbowBeginColor, pathRainbowEndColor, f); html.append(String.format("%4$s/", c.getRed(), c.getGreen(), c.getBlue(), escapeHTML(FileUtilities.getName(path.get(i))))); } - + // only colorize extension if (hasExtension) { html.append(escapeHTML(FileUtilities.getName(file))); @@ -192,100 +185,89 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer { } else { html.append(file.getName()); } - + return html.append("").toString(); } - - + protected File resolveAbsolutePath(File targetDir, String path, String extension) { File f = new File(extension == null || extension.isEmpty() ? path : String.format("%s.%s", path, extension)); if (!f.isAbsolute()) { f = new File(targetDir, f.getPath()); // resolve path against target folder } - - try { - return f.getCanonicalFile(); - } catch (Exception e) { - return f.getAbsoluteFile(); - } + + return f.getAbsoluteFile(); } - - + protected float getMatchProbablity(Match match) { if (match.getValue() instanceof Episode) { float f = verificationMetric().getSimilarity(match.getValue(), match.getCandidate()); return (f + 1) / 2; // normalize -1..1 to 0..1 } - + SimilarityMetric fsm = new MetricCascade(new MetricMin(FileSize, 0), FileName, EpisodeIdentifier); float f = fsm.getSimilarity(match.getValue(), match.getCandidate()); if (f != 0) { return (Math.max(f, 0)); // normalize -1..1 and boost by 0.25 (because file <-> file matches are not necessarily about Episodes) } - + return 1; // assume match is OK } - - + protected String getType(File file) { if (file.isDirectory()) { return "Folder"; } - + String extension = FileUtilities.getExtension(file); if (extension != null) { return extension.toLowerCase(); } - + // some file with no extension return "File"; } - - + private static class TypeRenderer extends DefaultListCellRenderer { - + private final Insets margin = new Insets(0, 10, 0, 0); private final Insets padding = new Insets(0, 6, 0, 5); private final int arc = 10; - + private Color gradientBeginColor = new Color(0xFFCC00); private Color gradientEndColor = new Color(0xFF9900); - + private float alpha = 1.0f; - - + public TypeRenderer() { setOpaque(false); setForeground(new Color(0x141414)); - + setBorder(new CompoundBorder(new EmptyBorder(margin), new EmptyBorder(padding))); } - - + @Override protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; - + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - + RoundRectangle2D shape = new RoundRectangle2D.Float(margin.left, margin.top, getWidth() - (margin.left + margin.right), getHeight(), arc, arc); - + g2d.setComposite(AlphaComposite.SrcOver.derive(alpha)); - + g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(shape, gradientBeginColor, gradientEndColor)); g2d.fill(shape); - + g2d.setFont(getFont()); g2d.setPaint(getForeground()); - + Rectangle2D textBounds = g2d.getFontMetrics().getStringBounds(getText(), g2d); g2d.drawString(getText(), (float) (shape.getCenterX() - textBounds.getX() - (textBounds.getWidth() / 2f)), (float) (shape.getCenterY() - textBounds.getY() - (textBounds.getHeight() / 2))); } - - + public void setAlpha(float alpha) { this.alpha = alpha; } } - + } diff --git a/source/net/sourceforge/tuned/FileUtilities.java b/source/net/sourceforge/tuned/FileUtilities.java index c0edd252..a35f6551 100644 --- a/source/net/sourceforge/tuned/FileUtilities.java +++ b/source/net/sourceforge/tuned/FileUtilities.java @@ -52,7 +52,8 @@ public final class FileUtilities { } else { // move file try { - java.nio.file.Files.move(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); + // * On Windows ATOMIC_MOVE allows us to rename files even if only lower/upper-case changes (without ATOMIC_MOVE the operation would be ignored) + java.nio.file.Files.move(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); } catch (LinkageError e) { org.apache.commons.io.FileUtils.moveFile(source, destination); // use "copy and delete" as fallback if standard rename fails }