mirror of
https://github.com/mitb-archive/filebot
synced 2024-11-04 16:35:08 -05:00
* encapsulate the two rename Eventlists into RenameModel
* improved NameSimilarityMetric * mostly refactoring
This commit is contained in:
parent
c217d06eeb
commit
c5c12513fa
@ -30,7 +30,7 @@ public class NameSimilarityMetric implements SimilarityMetric {
|
||||
String name = removeEmbeddedChecksum(object.toString());
|
||||
|
||||
// normalize separators
|
||||
name = name.replaceAll("[\\._ ]+", " ");
|
||||
name = name.replaceAll("[^\\p{Alnum}]+", " ");
|
||||
|
||||
// normalize case and trim
|
||||
return name.trim().toLowerCase();
|
||||
|
@ -65,7 +65,7 @@ public abstract class AbstractSearchPanel<S, E> extends FileBotPanel {
|
||||
tabbedPaneGroup.setBorder(BorderFactory.createTitledBorder("Search Results"));
|
||||
tabbedPaneGroup.add(tabbedPane, "grow, wrap 8px");
|
||||
|
||||
setLayout(new MigLayout("nogrid, fill, insets 0 0 5px 0"));
|
||||
setLayout(new MigLayout("nogrid, fill, insets 10px 10px 15px 10px"));
|
||||
add(searchTextField, "alignx center, gapafter indent");
|
||||
add(new JButton(searchAction), "gap 18px, wrap 10px");
|
||||
add(tabbedPaneGroup, "grow");
|
||||
|
@ -26,17 +26,16 @@ import ca.odell.glazedlists.swing.EventListModel;
|
||||
|
||||
public class FileBotList<E> extends JComponent {
|
||||
|
||||
protected final EventList<E> model = new BasicEventList<E>();
|
||||
protected EventList<E> model = new BasicEventList<E>();
|
||||
|
||||
protected final JList list = new JList(new EventListModel<E>(model));
|
||||
protected JList list = new JList(new EventListModel<E>(model));
|
||||
|
||||
protected final JScrollPane listScrollPane = new JScrollPane(list);
|
||||
protected JScrollPane listScrollPane = new JScrollPane(list);
|
||||
|
||||
private String title = null;
|
||||
|
||||
|
||||
public FileBotList() {
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new TitledBorder(getTitle()));
|
||||
|
||||
@ -60,6 +59,12 @@ public class FileBotList<E> extends JComponent {
|
||||
}
|
||||
|
||||
|
||||
public void setModel(EventList<E> model) {
|
||||
this.model = model;
|
||||
list.setModel(new EventListModel<E>(model));
|
||||
}
|
||||
|
||||
|
||||
public JList getListComponent() {
|
||||
return list;
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
|
||||
|
||||
private JComponent createPageLayer() {
|
||||
JPanel pageLayer = new JPanel(new BorderLayout());
|
||||
pagePanel.setBorder(new EmptyBorder(10, 110, 10, 10));
|
||||
pagePanel.setBorder(new EmptyBorder(0, 95, 0, 0));
|
||||
|
||||
pageLayer.add(headerPanel, BorderLayout.NORTH);
|
||||
pageLayer.add(pagePanel, BorderLayout.CENTER);
|
||||
|
@ -29,7 +29,7 @@ public class AnalyzePanel extends FileBotPanel {
|
||||
|
||||
toolsPanel.setBorder(BorderFactory.createTitledBorder("Tools"));
|
||||
|
||||
setLayout(new MigLayout("insets 0, gapx 50, fill"));
|
||||
setLayout(new MigLayout("insets dialog, gapx 50, fill"));
|
||||
|
||||
add(fileTreePanel, "grow, sizegroupx column");
|
||||
add(toolsPanel, "grow, sizegroupx column");
|
||||
|
@ -5,7 +5,7 @@ package net.sourceforge.filebot.ui.panel.episodelist;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
|
||||
|
||||
public class SeasonSpinnerModel extends SpinnerNumberModel {
|
||||
class SeasonSpinnerModel extends SpinnerNumberModel {
|
||||
|
||||
public static final int ALL_SEASONS = 0;
|
||||
|
||||
|
@ -18,6 +18,7 @@ import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.JSpinner.NumberEditor;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
@ -57,10 +58,10 @@ public class ListPanel extends FileBotPanel {
|
||||
JSpinner fromSpinner = new JSpinner(fromSpinnerModel);
|
||||
JSpinner toSpinner = new JSpinner(toSpinnerModel);
|
||||
|
||||
fromSpinner.setEditor(new JSpinner.NumberEditor(fromSpinner, "#"));
|
||||
toSpinner.setEditor(new JSpinner.NumberEditor(toSpinner, "#"));
|
||||
fromSpinner.setEditor(new NumberEditor(fromSpinner, "#"));
|
||||
toSpinner.setEditor(new NumberEditor(toSpinner, "#"));
|
||||
|
||||
setLayout(new MigLayout("nogrid, fill, insets 6px 2px 6px 2px", "align center"));
|
||||
setLayout(new MigLayout("nogrid, fill, insets dialog", "align center"));
|
||||
|
||||
add(new JLabel("Pattern:"), "gapbefore indent");
|
||||
add(textField, "gap related, wmin 2cm");
|
||||
|
@ -2,7 +2,7 @@
|
||||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
public class AbstractFileEntry {
|
||||
class AbstractFileEntry {
|
||||
|
||||
private final String name;
|
||||
private final long length;
|
||||
|
@ -1,4 +1,7 @@
|
||||
|
||||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
@ -17,7 +20,7 @@ import javax.swing.text.JTextComponent;
|
||||
import net.sourceforge.tuned.ui.GradientStyle;
|
||||
|
||||
|
||||
public class CharacterHighlightPainter implements Highlighter.HighlightPainter {
|
||||
class CharacterHighlightPainter implements Highlighter.HighlightPainter {
|
||||
|
||||
private Color gradientBeginColor;
|
||||
private Color gradientEndColor;
|
||||
|
@ -7,7 +7,7 @@ import java.io.File;
|
||||
import net.sourceforge.tuned.FileUtil;
|
||||
|
||||
|
||||
public class FileEntry extends AbstractFileEntry {
|
||||
class FileEntry extends AbstractFileEntry {
|
||||
|
||||
private final File file;
|
||||
private final String type;
|
||||
|
@ -23,7 +23,7 @@ import net.sourceforge.tuned.ui.AbstractFancyListCellRenderer;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
|
||||
class HighlightListCellRenderer extends AbstractFancyListCellRenderer {
|
||||
|
||||
private final JTextComponent textComponent = new JTextField();
|
||||
|
||||
|
@ -33,19 +33,17 @@ import net.sourceforge.tuned.ui.ProgressDialog.Cancellable;
|
||||
|
||||
class MatchAction extends AbstractAction {
|
||||
|
||||
private final List<Object> namesModel;
|
||||
private final List<FileEntry> filesModel;
|
||||
private final RenameModel model;
|
||||
|
||||
private final SimilarityMetric[] metrics;
|
||||
|
||||
|
||||
public MatchAction(List<Object> namesModel, List<FileEntry> filesModel) {
|
||||
public MatchAction(RenameModel model) {
|
||||
super("Match", ResourceManager.getIcon("action.match"));
|
||||
|
||||
putValue(SHORT_DESCRIPTION, "Match names to files");
|
||||
|
||||
this.namesModel = namesModel;
|
||||
this.filesModel = filesModel;
|
||||
this.model = model;
|
||||
|
||||
metrics = new SimilarityMetric[3];
|
||||
|
||||
@ -75,7 +73,7 @@ class MatchAction extends AbstractAction {
|
||||
|
||||
SwingUtilities.getRoot(eventSource).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
BackgroundMatcher backgroundMatcher = new BackgroundMatcher(namesModel, filesModel, Arrays.asList(metrics));
|
||||
BackgroundMatcher backgroundMatcher = new BackgroundMatcher(model, Arrays.asList(metrics));
|
||||
backgroundMatcher.execute();
|
||||
|
||||
try {
|
||||
@ -118,17 +116,16 @@ class MatchAction extends AbstractAction {
|
||||
|
||||
protected static class BackgroundMatcher extends SwingWorker<List<Match<Object, FileEntry>>, Void> implements Cancellable {
|
||||
|
||||
private final List<Object> namesModel;
|
||||
private final List<FileEntry> filesModel;
|
||||
private final RenameModel model;
|
||||
|
||||
private final Matcher<Object, FileEntry> matcher;
|
||||
|
||||
|
||||
public BackgroundMatcher(List<Object> namesModel, List<FileEntry> filesModel, List<SimilarityMetric> metrics) {
|
||||
this.namesModel = namesModel;
|
||||
this.filesModel = filesModel;
|
||||
public BackgroundMatcher(RenameModel model, List<SimilarityMetric> metrics) {
|
||||
this.model = model;
|
||||
|
||||
this.matcher = new Matcher<Object, FileEntry>(namesModel, filesModel, metrics);
|
||||
// match names against files
|
||||
this.matcher = new Matcher<Object, FileEntry>(model.names(), model.files(), metrics);
|
||||
}
|
||||
|
||||
|
||||
@ -144,18 +141,12 @@ class MatchAction extends AbstractAction {
|
||||
return;
|
||||
|
||||
try {
|
||||
List<Match<Object, FileEntry>> matches = get();
|
||||
// put new data into model
|
||||
model.setData(get());
|
||||
|
||||
namesModel.clear();
|
||||
filesModel.clear();
|
||||
|
||||
for (Match<Object, FileEntry> match : matches) {
|
||||
namesModel.add(match.getValue());
|
||||
filesModel.add(match.getCandidate());
|
||||
}
|
||||
|
||||
namesModel.addAll(matcher.remainingValues());
|
||||
namesModel.addAll(matcher.remainingCandidates());
|
||||
// insert objects that could not be matched at the end
|
||||
model.names().addAll(matcher.remainingValues());
|
||||
model.files().addAll(matcher.remainingCandidates());
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger("global").log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import static java.awt.datatransfer.DataFlavor.stringFlavor;
|
||||
import static net.sourceforge.filebot.FileBotUtil.LIST_FILE_EXTENSIONS;
|
||||
import static net.sourceforge.filebot.FileBotUtil.TORRENT_FILE_EXTENSIONS;
|
||||
import static net.sourceforge.filebot.FileBotUtil.containsOnly;
|
||||
import static net.sourceforge.filebot.FileBotUtil.containsOnlyFolders;
|
||||
import static net.sourceforge.filebot.FileBotUtil.isInvalidFileName;
|
||||
import static net.sourceforge.tuned.FileUtil.getNameWithoutExtension;
|
||||
|
||||
@ -14,6 +15,7 @@ import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Level;
|
||||
@ -22,26 +24,38 @@ import java.util.logging.Logger;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.sourceforge.filebot.torrent.Torrent;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
|
||||
import net.sourceforge.tuned.FileUtil;
|
||||
|
||||
|
||||
class NamesListTransferablePolicy extends FilesListTransferablePolicy {
|
||||
class NamesListTransferablePolicy extends FileTransferablePolicy {
|
||||
|
||||
private final RenameList<Object> list;
|
||||
|
||||
|
||||
public NamesListTransferablePolicy(RenameList<Object> list) {
|
||||
super(list.getModel());
|
||||
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
list.getModel().clear();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean accept(Transferable tr) {
|
||||
return tr.isDataFlavorSupported(stringFlavor) || super.accept(tr);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean accept(List<File> files) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleTransferable(Transferable tr, TransferAction action) {
|
||||
if (action == TransferAction.PUT) {
|
||||
@ -111,13 +125,25 @@ class NamesListTransferablePolicy extends FilesListTransferablePolicy {
|
||||
loadListFiles(files);
|
||||
} else if (containsOnly(files, TORRENT_FILE_EXTENSIONS)) {
|
||||
loadTorrentFiles(files);
|
||||
} else if (containsOnlyFolders(files)) {
|
||||
// load files from each folder
|
||||
for (File folder : files) {
|
||||
loadFiles(Arrays.asList(folder.listFiles()));
|
||||
}
|
||||
} else {
|
||||
super.load(files);
|
||||
loadFiles(files);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loadListFiles(List<File> files) {
|
||||
protected void loadFiles(List<File> files) {
|
||||
for (File file : files) {
|
||||
list.getModel().add(new AbstractFileEntry(FileUtil.getFileName(file), file.length()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void loadListFiles(List<File> files) {
|
||||
try {
|
||||
List<StringEntry> entries = new ArrayList<StringEntry>();
|
||||
|
||||
@ -142,7 +168,7 @@ class NamesListTransferablePolicy extends FilesListTransferablePolicy {
|
||||
}
|
||||
|
||||
|
||||
private void loadTorrentFiles(List<File> files) {
|
||||
protected void loadTorrentFiles(List<File> files) {
|
||||
try {
|
||||
List<AbstractFileEntry> entries = new ArrayList<AbstractFileEntry>();
|
||||
|
||||
|
@ -7,8 +7,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
@ -18,49 +16,44 @@ import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.tuned.FileUtil;
|
||||
|
||||
|
||||
public class RenameAction extends AbstractAction {
|
||||
class RenameAction extends AbstractAction {
|
||||
|
||||
private final List<Object> namesModel;
|
||||
private final List<FileEntry> filesModel;
|
||||
private final RenameModel model;
|
||||
|
||||
|
||||
public RenameAction(List<Object> namesModel, List<FileEntry> filesModel) {
|
||||
public RenameAction(RenameModel model) {
|
||||
super("Rename", ResourceManager.getIcon("action.rename"));
|
||||
|
||||
putValue(SHORT_DESCRIPTION, "Rename files");
|
||||
|
||||
this.namesModel = namesModel;
|
||||
this.filesModel = filesModel;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
|
||||
Deque<Match<File, File>> renameMatches = new ArrayDeque<Match<File, File>>();
|
||||
Deque<Match<File, File>> revertMatches = new ArrayDeque<Match<File, File>>();
|
||||
Deque<Match<File, File>> todoQueue = new ArrayDeque<Match<File, File>>();
|
||||
Deque<Match<File, File>> doneQueue = new ArrayDeque<Match<File, File>>();
|
||||
|
||||
Iterator<Object> names = namesModel.iterator();
|
||||
Iterator<FileEntry> files = filesModel.iterator();
|
||||
for (Match<Object, FileEntry> match : model.matches()) {
|
||||
File source = match.getCandidate().getFile();
|
||||
|
||||
while (names.hasNext() && files.hasNext()) {
|
||||
File source = files.next().getFile();
|
||||
String newName = match.getValue().toString() + FileUtil.getExtension(source, true);
|
||||
File target = new File(source.getParentFile(), newName);
|
||||
|
||||
String targetName = names.next().toString() + FileUtil.getExtension(source, true);
|
||||
File target = new File(source.getParentFile(), targetName);
|
||||
|
||||
renameMatches.addLast(new Match<File, File>(source, target));
|
||||
todoQueue.addLast(new Match<File, File>(source, target));
|
||||
}
|
||||
|
||||
try {
|
||||
int renameCount = renameMatches.size();
|
||||
int renameCount = todoQueue.size();
|
||||
|
||||
for (Match<File, File> match : renameMatches) {
|
||||
for (Match<File, File> match : todoQueue) {
|
||||
// rename file
|
||||
if (!match.getValue().renameTo(match.getCandidate()))
|
||||
throw new IOException(String.format("Failed to rename file: %s.", match.getValue().getName()));
|
||||
|
||||
// revert in reverse order if renaming of all matches fails
|
||||
revertMatches.addFirst(match);
|
||||
doneQueue.addFirst(match);
|
||||
}
|
||||
|
||||
// renamed all matches successfully
|
||||
@ -72,7 +65,7 @@ public class RenameAction extends AbstractAction {
|
||||
boolean revertFailed = false;
|
||||
|
||||
// revert rename operations
|
||||
for (Match<File, File> match : revertMatches) {
|
||||
for (Match<File, File> match : doneQueue) {
|
||||
if (!match.getCandidate().renameTo(match.getValue())) {
|
||||
revertFailed = true;
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ import java.awt.BorderLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JButton;
|
||||
@ -20,29 +18,29 @@ import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.FileBotList;
|
||||
import net.sourceforge.filebot.ui.transfer.LoadAction;
|
||||
import net.sourceforge.filebot.ui.transfer.TransferablePolicy;
|
||||
import ca.odell.glazedlists.EventList;
|
||||
|
||||
|
||||
class RenameList<E> extends FileBotList<E> {
|
||||
|
||||
private JButton loadButton = new JButton();
|
||||
public RenameList(EventList<E> model) {
|
||||
// replace default model with given model
|
||||
setModel(model);
|
||||
|
||||
|
||||
public RenameList() {
|
||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
list.addMouseListener(dndReorderMouseAdapter);
|
||||
list.addMouseMotionListener(dndReorderMouseAdapter);
|
||||
|
||||
JViewport viewport = (JViewport) list.getParent();
|
||||
viewport.setBackground(list.getBackground());
|
||||
getViewPort().setBackground(list.getBackground());
|
||||
|
||||
getRemoveAction().setEnabled(true);
|
||||
|
||||
JPanel buttonPanel = new JPanel(new MigLayout("insets 1.2mm, nogrid, fill", "align center"));
|
||||
|
||||
buttonPanel.add(new JButton(downAction));
|
||||
buttonPanel.add(new JButton(downAction), "gap 10px");
|
||||
buttonPanel.add(new JButton(upAction), "gap 0");
|
||||
buttonPanel.add(loadButton, "gap 10px");
|
||||
buttonPanel.add(new JButton(loadAction), "gap 10px");
|
||||
|
||||
add(buttonPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
@ -51,31 +49,34 @@ class RenameList<E> extends FileBotList<E> {
|
||||
@Override
|
||||
public void setTransferablePolicy(TransferablePolicy transferablePolicy) {
|
||||
super.setTransferablePolicy(transferablePolicy);
|
||||
loadButton.setAction(new LoadAction(transferablePolicy));
|
||||
loadAction.putValue(LoadAction.TRANSFERABLE_POLICY, transferablePolicy);
|
||||
}
|
||||
|
||||
|
||||
public List<E> getEntries() {
|
||||
return new ArrayList<E>(getModel());
|
||||
}
|
||||
|
||||
|
||||
private boolean moveEntry(int fromIndex, int toIndex) {
|
||||
protected boolean moveEntry(int fromIndex, int toIndex) {
|
||||
if (toIndex < 0 || toIndex >= getModel().size())
|
||||
return false;
|
||||
|
||||
// move element
|
||||
getModel().add(toIndex, getModel().remove(fromIndex));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public JViewport getViewPort() {
|
||||
return listScrollPane.getViewport();
|
||||
}
|
||||
|
||||
private final LoadAction loadAction = new LoadAction(null);
|
||||
|
||||
private final AbstractAction upAction = new AbstractAction(null, ResourceManager.getIcon("action.up")) {
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int selectedIndex = getListComponent().getSelectedIndex();
|
||||
int toIndex = selectedIndex + 1;
|
||||
int index = getListComponent().getSelectedIndex();
|
||||
|
||||
if (moveEntry(selectedIndex, toIndex)) {
|
||||
getListComponent().setSelectedIndex(toIndex);
|
||||
if (moveEntry(index, index - 1)) {
|
||||
getListComponent().setSelectedIndex(index - 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -83,11 +84,10 @@ class RenameList<E> extends FileBotList<E> {
|
||||
private final AbstractAction downAction = new AbstractAction(null, ResourceManager.getIcon("action.down")) {
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int selectedIndex = getListComponent().getSelectedIndex();
|
||||
int toIndex = selectedIndex - 1;
|
||||
int index = getListComponent().getSelectedIndex();
|
||||
|
||||
if (moveEntry(selectedIndex, toIndex)) {
|
||||
getListComponent().setSelectedIndex(toIndex);
|
||||
if (moveEntry(index, index + 1)) {
|
||||
getListComponent().setSelectedIndex(index + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -15,25 +15,21 @@ import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.ListModel;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
|
||||
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
||||
|
||||
|
||||
class RenameListCellRenderer extends DefaultFancyListCellRenderer {
|
||||
|
||||
private final ListModel names;
|
||||
private final ListModel files;
|
||||
private final RenameModel model;
|
||||
|
||||
private final ExtensionLabel extension = new ExtensionLabel();
|
||||
|
||||
|
||||
public RenameListCellRenderer(ListModel names, ListModel files) {
|
||||
this.names = names;
|
||||
this.files = files;
|
||||
public RenameListCellRenderer(RenameModel model) {
|
||||
this.model = model;
|
||||
|
||||
setHighlightingEnabled(false);
|
||||
|
||||
@ -50,7 +46,8 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer {
|
||||
public void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
|
||||
if ((list.getModel() == files) && (value instanceof FileEntry)) {
|
||||
// show extension label only for items of the files model
|
||||
if (value instanceof FileEntry) {
|
||||
FileEntry entry = (FileEntry) value;
|
||||
|
||||
extension.setText(entry.getType());
|
||||
@ -61,7 +58,7 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer {
|
||||
|
||||
extension.setAlpha(1.0f);
|
||||
|
||||
if (index >= getMinLength()) {
|
||||
if (index >= model.matchCount()) {
|
||||
if (isSelected) {
|
||||
setGradientColors(noMatchGradientBeginColor, noMatchGradientEndColor);
|
||||
} else {
|
||||
@ -72,18 +69,7 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer {
|
||||
}
|
||||
|
||||
|
||||
private int getMinLength() {
|
||||
if ((names == null) || (files == null))
|
||||
return 0;
|
||||
|
||||
int n1 = names.getSize();
|
||||
int n2 = files.getSize();
|
||||
|
||||
return Math.min(n1, n2);
|
||||
}
|
||||
|
||||
|
||||
private class ExtensionLabel extends JLabel {
|
||||
protected class ExtensionLabel extends JLabel {
|
||||
|
||||
private final Insets margin = new Insets(0, 10, 0, 0);
|
||||
private final Insets padding = new Insets(0, 6, 0, 5);
|
||||
|
@ -0,0 +1,76 @@
|
||||
|
||||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import ca.odell.glazedlists.BasicEventList;
|
||||
import ca.odell.glazedlists.EventList;
|
||||
|
||||
|
||||
class RenameModel {
|
||||
|
||||
private final EventList<Object> names = new BasicEventList<Object>();
|
||||
private final EventList<FileEntry> files = new BasicEventList<FileEntry>();
|
||||
|
||||
|
||||
public EventList<Object> names() {
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
public EventList<FileEntry> files() {
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
names.clear();
|
||||
files.clear();
|
||||
}
|
||||
|
||||
|
||||
public void setData(Collection<Match<Object, FileEntry>> matches) {
|
||||
// clear names and files
|
||||
clear();
|
||||
|
||||
// add all matches
|
||||
for (Match<Object, FileEntry> match : matches) {
|
||||
names.add(match.getValue());
|
||||
files.add(match.getCandidate());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int matchCount() {
|
||||
return Math.min(names.size(), files.size());
|
||||
}
|
||||
|
||||
|
||||
public Match<Object, FileEntry> getMatch(int index) {
|
||||
if (index >= matchCount())
|
||||
throw new IndexOutOfBoundsException();
|
||||
|
||||
return new Match<Object, FileEntry>(names.get(index), files.get(index));
|
||||
}
|
||||
|
||||
|
||||
public Collection<Match<Object, FileEntry>> matches() {
|
||||
return new AbstractList<Match<Object, FileEntry>>() {
|
||||
|
||||
@Override
|
||||
public Match<Object, FileEntry> get(int index) {
|
||||
return getMatch(index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return matchCount();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
@ -6,8 +6,6 @@ import java.awt.Insets;
|
||||
|
||||
import javax.swing.DefaultListSelectionModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
@ -20,12 +18,15 @@ import ca.odell.glazedlists.event.ListEventListener;
|
||||
|
||||
public class RenamePanel extends FileBotPanel {
|
||||
|
||||
private RenameList<Object> namesList = new RenameList<Object>();
|
||||
private RenameList<FileEntry> filesList = new RenameList<FileEntry>();
|
||||
private RenameModel model = new RenameModel();
|
||||
|
||||
private MatchAction matchAction = new MatchAction(namesList.getModel(), filesList.getModel());
|
||||
private RenameList<Object> namesList = new RenameList<Object>(model.names());
|
||||
|
||||
private RenameAction renameAction = new RenameAction(namesList.getModel(), filesList.getModel());
|
||||
private RenameList<FileEntry> filesList = new RenameList<FileEntry>(model.files());
|
||||
|
||||
private MatchAction matchAction = new MatchAction(model);
|
||||
|
||||
private RenameAction renameAction = new RenameAction(model);
|
||||
|
||||
|
||||
public RenamePanel() {
|
||||
@ -37,22 +38,20 @@ public class RenamePanel extends FileBotPanel {
|
||||
filesList.setTitle("Current");
|
||||
filesList.setTransferablePolicy(new FilesListTransferablePolicy(filesList.getModel()));
|
||||
|
||||
JList namesListComponent = namesList.getListComponent();
|
||||
JList filesListComponent = filesList.getListComponent();
|
||||
RenameListCellRenderer cellrenderer = new RenameListCellRenderer(model);
|
||||
|
||||
RenameListCellRenderer cellrenderer = new RenameListCellRenderer(namesListComponent.getModel(), filesListComponent.getModel());
|
||||
|
||||
namesListComponent.setCellRenderer(cellrenderer);
|
||||
filesListComponent.setCellRenderer(cellrenderer);
|
||||
namesList.getListComponent().setCellRenderer(cellrenderer);
|
||||
filesList.getListComponent().setCellRenderer(cellrenderer);
|
||||
|
||||
ListSelectionModel selectionModel = new DefaultListSelectionModel();
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
namesListComponent.setSelectionModel(selectionModel);
|
||||
filesListComponent.setSelectionModel(selectionModel);
|
||||
// use the same selection model for both lists to synchronize selection
|
||||
namesList.getListComponent().setSelectionModel(selectionModel);
|
||||
filesList.getListComponent().setSelectionModel(selectionModel);
|
||||
|
||||
// synchronize viewports
|
||||
new ViewPortSynchronizer((JViewport) namesListComponent.getParent(), (JViewport) filesListComponent.getParent());
|
||||
new ViewPortSynchronizer(namesList.getViewPort(), filesList.getViewPort());
|
||||
|
||||
// create Match button
|
||||
JButton matchButton = new JButton(matchAction);
|
||||
@ -64,7 +63,7 @@ public class RenamePanel extends FileBotPanel {
|
||||
renameButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
renameButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
|
||||
setLayout(new MigLayout("fill, insets 0, gapx 10px", null, "align 33%"));
|
||||
setLayout(new MigLayout("fill, insets dialog, gapx 10px", null, "align 33%"));
|
||||
|
||||
add(namesList, "grow");
|
||||
|
||||
@ -77,8 +76,9 @@ public class RenamePanel extends FileBotPanel {
|
||||
|
||||
add(filesList, "grow");
|
||||
|
||||
namesList.getModel().addListEventListener(new RepaintHandler<Object>());
|
||||
filesList.getModel().addListEventListener(new RepaintHandler<FileEntry>());
|
||||
// repaint on change
|
||||
model.names().addListEventListener(new RepaintHandler<Object>());
|
||||
model.files().addListEventListener(new RepaintHandler<FileEntry>());
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
public class StringEntry {
|
||||
class StringEntry {
|
||||
|
||||
private String value;
|
||||
|
||||
|
@ -29,7 +29,7 @@ import net.sourceforge.tuned.ui.ArrayListModel;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
public class ValidateNamesDialog extends JDialog {
|
||||
class ValidateNamesDialog extends JDialog {
|
||||
|
||||
private final Collection<StringEntry> entries;
|
||||
|
||||
|
@ -8,11 +8,12 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
@ -38,17 +39,19 @@ public class SfvPanel extends FileBotPanel {
|
||||
public SfvPanel() {
|
||||
super("SFV", ResourceManager.getIcon("panel.sfv"));
|
||||
|
||||
setBorder(BorderFactory.createTitledBorder("SFV"));
|
||||
JPanel contentPane = new JPanel(new MigLayout("insets 0, nogrid, fill", null, "align bottom"));
|
||||
contentPane.setBorder(new TitledBorder("SFV"));
|
||||
|
||||
setLayout(new MigLayout("insets 0, nogrid, fill", "", "align bottom"));
|
||||
this.setLayout(new MigLayout("insets dialog, fill"));
|
||||
this.add(contentPane, "grow");
|
||||
|
||||
add(new JScrollPane(sfvTable), "grow, wrap 10px");
|
||||
contentPane.add(new JScrollPane(sfvTable), "grow, wrap 10px");
|
||||
|
||||
add(new JButton(loadAction), "gap 15px, gap bottom 4px");
|
||||
add(new JButton(saveAction), "gap rel, gap bottom 4px");
|
||||
add(new JButton(clearAction), "gap rel, gap bottom 4px");
|
||||
contentPane.add(new JButton(loadAction), "gap 15px, gap bottom 4px");
|
||||
contentPane.add(new JButton(saveAction), "gap rel, gap bottom 4px");
|
||||
contentPane.add(new JButton(clearAction), "gap rel, gap bottom 4px");
|
||||
|
||||
add(totalProgressPanel, "gap left indent:push, gap bottom 2px, gap right 7px, hidemode 3");
|
||||
contentPane.add(totalProgressPanel, "gap left indent:push, gap bottom 2px, gap right 7px, hidemode 3");
|
||||
|
||||
// Shortcut DELETE
|
||||
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
|
@ -10,7 +10,6 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Level;
|
||||
@ -30,7 +29,7 @@ public abstract class FileTransferablePolicy extends TransferablePolicy {
|
||||
public boolean accept(Transferable tr) {
|
||||
List<File> files = getFilesFromTransferable(tr);
|
||||
|
||||
if (files.isEmpty())
|
||||
if (files == null || files.isEmpty())
|
||||
return false;
|
||||
|
||||
return accept(files);
|
||||
@ -82,7 +81,7 @@ public abstract class FileTransferablePolicy extends TransferablePolicy {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return Collections.EMPTY_LIST;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,17 +13,23 @@ import net.sourceforge.filebot.ui.transfer.TransferablePolicy.TransferAction;
|
||||
|
||||
public class LoadAction extends AbstractAction {
|
||||
|
||||
private final TransferablePolicy transferablePolicy;
|
||||
public static final String TRANSFERABLE_POLICY = "transferable policy";
|
||||
|
||||
|
||||
public LoadAction(TransferablePolicy transferablePolicy) {
|
||||
super("Load", ResourceManager.getIcon("action.load"));
|
||||
|
||||
this.transferablePolicy = transferablePolicy;
|
||||
putValue(TRANSFERABLE_POLICY, transferablePolicy);
|
||||
}
|
||||
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// get transferable policy from action properties
|
||||
TransferablePolicy transferablePolicy = (TransferablePolicy) getValue(TRANSFERABLE_POLICY);
|
||||
|
||||
if (transferablePolicy == null)
|
||||
return;
|
||||
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
|
||||
chooser.setFileFilter(new TransferablePolicyFileFilter(transferablePolicy));
|
||||
@ -45,5 +51,4 @@ public class LoadAction extends AbstractAction {
|
||||
if (transferablePolicy.accept(transferable))
|
||||
transferablePolicy.handleTransferable(transferable, action);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public class NameSimilarityMetricTest {
|
||||
public void getSimilarity() {
|
||||
// normalize separators, lower-case
|
||||
assertEquals(1, metric.getSimilarity("test s01e01 first", "test.S01E01.First"));
|
||||
assertEquals(1, metric.getSimilarity("test s01e02 second", "test_S01E02_Second"));
|
||||
assertEquals(1, metric.getSimilarity("test s01e02 second", "test_[S01E02]_Second"));
|
||||
assertEquals(1, metric.getSimilarity("test s01e03 third", "__test__S01E03__Third__"));
|
||||
assertEquals(1, metric.getSimilarity("test s01e04 four", "test s01e04 four"));
|
||||
|
||||
|
@ -88,6 +88,9 @@ public class SeasonEpisodeSimilarityMetricTest {
|
||||
|
||||
// first two digits <= 29
|
||||
assertEquals(null, metric.match("The 4400"));
|
||||
|
||||
// test lookbehind
|
||||
assertEquals(null, metric.match("720p"));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user