* refactored and simplified transfer api

* use more GlazedLists stuff (EventList, AutoCompleteSupport) and remove obsolete classes (SimpleListModel, TextCompletion)
* don't use SearchResultCache in EpisodeListClient (was only done for better ui interactions)
* removed caching from ResourceManager
* some improvements based on FindBugs warnings
* use args4j for improved argument parsing
* updated ant build script
* more general MessageBus/Handler (use Object as message type instead of string)
* ChecksumComputationService is not a singleton anymore
* TemporaryFolder is always recreated if it is deleted by the user, or another instance shutting down
* Notifications flicker less when one window is removed and the others are layouted
* lots of other refactoring
This commit is contained in:
Reinhard Pointner 2008-07-30 22:37:01 +00:00
parent a401a51c75
commit 0c674849d8
100 changed files with 1949 additions and 1661 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0"?>
<project name="FileBot" basedir="." default="fatjar">
<project name="FileBot" default="fatjar">
<property name="title" value="${ant.project.name}" />
<property name="version" value="1.9" />
@ -9,20 +9,26 @@
<property name="dir.source" location="${basedir}/source" />
<property name="dir.test" location="${basedir}/test" />
<property name="dir.build" location="${basedir}/build" />
<property name="dir.dist" location="${basedir}/dist" />
<property name="dir.lib" location="${basedir}/lib" />
<property name="executable" location="${dir.build}/${title}.jar" />
<property name="executable" location="${dir.dist}/${title}.jar" />
<property name="lib.xerces" value="${dir.lib}/xercesImpl.jar" />
<property name="lib.nekohtml" value="${dir.lib}/nekohtml-1.9.7.jar" />
<property name="lib.simmetrics" value="${dir.lib}/simmetrics_jar_v1_6_2_d07_02_07.jar" />
<property name="lib.xmlrpc" value="${dir.lib}/xmlrpc-client-1.1.jar" />
<property name="lib.glazedlists" value="${dir.lib}/glazedlists-1.7.0_java15.jar" />
<property name="lib.junit" value="${dir.lib}/junit-4.4.jar" />
<property name="lib.xerces" location="${dir.lib}/xercesImpl.jar" />
<property name="lib.nekohtml" location="${dir.lib}/nekohtml-1.9.7.jar" />
<property name="lib.simmetrics" location="${dir.lib}/simmetrics_jar_v1_6_2_d07_02_07.jar" />
<property name="lib.xmlrpc" location="${dir.lib}/xmlrpc-client-1.1.jar" />
<property name="lib.glazedlists" location="${dir.lib}/glazedlists-1.7.0_java15.jar" />
<property name="lib.args4j" location="${dir.lib}/args4j-2.0.9.jar" />
<property name="lib.junit" location="${dir.lib}/junit-4.4.jar" />
<target name="fatjar" depends="clean, build">
<!-- create dist dir -->
<mkdir dir="${dir.dist}" />
<jar destfile="${executable}" duplicate="fail">
<fileset dir="${dir.build}" />
@ -63,6 +69,10 @@
<zipfileset src="${lib.glazedlists}">
<include name="ca/odell/glazedlists/**" />
</zipfileset>
<zipfileset src="${lib.args4j}">
<include name="*/**" />
</zipfileset>
</jar>
</target>
@ -98,13 +108,13 @@
<target name="clean">
<delete file="${executable}" verbose="yes" />
<delete dir="${dir.dist}" />
<delete dir="${dir.build}" />
</target>
<target name="test" depends="clean, build-test, fatjar">
<junit printsummary="yes" fork="yes">
<junit printsummary="yes" showoutput="yes">
<classpath>
<pathelement location="${executable}"/>
<pathelement location="${lib.junit}"/>

BIN
lib/args4j-2.0.9.jar Normal file

Binary file not shown.

View File

