diff --git a/source/net/sourceforge/filebot/ui/panel/rename/MatchModel.java b/source/net/sourceforge/filebot/ui/panel/rename/MatchModel.java index 821e5493..88a493b9 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/MatchModel.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/MatchModel.java @@ -83,7 +83,11 @@ public class MatchModel { public boolean hasComplement(int index) { - return source.get(index).getValue() != null && source.get(index).getCandidate() != null; + if (index >= 0 && index < size()) { + return source.get(index).getValue() != null && source.get(index).getCandidate() != null; + } + + return false; } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java b/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java index fa64469a..1e4e06fb 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java @@ -2,11 +2,17 @@ package net.sourceforge.filebot.ui.panel.rename; +import static java.util.Collections.*; +import static net.sourceforge.tuned.ui.TunedUtilities.*; + +import java.awt.Window; import java.awt.event.ActionEvent; import java.io.File; +import java.util.AbstractList; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.Map.Entry; import java.util.logging.Logger; @@ -21,11 +27,11 @@ class RenameAction extends AbstractAction { public RenameAction(RenameModel model) { - super("Rename", ResourceManager.getIcon("action.rename")); - - putValue(SHORT_DESCRIPTION, "Rename files"); - this.model = model; + + putValue(NAME, "Rename"); + putValue(SMALL_ICON, ResourceManager.getIcon("action.rename")); + putValue(SHORT_DESCRIPTION, "Rename files"); } @@ -33,7 +39,7 @@ class RenameAction extends AbstractAction { List> renameLog = new ArrayList>(); try { - for (Entry mapping : model.getRenameMap().entrySet()) { + for (Entry mapping : validate(model.getRenameMap(), getWindow(evt.getSource()))) { // rename file //DISABLE RENAME // if (!mapping.getKey().renameTo(mapping.getValue())) @@ -44,7 +50,9 @@ class RenameAction extends AbstractAction { } // renamed all matches successfully - Logger.getLogger("ui").info(String.format("%d files renamed.", renameLog.size())); + if (renameLog.size() > 0) { + Logger.getLogger("ui").info(String.format("%d files renamed.", renameLog.size())); + } } catch (Exception e) { // could not rename one of the files, revert all changes Logger.getLogger("ui").warning(e.getMessage()); @@ -63,7 +71,58 @@ class RenameAction extends AbstractAction { } } + // remove renamed matches + for (Entry entry : renameLog) { + // find index of source file + int index = model.files().indexOf(entry.getKey()); + + // remove complete match + model.matches().remove(index); + } + // update history - HistorySpooler.getInstance().append(renameLog); + if (renameLog.size() > 0) { + HistorySpooler.getInstance().append(renameLog); + } } + + + private Iterable> validate(Map renameMap, Window parent) { + final List> source = new ArrayList>(renameMap.entrySet()); + + List destinationFileNameView = new AbstractList() { + + @Override + public String get(int index) { + return source.get(index).getValue().getName(); + } + + + @Override + public String set(int index, String name) { + Entry entry = source.get(index); + File old = entry.getValue(); + + // update name + entry.setValue(new File(old.getParent(), name)); + + return old.getName(); + } + + + @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(); + } + } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/ValidateDialog.java b/source/net/sourceforge/filebot/ui/panel/rename/ValidateDialog.java new file mode 100644 index 00000000..b12c5f89 --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/rename/ValidateDialog.java @@ -0,0 +1,284 @@ + +package net.sourceforge.filebot.ui.panel.rename; + + +import static java.util.Collections.*; +import static net.sourceforge.filebot.FileBotUtilities.*; +import static net.sourceforge.tuned.ui.TunedUtilities.*; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.KeyStroke; + +import net.miginfocom.swing.MigLayout; +import net.sourceforge.filebot.ResourceManager; + + +class ValidateDialog extends JDialog { + + private final JList list; + + private final Action validateAction = new ValidateAction(); + private final Action continueAction = new ContinueAction(); + private final Action cancelAction = new CancelAction(); + + private String[] model; + + private boolean cancelled = true; + + + public ValidateDialog(Window owner, Collection source) { + super(owner, "Invalid Names", ModalityType.DOCUMENT_MODAL); + + model = source.toArray(new String[0]); + + list = new JList(model); + list.setEnabled(false); + + list.setCellRenderer(new HighlightListCellRenderer(INVALID_CHARACTERS_PATTERN, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4)); + + JLabel label = new JLabel("Some names contain invalid characters:"); + + JComponent content = (JComponent) getContentPane(); + + content.setLayout(new MigLayout("insets dialog, nogrid, fill")); + + content.add(label, "wrap"); + content.add(new JScrollPane(list), "grow, wrap 2mm"); + + content.add(new JButton(validateAction), "align center"); + content.add(new AlphaButton(continueAction), "gap related"); + content.add(new JButton(cancelAction), "gap 12mm"); + + installAction(content, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction); + + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setLocationByPlatform(true); + setSize(365, 280); + } + + + public List getModel() { + return unmodifiableList(Arrays.asList(model)); + } + + + public boolean isCancelled() { + return cancelled; + } + + + private void finish(boolean cancelled) { + this.cancelled = cancelled; + + setVisible(false); + dispose(); + } + + + private class ValidateAction extends AbstractAction { + + public ValidateAction() { + putValue(NAME, "Validate"); + putValue(SMALL_ICON, ResourceManager.getIcon("dialog.continue")); + putValue(SHORT_DESCRIPTION, "Remove invalid characters"); + } + + + @Override + public void actionPerformed(ActionEvent e) { + // validate names + for (int i = 0; i < model.length; i++) { + model[i] = validateFileName(model[i]); + } + + // update view + list.repaint(); + + continueAction.putValue(SMALL_ICON, getValue(SMALL_ICON)); + continueAction.putValue(ContinueAction.ALPHA, 1.0f); + + // disable action + setEnabled(false); + } + } + + + private class ContinueAction extends AbstractAction { + + public static final String ALPHA = "alpha"; + + + public ContinueAction() { + putValue(NAME, "Continue"); + putValue(SMALL_ICON, ResourceManager.getIcon("dialog.continue.invalid")); + putValue(ALPHA, 0.75f); + } + + + public void actionPerformed(ActionEvent e) { + finish(false); + } + } + + + protected class CancelAction extends AbstractAction { + + public CancelAction() { + putValue(NAME, "Cancel"); + putValue(SMALL_ICON, ResourceManager.getIcon("dialog.cancel")); + } + + + public void actionPerformed(ActionEvent e) { + finish(true); + } + } + + + protected static class AlphaButton extends JButton { + + private float alpha; + + + public AlphaButton(Action action) { + super(action); + + } + + + @Override + protected void configurePropertiesFromAction(Action action) { + super.configurePropertiesFromAction(action); + + alpha = getAlpha(action); + } + + + @Override + protected void actionPropertyChanged(Action action, String propertyName) { + super.actionPropertyChanged(action, propertyName); + + if (propertyName.equals(ContinueAction.ALPHA)) { + alpha = getAlpha(action); + } + } + + + private float getAlpha(Action action) { + Object value = action.getValue(ContinueAction.ALPHA); + + if (value instanceof Float) { + return (Float) value; + } + + return 1.0f; + } + + + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g2d.setComposite(AlphaComposite.SrcOver.derive(alpha)); + super.paintComponent(g2d); + } + } + + + public static boolean validate(Component parent, List source) { + IndexView invalid = new IndexView(source); + + for (int i = 0; i < source.size(); i++) { + String name = source.get(i); + + if (isInvalidFileName(name)) { + invalid.addIndex(i); + } + } + + if (invalid.isEmpty()) { + // nothing to do + return true; + } + + ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalid); + + // show and block + dialog.setVisible(true); + + if (dialog.isCancelled()) { + // no output + return false; + } + + List valid = dialog.getModel(); + + // validate source list via index view + for (int i = 0; i < invalid.size(); i++) { + invalid.set(i, valid.get(i)); + } + + return true; + } + + + private static class IndexView extends AbstractList { + + private final List mapping = new ArrayList(); + + private final List source; + + + public IndexView(List source) { + this.source = source; + } + + + public boolean addIndex(int index) { + return mapping.add(index); + } + + + @Override + public E get(int index) { + int sourceIndex = mapping.get(index); + + if (sourceIndex >= 0) + return source.get(sourceIndex); + + return null; + } + + + @Override + public E set(int index, E element) { + return source.set(mapping.get(index), element); + } + + + @Override + public int size() { + return mapping.size(); + } + } + +} diff --git a/source/net/sourceforge/filebot/ui/panel/rename/ValidateNamesDialog.java b/source/net/sourceforge/filebot/ui/panel/rename/ValidateNamesDialog.java deleted file mode 100644 index d28416fe..00000000 --- a/source/net/sourceforge/filebot/ui/panel/rename/ValidateNamesDialog.java +++ /dev/null @@ -1,215 +0,0 @@ - -package net.sourceforge.filebot.ui.panel.rename; - - -import static net.sourceforge.filebot.FileBotUtilities.*; - -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Component; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.util.List; - -import javax.swing.AbstractAction; -import javax.swing.AbstractListModel; -import javax.swing.Action; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JScrollPane; -import javax.swing.KeyStroke; - -import net.miginfocom.swing.MigLayout; -import net.sourceforge.filebot.ResourceManager; -import net.sourceforge.tuned.ui.TunedUtilities; - - -class ValidateNamesDialog extends JDialog { - - private final List source; - private String[] validatedValues; - - private boolean cancelled = true; - - protected final JList list; - - protected final Action validateAction = new ValidateAction(); - protected final Action continueAction = new ContinueAction(); - protected final Action cancelAction = new CancelAction(); - - - public ValidateNamesDialog(Window owner, List source) { - super(owner, "Invalid Names", ModalityType.DOCUMENT_MODAL); - - this.source = source; - - setDefaultCloseOperation(DISPOSE_ON_CLOSE); - - list = new JList(source.toArray()); - list.setEnabled(false); - - list.setCellRenderer(new HighlightListCellRenderer(INVALID_CHARACTERS_PATTERN, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4)); - - JLabel label = new JLabel("Some names contain invalid characters:"); - - JComponent c = (JComponent) getContentPane(); - - c.setLayout(new MigLayout("insets dialog, nogrid, fill")); - - c.add(label, "wrap"); - c.add(new JScrollPane(list), "grow, wrap 2mm"); - - c.add(new JButton(validateAction), "align center"); - c.add(new AlphaButton(continueAction), "gap related"); - c.add(new JButton(cancelAction), "gap 12mm"); - - setSize(365, 280); - setLocation(TunedUtilities.getPreferredLocation(this)); - - TunedUtilities.installAction(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction); - } - - - public boolean isCancelled() { - return cancelled; - } - - - private void finish(boolean cancelled) { - this.cancelled = cancelled; - - setVisible(false); - dispose(); - - if (validatedValues != null && !cancelled) { - // update source list - for (int i = 0; i < validatedValues.length; i++) { - source.set(i, validatedValues[i]); - } - } - } - - - private class ValidateAction extends AbstractAction { - - public ValidateAction() { - super("Validate", ResourceManager.getIcon("dialog.continue")); - putValue(SHORT_DESCRIPTION, "Remove invalid characters"); - } - - - @Override - public void actionPerformed(ActionEvent e) { - validatedValues = new String[source.size()]; - - for (int i = 0; i < validatedValues.length; i++) { - validatedValues[i] = validateFileName(source.get(i)); - } - - setEnabled(false); - - continueAction.putValue(SMALL_ICON, getValue(SMALL_ICON)); - continueAction.putValue(ContinueAction.ALPHA, 1.0f); - - // update displayed values - list.setModel(new AbstractListModel() { - - @Override - public Object getElementAt(int i) { - return validatedValues[i]; - } - - - @Override - public int getSize() { - return validatedValues.length; - } - }); - } - }; - - - private class ContinueAction extends AbstractAction { - - public static final String ALPHA = "Alpha"; - - - public ContinueAction() { - super("Continue", ResourceManager.getIcon("dialog.continue.invalid")); - putValue(ALPHA, 0.75f); - } - - - public void actionPerformed(ActionEvent e) { - finish(false); - } - }; - - - protected class CancelAction extends AbstractAction { - - public CancelAction() { - super("Cancel", ResourceManager.getIcon("dialog.cancel")); - } - - - public void actionPerformed(ActionEvent e) { - finish(true); - } - }; - - - protected static class AlphaButton extends JButton { - - private float alpha; - - - public AlphaButton(Action action) { - super(action); - alpha = getAlpha(action); - } - - - @Override - protected void actionPropertyChanged(Action action, String propertyName) { - super.actionPropertyChanged(action, propertyName); - - if (propertyName.equals(ContinueAction.ALPHA)) { - alpha = getAlpha(action); - } - } - - - private float getAlpha(Action action) { - Object value = action.getValue(ContinueAction.ALPHA); - - if (value instanceof Float) { - return (Float) value; - } - - return 1.0f; - } - - - @Override - protected void paintComponent(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - g2d.setComposite(AlphaComposite.SrcOver.derive(alpha)); - super.paintComponent(g2d); - } - } - - - public static boolean showDialog(Component parent, List source) { - ValidateNamesDialog dialog = new ValidateNamesDialog(TunedUtilities.getWindow(parent), source); - - dialog.setVisible(true); - - return !dialog.isCancelled(); - } -}