* refactored and simplified transfer api

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

View File

@ -1,6 +1,6 @@
<?xml version="1.0"?> <?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" value="${dir.lib}/xercesImpl.jar" /> <property name="lib.xerces" location="${dir.lib}/xercesImpl.jar" />
<property name="lib.nekohtml" value="${dir.lib}/nekohtml-1.9.7.jar" /> <property name="lib.nekohtml" location="${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.simmetrics" location="${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.xmlrpc" location="${dir.lib}/xmlrpc-client-1.1.jar" />
<property name="lib.glazedlists" value="${dir.lib}/glazedlists-1.7.0_java15.jar" /> <property name="lib.glazedlists" location="${dir.lib}/glazedlists-1.7.0_java15.jar" />
<property name="lib.junit" value="${dir.lib}/junit-4.4.jar" /> <property name="lib.args4j" location="${dir.lib}/args4j-2.0.9.jar" />
<property name="lib.junit" location="${dir.lib}/junit-4.4.jar" />
<target name="fatjar" depends="clean, build"> <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}"/>

BIN
lib/args4j-2.0.9.jar Normal file

Binary file not shown.

View File

@ -1,20 +1,15 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.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);
@ -51,43 +45,26 @@ public class Main {
}); });
} }
private static class Arguments {
private final Set<String> parameters = new HashSet<String>(3);
private final Map<String, List<String>> messages = new LinkedHashMap<String, List<String>>();
public Arguments(String[] args) {
Pattern topicPattern = Pattern.compile("--(\\w+)");
String currentTopic = null;
for (String arg : args) {
Matcher m = topicPattern.matcher(arg);
if (m.matches()) {
currentTopic = m.group(1).toLowerCase();
messages.put(currentTopic, new ArrayList<String>(1));
} else if (currentTopic != null) {
messages.get(currentTopic).add(arg);
} else {
parameters.add(arg.toLowerCase());
}
}
}
public boolean containsParameter(String argument) { private static ArgumentBean parseArguments(String... args) {
return parameters.contains(argument);
ArgumentBean argumentBean = new ArgumentBean();
CmdLineParser argumentParser = new CmdLineParser(argumentBean);
try {
argumentParser.parseArgument(args);
} catch (CmdLineException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.getMessage());
} }
if (argumentBean.isHelp()) {
public void publishMessages() { System.out.println("Options:");
for (String topic : messages.keySet()) { argumentParser.printUsage(System.out);
MessageBus.getDefault().publish(topic, messages.get(topic).toArray(new String[0]));
} // just print help message and exit afterwards
System.exit(0);
} }
return argumentBean;
} }
} }

View File

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

View File

@ -13,10 +13,6 @@ import net.sourceforge.tuned.FileUtil;
public class FileBotUtil { 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
}
} }

View File

@ -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();

View File

@ -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); URL resource = getResource(name, def);
if (icon == null) { if (resource != null)
// load image if not in cache return new ImageIcon(resource);
URL resource = getResource(name, def);
if (resource != null) {
icon = 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

View File

@ -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;
@ -173,17 +174,6 @@ public class Torrent {
return singleFileTorrent; return singleFileTorrent;
} }
private static Map<?, ?> decodeTorrent(InputStream torrent) throws IOException {
BufferedInputStream in = new BufferedInputStream(torrent);
try {
return BDecoder.decode(in);
} finally {
in.close();
}
}
public static class Entry { public static class Entry {

View File

@ -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();
}
} }

View File

@ -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); public EventList<E> getModel() {
return model;
if (enableRemoveAction) {
// Shortcut DELETE
TunedUtil.registerActionForKeystroke(this, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
}
} }
@ -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();
repaint();
}
public SimpleListModel getModel() {
return (SimpleListModel) list.getModel();
}
public void save(File file) {
try {
PrintStream out = new PrintStream(file);
for (Object object : getModel().getCopy()) { revalidate();
out.println(object.toString()); repaint();
}
out.close();
} catch (Exception e) {
// should not happen
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
} }
} }
public String getDefaultFileName() { public Action getRemoveAction() {
return title + ".txt"; return removeAction;
}
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") {

View File

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

View File

@ -3,51 +3,13 @@ package net.sourceforge.filebot.ui;
import java.awt.BorderLayout; import java.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;

View File

@ -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()));
public EventList<FileBotPanel> getPanelModel() {
return panelModel;
} }
private class PanelCellRenderer extends DefaultFancyListCellRenderer { 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));

View File

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

View File

@ -20,10 +20,15 @@ import javax.swing.event.ListSelectionListener;
import net.sourceforge.filebot.Settings; import net.sourceforge.filebot.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);
} }
} }

View File