@ -1,20 +1,15 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import net.sourceforge.filebot.ArgumentBean;
import net.sourceforge.filebot.Settings;
import net.sourceforge.filebot.ui.FileBotWindow;
import net.sourceforge.tuned.MessageBus;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
public class Main {
@ -24,11 +19,10 @@ public class Main {
*/
public static void main(String... args) {
final Arguments arguments = new Arguments(args);
final ArgumentBean argumentBean = parseArguments(args);
if (arguments.containsParameter("clear")) {
if (argumentBean.isClear())
Settings.getSettings().clear();
}
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
@ -43,7 +37,7 @@ public class Main {
FileBotWindow window = new FileBotWindow();
// publish messages from arguments to the newly created components
arguments.publishMessages();
argumentBean.publishMessages();
// start
window.setVisible(true);
@ -51,43 +45,26 @@ public class Main {
});
}
private static class Arguments {
private final Set<String> parameters = new HashSet<String>(3);
private final Map<String, List<String>> messages = new LinkedHashMap<String, List<String>>();
public Arguments(String[] args) {
Pattern topicPattern = Pattern.compile("--(\\w+)");
String currentTopic = null;
for (String arg : args) {
Matcher m = topicPattern.matcher(arg);
if (m.matches()) {
currentTopic = m.group(1).toLowerCase();
messages.put(currentTopic, new ArrayList<String>(1));
} else if (currentTopic != null) {
messages.get(currentTopic).add(arg);
} else {
parameters.add(arg.toLowerCase());
}
}
}
public boolean containsParameter(String argument) {
return parameters.contains(argument);
private static ArgumentBean parseArguments(String... args) {
ArgumentBean argumentBean = new ArgumentBean();
CmdLineParser argumentParser = new CmdLineParser(argumentBean);
try {
argumentParser.parseArgument(args);
} catch (CmdLineException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.getMessage());
}
public void publishMessages() {
for (String topic : messages.keySet()) {
MessageBus.getDefault().publish(topic, messages.get(topic).toArray(new String[0]));
}
if (argumentBean.isHelp()) {
System.out.println("Options:");
argumentParser.printUsage(System.out);
// just print help message and exit afterwards
System.exit(0);
}
return argumentBean;
}
}

View File

@ -0,0 +1,95 @@
package net.sourceforge.filebot;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.tuned.MessageBus;
import org.kohsuke.args4j.Option;
public class ArgumentBean {
@Option(name = "-help", usage = "Print this help message")
private boolean help = false;
@Option(name = "-clear", usage = "Clear history and settings")
private boolean clear = false;
@Message(topic = "list")
@Option(name = "--list", usage = "Open file in 'List' panel", metaVar = "<file>")
private File listPanelFile;
@Message(topic = "analyze")
@Option(name = "--analyze", usage = "Open file in 'Analyze' panel", metaVar = "<file>")
private File analyzePanelFile;
@Message(topic = "sfv")
@Option(name = "--sfv", usage = "Open file in 'SFV' panel", metaVar = "<file>")
private File sfvPanelFile;
public boolean isHelp() {
return help;
}
public boolean isClear() {
return clear;
}
public File getListPanelFile() {
return listPanelFile;
}
public File getAnalyzePanelFile() {
return analyzePanelFile;
}
public File getSfvPanelFile() {
return sfvPanelFile;
}
public void publishMessages() {
for (Field field : getClass().getDeclaredFields()) {
Message message = field.getAnnotation(Message.class);
if (message == null)
continue;
try {
Object value = field.get(this);
if (value != null) {
MessageBus.getDefault().publish(message.topic(), value);
}
} catch (Exception e) {
// should not happen
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
}
@Retention(RUNTIME)
@Target(FIELD)
private @interface Message {
String topic();
}
}

View File

@ -13,10 +13,6 @@ import net.sourceforge.tuned.FileUtil;
public class FileBotUtil {
private FileBotUtil() {
// hide constructor
}
/**
* Invalid characters in filenames: \, /, :, *, ?, ", <, >, |, \r and \n
*/
@ -107,4 +103,9 @@ public class FileBotUtil {
};
private FileBotUtil() {
// hide constructor
}
}

View File

@ -23,7 +23,6 @@ public class Settings {
public static final String SELECTED_PANEL = "panel";
public static final String SEARCH_HISTORY = "search/history";
public static final String SUBTITLE_HISTORY = "subtitle/history";
public static final String SUBTITLE_LANGUAGE = "subtitle/language";
private static final Settings settings = new Settings();

View File

@ -5,10 +5,6 @@ package net.sourceforge.filebot.resources;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
@ -16,40 +12,18 @@ import javax.swing.ImageIcon;
public class ResourceManager {
private ResourceManager() {
// hide constructor
}
private static final Map<String, String> aliasMap = new HashMap<String, String>();
static {
aliasMap.put("tab.loading", "tab.loading.gif");
aliasMap.put("tab.history", "action.find.png");
aliasMap.put("loading", "loading.gif");
}
private static final Map<String, ImageIcon> iconCache = Collections.synchronizedMap(new WeakHashMap<String, ImageIcon>());
public static ImageIcon getIcon(String name) {
return getIcon(name, null);
}
public static ImageIcon getIcon(String name, String def) {
ImageIcon icon = iconCache.get(name);
URL resource = getResource(name, def);
if (icon == null) {
// load image if not in cache
URL resource = getResource(name, def);
if (resource != null) {
icon = new ImageIcon(resource);
iconCache.put(name, icon);
}
}
if (resource != null)
return new ImageIcon(resource);
return icon;
return null;
}
@ -73,14 +47,7 @@ public class ResourceManager {
private static URL getResource(String name) {
String resource = null;
if (aliasMap.containsKey(name))
resource = aliasMap.get(name);
else
resource = name + ".png";
return ResourceManager.class.getResource(resource);
return ResourceManager.class.getResource(name + ".png");
}
@ -93,4 +60,9 @@ public class ResourceManager {
return resource;
}
private ResourceManager() {
throw new UnsupportedOperationException();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

View File

@ -6,7 +6,6 @@ import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
@ -33,16 +32,7 @@ public class Torrent {
public Torrent(File torrent) throws IOException {
this(new FileInputStream(torrent));
}
/**
* Load torrent data from an <code>InputStream</code>. The given stream will be closed
* after data has been read.
*/
public Torrent(InputStream inputStream) throws IOException {
this(decodeTorrent(inputStream));
this(decodeTorrent(torrent));
}
@ -56,7 +46,7 @@ public class Torrent {
charset = Charset.forName(encoding);
} catch (IllegalArgumentException e) {
// invalid encoding, just keep using UTF-8
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.getMessage());
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid encoding: " + encoding);
}
createdBy = decodeString(torrentMap.get("created by"), charset);
@ -113,6 +103,17 @@ public class Torrent {
}
private static Map<?, ?> decodeTorrent(File torrent) throws IOException {
BufferedInputStream in = new BufferedInputStream(new FileInputStream(torrent));
try {
return BDecoder.decode(in);
} finally {
in.close();
}
}
private String decodeString(Object byteArray, Charset charset) {
if (byteArray == null)
return null;
@ -173,17 +174,6 @@ public class Torrent {
return singleFileTorrent;
}
private static Map<?, ?> decodeTorrent(InputStream torrent) throws IOException {
BufferedInputStream in = new BufferedInputStream(torrent);
try {
return BDecoder.decode(in);
} finally {
in.close();
}
}
public static class Entry {

View File

@ -91,12 +91,14 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
completionList.addMemberList(fetchHistory);
*/
searchField.getEditor().setAction(searchAction);
searchField.getSelectButton().setModel(createSearchEngines());
searchField.getSelectButton().setLabelProvider(createSearchEngineLabelProvider());
AutoCompleteSupport.install(searchField.getEditor(), searchHistory);
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
}
@ -109,9 +111,6 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
protected abstract SearchTask createSearchTask();
protected abstract void configureSelectDialog(SelectDialog<SearchResult> selectDialog);
protected abstract FetchTask createFetchTask(SearchTask searchTask, SearchResult selectedSearchResult);
@ -163,6 +162,41 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
protected abstract Collection<SearchResult> doInBackground() throws Exception;
protected SearchResult chooseSearchResult() throws Exception {
switch (get().size()) {
case 0:
MessageManager.showWarning(String.format("\"%s\" has not been found.", getSearchText()));
return null;
case 1:
return get().iterator().next();
}
// check if an exact match has been found
for (SearchResult searchResult : get()) {
if (getSearchText().equalsIgnoreCase(searchResult.getName()))
return searchResult;
}
// multiple results have been found, user must select one
Window window = SwingUtilities.getWindowAncestor(AbstractSearchPanel.this);
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(window, get());
configureSelectDialog(selectDialog);
selectDialog.setVisible(true);
// selected value or null if the dialog was canceled by the user
return selectDialog.getSelectedValue();
}
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) throws Exception {
selectDialog.setIconImage(TunedUtil.getImage(searchField.getSelectButton().getLabelProvider().getIcon(getClient())));
}
public String getSearchText() {
return searchText;
}
@ -210,14 +244,9 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
SearchTask task = (SearchTask) evt.getSource();
try {
SearchResult selectedResult = selectSearchResult(task);
SearchResult selectedResult = task.chooseSearchResult();
if (selectedResult == null) {
if (task.get().isEmpty()) {
// no search results
MessageManager.showWarning(String.format("\"%s\" has not been found.", task.getSearchText()));
}
tab.close();
return;
}
@ -245,31 +274,6 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
}
private SearchResult selectSearchResult(SearchTask task) throws Exception {
Collection<SearchResult> searchResults = task.get();
switch (searchResults.size()) {
case 0:
return null;
case 1:
return searchResults.iterator().next();
}
// multiple results have been found, user must selected one
Window window = SwingUtilities.getWindowAncestor(AbstractSearchPanel.this);
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(window, searchResults);
selectDialog.setIconImage(TunedUtil.getImage(searchField.getSelectButton().getLabelProvider().getIcon(task.getClient())));
configureSelectDialog(selectDialog);
selectDialog.setVisible(true);
// selected value or null if canceled by the user
return selectDialog.getSelectedValue();
}
}

View File

@ -4,14 +4,9 @@ package net.sourceforge.filebot.ui;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.PrintStream;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Action;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
@ -20,59 +15,48 @@ import javax.swing.ListSelectionModel;
import javax.swing.border.TitledBorder;
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
import net.sourceforge.filebot.ui.transfer.ExportHandler;
import net.sourceforge.filebot.ui.transfer.FileTransferable;
import net.sourceforge.filebot.ui.transfer.Saveable;
import net.sourceforge.filebot.ui.transfer.SaveableExportHandler;
import net.sourceforge.filebot.ui.transfer.TransferablePolicyImportHandler;
import net.sourceforge.filebot.ui.transferablepolicies.MutableTransferablePolicy;
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
import net.sourceforge.filebot.ui.transfer.FileExportHandler;
import net.sourceforge.filebot.ui.transfer.TransferablePolicy;
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
import net.sourceforge.tuned.ui.SimpleListModel;
import net.sourceforge.tuned.ui.TunedUtil;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.swing.EventListModel;
public class FileBotList extends JPanel implements Saveable {
public class FileBotList<E> extends JPanel {
private final JList list = new JList(new SimpleListModel());
protected final EventList<E> model = new BasicEventList<E>();
private final MutableTransferablePolicy mutableTransferablePolicy = new MutableTransferablePolicy();
protected final JList list = new JList(new EventListModel<E>(model));
private final TitledBorder titledBorder;
protected final JScrollPane listScrollPane = new JScrollPane(list);
private String title;
private String title = null;
public FileBotList(boolean enableExport, boolean enableRemoveAction, boolean border) {
public FileBotList() {
super(new BorderLayout());
JScrollPane listScrollPane = new JScrollPane(list);
if (border) {
titledBorder = new TitledBorder("");
setBorder(titledBorder);
} else {
titledBorder = null;
listScrollPane.setBorder(BorderFactory.createEmptyBorder());
}
setBorder(new TitledBorder(getTitle()));
list.setCellRenderer(new DefaultFancyListCellRenderer());
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
list.setTransferHandler(new DefaultTransferHandler(null, null));
list.setDragEnabled(false);
add(listScrollPane, BorderLayout.CENTER);
ExportHandler exportHandler = null;
// Shortcut DELETE, disabled by default
removeAction.setEnabled(false);
if (enableExport)
exportHandler = new SaveableExportHandler(this);
list.setTransferHandler(new DefaultTransferHandler(new TransferablePolicyImportHandler(mutableTransferablePolicy), exportHandler));
list.setDragEnabled(enableExport);
if (enableRemoveAction) {
// Shortcut DELETE
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
}
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
}
public EventList<E> getModel() {
return model;
}
@ -81,13 +65,34 @@ public class FileBotList extends JPanel implements Saveable {
}
@Override
public DefaultTransferHandler getTransferHandler() {
return (DefaultTransferHandler) list.getTransferHandler();
}
public void setTransferablePolicy(TransferablePolicy transferablePolicy) {
mutableTransferablePolicy.setTransferablePolicy(transferablePolicy);
getTransferHandler().setImportHandler(transferablePolicy);
}
public TransferablePolicy getTransferablePolicy() {
return mutableTransferablePolicy;
TransferablePolicy importHandler = (TransferablePolicy) getTransferHandler().getImportHandler();
return importHandler;
}
public void setExportHandler(FileExportHandler exportHandler) {
getTransferHandler().setExportHandler(exportHandler);
// enable drag if ExportHandler is available
list.setDragEnabled(exportHandler != null);
}
public FileExportHandler getExportHandler() {
return (FileExportHandler) getTransferHandler().getExportHandler();
}
@ -99,50 +104,18 @@ public class FileBotList extends JPanel implements Saveable {
public void setTitle(String title) {
this.title = title;
if (titledBorder != null)
if (getBorder() instanceof TitledBorder) {
TitledBorder titledBorder = (TitledBorder) getBorder();
titledBorder.setTitle(title);
revalidate();
repaint();
}
public SimpleListModel getModel() {
return (SimpleListModel) list.getModel();
}
public void save(File file) {
try {
PrintStream out = new PrintStream(file);
for (Object object : getModel().getCopy()) {
out.println(object.toString());
}
out.close();
} catch (Exception e) {
// should not happen
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
revalidate();
repaint();
}
}
public String getDefaultFileName() {
return title + ".txt";
}
public boolean isSaveable() {
return !getModel().isEmpty();
}
public void load(List<File> files) {
FileTransferable tr = new FileTransferable(files);
if (mutableTransferablePolicy.accept(tr))
mutableTransferablePolicy.handleTransferable(tr, false);
public Action getRemoveAction() {
return removeAction;
}
private final AbstractAction removeAction = new AbstractAction("Remove") {

View File

@ -0,0 +1,43 @@
package net.sourceforge.filebot.ui;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import net.sourceforge.filebot.ui.transfer.FileExportHandler;
public class FileBotListExportHandler extends FileExportHandler {
private final FileBotList<?> list;
public FileBotListExportHandler(FileBotList<?> list) {
this.list = list;
}
@Override
public boolean canExport() {
return !list.getModel().isEmpty();
}
@Override
public void export(OutputStream out) throws IOException {
PrintStream printer = new PrintStream(out);
for (Object entry : list.getModel()) {
printer.println(entry);
}
}
@Override
public String getDefaultFileName() {
return list.getTitle() + ".txt";
}
}

View File

@ -3,51 +3,13 @@ package net.sourceforge.filebot.ui;
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JPanel;
import net.sourceforge.filebot.ui.panel.analyze.AnalyzePanel;
import net.sourceforge.filebot.ui.panel.list.ListPanel;
import net.sourceforge.filebot.ui.panel.rename.RenamePanel;
import net.sourceforge.filebot.ui.panel.search.SearchPanel;
import net.sourceforge.filebot.ui.panel.sfv.SfvPanel;
import net.sourceforge.filebot.ui.panel.subtitle.SubtitlePanel;
public class FileBotPanel extends JPanel {
private static List<FileBotPanel> registry;
public static synchronized List<FileBotPanel> getAvailablePanels() {
if (registry == null) {
registry = new ArrayList<FileBotPanel>(6);
registry.add(new ListPanel());
registry.add(new RenamePanel());
registry.add(new AnalyzePanel());
registry.add(new SearchPanel());
registry.add(new SubtitlePanel());
registry.add(new SfvPanel());
}
return Collections.unmodifiableList(registry);
}
public static FileBotPanel forName(String name) {
for (FileBotPanel panel : registry) {
if (panel.getPanelName().equalsIgnoreCase(name))
return panel;
}
return null;
}
private final String name;
private final Icon icon;

View File

@ -18,16 +18,23 @@ import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
import net.sourceforge.tuned.ui.SimpleListModel;
import net.sourceforge.tuned.ui.TunedUtil;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.swing.EventListModel;
class FileBotPanelSelectionList extends JList {
private static final int SELECTDELAY_ON_DRAG_OVER = 300;
private final EventList<FileBotPanel> panelModel = new BasicEventList<FileBotPanel>();
public FileBotPanelSelectionList() {
setModel(new EventListModel<FileBotPanel>(panelModel));
setCellRenderer(new PanelCellRenderer());
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@ -35,12 +42,15 @@ class FileBotPanelSelectionList extends JList {
// initialize "drag over" panel selection
new DropTarget(this, new DragDropListener());
setModel(new SimpleListModel(FileBotPanel.getAvailablePanels()));
}
public EventList<FileBotPanel> getPanelModel() {
return panelModel;
}
private class PanelCellRenderer extends DefaultFancyListCellRenderer {
private static class PanelCellRenderer extends DefaultFancyListCellRenderer {
public PanelCellRenderer() {
super(BorderLayout.CENTER, 10, 0, new Color(0x163264));

View File

@ -42,12 +42,17 @@ public class FileBotTree extends JTree {
}
@Override
public DefaultTreeModel getModel() {
return (DefaultTreeModel) super.getModel();
}
public void clear() {
DefaultTreeModel model = (DefaultTreeModel) getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
root.removeAllChildren();
model.reload(root);
getModel().reload(root);
}

View File

@ -20,10 +20,15 @@ import javax.swing.event.ListSelectionListener;
import net.sourceforge.filebot.Settings;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.panel.analyze.AnalyzePanel;
import net.sourceforge.filebot.ui.panel.list.ListPanel;
import net.sourceforge.filebot.ui.panel.rename.RenamePanel;
import net.sourceforge.filebot.ui.panel.search.SearchPanel;
import net.sourceforge.filebot.ui.panel.sfv.SfvPanel;
import net.sourceforge.filebot.ui.panel.subtitle.SubtitlePanel;
import net.sourceforge.tuned.MessageBus;
import net.sourceforge.tuned.MessageHandler;
import net.sourceforge.tuned.ui.ShadowBorder;
import net.sourceforge.tuned.ui.SimpleListModel;
public class FileBotWindow extends JFrame implements ListSelectionListener {
@ -45,6 +50,7 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
icons.add(ResourceManager.getImage("window.icon.big"));
setIconImages(icons);
selectionListPanel.getPanelModel().addAll(createPanels());
selectionListPanel.addListSelectionListener(this);
JComponent contentPane = createContentPane();
@ -55,7 +61,21 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
selectionListPanel.setSelectedIndex(Settings.getSettings().getInt(Settings.SELECTED_PANEL, 3));
MessageBus.getDefault().addMessageHandler("panel", panelMessageHandler);
MessageBus.getDefault().addMessageHandler("panel", panelSelectMessageHandler);
}
private List<FileBotPanel> createPanels() {
List<FileBotPanel> panels = new ArrayList<FileBotPanel>();
panels.add(new ListPanel());
panels.add(new RenamePanel());
panels.add(new AnalyzePanel());
panels.add(new SearchPanel());
panels.add(new SubtitlePanel());
panels.add(new SfvPanel());
return panels;
}
@ -99,7 +119,6 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
}
@SuppressWarnings("unchecked")
private JComponent createPageLayer() {
JPanel pageLayer = new JPanel(new BorderLayout());
@ -108,10 +127,7 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
pageLayer.add(headerPanel, BorderLayout.NORTH);
pageLayer.add(pagePanel, BorderLayout.CENTER);
SimpleListModel model = (SimpleListModel) selectionListPanel.getModel();
for (FileBotPanel panel : (List<FileBotPanel>) model.getCopy()) {
panel.setVisible(false);
for (FileBotPanel panel : selectionListPanel.getPanelModel()) {
pagePanel.add(panel, panel.getPanelName());
}
@ -132,12 +148,15 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
return contentPane;
}
private final MessageHandler panelMessageHandler = new MessageHandler() {
private final MessageHandler panelSelectMessageHandler = new MessageHandler() {
@Override
public void handle(String topic, String... messages) {
for (String panel : messages) {
selectionListPanel.setSelectedValue(FileBotPanel.forName(panel), true);
public void handle(String topic, Object... messages) {
if (messages.length >= 1) {
Object panel = messages[messages.length - 1];
if (panel instanceof FileBotPanel)
selectionListPanel.setSelectedValue(panel, true);
}
}

View File

@ -10,45 +10,63 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.filebot.ui.transfer.FileTransferable;
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
import net.sourceforge.filebot.ui.transfer.TransferablePolicy;
import net.sourceforge.filebot.ui.transfer.TransferablePolicy.TransferAction;
import net.sourceforge.tuned.MessageBus;
import net.sourceforge.tuned.MessageHandler;
public class FileTransferableMessageHandler implements MessageHandler {
private final String name;
private final FileBotPanel panel;
private final TransferablePolicy transferablePolicy;
public FileTransferableMessageHandler(String name, TransferablePolicy transferablePolicy) {
this.name = name;
public FileTransferableMessageHandler(FileBotPanel panel, TransferablePolicy transferablePolicy) {
this.panel = panel;
this.transferablePolicy = transferablePolicy;
}
@Override
public void handle(String topic, String... messages) {
// change panel
MessageBus.getDefault().publish("panel", name);
public void handle(String topic, Object... messages) {
// switch to panel
MessageBus.getDefault().publish("panel", panel);
List<File> files = new ArrayList<File>(messages.length);
for (String filename : messages) {
try {
File file = new File(filename);
if (file.exists()) {
// file might be relative, use absolute file
for (Object message : messages) {
File file = fromMessage(message);
if (file == null)
continue;
if (file.exists()) {
try {
// path may be relative, use absolute path
files.add(file.getCanonicalFile());
} else {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, String.format("Invalid File: %s", filename));
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
} else {
// file doesn't exist
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid File: " + file);
}
}
transferablePolicy.handleTransferable(new FileTransferable(files), true);
transferablePolicy.handleTransferable(new FileTransferable(files), TransferAction.PUT);
}
private File fromMessage(Object message) {
if (message instanceof File)
return (File) message;
if (message instanceof String)
return new File((String) message);
return null;
}
}

View File

@ -25,8 +25,8 @@ import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.tuned.ui.ArrayListModel;
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
import net.sourceforge.tuned.ui.SimpleListModel;
import net.sourceforge.tuned.ui.TunedUtil;
@ -78,14 +78,14 @@ public class SelectDialog<T> extends JDialog {
setLocation(TunedUtil.getPreferredLocation(this));
// default selection
list.setModel(new SimpleListModel(options));
list.setModel(new ArrayListModel(options));
list.setSelectedIndex(0);
// Shortcut Enter
TunedUtil.registerActionForKeystroke(list, KeyStroke.getKeyStroke("released ENTER"), selectAction);
TunedUtil.putActionForKeystroke(list, KeyStroke.getKeyStroke("released ENTER"), selectAction);
// Shortcut Escape
TunedUtil.registerActionForKeystroke(list, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
TunedUtil.putActionForKeystroke(list, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
}

View File

@ -62,7 +62,7 @@ public class AnalyzePanel extends FileBotPanel {
fileTreePanel.getFileTree().addPropertyChangeListener(FileTree.CONTENT_PROPERTY, fileTreeChangeListener);
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(getPanelName(), fileTreePanel.getFileTree().getTransferablePolicy()));
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(this, fileTreePanel.getFileTree().getTransferablePolicy()));
}

View File

@ -12,15 +12,12 @@ import java.util.logging.Logger;
import javax.swing.SwingWorker;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import net.sourceforge.filebot.ui.FileBotTree;
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
import net.sourceforge.filebot.ui.transfer.FileTransferable;
import net.sourceforge.filebot.ui.transfer.TransferablePolicyImportHandler;
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
class FileTree extends FileBotTree {
@ -37,11 +34,11 @@ class FileTree extends FileBotTree {
transferablePolicy = new FileTreeTransferablePolicy(this);
transferablePolicy.addPropertyChangeListener(LOADING_PROPERTY, new LoadingPropertyChangeListener());
setTransferHandler(new DefaultTransferHandler(new TransferablePolicyImportHandler(transferablePolicy), null));
setTransferHandler(new DefaultTransferHandler(transferablePolicy, null));
}
public TransferablePolicy getTransferablePolicy() {
public FileTransferablePolicy getTransferablePolicy() {
return transferablePolicy;
}
@ -58,24 +55,14 @@ class FileTree extends FileBotTree {
}
}
DefaultTreeModel model = (DefaultTreeModel) getModel();
for (TreeNode treeNode : changedNodes) {
model.reload(treeNode);
getModel().reload(treeNode);
}
contentChanged();
}
public void load(List<File> files) {
FileTransferable tr = new FileTransferable(files);
if (transferablePolicy.accept(tr))
transferablePolicy.handleTransferable(tr, true);
}
@Override
public void clear() {
transferablePolicy.reset();
@ -85,14 +72,12 @@ class FileTree extends FileBotTree {
}
private void contentChanged() {
synchronized (this) {
if (postProcessor != null)
postProcessor.cancel(true);
postProcessor = new PostProcessor();
postProcessor.execute();
}
private synchronized void contentChanged() {
if (postProcessor != null)
postProcessor.cancel(true);
postProcessor = new PostProcessor();
postProcessor.execute();
};
@ -105,7 +90,7 @@ class FileTree extends FileBotTree {
firePropertyChange(FileTree.LOADING_PROPERTY, null, loading);
if (!loading) {
((DefaultTreeModel) getModel()).reload();
getModel().reload();
contentChanged();
}
}

View File

@ -39,7 +39,7 @@ class FileTreePanel extends JPanel {
buttons.add(Box.createGlue());
// Shortcut DELETE
TunedUtil.registerActionForKeystroke(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
TunedUtil.putActionForKeystroke(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
add(new LoadingOverlayPane(new JScrollPane(fileTree), ResourceManager.getIcon("loading")), BorderLayout.CENTER);
add(buttons, BorderLayout.SOUTH);

View File

@ -9,7 +9,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import net.sourceforge.filebot.FileBotUtil;
import net.sourceforge.filebot.ui.transferablepolicies.BackgroundFileTransferablePolicy;
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<DefaultMutableTreeNode> {
@ -74,7 +74,7 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Defaul
@Override
public String getDescription() {
public String getFileFilterDescription() {
return "files and folders";
}

View File

@ -12,16 +12,16 @@ import java.util.logging.Logger;
import net.sourceforge.filebot.FileBotUtil;
import net.sourceforge.filebot.torrent.Torrent;
import net.sourceforge.filebot.ui.FileBotList;
import net.sourceforge.filebot.ui.transferablepolicies.FileTransferablePolicy;
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
import net.sourceforge.tuned.FileUtil;
class FileListTransferablePolicy extends FileTransferablePolicy {
private FileBotList list;
private FileBotList<? super String> list;
public FileListTransferablePolicy(FileBotList list) {
public FileListTransferablePolicy(FileBotList<? super String> list) {
this.list = list;
}
@ -50,12 +50,13 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
private void loadFolderList(List<File> folders) {
if (folders.size() == 1) {
// if only one folder was dropped, use its name as title
list.setTitle(FileUtil.getFolderName(folders.get(0)));
}
for (File folder : folders) {
for (File file : folder.listFiles()) {
list.getModel().add(FileUtil.getFolderName(file));
list.getModel().add(FileUtil.getFileName(file));
}
}
}
@ -92,7 +93,7 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
@Override
public String getDescription() {
public String getFileFilterDescription() {
return "files, folders and torrents";
}

View File

@ -23,6 +23,7 @@ import javax.swing.border.EmptyBorder;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.FileBotList;
import net.sourceforge.filebot.ui.FileBotListExportHandler;
import net.sourceforge.filebot.ui.FileBotPanel;
import net.sourceforge.filebot.ui.FileTransferableMessageHandler;
import net.sourceforge.filebot.ui.MessageManager;
@ -36,10 +37,7 @@ public class ListPanel extends FileBotPanel {
private static final String INDEX_VARIABLE = "<i>";
private FileBotList list = new FileBotList(true, true, true);
private SaveAction saveAction = new SaveAction(list);
private LoadAction loadAction = new LoadAction(list.getTransferablePolicy());
private FileBotList<String> list = new FileBotList<String>();
private JTextField textField = new JTextField(String.format("Name - %s", INDEX_VARIABLE), 25);
private SpinnerNumberModel fromSpinnerModel = new SpinnerNumberModel(1, 0, Integer.MAX_VALUE, 1);
@ -50,14 +48,17 @@ public class ListPanel extends FileBotPanel {
super("List", ResourceManager.getIcon("panel.list"));
list.setTransferablePolicy(new FileListTransferablePolicy(list));
list.setExportHandler(new FileBotListExportHandler(list));
list.getRemoveAction().setEnabled(true);
Box buttons = Box.createHorizontalBox();
buttons.setBorder(new EmptyBorder(5, 5, 5, 5));
buttons.add(Box.createHorizontalGlue());
buttons.add(new JButton(loadAction));
buttons.add(new JButton(new LoadAction(list.getTransferablePolicy())));
buttons.add(Box.createHorizontalStrut(5));
buttons.add(new JButton(saveAction));
buttons.add(new JButton(new SaveAction(list.getExportHandler())));
buttons.add(Box.createHorizontalGlue());
list.add(buttons, BorderLayout.SOUTH);
@ -88,9 +89,9 @@ public class ListPanel extends FileBotPanel {
add(spinners, BorderLayout.NORTH);
add(list, BorderLayout.CENTER);
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), createAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), createAction);
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(getPanelName(), list.getTransferablePolicy()));
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(this, list.getTransferablePolicy()));
}
@ -144,7 +145,8 @@ public class ListPanel extends FileBotPanel {
index += increment;
} while (index != (to + increment));
list.getModel().set(entries);
list.getModel().clear();
list.getModel().addAll(entries);
}
};

View File

@ -8,17 +8,17 @@ import java.util.List;
import net.sourceforge.filebot.FileBotUtil;
import net.sourceforge.filebot.ui.panel.rename.entry.FileEntry;
import net.sourceforge.filebot.ui.transferablepolicies.FileTransferablePolicy;
import net.sourceforge.tuned.ui.SimpleListModel;
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
import ca.odell.glazedlists.EventList;
class FilesListTransferablePolicy extends FileTransferablePolicy {
private final SimpleListModel model;
private final EventList<? super FileEntry> model;
public FilesListTransferablePolicy(SimpleListModel listModel) {
this.model = listModel;
public FilesListTransferablePolicy(EventList<? super FileEntry> model) {
this.model = model;
}
@ -47,7 +47,7 @@ class FilesListTransferablePolicy extends FileTransferablePolicy {
@Override
public String getDescription() {
public String getFileFilterDescription() {
return "files and folders";
}

View File

@ -18,6 +18,7 @@ import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.panel.rename.entry.FileEntry;
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
import net.sourceforge.filebot.ui.panel.rename.matcher.Match;
import net.sourceforge.filebot.ui.panel.rename.matcher.Matcher;
@ -32,8 +33,8 @@ class MatchAction extends AbstractAction {
private CompositeSimilarityMetric metrics;
private final RenameList namesList;
private final RenameList filesList;
private final RenameList<ListEntry> namesList;
private final RenameList<FileEntry> filesList;
private boolean matchName2File;
@ -41,7 +42,7 @@ class MatchAction extends AbstractAction {
public static final String MATCH_FILES_2_NAMES_DESCRIPTION = "Match files to names";
public MatchAction(RenameList namesList, RenameList filesList) {
public MatchAction(RenameList<ListEntry> namesList, RenameList<FileEntry> filesList) {
super("Match");
this.namesList = namesList;
@ -77,13 +78,14 @@ class MatchAction extends AbstractAction {
}
@SuppressWarnings("unchecked")
public void actionPerformed(ActionEvent evt) {
JComponent source = (JComponent) evt.getSource();
SwingUtilities.getRoot(source).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
RenameList primaryList = matchName2File ? namesList : filesList;
RenameList secondaryList = matchName2File ? filesList : namesList;
RenameList<ListEntry> primaryList = (RenameList<ListEntry>) (matchName2File ? namesList : filesList);
RenameList<ListEntry> secondaryList = (RenameList<ListEntry>) (matchName2File ? filesList : namesList);
BackgroundMatcher backgroundMatcher = new BackgroundMatcher(primaryList, secondaryList, metrics);
SwingWorkerProgressMonitor monitor = new SwingWorkerProgressMonitor(SwingUtilities.getWindowAncestor(source), backgroundMatcher);
@ -109,15 +111,15 @@ class MatchAction extends AbstractAction {
}
private class BackgroundMatcher extends SwingWorker<List<Match>, Void> {
private static class BackgroundMatcher extends SwingWorker<List<Match>, Void> {
private final RenameList primaryList;
private final RenameList secondaryList;
private final RenameList<ListEntry> primaryList;
private final RenameList<ListEntry> secondaryList;
private final Matcher matcher;
public BackgroundMatcher(RenameList primaryList, RenameList secondaryList, SimilarityMetric similarityMetric) {
public BackgroundMatcher(RenameList<ListEntry> primaryList, RenameList<ListEntry> secondaryList, SimilarityMetric similarityMetric) {
this.primaryList = primaryList;
this.secondaryList = secondaryList;
@ -167,7 +169,6 @@ class MatchAction extends AbstractAction {
primaryList.getModel().clear();
secondaryList.getModel().clear();
for (Match match : matches) {
primaryList.getModel().add(match.getA());
secondaryList.getModel().add(match.getB());

View File

@ -2,6 +2,7 @@
package net.sourceforge.filebot.ui.panel.rename;
import java.awt.datatransfer.Transferable;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@ -19,20 +20,35 @@ import net.sourceforge.filebot.torrent.Torrent;
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
import net.sourceforge.filebot.ui.panel.rename.entry.StringEntry;
import net.sourceforge.filebot.ui.panel.rename.entry.TorrentEntry;
import net.sourceforge.filebot.ui.transferablepolicies.CompositeTransferablePolicy;
import net.sourceforge.filebot.ui.transferablepolicies.TextTransferablePolicy;
import net.sourceforge.filebot.ui.transfer.StringTransferablePolicy;
class NamesListTransferablePolicy extends CompositeTransferablePolicy {
class NamesListTransferablePolicy extends FilesListTransferablePolicy {
private final RenameList list;
private final RenameList<ListEntry> list;
private final TextPolicy textPolicy = new TextPolicy();
public NamesListTransferablePolicy(RenameList list) {
this.list = list;
public NamesListTransferablePolicy(RenameList<ListEntry> list) {
super(list.getModel());
addPolicy(new FilePolicy());
addPolicy(new TextPolicy());
this.list = list;
}
@Override
public boolean accept(Transferable tr) {
return textPolicy.accept(tr) || super.accept(tr);
}
@Override
public void handleTransferable(Transferable tr, TransferAction action) {
if (super.accept(tr))
super.handleTransferable(tr, action);
else if (textPolicy.accept(tr))
textPolicy.handleTransferable(tr, action);
}
@ -57,90 +73,81 @@ class NamesListTransferablePolicy extends CompositeTransferablePolicy {
@Override
protected void clear() {
list.getModel().clear();
protected void load(List<File> files) {
if (FileBotUtil.containsOnlyListFiles(files)) {
loadListFiles(files);
} else if (FileBotUtil.containsOnlyTorrentFiles(files)) {
loadTorrentFiles(files);
} else {
super.load(files);
}
}
private void loadListFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line = null;
while ((line = in.readLine()) != null) {
if (line.trim().length() > 0) {
entries.add(new StringEntry(line));
}
}
in.close();
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
private void loadTorrentFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
Torrent torrent = new Torrent(file);
for (Torrent.Entry entry : torrent.getFiles()) {
entries.add(new TorrentEntry(entry));
}
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
@Override
public String getFileFilterDescription() {
return "text files and torrent files";
}
private class FilePolicy extends FilesListTransferablePolicy {
private class TextPolicy extends StringTransferablePolicy {
public FilePolicy() {
super(list.getModel());
@Override
protected void clear() {
NamesListTransferablePolicy.this.clear();
}
@Override
protected void load(List<File> files) {
if (FileBotUtil.containsOnlyListFiles(files)) {
loadListFiles(files);
} else if (FileBotUtil.containsOnlyTorrentFiles(files)) {
loadTorrentFiles(files);
} else {
super.load(files);
}
}
private void loadListFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line = null;
while ((line = in.readLine()) != null) {
if (line.trim().length() > 0) {
entries.add(new StringEntry(line));
}
}
in.close();
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
private void loadTorrentFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
Torrent torrent = new Torrent(file);
for (Torrent.Entry entry : torrent.getFiles()) {
entries.add(new TorrentEntry(entry));
}
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
@Override
public String getDescription() {
return "text files and torrent files";
}
};
private class TextPolicy extends TextTransferablePolicy {
@Override
protected void load(String text) {
protected void load(String string) {
List<ListEntry> entries = new ArrayList<ListEntry>();
String[] lines = text.split("\r?\n");
String[] lines = string.split("\r?\n");
for (String line : lines) {
@ -153,11 +160,6 @@ class NamesListTransferablePolicy extends CompositeTransferablePolicy {
}
}
@Override
public String getDescription() {
return "lines of text";
}
};
}
}

View File

@ -18,11 +18,11 @@ import net.sourceforge.tuned.FileUtil;
public class RenameAction extends AbstractAction {
private final RenameList namesList;
private final RenameList filesList;
private final RenameList<ListEntry> namesList;
private final RenameList<FileEntry> filesList;
public RenameAction(RenameList namesList, RenameList filesList) {
public RenameAction(RenameList<ListEntry> namesList, RenameList<FileEntry> filesList) {
super("Rename", ResourceManager.getIcon("action.rename"));
this.namesList = namesList;
this.filesList = filesList;
@ -33,7 +33,7 @@ public class RenameAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
List<ListEntry> nameEntries = namesList.getEntries();
List<ListEntry> fileEntries = filesList.getEntries();
List<FileEntry> fileEntries = filesList.getEntries();
int minLength = Math.min(nameEntries.size(), fileEntries.size());
@ -41,7 +41,7 @@ public class RenameAction extends AbstractAction {
int errors = 0;
for (i = 0; i < minLength; i++) {
FileEntry fileEntry = (FileEntry) fileEntries.get(i);
FileEntry fileEntry = fileEntries.get(i);
File f = fileEntry.getFile();
String newName = nameEntries.get(i).toString() + FileUtil.getExtension(f, true);

View File

@ -6,12 +6,12 @@ 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.Box;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
@ -20,25 +20,26 @@ import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.FileBotList;
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
import net.sourceforge.filebot.ui.transfer.LoadAction;
import net.sourceforge.filebot.ui.transfer.TransferablePolicy;
class RenameList extends FileBotList {
class RenameList<E extends ListEntry> extends FileBotList<E> {
private JButton loadButton = new JButton();
public RenameList() {
super(false, true, true);
Box buttons = Box.createHorizontalBox();
buttons.setBorder(new EmptyBorder(5, 5, 5, 5));
buttons.add(Box.createGlue());
buttons.add(new JButton(downAction));
buttons.add(new JButton(upAction));
buttons.add(Box.createHorizontalStrut(10));
buttons.add(new JButton(loadAction));
buttons.add(loadButton);
buttons.add(Box.createGlue());
add(buttons, BorderLayout.SOUTH);
JList list = getListComponent();
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addMouseListener(dndReorderMouseAdapter);
@ -46,69 +47,76 @@ class RenameList extends FileBotList {
JViewport viewport = (JViewport) list.getParent();
viewport.setBackground(list.getBackground());
getRemoveAction().setEnabled(true);
}
@SuppressWarnings("unchecked")
public List<ListEntry> getEntries() {
return (List<ListEntry>) getModel().getCopy();
@Override
public void setTransferablePolicy(TransferablePolicy transferablePolicy) {
super.setTransferablePolicy(transferablePolicy);
loadButton.setAction(new LoadAction(transferablePolicy));
}
public List<E> getEntries() {
return new ArrayList<E>(getModel());
}
private boolean moveEntry(int fromIndex, int toIndex) {
if (toIndex < 0 || toIndex >= getModel().size())
return false;
getModel().add(toIndex, getModel().remove(fromIndex));
return true;
}
private final AbstractAction upAction = new AbstractAction(null, ResourceManager.getIcon("action.up")) {
public void actionPerformed(ActionEvent e) {
int index = getListComponent().getSelectedIndex();
int selectedIndex = getListComponent().getSelectedIndex();
int toIndex = selectedIndex + 1;
if (index <= 0) // first element
return;
Object object = getModel().remove(index);
int newIndex = index - 1;
getModel().add(newIndex, object);
getListComponent().setSelectedIndex(newIndex);
if (moveEntry(selectedIndex, toIndex)) {
getListComponent().setSelectedIndex(toIndex);
}
}
};
private final AbstractAction downAction = new AbstractAction(null, ResourceManager.getIcon("action.down")) {
public void actionPerformed(ActionEvent e) {
int index = getListComponent().getSelectedIndex();
int selectedIndex = getListComponent().getSelectedIndex();
int toIndex = selectedIndex - 1;
if (index >= getModel().getSize() - 1) // last element
return;
Object object = getModel().remove(index);
int newIndex = index + 1;
getModel().add(newIndex, object);
getListComponent().setSelectedIndex(newIndex);
if (moveEntry(selectedIndex, toIndex)) {
getListComponent().setSelectedIndex(toIndex);
}
}
};
protected final LoadAction loadAction = new LoadAction(getTransferablePolicy());
private MouseAdapter dndReorderMouseAdapter = new MouseAdapter() {
private final MouseAdapter dndReorderMouseAdapter = new MouseAdapter() {
private int from = -1;
private int fromIndex = -1;
@Override
public void mousePressed(MouseEvent m) {
from = getListComponent().getSelectedIndex();
fromIndex = getListComponent().getSelectedIndex();
}
@Override
public void mouseDragged(MouseEvent m) {
int to = getListComponent().getSelectedIndex();
int toIndex = getListComponent().getSelectedIndex();
if (to == from)
if (toIndex == fromIndex)
return;
Object object = getModel().remove(from);
getModel().add(to, object);
from = to;
moveEntry(fromIndex, toIndex);
fromIndex = toIndex;
}
};

View File

@ -27,12 +27,14 @@ import javax.swing.event.ListDataListener;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.FileBotPanel;
import net.sourceforge.filebot.ui.panel.rename.entry.FileEntry;
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
public class RenamePanel extends FileBotPanel {
private RenameList namesList = new RenameList();
private RenameList filesList = new RenameList();
private RenameList<ListEntry> namesList = new RenameList<ListEntry>();
private RenameList<FileEntry> filesList = new RenameList<FileEntry>();
private MatchAction matchAction = new MatchAction(namesList, filesList);
@ -52,23 +54,23 @@ public class RenamePanel extends FileBotPanel {
filesList.setTitle("Files");
filesList.setTransferablePolicy(new FilesListTransferablePolicy(filesList.getModel()));
RenameListCellRenderer cellrenderer = new RenameListCellRenderer(namesList.getModel(), filesList.getModel());
JList namesListComponent = namesList.getListComponent();
JList filesListComponent = filesList.getListComponent();
namesList.getListComponent().setCellRenderer(cellrenderer);
filesList.getListComponent().setCellRenderer(cellrenderer);
RenameListCellRenderer cellrenderer = new RenameListCellRenderer(namesListComponent.getModel(), filesListComponent.getModel());
JList list1 = namesList.getListComponent();
JList list2 = filesList.getListComponent();
namesListComponent.setCellRenderer(cellrenderer);
filesListComponent.setCellRenderer(cellrenderer);
ListSelectionModel selectionModel = new DefaultListSelectionModel();
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
namesList.getListComponent().setSelectionModel(selectionModel);
filesList.getListComponent().setSelectionModel(selectionModel);
namesListComponent.setSelectionModel(selectionModel);
filesListComponent.setSelectionModel(selectionModel);
viewPortSynchroniser = new ViewPortSynchronizer((JViewport) list1.getParent(), (JViewport) list2.getParent());
viewPortSynchroniser = new ViewPortSynchronizer((JViewport) namesListComponent.getParent(), (JViewport) filesListComponent.getParent());
similarityPanel = new SimilarityPanel(list1, list2);
similarityPanel = new SimilarityPanel(namesListComponent, filesListComponent);
similarityPanel.setVisible(false);
similarityPanel.setMetrics(matchAction.getMetrics());
@ -89,8 +91,8 @@ public class RenamePanel extends FileBotPanel {
add(box, BorderLayout.CENTER);
namesList.getModel().addListDataListener(repaintOnDataChange);
filesList.getModel().addListDataListener(repaintOnDataChange);
namesListComponent.getModel().addListDataListener(repaintOnDataChange);
filesListComponent.getModel().addListDataListener(repaintOnDataChange);
}
@ -120,7 +122,7 @@ public class RenamePanel extends FileBotPanel {
return centerBox;
}
private ListDataListener repaintOnDataChange = new ListDataListener() {
private final ListDataListener repaintOnDataChange = new ListDataListener() {
public void contentsChanged(ListDataEvent e) {

View File

@ -29,7 +29,7 @@ import javax.swing.border.EmptyBorder;
import net.sourceforge.filebot.FileBotUtil;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
import net.sourceforge.tuned.ui.SimpleListModel;
import net.sourceforge.tuned.ui.ArrayListModel;
import net.sourceforge.tuned.ui.TunedUtil;
@ -51,7 +51,7 @@ public class ValidateNamesDialog extends JDialog {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JList list = new JList(new SimpleListModel(entries));
JList list = new JList(new ArrayListModel(entries));
list.setEnabled(false);
list.setCellRenderer(new HighlightListCellRenderer(FileBotUtil.INVALID_CHARACTERS_PATTERN, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4));
@ -89,7 +89,7 @@ public class ValidateNamesDialog extends JDialog {
setPreferredSize(new Dimension(365, 280));
pack();
TunedUtil.registerActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
TunedUtil.putActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
}
@ -176,7 +176,7 @@ public class ValidateNamesDialog extends JDialog {
protected void actionPropertyChanged(Action action, String propertyName) {
super.actionPropertyChanged(action, propertyName);
if (propertyName == ContinueAction.ALPHA) {
if (propertyName.equals(ContinueAction.ALPHA)) {
alpha = getAlpha(action);
}
}

View File

@ -38,4 +38,5 @@ public class FileEntry extends AbstractFileEntry {
public File getFile() {
return file;
}
}

View File

@ -16,10 +16,8 @@ public abstract class AbstractNameSimilarityMetric implements SimilarityMetric {
protected String normalize(String name) {
name = stripChecksum(name);
name = normalizeSeparators(name);
name = name.trim();
name = name.toLowerCase();
return name;
return name.trim().toLowerCase();
}

View File

@ -46,7 +46,7 @@ public class NumericSimilarityMetric extends AbstractNameSimilarityMetric {
private static class NumberTokeniser implements InterfaceTokeniser {
private final String delimiter = "(\\D)+";
private static final String delimiter = "(\\D)+";
@Override

View File

@ -8,9 +8,11 @@ import javax.swing.JComponent;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.FileBotList;
import net.sourceforge.filebot.ui.FileBotTabComponent;
import net.sourceforge.filebot.ui.transfer.DefaultListExportHandler;
import net.sourceforge.filebot.web.Episode;
public class EpisodeListPanel extends FileBotList {
public class EpisodeListPanel extends FileBotList<Episode> {
private final FileBotTabComponent tabComponent = new FileBotTabComponent();
@ -20,7 +22,11 @@ public class EpisodeListPanel extends FileBotList {
public EpisodeListPanel() {
super(true, true, false);
setExportHandler(new DefaultListExportHandler(list));
getRemoveAction().setEnabled(true);
setBorder(null);
listScrollPane.setBorder(null);
}

View File

@ -3,7 +3,6 @@ package net.sourceforge.filebot.ui.panel.search;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
@ -32,12 +31,14 @@ import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.FileBotList;
import net.sourceforge.filebot.ui.FileBotPanel;
import net.sourceforge.filebot.ui.HistoryPanel;
import net.sourceforge.filebot.ui.MessageManager;
import net.sourceforge.filebot.ui.SelectDialog;
import net.sourceforge.filebot.ui.transfer.AdaptiveFileExportHandler;
import net.sourceforge.filebot.ui.transfer.FileExportHandler;
import net.sourceforge.filebot.ui.transfer.SaveAction;
import net.sourceforge.filebot.ui.transfer.Saveable;
import net.sourceforge.filebot.web.AnidbClient;
import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.EpisodeListClient;
@ -118,9 +119,9 @@ public class SearchPanel extends FileBotPanel {
this.add(mainPanel, BorderLayout.CENTER);
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("UP"), upAction);
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("DOWN"), downAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("UP"), upAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("DOWN"), downAction);
}
@ -179,21 +180,27 @@ public class SearchPanel extends FileBotPanel {
}
};
private final SaveAction saveAction = new SaveAction(null) {
private final SaveAction saveAction = new SaveAction(new SelectedTabExportHandler());
private class SelectedTabExportHandler extends AdaptiveFileExportHandler {
/**
* @return the <code>FileExportHandler</code> of the currently selected tab
*/
@Override
public void actionPerformed(ActionEvent e) {
Component comp = tabbedPane.getSelectedComponent();
if (comp instanceof Saveable) {
setSaveable((Saveable) comp);
super.actionPerformed(e);
protected FileExportHandler getExportHandler() {
try {
FileBotList<?> list = (FileBotList<?>) tabbedPane.getSelectedComponent();
return list.getExportHandler();
} catch (ClassCastException e) {
// selected component is the history panel
return null;
}
}
};
}
private class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
private final String query;
@ -267,14 +274,7 @@ public class SearchPanel extends FileBotPanel {
}
SearchResult selectedResult = null;
/*
* NEEDED??? exact find without cache???
/// TODO: ??????
if (task.client.getFoundName(task.query) != null) {
// a show matching the search term exactly has already been found
showname = task.client.getFoundName(task.query);
}*/
if (searchResults.size() == 1) {
// only one show found, select this one
selectedResult = searchResults.iterator().next();

View File

@ -92,7 +92,7 @@ public class Checksum {
}
public Integer getProgress() {
public synchronized Integer getProgress() {
if (state == State.INPROGRESS)
return computationTask.getProgress();
@ -130,8 +130,10 @@ public class Checksum {
@Override
public void done(PropertyChangeEvent evt) {
try {
if (!computationTask.isCancelled()) {
setChecksum(computationTask.get());
ChecksumComputationTask task = (ChecksumComputationTask) evt.getSource();
if (!task.isCancelled()) {
setChecksum(task.get());
}
} catch (Exception e) {
// might happen if file system is corrupt (e.g. CRC errors)

View File

@ -2,6 +2,7 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
@ -15,6 +16,8 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
@ -23,36 +26,37 @@ import net.sourceforge.tuned.DefaultThreadFactory;
public class ChecksumComputationService {
public static final String ACTIVE_PROPERTY = "ACTIVE_PROPERTY";
public static final String REMAINING_TASK_COUNT_PROPERTY = "REMAINING_TASK_COUNT_PROPERTY";
private static final ThreadFactory checksumComputationThreadFactory = new DefaultThreadFactory("ChecksumComputationPool", Thread.MIN_PRIORITY);
private static final ChecksumComputationService service = new ChecksumComputationService();
public static ChecksumComputationService getService() {
return service;
}
public static final String ACTIVE_PROPERTY = "active";
public static final String REMAINING_TASK_COUNT_PROPERTY = "remainingTaskCount";
private final Map<File, ChecksumComputationExecutor> executors = new HashMap<File, ChecksumComputationExecutor>();
private final AtomicInteger activeSessionTaskCount = new AtomicInteger(0);
private final AtomicInteger remainingTaskCount = new AtomicInteger(0);
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private final ThreadFactory threadFactory;
/**
* Property change events will be fired on the event dispatch thread
*/
private final SwingWorkerPropertyChangeSupport propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
private ChecksumComputationService() {
// hide constructor
public ChecksumComputationService() {
this(new DefaultThreadFactory("ChecksumComputationPool", Thread.MIN_PRIORITY));
}
public Checksum getChecksum(File file, File workerQueueKey) {
public ChecksumComputationService(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
public Checksum schedule(File file, File workerQueue) {
ChecksumComputationTask task = new ChecksumComputationTask(file);
Checksum checksum = new Checksum(task);
getExecutor(workerQueueKey).execute(task);
getExecutor(workerQueue).execute(task);
return checksum;
}
@ -63,21 +67,18 @@ public class ChecksumComputationService {
}
private void deactivate(boolean shutdownNow) {
synchronized (executors) {
for (ChecksumComputationExecutor executor : executors.values()) {
if (shutdownNow) {
executor.shutdownNow();
} else {
executor.shutdown();
}
}
executors.clear();
activeSessionTaskCount.set(0);
remainingTaskCount.set(0);
private synchronized void deactivate(boolean shutdownNow) {
for (ChecksumComputationExecutor executor : executors.values()) {
if (shutdownNow)
executor.shutdownNow();
else
executor.shutdown();
}
executors.clear();
activeSessionTaskCount.set(0);
remainingTaskCount.set(0);
}
@ -96,26 +97,22 @@ public class ChecksumComputationService {
}
public void purge() {
synchronized (executors) {
for (ChecksumComputationExecutor executor : executors.values()) {
executor.purge();
}
public synchronized void purge() {
for (ChecksumComputationExecutor executor : executors.values()) {
executor.purge();
}
}
private ChecksumComputationExecutor getExecutor(File workerQueueKey) {
synchronized (executors) {
ChecksumComputationExecutor executor = executors.get(workerQueueKey);
if (executor == null) {
executor = new ChecksumComputationExecutor();
executors.put(workerQueueKey, executor);
}
return executor;
private synchronized ChecksumComputationExecutor getExecutor(File workerQueue) {
ChecksumComputationExecutor executor = executors.get(workerQueue);
if (executor == null) {
executor = new ChecksumComputationExecutor();
executors.put(workerQueue, executor);
}
return executor;
}
@ -125,7 +122,7 @@ public class ChecksumComputationService {
public ChecksumComputationExecutor() {
super(MINIMUM_POOL_SIZE, MINIMUM_POOL_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), checksumComputationThreadFactory);
super(MINIMUM_POOL_SIZE, MINIMUM_POOL_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
}
@ -134,13 +131,7 @@ public class ChecksumComputationService {
// for lots of files, use multiple threads
// e.g 50 files ~ 1 thread, 1000 files ~ 3 threads, 40000 files ~ 5 threads
int preferredPoolSize = MINIMUM_POOL_SIZE;
int queueSize = getQueue().size();
if (queueSize > 0) {
preferredPoolSize += Math.log10(Math.max(queueSize / 10, 1));
}
int preferredPoolSize = (int) Math.log10(Math.max(getQueue().size() / 10, MINIMUM_POOL_SIZE));
if (getCorePoolSize() != preferredPoolSize) {
setCorePoolSize(preferredPoolSize);
@ -179,8 +170,8 @@ public class ChecksumComputationService {
for (ChecksumComputationTask task : cancelledTasks) {
remove(task);
}
} catch (ConcurrentModificationException ex) {
} catch (ConcurrentModificationException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
@ -218,12 +209,12 @@ public class ChecksumComputationService {
private void setActive(boolean active) {
SwingUtilities.invokeLater(new FirePropertyChangeRunnable(ACTIVE_PROPERTY, active));
propertyChangeSupport.firePropertyChange(ACTIVE_PROPERTY, null, active);
}
private void fireRemainingTaskCountChange() {
SwingUtilities.invokeLater(new FirePropertyChangeRunnable(REMAINING_TASK_COUNT_PROPERTY, getRemainingTaskCount()));
propertyChangeSupport.firePropertyChange(REMAINING_TASK_COUNT_PROPERTY, null, getRemainingTaskCount());
}
@ -237,21 +228,23 @@ public class ChecksumComputationService {
}
private class FirePropertyChangeRunnable implements Runnable {
private static class SwingWorkerPropertyChangeSupport extends PropertyChangeSupport {
private final String property;
private final Object newValue;
public FirePropertyChangeRunnable(String property, Object newValue) {
this.property = property;
this.newValue = newValue;
public SwingWorkerPropertyChangeSupport(Object sourceBean) {
super(sourceBean);
}
@Override
public void run() {
propertyChangeSupport.firePropertyChange(property, null, newValue);
public void firePropertyChange(final PropertyChangeEvent evt) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SwingWorkerPropertyChangeSupport.super.firePropertyChange(evt);
}
});
}
}

View File

@ -16,24 +16,28 @@ public class ChecksumRow {
private HashMap<File, Checksum> checksumMap = new HashMap<File, Checksum>();
private Long checksumFromFileName = null;
/**
* Checksum that is embedded in the file name (e.g. My File [49A93C5F].txt)
*/
private Long embeddedChecksum = null;
public static enum State {
OK, UNKNOWN, WARNING, ERROR;
OK,
WARNING,
ERROR,
UNKNOWN;
}
public ChecksumRow(String name) {
this.name = name;
// look for a patter like [49A93C5F]
Pattern pattern = Pattern.compile(".*\\[(\\p{XDigit}{8})\\].*");
Matcher matcher = pattern.matcher(getName());
// look for a checksum pattern like [49A93C5F]
Matcher matcher = Pattern.compile("\\[(\\p{XDigit}{8})\\]").matcher(name);
if (matcher.matches()) {
String checksumString = matcher.group(matcher.groupCount());
checksumFromFileName = Long.parseLong(checksumString, 16);
if (matcher.find()) {
embeddedChecksum = Long.parseLong(matcher.group(1), 16);
}
}
@ -61,9 +65,9 @@ public class ChecksumRow {
return State.ERROR;
}
if (!checksums.isEmpty() && checksumFromFileName != null) {
// check if the checksum in the filename matches
if (!checksums.contains(checksumFromFileName))
if (!checksums.isEmpty() && embeddedChecksum != null) {
// check if the embedded checksum matches
if (!checksums.contains(embeddedChecksum))
return State.WARNING;
}
@ -71,8 +75,8 @@ public class ChecksumRow {
}
public Checksum getChecksum(File columnRoot) {
return checksumMap.get(columnRoot);
public Checksum getChecksum(File column) {
return checksumMap.get(column);
}
@ -81,13 +85,13 @@ public class ChecksumRow {
}
public void putChecksum(File columnRoot, Checksum checksum) {
checksumMap.put(columnRoot, checksum);
public void putChecksum(File column, Checksum checksum) {
checksumMap.put(column, checksum);
}
public void removeChecksum(File columnRoot) {
checksumMap.remove(columnRoot);
public void removeChecksum(File column) {
checksumMap.remove(column);
}
}

View File

@ -0,0 +1,90 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import net.sourceforge.filebot.ui.transfer.FileExportHandler;
import net.sourceforge.tuned.FileUtil;
public class ChecksumTableExportHandler extends FileExportHandler {
private final ChecksumTableModel model;
public ChecksumTableExportHandler(ChecksumTableModel model) {
this.model = model;
}
@Override
public boolean canExport() {
return model.getRowCount() > 0 && model.getChecksumColumnCount() > 0;
}
@Override
public void export(OutputStream out) throws IOException {
export(out, model.getChecksumColumn(0));
}
@Override
public String getDefaultFileName() {
return getDefaultFileName(model.getChecksumColumn(0));
}
public void export(File file, File column) throws IOException {
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
try {
export(out, column);
} finally {
out.close();
}
}
public void export(OutputStream out, File column) throws IOException {
PrintStream printer = new PrintStream(out);
SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
Date now = new Date();
printer.println("; Generated by FileBot on " + date.format(now) + " at " + time.format(now));
printer.println(";");
printer.println(";");
Map<String, Checksum> checksumMap = model.getChecksumColumn(column);
for (Entry<String, Checksum> entry : checksumMap.entrySet()) {
printer.println(String.format("%s %s", entry.getKey(), entry.getValue()));
}
}
public String getDefaultFileName(File column) {
String name = "";
if (column != null)
name = FileUtil.getFileName(column);
if (name.isEmpty())
name = "name";
return name + ".sfv";
}
}

View File

@ -6,6 +6,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@ -20,12 +21,19 @@ import net.sourceforge.tuned.FileUtil;
class ChecksumTableModel extends AbstractTableModel {
private List<ChecksumRow> rows = new ArrayList<ChecksumRow>();
private Map<String, ChecksumRow> rowMap = new HashMap<String, ChecksumRow>();
private List<ChecksumRow> rows = new ArrayList<ChecksumRow>(50);
private List<File> checksumColumnRoots = new ArrayList<File>();
/**
* Hash map for fast access to the row of a given name
*/
private Map<String, ChecksumRow> rowMap = new HashMap<String, ChecksumRow>(50);
private final int checksumColumnsOffset = 2;
private List<File> columns = new ArrayList<File>();
/**
* Checksum start at column 3
*/
private static final int checksumColumnOffset = 2;
@Override
@ -36,9 +44,11 @@ class ChecksumTableModel extends AbstractTableModel {
if (columnIndex == 1)
return "Name";
if (columnIndex >= checksumColumnsOffset) {
File columnRoot = checksumColumnRoots.get(columnIndex - checksumColumnsOffset);
return FileUtil.getFolderName(columnRoot);
if (columnIndex >= checksumColumnOffset) {
File column = columns.get(columnIndex - checksumColumnOffset);
// works for files too and simply returns the name unchanged
return FileUtil.getFolderName(column);
}
return null;
@ -53,7 +63,7 @@ class ChecksumTableModel extends AbstractTableModel {
if (columnIndex == 1)
return String.class;
if (columnIndex >= checksumColumnsOffset)
if (columnIndex >= checksumColumnOffset)
return Checksum.class;
return null;
@ -61,12 +71,17 @@ class ChecksumTableModel extends AbstractTableModel {
public int getColumnCount() {
return checksumColumnsOffset + getChecksumColumnCount();
return checksumColumnOffset + getChecksumColumnCount();
}
public int getChecksumColumnCount() {
return checksumColumnRoots.size();
return columns.size();
}
public List<File> getChecksumColumns() {
return Collections.unmodifiableList(columns);
}
@ -84,20 +99,20 @@ class ChecksumTableModel extends AbstractTableModel {
if (columnIndex == 1)
return row.getName();
if (columnIndex >= checksumColumnsOffset) {
File columnRoot = checksumColumnRoots.get(columnIndex - checksumColumnsOffset);
return row.getChecksum(columnRoot);
if (columnIndex >= checksumColumnOffset) {
File column = columns.get(columnIndex - checksumColumnOffset);
return row.getChecksum(column);
}
return null;
}
public synchronized void addAll(List<ChecksumCell> list) {
public void addAll(List<ChecksumCell> list) {
int firstRow = getRowCount();
for (ChecksumCell entry : list) {
addChecksum(entry.getName(), entry.getChecksum(), entry.getColumnRoot());
addChecksum(entry.getName(), entry.getChecksum(), entry.getColumn());
}
int lastRow = getRowCount() - 1;
@ -108,7 +123,7 @@ class ChecksumTableModel extends AbstractTableModel {
}
private synchronized void addChecksum(String name, Checksum checksum, File columnRoot) {
private void addChecksum(String name, Checksum checksum, File column) {
ChecksumRow row = rowMap.get(name);
if (row == null) {
@ -117,17 +132,17 @@ class ChecksumTableModel extends AbstractTableModel {
rowMap.put(name, row);
}
row.putChecksum(columnRoot, checksum);
row.putChecksum(column, checksum);
checksum.addPropertyChangeListener(checksumListener);
if (!checksumColumnRoots.contains(columnRoot)) {
checksumColumnRoots.add(columnRoot);
if (!columns.contains(column)) {
columns.add(column);
fireTableStructureChanged();
}
}
public synchronized void removeRows(int... rowIndices) {
public void removeRows(int... rowIndices) {
ArrayList<ChecksumRow> rowsToRemove = new ArrayList<ChecksumRow>(rowIndices.length);
for (int i : rowIndices) {
@ -137,37 +152,35 @@ class ChecksumTableModel extends AbstractTableModel {
for (Checksum checksum : row.getChecksums()) {
checksum.cancelComputationTask();
}
rowMap.remove(row.getName());
}
rows.removeAll(rowsToRemove);
fireTableRowsDeleted(rowIndices[0], rowIndices[rowIndices.length - 1]);
ChecksumComputationService.getService().purge();
}
public synchronized void clear() {
ChecksumComputationService.getService().reset();
checksumColumnRoots.clear();
public void clear() {
columns.clear();
rows.clear();
rowMap.clear();
fireTableStructureChanged();
fireTableDataChanged();
}
public File getChecksumColumnRoot(int checksumColumnIndex) {
return checksumColumnRoots.get(checksumColumnIndex);
public File getChecksumColumn(int columnIndex) {
return columns.get(columnIndex);
}
public Map<String, Checksum> getChecksumColumn(File columnRoot) {
public Map<String, Checksum> getChecksumColumn(File column) {
LinkedHashMap<String, Checksum> checksumMap = new LinkedHashMap<String, Checksum>();
for (ChecksumRow row : rows) {
Checksum checksum = row.getChecksum(columnRoot);
Checksum checksum = row.getChecksum(column);
if ((checksum != null) && (checksum.getState() == Checksum.State.READY)) {
checksumMap.put(row.getName(), checksum);
@ -189,13 +202,13 @@ class ChecksumTableModel extends AbstractTableModel {
private final String name;
private final Checksum checksum;
private final File columnRoot;
private final File column;
public ChecksumCell(String name, Checksum checksum, File columnRoot) {
public ChecksumCell(String name, Checksum checksum, File column) {
this.name = name;
this.checksum = checksum;
this.columnRoot = columnRoot;
this.column = column;
}
@ -209,8 +222,8 @@ class ChecksumTableModel extends AbstractTableModel {
}
public File getColumnRoot() {
return columnRoot;
public File getColumn() {
return column;
}

View File

@ -5,7 +5,8 @@ package net.sourceforge.filebot.ui.panel.sfv;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;
import java.io.IOException;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
@ -32,7 +33,7 @@ public class SfvPanel extends FileBotPanel {
private SfvTable sfvTable = new SfvTable();
private TotalProgressPanel totalProgressPanel = new TotalProgressPanel();
private TotalProgressPanel totalProgressPanel = new TotalProgressPanel(sfvTable.getChecksumComputationService());
public SfvPanel() {
@ -61,85 +62,12 @@ public class SfvPanel extends FileBotPanel {
add(southPanel, BorderLayout.SOUTH);
// Shortcut DELETE
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(getPanelName(), sfvTable.getTransferablePolicy()));
MessageBus.getDefault().addMessageHandler(getPanelName(), new FileTransferableMessageHandler(this, sfvTable.getTransferablePolicy()));
}
private final SaveAction saveAction = new SaveAction(sfvTable) {
private int index;
private String name;
private File folder = null;
@Override
protected void save(File file) {
sfvTable.save(file, index);
}
@Override
protected String getDefaultFileName() {
return name;
}
@Override
protected File getDefaultFolder() {
return folder;
}
@Override
public void actionPerformed(ActionEvent e) {
ChecksumTableModel model = (ChecksumTableModel) sfvTable.getModel();
ArrayList<File> options = new ArrayList<File>();
for (int i = 0; i < model.getChecksumColumnCount(); i++) {
options.add(model.getChecksumColumnRoot(i));
}
File selected = null;
if (options.size() > 1) {
SelectDialog<File> selectDialog = new SelectDialog<File>(SwingUtilities.getWindowAncestor(SfvPanel.this), options) {
@Override
protected String convertValueToString(Object value) {
File columnRoot = (File) value;
return FileUtil.getFolderName(columnRoot);
}
};
selectDialog.setText("Select checksum column:");
selectDialog.setVisible(true);
selected = selectDialog.getSelectedValue();
} else if (options.size() == 1) {
selected = options.get(0);
}
if (selected == null)
return;
index = options.indexOf(selected);
name = FileUtil.getFileName(selected);
if (name.isEmpty())
name = "name";
name += ".sfv";
// selected is either a folder or a sfv file
if (selected.isDirectory()) {
folder = selected;
}
super.actionPerformed(e);
}
};
private final SaveAction saveAction = new ChecksumTableSaveAction();
private final LoadAction loadAction = new LoadAction(sfvTable.getTransferablePolicy());
@ -169,4 +97,68 @@ public class SfvPanel extends FileBotPanel {
}
};
private class ChecksumTableSaveAction extends SaveAction {
private File selectedColumn = null;
@Override
protected boolean canExport() {
return selectedColumn != null && sfvTable.getExportHandler().canExport();
}
@Override
protected void export(File file) throws IOException {
sfvTable.getExportHandler().export(file, selectedColumn);
}
@Override
protected String getDefaultFileName() {
return sfvTable.getExportHandler().getDefaultFileName(selectedColumn);
}
@Override
protected File getDefaultFolder() {
// if column is a folder use it as default folder in file dialog
return selectedColumn.isDirectory() ? selectedColumn : null;
}
@Override
public void actionPerformed(ActionEvent e) {
List<File> options = sfvTable.getModel().getChecksumColumns();
this.selectedColumn = null;
if (options.size() == 1) {
// auto-select if there is only one option
this.selectedColumn = options.get(0);
} else if (options.size() > 1) {
// show user his/her options
SelectDialog<File> selectDialog = new SelectDialog<File>(SwingUtilities.getWindowAncestor(SfvPanel.this), options) {
@Override
protected String convertValueToString(Object value) {
return FileUtil.getFolderName((File) value);
}
};
selectDialog.setText("Select checksum column:");
selectDialog.setVisible(true);
this.selectedColumn = selectDialog.getSelectedValue();
}
if (this.selectedColumn != null) {
// continue if a column was selected
super.actionPerformed(e);
}
}
}
}

View File

@ -2,14 +2,6 @@
package net.sourceforge.filebot.ui.panel.sfv;
import java.io.File;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.TableModelEvent;
@ -20,26 +12,20 @@ import net.sourceforge.filebot.ui.panel.sfv.ChecksumTableModel.ChecksumTableMode
import net.sourceforge.filebot.ui.panel.sfv.renderer.ChecksumTableCellRenderer;
import net.sourceforge.filebot.ui.panel.sfv.renderer.StateIconTableCellRenderer;
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
import net.sourceforge.filebot.ui.transfer.ExportHandler;
import net.sourceforge.filebot.ui.transfer.ImportHandler;
import net.sourceforge.filebot.ui.transfer.Saveable;
import net.sourceforge.filebot.ui.transfer.SaveableExportHandler;
import net.sourceforge.filebot.ui.transfer.TransferablePolicyImportHandler;
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
import net.sourceforge.tuned.FileUtil;
class SfvTable extends JTable implements Saveable {
class SfvTable extends JTable {
private final SfvTransferablePolicy transferablePolicy;
private final ChecksumTableExportHandler exportHandler;
private final ChecksumComputationService checksumComputationService = new ChecksumComputationService();
public SfvTable() {
ChecksumTableModel model = (ChecksumTableModel) getModel();
transferablePolicy = new SfvTransferablePolicy(model);
setModel(model);
transferablePolicy = new SfvTransferablePolicy(getModel(), checksumComputationService);
exportHandler = new ChecksumTableExportHandler(getModel());
setFillsViewportHeight(true);
setAutoCreateRowSorter(true);
@ -50,10 +36,7 @@ class SfvTable extends JTable implements Saveable {
setRowHeight(20);
ImportHandler importHandler = new TransferablePolicyImportHandler(transferablePolicy);
ExportHandler exportHandler = new SaveableExportHandler(this);
setTransferHandler(new DefaultTransferHandler(importHandler, exportHandler));
setTransferHandler(new DefaultTransferHandler(transferablePolicy, exportHandler));
setDragEnabled(true);
setDefaultRenderer(ChecksumRow.State.class, new StateIconTableCellRenderer());
@ -61,17 +44,39 @@ class SfvTable extends JTable implements Saveable {
}
public TransferablePolicy getTransferablePolicy() {
public SfvTransferablePolicy getTransferablePolicy() {
return transferablePolicy;
}
public ChecksumTableExportHandler getExportHandler() {
return exportHandler;
}
public ChecksumComputationService getChecksumComputationService() {
return checksumComputationService;
}
@Override
public DefaultTransferHandler getTransferHandler() {
return (DefaultTransferHandler) super.getTransferHandler();
}
@Override
protected TableModel createDefaultDataModel() {
return new ChecksumTableModel();
}
@Override
public ChecksumTableModel getModel() {
return (ChecksumTableModel) super.getModel();
}
@Override
public void createDefaultColumnsFromModel() {
super.createDefaultColumnsFromModel();
@ -90,82 +95,30 @@ class SfvTable extends JTable implements Saveable {
public void clear() {
checksumComputationService.reset();
transferablePolicy.reset();
((ChecksumTableModel) getModel()).clear();
}
public String getDefaultFileName() {
ChecksumTableModel model = (ChecksumTableModel) getModel();
File columnRoot = model.getChecksumColumnRoot(0);
String name = "";
if (columnRoot != null)
name = FileUtil.getFileName(columnRoot);
if (name.isEmpty())
name = "name";
return name + ".sfv";
}
public boolean isSaveable() {
return getModel().getRowCount() > 0;
getModel().clear();
}
public void removeRows(int... rowIndices) {
ChecksumTableModel model = (ChecksumTableModel) getModel();
model.removeRows(rowIndices);
getModel().removeRows(rowIndices);
}
@Override
public void tableChanged(TableModelEvent e) {
// only request repaint when progress changes, or selection will go haywire
if (e.getType() == ChecksumTableModelEvent.CHECKSUM_PROGRESS) {
repaint();
} else {
super.tableChanged(e);
}
}
public void save(File file, int checksumColumnIndex) {
try {
PrintStream out = new PrintStream(file);
ChecksumTableModel model = (ChecksumTableModel) getModel();
File columnRoot = model.getChecksumColumnRoot(checksumColumnIndex);
if (columnRoot != null) {
SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
Date now = new Date();
out.println("; Generated by FileBot on " + date.format(now) + " at " + time.format(now));
out.println(";");
out.println(";");
Map<String, Checksum> checksumMap = model.getChecksumColumn(columnRoot);
for (String name : checksumMap.keySet()) {
out.println(name + " " + checksumMap.get(name).getChecksumString());
}
if (e.getType() == TableModelEvent.DELETE) {
// remove cancelled task from queue
checksumComputationService.purge();
}
out.close();
} catch (Exception e) {
// should not happen
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
public void save(File file) {
save(file, 0);
}
}

View File

@ -14,21 +14,24 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.filebot.FileBotUtil;
import net.sourceforge.filebot.ui.transferablepolicies.BackgroundFileTransferablePolicy;
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTableModel.ChecksumCell> {
private ChecksumTableModel tableModel;
private final ChecksumTableModel tableModel;
private final ChecksumComputationService checksumComputationService;
public SfvTransferablePolicy(ChecksumTableModel tableModel) {
public SfvTransferablePolicy(ChecksumTableModel tableModel, ChecksumComputationService checksumComputationService) {
this.tableModel = tableModel;
this.checksumComputationService = checksumComputationService;
}
@Override
protected void clear() {
checksumComputationService.reset();
tableModel.clear();
}
@ -60,11 +63,11 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
publish(new ChecksumTableModel.ChecksumCell(filename, new Checksum(checksumString), sfvFile));
File compareColumnRoot = sfvFile.getParentFile();
File compareFile = new File(compareColumnRoot, filename);
File column = sfvFile.getParentFile();
File file = new File(column, filename);
if (compareFile.exists()) {
publish(new ChecksumTableModel.ChecksumCell(filename, ChecksumComputationService.getService().getChecksum(compareFile, compareColumnRoot), compareColumnRoot));
if (file.exists()) {
publish(new ChecksumTableModel.ChecksumCell(filename, checksumComputationService.schedule(file, column), column));
}
}
@ -77,7 +80,7 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
@Override
public String getDescription() {
public String getFileFilterDescription() {
return "files, folders and sfv files";
}
@ -105,7 +108,7 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
}
protected void load(File file, File columnRoot, String prefix) {
protected void load(File file, File column, String prefix) {
if (Thread.currentThread().isInterrupted())
return;
@ -113,10 +116,10 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
// load all files in the file tree
String newPrefix = prefix + file.getName() + "/";
for (File f : file.listFiles()) {
load(f, columnRoot, newPrefix);
load(f, column, newPrefix);
}
} else if (file.isFile()) {
publish(new ChecksumTableModel.ChecksumCell(prefix + file.getName(), ChecksumComputationService.getService().getChecksum(file, columnRoot), columnRoot));
publish(new ChecksumTableModel.ChecksumCell(prefix + file.getName(), checksumComputationService.schedule(file, column), column));
}
}
}

View File

@ -18,14 +18,18 @@ class TotalProgressPanel extends Box {
private int millisToSetVisible = 200;
private JProgressBar progressBar = new JProgressBar(0, 0);
private final JProgressBar progressBar = new JProgressBar(0, 0);
private final ChecksumComputationService checksumComputationService;
public TotalProgressPanel() {
public TotalProgressPanel(ChecksumComputationService checksumComputationService) {
super(BoxLayout.Y_AXIS);
// start invisible
super.setVisible(false);
this.checksumComputationService = checksumComputationService;
// invisible by default
setVisible(false);
progressBar.setStringPainted(true);
progressBar.setBorderPainted(false);
@ -37,10 +41,11 @@ class TotalProgressPanel extends Box {
setBorder(BorderFactory.createCompoundBorder(margin, title));
add(progressBar);
ChecksumComputationService.getService().addPropertyChangeListener(executorListener);
checksumComputationService.addPropertyChangeListener(progressListener);
}
private PropertyChangeListener executorListener = new PropertyChangeListener() {
private PropertyChangeListener progressListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
@ -54,24 +59,23 @@ class TotalProgressPanel extends Box {
@Override
public void run() {
setVisible(ChecksumComputationService.getService().isActive());
setVisible(checksumComputationService.isActive());
}
});
} else {
// hide when not active
setVisible(false);
}
} else if (property == ChecksumComputationService.REMAINING_TASK_COUNT_PROPERTY) {
int taskCount = ChecksumComputationService.getService().getActiveSessionTaskCount();
int progress = taskCount - ChecksumComputationService.getService().getRemainingTaskCount();
int taskCount = checksumComputationService.getActiveSessionTaskCount();
int progress = taskCount - checksumComputationService.getRemainingTaskCount();
progressBar.setValue(progress);
progressBar.setMaximum(taskCount);
progressBar.setString(progressBar.getValue() + " / " + progressBar.getMaximum());
}
if (!ChecksumComputationService.getService().isActive()) {
setVisible(false);
}
}
};

View File

@ -5,7 +5,6 @@ package net.sourceforge.filebot.ui.panel.subtitle;
import java.util.Locale;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import net.sourceforge.filebot.resources.ResourceManager;
@ -16,7 +15,7 @@ class Language implements Comparable<Language> {
private final Locale locale;
private final String code;
private final ImageIcon icon;
private final Icon icon;
public Language(String languageName) {
@ -57,13 +56,11 @@ class Language implements Comparable<Language> {
@Override
public boolean equals(Object obj) {
if (this == obj) {
if (this == obj)
return true;
}
if (obj instanceof Language) {
if (obj instanceof Language)
return getName().equalsIgnoreCase(((Language) obj).getName());
}
return false;
}

View File

@ -26,13 +26,12 @@ public class LanguageResolver {
* @return the locale for this language or null if no locale for this language exists
*/
public synchronized Locale getLocale(String languageName) {
languageName = languageName.toLowerCase();
Locale locale = cache.get(languageName);
Locale locale = cache.get(languageName.toLowerCase());
if (locale == null) {
locale = findLocale(languageName);
cache.put(languageName, locale);
cache.put(languageName.toLowerCase(), locale);
}
return locale;
@ -59,16 +58,17 @@ public class LanguageResolver {
/**
* Find the {@link Locale} for a given language name.
*
* @param languageName lower-case language name
* @param languageName language name
* @return {@link Locale} for the given language, or null if no matching {@link Locale} is
* available
*/
private Locale findLocale(String languageName) {
for (Locale locale : Locale.getAvailableLocales()) {
if (locale.getDisplayLanguage(Locale.ENGLISH).toLowerCase().equals(languageName))
if (locale.getDisplayLanguage(Locale.ENGLISH).equalsIgnoreCase(languageName))
return locale;
}
return null;
}
}

View File

@ -16,7 +16,6 @@ import java.util.TreeMap;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import net.sourceforge.filebot.Settings;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FunctionList;
import ca.odell.glazedlists.ListSelection;
@ -31,13 +30,14 @@ public class LanguageSelectionPanel extends JPanel {
private final ListSelection<Language> selectionModel;
private final Map<String, Boolean> defaultSelection = new TreeMap<String, Boolean>(String.CASE_INSENSITIVE_ORDER);
private final Map<String, Boolean> globalSelection = Settings.getSettings().asBooleanMap(Settings.SUBTITLE_LANGUAGE);
// private final Map<String, Boolean> globalSelection = Settings.getSettings().asBooleanMap(Settings.SUBTITLE_LANGUAGE);
public LanguageSelectionPanel(EventList<SubtitlePackage> source) {
super(new FlowLayout(FlowLayout.RIGHT, 5, 1));
defaultSelection.putAll(globalSelection);
// defaultSelection.putAll(globalSelection);
EventList<Language> languageList = new FunctionList<SubtitlePackage, Language>(source, new LanguageFunction());
EventList<Language> languageSet = new UniqueList<Language>(languageList);
@ -68,7 +68,7 @@ public class LanguageSelectionPanel extends JPanel {
String key = language.getName();
defaultSelection.put(key, selected);
globalSelection.put(key, selected);
// globalSelection.put(key, selected);
if (selected)
selectionModel.select(language);

View File

@ -14,6 +14,7 @@ import javax.swing.SwingConstants;
import net.sourceforge.tuned.ui.ColorTintImageFilter;
import net.sourceforge.tuned.ui.IconViewCellRenderer;
import net.sourceforge.tuned.ui.TunedUtil;
public class SubtitleCellRenderer extends IconViewCellRenderer {
@ -52,10 +53,10 @@ public class SubtitleCellRenderer extends IconViewCellRenderer {
info1.setIcon(icon);
ImageIcon icon = subtitle.getArchiveIcon();
Icon icon = subtitle.getArchiveIcon();
if (isSelected) {
setIcon(new ImageIcon(createImage(new FilteredImageSource(icon.getImage().getSource(), new ColorTintImageFilter(list.getSelectionBackground(), 0.5f)))));
setIcon(new ImageIcon(createImage(new FilteredImageSource(TunedUtil.getImage(icon).getSource(), new ColorTintImageFilter(list.getSelectionBackground(), 0.5f)))));
info1.setForeground(list.getSelectionForeground());
info2.setForeground(list.getSelectionForeground());

View File

@ -4,6 +4,7 @@ package net.sourceforge.filebot.ui.panel.subtitle;
import java.beans.PropertyChangeEvent;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingWorker.StateValue;
@ -57,7 +58,7 @@ public class SubtitlePackage extends AbstractBean {
}
public ImageIcon getArchiveIcon() {
public Icon getArchiveIcon() {
return archiveIcon;
}
@ -68,9 +69,9 @@ public class SubtitlePackage extends AbstractBean {
}
public synchronized void download() {
public synchronized void startDownload() {
if (downloadTask != null)
throw new IllegalStateException("Download has been started already");
throw new IllegalStateException("Download has already been started");
downloadTask = subtitleDescriptor.createDownloadTask();
downloadTask.addPropertyChangeListener(new DownloadTaskPropertyChangeAdapter());

View File

@ -8,7 +8,6 @@ import java.util.Collection;
import java.util.List;
import java.util.Locale;
import net.sourceforge.filebot.ListChangeSynchronizer;
import net.sourceforge.filebot.Settings;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.AbstractSearchPanel;
@ -18,6 +17,7 @@ import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.filebot.web.SubsceneSubtitleClient;
import net.sourceforge.filebot.web.SubtitleClient;
import net.sourceforge.filebot.web.SubtitleDescriptor;
import net.sourceforge.tuned.ListChangeSynchronizer;
import net.sourceforge.tuned.ui.LabelProvider;
import net.sourceforge.tuned.ui.SimpleLabelProvider;
@ -63,12 +63,6 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
}
@Override
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
selectDialog.setText("Select a Show / Movie:");
}
@Override
protected FetchTask createFetchTask(SearchTask searchTask, SearchResult selectedSearchResult) {
return new SubtitleFetchTask(searchTask.getClient(), selectedSearchResult, searchTask.getTabPanel());
@ -93,6 +87,13 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
return getClient().search(getSearchText());
}
@Override
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) throws Exception {
super.configureSelectDialog(selectDialog);
selectDialog.setText("Select a Show / Movie:");
}
}

View File

@ -0,0 +1,40 @@
package net.sourceforge.filebot.ui.transfer;
import java.io.IOException;
import java.io.OutputStream;
public abstract class AdaptiveFileExportHandler extends FileExportHandler {
/**
* @return the <code>FileExportHandler</code> that that should be used, or
* <code>null</code> if export is not possible in the first place
*/
protected abstract FileExportHandler getExportHandler();
@Override
public boolean canExport() {
FileExportHandler handler = getExportHandler();
if (handler == null)
return false;
return handler.canExport();
}
@Override
public void export(OutputStream out) throws IOException {
getExportHandler().export(out);
}
@Override
public String getDefaultFileName() {
return getExportHandler().getDefaultFileName();
}
}

View File

@ -0,0 +1,138 @@
package net.sourceforge.filebot.ui.transfer;
import java.awt.datatransfer.Transferable;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.List;
import javax.swing.SwingWorker;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferablePolicy {
public static final String LOADING_PROPERTY = "loading";
private BackgroundWorker worker = null;
@Override
public boolean accept(Transferable tr) {
if (isActive())
return false;
return super.accept(tr);
}
@Override
public synchronized void handleTransferable(Transferable tr, TransferAction action) {
List<File> files = getFilesFromTransferable(tr);
if (action != TransferAction.ADD)
clear();
worker = new BackgroundWorker(files);
worker.addPropertyChangeListener(new BackgroundWorkerListener());
worker.execute();
}
public synchronized boolean isActive() {
return (worker != null) && !worker.isDone();
}
public synchronized void reset() {
if (isActive()) {
worker.cancel(true);
}
}
/**
* Receives data chunks from the publish method asynchronously on the Event Dispatch
* Thread.
*
* @param chunks
*/
protected abstract void process(List<V> chunks);
/**
* Sends data chunks to the process method.
*
* @param chunks
*/
protected synchronized final void publish(V... chunks) {
if (worker != null) {
worker.publishChunks(chunks);
}
}
private class BackgroundWorker extends SwingWorker<Void, V> {
private final List<File> files;
public BackgroundWorker(List<File> files) {
this.files = files;
}
@Override
protected Void doInBackground() {
load(files);
return null;
}
public void publishChunks(V... chunks) {
if (!isCancelled()) {
publish(chunks);
}
}
@Override
protected void process(List<V> chunks) {
if (!isCancelled()) {
BackgroundFileTransferablePolicy.this.process(chunks);
}
}
}
private class BackgroundWorkerListener extends SwingWorkerPropertyChangeAdapter {
@Override
public void started(PropertyChangeEvent evt) {
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, null, true);
}
@Override
public void done(PropertyChangeEvent evt) {
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, null, false);
}
}
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
}

View File

@ -0,0 +1,45 @@
package net.sourceforge.filebot.ui.transfer;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javax.swing.JList;
import javax.swing.ListModel;
public class DefaultListExportHandler extends FileExportHandler {
private final JList list;
public DefaultListExportHandler(JList list) {
this.list = list;
}
@Override
public boolean canExport() {
return list.getModel().getSize() > 0;
}
@Override
public void export(OutputStream out) throws IOException {
PrintStream printer = new PrintStream(out);
ListModel model = list.getModel();
for (int i = 0; i < model.getSize(); i++) {
printer.println(model.getElementAt(i));
}
}
@Override
public String getDefaultFileName() {
return list.getClientProperty("title") + ".txt";
}
}

View File

@ -94,16 +94,31 @@ public class DefaultTransferHandler extends TransferHandler {
}
public ImportHandler getImportHandler() {
return importHandler;
}
public void setImportHandler(ImportHandler importHandler) {
this.importHandler = importHandler;
}
public ExportHandler getExportHandler() {
return exportHandler;
}
public void setExportHandler(ExportHandler exportHandler) {
this.exportHandler = exportHandler;
}
public ClipboardHandler getClipboardHandler() {
return clipboardHandler;
}
public void setClipboardHandler(ClipboardHandler clipboardHandler) {
this.clipboardHandler = clipboardHandler;
}

View File

@ -3,8 +3,11 @@ package net.sourceforge.filebot.ui.transfer;
import java.awt.datatransfer.Transferable;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -16,19 +19,31 @@ import net.sourceforge.filebot.Settings;
import net.sourceforge.tuned.TemporaryFolder;
public class SaveableExportHandler implements ExportHandler {
public abstract class FileExportHandler implements ExportHandler {
private final Saveable saveable;
public abstract boolean canExport();
public abstract void export(OutputStream out) throws IOException;
public SaveableExportHandler(Saveable saveable) {
this.saveable = saveable;
public abstract String getDefaultFileName();
public void export(File file) throws IOException {
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
try {
export(out);
} finally {
out.close();
}
}
@Override
public int getSourceActions(JComponent c) {
if ((saveable == null) || !saveable.isSaveable())
if (!canExport())
return TransferHandler.NONE;
return TransferHandler.MOVE | TransferHandler.COPY;
@ -38,11 +53,12 @@ public class SaveableExportHandler implements ExportHandler {
@Override
public Transferable createTransferable(JComponent c) {
try {
// Remove invalid characters from default filename
String name = FileBotUtil.validateFileName(saveable.getDefaultFileName());
// remove invalid characters from file name
String name = FileBotUtil.validateFileName(getDefaultFileName());
File temporaryFile = TemporaryFolder.getFolder(Settings.ROOT).createFile(name);
saveable.save(temporaryFile);
export(temporaryFile);
return new FileTransferable(temporaryFile);
} catch (IOException e) {
@ -58,4 +74,5 @@ public class SaveableExportHandler implements ExportHandler {
public void exportDone(JComponent source, Transferable data, int action) {
}
}

View File

@ -56,7 +56,6 @@ public class FileTransferable implements Transferable {
/**
*
* @return line separated list of file uris
*/
private String getUriList() {
@ -72,7 +71,7 @@ public class FileTransferable implements Transferable {
public DataFlavor[] getTransferDataFlavors() {
return supportedFlavors;
return supportedFlavors.clone();
}

View File

@ -0,0 +1,124 @@
package net.sourceforge.filebot.ui.transfer;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
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.logging.Level;
import java.util.logging.Logger;
public abstract class FileTransferablePolicy extends TransferablePolicy {
@Override
public boolean accept(Transferable tr) {
List<File> files = getFilesFromTransferable(tr);
if (files.isEmpty())
return false;
return accept(files);
}
@SuppressWarnings("unchecked")
protected List<File> getFilesFromTransferable(Transferable tr) {
try {
if (tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
// file list flavor
return (List<File>) tr.getTransferData(DataFlavor.javaFileListFlavor);
} else if (tr.isDataFlavorSupported(FileTransferable.uriListFlavor)) {
// file uri list flavor
String transferString = (String) tr.getTransferData(FileTransferable.uriListFlavor);
String lines[] = transferString.split("\r?\n");
ArrayList<File> files = new ArrayList<File>(lines.length);
for (String line : lines) {
if (line.startsWith("#")) {
// the line is a comment (as per the RFC 2483)
continue;
}
try {
File file = new File(new URI(line));
if (!file.exists())
throw new FileNotFoundException(file.toString());
files.add(file);
} catch (Exception e) {
// URISyntaxException, IllegalArgumentException, FileNotFoundException
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid file url: " + line);
}
}
return files;
}
} catch (UnsupportedFlavorException e) {
// should not happen
throw new RuntimeException(e);
} catch (IOException e) {
// should not happen
throw new RuntimeException(e);
}
return Collections.EMPTY_LIST;
}
@Override
public void handleTransferable(Transferable tr, TransferAction action) {
List<File> files = getFilesFromTransferable(tr);
if (action != TransferAction.ADD)
clear();
load(files);
}
protected boolean accept(List<File> files) {
for (File f : files)
if (!accept(f))
return false;
return true;
}
protected void load(List<File> files) {
for (File file : files) {
load(file);
}
}
protected boolean accept(File file) {
return file.isFile() || file.isDirectory();
}
protected void clear() {
}
protected void load(File file) {
}
public String getFileFilterDescription() {
return null;
}
}

View File

@ -8,7 +8,7 @@ import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import net.sourceforge.filebot.resources.ResourceManager;
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
import net.sourceforge.filebot.ui.transfer.TransferablePolicy.TransferAction;
public class LoadAction extends AbstractAction {
@ -18,6 +18,7 @@ public class LoadAction extends AbstractAction {
public LoadAction(TransferablePolicy transferablePolicy) {
super("Load", ResourceManager.getIcon("action.load"));
this.transferablePolicy = transferablePolicy;
}
@ -35,10 +36,14 @@ public class LoadAction extends AbstractAction {
FileTransferable transferable = new FileTransferable(chooser.getSelectedFiles());
boolean add = ((e.getModifiers() & ActionEvent.CTRL_MASK) != 0);
TransferAction action = TransferAction.PUT;
// if CTRL was pressed when the button was clicked, assume ADD action (same as with dnd)
if ((e.getModifiers() & ActionEvent.CTRL_MASK) != 0)
action = TransferAction.ADD;
if (transferablePolicy.accept(transferable))
transferablePolicy.handleTransferable(transferable, add);
transferablePolicy.handleTransferable(transferable, action);
}
}

View File

@ -4,8 +4,12 @@ package net.sourceforge.filebot.ui.transfer;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import net.sourceforge.filebot.FileBotUtil;
@ -14,22 +18,32 @@ import net.sourceforge.filebot.resources.ResourceManager;
public class SaveAction extends AbstractAction {
protected Saveable saveable;
protected final FileExportHandler exportHandler;
public SaveAction(Saveable saveable) {
public SaveAction(FileExportHandler exportHandler) {
super("Save as ...", ResourceManager.getIcon("action.save"));
this.saveable = saveable;
this.exportHandler = exportHandler;
}
protected void save(File file) {
saveable.save(file);
protected SaveAction() {
this(null);
}
protected boolean canExport() {
return exportHandler.canExport();
}
protected void export(File file) throws IOException {
exportHandler.export(file);
}
protected String getDefaultFileName() {
return saveable.getDefaultFileName();
return exportHandler.getDefaultFileName();
}
@ -38,18 +52,8 @@ public class SaveAction extends AbstractAction {
}
protected boolean isSaveable() {
return saveable.isSaveable();
}
protected void setSaveable(Saveable saveable) {
this.saveable = saveable;
}
public void actionPerformed(ActionEvent e) {
if (!isSaveable())
public void actionPerformed(ActionEvent evt) {
if (!canExport())
return;
JFileChooser chooser = new JFileChooser();
@ -58,10 +62,13 @@ public class SaveAction extends AbstractAction {
chooser.setSelectedFile(new File(getDefaultFolder(), FileBotUtil.validateFileName(getDefaultFileName())));
if (chooser.showSaveDialog(null) != JFileChooser.APPROVE_OPTION)
if (chooser.showSaveDialog((JComponent) evt.getSource()) != JFileChooser.APPROVE_OPTION)
return;
save(chooser.getSelectedFile());
try {
export(chooser.getSelectedFile());
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
}

View File

@ -1,17 +0,0 @@
package net.sourceforge.filebot.ui.transfer;
import java.io.File;
public interface Saveable {
public abstract void save(File file);
public abstract boolean isSaveable();
public abstract String getDefaultFileName();
}

View File

@ -0,0 +1,47 @@
package net.sourceforge.filebot.ui.transfer;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
public abstract class StringTransferablePolicy extends TransferablePolicy {
@Override
public boolean accept(Transferable tr) {
return tr.isDataFlavorSupported(DataFlavor.stringFlavor);
}
@Override
public void handleTransferable(Transferable tr, TransferAction action) {
String string;
try {
string = (String) tr.getTransferData(DataFlavor.stringFlavor);
} catch (UnsupportedFlavorException e) {
// should no happen
throw new RuntimeException(e);
} catch (IOException e) {
// should no happen
throw new RuntimeException(e);
}
if (action != TransferAction.ADD)
clear();
load(string);
}
protected void clear() {
}
protected abstract void load(String string);
}

View File

@ -0,0 +1,96 @@
package net.sourceforge.filebot.ui.transfer;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.InvalidDnDOperationException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
public abstract class TransferablePolicy implements ImportHandler {
public abstract boolean accept(Transferable tr);
public abstract void handleTransferable(Transferable tr, TransferAction action);
public static enum TransferAction {
PUT(TransferHandler.MOVE),
ADD(TransferHandler.COPY),
LINK(TransferHandler.LINK);
private final int dndConstant;
private TransferAction(int dndConstant) {
this.dndConstant = dndConstant;
}
public int getDnDConstant() {
return dndConstant;
}
public static TransferAction fromDnDConstant(int dndConstant) {
for (TransferAction action : values()) {
if (dndConstant == action.dndConstant)
return action;
}
throw new IllegalArgumentException("Unsupported dndConstant: " + dndConstant);
}
}
@Override
public boolean canImport(TransferSupport support) {
if (support.isDrop())
support.setShowDropLocation(false);
try {
return accept(support.getTransferable());
} catch (InvalidDnDOperationException e) {
// final drop may cause this exception because, the transfer data can only be accessed
// *after* the drop has been accepted, but canImport is called before that
// just assume that the transferable will be accepted, accept will be called in importData again anyway
return true;
}
}
@Override
public boolean importData(TransferSupport support) {
Transferable transferable = support.getTransferable();
try {
if (accept(transferable)) {
handleTransferable(transferable, getTransferAction(support));
return true;
}
} catch (Exception e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.toString(), e);
}
// transferable was not accepted, or transfer failed
return false;
}
protected TransferAction getTransferAction(TransferSupport support) {
if (support.isDrop()) {
return TransferAction.fromDnDConstant(support.getDropAction());
}
// use PUT by default (e.g. clipboard transfers)
return TransferAction.PUT;
}
}

View File

@ -6,9 +6,6 @@ import java.io.File;
import javax.swing.filechooser.FileFilter;
import net.sourceforge.filebot.ui.transferablepolicies.FileTransferablePolicy;
import net.sourceforge.filebot.ui.transferablepolicies.CompositeTransferablePolicy;
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
public class TransferablePolicyFileFilter extends FileFilter {
@ -32,12 +29,10 @@ public class TransferablePolicyFileFilter extends FileFilter {
@Override
public String getDescription() {
if (transferablePolicy instanceof CompositeTransferablePolicy) {
CompositeTransferablePolicy multi = (CompositeTransferablePolicy) transferablePolicy;
return multi.getDescription(FileTransferablePolicy.class);
if (transferablePolicy instanceof FileTransferablePolicy) {
return ((FileTransferablePolicy) transferablePolicy).getFileFilterDescription();
}
return transferablePolicy.getDescription();
return null;
}
}

View File

@ -1,63 +0,0 @@
package net.sourceforge.filebot.ui.transfer;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.InvalidDnDOperationException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
public class TransferablePolicyImportHandler implements ImportHandler {
private final TransferablePolicy transferablePolicy;
public TransferablePolicyImportHandler(TransferablePolicy transferablePolicy) {
this.transferablePolicy = transferablePolicy;
}
private boolean canImportCache = false;
@Override
public boolean canImport(TransferSupport support) {
if (support.isDrop())
support.setShowDropLocation(false);
Transferable t = support.getTransferable();
try {
canImportCache = transferablePolicy.accept(t);
} catch (InvalidDnDOperationException e) {
// for some reason the last transferable has no drop current
}
return canImportCache;
}
@Override
public boolean importData(TransferSupport support) {
boolean add = false;
if (support.isDrop() && (support.getDropAction() == TransferHandler.COPY))
add = true;
Transferable t = support.getTransferable();
try {
transferablePolicy.handleTransferable(t, add);
} catch (Exception e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
return true;
}
}

View File

@ -11,7 +11,6 @@ import java.net.URLEncoder;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
@ -29,9 +28,7 @@ import org.xml.sax.SAXException;
public class AnidbClient implements EpisodeListClient {
private final SearchResultCache searchResultCache = new SearchResultCache();
private final String host = "anidb.net";
private static final String host = "anidb.net";
@Override
@ -48,9 +45,6 @@ public class AnidbClient implements EpisodeListClient {
@Override
public List<SearchResult> search(String searchterm) throws IOException, SAXException {
if (searchResultCache.containsKey(searchterm)) {
return Collections.singletonList(searchResultCache.get(searchterm));
}
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
@ -91,8 +85,6 @@ public class AnidbClient implements EpisodeListClient {
}
}
searchResultCache.addAll(searchResults);
return searchResults;
}
@ -122,7 +114,7 @@ public class AnidbClient implements EpisodeListClient {
number = numberFormat.format(Integer.parseInt(number));
// no seasons for anime
episodes.add(new Episode(searchResult.getName(), null, number, title));
episodes.add(new Episode(searchResult.getName(), number, title));
} catch (NumberFormatException ex) {
// ignore node, episode is probably some kind of special (S1, S2, ...)
}

View File

@ -4,10 +4,10 @@ package net.sourceforge.filebot.web;
public class Episode {
private String showName;
private String numberOfSeason;
private String numberOfEpisode;
private String title;
private final String showName;
private final String numberOfSeason;
private final String numberOfEpisode;
private final String title;
public Episode(String showname, String numberOfSeason, String numberOfEpisode, String title) {

View File

@ -11,6 +11,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@ -60,8 +61,8 @@ public class HtmlUtil {
public static Document getHtmlDocument(URL url, Map<String, String> requestHeaders) throws IOException, SAXException {
URLConnection connection = url.openConnection();
for (String key : requestHeaders.keySet()) {
connection.addRequestProperty(key, requestHeaders.get(key));
for (Entry<String, String> entry : requestHeaders.entrySet()) {
connection.addRequestProperty(entry.getKey(), entry.getValue());
}
return getHtmlDocument(connection);

View File

@ -9,8 +9,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -21,23 +19,10 @@ import redstone.xmlrpc.XmlRpcFault;
/**
* Client for the OpenSubtitles XML-RPC API.
*
*/
public class OpenSubtitlesClient {
/**
* <table>
* <tr>
* <td>Main server:</td>
* <td>http://www.opensubtitles.org/xml-rpc</td>
* </tr>
* <tr>
* <td>Developing tests:</td>
* <td>http://dev.opensubtitles.org/xml-rpc</td>
* </tr>
* </table>
*/
private final String url = "http://www.opensubtitles.org/xml-rpc";
private static final String url = "http://www.opensubtitles.org/xml-rpc";
private final String useragent;
@ -60,7 +45,7 @@ public class OpenSubtitlesClient {
/**
* Login as user and use english as language
* Login as user and use English as language
*
* @param username
* @param password
@ -77,8 +62,9 @@ public class OpenSubtitlesClient {
*
* @param username username (blank for anonymous user)
* @param password password (blank for anonymous user)
* @param language <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes">ISO639</a>
* 2 letter codes as language and later communication will be done in this
* @param language <a
* href="http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes">ISO639</a>
* 2-letter codes as language and later communication will be done in this
* language if applicable (error codes and so on).
*/
@SuppressWarnings("unchecked")
@ -116,7 +102,7 @@ public class OpenSubtitlesClient {
/**
* Check status whether it is OK or not
* Check whether status is OK or not
*
* @param status status code and message (e.g. 200 OK, 401 Unauthorized, ...)
* @throws XmlRpcFault thrown if status code is not OK
@ -139,9 +125,7 @@ public class OpenSubtitlesClient {
XmlRpcClient rpc = new XmlRpcClient(url, false);
return rpc.invoke(method, arguments);
} catch (MalformedURLException e) {
// will never happen
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, "Invalid xml-rpc url: " + url, e);
return null;
throw new RuntimeException(e);
}
}
@ -169,14 +153,15 @@ public class OpenSubtitlesClient {
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, searchList);
ArrayList<OpenSubtitlesSubtitleDescriptor> subs = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
List<OpenSubtitlesSubtitleDescriptor> subs = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
if (!(response.get("data") instanceof List))
throw new XmlRpcException("Illegal response: " + response.toString());
// if there was an error data may not be a list
for (Map<String, String> subtitle : response.get("data")) {
subs.add(new OpenSubtitlesSubtitleDescriptor(subtitle));
try {
for (Map<String, String> subtitle : response.get("data")) {
subs.add(new OpenSubtitlesSubtitleDescriptor(subtitle));
}
} catch (ClassCastException e) {
// if the response is an error message, generic types won't match
throw new XmlRpcException("Illegal response: " + response.toString(), e);
}
return subs;

View File

@ -31,12 +31,14 @@ public class OpenSubtitlesHasher {
FileChannel fileChannel = new FileInputStream(file).getChannel();
long head = computeHashForChunk(fileChannel, 0, chunkSizeForFile);
long tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile);
fileChannel.close();
return String.format("%016x", size + head + tail);
try {
long head = computeHashForChunk(fileChannel, 0, chunkSizeForFile);
long tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile);
return String.format("%016x", size + head + tail);
} finally {
fileChannel.close();
}
}

View File

@ -19,7 +19,6 @@ import net.sourceforge.filebot.resources.ResourceManager;
/**
* {@link SubtitleClient} for OpenSubtitles.
*
*/
public class OpenSubtitlesSubtitleClient implements SubtitleClient {
@ -102,7 +101,7 @@ public class OpenSubtitlesSubtitleClient implements SubtitleClient {
private class LogoutTimer {
private final long LOGOUT_DELAY = 12 * 60 * 1000; // 12 minutes
private static final long LOGOUT_DELAY = 12 * 60 * 1000; // 12 minutes
private Timer daemon = null;
private LogoutTimerTask currentTimerTask = null;

View File

@ -4,6 +4,7 @@ package net.sourceforge.filebot.web;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -58,7 +59,7 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor {
public OpenSubtitlesSubtitleDescriptor(Map<String, String> properties) {
this.properties = properties;
this.properties = new HashMap<String, String>(properties);
}

View File

@ -1,39 +0,0 @@
package net.sourceforge.filebot.web;
import java.util.concurrent.ConcurrentHashMap;
public class SearchResultCache {
private final ConcurrentHashMap<String, SearchResult> cache = new ConcurrentHashMap<String, SearchResult>();
public boolean containsKey(String name) {
return cache.containsKey(key(name));
}
public SearchResult get(String name) {
return cache.get(key(name));
}
public void add(SearchResult searchResult) {
cache.putIfAbsent(key(searchResult.getName()), searchResult);
}
public void addAll(Iterable<SearchResult> searchResults) {
for (SearchResult searchResult : searchResults) {
add(searchResult);
}
}
private String key(String name) {
return name.toLowerCase();
}
}

View File

@ -9,7 +9,6 @@ import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@ -34,12 +33,10 @@ import org.xml.sax.SAXException;
public class SubsceneSubtitleClient implements SubtitleClient {
private final SearchResultCache searchResultCache = new SearchResultCache();
private static final String host = "subscene.com";
private final Map<String, Integer> languageFilterMap = new ConcurrentHashMap<String, Integer>(50);
private final String host = "subscene.com";
@Override
public String getName() {
@ -55,9 +52,6 @@ public class SubsceneSubtitleClient implements SubtitleClient {
@Override
public List<SearchResult> search(String searchterm) throws IOException, SAXException {
if (searchResultCache.containsKey(searchterm)) {
return Collections.singletonList(searchResultCache.get(searchterm));
}
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
@ -103,8 +97,6 @@ public class SubsceneSubtitleClient implements SubtitleClient {
}
}
searchResultCache.addAll(searchResults);
return searchResults;
}
@ -224,7 +216,7 @@ public class SubsceneSubtitleClient implements SubtitleClient {
Matcher matcher = hrefPattern.matcher(href);
if (!matcher.matches())
throw new IllegalArgumentException("Cannot extract download parameters: " + href);
throw new IllegalArgumentException("Cannot parse download parameters: " + href);
String subtitleId = matcher.group(1);
String typeId = matcher.group(2);

View File

@ -10,7 +10,6 @@ import java.net.URL;
import java.net.URLEncoder;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Callable;
@ -33,9 +32,7 @@ import org.xml.sax.SAXException;
public class TVDotComClient implements EpisodeListClient {
private final SearchResultCache searchResultCache = new SearchResultCache();
private final String host = "www.tv.com";
private static final String host = "www.tv.com";
@Override
@ -58,9 +55,6 @@ public class TVDotComClient implements EpisodeListClient {
@Override
public List<SearchResult> search(String searchterm) throws IOException, SAXException {
if (searchResultCache.containsKey(searchterm)) {
return Collections.singletonList(searchResultCache.get(searchterm));
}
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
@ -81,8 +75,6 @@ public class TVDotComClient implements EpisodeListClient {
}
}
searchResultCache.addAll(searchResults);
return searchResults;
}
@ -99,7 +91,7 @@ public class TVDotComClient implements EpisodeListClient {
List<Future<List<Episode>>> futures = new ArrayList<Future<List<Episode>>>(seasonCount);
if (seasonCount > 1) {
// max. 12 threads so we don't get too many concurrent downloads
// max. 12 threads so we don't get too many concurrent connections
ExecutorService executor = Executors.newFixedThreadPool(Math.min(seasonCount - 1, 12));
// we already have the document for season 1, start with season 2
@ -111,7 +103,7 @@ public class TVDotComClient implements EpisodeListClient {
executor.shutdown();
}
List<Episode> episodes = new ArrayList<Episode>(150);
List<Episode> episodes = new ArrayList<Episode>(25 * seasonCount);
// get episode list from season 1 document
episodes.addAll(getEpisodeList(searchResult, 1, dom));

View File

@ -6,7 +6,6 @@ import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
@ -23,9 +22,7 @@ import org.xml.sax.SAXException;
public class TVRageClient implements EpisodeListClient {
private final SearchResultCache searchResultCache = new SearchResultCache();
private final String host = "www.tvrage.com";
private static final String host = "www.tvrage.com";
@Override
@ -48,9 +45,6 @@ public class TVRageClient implements EpisodeListClient {
@Override
public List<SearchResult> search(String searchterm) throws SAXException, IOException, ParserConfigurationException {
if (searchResultCache.containsKey(searchterm)) {
return Collections.singletonList(searchResultCache.get(searchterm));
}
String searchUri = String.format("http://" + host + "/feeds/search.php?show=" + URLEncoder.encode(searchterm, "UTF-8"));
@ -68,8 +62,6 @@ public class TVRageClient implements EpisodeListClient {
searchResults.add(new TVRageSearchResult(name, showid, link));
}
searchResultCache.addAll(searchResults);
return searchResults;
}

View File

@ -16,6 +16,7 @@ import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -35,7 +36,7 @@ public class DownloadTask extends SwingWorker<ByteBuffer, Void> {
DONE
}
private final int BUFFER_SIZE = 4 * 1024;
private static final int BUFFER_SIZE = 4 * 1024;
private URL url;
private ByteBuffer postdata;
@ -175,15 +176,15 @@ public class DownloadTask extends SwingWorker<ByteBuffer, Void> {
int i = 0;
for (String key : parameters.keySet()) {
for (Entry<String, String> entry : parameters.entrySet()) {
if (i > 0)
sb.append("&");
sb.append(key);
sb.append(entry.getKey());
sb.append("=");
try {
sb.append(URLEncoder.encode(parameters.get(key), "UTF-8"));
sb.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
// will never happen
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);

View File

@ -16,9 +16,9 @@ public class FileUtil {
public static String formatSize(long size) {
if (size >= MEGA)
return String.format("%d MB", (double) size / MEGA);
return String.format("%d MB", size / MEGA);
else if (size >= KILO)
return String.format("%d KB", (double) size / KILO);
return String.format("%d KB", size / KILO);
else
return String.format("%d Byte", size);
}

View File

@ -1,5 +1,5 @@
package net.sourceforge.filebot;
package net.sourceforge.tuned;
import java.util.List;
@ -9,7 +9,6 @@ import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
//TODO: testcase, class doc
public class ListChangeSynchronizer<E> implements ListEventListener<E> {
private final List<E> target;

View File

@ -19,19 +19,7 @@ public class MessageBus {
return instance;
}
private final Map<String, List<MessageHandler>> handlers = new HashMap<String, List<MessageHandler>>() {
@Override
public List<MessageHandler> get(Object key) {
return super.get(key.toString().toLowerCase());
}
@Override
public List<MessageHandler> put(String key, List<MessageHandler> value) {
return super.put(key.toLowerCase(), value);
}
};
private final Map<String, List<MessageHandler>> handlers = new HashMap<String, List<MessageHandler>>();
private MessageBus() {
@ -40,11 +28,12 @@ public class MessageBus {
public synchronized void addMessageHandler(String topic, MessageHandler handler) {
List<MessageHandler> list = handlers.get(topic);
List<MessageHandler> list = handlers.get(topic.toLowerCase());
if (list == null) {
list = new ArrayList<MessageHandler>(3);
handlers.put(topic, list);
handlers.put(topic.toLowerCase(), list);
}
list.add(handler);
@ -52,7 +41,7 @@ public class MessageBus {
public synchronized void removeMessageHandler(String topic, MessageHandler handler) {
List<MessageHandler> list = handlers.get(topic);
List<MessageHandler> list = handlers.get(topic.toLowerCase());
if (list != null) {
list.remove(handler);
@ -61,7 +50,7 @@ public class MessageBus {
public synchronized MessageHandler[] getHandlers(String topic) {
List<MessageHandler> list = handlers.get(topic);
List<MessageHandler> list = handlers.get(topic.toLowerCase());
if (list == null)
return new MessageHandler[0];
@ -70,17 +59,15 @@ public class MessageBus {
}
public void publish(final String topic, final String... messages) {
public void publish(final String topic, final Object... messages) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
for (MessageHandler handler : getHandlers(topic)) {
handler.handle(topic, messages);
for (MessageHandler handler : getHandlers(topic.toLowerCase())) {
handler.handle(topic.toLowerCase(), messages);
}
}
});
}
}

View File

@ -2,10 +2,8 @@
package net.sourceforge.tuned;
public interface MessageHandler {
public void handle(String topic, String... messages);
public void handle(String topic, Object... messages);
}

View File

@ -44,10 +44,14 @@ public class PreferencesList<T> extends AbstractList<T> {
}
//TODO: assert invalid index
@Override
public void add(int index, T element) {
copy(index, index + 1, size() - index);
int size = size();
if (index > size)
throw new IndexOutOfBoundsException(String.format("Index: %d, Size: %d", index, size));
copy(index, index + 1, size - index);
setImpl(index, element);
}
@ -99,12 +103,6 @@ public class PreferencesList<T> extends AbstractList<T> {
}
public void set(List<T> data) {
clear();
addAll(data);
}
public static <T> PreferencesList<T> map(Preferences prefs, Class<T> type) {
return new PreferencesList<T>(PreferencesMap.map(prefs, type));
}
@ -113,4 +111,5 @@ public class PreferencesList<T> extends AbstractList<T> {
public static <T> PreferencesList<T> map(Preferences prefs, Adapter<T> adapter) {
return new PreferencesList<T>(PreferencesMap.map(prefs, adapter));
}
}

View File

@ -81,12 +81,6 @@ public class PreferencesMap<T> implements Map<String, T> {
}
public void set(Map<String, T> data) {
clear();
putAll(data);
}
@Override
public boolean containsKey(Object key) {
if (key instanceof String) {
@ -134,8 +128,8 @@ public class PreferencesMap<T> implements Map<String, T> {
@Override
public void putAll(Map<? extends String, ? extends T> map) {
for (String key : map.keySet()) {
put(key, map.get(key));
for (Map.Entry<? extends String, ? extends T> entry : map.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}

View File

@ -19,11 +19,11 @@ public class TemporaryFolder {
public static TemporaryFolder getFolder(String name) {
synchronized (folders) {
TemporaryFolder folder = folders.get(name);
TemporaryFolder folder = folders.get(name.toLowerCase());
if (folder == null) {
folder = new TemporaryFolder(new File(tmpdir, name));
folders.put(name, folder);
folders.put(name.toLowerCase(), folder);
}
return folder;
@ -63,14 +63,12 @@ public class TemporaryFolder {
* @throws IOException if an I/O error occurred
*/
public File createFile(String name) throws IOException {
if (!root.exists()) {
root.mkdir();
}
File file = new File(root, name);
File file = new File(getFolder(), name);
file.createNewFile();
return file;
}
@ -87,16 +85,12 @@ public class TemporaryFolder {
* @see File#createTempFile(String, String)
*/
public File createFile(String prefix, String suffix) throws IOException {
if (!root.exists()) {
root.mkdir();
}
return File.createTempFile(prefix, suffix, root);
return File.createTempFile(prefix, suffix, getFolder());
}
public boolean deleteFile(String name) {
return new File(root, name).delete();
return new File(getFolder(), name).delete();
}
@ -106,20 +100,15 @@ public class TemporaryFolder {
* @return the {@link File} object for this {@link TemporaryFolder}
*/
public File getFolder() {
if (!root.exists())
root.mkdirs();
return root;
}
public TemporaryFolder createFolder(String name) {
if (!root.exists()) {
root.mkdir();
}
TemporaryFolder folder = new TemporaryFolder(new File(root, name));
folder.root.mkdir();
return folder;
return new TemporaryFolder(new File(getFolder(), name));
}

View File

@ -0,0 +1,45 @@
package net.sourceforge.tuned.ui;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.ListModel;
import javax.swing.event.ListDataListener;
public class ArrayListModel implements ListModel {
private final ArrayList<Object> data;
public ArrayListModel(Collection<? extends Object> data) {
this.data = new ArrayList<Object>(data);
}
@Override
public Object getElementAt(int index) {
return data.get(index);
}
@Override
public int getSize() {
return data.size();
}
@Override
public void addListDataListener(ListDataListener l) {
// ignore, model is unmodifiable
}
@Override
public void removeListDataListener(ListDataListener l) {
// ignore, model is unmodifiable
}
}

View File

@ -1,85 +0,0 @@
package net.sourceforge.tuned.ui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.geom.Point2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.border.Border;
public class FancyBorder implements Border {
private int borderWidth;
private float[] dist;
private Color[] colors;
private float radius;
public FancyBorder(int width, Color... colors) {
this.borderWidth = width;
this.dist = new float[colors.length];
for (int i = 0; i < colors.length; i++) {
this.dist[i] = (1.0f / colors.length) * i;
}
this.colors = colors;
this.radius = 100;
}
public FancyBorder(int width, float[] dist, Color[] colors, float radius) {
this.borderWidth = width;
this.dist = dist;
this.colors = colors;
this.radius = radius;
}
@Override
public Insets getBorderInsets(Component c) {
int horizontalOffset = 8;
return new Insets(borderWidth, borderWidth + horizontalOffset, borderWidth, borderWidth + horizontalOffset);
}
@Override
public boolean isBorderOpaque() {
return false;
}
@Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2d = (Graphics2D) g;
float arch = Math.min(width, height) / 2;
Shape shape = new RoundRectangle2D.Float(x + borderWidth, y + borderWidth, width - borderWidth * 2, height - borderWidth * 2, arch, arch);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Point2D center = new Point2D.Float(width, 0);
g2d.setPaint(new RadialGradientPaint(center, radius, dist, colors, CycleMethod.REFLECT));
g2d.setStroke(new BasicStroke(borderWidth));
g2d.draw(shape);
}
}

View File

@ -13,10 +13,12 @@ import java.awt.Insets;
import java.awt.Paint;
import java.awt.geom.Rectangle2D;
import javax.swing.DefaultListModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListModel;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
@ -24,8 +26,10 @@ import javax.swing.border.EmptyBorder;
public class IconViewPanel extends JPanel {
private final JList list = new JList(new SimpleListModel());
private final JLabel title = new JLabel();
private final JList list = new JList(createModel());
private final JLabel titleLabel = new JLabel();
private final JPanel headerPanel = new JPanel(new BorderLayout());
@ -35,10 +39,10 @@ public class IconViewPanel extends JPanel {
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
title.setFont(title.getFont().deriveFont(Font.BOLD));
titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD));
headerPanel.setOpaque(false);
headerPanel.add(title, BorderLayout.WEST);
headerPanel.add(titleLabel, BorderLayout.WEST);
setBackground(list.getBackground());
@ -53,13 +57,23 @@ public class IconViewPanel extends JPanel {
}
protected ListModel createModel() {
return new DefaultListModel();
}
public JPanel getHeaderPanel() {
return headerPanel;
}
public void setTitle(String text) {
title.setText(text);
titleLabel.setText(text);
}
public String getTitle() {
return titleLabel.getText();
}

View File

@ -93,7 +93,7 @@ public class ProgressDialog extends JDialog {
setLocation(TunedUtil.getPreferredLocation(this));
// Shortcut Escape
TunedUtil.registerActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
TunedUtil.putActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
}

View File

@ -17,25 +17,25 @@ import javax.swing.KeyStroke;
import javax.swing.border.Border;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.text.JTextComponent;
import net.sourceforge.filebot.resources.ResourceManager;
public class SelectButtonTextField<T> extends JPanel {
private SelectButton<T> selectButton;
private SelectButton<T> selectButton = new SelectButton<T>();
private ComboBoxTextField editor = new ComboBoxTextField();
private Color borderColor = new Color(0xA4A4A4);
public SelectButtonTextField() {
setLayout(new BorderLayout(0, 0));
selectButton = new SelectButton<T>();
selectButton.addActionListener(textFieldFocusOnClick);
Color borderColor = new Color(0xA4A4A4);
Border lineBorder = BorderFactory.createLineBorder(borderColor, 1);
Border matteBorder = BorderFactory.createMatteBorder(1, 0, 1, 1, borderColor);
Border emptyBorder = BorderFactory.createEmptyBorder(0, 3, 0, 3);
@ -48,8 +48,8 @@ public class SelectButtonTextField<T> extends JPanel {
setPreferredSize(new Dimension(280, 22));
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinClientAction(1));
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinClientAction(1));
}
@ -57,7 +57,7 @@ public class SelectButtonTextField<T> extends JPanel {
* Convenience method for <code>getEditor().getSelectedItem().toString()</code>
*/
public String getText() {
return getEditor().getSelectedItem().toString();
return editor.getText();
}
@ -116,6 +116,28 @@ public class SelectButtonTextField<T> extends JPanel {
// don't reset the UI delegate if laf is changed, or we use our custom ui
}
public String getText() {
return ((TextFieldComboBoxUI) getUI()).getEditor().getText();
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// super.actionPerformed(e);
// Object newItem = getEditor().getItem();
// setPopupVisible(false);
// getModel().setSelectedItem(newItem);
// String oldCommand = getActionCommand();
// setActionCommand("comboBoxEdited");
//
//TODO sysout
System.out.println("combobox: " + e);
// for (ActionListener actionListener : getActionListeners()) {
// actionListener.actionPerformed(e);
// }
}
}
@ -123,14 +145,48 @@ public class SelectButtonTextField<T> extends JPanel {
@Override
protected JButton createArrowButton() {
JButton b = new JButton(ResourceManager.getIcon("action.list"));
b.setContentAreaFilled(false);
b.setFocusable(false);
return b;
return new JButton(ResourceManager.getIcon("action.list"));
}
@Override
public void configureArrowButton() {
super.configureArrowButton();
arrowButton.setContentAreaFilled(false);
arrowButton.setFocusable(false);
}
@Override
protected void configureEditor() {
editor.setEnabled(comboBox.isEnabled());
editor.setFocusable(comboBox.isFocusable());
editor.setFont(comboBox.getFont());
editor.addFocusListener(createFocusListener());
}
public JTextComponent getEditor() {
return (JTextComponent) editor;
}
// @Override
// protected FocusListener createFocusListener() {
// return new FocusHandler() {
//
// /**
// * Prevent action events from being fired on focusLost.
// */
// @Override
// public void focusLost(FocusEvent e) {
// if (isPopupVisible(comboBox))
// setPopupVisible(comboBox, false);
// }
// };
// }
}
}

View File

@ -3,6 +3,7 @@ package net.sourceforge.tuned.ui;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.swing.Icon;
@ -15,11 +16,11 @@ import net.sourceforge.tuned.ExceptionUtil;
public class SimpleLabelProvider<T> implements LabelProvider<T> {
private final Method getIconMethod;
private final Method getNameMethod;
private final Method getTextMethod;
/**
* Same as <code>new SimpleLabelProvider&lt;T&gt;(T.class)</code>.
* Factory method for {@link #SimpleLabelProvider(Class)}.
*
* @return new <code>LabelProvider</code>
*/
@ -29,13 +30,15 @@ public class SimpleLabelProvider<T> implements LabelProvider<T> {
/**
* Create a new LabelProvider which will use the <code>getName</code> and
* <code>getIcon</code> method of the given class.
* Create a new LabelProvider which will use the <code>getText</code>, <code>getName</code>
* or <code>toString</code> method for text and the <code>getIcon</code> method for the
* icon.
*
* @param type a class that has a <code>getName</code> and a <code>getIcon</code> method
* @param type a class that has one of the text methods and the icon method
*/
public SimpleLabelProvider(Class<T> type) {
this(type, "getName", "getIcon");
getTextMethod = findAnyMethod(type, "getText", "getName", "toString");
getIconMethod = findAnyMethod(type, "getIcon");
}
@ -43,23 +46,32 @@ public class SimpleLabelProvider<T> implements LabelProvider<T> {
* Create a new LabelProvider which will use a specified method of a given class
*
* @param type a class with the specified method
* @param getName a method name such as <code>getName</code>
* @param getText a method name such as <code>getText</code>
* @param getIcon a method name such as <code>getIcon</code>
*/
public SimpleLabelProvider(Class<T> type, String getName, String getIcon) {
try {
getNameMethod = type.getMethod(getName);
getIconMethod = type.getMethod(getIcon);
} catch (Exception e) {
throw new RuntimeException(e);
public SimpleLabelProvider(Class<T> type, String getText, String getIcon) {
getTextMethod = findAnyMethod(type, getText);
getIconMethod = findAnyMethod(type, getIcon);
}
private Method findAnyMethod(Class<T> type, String... names) {
for (String name : names) {
try {
return type.getMethod(name);
} catch (NoSuchMethodException e) {
// try next method name
}
}
throw new IllegalArgumentException("Method not found: " + Arrays.toString(names));
}
@Override
public String getText(T value) {
try {
return (String) getNameMethod.invoke(value);
return (String) getTextMethod.invoke(value);
} catch (Exception e) {
throw ExceptionUtil.asRuntimeException(e);
}

View File

@ -1,139 +0,0 @@
package net.sourceforge.tuned.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.swing.AbstractListModel;
public class SimpleListModel extends AbstractListModel {
private final List<Object> list;
public SimpleListModel() {
list = Collections.synchronizedList(new ArrayList<Object>());
}
public SimpleListModel(Collection<? extends Object> collection) {
list = Collections.synchronizedList(new ArrayList<Object>(collection));
}
@Override
public Object getElementAt(int index) {
return list.get(index);
}
@Override
public int getSize() {
return list.size();
}
public void add(Object object) {
int index = list.size();
list.add(object);
fireIntervalAdded(this, index, index);
}
public void add(int index, Object object) {
list.add(index, object);
fireIntervalAdded(this, index, index);
}
public void addAll(Collection<? extends Object> c) {
int begin = list.size();
list.addAll(c);
int end = list.size() - 1;
if (end >= 0) {
fireIntervalAdded(this, begin, end);
}
}
public void clear() {
int end = list.size() - 1;
list.clear();
if (end >= 0) {
fireIntervalRemoved(this, 0, end);
}
}
public boolean contains(Object o) {
return list.contains(o);
}
public int indexOf(Object o) {
return list.indexOf(o);
}
public boolean isEmpty() {
return list.isEmpty();
}
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
public Object remove(int index) {
Object object = list.remove(index);
fireIntervalRemoved(this, index, index);
return object;
}
public void remove(Object object) {
synchronized (list) {
remove(indexOf(object));
}
}
public void sort() {
synchronized (list) {
Collections.sort(list, null);
}
fireContentsChanged(this, 0, list.size() - 1);
}
public List<? extends Object> getCopy() {
synchronized (list) {
return new ArrayList<Object>(list);
}
}
public void set(Collection<? extends Object> c) {
int end = Math.max(list.size(), c.size()) - 1;
synchronized (list) {
list.clear();
list.addAll(c);
}
if (end >= 0) {
fireContentsChanged(this, 0, end);
}
}
}

View File

@ -1,129 +0,0 @@
package net.sourceforge.tuned.ui;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;
public class TextCompletion {
private Set<String> completionTerms = Collections.synchronizedSet(new TreeSet<String>(String.CASE_INSENSITIVE_ORDER));
private int completionStartLength = 1;
private JTextComponent component;
public TextCompletion(JTextComponent component) {
this.component = component;
}
public void hook() {
component.getDocument().addDocumentListener(documentListener);
}
public void unhook() {
component.getDocument().removeDocumentListener(documentListener);
}
public void addTerm(String term) {
completionTerms.add(term);
}
public void addTerms(Collection<String> terms) {
completionTerms.addAll(terms);
}
public void removeTerm(String term) {
completionTerms.remove(term);
}
public void removeTerms(Collection<String> terms) {
completionTerms.removeAll(terms);
}
public void setStartLength(int codeCompletionStartLength) {
this.completionStartLength = codeCompletionStartLength;
}
public Set<String> getTerms() {
return completionTerms;
}
public int getStartLength() {
return completionStartLength;
}
private void complete() {
String text = component.getText();
if (text.length() < completionStartLength)
return;
String completionTerm = findCompletionTerm(text);
if (completionTerm == null)
return;
component.setText(completionTerm);
component.select(text.length(), completionTerm.length());
}
private String findCompletionTerm(String text) {
for (String completionTerm : completionTerms) {
if (text.length() >= completionTerm.length())
continue;
String compareTerm = completionTerm.substring(0, text.length());
if (text.equalsIgnoreCase(compareTerm))
return completionTerm;
}
return null;
}
private final DocumentListener documentListener = new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
}
public void insertUpdate(DocumentEvent e) {
SwingUtilities.invokeLater(doComplete);
}
public void removeUpdate(DocumentEvent e) {
}
};
private final Runnable doComplete = new Runnable() {
public void run() {
complete();
}
};
}

View File

@ -13,20 +13,24 @@ import java.awt.image.BufferedImage;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TunedUtil {
private TunedUtil() {
// hide constructor
public static void checkEventDispatchThread() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Method must be accessed from the Swing Event Dispatch Thread, but was called on Thread \"" + Thread.currentThread().getName() + "\"");
}
}
public static void registerActionForKeystroke(JComponent component, KeyStroke keystroke, Action action) {
public static void putActionForKeystroke(JComponent component, KeyStroke keystroke, Action action) {
Integer key = action.hashCode();
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key);
component.getActionMap().put(key, action);
@ -47,10 +51,9 @@ public class TunedUtil {
public static Image getImage(Icon icon) {
//TODO uncomment
// if (icon instanceof ImageIcon) {
// return ((ImageIcon) icon).getImage();
// }
if (icon instanceof ImageIcon) {
return ((ImageIcon) icon).getImage();
}
BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
@ -78,4 +81,9 @@ public class TunedUtil {
return timer;
}
private TunedUtil() {
// hide constructor
}
}

View File

@ -5,17 +5,12 @@ package net.sourceforge.tuned.ui.notification;
import javax.swing.SwingConstants;
class Factor extends Object implements SwingConstants {
class Factor implements SwingConstants {
public double fx = 0;
public double fy = 0;
public final double fx;
public final double fy;
public Factor() {
}
public Factor(double fx, double fy) {
this.fx = fx;
this.fy = fy;

View File

@ -6,13 +6,15 @@
package net.sourceforge.tuned.ui.notification;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import net.sourceforge.tuned.ui.TunedUtil;
public class NotificationManager {
private NotificationLayout layout;
private final NotificationLayout layout;
public NotificationManager() {
@ -21,45 +23,25 @@ public class NotificationManager {
public NotificationManager(NotificationLayout layout) {
setLayoutManager(layout);
}
public void setLayoutManager(NotificationLayout layout) {
this.layout = layout;
}
public NotificationLayout getLayoutManager() {
return layout;
}
public void show(NotificationWindow notification) {
if (layout == null)
return;
TunedUtil.checkEventDispatchThread();
notification.addComponentListener(new RemoveListener(layout));
notification.addWindowListener(new RemoveListener());
layout.add(notification);
notification.setVisible(true);
}
private static class RemoveListener extends ComponentAdapter {
private class RemoveListener extends WindowAdapter {
private NotificationLayout layout;
public RemoveListener(NotificationLayout layout) {
this.layout = layout;
}
@Override
public void componentHidden(ComponentEvent e) {
NotificationWindow window = (NotificationWindow) e.getSource();
layout.remove(window);
window.dispose();
public void windowClosing(WindowEvent e) {
layout.remove((NotificationWindow) e.getWindow());
}
}

View File

@ -12,6 +12,7 @@ import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import javax.swing.JWindow;
import javax.swing.Timer;
@ -21,7 +22,7 @@ import net.sourceforge.tuned.ui.TunedUtil;
public class NotificationWindow extends JWindow {
private int timeout;
private final int timeout;
public NotificationWindow(Window owner, int timeout) {
@ -35,17 +36,12 @@ public class NotificationWindow extends JWindow {
setAlwaysOnTop(true);
if (closeOnClick)
if (closeOnClick) {
getGlassPane().addMouseListener(clickListener);
getGlassPane().setVisible(true);
}
getGlassPane().setVisible(true);
addComponentListener(visibleListener);
}
public NotificationWindow(int timeout) {
this((Window) null, timeout);
addComponentListener(closeOnTimeout);
}
@ -54,21 +50,23 @@ public class NotificationWindow extends JWindow {
}
public NotificationWindow() {
this((Window) null, -1);
}
public final void close() {
TunedUtil.checkEventDispatchThread();
// window events are not fired automatically, required for layout updates
processWindowEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
setVisible(false);
// component hidden is not fired automatically
// component events are not fired automatically, used to cancel timeout timer
processComponentEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_HIDDEN));
dispose();
}
private ComponentListener visibleListener = new ComponentAdapter() {
private final ComponentListener closeOnTimeout = new ComponentAdapter() {
private Timer timer;
private Timer timer = null;
@Override
@ -94,7 +92,7 @@ public class NotificationWindow extends JWindow {
};
private MouseAdapter clickListener = new MouseAdapter() {
private final MouseAdapter clickListener = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {

View File

@ -29,7 +29,10 @@ public class SeparatorBorder extends AbstractBorder {
public static enum Position {
TOP, BOTTOM, LEFT, RIGHT;
TOP,
BOTTOM,
LEFT,
RIGHT;
public Rectangle2D getRectangle(RectangularShape shape, int borderWidth) {
switch (this) {
@ -72,10 +75,6 @@ public class SeparatorBorder extends AbstractBorder {
}
protected SeparatorBorder() {
}
public SeparatorBorder(int height, Color color, Position position) {
this(height, color, null, null, position);
}

View File

@ -0,0 +1,62 @@
package net.sourceforge.filebot;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
public class ArgumentBeanTest {
@Test
public void clear() throws Exception {
ArgumentBean bean = parse("--sfv", "One Piece", "-clear");
assertTrue(bean.isClear());
assertFalse(bean.isHelp());
assertEquals("One Piece", bean.getSfvPanelFile().getName());
}
@Test
public void noClear() throws Exception {
ArgumentBean bean = parse("-help", "--sfv", "One Piece");
assertTrue(bean.isHelp());
assertFalse(bean.isClear());
assertEquals("One Piece", bean.getSfvPanelFile().getName());
}
@Test
public void oneArgument() throws Exception {
ArgumentBean bean = parse("--sfv", "One Piece.sfv");
assertFalse(bean.isClear());
assertFalse(bean.isHelp());
assertEquals("One Piece.sfv", bean.getSfvPanelFile().getName());
}
@Test
public void mixedArguments() throws Exception {
ArgumentBean bean = parse("--list", "Twin Peaks.txt", "--sfv", "Death Note.sfv");
assertEquals("Twin Peaks.txt", bean.getListPanelFile().getName());
assertEquals("Death Note.sfv", bean.getSfvPanelFile().getName());
}
private static ArgumentBean parse(String... args) throws CmdLineException {
ArgumentBean bean = new ArgumentBean();
new CmdLineParser(bean).parseArgument(args);
return bean;
}
}

View File

@ -13,7 +13,7 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses( { MatcherTestSuite.class, WebTestSuite.class })
@SuiteClasses( { MatcherTestSuite.class, WebTestSuite.class, ArgumentBeanTest.class })
public class FileBotTestSuite {
public static Test suite() {