* 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:
parent
a401a51c75
commit
0c674849d8
30
build.xml
30
build.xml
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
<project name="FileBot" basedir="." default="fatjar">
|
<project name="FileBot" default="fatjar">
|
||||||
|
|
||||||
<property name="title" value="${ant.project.name}" />
|
<property name="title" value="${ant.project.name}" />
|
||||||
<property name="version" value="1.9" />
|
<property name="version" value="1.9" />
|
||||||
|
@ -9,20 +9,26 @@
|
||||||
<property name="dir.source" location="${basedir}/source" />
|
<property name="dir.source" location="${basedir}/source" />
|
||||||
<property name="dir.test" location="${basedir}/test" />
|
<property name="dir.test" location="${basedir}/test" />
|
||||||
<property name="dir.build" location="${basedir}/build" />
|
<property name="dir.build" location="${basedir}/build" />
|
||||||
|
<property name="dir.dist" location="${basedir}/dist" />
|
||||||
<property name="dir.lib" location="${basedir}/lib" />
|
<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" 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" />
|
||||||
|
|
||||||
<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" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<target name="fatjar" depends="clean, build">
|
<target name="fatjar" depends="clean, build">
|
||||||
|
<!-- create dist dir -->
|
||||||
|
<mkdir dir="${dir.dist}" />
|
||||||
|
|
||||||
<jar destfile="${executable}" duplicate="fail">
|
<jar destfile="${executable}" duplicate="fail">
|
||||||
<fileset dir="${dir.build}" />
|
<fileset dir="${dir.build}" />
|
||||||
|
|
||||||
|
@ -63,6 +69,10 @@
|
||||||
<zipfileset src="${lib.glazedlists}">
|
<zipfileset src="${lib.glazedlists}">
|
||||||
<include name="ca/odell/glazedlists/**" />
|
<include name="ca/odell/glazedlists/**" />
|
||||||
</zipfileset>
|
</zipfileset>
|
||||||
|
|
||||||
|
<zipfileset src="${lib.args4j}">
|
||||||
|
<include name="*/**" />
|
||||||
|
</zipfileset>
|
||||||
</jar>
|
</jar>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
@ -98,13 +108,13 @@
|
||||||
|
|
||||||
|
|
||||||
<target name="clean">
|
<target name="clean">
|
||||||
<delete file="${executable}" verbose="yes" />
|
<delete dir="${dir.dist}" />
|
||||||
<delete dir="${dir.build}" />
|
<delete dir="${dir.build}" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
||||||
<target name="test" depends="clean, build-test, fatjar">
|
<target name="test" depends="clean, build-test, fatjar">
|
||||||
<junit printsummary="yes" fork="yes">
|
<junit printsummary="yes" showoutput="yes">
|
||||||
<classpath>
|
<classpath>
|
||||||
<pathelement location="${executable}"/>
|
<pathelement location="${executable}"/>
|
||||||
<pathelement location="${lib.junit}"/>
|
<pathelement location="${lib.junit}"/>
|
||||||
|
|
Binary file not shown.
|
@ -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.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
import net.sourceforge.filebot.ArgumentBean;
|
||||||
import net.sourceforge.filebot.Settings;
|
import net.sourceforge.filebot.Settings;
|
||||||
import net.sourceforge.filebot.ui.FileBotWindow;
|
import net.sourceforge.filebot.ui.FileBotWindow;
|
||||||
import net.sourceforge.tuned.MessageBus;
|
|
||||||
|
import org.kohsuke.args4j.CmdLineException;
|
||||||
|
import org.kohsuke.args4j.CmdLineParser;
|
||||||
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
@ -24,11 +19,10 @@ public class Main {
|
||||||
*/
|
*/
|
||||||
public static void main(String... args) {
|
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();
|
Settings.getSettings().clear();
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
|
@ -43,7 +37,7 @@ public class Main {
|
||||||
FileBotWindow window = new FileBotWindow();
|
FileBotWindow window = new FileBotWindow();
|
||||||
|
|
||||||
// publish messages from arguments to the newly created components
|
// publish messages from arguments to the newly created components
|
||||||
arguments.publishMessages();
|
argumentBean.publishMessages();
|
||||||
|
|
||||||
// start
|
// start
|
||||||
window.setVisible(true);
|
window.setVisible(true);
|
||||||
|
@ -52,42 +46,25 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class Arguments {
|
private static ArgumentBean parseArguments(String... args) {
|
||||||
|
|
||||||
private final Set<String> parameters = new HashSet<String>(3);
|
ArgumentBean argumentBean = new ArgumentBean();
|
||||||
private final Map<String, List<String>> messages = new LinkedHashMap<String, List<String>>();
|
CmdLineParser argumentParser = new CmdLineParser(argumentBean);
|
||||||
|
|
||||||
|
try {
|
||||||
public Arguments(String[] args) {
|
argumentParser.parseArgument(args);
|
||||||
Pattern topicPattern = Pattern.compile("--(\\w+)");
|
} catch (CmdLineException e) {
|
||||||
|
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.getMessage());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (argumentBean.isHelp()) {
|
||||||
|
System.out.println("Options:");
|
||||||
|
argumentParser.printUsage(System.out);
|
||||||
|
|
||||||
public boolean containsParameter(String argument) {
|
// just print help message and exit afterwards
|
||||||
return parameters.contains(argument);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return argumentBean;
|
||||||
public void publishMessages() {
|
|
||||||
for (String topic : messages.keySet()) {
|
|
||||||
MessageBus.getDefault().publish(topic, messages.get(topic).toArray(new String[0]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,10 +13,6 @@ import net.sourceforge.tuned.FileUtil;
|
||||||
|
|
||||||
public class FileBotUtil {
|
public class FileBotUtil {
|
||||||
|
|
||||||
private FileBotUtil() {
|
|
||||||
// hide constructor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalid characters in filenames: \, /, :, *, ?, ", <, >, |, \r and \n
|
* Invalid characters in filenames: \, /, :, *, ?, ", <, >, |, \r and \n
|
||||||
*/
|
*/
|
||||||
|
@ -107,4 +103,9 @@ public class FileBotUtil {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private FileBotUtil() {
|
||||||
|
// hide constructor
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ public class Settings {
|
||||||
public static final String SELECTED_PANEL = "panel";
|
public static final String SELECTED_PANEL = "panel";
|
||||||
public static final String SEARCH_HISTORY = "search/history";
|
public static final String SEARCH_HISTORY = "search/history";
|
||||||
public static final String SUBTITLE_HISTORY = "subtitle/history";
|
public static final String SUBTITLE_HISTORY = "subtitle/history";
|
||||||
public static final String SUBTITLE_LANGUAGE = "subtitle/language";
|
|
||||||
|
|
||||||
private static final Settings settings = new Settings();
|
private static final Settings settings = new Settings();
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,6 @@ package net.sourceforge.filebot.resources;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
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.imageio.ImageIO;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
@ -16,40 +12,18 @@ import javax.swing.ImageIcon;
|
||||||
|
|
||||||
public class ResourceManager {
|
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) {
|
public static ImageIcon getIcon(String name) {
|
||||||
return getIcon(name, null);
|
return getIcon(name, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ImageIcon getIcon(String name, String def) {
|
public static ImageIcon getIcon(String name, String def) {
|
||||||
ImageIcon icon = iconCache.get(name);
|
|
||||||
|
|
||||||
if (icon == null) {
|
|
||||||
// load image if not in cache
|
|
||||||
URL resource = getResource(name, def);
|
URL resource = getResource(name, def);
|
||||||
|
|
||||||
if (resource != null) {
|
if (resource != null)
|
||||||
icon = new ImageIcon(resource);
|
return new ImageIcon(resource);
|
||||||
iconCache.put(name, icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return icon;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,14 +47,7 @@ public class ResourceManager {
|
||||||
|
|
||||||
|
|
||||||
private static URL getResource(String name) {
|
private static URL getResource(String name) {
|
||||||
String resource = null;
|
return ResourceManager.class.getResource(name + ".png");
|
||||||
|
|
||||||
if (aliasMap.containsKey(name))
|
|
||||||
resource = aliasMap.get(name);
|
|
||||||
else
|
|
||||||
resource = name + ".png";
|
|
||||||
|
|
||||||
return ResourceManager.class.getResource(resource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,4 +60,9 @@ public class ResourceManager {
|
||||||
return resource;
|
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 |
|
@ -6,7 +6,6 @@ import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -33,16 +32,7 @@ public class Torrent {
|
||||||
|
|
||||||
|
|
||||||
public Torrent(File torrent) throws IOException {
|
public Torrent(File torrent) throws IOException {
|
||||||
this(new FileInputStream(torrent));
|
this(decodeTorrent(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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,7 +46,7 @@ public class Torrent {
|
||||||
charset = Charset.forName(encoding);
|
charset = Charset.forName(encoding);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// invalid encoding, just keep using UTF-8
|
// 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);
|
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) {
|
private String decodeString(Object byteArray, Charset charset) {
|
||||||
if (byteArray == null)
|
if (byteArray == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -174,17 +175,6 @@ public class Torrent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 {
|
public static class Entry {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
|
@ -91,12 +91,14 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
|
||||||
completionList.addMemberList(fetchHistory);
|
completionList.addMemberList(fetchHistory);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
searchField.getEditor().setAction(searchAction);
|
||||||
|
|
||||||
searchField.getSelectButton().setModel(createSearchEngines());
|
searchField.getSelectButton().setModel(createSearchEngines());
|
||||||
searchField.getSelectButton().setLabelProvider(createSearchEngineLabelProvider());
|
searchField.getSelectButton().setLabelProvider(createSearchEngineLabelProvider());
|
||||||
|
|
||||||
AutoCompleteSupport.install(searchField.getEditor(), searchHistory);
|
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 SearchTask createSearchTask();
|
||||||
|
|
||||||
|
|
||||||
protected abstract void configureSelectDialog(SelectDialog<SearchResult> selectDialog);
|
|
||||||
|
|
||||||
|
|
||||||
protected abstract FetchTask createFetchTask(SearchTask searchTask, SearchResult selectedSearchResult);
|
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 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() {
|
public String getSearchText() {
|
||||||
return searchText;
|
return searchText;
|
||||||
}
|
}
|
||||||
|
@ -210,14 +244,9 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
|
||||||
SearchTask task = (SearchTask) evt.getSource();
|
SearchTask task = (SearchTask) evt.getSource();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SearchResult selectedResult = selectSearchResult(task);
|
SearchResult selectedResult = task.chooseSearchResult();
|
||||||
|
|
||||||
if (selectedResult == null) {
|
if (selectedResult == null) {
|
||||||
if (task.get().isEmpty()) {
|
|
||||||
// no search results
|
|
||||||
MessageManager.showWarning(String.format("\"%s\" has not been found.", task.getSearchText()));
|
|
||||||
}
|
|
||||||
|
|
||||||
tab.close();
|
tab.close();
|
||||||
return;
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,9 @@ package net.sourceforge.filebot.ui;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.event.ActionEvent;
|
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.AbstractAction;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.Action;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
@ -20,59 +15,48 @@ import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.border.TitledBorder;
|
import javax.swing.border.TitledBorder;
|
||||||
|
|
||||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||||
import net.sourceforge.filebot.ui.transfer.ExportHandler;
|
import net.sourceforge.filebot.ui.transfer.FileExportHandler;
|
||||||
import net.sourceforge.filebot.ui.transfer.FileTransferable;
|
import net.sourceforge.filebot.ui.transfer.TransferablePolicy;
|
||||||
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.tuned.ui.DefaultFancyListCellRenderer;
|
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
||||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
|
||||||
import net.sourceforge.tuned.ui.TunedUtil;
|
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());
|
super(new BorderLayout());
|
||||||
|
|
||||||
JScrollPane listScrollPane = new JScrollPane(list);
|
setBorder(new TitledBorder(getTitle()));
|
||||||
|
|
||||||
if (border) {
|
|
||||||
titledBorder = new TitledBorder("");
|
|
||||||
setBorder(titledBorder);
|
|
||||||
} else {
|
|
||||||
titledBorder = null;
|
|
||||||
listScrollPane.setBorder(BorderFactory.createEmptyBorder());
|
|
||||||
}
|
|
||||||
|
|
||||||
list.setCellRenderer(new DefaultFancyListCellRenderer());
|
list.setCellRenderer(new DefaultFancyListCellRenderer());
|
||||||
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
|
|
||||||
|
list.setTransferHandler(new DefaultTransferHandler(null, null));
|
||||||
|
list.setDragEnabled(false);
|
||||||
|
|
||||||
add(listScrollPane, BorderLayout.CENTER);
|
add(listScrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
ExportHandler exportHandler = null;
|
// Shortcut DELETE, disabled by default
|
||||||
|
removeAction.setEnabled(false);
|
||||||
|
|
||||||
if (enableExport)
|
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
public void setTransferablePolicy(TransferablePolicy transferablePolicy) {
|
||||||
mutableTransferablePolicy.setTransferablePolicy(transferablePolicy);
|
getTransferHandler().setImportHandler(transferablePolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TransferablePolicy getTransferablePolicy() {
|
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) {
|
public void setTitle(String title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
||||||
if (titledBorder != null)
|
if (getBorder() instanceof TitledBorder) {
|
||||||
|
TitledBorder titledBorder = (TitledBorder) getBorder();
|
||||||
titledBorder.setTitle(title);
|
titledBorder.setTitle(title);
|
||||||
|
|
||||||
revalidate();
|
revalidate();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SimpleListModel getModel() {
|
|
||||||
return (SimpleListModel) list.getModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void save(File file) {
|
public Action getRemoveAction() {
|
||||||
try {
|
return removeAction;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AbstractAction removeAction = new AbstractAction("Remove") {
|
private final AbstractAction removeAction = new AbstractAction("Remove") {
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,51 +3,13 @@ package net.sourceforge.filebot.ui;
|
||||||
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JPanel;
|
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 {
|
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 String name;
|
||||||
private final Icon icon;
|
private final Icon icon;
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,23 @@ import javax.swing.Timer;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
||||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
|
||||||
import net.sourceforge.tuned.ui.TunedUtil;
|
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 {
|
class FileBotPanelSelectionList extends JList {
|
||||||
|
|
||||||
private static final int SELECTDELAY_ON_DRAG_OVER = 300;
|
private static final int SELECTDELAY_ON_DRAG_OVER = 300;
|
||||||
|
|
||||||
|
private final EventList<FileBotPanel> panelModel = new BasicEventList<FileBotPanel>();
|
||||||
|
|
||||||
|
|
||||||
public FileBotPanelSelectionList() {
|
public FileBotPanelSelectionList() {
|
||||||
|
|
||||||
|
setModel(new EventListModel<FileBotPanel>(panelModel));
|
||||||
|
|
||||||
setCellRenderer(new PanelCellRenderer());
|
setCellRenderer(new PanelCellRenderer());
|
||||||
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
||||||
|
@ -35,12 +42,15 @@ class FileBotPanelSelectionList extends JList {
|
||||||
|
|
||||||
// initialize "drag over" panel selection
|
// initialize "drag over" panel selection
|
||||||
new DropTarget(this, new DragDropListener());
|
new DropTarget(this, new DragDropListener());
|
||||||
|
|
||||||
setModel(new SimpleListModel(FileBotPanel.getAvailablePanels()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class PanelCellRenderer extends DefaultFancyListCellRenderer {
|
public EventList<FileBotPanel> getPanelModel() {
|
||||||
|
return panelModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class PanelCellRenderer extends DefaultFancyListCellRenderer {
|
||||||
|
|
||||||
public PanelCellRenderer() {
|
public PanelCellRenderer() {
|
||||||
super(BorderLayout.CENTER, 10, 0, new Color(0x163264));
|
super(BorderLayout.CENTER, 10, 0, new Color(0x163264));
|
||||||
|
|
|
@ -42,12 +42,17 @@ public class FileBotTree extends JTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void clear() {
|
@Override
|
||||||
DefaultTreeModel model = (DefaultTreeModel) getModel();
|
public DefaultTreeModel getModel() {
|
||||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
|
return (DefaultTreeModel) super.getModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
|
||||||
root.removeAllChildren();
|
root.removeAllChildren();
|
||||||
model.reload(root);
|
|
||||||
|
getModel().reload(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,15 @@ import javax.swing.event.ListSelectionListener;
|
||||||
|
|
||||||
import net.sourceforge.filebot.Settings;
|
import net.sourceforge.filebot.Settings;
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
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.MessageBus;
|
||||||
import net.sourceforge.tuned.MessageHandler;
|
import net.sourceforge.tuned.MessageHandler;
|
||||||
import net.sourceforge.tuned.ui.ShadowBorder;
|
import net.sourceforge.tuned.ui.ShadowBorder;
|
||||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
|
||||||
|
|
||||||
|
|
||||||
public class FileBotWindow extends JFrame implements ListSelectionListener {
|
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"));
|
icons.add(ResourceManager.getImage("window.icon.big"));
|
||||||
setIconImages(icons);
|
setIconImages(icons);
|
||||||
|
|
||||||
|
selectionListPanel.getPanelModel().addAll(createPanels());
|
||||||
selectionListPanel.addListSelectionListener(this);
|
selectionListPanel.addListSelectionListener(this);
|
||||||
|
|
||||||
JComponent contentPane = createContentPane();
|
JComponent contentPane = createContentPane();
|
||||||
|
@ -55,7 +61,21 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
|
||||||
|
|
||||||
selectionListPanel.setSelectedIndex(Settings.getSettings().getInt(Settings.SELECTED_PANEL, 3));
|
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() {
|
private JComponent createPageLayer() {
|
||||||
JPanel pageLayer = new JPanel(new BorderLayout());
|
JPanel pageLayer = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
|
@ -108,10 +127,7 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
|
||||||
pageLayer.add(headerPanel, BorderLayout.NORTH);
|
pageLayer.add(headerPanel, BorderLayout.NORTH);
|
||||||
pageLayer.add(pagePanel, BorderLayout.CENTER);
|
pageLayer.add(pagePanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
SimpleListModel model = (SimpleListModel) selectionListPanel.getModel();
|
for (FileBotPanel panel : selectionListPanel.getPanelModel()) {
|
||||||
|
|
||||||
for (FileBotPanel panel : (List<FileBotPanel>) model.getCopy()) {
|
|
||||||
panel.setVisible(false);
|
|
||||||
pagePanel.add(panel, panel.getPanelName());
|
pagePanel.add(panel, panel.getPanelName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,12 +148,15 @@ public class FileBotWindow extends JFrame implements ListSelectionListener {
|
||||||
return contentPane;
|
return contentPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MessageHandler panelMessageHandler = new MessageHandler() {
|
private final MessageHandler panelSelectMessageHandler = new MessageHandler() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(String topic, String... messages) {
|
public void handle(String topic, Object... messages) {
|
||||||
for (String panel : messages) {
|
if (messages.length >= 1) {
|
||||||
selectionListPanel.setSelectedValue(FileBotPanel.forName(panel), true);
|
Object panel = messages[messages.length - 1];
|
||||||
|
|
||||||
|
if (panel instanceof FileBotPanel)
|
||||||
|
selectionListPanel.setSelectedValue(panel, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,45 +10,63 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import net.sourceforge.filebot.ui.transfer.FileTransferable;
|
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.MessageBus;
|
||||||
import net.sourceforge.tuned.MessageHandler;
|
import net.sourceforge.tuned.MessageHandler;
|
||||||
|
|
||||||
|
|
||||||
public class FileTransferableMessageHandler implements MessageHandler {
|
public class FileTransferableMessageHandler implements MessageHandler {
|
||||||
|
|
||||||
private final String name;
|
private final FileBotPanel panel;
|
||||||
private final TransferablePolicy transferablePolicy;
|
private final TransferablePolicy transferablePolicy;
|
||||||
|
|
||||||
|
|
||||||
public FileTransferableMessageHandler(String name, TransferablePolicy transferablePolicy) {
|
public FileTransferableMessageHandler(FileBotPanel panel, TransferablePolicy transferablePolicy) {
|
||||||
this.name = name;
|
this.panel = panel;
|
||||||
this.transferablePolicy = transferablePolicy;
|
this.transferablePolicy = transferablePolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(String topic, String... messages) {
|
public void handle(String topic, Object... messages) {
|
||||||
// change panel
|
// switch to panel
|
||||||
MessageBus.getDefault().publish("panel", name);
|
MessageBus.getDefault().publish("panel", panel);
|
||||||
|
|
||||||
List<File> files = new ArrayList<File>(messages.length);
|
List<File> files = new ArrayList<File>(messages.length);
|
||||||
|
|
||||||
for (String filename : messages) {
|
for (Object message : messages) {
|
||||||
try {
|
File file = fromMessage(message);
|
||||||
File file = new File(filename);
|
|
||||||
|
if (file == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
// file might be relative, use absolute file
|
try {
|
||||||
|
// path may be relative, use absolute path
|
||||||
files.add(file.getCanonicalFile());
|
files.add(file.getCanonicalFile());
|
||||||
} else {
|
|
||||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, String.format("Invalid File: %s", filename));
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ import javax.swing.SwingUtilities;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
|
import net.sourceforge.tuned.ui.ArrayListModel;
|
||||||
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
import net.sourceforge.tuned.ui.DefaultFancyListCellRenderer;
|
||||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
|
||||||
import net.sourceforge.tuned.ui.TunedUtil;
|
import net.sourceforge.tuned.ui.TunedUtil;
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,14 +78,14 @@ public class SelectDialog<T> extends JDialog {
|
||||||
setLocation(TunedUtil.getPreferredLocation(this));
|
setLocation(TunedUtil.getPreferredLocation(this));
|
||||||
|
|
||||||
// default selection
|
// default selection
|
||||||
list.setModel(new SimpleListModel(options));
|
list.setModel(new ArrayListModel(options));
|
||||||
list.setSelectedIndex(0);
|
list.setSelectedIndex(0);
|
||||||
|
|
||||||
// Shortcut Enter
|
// Shortcut Enter
|
||||||
TunedUtil.registerActionForKeystroke(list, KeyStroke.getKeyStroke("released ENTER"), selectAction);
|
TunedUtil.putActionForKeystroke(list, KeyStroke.getKeyStroke("released ENTER"), selectAction);
|
||||||
|
|
||||||
// Shortcut Escape
|
// Shortcut Escape
|
||||||
TunedUtil.registerActionForKeystroke(list, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
TunedUtil.putActionForKeystroke(list, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class AnalyzePanel extends FileBotPanel {
|
||||||
|
|
||||||
fileTreePanel.getFileTree().addPropertyChangeListener(FileTree.CONTENT_PROPERTY, fileTreeChangeListener);
|
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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,12 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.tree.DefaultMutableTreeNode;
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
import javax.swing.tree.DefaultTreeModel;
|
|
||||||
import javax.swing.tree.TreeNode;
|
import javax.swing.tree.TreeNode;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import net.sourceforge.filebot.ui.FileBotTree;
|
import net.sourceforge.filebot.ui.FileBotTree;
|
||||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||||
import net.sourceforge.filebot.ui.transfer.FileTransferable;
|
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
|
||||||
import net.sourceforge.filebot.ui.transfer.TransferablePolicyImportHandler;
|
|
||||||
import net.sourceforge.filebot.ui.transferablepolicies.TransferablePolicy;
|
|
||||||
|
|
||||||
|
|
||||||
class FileTree extends FileBotTree {
|
class FileTree extends FileBotTree {
|
||||||
|
@ -37,11 +34,11 @@ class FileTree extends FileBotTree {
|
||||||
transferablePolicy = new FileTreeTransferablePolicy(this);
|
transferablePolicy = new FileTreeTransferablePolicy(this);
|
||||||
transferablePolicy.addPropertyChangeListener(LOADING_PROPERTY, new LoadingPropertyChangeListener());
|
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;
|
return transferablePolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,24 +55,14 @@ class FileTree extends FileBotTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultTreeModel model = (DefaultTreeModel) getModel();
|
|
||||||
|
|
||||||
for (TreeNode treeNode : changedNodes) {
|
for (TreeNode treeNode : changedNodes) {
|
||||||
model.reload(treeNode);
|
getModel().reload(treeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
contentChanged();
|
contentChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void load(List<File> files) {
|
|
||||||
FileTransferable tr = new FileTransferable(files);
|
|
||||||
|
|
||||||
if (transferablePolicy.accept(tr))
|
|
||||||
transferablePolicy.handleTransferable(tr, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
transferablePolicy.reset();
|
transferablePolicy.reset();
|
||||||
|
@ -85,14 +72,12 @@ class FileTree extends FileBotTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void contentChanged() {
|
private synchronized void contentChanged() {
|
||||||
synchronized (this) {
|
|
||||||
if (postProcessor != null)
|
if (postProcessor != null)
|
||||||
postProcessor.cancel(true);
|
postProcessor.cancel(true);
|
||||||
|
|
||||||
postProcessor = new PostProcessor();
|
postProcessor = new PostProcessor();
|
||||||
postProcessor.execute();
|
postProcessor.execute();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,7 +90,7 @@ class FileTree extends FileBotTree {
|
||||||
firePropertyChange(FileTree.LOADING_PROPERTY, null, loading);
|
firePropertyChange(FileTree.LOADING_PROPERTY, null, loading);
|
||||||
|
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
((DefaultTreeModel) getModel()).reload();
|
getModel().reload();
|
||||||
contentChanged();
|
contentChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ class FileTreePanel extends JPanel {
|
||||||
buttons.add(Box.createGlue());
|
buttons.add(Box.createGlue());
|
||||||
|
|
||||||
// Shortcut DELETE
|
// 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(new LoadingOverlayPane(new JScrollPane(fileTree), ResourceManager.getIcon("loading")), BorderLayout.CENTER);
|
||||||
add(buttons, BorderLayout.SOUTH);
|
add(buttons, BorderLayout.SOUTH);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
import javax.swing.tree.DefaultTreeModel;
|
import javax.swing.tree.DefaultTreeModel;
|
||||||
|
|
||||||
import net.sourceforge.filebot.FileBotUtil;
|
import net.sourceforge.filebot.FileBotUtil;
|
||||||
import net.sourceforge.filebot.ui.transferablepolicies.BackgroundFileTransferablePolicy;
|
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
|
||||||
|
|
||||||
|
|
||||||
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<DefaultMutableTreeNode> {
|
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<DefaultMutableTreeNode> {
|
||||||
|
@ -74,7 +74,7 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Defaul
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getFileFilterDescription() {
|
||||||
return "files and folders";
|
return "files and folders";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,16 @@ import java.util.logging.Logger;
|
||||||
import net.sourceforge.filebot.FileBotUtil;
|
import net.sourceforge.filebot.FileBotUtil;
|
||||||
import net.sourceforge.filebot.torrent.Torrent;
|
import net.sourceforge.filebot.torrent.Torrent;
|
||||||
import net.sourceforge.filebot.ui.FileBotList;
|
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;
|
import net.sourceforge.tuned.FileUtil;
|
||||||
|
|
||||||
|
|
||||||
class FileListTransferablePolicy extends FileTransferablePolicy {
|
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;
|
this.list = list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,12 +50,13 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
|
||||||
|
|
||||||
private void loadFolderList(List<File> folders) {
|
private void loadFolderList(List<File> folders) {
|
||||||
if (folders.size() == 1) {
|
if (folders.size() == 1) {
|
||||||
|
// if only one folder was dropped, use its name as title
|
||||||
list.setTitle(FileUtil.getFolderName(folders.get(0)));
|
list.setTitle(FileUtil.getFolderName(folders.get(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (File folder : folders) {
|
for (File folder : folders) {
|
||||||
for (File file : folder.listFiles()) {
|
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
|
@Override
|
||||||
public String getDescription() {
|
public String getFileFilterDescription() {
|
||||||
return "files, folders and torrents";
|
return "files, folders and torrents";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
import net.sourceforge.filebot.ui.FileBotList;
|
import net.sourceforge.filebot.ui.FileBotList;
|
||||||
|
import net.sourceforge.filebot.ui.FileBotListExportHandler;
|
||||||
import net.sourceforge.filebot.ui.FileBotPanel;
|
import net.sourceforge.filebot.ui.FileBotPanel;
|
||||||
import net.sourceforge.filebot.ui.FileTransferableMessageHandler;
|
import net.sourceforge.filebot.ui.FileTransferableMessageHandler;
|
||||||
import net.sourceforge.filebot.ui.MessageManager;
|
import net.sourceforge.filebot.ui.MessageManager;
|
||||||
|
@ -36,10 +37,7 @@ public class ListPanel extends FileBotPanel {
|
||||||
|
|
||||||
private static final String INDEX_VARIABLE = "<i>";
|
private static final String INDEX_VARIABLE = "<i>";
|
||||||
|
|
||||||
private FileBotList list = new FileBotList(true, true, true);
|
private FileBotList<String> list = new FileBotList<String>();
|
||||||
|
|
||||||
private SaveAction saveAction = new SaveAction(list);
|
|
||||||
private LoadAction loadAction = new LoadAction(list.getTransferablePolicy());
|
|
||||||
|
|
||||||
private JTextField textField = new JTextField(String.format("Name - %s", INDEX_VARIABLE), 25);
|
private JTextField textField = new JTextField(String.format("Name - %s", INDEX_VARIABLE), 25);
|
||||||
private SpinnerNumberModel fromSpinnerModel = new SpinnerNumberModel(1, 0, Integer.MAX_VALUE, 1);
|
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"));
|
super("List", ResourceManager.getIcon("panel.list"));
|
||||||
|
|
||||||
list.setTransferablePolicy(new FileListTransferablePolicy(list));
|
list.setTransferablePolicy(new FileListTransferablePolicy(list));
|
||||||
|
list.setExportHandler(new FileBotListExportHandler(list));
|
||||||
|
|
||||||
|
list.getRemoveAction().setEnabled(true);
|
||||||
|
|
||||||
Box buttons = Box.createHorizontalBox();
|
Box buttons = Box.createHorizontalBox();
|
||||||
buttons.setBorder(new EmptyBorder(5, 5, 5, 5));
|
buttons.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||||
buttons.add(Box.createHorizontalGlue());
|
buttons.add(Box.createHorizontalGlue());
|
||||||
|
|
||||||
buttons.add(new JButton(loadAction));
|
buttons.add(new JButton(new LoadAction(list.getTransferablePolicy())));
|
||||||
buttons.add(Box.createHorizontalStrut(5));
|
buttons.add(Box.createHorizontalStrut(5));
|
||||||
buttons.add(new JButton(saveAction));
|
buttons.add(new JButton(new SaveAction(list.getExportHandler())));
|
||||||
buttons.add(Box.createHorizontalGlue());
|
buttons.add(Box.createHorizontalGlue());
|
||||||
|
|
||||||
list.add(buttons, BorderLayout.SOUTH);
|
list.add(buttons, BorderLayout.SOUTH);
|
||||||
|
@ -88,9 +89,9 @@ public class ListPanel extends FileBotPanel {
|
||||||
add(spinners, BorderLayout.NORTH);
|
add(spinners, BorderLayout.NORTH);
|
||||||
add(list, BorderLayout.CENTER);
|
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;
|
index += increment;
|
||||||
} while (index != (to + increment));
|
} while (index != (to + increment));
|
||||||
|
|
||||||
list.getModel().set(entries);
|
list.getModel().clear();
|
||||||
|
list.getModel().addAll(entries);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,17 @@ import java.util.List;
|
||||||
|
|
||||||
import net.sourceforge.filebot.FileBotUtil;
|
import net.sourceforge.filebot.FileBotUtil;
|
||||||
import net.sourceforge.filebot.ui.panel.rename.entry.FileEntry;
|
import net.sourceforge.filebot.ui.panel.rename.entry.FileEntry;
|
||||||
import net.sourceforge.filebot.ui.transferablepolicies.FileTransferablePolicy;
|
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
|
||||||
import net.sourceforge.tuned.ui.SimpleListModel;
|
import ca.odell.glazedlists.EventList;
|
||||||
|
|
||||||
|
|
||||||
class FilesListTransferablePolicy extends FileTransferablePolicy {
|
class FilesListTransferablePolicy extends FileTransferablePolicy {
|
||||||
|
|
||||||
private final SimpleListModel model;
|
private final EventList<? super FileEntry> model;
|
||||||
|
|
||||||
|
|
||||||
public FilesListTransferablePolicy(SimpleListModel listModel) {
|
public FilesListTransferablePolicy(EventList<? super FileEntry> model) {
|
||||||
this.model = listModel;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class FilesListTransferablePolicy extends FileTransferablePolicy {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getFileFilterDescription() {
|
||||||
return "files and folders";
|
return "files and folders";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import javax.swing.SwingUtilities;
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
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.entry.ListEntry;
|
||||||
import net.sourceforge.filebot.ui.panel.rename.matcher.Match;
|
import net.sourceforge.filebot.ui.panel.rename.matcher.Match;
|
||||||
import net.sourceforge.filebot.ui.panel.rename.matcher.Matcher;
|
import net.sourceforge.filebot.ui.panel.rename.matcher.Matcher;
|
||||||
|
@ -32,8 +33,8 @@ class MatchAction extends AbstractAction {
|
||||||
|
|
||||||
private CompositeSimilarityMetric metrics;
|
private CompositeSimilarityMetric metrics;
|
||||||
|
|
||||||
private final RenameList namesList;
|
private final RenameList<ListEntry> namesList;
|
||||||
private final RenameList filesList;
|
private final RenameList<FileEntry> filesList;
|
||||||
|
|
||||||
private boolean matchName2File;
|
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 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");
|
super("Match");
|
||||||
|
|
||||||
this.namesList = namesList;
|
this.namesList = namesList;
|
||||||
|
@ -77,13 +78,14 @@ class MatchAction extends AbstractAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
JComponent source = (JComponent) evt.getSource();
|
JComponent source = (JComponent) evt.getSource();
|
||||||
|
|
||||||
SwingUtilities.getRoot(source).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
SwingUtilities.getRoot(source).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
RenameList primaryList = matchName2File ? namesList : filesList;
|
RenameList<ListEntry> primaryList = (RenameList<ListEntry>) (matchName2File ? namesList : filesList);
|
||||||
RenameList secondaryList = matchName2File ? filesList : namesList;
|
RenameList<ListEntry> secondaryList = (RenameList<ListEntry>) (matchName2File ? filesList : namesList);
|
||||||
|
|
||||||
BackgroundMatcher backgroundMatcher = new BackgroundMatcher(primaryList, secondaryList, metrics);
|
BackgroundMatcher backgroundMatcher = new BackgroundMatcher(primaryList, secondaryList, metrics);
|
||||||
SwingWorkerProgressMonitor monitor = new SwingWorkerProgressMonitor(SwingUtilities.getWindowAncestor(source), backgroundMatcher);
|
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<ListEntry> primaryList;
|
||||||
private final RenameList secondaryList;
|
private final RenameList<ListEntry> secondaryList;
|
||||||
|
|
||||||
private final Matcher matcher;
|
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.primaryList = primaryList;
|
||||||
this.secondaryList = secondaryList;
|
this.secondaryList = secondaryList;
|
||||||
|
|
||||||
|
@ -167,7 +169,6 @@ class MatchAction extends AbstractAction {
|
||||||
|
|
||||||
primaryList.getModel().clear();
|
primaryList.getModel().clear();
|
||||||
secondaryList.getModel().clear();
|
secondaryList.getModel().clear();
|
||||||
|
|
||||||
for (Match match : matches) {
|
for (Match match : matches) {
|
||||||
primaryList.getModel().add(match.getA());
|
primaryList.getModel().add(match.getA());
|
||||||
secondaryList.getModel().add(match.getB());
|
secondaryList.getModel().add(match.getB());
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package net.sourceforge.filebot.ui.panel.rename;
|
package net.sourceforge.filebot.ui.panel.rename;
|
||||||
|
|
||||||
|
|
||||||
|
import java.awt.datatransfer.Transferable;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
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.ListEntry;
|
||||||
import net.sourceforge.filebot.ui.panel.rename.entry.StringEntry;
|
import net.sourceforge.filebot.ui.panel.rename.entry.StringEntry;
|
||||||
import net.sourceforge.filebot.ui.panel.rename.entry.TorrentEntry;
|
import net.sourceforge.filebot.ui.panel.rename.entry.TorrentEntry;
|
||||||
import net.sourceforge.filebot.ui.transferablepolicies.CompositeTransferablePolicy;
|
import net.sourceforge.filebot.ui.transfer.StringTransferablePolicy;
|
||||||
import net.sourceforge.filebot.ui.transferablepolicies.TextTransferablePolicy;
|
|
||||||
|
|
||||||
|
|
||||||
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) {
|
public NamesListTransferablePolicy(RenameList<ListEntry> list) {
|
||||||
|
super(list.getModel());
|
||||||
|
|
||||||
this.list = list;
|
this.list = list;
|
||||||
|
}
|
||||||
|
|
||||||
addPolicy(new FilePolicy());
|
|
||||||
addPolicy(new TextPolicy());
|
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,19 +72,6 @@ class NamesListTransferablePolicy extends CompositeTransferablePolicy {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void clear() {
|
|
||||||
list.getModel().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class FilePolicy extends FilesListTransferablePolicy {
|
|
||||||
|
|
||||||
public FilePolicy() {
|
|
||||||
super(list.getModel());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void load(List<File> files) {
|
protected void load(List<File> files) {
|
||||||
|
|
||||||
|
@ -127,20 +130,24 @@ class NamesListTransferablePolicy extends CompositeTransferablePolicy {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getFileFilterDescription() {
|
||||||
return "text files and torrent files";
|
return "text files and torrent files";
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
private class TextPolicy extends StringTransferablePolicy {
|
||||||
private class TextPolicy extends TextTransferablePolicy {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void load(String text) {
|
protected void clear() {
|
||||||
|
NamesListTransferablePolicy.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void load(String string) {
|
||||||
List<ListEntry> entries = new ArrayList<ListEntry>();
|
List<ListEntry> entries = new ArrayList<ListEntry>();
|
||||||
|
|
||||||
String[] lines = text.split("\r?\n");
|
String[] lines = string.split("\r?\n");
|
||||||
|
|
||||||
for (String line : lines) {
|
for (String line : lines) {
|
||||||
|
|
||||||
|
@ -153,11 +160,6 @@ class NamesListTransferablePolicy extends CompositeTransferablePolicy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return "lines of text";
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@ import net.sourceforge.tuned.FileUtil;
|
||||||
|
|
||||||
public class RenameAction extends AbstractAction {
|
public class RenameAction extends AbstractAction {
|
||||||
|
|
||||||
private final RenameList namesList;
|
private final RenameList<ListEntry> namesList;
|
||||||
private final RenameList filesList;
|
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"));
|
super("Rename", ResourceManager.getIcon("action.rename"));
|
||||||
this.namesList = namesList;
|
this.namesList = namesList;
|
||||||
this.filesList = filesList;
|
this.filesList = filesList;
|
||||||
|
@ -33,7 +33,7 @@ public class RenameAction extends AbstractAction {
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
List<ListEntry> nameEntries = namesList.getEntries();
|
List<ListEntry> nameEntries = namesList.getEntries();
|
||||||
List<ListEntry> fileEntries = filesList.getEntries();
|
List<FileEntry> fileEntries = filesList.getEntries();
|
||||||
|
|
||||||
int minLength = Math.min(nameEntries.size(), fileEntries.size());
|
int minLength = Math.min(nameEntries.size(), fileEntries.size());
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ public class RenameAction extends AbstractAction {
|
||||||
int errors = 0;
|
int errors = 0;
|
||||||
|
|
||||||
for (i = 0; i < minLength; i++) {
|
for (i = 0; i < minLength; i++) {
|
||||||
FileEntry fileEntry = (FileEntry) fileEntries.get(i);
|
FileEntry fileEntry = fileEntries.get(i);
|
||||||
File f = fileEntry.getFile();
|
File f = fileEntry.getFile();
|
||||||
|
|
||||||
String newName = nameEntries.get(i).toString() + FileUtil.getExtension(f, true);
|
String newName = nameEntries.get(i).toString() + FileUtil.getExtension(f, true);
|
||||||
|
|
|
@ -6,12 +6,12 @@ import java.awt.BorderLayout;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JList;
|
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.border.EmptyBorder;
|
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.FileBotList;
|
||||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||||
import net.sourceforge.filebot.ui.transfer.LoadAction;
|
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() {
|
public RenameList() {
|
||||||
super(false, true, true);
|
|
||||||
|
|
||||||
Box buttons = Box.createHorizontalBox();
|
Box buttons = Box.createHorizontalBox();
|
||||||
buttons.setBorder(new EmptyBorder(5, 5, 5, 5));
|
buttons.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||||
buttons.add(Box.createGlue());
|
buttons.add(Box.createGlue());
|
||||||
buttons.add(new JButton(downAction));
|
buttons.add(new JButton(downAction));
|
||||||
buttons.add(new JButton(upAction));
|
buttons.add(new JButton(upAction));
|
||||||
buttons.add(Box.createHorizontalStrut(10));
|
buttons.add(Box.createHorizontalStrut(10));
|
||||||
buttons.add(new JButton(loadAction));
|
buttons.add(loadButton);
|
||||||
buttons.add(Box.createGlue());
|
buttons.add(Box.createGlue());
|
||||||
|
|
||||||
add(buttons, BorderLayout.SOUTH);
|
add(buttons, BorderLayout.SOUTH);
|
||||||
|
|
||||||
JList list = getListComponent();
|
|
||||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
||||||
list.addMouseListener(dndReorderMouseAdapter);
|
list.addMouseListener(dndReorderMouseAdapter);
|
||||||
|
@ -46,69 +47,76 @@ class RenameList extends FileBotList {
|
||||||
|
|
||||||
JViewport viewport = (JViewport) list.getParent();
|
JViewport viewport = (JViewport) list.getParent();
|
||||||
viewport.setBackground(list.getBackground());
|
viewport.setBackground(list.getBackground());
|
||||||
|
|
||||||
|
getRemoveAction().setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@Override
|
||||||
public List<ListEntry> getEntries() {
|
public void setTransferablePolicy(TransferablePolicy transferablePolicy) {
|
||||||
return (List<ListEntry>) getModel().getCopy();
|
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")) {
|
private final AbstractAction upAction = new AbstractAction(null, ResourceManager.getIcon("action.up")) {
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
int index = getListComponent().getSelectedIndex();
|
int selectedIndex = getListComponent().getSelectedIndex();
|
||||||
|
int toIndex = selectedIndex + 1;
|
||||||
|
|
||||||
if (index <= 0) // first element
|
if (moveEntry(selectedIndex, toIndex)) {
|
||||||
return;
|
getListComponent().setSelectedIndex(toIndex);
|
||||||
|
}
|
||||||
Object object = getModel().remove(index);
|
|
||||||
|
|
||||||
int newIndex = index - 1;
|
|
||||||
getModel().add(newIndex, object);
|
|
||||||
getListComponent().setSelectedIndex(newIndex);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final AbstractAction downAction = new AbstractAction(null, ResourceManager.getIcon("action.down")) {
|
private final AbstractAction downAction = new AbstractAction(null, ResourceManager.getIcon("action.down")) {
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
int index = getListComponent().getSelectedIndex();
|
int selectedIndex = getListComponent().getSelectedIndex();
|
||||||
|
int toIndex = selectedIndex - 1;
|
||||||
|
|
||||||
if (index >= getModel().getSize() - 1) // last element
|
if (moveEntry(selectedIndex, toIndex)) {
|
||||||
return;
|
getListComponent().setSelectedIndex(toIndex);
|
||||||
|
}
|
||||||
Object object = getModel().remove(index);
|
|
||||||
|
|
||||||
int newIndex = index + 1;
|
|
||||||
getModel().add(newIndex, object);
|
|
||||||
getListComponent().setSelectedIndex(newIndex);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected final LoadAction loadAction = new LoadAction(getTransferablePolicy());
|
private final MouseAdapter dndReorderMouseAdapter = new MouseAdapter() {
|
||||||
|
|
||||||
private MouseAdapter dndReorderMouseAdapter = new MouseAdapter() {
|
private int fromIndex = -1;
|
||||||
|
|
||||||
private int from = -1;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent m) {
|
public void mousePressed(MouseEvent m) {
|
||||||
from = getListComponent().getSelectedIndex();
|
fromIndex = getListComponent().getSelectedIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged(MouseEvent m) {
|
public void mouseDragged(MouseEvent m) {
|
||||||
int to = getListComponent().getSelectedIndex();
|
int toIndex = getListComponent().getSelectedIndex();
|
||||||
|
|
||||||
if (to == from)
|
if (toIndex == fromIndex)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object object = getModel().remove(from);
|
moveEntry(fromIndex, toIndex);
|
||||||
getModel().add(to, object);
|
|
||||||
from = to;
|
fromIndex = toIndex;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,14 @@ import javax.swing.event.ListDataListener;
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
import net.sourceforge.filebot.ui.FileBotPanel;
|
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 {
|
public class RenamePanel extends FileBotPanel {
|
||||||
|
|
||||||
private RenameList namesList = new RenameList();
|
private RenameList<ListEntry> namesList = new RenameList<ListEntry>();
|
||||||
private RenameList filesList = new RenameList();
|
private RenameList<FileEntry> filesList = new RenameList<FileEntry>();
|
||||||
|
|
||||||
private MatchAction matchAction = new MatchAction(namesList, filesList);
|
private MatchAction matchAction = new MatchAction(namesList, filesList);
|
||||||
|
|
||||||
|
@ -52,23 +54,23 @@ public class RenamePanel extends FileBotPanel {
|
||||||
filesList.setTitle("Files");
|
filesList.setTitle("Files");
|
||||||
filesList.setTransferablePolicy(new FilesListTransferablePolicy(filesList.getModel()));
|
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);
|
RenameListCellRenderer cellrenderer = new RenameListCellRenderer(namesListComponent.getModel(), filesListComponent.getModel());
|
||||||
filesList.getListComponent().setCellRenderer(cellrenderer);
|
|
||||||
|
|
||||||
JList list1 = namesList.getListComponent();
|
namesListComponent.setCellRenderer(cellrenderer);
|
||||||
JList list2 = filesList.getListComponent();
|
filesListComponent.setCellRenderer(cellrenderer);
|
||||||
|
|
||||||
ListSelectionModel selectionModel = new DefaultListSelectionModel();
|
ListSelectionModel selectionModel = new DefaultListSelectionModel();
|
||||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
||||||
namesList.getListComponent().setSelectionModel(selectionModel);
|
namesListComponent.setSelectionModel(selectionModel);
|
||||||
filesList.getListComponent().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.setVisible(false);
|
||||||
similarityPanel.setMetrics(matchAction.getMetrics());
|
similarityPanel.setMetrics(matchAction.getMetrics());
|
||||||
|
@ -89,8 +91,8 @@ public class RenamePanel extends FileBotPanel {
|
||||||
|
|
||||||
add(box, BorderLayout.CENTER);
|
add(box, BorderLayout.CENTER);
|
||||||
|
|
||||||
namesList.getModel().addListDataListener(repaintOnDataChange);
|
namesListComponent.getModel().addListDataListener(repaintOnDataChange);
|
||||||
filesList.getModel().addListDataListener(repaintOnDataChange);
|
filesListComponent.getModel().addListDataListener(repaintOnDataChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,7 +122,7 @@ public class RenamePanel extends FileBotPanel {
|
||||||
return centerBox;
|
return centerBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListDataListener repaintOnDataChange = new ListDataListener() {
|
private final ListDataListener repaintOnDataChange = new ListDataListener() {
|
||||||
|
|
||||||
public void contentsChanged(ListDataEvent e) {
|
public void contentsChanged(ListDataEvent e) {
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import javax.swing.border.EmptyBorder;
|
||||||
import net.sourceforge.filebot.FileBotUtil;
|
import net.sourceforge.filebot.FileBotUtil;
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
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;
|
import net.sourceforge.tuned.ui.TunedUtil;
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ public class ValidateNamesDialog extends JDialog {
|
||||||
|
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
JList list = new JList(new SimpleListModel(entries));
|
JList list = new JList(new ArrayListModel(entries));
|
||||||
list.setEnabled(false);
|
list.setEnabled(false);
|
||||||
|
|
||||||
list.setCellRenderer(new HighlightListCellRenderer(FileBotUtil.INVALID_CHARACTERS_PATTERN, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4));
|
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));
|
setPreferredSize(new Dimension(365, 280));
|
||||||
pack();
|
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) {
|
protected void actionPropertyChanged(Action action, String propertyName) {
|
||||||
super.actionPropertyChanged(action, propertyName);
|
super.actionPropertyChanged(action, propertyName);
|
||||||
|
|
||||||
if (propertyName == ContinueAction.ALPHA) {
|
if (propertyName.equals(ContinueAction.ALPHA)) {
|
||||||
alpha = getAlpha(action);
|
alpha = getAlpha(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,4 +38,5 @@ public class FileEntry extends AbstractFileEntry {
|
||||||
public File getFile() {
|
public File getFile() {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,8 @@ public abstract class AbstractNameSimilarityMetric implements SimilarityMetric {
|
||||||
protected String normalize(String name) {
|
protected String normalize(String name) {
|
||||||
name = stripChecksum(name);
|
name = stripChecksum(name);
|
||||||
name = normalizeSeparators(name);
|
name = normalizeSeparators(name);
|
||||||
name = name.trim();
|
|
||||||
name = name.toLowerCase();
|
|
||||||
|
|
||||||
return name;
|
return name.trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class NumericSimilarityMetric extends AbstractNameSimilarityMetric {
|
||||||
|
|
||||||
private static class NumberTokeniser implements InterfaceTokeniser {
|
private static class NumberTokeniser implements InterfaceTokeniser {
|
||||||
|
|
||||||
private final String delimiter = "(\\D)+";
|
private static final String delimiter = "(\\D)+";
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,9 +8,11 @@ import javax.swing.JComponent;
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
import net.sourceforge.filebot.ui.FileBotList;
|
import net.sourceforge.filebot.ui.FileBotList;
|
||||||
import net.sourceforge.filebot.ui.FileBotTabComponent;
|
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();
|
private final FileBotTabComponent tabComponent = new FileBotTabComponent();
|
||||||
|
|
||||||
|
@ -20,7 +22,11 @@ public class EpisodeListPanel extends FileBotList {
|
||||||
|
|
||||||
|
|
||||||
public EpisodeListPanel() {
|
public EpisodeListPanel() {
|
||||||
super(true, true, false);
|
setExportHandler(new DefaultListExportHandler(list));
|
||||||
|
getRemoveAction().setEnabled(true);
|
||||||
|
|
||||||
|
setBorder(null);
|
||||||
|
listScrollPane.setBorder(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package net.sourceforge.filebot.ui.panel.search;
|
||||||
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
@ -32,12 +31,14 @@ import javax.swing.SwingWorker;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
|
import net.sourceforge.filebot.ui.FileBotList;
|
||||||
import net.sourceforge.filebot.ui.FileBotPanel;
|
import net.sourceforge.filebot.ui.FileBotPanel;
|
||||||
import net.sourceforge.filebot.ui.HistoryPanel;
|
import net.sourceforge.filebot.ui.HistoryPanel;
|
||||||
import net.sourceforge.filebot.ui.MessageManager;
|
import net.sourceforge.filebot.ui.MessageManager;
|
||||||
import net.sourceforge.filebot.ui.SelectDialog;
|
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.SaveAction;
|
||||||
import net.sourceforge.filebot.ui.transfer.Saveable;
|
|
||||||
import net.sourceforge.filebot.web.AnidbClient;
|
import net.sourceforge.filebot.web.AnidbClient;
|
||||||
import net.sourceforge.filebot.web.Episode;
|
import net.sourceforge.filebot.web.Episode;
|
||||||
import net.sourceforge.filebot.web.EpisodeListClient;
|
import net.sourceforge.filebot.web.EpisodeListClient;
|
||||||
|
@ -118,9 +119,9 @@ public class SearchPanel extends FileBotPanel {
|
||||||
|
|
||||||
this.add(mainPanel, BorderLayout.CENTER);
|
this.add(mainPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
|
||||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("UP"), upAction);
|
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("UP"), upAction);
|
||||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("DOWN"), downAction);
|
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("DOWN"), downAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,19 +180,25 @@ 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
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
protected FileExportHandler getExportHandler() {
|
||||||
Component comp = tabbedPane.getSelectedComponent();
|
try {
|
||||||
|
FileBotList<?> list = (FileBotList<?>) tabbedPane.getSelectedComponent();
|
||||||
if (comp instanceof Saveable) {
|
return list.getExportHandler();
|
||||||
setSaveable((Saveable) comp);
|
} catch (ClassCastException e) {
|
||||||
super.actionPerformed(e);
|
// selected component is the history panel
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
private class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
|
private class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
|
||||||
|
@ -267,13 +274,6 @@ public class SearchPanel extends FileBotPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchResult selectedResult = null;
|
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) {
|
if (searchResults.size() == 1) {
|
||||||
// only one show found, select this one
|
// only one show found, select this one
|
||||||
|
|
|
@ -92,7 +92,7 @@ public class Checksum {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Integer getProgress() {
|
public synchronized Integer getProgress() {
|
||||||
if (state == State.INPROGRESS)
|
if (state == State.INPROGRESS)
|
||||||
return computationTask.getProgress();
|
return computationTask.getProgress();
|
||||||
|
|
||||||
|
@ -130,8 +130,10 @@ public class Checksum {
|
||||||
@Override
|
@Override
|
||||||
public void done(PropertyChangeEvent evt) {
|
public void done(PropertyChangeEvent evt) {
|
||||||
try {
|
try {
|
||||||
if (!computationTask.isCancelled()) {
|
ChecksumComputationTask task = (ChecksumComputationTask) evt.getSource();
|
||||||
setChecksum(computationTask.get());
|
|
||||||
|
if (!task.isCancelled()) {
|
||||||
|
setChecksum(task.get());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// might happen if file system is corrupt (e.g. CRC errors)
|
// might happen if file system is corrupt (e.g. CRC errors)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package net.sourceforge.filebot.ui.panel.sfv;
|
package net.sourceforge.filebot.ui.panel.sfv;
|
||||||
|
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.beans.PropertyChangeSupport;
|
import java.beans.PropertyChangeSupport;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -15,6 +16,8 @@ import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
@ -23,36 +26,37 @@ import net.sourceforge.tuned.DefaultThreadFactory;
|
||||||
|
|
||||||
public class ChecksumComputationService {
|
public class ChecksumComputationService {
|
||||||
|
|
||||||
public static final String ACTIVE_PROPERTY = "ACTIVE_PROPERTY";
|
public static final String ACTIVE_PROPERTY = "active";
|
||||||
public static final String REMAINING_TASK_COUNT_PROPERTY = "REMAINING_TASK_COUNT_PROPERTY";
|
public static final String REMAINING_TASK_COUNT_PROPERTY = "remainingTaskCount";
|
||||||
|
|
||||||
private static final ThreadFactory checksumComputationThreadFactory = new DefaultThreadFactory("ChecksumComputationPool", Thread.MIN_PRIORITY);
|
|
||||||
|
|
||||||
private static final ChecksumComputationService service = new ChecksumComputationService();
|
|
||||||
|
|
||||||
|
|
||||||
public static ChecksumComputationService getService() {
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<File, ChecksumComputationExecutor> executors = new HashMap<File, ChecksumComputationExecutor>();
|
private final Map<File, ChecksumComputationExecutor> executors = new HashMap<File, ChecksumComputationExecutor>();
|
||||||
|
|
||||||
private final AtomicInteger activeSessionTaskCount = new AtomicInteger(0);
|
private final AtomicInteger activeSessionTaskCount = new AtomicInteger(0);
|
||||||
private final AtomicInteger remainingTaskCount = 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() {
|
public ChecksumComputationService() {
|
||||||
// hide constructor
|
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);
|
ChecksumComputationTask task = new ChecksumComputationTask(file);
|
||||||
Checksum checksum = new Checksum(task);
|
Checksum checksum = new Checksum(task);
|
||||||
|
|
||||||
getExecutor(workerQueueKey).execute(task);
|
getExecutor(workerQueue).execute(task);
|
||||||
|
|
||||||
return checksum;
|
return checksum;
|
||||||
}
|
}
|
||||||
|
@ -63,22 +67,19 @@ public class ChecksumComputationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void deactivate(boolean shutdownNow) {
|
private synchronized void deactivate(boolean shutdownNow) {
|
||||||
synchronized (executors) {
|
|
||||||
for (ChecksumComputationExecutor executor : executors.values()) {
|
for (ChecksumComputationExecutor executor : executors.values()) {
|
||||||
if (shutdownNow) {
|
if (shutdownNow)
|
||||||
executor.shutdownNow();
|
executor.shutdownNow();
|
||||||
} else {
|
else
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
executors.clear();
|
executors.clear();
|
||||||
|
|
||||||
activeSessionTaskCount.set(0);
|
activeSessionTaskCount.set(0);
|
||||||
remainingTaskCount.set(0);
|
remainingTaskCount.set(0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
|
@ -96,27 +97,23 @@ public class ChecksumComputationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void purge() {
|
public synchronized void purge() {
|
||||||
synchronized (executors) {
|
|
||||||
for (ChecksumComputationExecutor executor : executors.values()) {
|
for (ChecksumComputationExecutor executor : executors.values()) {
|
||||||
executor.purge();
|
executor.purge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private ChecksumComputationExecutor getExecutor(File workerQueueKey) {
|
private synchronized ChecksumComputationExecutor getExecutor(File workerQueue) {
|
||||||
synchronized (executors) {
|
ChecksumComputationExecutor executor = executors.get(workerQueue);
|
||||||
ChecksumComputationExecutor executor = executors.get(workerQueueKey);
|
|
||||||
|
|
||||||
if (executor == null) {
|
if (executor == null) {
|
||||||
executor = new ChecksumComputationExecutor();
|
executor = new ChecksumComputationExecutor();
|
||||||
executors.put(workerQueueKey, executor);
|
executors.put(workerQueue, executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class ChecksumComputationExecutor extends ThreadPoolExecutor {
|
private class ChecksumComputationExecutor extends ThreadPoolExecutor {
|
||||||
|
@ -125,7 +122,7 @@ public class ChecksumComputationService {
|
||||||
|
|
||||||
|
|
||||||
public ChecksumComputationExecutor() {
|
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
|
// for lots of files, use multiple threads
|
||||||
// e.g 50 files ~ 1 thread, 1000 files ~ 3 threads, 40000 files ~ 5 threads
|
// e.g 50 files ~ 1 thread, 1000 files ~ 3 threads, 40000 files ~ 5 threads
|
||||||
|
|
||||||
int preferredPoolSize = MINIMUM_POOL_SIZE;
|
int preferredPoolSize = (int) Math.log10(Math.max(getQueue().size() / 10, MINIMUM_POOL_SIZE));
|
||||||
|
|
||||||
int queueSize = getQueue().size();
|
|
||||||
|
|
||||||
if (queueSize > 0) {
|
|
||||||
preferredPoolSize += Math.log10(Math.max(queueSize / 10, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getCorePoolSize() != preferredPoolSize) {
|
if (getCorePoolSize() != preferredPoolSize) {
|
||||||
setCorePoolSize(preferredPoolSize);
|
setCorePoolSize(preferredPoolSize);
|
||||||
|
@ -179,8 +170,8 @@ public class ChecksumComputationService {
|
||||||
for (ChecksumComputationTask task : cancelledTasks) {
|
for (ChecksumComputationTask task : cancelledTasks) {
|
||||||
remove(task);
|
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) {
|
private void setActive(boolean active) {
|
||||||
SwingUtilities.invokeLater(new FirePropertyChangeRunnable(ACTIVE_PROPERTY, active));
|
propertyChangeSupport.firePropertyChange(ACTIVE_PROPERTY, null, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void fireRemainingTaskCountChange() {
|
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;
|
public SwingWorkerPropertyChangeSupport(Object sourceBean) {
|
||||||
private final Object newValue;
|
super(sourceBean);
|
||||||
|
|
||||||
|
|
||||||
public FirePropertyChangeRunnable(String property, Object newValue) {
|
|
||||||
this.property = property;
|
|
||||||
this.newValue = newValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firePropertyChange(final PropertyChangeEvent evt) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
propertyChangeSupport.firePropertyChange(property, null, newValue);
|
SwingWorkerPropertyChangeSupport.super.firePropertyChange(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,24 +16,28 @@ public class ChecksumRow {
|
||||||
|
|
||||||
private HashMap<File, Checksum> checksumMap = new HashMap<File, Checksum>();
|
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 {
|
public static enum State {
|
||||||
OK, UNKNOWN, WARNING, ERROR;
|
OK,
|
||||||
|
WARNING,
|
||||||
|
ERROR,
|
||||||
|
UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChecksumRow(String name) {
|
public ChecksumRow(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
// look for a patter like [49A93C5F]
|
// look for a checksum pattern like [49A93C5F]
|
||||||
Pattern pattern = Pattern.compile(".*\\[(\\p{XDigit}{8})\\].*");
|
Matcher matcher = Pattern.compile("\\[(\\p{XDigit}{8})\\]").matcher(name);
|
||||||
Matcher matcher = pattern.matcher(getName());
|
|
||||||
|
|
||||||
if (matcher.matches()) {
|
if (matcher.find()) {
|
||||||
String checksumString = matcher.group(matcher.groupCount());
|
embeddedChecksum = Long.parseLong(matcher.group(1), 16);
|
||||||
checksumFromFileName = Long.parseLong(checksumString, 16);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +65,9 @@ public class ChecksumRow {
|
||||||
return State.ERROR;
|
return State.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checksums.isEmpty() && checksumFromFileName != null) {
|
if (!checksums.isEmpty() && embeddedChecksum != null) {
|
||||||
// check if the checksum in the filename matches
|
// check if the embedded checksum matches
|
||||||
if (!checksums.contains(checksumFromFileName))
|
if (!checksums.contains(embeddedChecksum))
|
||||||
return State.WARNING;
|
return State.WARNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,8 +75,8 @@ public class ChecksumRow {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Checksum getChecksum(File columnRoot) {
|
public Checksum getChecksum(File column) {
|
||||||
return checksumMap.get(columnRoot);
|
return checksumMap.get(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,13 +85,13 @@ public class ChecksumRow {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void putChecksum(File columnRoot, Checksum checksum) {
|
public void putChecksum(File column, Checksum checksum) {
|
||||||
checksumMap.put(columnRoot, checksum);
|
checksumMap.put(column, checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removeChecksum(File columnRoot) {
|
public void removeChecksum(File column) {
|
||||||
checksumMap.remove(columnRoot);
|
checksumMap.remove(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -20,12 +21,19 @@ import net.sourceforge.tuned.FileUtil;
|
||||||
|
|
||||||
class ChecksumTableModel extends AbstractTableModel {
|
class ChecksumTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
private List<ChecksumRow> rows = new ArrayList<ChecksumRow>();
|
private List<ChecksumRow> rows = new ArrayList<ChecksumRow>(50);
|
||||||
private Map<String, ChecksumRow> rowMap = new HashMap<String, ChecksumRow>();
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
|
@ -36,9 +44,11 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
if (columnIndex == 1)
|
if (columnIndex == 1)
|
||||||
return "Name";
|
return "Name";
|
||||||
|
|
||||||
if (columnIndex >= checksumColumnsOffset) {
|
if (columnIndex >= checksumColumnOffset) {
|
||||||
File columnRoot = checksumColumnRoots.get(columnIndex - checksumColumnsOffset);
|
File column = columns.get(columnIndex - checksumColumnOffset);
|
||||||
return FileUtil.getFolderName(columnRoot);
|
|
||||||
|
// works for files too and simply returns the name unchanged
|
||||||
|
return FileUtil.getFolderName(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -53,7 +63,7 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
if (columnIndex == 1)
|
if (columnIndex == 1)
|
||||||
return String.class;
|
return String.class;
|
||||||
|
|
||||||
if (columnIndex >= checksumColumnsOffset)
|
if (columnIndex >= checksumColumnOffset)
|
||||||
return Checksum.class;
|
return Checksum.class;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -61,12 +71,17 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
|
|
||||||
public int getColumnCount() {
|
public int getColumnCount() {
|
||||||
return checksumColumnsOffset + getChecksumColumnCount();
|
return checksumColumnOffset + getChecksumColumnCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int 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)
|
if (columnIndex == 1)
|
||||||
return row.getName();
|
return row.getName();
|
||||||
|
|
||||||
if (columnIndex >= checksumColumnsOffset) {
|
if (columnIndex >= checksumColumnOffset) {
|
||||||
File columnRoot = checksumColumnRoots.get(columnIndex - checksumColumnsOffset);
|
File column = columns.get(columnIndex - checksumColumnOffset);
|
||||||
return row.getChecksum(columnRoot);
|
return row.getChecksum(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized void addAll(List<ChecksumCell> list) {
|
public void addAll(List<ChecksumCell> list) {
|
||||||
int firstRow = getRowCount();
|
int firstRow = getRowCount();
|
||||||
|
|
||||||
for (ChecksumCell entry : list) {
|
for (ChecksumCell entry : list) {
|
||||||
addChecksum(entry.getName(), entry.getChecksum(), entry.getColumnRoot());
|
addChecksum(entry.getName(), entry.getChecksum(), entry.getColumn());
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastRow = getRowCount() - 1;
|
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);
|
ChecksumRow row = rowMap.get(name);
|
||||||
|
|
||||||
if (row == null) {
|
if (row == null) {
|
||||||
|
@ -117,17 +132,17 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
rowMap.put(name, row);
|
rowMap.put(name, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
row.putChecksum(columnRoot, checksum);
|
row.putChecksum(column, checksum);
|
||||||
checksum.addPropertyChangeListener(checksumListener);
|
checksum.addPropertyChangeListener(checksumListener);
|
||||||
|
|
||||||
if (!checksumColumnRoots.contains(columnRoot)) {
|
if (!columns.contains(column)) {
|
||||||
checksumColumnRoots.add(columnRoot);
|
columns.add(column);
|
||||||
fireTableStructureChanged();
|
fireTableStructureChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized void removeRows(int... rowIndices) {
|
public void removeRows(int... rowIndices) {
|
||||||
ArrayList<ChecksumRow> rowsToRemove = new ArrayList<ChecksumRow>(rowIndices.length);
|
ArrayList<ChecksumRow> rowsToRemove = new ArrayList<ChecksumRow>(rowIndices.length);
|
||||||
|
|
||||||
for (int i : rowIndices) {
|
for (int i : rowIndices) {
|
||||||
|
@ -137,37 +152,35 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
for (Checksum checksum : row.getChecksums()) {
|
for (Checksum checksum : row.getChecksums()) {
|
||||||
checksum.cancelComputationTask();
|
checksum.cancelComputationTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rowMap.remove(row.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.removeAll(rowsToRemove);
|
rows.removeAll(rowsToRemove);
|
||||||
fireTableRowsDeleted(rowIndices[0], rowIndices[rowIndices.length - 1]);
|
fireTableRowsDeleted(rowIndices[0], rowIndices[rowIndices.length - 1]);
|
||||||
|
|
||||||
ChecksumComputationService.getService().purge();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized void clear() {
|
public void clear() {
|
||||||
ChecksumComputationService.getService().reset();
|
columns.clear();
|
||||||
|
|
||||||
checksumColumnRoots.clear();
|
|
||||||
rows.clear();
|
rows.clear();
|
||||||
rowMap.clear();
|
rowMap.clear();
|
||||||
|
|
||||||
fireTableStructureChanged();
|
fireTableStructureChanged();
|
||||||
|
|
||||||
fireTableDataChanged();
|
fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public File getChecksumColumnRoot(int checksumColumnIndex) {
|
public File getChecksumColumn(int columnIndex) {
|
||||||
return checksumColumnRoots.get(checksumColumnIndex);
|
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>();
|
LinkedHashMap<String, Checksum> checksumMap = new LinkedHashMap<String, Checksum>();
|
||||||
|
|
||||||
for (ChecksumRow row : rows) {
|
for (ChecksumRow row : rows) {
|
||||||
Checksum checksum = row.getChecksum(columnRoot);
|
Checksum checksum = row.getChecksum(column);
|
||||||
|
|
||||||
if ((checksum != null) && (checksum.getState() == Checksum.State.READY)) {
|
if ((checksum != null) && (checksum.getState() == Checksum.State.READY)) {
|
||||||
checksumMap.put(row.getName(), checksum);
|
checksumMap.put(row.getName(), checksum);
|
||||||
|
@ -189,13 +202,13 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Checksum checksum;
|
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.name = name;
|
||||||
this.checksum = checksum;
|
this.checksum = checksum;
|
||||||
this.columnRoot = columnRoot;
|
this.column = column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -209,8 +222,8 @@ class ChecksumTableModel extends AbstractTableModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public File getColumnRoot() {
|
public File getColumn() {
|
||||||
return columnRoot;
|
return column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ package net.sourceforge.filebot.ui.panel.sfv;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
|
@ -32,7 +33,7 @@ public class SfvPanel extends FileBotPanel {
|
||||||
|
|
||||||
private SfvTable sfvTable = new SfvTable();
|
private SfvTable sfvTable = new SfvTable();
|
||||||
|
|
||||||
private TotalProgressPanel totalProgressPanel = new TotalProgressPanel();
|
private TotalProgressPanel totalProgressPanel = new TotalProgressPanel(sfvTable.getChecksumComputationService());
|
||||||
|
|
||||||
|
|
||||||
public SfvPanel() {
|
public SfvPanel() {
|
||||||
|
@ -61,85 +62,12 @@ public class SfvPanel extends FileBotPanel {
|
||||||
add(southPanel, BorderLayout.SOUTH);
|
add(southPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
// Shortcut DELETE
|
// 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 final SaveAction saveAction = new ChecksumTableSaveAction();
|
||||||
|
|
||||||
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 LoadAction loadAction = new LoadAction(sfvTable.getTransferablePolicy());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,6 @@
|
||||||
package net.sourceforge.filebot.ui.panel.sfv;
|
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.JTable;
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.event.TableModelEvent;
|
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.ChecksumTableCellRenderer;
|
||||||
import net.sourceforge.filebot.ui.panel.sfv.renderer.StateIconTableCellRenderer;
|
import net.sourceforge.filebot.ui.panel.sfv.renderer.StateIconTableCellRenderer;
|
||||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
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 SfvTransferablePolicy transferablePolicy;
|
||||||
|
private final ChecksumTableExportHandler exportHandler;
|
||||||
|
|
||||||
|
private final ChecksumComputationService checksumComputationService = new ChecksumComputationService();
|
||||||
|
|
||||||
|
|
||||||
public SfvTable() {
|
public SfvTable() {
|
||||||
ChecksumTableModel model = (ChecksumTableModel) getModel();
|
|
||||||
|
|
||||||
transferablePolicy = new SfvTransferablePolicy(model);
|
transferablePolicy = new SfvTransferablePolicy(getModel(), checksumComputationService);
|
||||||
|
exportHandler = new ChecksumTableExportHandler(getModel());
|
||||||
setModel(model);
|
|
||||||
|
|
||||||
setFillsViewportHeight(true);
|
setFillsViewportHeight(true);
|
||||||
setAutoCreateRowSorter(true);
|
setAutoCreateRowSorter(true);
|
||||||
|
@ -50,10 +36,7 @@ class SfvTable extends JTable implements Saveable {
|
||||||
|
|
||||||
setRowHeight(20);
|
setRowHeight(20);
|
||||||
|
|
||||||
ImportHandler importHandler = new TransferablePolicyImportHandler(transferablePolicy);
|
setTransferHandler(new DefaultTransferHandler(transferablePolicy, exportHandler));
|
||||||
ExportHandler exportHandler = new SaveableExportHandler(this);
|
|
||||||
|
|
||||||
setTransferHandler(new DefaultTransferHandler(importHandler, exportHandler));
|
|
||||||
setDragEnabled(true);
|
setDragEnabled(true);
|
||||||
|
|
||||||
setDefaultRenderer(ChecksumRow.State.class, new StateIconTableCellRenderer());
|
setDefaultRenderer(ChecksumRow.State.class, new StateIconTableCellRenderer());
|
||||||
|
@ -61,17 +44,39 @@ class SfvTable extends JTable implements Saveable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TransferablePolicy getTransferablePolicy() {
|
public SfvTransferablePolicy getTransferablePolicy() {
|
||||||
return transferablePolicy;
|
return transferablePolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ChecksumTableExportHandler getExportHandler() {
|
||||||
|
return exportHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ChecksumComputationService getChecksumComputationService() {
|
||||||
|
return checksumComputationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultTransferHandler getTransferHandler() {
|
||||||
|
return (DefaultTransferHandler) super.getTransferHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TableModel createDefaultDataModel() {
|
protected TableModel createDefaultDataModel() {
|
||||||
return new ChecksumTableModel();
|
return new ChecksumTableModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChecksumTableModel getModel() {
|
||||||
|
return (ChecksumTableModel) super.getModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createDefaultColumnsFromModel() {
|
public void createDefaultColumnsFromModel() {
|
||||||
super.createDefaultColumnsFromModel();
|
super.createDefaultColumnsFromModel();
|
||||||
|
@ -90,82 +95,30 @@ class SfvTable extends JTable implements Saveable {
|
||||||
|
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
|
checksumComputationService.reset();
|
||||||
transferablePolicy.reset();
|
transferablePolicy.reset();
|
||||||
|
|
||||||
((ChecksumTableModel) getModel()).clear();
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removeRows(int... rowIndices) {
|
public void removeRows(int... rowIndices) {
|
||||||
ChecksumTableModel model = (ChecksumTableModel) getModel();
|
getModel().removeRows(rowIndices);
|
||||||
model.removeRows(rowIndices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tableChanged(TableModelEvent e) {
|
public void tableChanged(TableModelEvent e) {
|
||||||
|
// only request repaint when progress changes, or selection will go haywire
|
||||||
if (e.getType() == ChecksumTableModelEvent.CHECKSUM_PROGRESS) {
|
if (e.getType() == ChecksumTableModelEvent.CHECKSUM_PROGRESS) {
|
||||||
repaint();
|
repaint();
|
||||||
} else {
|
} else {
|
||||||
super.tableChanged(e);
|
super.tableChanged(e);
|
||||||
|
|
||||||
|
if (e.getType() == TableModelEvent.DELETE) {
|
||||||
|
// remove cancelled task from queue
|
||||||
|
checksumComputationService.purge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,21 +14,24 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import net.sourceforge.filebot.FileBotUtil;
|
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> {
|
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.tableModel = tableModel;
|
||||||
|
this.checksumComputationService = checksumComputationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clear() {
|
protected void clear() {
|
||||||
|
checksumComputationService.reset();
|
||||||
tableModel.clear();
|
tableModel.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,11 +63,11 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
||||||
|
|
||||||
publish(new ChecksumTableModel.ChecksumCell(filename, new Checksum(checksumString), sfvFile));
|
publish(new ChecksumTableModel.ChecksumCell(filename, new Checksum(checksumString), sfvFile));
|
||||||
|
|
||||||
File compareColumnRoot = sfvFile.getParentFile();
|
File column = sfvFile.getParentFile();
|
||||||
File compareFile = new File(compareColumnRoot, filename);
|
File file = new File(column, filename);
|
||||||
|
|
||||||
if (compareFile.exists()) {
|
if (file.exists()) {
|
||||||
publish(new ChecksumTableModel.ChecksumCell(filename, ChecksumComputationService.getService().getChecksum(compareFile, compareColumnRoot), compareColumnRoot));
|
publish(new ChecksumTableModel.ChecksumCell(filename, checksumComputationService.schedule(file, column), column));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +80,7 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getFileFilterDescription() {
|
||||||
return "files, folders and sfv files";
|
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())
|
if (Thread.currentThread().isInterrupted())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -113,10 +116,10 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
||||||
// load all files in the file tree
|
// load all files in the file tree
|
||||||
String newPrefix = prefix + file.getName() + "/";
|
String newPrefix = prefix + file.getName() + "/";
|
||||||
for (File f : file.listFiles()) {
|
for (File f : file.listFiles()) {
|
||||||
load(f, columnRoot, newPrefix);
|
load(f, column, newPrefix);
|
||||||
}
|
}
|
||||||
} else if (file.isFile()) {
|
} 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,18 @@ class TotalProgressPanel extends Box {
|
||||||
|
|
||||||
private int millisToSetVisible = 200;
|
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);
|
super(BoxLayout.Y_AXIS);
|
||||||
|
|
||||||
// start invisible
|
this.checksumComputationService = checksumComputationService;
|
||||||
super.setVisible(false);
|
|
||||||
|
// invisible by default
|
||||||
|
setVisible(false);
|
||||||
|
|
||||||
progressBar.setStringPainted(true);
|
progressBar.setStringPainted(true);
|
||||||
progressBar.setBorderPainted(false);
|
progressBar.setBorderPainted(false);
|
||||||
|
@ -37,10 +41,11 @@ class TotalProgressPanel extends Box {
|
||||||
setBorder(BorderFactory.createCompoundBorder(margin, title));
|
setBorder(BorderFactory.createCompoundBorder(margin, title));
|
||||||
|
|
||||||
add(progressBar);
|
add(progressBar);
|
||||||
ChecksumComputationService.getService().addPropertyChangeListener(executorListener);
|
|
||||||
|
checksumComputationService.addPropertyChangeListener(progressListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyChangeListener executorListener = new PropertyChangeListener() {
|
private PropertyChangeListener progressListener = new PropertyChangeListener() {
|
||||||
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
|
||||||
|
@ -54,24 +59,23 @@ class TotalProgressPanel extends Box {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
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) {
|
} else if (property == ChecksumComputationService.REMAINING_TASK_COUNT_PROPERTY) {
|
||||||
|
|
||||||
int taskCount = ChecksumComputationService.getService().getActiveSessionTaskCount();
|
int taskCount = checksumComputationService.getActiveSessionTaskCount();
|
||||||
int progress = taskCount - ChecksumComputationService.getService().getRemainingTaskCount();
|
int progress = taskCount - checksumComputationService.getRemainingTaskCount();
|
||||||
|
|
||||||
progressBar.setValue(progress);
|
progressBar.setValue(progress);
|
||||||
progressBar.setMaximum(taskCount);
|
progressBar.setMaximum(taskCount);
|
||||||
|
|
||||||
progressBar.setString(progressBar.getValue() + " / " + progressBar.getMaximum());
|
progressBar.setString(progressBar.getValue() + " / " + progressBar.getMaximum());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ChecksumComputationService.getService().isActive()) {
|
|
||||||
setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ class Language implements Comparable<Language> {
|
||||||
private final Locale locale;
|
private final Locale locale;
|
||||||
|
|
||||||
private final String code;
|
private final String code;
|
||||||
private final ImageIcon icon;
|
private final Icon icon;
|
||||||
|
|
||||||
|
|
||||||
public Language(String languageName) {
|
public Language(String languageName) {
|
||||||
|
@ -57,13 +56,11 @@ class Language implements Comparable<Language> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj) {
|
if (this == obj)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
if (obj instanceof Language) {
|
if (obj instanceof Language)
|
||||||
return getName().equalsIgnoreCase(((Language) obj).getName());
|
return getName().equalsIgnoreCase(((Language) obj).getName());
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,12 @@ public class LanguageResolver {
|
||||||
* @return the locale for this language or null if no locale for this language exists
|
* @return the locale for this language or null if no locale for this language exists
|
||||||
*/
|
*/
|
||||||
public synchronized Locale getLocale(String languageName) {
|
public synchronized Locale getLocale(String languageName) {
|
||||||
languageName = languageName.toLowerCase();
|
|
||||||
|
|
||||||
Locale locale = cache.get(languageName);
|
Locale locale = cache.get(languageName.toLowerCase());
|
||||||
|
|
||||||
if (locale == null) {
|
if (locale == null) {
|
||||||
locale = findLocale(languageName);
|
locale = findLocale(languageName);
|
||||||
cache.put(languageName, locale);
|
cache.put(languageName.toLowerCase(), locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
return locale;
|
return locale;
|
||||||
|
@ -59,16 +58,17 @@ public class LanguageResolver {
|
||||||
/**
|
/**
|
||||||
* Find the {@link Locale} for a given language name.
|
* 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
|
* @return {@link Locale} for the given language, or null if no matching {@link Locale} is
|
||||||
* available
|
* available
|
||||||
*/
|
*/
|
||||||
private Locale findLocale(String languageName) {
|
private Locale findLocale(String languageName) {
|
||||||
for (Locale locale : Locale.getAvailableLocales()) {
|
for (Locale locale : Locale.getAvailableLocales()) {
|
||||||
if (locale.getDisplayLanguage(Locale.ENGLISH).toLowerCase().equals(languageName))
|
if (locale.getDisplayLanguage(Locale.ENGLISH).equalsIgnoreCase(languageName))
|
||||||
return locale;
|
return locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ import java.util.TreeMap;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
|
|
||||||
import net.sourceforge.filebot.Settings;
|
|
||||||
import ca.odell.glazedlists.EventList;
|
import ca.odell.glazedlists.EventList;
|
||||||
import ca.odell.glazedlists.FunctionList;
|
import ca.odell.glazedlists.FunctionList;
|
||||||
import ca.odell.glazedlists.ListSelection;
|
import ca.odell.glazedlists.ListSelection;
|
||||||
|
@ -31,13 +30,14 @@ public class LanguageSelectionPanel extends JPanel {
|
||||||
private final ListSelection<Language> selectionModel;
|
private final ListSelection<Language> selectionModel;
|
||||||
|
|
||||||
private final Map<String, Boolean> defaultSelection = new TreeMap<String, Boolean>(String.CASE_INSENSITIVE_ORDER);
|
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) {
|
public LanguageSelectionPanel(EventList<SubtitlePackage> source) {
|
||||||
super(new FlowLayout(FlowLayout.RIGHT, 5, 1));
|
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> languageList = new FunctionList<SubtitlePackage, Language>(source, new LanguageFunction());
|
||||||
EventList<Language> languageSet = new UniqueList<Language>(languageList);
|
EventList<Language> languageSet = new UniqueList<Language>(languageList);
|
||||||
|
@ -68,7 +68,7 @@ public class LanguageSelectionPanel extends JPanel {
|
||||||
String key = language.getName();
|
String key = language.getName();
|
||||||
|
|
||||||
defaultSelection.put(key, selected);
|
defaultSelection.put(key, selected);
|
||||||
globalSelection.put(key, selected);
|
// globalSelection.put(key, selected);
|
||||||
|
|
||||||
if (selected)
|
if (selected)
|
||||||
selectionModel.select(language);
|
selectionModel.select(language);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import javax.swing.SwingConstants;
|
||||||
|
|
||||||
import net.sourceforge.tuned.ui.ColorTintImageFilter;
|
import net.sourceforge.tuned.ui.ColorTintImageFilter;
|
||||||
import net.sourceforge.tuned.ui.IconViewCellRenderer;
|
import net.sourceforge.tuned.ui.IconViewCellRenderer;
|
||||||
|
import net.sourceforge.tuned.ui.TunedUtil;
|
||||||
|
|
||||||
|
|
||||||
public class SubtitleCellRenderer extends IconViewCellRenderer {
|
public class SubtitleCellRenderer extends IconViewCellRenderer {
|
||||||
|
@ -52,10 +53,10 @@ public class SubtitleCellRenderer extends IconViewCellRenderer {
|
||||||
|
|
||||||
info1.setIcon(icon);
|
info1.setIcon(icon);
|
||||||
|
|
||||||
ImageIcon icon = subtitle.getArchiveIcon();
|
Icon icon = subtitle.getArchiveIcon();
|
||||||
|
|
||||||
if (isSelected) {
|
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());
|
info1.setForeground(list.getSelectionForeground());
|
||||||
info2.setForeground(list.getSelectionForeground());
|
info2.setForeground(list.getSelectionForeground());
|
||||||
|
|
|
@ -4,6 +4,7 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.SwingWorker.StateValue;
|
import javax.swing.SwingWorker.StateValue;
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ public class SubtitlePackage extends AbstractBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ImageIcon getArchiveIcon() {
|
public Icon getArchiveIcon() {
|
||||||
return archiveIcon;
|
return archiveIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,9 +69,9 @@ public class SubtitlePackage extends AbstractBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized void download() {
|
public synchronized void startDownload() {
|
||||||
if (downloadTask != null)
|
if (downloadTask != null)
|
||||||
throw new IllegalStateException("Download has been started already");
|
throw new IllegalStateException("Download has already been started");
|
||||||
|
|
||||||
downloadTask = subtitleDescriptor.createDownloadTask();
|
downloadTask = subtitleDescriptor.createDownloadTask();
|
||||||
downloadTask.addPropertyChangeListener(new DownloadTaskPropertyChangeAdapter());
|
downloadTask.addPropertyChangeListener(new DownloadTaskPropertyChangeAdapter());
|
||||||
|
|
|
@ -8,7 +8,6 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import net.sourceforge.filebot.ListChangeSynchronizer;
|
|
||||||
import net.sourceforge.filebot.Settings;
|
import net.sourceforge.filebot.Settings;
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
import net.sourceforge.filebot.ui.AbstractSearchPanel;
|
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.SubsceneSubtitleClient;
|
||||||
import net.sourceforge.filebot.web.SubtitleClient;
|
import net.sourceforge.filebot.web.SubtitleClient;
|
||||||
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
||||||
|
import net.sourceforge.tuned.ListChangeSynchronizer;
|
||||||
import net.sourceforge.tuned.ui.LabelProvider;
|
import net.sourceforge.tuned.ui.LabelProvider;
|
||||||
import net.sourceforge.tuned.ui.SimpleLabelProvider;
|
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
|
@Override
|
||||||
protected FetchTask createFetchTask(SearchTask searchTask, SearchResult selectedSearchResult) {
|
protected FetchTask createFetchTask(SearchTask searchTask, SearchResult selectedSearchResult) {
|
||||||
return new SubtitleFetchTask(searchTask.getClient(), selectedSearchResult, searchTask.getTabPanel());
|
return new SubtitleFetchTask(searchTask.getClient(), selectedSearchResult, searchTask.getTabPanel());
|
||||||
|
@ -93,6 +87,13 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
|
||||||
return getClient().search(getSearchText());
|
return getClient().search(getSearchText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) throws Exception {
|
||||||
|
super.configureSelectDialog(selectDialog);
|
||||||
|
selectDialog.setText("Select a Show / Movie:");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
|
@ -94,16 +94,31 @@ public class DefaultTransferHandler extends TransferHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ImportHandler getImportHandler() {
|
||||||
|
return importHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setImportHandler(ImportHandler importHandler) {
|
public void setImportHandler(ImportHandler importHandler) {
|
||||||
this.importHandler = importHandler;
|
this.importHandler = importHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ExportHandler getExportHandler() {
|
||||||
|
return exportHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setExportHandler(ExportHandler exportHandler) {
|
public void setExportHandler(ExportHandler exportHandler) {
|
||||||
this.exportHandler = exportHandler;
|
this.exportHandler = exportHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ClipboardHandler getClipboardHandler() {
|
||||||
|
return clipboardHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setClipboardHandler(ClipboardHandler clipboardHandler) {
|
public void setClipboardHandler(ClipboardHandler clipboardHandler) {
|
||||||
this.clipboardHandler = clipboardHandler;
|
this.clipboardHandler = clipboardHandler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,11 @@ package net.sourceforge.filebot.ui.transfer;
|
||||||
|
|
||||||
|
|
||||||
import java.awt.datatransfer.Transferable;
|
import java.awt.datatransfer.Transferable;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -16,19 +19,31 @@ import net.sourceforge.filebot.Settings;
|
||||||
import net.sourceforge.tuned.TemporaryFolder;
|
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 SaveableExportHandler(Saveable saveable) {
|
public abstract void export(OutputStream out) throws IOException;
|
||||||
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
|
@Override
|
||||||
public int getSourceActions(JComponent c) {
|
public int getSourceActions(JComponent c) {
|
||||||
if ((saveable == null) || !saveable.isSaveable())
|
if (!canExport())
|
||||||
return TransferHandler.NONE;
|
return TransferHandler.NONE;
|
||||||
|
|
||||||
return TransferHandler.MOVE | TransferHandler.COPY;
|
return TransferHandler.MOVE | TransferHandler.COPY;
|
||||||
|
@ -38,11 +53,12 @@ public class SaveableExportHandler implements ExportHandler {
|
||||||
@Override
|
@Override
|
||||||
public Transferable createTransferable(JComponent c) {
|
public Transferable createTransferable(JComponent c) {
|
||||||
try {
|
try {
|
||||||
// Remove invalid characters from default filename
|
// remove invalid characters from file name
|
||||||
String name = FileBotUtil.validateFileName(saveable.getDefaultFileName());
|
String name = FileBotUtil.validateFileName(getDefaultFileName());
|
||||||
|
|
||||||
File temporaryFile = TemporaryFolder.getFolder(Settings.ROOT).createFile(name);
|
File temporaryFile = TemporaryFolder.getFolder(Settings.ROOT).createFile(name);
|
||||||
saveable.save(temporaryFile);
|
|
||||||
|
export(temporaryFile);
|
||||||
|
|
||||||
return new FileTransferable(temporaryFile);
|
return new FileTransferable(temporaryFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -58,4 +74,5 @@ public class SaveableExportHandler implements ExportHandler {
|
||||||
public void exportDone(JComponent source, Transferable data, int action) {
|
public void exportDone(JComponent source, Transferable data, int action) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -56,7 +56,6 @@ public class FileTransferable implements Transferable {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return line separated list of file uris
|
* @return line separated list of file uris
|
||||||
*/
|
*/
|
||||||
private String getUriList() {
|
private String getUriList() {
|
||||||
|
@ -72,7 +71,7 @@ public class FileTransferable implements Transferable {
|
||||||
|
|
||||||
|
|
||||||
public DataFlavor[] getTransferDataFlavors() {
|
public DataFlavor[] getTransferDataFlavors() {
|
||||||
return supportedFlavors;
|
return supportedFlavors.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import javax.swing.AbstractAction;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
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 {
|
public class LoadAction extends AbstractAction {
|
||||||
|
@ -18,6 +18,7 @@ public class LoadAction extends AbstractAction {
|
||||||
|
|
||||||
public LoadAction(TransferablePolicy transferablePolicy) {
|
public LoadAction(TransferablePolicy transferablePolicy) {
|
||||||
super("Load", ResourceManager.getIcon("action.load"));
|
super("Load", ResourceManager.getIcon("action.load"));
|
||||||
|
|
||||||
this.transferablePolicy = transferablePolicy;
|
this.transferablePolicy = transferablePolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,10 +36,14 @@ public class LoadAction extends AbstractAction {
|
||||||
|
|
||||||
FileTransferable transferable = new FileTransferable(chooser.getSelectedFiles());
|
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))
|
if (transferablePolicy.accept(transferable))
|
||||||
transferablePolicy.handleTransferable(transferable, add);
|
transferablePolicy.handleTransferable(transferable, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,12 @@ package net.sourceforge.filebot.ui.transfer;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.io.File;
|
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.AbstractAction;
|
||||||
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
|
|
||||||
import net.sourceforge.filebot.FileBotUtil;
|
import net.sourceforge.filebot.FileBotUtil;
|
||||||
|
@ -14,22 +18,32 @@ import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
|
|
||||||
public class SaveAction extends AbstractAction {
|
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"));
|
super("Save as ...", ResourceManager.getIcon("action.save"));
|
||||||
this.saveable = saveable;
|
this.exportHandler = exportHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void save(File file) {
|
protected SaveAction() {
|
||||||
saveable.save(file);
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected boolean canExport() {
|
||||||
|
return exportHandler.canExport();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void export(File file) throws IOException {
|
||||||
|
exportHandler.export(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getDefaultFileName() {
|
protected String getDefaultFileName() {
|
||||||
return saveable.getDefaultFileName();
|
return exportHandler.getDefaultFileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,18 +52,8 @@ public class SaveAction extends AbstractAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected boolean isSaveable() {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
return saveable.isSaveable();
|
if (!canExport())
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void setSaveable(Saveable saveable) {
|
|
||||||
this.saveable = saveable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
if (!isSaveable())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JFileChooser chooser = new JFileChooser();
|
JFileChooser chooser = new JFileChooser();
|
||||||
|
@ -58,10 +62,13 @@ public class SaveAction extends AbstractAction {
|
||||||
|
|
||||||
chooser.setSelectedFile(new File(getDefaultFolder(), FileBotUtil.validateFileName(getDefaultFileName())));
|
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;
|
return;
|
||||||
|
|
||||||
save(chooser.getSelectedFile());
|
try {
|
||||||
|
export(chooser.getSelectedFile());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,9 +6,6 @@ import java.io.File;
|
||||||
|
|
||||||
import javax.swing.filechooser.FileFilter;
|
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 {
|
public class TransferablePolicyFileFilter extends FileFilter {
|
||||||
|
@ -32,12 +29,10 @@ public class TransferablePolicyFileFilter extends FileFilter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
if (transferablePolicy instanceof CompositeTransferablePolicy) {
|
if (transferablePolicy instanceof FileTransferablePolicy) {
|
||||||
CompositeTransferablePolicy multi = (CompositeTransferablePolicy) transferablePolicy;
|
return ((FileTransferablePolicy) transferablePolicy).getFileFilterDescription();
|
||||||
return multi.getDescription(FileTransferablePolicy.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return transferablePolicy.getDescription();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -11,7 +11,6 @@ import java.net.URLEncoder;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
@ -29,9 +28,7 @@ import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class AnidbClient implements EpisodeListClient {
|
public class AnidbClient implements EpisodeListClient {
|
||||||
|
|
||||||
private final SearchResultCache searchResultCache = new SearchResultCache();
|
private static final String host = "anidb.net";
|
||||||
|
|
||||||
private final String host = "anidb.net";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,9 +45,6 @@ public class AnidbClient implements EpisodeListClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SearchResult> search(String searchterm) throws IOException, SAXException {
|
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));
|
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
|
||||||
|
|
||||||
|
@ -91,8 +85,6 @@ public class AnidbClient implements EpisodeListClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResultCache.addAll(searchResults);
|
|
||||||
|
|
||||||
return searchResults;
|
return searchResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +114,7 @@ public class AnidbClient implements EpisodeListClient {
|
||||||
number = numberFormat.format(Integer.parseInt(number));
|
number = numberFormat.format(Integer.parseInt(number));
|
||||||
|
|
||||||
// no seasons for anime
|
// no seasons for anime
|
||||||
episodes.add(new Episode(searchResult.getName(), null, number, title));
|
episodes.add(new Episode(searchResult.getName(), number, title));
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
// ignore node, episode is probably some kind of special (S1, S2, ...)
|
// ignore node, episode is probably some kind of special (S1, S2, ...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ package net.sourceforge.filebot.web;
|
||||||
|
|
||||||
public class Episode {
|
public class Episode {
|
||||||
|
|
||||||
private String showName;
|
private final String showName;
|
||||||
private String numberOfSeason;
|
private final String numberOfSeason;
|
||||||
private String numberOfEpisode;
|
private final String numberOfEpisode;
|
||||||
private String title;
|
private final String title;
|
||||||
|
|
||||||
|
|
||||||
public Episode(String showname, String numberOfSeason, String numberOfEpisode, String title) {
|
public Episode(String showname, String numberOfSeason, String numberOfEpisode, String title) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Matcher;
|
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 {
|
public static Document getHtmlDocument(URL url, Map<String, String> requestHeaders) throws IOException, SAXException {
|
||||||
URLConnection connection = url.openConnection();
|
URLConnection connection = url.openConnection();
|
||||||
|
|
||||||
for (String key : requestHeaders.keySet()) {
|
for (Entry<String, String> entry : requestHeaders.entrySet()) {
|
||||||
connection.addRequestProperty(key, requestHeaders.get(key));
|
connection.addRequestProperty(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return getHtmlDocument(connection);
|
return getHtmlDocument(connection);
|
||||||
|
|
|
@ -9,8 +9,6 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -21,23 +19,10 @@ import redstone.xmlrpc.XmlRpcFault;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client for the OpenSubtitles XML-RPC API.
|
* Client for the OpenSubtitles XML-RPC API.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class OpenSubtitlesClient {
|
public class OpenSubtitlesClient {
|
||||||
|
|
||||||
/**
|
private static final String url = "http://www.opensubtitles.org/xml-rpc";
|
||||||
* <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 final String useragent;
|
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 username
|
||||||
* @param password
|
* @param password
|
||||||
|
@ -77,8 +62,9 @@ public class OpenSubtitlesClient {
|
||||||
*
|
*
|
||||||
* @param username username (blank for anonymous user)
|
* @param username username (blank for anonymous user)
|
||||||
* @param password password (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>
|
* @param language <a
|
||||||
* 2 letter codes as language and later communication will be done in this
|
* 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).
|
* language if applicable (error codes and so on).
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@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, ...)
|
* @param status status code and message (e.g. 200 OK, 401 Unauthorized, ...)
|
||||||
* @throws XmlRpcFault thrown if status code is not OK
|
* @throws XmlRpcFault thrown if status code is not OK
|
||||||
|
@ -139,9 +125,7 @@ public class OpenSubtitlesClient {
|
||||||
XmlRpcClient rpc = new XmlRpcClient(url, false);
|
XmlRpcClient rpc = new XmlRpcClient(url, false);
|
||||||
return rpc.invoke(method, arguments);
|
return rpc.invoke(method, arguments);
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
// will never happen
|
throw new RuntimeException(e);
|
||||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, "Invalid xml-rpc url: " + url, e);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,15 +153,16 @@ public class OpenSubtitlesClient {
|
||||||
|
|
||||||
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, searchList);
|
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))
|
try {
|
||||||
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")) {
|
for (Map<String, String> subtitle : response.get("data")) {
|
||||||
subs.add(new OpenSubtitlesSubtitleDescriptor(subtitle));
|
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;
|
return subs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,14 @@ public class OpenSubtitlesHasher {
|
||||||
|
|
||||||
FileChannel fileChannel = new FileInputStream(file).getChannel();
|
FileChannel fileChannel = new FileInputStream(file).getChannel();
|
||||||
|
|
||||||
|
try {
|
||||||
long head = computeHashForChunk(fileChannel, 0, chunkSizeForFile);
|
long head = computeHashForChunk(fileChannel, 0, chunkSizeForFile);
|
||||||
long tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile);
|
long tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile);
|
||||||
|
|
||||||
fileChannel.close();
|
|
||||||
|
|
||||||
return String.format("%016x", size + head + tail);
|
return String.format("%016x", size + head + tail);
|
||||||
|
} finally {
|
||||||
|
fileChannel.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link SubtitleClient} for OpenSubtitles.
|
* {@link SubtitleClient} for OpenSubtitles.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class OpenSubtitlesSubtitleClient implements SubtitleClient {
|
public class OpenSubtitlesSubtitleClient implements SubtitleClient {
|
||||||
|
|
||||||
|
@ -102,7 +101,7 @@ public class OpenSubtitlesSubtitleClient implements SubtitleClient {
|
||||||
|
|
||||||
private class LogoutTimer {
|
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 Timer daemon = null;
|
||||||
private LogoutTimerTask currentTimerTask = null;
|
private LogoutTimerTask currentTimerTask = null;
|
||||||
|
|
|
@ -4,6 +4,7 @@ package net.sourceforge.filebot.web;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
@ -58,7 +59,7 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor {
|
||||||
|
|
||||||
|
|
||||||
public OpenSubtitlesSubtitleDescriptor(Map<String, String> properties) {
|
public OpenSubtitlesSubtitleDescriptor(Map<String, String> properties) {
|
||||||
this.properties = properties;
|
this.properties = new HashMap<String, String>(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -9,7 +9,6 @@ import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -34,12 +33,10 @@ import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class SubsceneSubtitleClient implements SubtitleClient {
|
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 Map<String, Integer> languageFilterMap = new ConcurrentHashMap<String, Integer>(50);
|
||||||
|
|
||||||
private final String host = "subscene.com";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -55,9 +52,6 @@ public class SubsceneSubtitleClient implements SubtitleClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SearchResult> search(String searchterm) throws IOException, SAXException {
|
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));
|
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
|
||||||
|
|
||||||
|
@ -103,8 +97,6 @@ public class SubsceneSubtitleClient implements SubtitleClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResultCache.addAll(searchResults);
|
|
||||||
|
|
||||||
return searchResults;
|
return searchResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +216,7 @@ public class SubsceneSubtitleClient implements SubtitleClient {
|
||||||
Matcher matcher = hrefPattern.matcher(href);
|
Matcher matcher = hrefPattern.matcher(href);
|
||||||
|
|
||||||
if (!matcher.matches())
|
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 subtitleId = matcher.group(1);
|
||||||
String typeId = matcher.group(2);
|
String typeId = matcher.group(2);
|
||||||
|
|
|
@ -10,7 +10,6 @@ import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
@ -33,9 +32,7 @@ import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class TVDotComClient implements EpisodeListClient {
|
public class TVDotComClient implements EpisodeListClient {
|
||||||
|
|
||||||
private final SearchResultCache searchResultCache = new SearchResultCache();
|
private static final String host = "www.tv.com";
|
||||||
|
|
||||||
private final String host = "www.tv.com";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -58,9 +55,6 @@ public class TVDotComClient implements EpisodeListClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SearchResult> search(String searchterm) throws IOException, SAXException {
|
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));
|
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
|
||||||
|
|
||||||
|
@ -81,8 +75,6 @@ public class TVDotComClient implements EpisodeListClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResultCache.addAll(searchResults);
|
|
||||||
|
|
||||||
return searchResults;
|
return searchResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +91,7 @@ public class TVDotComClient implements EpisodeListClient {
|
||||||
List<Future<List<Episode>>> futures = new ArrayList<Future<List<Episode>>>(seasonCount);
|
List<Future<List<Episode>>> futures = new ArrayList<Future<List<Episode>>>(seasonCount);
|
||||||
|
|
||||||
if (seasonCount > 1) {
|
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));
|
ExecutorService executor = Executors.newFixedThreadPool(Math.min(seasonCount - 1, 12));
|
||||||
|
|
||||||
// we already have the document for season 1, start with season 2
|
// we already have the document for season 1, start with season 2
|
||||||
|
@ -111,7 +103,7 @@ public class TVDotComClient implements EpisodeListClient {
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Episode> episodes = new ArrayList<Episode>(150);
|
List<Episode> episodes = new ArrayList<Episode>(25 * seasonCount);
|
||||||
|
|
||||||
// get episode list from season 1 document
|
// get episode list from season 1 document
|
||||||
episodes.addAll(getEpisodeList(searchResult, 1, dom));
|
episodes.addAll(getEpisodeList(searchResult, 1, dom));
|
||||||
|
|
|
@ -6,7 +6,6 @@ import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
@ -23,9 +22,7 @@ import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class TVRageClient implements EpisodeListClient {
|
public class TVRageClient implements EpisodeListClient {
|
||||||
|
|
||||||
private final SearchResultCache searchResultCache = new SearchResultCache();
|
private static final String host = "www.tvrage.com";
|
||||||
|
|
||||||
private final String host = "www.tvrage.com";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,9 +45,6 @@ public class TVRageClient implements EpisodeListClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SearchResult> search(String searchterm) throws SAXException, IOException, ParserConfigurationException {
|
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"));
|
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));
|
searchResults.add(new TVRageSearchResult(name, showid, link));
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResultCache.addAll(searchResults);
|
|
||||||
|
|
||||||
return searchResults;
|
return searchResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ public class DownloadTask extends SwingWorker<ByteBuffer, Void> {
|
||||||
DONE
|
DONE
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int BUFFER_SIZE = 4 * 1024;
|
private static final int BUFFER_SIZE = 4 * 1024;
|
||||||
|
|
||||||
private URL url;
|
private URL url;
|
||||||
private ByteBuffer postdata;
|
private ByteBuffer postdata;
|
||||||
|
@ -175,15 +176,15 @@ public class DownloadTask extends SwingWorker<ByteBuffer, Void> {
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (String key : parameters.keySet()) {
|
for (Entry<String, String> entry : parameters.entrySet()) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
sb.append("&");
|
sb.append("&");
|
||||||
|
|
||||||
sb.append(key);
|
sb.append(entry.getKey());
|
||||||
sb.append("=");
|
sb.append("=");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sb.append(URLEncoder.encode(parameters.get(key), "UTF-8"));
|
sb.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
// will never happen
|
// will never happen
|
||||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||||
|
|
|
@ -16,9 +16,9 @@ public class FileUtil {
|
||||||
|
|
||||||
public static String formatSize(long size) {
|
public static String formatSize(long size) {
|
||||||
if (size >= MEGA)
|
if (size >= MEGA)
|
||||||
return String.format("%d MB", (double) size / MEGA);
|
return String.format("%d MB", size / MEGA);
|
||||||
else if (size >= KILO)
|
else if (size >= KILO)
|
||||||
return String.format("%d KB", (double) size / KILO);
|
return String.format("%d KB", size / KILO);
|
||||||
else
|
else
|
||||||
return String.format("%d Byte", size);
|
return String.format("%d Byte", size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot;
|
package net.sourceforge.tuned;
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -9,7 +9,6 @@ import ca.odell.glazedlists.event.ListEvent;
|
||||||
import ca.odell.glazedlists.event.ListEventListener;
|
import ca.odell.glazedlists.event.ListEventListener;
|
||||||
|
|
||||||
|
|
||||||
//TODO: testcase, class doc
|
|
||||||
public class ListChangeSynchronizer<E> implements ListEventListener<E> {
|
public class ListChangeSynchronizer<E> implements ListEventListener<E> {
|
||||||
|
|
||||||
private final List<E> target;
|
private final List<E> target;
|
|
@ -19,19 +19,7 @@ public class MessageBus {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, List<MessageHandler>> handlers = new HashMap<String, List<MessageHandler>>() {
|
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 MessageBus() {
|
private MessageBus() {
|
||||||
|
@ -40,11 +28,12 @@ public class MessageBus {
|
||||||
|
|
||||||
|
|
||||||
public synchronized void addMessageHandler(String topic, MessageHandler handler) {
|
public synchronized void addMessageHandler(String topic, MessageHandler handler) {
|
||||||
List<MessageHandler> list = handlers.get(topic);
|
|
||||||
|
List<MessageHandler> list = handlers.get(topic.toLowerCase());
|
||||||
|
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
list = new ArrayList<MessageHandler>(3);
|
list = new ArrayList<MessageHandler>(3);
|
||||||
handlers.put(topic, list);
|
handlers.put(topic.toLowerCase(), list);
|
||||||
}
|
}
|
||||||
|
|
||||||
list.add(handler);
|
list.add(handler);
|
||||||
|
@ -52,7 +41,7 @@ public class MessageBus {
|
||||||
|
|
||||||
|
|
||||||
public synchronized void removeMessageHandler(String topic, MessageHandler handler) {
|
public synchronized void removeMessageHandler(String topic, MessageHandler handler) {
|
||||||
List<MessageHandler> list = handlers.get(topic);
|
List<MessageHandler> list = handlers.get(topic.toLowerCase());
|
||||||
|
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
list.remove(handler);
|
list.remove(handler);
|
||||||
|
@ -61,7 +50,7 @@ public class MessageBus {
|
||||||
|
|
||||||
|
|
||||||
public synchronized MessageHandler[] getHandlers(String topic) {
|
public synchronized MessageHandler[] getHandlers(String topic) {
|
||||||
List<MessageHandler> list = handlers.get(topic);
|
List<MessageHandler> list = handlers.get(topic.toLowerCase());
|
||||||
|
|
||||||
if (list == null)
|
if (list == null)
|
||||||
return new MessageHandler[0];
|
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() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
for (MessageHandler handler : getHandlers(topic)) {
|
for (MessageHandler handler : getHandlers(topic.toLowerCase())) {
|
||||||
handler.handle(topic, messages);
|
handler.handle(topic.toLowerCase(), messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
package net.sourceforge.tuned;
|
package net.sourceforge.tuned;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public interface MessageHandler {
|
public interface MessageHandler {
|
||||||
|
|
||||||
public void handle(String topic, String... messages);
|
public void handle(String topic, Object... messages);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,14 @@ public class PreferencesList<T> extends AbstractList<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO: assert invalid index
|
|
||||||
@Override
|
@Override
|
||||||
public void add(int index, T element) {
|
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);
|
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) {
|
public static <T> PreferencesList<T> map(Preferences prefs, Class<T> type) {
|
||||||
return new PreferencesList<T>(PreferencesMap.map(prefs, 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) {
|
public static <T> PreferencesList<T> map(Preferences prefs, Adapter<T> adapter) {
|
||||||
return new PreferencesList<T>(PreferencesMap.map(prefs, adapter));
|
return new PreferencesList<T>(PreferencesMap.map(prefs, adapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,12 +81,6 @@ public class PreferencesMap<T> implements Map<String, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void set(Map<String, T> data) {
|
|
||||||
clear();
|
|
||||||
putAll(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) {
|
public boolean containsKey(Object key) {
|
||||||
if (key instanceof String) {
|
if (key instanceof String) {
|
||||||
|
@ -134,8 +128,8 @@ public class PreferencesMap<T> implements Map<String, T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putAll(Map<? extends String, ? extends T> map) {
|
public void putAll(Map<? extends String, ? extends T> map) {
|
||||||
for (String key : map.keySet()) {
|
for (Map.Entry<? extends String, ? extends T> entry : map.entrySet()) {
|
||||||
put(key, map.get(key));
|
put(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@ public class TemporaryFolder {
|
||||||
|
|
||||||
public static TemporaryFolder getFolder(String name) {
|
public static TemporaryFolder getFolder(String name) {
|
||||||
synchronized (folders) {
|
synchronized (folders) {
|
||||||
TemporaryFolder folder = folders.get(name);
|
TemporaryFolder folder = folders.get(name.toLowerCase());
|
||||||
|
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
folder = new TemporaryFolder(new File(tmpdir, name));
|
folder = new TemporaryFolder(new File(tmpdir, name));
|
||||||
folders.put(name, folder);
|
folders.put(name.toLowerCase(), folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
return folder;
|
return folder;
|
||||||
|
@ -63,14 +63,12 @@ public class TemporaryFolder {
|
||||||
* @throws IOException if an I/O error occurred
|
* @throws IOException if an I/O error occurred
|
||||||
*/
|
*/
|
||||||
public File createFile(String name) throws IOException {
|
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();
|
file.createNewFile();
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,16 +85,12 @@ public class TemporaryFolder {
|
||||||
* @see File#createTempFile(String, String)
|
* @see File#createTempFile(String, String)
|
||||||
*/
|
*/
|
||||||
public File createFile(String prefix, String suffix) throws IOException {
|
public File createFile(String prefix, String suffix) throws IOException {
|
||||||
if (!root.exists()) {
|
return File.createTempFile(prefix, suffix, getFolder());
|
||||||
root.mkdir();
|
|
||||||
}
|
|
||||||
|
|
||||||
return File.createTempFile(prefix, suffix, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean deleteFile(String name) {
|
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}
|
* @return the {@link File} object for this {@link TemporaryFolder}
|
||||||
*/
|
*/
|
||||||
public File getFolder() {
|
public File getFolder() {
|
||||||
|
if (!root.exists())
|
||||||
|
root.mkdirs();
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TemporaryFolder createFolder(String name) {
|
public TemporaryFolder createFolder(String name) {
|
||||||
if (!root.exists()) {
|
return new TemporaryFolder(new File(getFolder(), name));
|
||||||
root.mkdir();
|
|
||||||
}
|
|
||||||
|
|
||||||
TemporaryFolder folder = new TemporaryFolder(new File(root, name));
|
|
||||||
|
|
||||||
folder.root.mkdir();
|
|
||||||
|
|
||||||
return folder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,10 +13,12 @@ import java.awt.Insets;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
|
||||||
|
import javax.swing.DefaultListModel;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.ListModel;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.border.CompoundBorder;
|
import javax.swing.border.CompoundBorder;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
@ -24,8 +26,10 @@ import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
public class IconViewPanel extends JPanel {
|
public class IconViewPanel extends JPanel {
|
||||||
|
|
||||||
private final JList list = new JList(new SimpleListModel());
|
private final JList list = new JList(createModel());
|
||||||
private final JLabel title = new JLabel();
|
|
||||||
|
private final JLabel titleLabel = new JLabel();
|
||||||
|
|
||||||
private final JPanel headerPanel = new JPanel(new BorderLayout());
|
private final JPanel headerPanel = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,10 +39,10 @@ public class IconViewPanel extends JPanel {
|
||||||
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
|
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
|
||||||
list.setVisibleRowCount(-1);
|
list.setVisibleRowCount(-1);
|
||||||
|
|
||||||
title.setFont(title.getFont().deriveFont(Font.BOLD));
|
titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD));
|
||||||
|
|
||||||
headerPanel.setOpaque(false);
|
headerPanel.setOpaque(false);
|
||||||
headerPanel.add(title, BorderLayout.WEST);
|
headerPanel.add(titleLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
setBackground(list.getBackground());
|
setBackground(list.getBackground());
|
||||||
|
|
||||||
|
@ -53,13 +57,23 @@ public class IconViewPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected ListModel createModel() {
|
||||||
|
return new DefaultListModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public JPanel getHeaderPanel() {
|
public JPanel getHeaderPanel() {
|
||||||
return headerPanel;
|
return headerPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setTitle(String text) {
|
public void setTitle(String text) {
|
||||||
title.setText(text);
|
titleLabel.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return titleLabel.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class ProgressDialog extends JDialog {
|
||||||
setLocation(TunedUtil.getPreferredLocation(this));
|
setLocation(TunedUtil.getPreferredLocation(this));
|
||||||
|
|
||||||
// Shortcut Escape
|
// Shortcut Escape
|
||||||
TunedUtil.registerActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
TunedUtil.putActionForKeystroke(c, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,25 +17,25 @@ import javax.swing.KeyStroke;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComboBoxUI;
|
import javax.swing.plaf.ComboBoxUI;
|
||||||
import javax.swing.plaf.basic.BasicComboBoxUI;
|
import javax.swing.plaf.basic.BasicComboBoxUI;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
|
||||||
import net.sourceforge.filebot.resources.ResourceManager;
|
import net.sourceforge.filebot.resources.ResourceManager;
|
||||||
|
|
||||||
|
|
||||||
public class SelectButtonTextField<T> extends JPanel {
|
public class SelectButtonTextField<T> extends JPanel {
|
||||||
|
|
||||||
private SelectButton<T> selectButton;
|
private SelectButton<T> selectButton = new SelectButton<T>();
|
||||||
|
|
||||||
private ComboBoxTextField editor = new ComboBoxTextField();
|
private ComboBoxTextField editor = new ComboBoxTextField();
|
||||||
|
|
||||||
private Color borderColor = new Color(0xA4A4A4);
|
|
||||||
|
|
||||||
|
|
||||||
public SelectButtonTextField() {
|
public SelectButtonTextField() {
|
||||||
setLayout(new BorderLayout(0, 0));
|
setLayout(new BorderLayout(0, 0));
|
||||||
|
|
||||||
selectButton = new SelectButton<T>();
|
|
||||||
selectButton.addActionListener(textFieldFocusOnClick);
|
selectButton.addActionListener(textFieldFocusOnClick);
|
||||||
|
|
||||||
|
Color borderColor = new Color(0xA4A4A4);
|
||||||
|
|
||||||
Border lineBorder = BorderFactory.createLineBorder(borderColor, 1);
|
Border lineBorder = BorderFactory.createLineBorder(borderColor, 1);
|
||||||
Border matteBorder = BorderFactory.createMatteBorder(1, 0, 1, 1, borderColor);
|
Border matteBorder = BorderFactory.createMatteBorder(1, 0, 1, 1, borderColor);
|
||||||
Border emptyBorder = BorderFactory.createEmptyBorder(0, 3, 0, 3);
|
Border emptyBorder = BorderFactory.createEmptyBorder(0, 3, 0, 3);
|
||||||
|
@ -48,8 +48,8 @@ public class SelectButtonTextField<T> extends JPanel {
|
||||||
|
|
||||||
setPreferredSize(new Dimension(280, 22));
|
setPreferredSize(new Dimension(280, 22));
|
||||||
|
|
||||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
|
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
|
||||||
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), 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>
|
* Convenience method for <code>getEditor().getSelectedItem().toString()</code>
|
||||||
*/
|
*/
|
||||||
public String getText() {
|
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
|
// 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,13 +145,47 @@ public class SelectButtonTextField<T> extends JPanel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JButton createArrowButton() {
|
protected JButton createArrowButton() {
|
||||||
JButton b = new JButton(ResourceManager.getIcon("action.list"));
|
return new JButton(ResourceManager.getIcon("action.list"));
|
||||||
|
}
|
||||||
|
|
||||||
b.setContentAreaFilled(false);
|
|
||||||
b.setFocusable(false);
|
@Override
|
||||||
|
public void configureArrowButton() {
|
||||||
return b;
|
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);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package net.sourceforge.tuned.ui;
|
||||||
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
@ -15,11 +16,11 @@ import net.sourceforge.tuned.ExceptionUtil;
|
||||||
public class SimpleLabelProvider<T> implements LabelProvider<T> {
|
public class SimpleLabelProvider<T> implements LabelProvider<T> {
|
||||||
|
|
||||||
private final Method getIconMethod;
|
private final Method getIconMethod;
|
||||||
private final Method getNameMethod;
|
private final Method getTextMethod;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as <code>new SimpleLabelProvider<T>(T.class)</code>.
|
* Factory method for {@link #SimpleLabelProvider(Class)}.
|
||||||
*
|
*
|
||||||
* @return new <code>LabelProvider</code>
|
* @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
|
* Create a new LabelProvider which will use the <code>getText</code>, <code>getName</code>
|
||||||
* <code>getIcon</code> method of the given class.
|
* 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) {
|
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
|
* Create a new LabelProvider which will use a specified method of a given class
|
||||||
*
|
*
|
||||||
* @param type a class with the specified method
|
* @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>
|
* @param getIcon a method name such as <code>getIcon</code>
|
||||||
*/
|
*/
|
||||||
public SimpleLabelProvider(Class<T> type, String getName, String getIcon) {
|
public SimpleLabelProvider(Class<T> type, String getText, String getIcon) {
|
||||||
try {
|
getTextMethod = findAnyMethod(type, getText);
|
||||||
getNameMethod = type.getMethod(getName);
|
getIconMethod = findAnyMethod(type, getIcon);
|
||||||
getIconMethod = type.getMethod(getIcon);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public String getText(T value) {
|
public String getText(T value) {
|
||||||
try {
|
try {
|
||||||
return (String) getNameMethod.invoke(value);
|
return (String) getTextMethod.invoke(value);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw ExceptionUtil.asRuntimeException(e);
|
throw ExceptionUtil.asRuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -13,20 +13,24 @@ import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
|
|
||||||
|
|
||||||
public class TunedUtil {
|
public class TunedUtil {
|
||||||
|
|
||||||
private TunedUtil() {
|
public static void checkEventDispatchThread() {
|
||||||
// hide constructor
|
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();
|
Integer key = action.hashCode();
|
||||||
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key);
|
component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keystroke, key);
|
||||||
component.getActionMap().put(key, action);
|
component.getActionMap().put(key, action);
|
||||||
|
@ -47,10 +51,9 @@ public class TunedUtil {
|
||||||
|
|
||||||
|
|
||||||
public static Image getImage(Icon icon) {
|
public static Image getImage(Icon icon) {
|
||||||
//TODO uncomment
|
if (icon instanceof ImageIcon) {
|
||||||
// if (icon instanceof ImageIcon) {
|
return ((ImageIcon) icon).getImage();
|
||||||
// return ((ImageIcon) icon).getImage();
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
|
BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||||
|
|
||||||
|
@ -78,4 +81,9 @@ public class TunedUtil {
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TunedUtil() {
|
||||||
|
// hide constructor
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,10 @@ package net.sourceforge.tuned.ui.notification;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
|
|
||||||
class Factor extends Object implements SwingConstants {
|
class Factor implements SwingConstants {
|
||||||
|
|
||||||
public double fx = 0;
|
public final double fx;
|
||||||
public double fy = 0;
|
public final double fy;
|
||||||
|
|
||||||
|
|
||||||
public Factor() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Factor(double fx, double fy) {
|
public Factor(double fx, double fy) {
|
||||||
|
|
|
@ -6,13 +6,15 @@
|
||||||
package net.sourceforge.tuned.ui.notification;
|
package net.sourceforge.tuned.ui.notification;
|
||||||
|
|
||||||
|
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
|
import net.sourceforge.tuned.ui.TunedUtil;
|
||||||
|
|
||||||
|
|
||||||
public class NotificationManager {
|
public class NotificationManager {
|
||||||
|
|
||||||
private NotificationLayout layout;
|
private final NotificationLayout layout;
|
||||||
|
|
||||||
|
|
||||||
public NotificationManager() {
|
public NotificationManager() {
|
||||||
|
@ -21,45 +23,25 @@ public class NotificationManager {
|
||||||
|
|
||||||
|
|
||||||
public NotificationManager(NotificationLayout layout) {
|
public NotificationManager(NotificationLayout layout) {
|
||||||
setLayoutManager(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setLayoutManager(NotificationLayout layout) {
|
|
||||||
this.layout = layout;
|
this.layout = layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public NotificationLayout getLayoutManager() {
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void show(NotificationWindow notification) {
|
public void show(NotificationWindow notification) {
|
||||||
if (layout == null)
|
TunedUtil.checkEventDispatchThread();
|
||||||
return;
|
|
||||||
|
|
||||||
notification.addComponentListener(new RemoveListener(layout));
|
notification.addWindowListener(new RemoveListener());
|
||||||
layout.add(notification);
|
layout.add(notification);
|
||||||
|
|
||||||
notification.setVisible(true);
|
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
|
@Override
|
||||||
public void componentHidden(ComponentEvent e) {
|
public void windowClosing(WindowEvent e) {
|
||||||
NotificationWindow window = (NotificationWindow) e.getSource();
|
layout.remove((NotificationWindow) e.getWindow());
|
||||||
layout.remove(window);
|
|
||||||
window.dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.awt.event.ComponentEvent;
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
import javax.swing.JWindow;
|
import javax.swing.JWindow;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
|
@ -21,7 +22,7 @@ import net.sourceforge.tuned.ui.TunedUtil;
|
||||||
|
|
||||||
public class NotificationWindow extends JWindow {
|
public class NotificationWindow extends JWindow {
|
||||||
|
|
||||||
private int timeout;
|
private final int timeout;
|
||||||
|
|
||||||
|
|
||||||
public NotificationWindow(Window owner, int timeout) {
|
public NotificationWindow(Window owner, int timeout) {
|
||||||
|
@ -35,17 +36,12 @@ public class NotificationWindow extends JWindow {
|
||||||
|
|
||||||
setAlwaysOnTop(true);
|
setAlwaysOnTop(true);
|
||||||
|
|
||||||
if (closeOnClick)
|
if (closeOnClick) {
|
||||||
getGlassPane().addMouseListener(clickListener);
|
getGlassPane().addMouseListener(clickListener);
|
||||||
|
|
||||||
getGlassPane().setVisible(true);
|
getGlassPane().setVisible(true);
|
||||||
|
|
||||||
addComponentListener(visibleListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addComponentListener(closeOnTimeout);
|
||||||
public NotificationWindow(int timeout) {
|
|
||||||
this((Window) null, timeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,21 +50,23 @@ public class NotificationWindow extends JWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public NotificationWindow() {
|
|
||||||
this((Window) null, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public final void close() {
|
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);
|
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));
|
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
|
@Override
|
||||||
|
@ -94,7 +92,7 @@ public class NotificationWindow extends JWindow {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private MouseAdapter clickListener = new MouseAdapter() {
|
private final MouseAdapter clickListener = new MouseAdapter() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
|
|
@ -29,7 +29,10 @@ public class SeparatorBorder extends AbstractBorder {
|
||||||
|
|
||||||
|
|
||||||
public static enum Position {
|
public static enum Position {
|
||||||
TOP, BOTTOM, LEFT, RIGHT;
|
TOP,
|
||||||
|
BOTTOM,
|
||||||
|
LEFT,
|
||||||
|
RIGHT;
|
||||||
|
|
||||||
public Rectangle2D getRectangle(RectangularShape shape, int borderWidth) {
|
public Rectangle2D getRectangle(RectangularShape shape, int borderWidth) {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
@ -72,10 +75,6 @@ public class SeparatorBorder extends AbstractBorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected SeparatorBorder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public SeparatorBorder(int height, Color color, Position position) {
|
public SeparatorBorder(int height, Color color, Position position) {
|
||||||
this(height, color, null, null, position);
|
this(height, color, null, null, position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||||
|
|
||||||
|
|
||||||
@RunWith(Suite.class)
|
@RunWith(Suite.class)
|
||||||
@SuiteClasses( { MatcherTestSuite.class, WebTestSuite.class })
|
@SuiteClasses( { MatcherTestSuite.class, WebTestSuite.class, ArgumentBeanTest.class })
|
||||||
public class FileBotTestSuite {
|
public class FileBotTestSuite {
|
||||||
|
|
||||||
public static Test suite() {
|
public static Test suite() {
|
||||||
|
|
Loading…
Reference in New Issue