@ -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)
if (file.exists()) { continue;
// file might be relative, use absolute file
if (file.exists()) {
try {
// path may be relative, use absolute path
files.add(file.getCanonicalFile()); files.add(file.getCanonicalFile());
} else { } catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, String.format("Invalid File: %s", filename)); Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
} }
} catch (IOException e) { } else {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e); // file doesn't exist
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid File: " + file);
} }
} }
transferablePolicy.handleTransferable(new FileTransferable(files), true); transferablePolicy.handleTransferable(new FileTransferable(files), TransferAction.PUT);
} }
private File fromMessage(Object message) {
if (message instanceof File)
return (File) message;
if (message instanceof String)
return new File((String) message);
return null;
}
} }

View File

@ -25,8 +25,8 @@ import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder; import 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);
} }

View File

@ -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()));
} }

View File

@ -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();
} }
} }

View File

@ -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);

View File

@ -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";
} }

View File

@ -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";
} }

View File

@ -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);
} }
}; };

View File

@ -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";
} }

View File

@ -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());

View File

@ -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) {
this.list = list; super(list.getModel());
addPolicy(new FilePolicy()); this.list = list;
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);
} }
@ -57,90 +73,81 @@ class NamesListTransferablePolicy extends CompositeTransferablePolicy {
@Override @Override
protected void clear() { protected void load(List<File> files) {
list.getModel().clear();
if (FileBotUtil.containsOnlyListFiles(files)) {
loadListFiles(files);
} else if (FileBotUtil.containsOnlyTorrentFiles(files)) {
loadTorrentFiles(files);
} else {
super.load(files);
}
}
private void loadListFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line = null;
while ((line = in.readLine()) != null) {
if (line.trim().length() > 0) {
entries.add(new StringEntry(line));
}
}
in.close();
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
private void loadTorrentFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
Torrent torrent = new Torrent(file);
for (Torrent.Entry entry : torrent.getFiles()) {
entries.add(new TorrentEntry(entry));
}
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
@Override
public String getFileFilterDescription() {
return "text files and torrent files";
} }
private class FilePolicy extends FilesListTransferablePolicy { private class TextPolicy extends StringTransferablePolicy {
public FilePolicy() { @Override
super(list.getModel()); protected void clear() {
NamesListTransferablePolicy.this.clear();
} }
@Override @Override
protected void load(List<File> files) { protected void load(String string) {
if (FileBotUtil.containsOnlyListFiles(files)) {
loadListFiles(files);
} else if (FileBotUtil.containsOnlyTorrentFiles(files)) {
loadTorrentFiles(files);
} else {
super.load(files);
}
}
private void loadListFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line = null;
while ((line = in.readLine()) != null) {
if (line.trim().length() > 0) {
entries.add(new StringEntry(line));
}
}
in.close();
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
private void loadTorrentFiles(List<File> files) {
try {
List<ListEntry> entries = new ArrayList<ListEntry>();
for (File file : files) {
Torrent torrent = new Torrent(file);
for (Torrent.Entry entry : torrent.getFiles()) {
entries.add(new TorrentEntry(entry));
}
}
submit(entries);
} catch (IOException e) {
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
}
}
@Override
public String getDescription() {
return "text files and torrent files";
}
};
private class TextPolicy extends TextTransferablePolicy {
@Override
protected void load(String text) {
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";
}
};
} }

View File

@ -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);

View File

@ -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 from = -1; private int fromIndex = -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;
} }
}; };

View File

@ -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) {

View File

@ -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);
} }
} }

View File

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

View 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();
} }

View File

@ -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

View File

@ -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);
} }

View File

@ -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,21 +180,27 @@ public class SearchPanel extends FileBotPanel {
} }
}; };
private final SaveAction saveAction = new SaveAction(null) { private final SaveAction saveAction = new SaveAction(new SelectedTabExportHandler());
private class SelectedTabExportHandler extends AdaptiveFileExportHandler {
/**
* @return the <code>FileExportHandler</code> of the currently selected tab
*/
@Override @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> {
private final String query; private final String query;
@ -267,14 +274,7 @@ 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
selectedResult = searchResults.iterator().next(); selectedResult = searchResults.iterator().next();

View File

@ -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)

View File

@ -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,21 +67,18 @@ 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();
activeSessionTaskCount.set(0);
remainingTaskCount.set(0);
} }
executors.clear();
activeSessionTaskCount.set(0);
remainingTaskCount.set(0);
} }
@ -96,26 +97,22 @@ 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(workerQueue, executor);
executors.put(workerQueueKey, executor);
}
return executor;
} }
return executor;
} }
@ -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 @Override
public void run() { public void firePropertyChange(final PropertyChangeEvent evt) {
propertyChangeSupport.firePropertyChange(property, null, newValue); SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SwingWorkerPropertyChangeSupport.super.firePropertyChange(evt);
}
});
} }
} }

View File

@ -16,24 +16,28 @@ public class ChecksumRow {
private HashMap<File, Checksum> checksumMap = new HashMap<File, Checksum>(); private 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);
} }
} }

View File

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

View File

@ -6,6 +6,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.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;
} }

View File

@ -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);
}
}
}
} }

View File

@ -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);
}
}
public void save(File file, int checksumColumnIndex) {
try {
PrintStream out = new PrintStream(file);
ChecksumTableModel model = (ChecksumTableModel) getModel(); if (e.getType() == TableModelEvent.DELETE) {
File columnRoot = model.getChecksumColumnRoot(checksumColumnIndex); // remove cancelled task from queue
checksumComputationService.purge();
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);
}
} }

View File

@ -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));
} }
} }
} }

View File

@ -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);
}
} }
}; };

View File

@ -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;
} }

View File

@ -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;
} }
} }

View File

@ -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);

View File

@ -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());

View File

@ -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());

View File

@ -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:");
}
} }

View File

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

View File

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

View File

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

View File

@ -94,16 +94,31 @@ public class DefaultTransferHandler extends TransferHandler {
} }
public ImportHandler getImportHandler() {
return importHandler;
}
public void setImportHandler(ImportHandler importHandler) { 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;
} }

View File

@ -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 abstract void export(OutputStream out) throws IOException;
public SaveableExportHandler(Saveable saveable) {
this.saveable = saveable; public abstract String getDefaultFileName();
public void export(File file) throws IOException {
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
try {
export(out);
} finally {
out.close();
}
} }
@Override @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) {
} }
} }

View File

@ -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();
} }

View File

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

View File

@ -8,7 +8,7 @@ import javax.swing.AbstractAction;
import javax.swing.JFileChooser; import 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);
} }
} }

View File

@ -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);
}
} }
} }

View File

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

View File

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

View File

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

View File

@ -6,9 +6,6 @@ import java.io.File;
import javax.swing.filechooser.FileFilter; import 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;
} }
} }

View File

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

View File

@ -11,7 +11,6 @@ import java.net.URLEncoder;
import java.text.NumberFormat; import java.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, ...)
} }

View File

@ -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) {

View File

@ -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);

View File

@ -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,14 +153,15 @@ 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()); for (Map<String, String> subtitle : response.get("data")) {
subs.add(new OpenSubtitlesSubtitleDescriptor(subtitle));
// if there was an error data may not be a list }
for (Map<String, String> subtitle : response.get("data")) { } catch (ClassCastException e) {
subs.add(new OpenSubtitlesSubtitleDescriptor(subtitle)); // if the response is an error message, generic types won't match
throw new XmlRpcException("Illegal response: " + response.toString(), e);
} }
return subs; return subs;

View File

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

View File

@ -19,7 +19,6 @@ import net.sourceforge.filebot.resources.ResourceManager;
/** /**
* {@link SubtitleClient} for OpenSubtitles. * {@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;

View File

@ -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);
} }

View File

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

View File

@ -9,7 +9,6 @@ import java.net.URI;
import java.net.URL; import java.net.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);

View File

@ -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));

View File

@ -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;
} }

View File

@ -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);

View File

@ -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);
} }

View File

@ -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;

View File

@ -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);
} }
} }
}); });
} }
} }

View File

@ -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);
} }

View File

@ -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));
} }
} }

View File

@ -81,12 +81,6 @@ public class PreferencesMap<T> implements Map<String, T> {
} }
public void set(Map<String, T> data) {
clear();
putAll(data);
}
@Override @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());
} }
} }

View File

@ -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;
} }

View File

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

View File

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

View File

@ -13,10 +13,12 @@ import java.awt.Insets;
import java.awt.Paint; import java.awt.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();
} }

View File

@ -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);
} }

View File

@ -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,14 +145,48 @@ 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);
return b;
} }
@Override
public void configureArrowButton() {
super.configureArrowButton();
arrowButton.setContentAreaFilled(false);
arrowButton.setFocusable(false);
}
@Override
protected void configureEditor() {
editor.setEnabled(comboBox.isEnabled());
editor.setFocusable(comboBox.isFocusable());
editor.setFont(comboBox.getFont());
editor.addFocusListener(createFocusListener());
}
public JTextComponent getEditor() {
return (JTextComponent) editor;
}
// @Override
// protected FocusListener createFocusListener() {
// return new FocusHandler() {
//
// /**
// * Prevent action events from being fired on focusLost.
// */
// @Override
// public void focusLost(FocusEvent e) {
// if (isPopupVisible(comboBox))
// setPopupVisible(comboBox, false);
// }
// };
// }
} }
} }

View File

@ -3,6 +3,7 @@ package net.sourceforge.tuned.ui;
import java.lang.reflect.Method; import java.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&lt;T&gt;(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);
} }

View File

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

View File

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

View File

@ -13,20 +13,24 @@ import java.awt.image.BufferedImage;
import javax.swing.Action; import javax.swing.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
}
} }

View File

@ -5,17 +5,12 @@ 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) {
this.fx = fx; this.fx = fx;
this.fy = fy; this.fy = fy;

View File

@ -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();
} }
} }

View File

@ -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(closeOnTimeout);
addComponentListener(visibleListener);
}
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) {

View File

@ -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);
} }

View File

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

View File

@ -13,7 +13,7 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class) @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() {