1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-11-04 16:35:08 -05:00

* heavy refactoring of AbstractSearchPanel, EpisodeListPanel and SubtitlePanel and related stuff

* added parameter to method in SubtitleClient interface
* fixed "stutter" issue in ProgressIndicator
* removed loading overlay progress indicator from tabs
* removed animated gifs
This commit is contained in:
Reinhard Pointner 2009-01-01 22:27:53 +00:00
parent 4c85cdce4a
commit 0bed877344
21 changed files with 528 additions and 834 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,11 +2,13 @@
package net.sourceforge.filebot.ui;
import java.awt.BorderLayout;
import static javax.swing.JTabbedPane.WRAP_TAB_LAYOUT;
import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER;
import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
import static javax.swing.SwingConstants.TOP;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.net.URI;
import java.util.Collection;
import java.util.List;
@ -15,7 +17,6 @@ import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
@ -23,64 +24,51 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import net.miginfocom.swing.MigLayout;
import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.tuned.ExceptionUtil;
import net.sourceforge.tuned.ui.LabelProvider;
import net.sourceforge.tuned.ui.SelectButtonTextField;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
import net.sourceforge.tuned.ui.TunedUtil;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.swing.AutoCompleteSupport;
public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends FileBotPanel {
public abstract class AbstractSearchPanel<S, E> extends FileBotPanel {
private final JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
protected final JPanel tabbedPaneGroup = new JPanel(new MigLayout("nogrid, fill, insets 0"));
private final HistoryPanel historyPanel = new HistoryPanel();
protected final JTabbedPane tabbedPane = new JTabbedPane(TOP, WRAP_TAB_LAYOUT);
private final SelectButtonTextField<S> searchField = new SelectButtonTextField<S>();
protected final HistoryPanel historyPanel = new HistoryPanel();
private final EventList<String> searchHistory = new BasicEventList<String>();
protected final SelectButtonTextField<S> searchTextField = new SelectButtonTextField<S>();
private EventList<String> searchHistory = new BasicEventList<String>();
public AbstractSearchPanel(String title, Icon icon) {
super(title, icon);
setLayout(new BorderLayout(10, 5));
Box searchBox = Box.createHorizontalBox();
searchBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
searchField.setMaximumSize(searchField.getPreferredSize());
searchBox.add(Box.createHorizontalGlue());
searchBox.add(searchField);
searchBox.add(Box.createHorizontalStrut(15));
searchBox.add(new JButton(searchAction));
searchBox.add(Box.createHorizontalGlue());
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.setBorder(BorderFactory.createTitledBorder("Search Results"));
centerPanel.add(tabbedPane, BorderLayout.CENTER);
historyPanel.setColumnHeader(2, "Duration");
JScrollPane historyScrollPane = new JScrollPane(historyPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JScrollPane historyScrollPane = new JScrollPane(historyPanel, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER);
historyScrollPane.setBorder(BorderFactory.createEmptyBorder());
tabbedPane.addTab("History", ResourceManager.getIcon("tab.history"), historyScrollPane);
add(searchBox, BorderLayout.NORTH);
add(centerPanel, BorderLayout.CENTER);
tabbedPaneGroup.setBorder(BorderFactory.createTitledBorder("Search Results"));
tabbedPaneGroup.add(tabbedPane, "grow, wrap 8px");
setLayout(new MigLayout("nogrid, fill, insets 0 0 5px 0"));
add(searchTextField, "alignx center, gapafter indent");
add(new JButton(searchAction), "gap 18px, wrap 10px");
add(tabbedPaneGroup, "grow");
/*
* TODO: fetchHistory
@ -91,12 +79,12 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
completionList.addMemberList(fetchHistory);
*/
searchField.getEditor().setAction(searchAction);
searchTextField.getEditor().setAction(searchAction);
searchField.getSelectButton().setModel(createSearchEngines());
searchField.getSelectButton().setLabelProvider(createSearchEngineLabelProvider());
searchTextField.getSelectButton().setModel(createSearchEngines());
searchTextField.getSelectButton().setLabelProvider(createSearchEngineLabelProvider());
AutoCompleteSupport.install(searchField.getEditor(), searchHistory);
AutoCompleteSupport.install(searchTextField.getEditor(), searchHistory);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
}
@ -108,13 +96,7 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
protected abstract LabelProvider<S> createSearchEngineLabelProvider();
protected abstract SearchTask createSearchTask();
protected abstract FetchTask createFetchTask(SearchTask searchTask, SearchResult selectedSearchResult);
protected abstract URI getLink(S client, SearchResult searchResult);
protected abstract RequestProcessor<?> createRequestProcessor();
public EventList<String> getSearchHistory() {
@ -122,13 +104,20 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
}
public HistoryPanel getHistoryPanel() {
return historyPanel;
}
public SelectButtonTextField<S> getSearchField() {
return searchField;
private void search(RequestProcessor<?> requestProcessor) {
FileBotTab<?> tab = requestProcessor.tab;
Request request = requestProcessor.request;
tab.setTitle(requestProcessor.getTitle());
tab.setLoading(true);
tab.setIcon(searchTextField.getSelectButton().getLabelProvider().getIcon(request.getClient()));
tab.addTo(tabbedPane);
tabbedPane.setSelectedComponent(tab);
// search in background
new SearchTask(requestProcessor).execute();
}
private final AbstractAction searchAction = new AbstractAction("Find", ResourceManager.getIcon("action.find")) {
@ -139,53 +128,112 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
return;
}
SearchTask searchTask = createSearchTask();
searchTask.addPropertyChangeListener(new SearchTaskListener());
searchTask.execute();
search(createRequestProcessor());
}
};
protected abstract class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
protected class Request {
private final String searchText;
private final S client;
private final T tabPanel;
private final String searchText;
public SearchTask(S client, String searchText, T tabPanel) {
this.searchText = searchText;
public Request(S client, String searchText) {
this.client = client;
this.tabPanel = tabPanel;
this.searchText = searchText;
}
@Override
protected abstract Collection<SearchResult> doInBackground() throws Exception;
public S getClient() {
return client;
}
protected SearchResult chooseSearchResult() throws Exception {
public String getSearchText() {
return searchText;
}
}
protected abstract class RequestProcessor<R extends Request> {
protected final R request;
private FileBotTab<JComponent> tab;
private SearchResult searchResult = null;
private long duration = 0;
public RequestProcessor(R request, JComponent component) {
this.request = request;
this.tab = new FileBotTab<JComponent>(component);
}
public abstract Collection<SearchResult> search() throws Exception;
public abstract Collection<E> fetch() throws Exception;
public abstract void process(Collection<E> elements);
public abstract URI getLink();
public JComponent getComponent() {
return tab.getComponent();
}
public SearchResult getSearchResult() {
return searchResult;
}
private void setSearchResult(SearchResult searchResult) {
this.searchResult = searchResult;
}
public String getStatusMessage(Collection<E> result) {
return String.format("%d elements found", result.size());
}
public String getTitle() {
if (searchResult != null)
return searchResult.getName();
switch (get().size()) {
return request.getSearchText();
}
protected SearchResult chooseSearchResult(Collection<SearchResult> searchResults) throws Exception {
switch (searchResults.size()) {
case 0:
Logger.getLogger("ui").warning(String.format("\"%s\" has not been found.", getSearchText()));
Logger.getLogger("ui").warning(String.format("\"%s\" has not been found.", request.getSearchText()));
return null;
case 1:
return get().iterator().next();
return searchResults.iterator().next();
}
// check if an exact match has been found
for (SearchResult searchResult : get()) {
if (getSearchText().equalsIgnoreCase(searchResult.getName()))
for (SearchResult searchResult : searchResults) {
if (request.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());
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(window, searchResults);
configureSelectDialog(selectDialog);
@ -196,146 +244,8 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
}
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) throws Exception {
selectDialog.setIconImage(TunedUtil.getImage(searchField.getSelectButton().getLabelProvider().getIcon(getClient())));
}
public String getSearchText() {
return searchText;
}
public S getClient() {
return client;
}
public T getTabPanel() {
return tabPanel;
}
}
private class SearchTaskListener extends SwingWorkerPropertyChangeAdapter {
private FileBotTab<T> tab;
@Override
public void started(PropertyChangeEvent evt) {
SearchTask task = (SearchTask) evt.getSource();
tab = new FileBotTab<T>(task.getTabPanel());
tab.setTitle(task.getSearchText());
tab.setLoading(true);
tab.setIcon(searchField.getSelectButton().getLabelProvider().getIcon(task.getClient()));
tab.addTo(tabbedPane);
tabbedPane.setSelectedComponent(tab);
}
@Override
public void done(PropertyChangeEvent evt) {
// tab might have been closed
if (tab.isClosed())
return;
SearchTask task = (SearchTask) evt.getSource();
try {
SearchResult selectedResult = task.chooseSearchResult();
if (selectedResult == null) {
tab.close();
return;
}
String title = selectedResult.getName();
if (!searchHistory.contains(title)) {
searchHistory.add(title);
}
tab.setTitle(title);
FetchTask fetchTask = createFetchTask(task, selectedResult);
fetchTask.addPropertyChangeListener(new FetchTaskListener(tab));
fetchTask.execute();
} catch (Exception e) {
tab.close();
Logger.getLogger("ui").warning(ExceptionUtil.getRootCause(e).getMessage());
Logger.getLogger("global").log(Level.SEVERE, "Search failed", e);
}
}
}
protected abstract class FetchTask extends SwingWorker<Void, E> {
private long duration = -1;
private int count = 0;
private final S client;
private final SearchResult searchResult;
private final T tabPanel;
public FetchTask(S client, SearchResult searchResult, T tabPanel) {
this.client = client;
this.searchResult = searchResult;
this.tabPanel = tabPanel;
}
@SuppressWarnings("unchecked")
@Override
protected final Void doInBackground() throws Exception {
long start = System.currentTimeMillis();
Collection<E> results = fetch();
for (E result : results) {
publish(result);
count++;
}
duration = System.currentTimeMillis() - start;
return null;
}
protected abstract Collection<E> fetch() throws Exception;
@Override
protected abstract void process(List<E> elements);
public abstract String getStatusMessage();
public S getClient() {
return client;
}
public SearchResult getSearchResult() {
return searchResult;
}
public T getTabPanel() {
return tabPanel;
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
selectDialog.setIconImage(TunedUtil.getImage(searchTextField.getSelectButton().getLabelProvider().getIcon(request.getClient())));
}
@ -343,87 +253,126 @@ public abstract class AbstractSearchPanel<S, E, T extends JComponent> extends Fi
return duration;
}
}
public int getCount() {
return count;
private class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
private final RequestProcessor<?> requestProcessor;
public SearchTask(RequestProcessor<?> requestProcessor) {
this.requestProcessor = requestProcessor;
}
@Override
protected Collection<SearchResult> doInBackground() throws Exception {
long start = System.currentTimeMillis();
try {
return requestProcessor.search();
} finally {
requestProcessor.duration += (System.currentTimeMillis() - start);
}
}
@Override
public void done() {
FileBotTab<?> tab = requestProcessor.tab;
// tab might have been closed
if (tab.isClosed())
return;
try {
// choose search result
requestProcessor.setSearchResult(requestProcessor.chooseSearchResult(get()));
if (requestProcessor.getSearchResult() == null) {
tab.close();
return;
}
String title = requestProcessor.getTitle();
if (!searchHistory.contains(title)) {
searchHistory.add(title);
}
tab.setTitle(title);
// fetch elements of the selected search result
new FetchTask(requestProcessor).execute();
} catch (Exception e) {
tab.close();
Logger.getLogger("ui").warning(ExceptionUtil.getRootCause(e).getMessage());
Logger.getLogger("global").log(Level.WARNING, "Search failed", e);
}
}
}
private class FetchTaskListener extends SwingWorkerPropertyChangeAdapter {
private class FetchTask extends SwingWorker<Collection<E>, Void> {
private final FileBotTab<T> tab;
private CancelAction cancelOnClose;
private final RequestProcessor<?> requestProcessor;
public FetchTaskListener(FileBotTab<T> tab) {
this.tab = tab;
public FetchTask(RequestProcessor<?> requestProcessor) {
this.requestProcessor = requestProcessor;
}
@Override
public void started(PropertyChangeEvent evt) {
cancelOnClose = new CancelAction((SwingWorker<?, ?>) evt.getSource());
tab.getTabComponent().getCloseButton().addActionListener(cancelOnClose);
protected final Collection<E> doInBackground() throws Exception {
long start = System.currentTimeMillis();
try {
return requestProcessor.fetch();
} finally {
requestProcessor.duration += (System.currentTimeMillis() - start);
}
}
@Override
public void done(PropertyChangeEvent evt) {
tab.getTabComponent().getCloseButton().removeActionListener(cancelOnClose);
public void done() {
FileBotTab<?> tab = requestProcessor.tab;
Request request = requestProcessor.request;
FetchTask task = (FetchTask) evt.getSource();
// tab might still be open, even if task was cancelled
if (tab.isClosed() || task.isCancelled())
if (tab.isClosed())
return;
try {
// check if exception occurred
task.get();
Collection<E> elements = get();
String title = task.getSearchResult().toString();
URI link = getLink(task.getClient(), task.getSearchResult());
Icon icon = searchField.getSelectButton().getLabelProvider().getIcon(task.getClient());
String info = task.getStatusMessage();
String duration = String.format("%,d ms", task.getDuration());
requestProcessor.process(elements);
historyPanel.add(title, link, icon, info, duration);
String title = requestProcessor.getSearchResult().toString();
Icon icon = searchTextField.getSelectButton().getLabelProvider().getIcon(request.getClient());
String statusMessage = requestProcessor.getStatusMessage(elements);
historyPanel.add(title, requestProcessor.getLink(), icon, statusMessage, String.format("%,d ms", requestProcessor.getDuration()));
// close tab if no elements were fetched
if (task.getCount() <= 0) {
Logger.getLogger("ui").warning(info);
if (get().size() <= 0) {
Logger.getLogger("ui").warning(statusMessage);
tab.close();
}
} catch (Exception e) {
tab.close();
Logger.getLogger("ui").warning(ExceptionUtil.getRootCause(e).getMessage());
Logger.getLogger("global").log(Level.SEVERE, "Fetch failed", e);
Logger.getLogger("global").log(Level.WARNING, "Fetch failed", e);
} finally {
tab.setLoading(false);
}
tab.setLoading(false);
}
}
private static class CancelAction implements ActionListener {
private final SwingWorker<?, ?> worker;
public CancelAction(SwingWorker<?, ?> worker) {
this.worker = worker;
}
@Override
public void actionPerformed(ActionEvent e) {
worker.cancel(false);
}
}
}

View File

@ -11,8 +11,6 @@ import javax.swing.JComponent;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import net.sourceforge.tuned.ui.LoadingOverlayPane;
public class FileBotTab<T extends JComponent> extends JComponent {
@ -20,17 +18,14 @@ public class FileBotTab<T extends JComponent> extends JComponent {
private final T component;
private final LoadingOverlayPane loadingOverlayPane;
public FileBotTab(T component) {
setLayout(new BorderLayout());
this.component = component;
tabComponent.getCloseButton().addActionListener(closeAction);
loadingOverlayPane = new LoadingOverlayPane(component, this);
add(loadingOverlayPane, BorderLayout.CENTER);
setLayout(new BorderLayout());
add(component, BorderLayout.CENTER);
}
@ -94,7 +89,6 @@ public class FileBotTab<T extends JComponent> extends JComponent {
public void setLoading(boolean loading) {
tabComponent.setLoading(loading);
loadingOverlayPane.setOverlayVisible(loading);
}
private final ActionListener closeAction = new ActionListener() {

View File

@ -2,41 +2,28 @@
package net.sourceforge.filebot.ui.panel.episodelist;
import java.awt.BorderLayout;
import java.awt.Window;
import static net.sourceforge.filebot.ui.panel.episodelist.SeasonSpinnerModel.ALL_SEASONS;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.ui.AbstractSearchPanel;
import net.sourceforge.filebot.ui.FileBotList;
import net.sourceforge.filebot.ui.FileBotPanel;
import net.sourceforge.filebot.ui.FileBotListExportHandler;
import net.sourceforge.filebot.ui.FileBotTab;
import net.sourceforge.filebot.ui.HistoryPanel;
import net.sourceforge.filebot.ui.SelectDialog;
import net.sourceforge.filebot.ui.transfer.FileExportHandler;
import net.sourceforge.filebot.ui.transfer.SaveAction;
@ -46,87 +33,42 @@ import net.sourceforge.filebot.web.EpisodeListClient;
import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.filebot.web.TVDotComClient;
import net.sourceforge.filebot.web.TVRageClient;
import net.sourceforge.tuned.ExceptionUtil;
import net.sourceforge.tuned.ui.LabelProvider;
import net.sourceforge.tuned.ui.SelectButton;
import net.sourceforge.tuned.ui.SelectButtonTextField;
import net.sourceforge.tuned.ui.SimpleLabelProvider;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
import net.sourceforge.tuned.ui.TunedUtil;
public class EpisodeListPanel extends FileBotPanel {
private JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
private HistoryPanel historyPanel = new HistoryPanel();
public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListClient, Episode> {
private SeasonSpinnerModel seasonSpinnerModel = new SeasonSpinnerModel();
private SelectButtonTextField<EpisodeListClient> searchField;
public EpisodeListPanel() {
super("Episodes", ResourceManager.getIcon("panel.episodelist"));
setLayout(new BorderLayout());
searchField = new SelectButtonTextField<EpisodeListClient>();
searchField.getSelectButton().setModel(createSearchEngines());
searchField.getSelectButton().setLabelProvider(createSearchEngineLabelProvider());
searchField.getSelectButton().addPropertyChangeListener(SelectButton.SELECTED_VALUE, selectButtonListener);
historyPanel.setColumnHeader(0, "Show");
historyPanel.setColumnHeader(1, "Number of Episodes");
historyPanel.setColumnHeader(2, "Duration");
JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
Box searchBox = Box.createHorizontalBox();
searchBox.setBorder(new EmptyBorder(5, 5, 5, 5));
JSpinner seasonSpinner = new JSpinner(seasonSpinnerModel);
seasonSpinner.setEditor(new SeasonSpinnerEditor(seasonSpinner));
searchField.setMaximumSize(searchField.getPreferredSize());
seasonSpinner.setMaximumSize(seasonSpinner.getPreferredSize());
searchBox.add(Box.createHorizontalGlue());
searchBox.add(searchField);
searchBox.add(Box.createHorizontalStrut(15));
searchBox.add(seasonSpinner);
searchBox.add(Box.createHorizontalStrut(15));
searchBox.add(new JButton(searchAction));
searchBox.add(Box.createHorizontalGlue());
// set minimum size to "All Seasons" preferred size
seasonSpinner.setMinimumSize(seasonSpinner.getPreferredSize());
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.setBorder(BorderFactory.createTitledBorder("Search Results"));
// add after text field
add(seasonSpinner, 1);
// add after tabbed pane
tabbedPaneGroup.add(new JButton(new SaveAction(new SelectedTabExportHandler())), "align center, wrap 5px");
Box buttonBox = Box.createHorizontalBox();
buttonBox.setBorder(new EmptyBorder(5, 5, 5, 5));
buttonBox.add(Box.createHorizontalGlue());
buttonBox.add(new JButton(saveAction));
buttonBox.add(Box.createHorizontalGlue());
searchTextField.getSelectButton().addPropertyChangeListener(SelectButton.SELECTED_VALUE, selectButtonListener);
centerPanel.add(tabbedPane, BorderLayout.CENTER);
centerPanel.add(buttonBox, BorderLayout.SOUTH);
JScrollPane historyScrollPane = new JScrollPane(historyPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
historyScrollPane.setBorder(BorderFactory.createEmptyBorder());
tabbedPane.addTab("History", ResourceManager.getIcon("tab.history"), historyScrollPane);
mainPanel.add(searchBox, BorderLayout.NORTH);
mainPanel.add(centerPanel, BorderLayout.CENTER);
this.add(mainPanel, BorderLayout.CENTER);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ENTER"), searchAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("UP"), upAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("DOWN"), downAction);
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinSeasonAction(1));
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinSeasonAction(-1));
}
@Override
protected List<EpisodeListClient> createSearchEngines() {
List<EpisodeListClient> engines = new ArrayList<EpisodeListClient>(3);
@ -138,61 +80,54 @@ public class EpisodeListPanel extends FileBotPanel {
}
@Override
protected LabelProvider<EpisodeListClient> createSearchEngineLabelProvider() {
return SimpleLabelProvider.forClass(EpisodeListClient.class);
}
@Override
protected EpisodeListRequestProcessor createRequestProcessor() {
EpisodeListClient client = searchTextField.getSelectButton().getSelectedValue();
String text = searchTextField.getText().trim();
int season = seasonSpinnerModel.getSeason();
return new EpisodeListRequestProcessor(new EpisodeListRequest(client, text, season));
};
private final PropertyChangeListener selectButtonListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
EpisodeListClient client = searchField.getSelected();
EpisodeListClient client = searchTextField.getSelectButton().getSelectedValue();
if (!client.hasSingleSeasonSupport()) {
seasonSpinnerModel.lock(SeasonSpinnerModel.ALL_SEASONS);
} else {
seasonSpinnerModel.unlock();
}
// lock season spinner on "All Seasons" if client doesn't support fetching of single seasons
seasonSpinnerModel.lock(!client.hasSingleSeasonSupport());
}
};
private final AbstractAction searchAction = new AbstractAction("Find", ResourceManager.getIcon("action.find")) {
private class SpinSeasonAction extends AbstractAction {
public SpinSeasonAction(int spin) {
putValue("spin", spin);
}
public void actionPerformed(ActionEvent e) {
EpisodeListClient searchEngine = searchField.getSelected();
SearchTask task = new SearchTask(searchEngine, searchField.getText(), seasonSpinnerModel.getSeason());
task.addPropertyChangeListener(new SearchTaskListener());
task.execute();
seasonSpinnerModel.spin((Integer) getValue("spin"));
}
};
private final AbstractAction upAction = new AbstractAction("Up") {
public void actionPerformed(ActionEvent e) {
seasonSpinnerModel.setValue(seasonSpinnerModel.getNextValue());
}
};
private final AbstractAction downAction = new AbstractAction("Down") {
public void actionPerformed(ActionEvent e) {
seasonSpinnerModel.setValue(seasonSpinnerModel.getPreviousValue());
}
};
private final SaveAction saveAction = new SaveAction(new SelectedTabExportHandler());
}
private class SelectedTabExportHandler implements FileExportHandler {
/**
* @return the <code>FileExportHandler</code> of the currently selected tab
*/
@SuppressWarnings("unchecked")
private FileExportHandler getExportHandler() {
try {
FileBotList<?> list = (FileBotList<?>) tabbedPane.getSelectedComponent();
EpisodeListTab list = ((FileBotTab<EpisodeListTab>) tabbedPane.getSelectedComponent()).getComponent();
return list.getExportHandler();
} catch (ClassCastException e) {
// selected component is the history panel
@ -226,166 +161,109 @@ public class EpisodeListPanel extends FileBotPanel {
}
private class SearchTask extends SwingWorker<Collection<SearchResult>, Void> {
private class EpisodeListRequest extends Request {
private final String query;
private final EpisodeListClient client;
private final int numberOfSeason;
private final int season;
public SearchTask(EpisodeListClient client, String query, int numberOfSeason) {
this.query = query;
this.client = client;
this.numberOfSeason = numberOfSeason;
public EpisodeListRequest(EpisodeListClient client, String searchText, int season) {
super(client, searchText);
this.season = season;
}
@Override
protected Collection<SearchResult> doInBackground() throws Exception {
return client.search(query);
public int getSeason() {
return season;
}
}
private class SearchTaskListener extends SwingWorkerPropertyChangeAdapter {
private class EpisodeListRequestProcessor extends RequestProcessor<EpisodeListRequest> {
private FileBotTab<FileBotList<Episode>> episodeList;
@Override
public void started(PropertyChangeEvent evt) {
SearchTask task = (SearchTask) evt.getSource();
episodeList = new EpisodeListTab();
String title = task.query;
if (task.numberOfSeason != SeasonSpinnerModel.ALL_SEASONS) {
title += String.format(" - Season %d", task.numberOfSeason);
}
episodeList.setTitle(title);
episodeList.setIcon(task.client.getIcon());
tabbedPane.addTab(title, episodeList);
tabbedPane.setTabComponentAt(tabbedPane.indexOfComponent(episodeList), episodeList.getTabComponent());
episodeList.setLoading(true);
public EpisodeListRequestProcessor(EpisodeListRequest request) {
super(request, new EpisodeListTab());
}
@Override
public void done(PropertyChangeEvent evt) {
// tab might have been closed
if (tabbedPane.indexOfComponent(episodeList) < 0)
return;
SearchTask task = (SearchTask) evt.getSource();
Collection<SearchResult> searchResults;
try {
searchResults = task.get();
} catch (Exception e) {
tabbedPane.remove(episodeList);
Throwable cause = ExceptionUtil.getRootCause(e);
Logger.getLogger("ui").warning(cause.getMessage());
Logger.getLogger("global").log(Level.WARNING, cause.toString());
return;
}
SearchResult selectedResult = null;
if (searchResults.size() == 1) {
// only one show found, select this one
selectedResult = searchResults.iterator().next();
} else if (searchResults.size() > 1) {
// multiple shows found, let user selected one
Window window = SwingUtilities.getWindowAncestor(EpisodeListPanel.this);
SelectDialog<SearchResult> select = new SelectDialog<SearchResult>(window, searchResults);
select.getHeaderLabel().setText("Select a Show:");
select.setIconImage(TunedUtil.getImage(episodeList.getIcon()));
select.setVisible(true);
selectedResult = select.getSelectedValue();
} else {
Logger.getLogger("ui").warning(String.format("\"%s\" has not been found.", task.query));
}
if (selectedResult == null) {
tabbedPane.remove(episodeList);
return;
}
String title = selectedResult.getName();
// searchFieldCompletion.addTerm(title);
//TODO fix
// Settings.getSettings().putStringList(Settings.SEARCH_HISTORY, searchFieldCompletion.getTerms());
if (task.numberOfSeason != SeasonSpinnerModel.ALL_SEASONS) {
title += String.format(" - Season %d", task.numberOfSeason);
}
episodeList.setTitle(title);
FetchEpisodeListTask getEpisodesTask = new FetchEpisodeListTask(task.client, selectedResult, task.numberOfSeason);
getEpisodesTask.addPropertyChangeListener(new FetchEpisodeListTaskListener(episodeList));
getEpisodesTask.execute();
public Collection<SearchResult> search() throws Exception {
return request.getClient().search(request.getSearchText());
}
@Override
public Collection<Episode> fetch() throws Exception {
if (request.getSeason() != ALL_SEASONS)
return request.getClient().getEpisodeList(getSearchResult(), request.getSeason());
else
return request.getClient().getEpisodeList(getSearchResult());
}
@Override
public URI getLink() {
if (request.getSeason() != ALL_SEASONS)
return request.getClient().getEpisodeListLink(getSearchResult(), request.getSeason());
else
return request.getClient().getEpisodeListLink(getSearchResult());
}
@Override
public void process(Collection<Episode> episodes) {
// set a proper title for the export handler before adding episodes
getComponent().setTitle(getTitle());
getComponent().getModel().addAll(episodes);
}
@Override
public String getStatusMessage(Collection<Episode> result) {
return (result.isEmpty()) ? "No episodes found" : String.format("%d episodes", result.size());
}
@Override
public EpisodeListTab getComponent() {
return (EpisodeListTab) super.getComponent();
}
@Override
public String getTitle() {
if (request.getSeason() == ALL_SEASONS)
return super.getTitle();
// add additional information to default title
return String.format("%s - Season %d", super.getTitle(), request.getSeason());
}
@Override
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
super.configureSelectDialog(selectDialog);
selectDialog.getHeaderLabel().setText("Select a Show:");
}
}
private class FetchEpisodeListTaskListener extends SwingWorkerPropertyChangeAdapter {
private static class EpisodeListTab extends FileBotList<Episode> {
private FileBotTab<FileBotList<Episode>> episodeList;
public FetchEpisodeListTaskListener(FileBotTab<FileBotList<Episode>> episodeList) {
this.episodeList = episodeList;
public EpisodeListTab() {
// set export handler for episode list
setExportHandler(new FileBotListExportHandler(this));
// allow removal of episode list entries
getRemoveAction().setEnabled(true);
// remove borders
listScrollPane.setBorder(null);
setBorder(null);
}
@Override
public void done(PropertyChangeEvent evt) {
// tab might have been closed
if (tabbedPane.indexOfComponent(episodeList) < 0)
return;
FetchEpisodeListTask task = (FetchEpisodeListTask) evt.getSource();
try {
URI link = task.getSearchEngine().getEpisodeListLink(task.getSearchResult(), task.getNumberOfSeason());
Collection<Episode> episodes = task.get();
String info = (episodes.size() > 0) ? String.format("%d episodes", episodes.size()) : "No episodes found";
historyPanel.add(episodeList.getTitle(), link, episodeList.getIcon(), info, NumberFormat.getInstance().format(task.getDuration()) + " ms");
if (episodes.size() <= 0)
tabbedPane.remove(episodeList);
else {
episodeList.setLoading(false);
episodeList.getComponent().getModel().addAll(episodes);
}
} catch (Exception e) {
tabbedPane.remove(episodeList);
Throwable cause = ExceptionUtil.getRootCause(e);
Logger.getLogger("ui").warning(cause.getMessage());
Logger.getLogger("global").log(Level.SEVERE, cause.getMessage(), cause);
}
}
}
}

View File

@ -1,23 +0,0 @@
package net.sourceforge.filebot.ui.panel.episodelist;
import net.sourceforge.filebot.ui.FileBotList;
import net.sourceforge.filebot.ui.FileBotListExportHandler;
import net.sourceforge.filebot.ui.FileBotTab;
import net.sourceforge.filebot.web.Episode;
public class EpisodeListTab extends FileBotTab<FileBotList<Episode>> {
public EpisodeListTab() {
super(new FileBotList<Episode>());
// set export handler for episode list
getComponent().setExportHandler(new FileBotListExportHandler(getComponent()));
// allow removal of episode list entries
getComponent().getRemoveAction().setEnabled(true);
}
}

View File

@ -1,67 +0,0 @@
package net.sourceforge.filebot.ui.panel.episodelist;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingWorker;
import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.EpisodeListClient;
import net.sourceforge.filebot.web.SearchResult;
class FetchEpisodeListTask extends SwingWorker<List<Episode>, Void> {
private final SearchResult searchResult;
private final EpisodeListClient searchEngine;
private final int numberOfSeason;
private long duration = -1;
public FetchEpisodeListTask(EpisodeListClient searchEngine, SearchResult searchResult, int numberOfSeason) {
this.searchEngine = searchEngine;
this.searchResult = searchResult;
this.numberOfSeason = numberOfSeason;
}
@Override
protected List<Episode> doInBackground() throws Exception {
long start = System.currentTimeMillis();
List<Episode> list = new ArrayList<Episode>();
if (numberOfSeason == SeasonSpinnerModel.ALL_SEASONS) {
list.addAll(searchEngine.getEpisodeList(searchResult));
} else {
list.addAll(searchEngine.getEpisodeList(searchResult, numberOfSeason));
}
duration = System.currentTimeMillis() - start;
return list;
}
public EpisodeListClient getSearchEngine() {
return searchEngine;
}
public SearchResult getSearchResult() {
return searchResult;
}
public int getNumberOfSeason() {
return numberOfSeason;
}
public long getDuration() {
return duration;
}
}

View File

@ -2,8 +2,11 @@
package net.sourceforge.filebot.ui.panel.episodelist;
import static net.sourceforge.filebot.ui.panel.episodelist.SeasonSpinnerModel.ALL_SEASONS;
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.event.ChangeEvent;
@ -13,8 +16,11 @@ import javax.swing.event.ChangeListener;
class SeasonSpinnerEditor extends JLabel implements ChangeListener {
public SeasonSpinnerEditor(JSpinner spinner) {
setHorizontalAlignment(RIGHT);
spinner.addChangeListener(this);
setValueFromSpinner(spinner);
setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3));
setBackground(Color.WHITE);
setOpaque(true);
@ -29,7 +35,7 @@ class SeasonSpinnerEditor extends JLabel implements ChangeListener {
private void setValueFromSpinner(JSpinner spinner) {
int season = ((SeasonSpinnerModel) spinner.getModel()).getSeason();
if (season == SeasonSpinnerModel.ALL_SEASONS)
if (season == ALL_SEASONS)
setText("All Seasons");
else
setText(String.format("Season %d", season));

View File

@ -15,18 +15,28 @@ public class SeasonSpinnerModel extends SpinnerNumberModel {
}
public int getSeason() {
public Integer getSeason() {
return getNumber().intValue();
}
public void lock(int maxSeason) {
setMaximum(maxSeason);
public void spin(int steps) {
int next = getSeason() + steps;
if (next < ALL_SEASONS)
next = ALL_SEASONS;
setValue(next);
}
public void unlock() {
setMaximum(Integer.MAX_VALUE);
public void lock(boolean lock) {
if (lock) {
setValue(ALL_SEASONS);
setMaximum(ALL_SEASONS);
} else {
setMaximum(Integer.MAX_VALUE);
}
}
}

View File

@ -60,7 +60,7 @@ public class ListPanel extends FileBotPanel {
fromSpinner.setEditor(new JSpinner.NumberEditor(fromSpinner, "#"));
toSpinner.setEditor(new JSpinner.NumberEditor(toSpinner, "#"));
setLayout(new MigLayout("insets dialog, nogrid, fill", "align center"));
setLayout(new MigLayout("nogrid, fill, insets 6px 2px 6px 2px", "align center"));
add(new JLabel("Pattern:"), "gapbefore indent");
add(textField, "gap related, wmin 2cm");

View File

@ -83,7 +83,7 @@ public class RenamePanel extends FileBotPanel {
renameButton.setVerticalTextPosition(SwingConstants.BOTTOM);
renameButton.setHorizontalTextPosition(SwingConstants.CENTER);
setLayout(new MigLayout("insets 0, gapx 10px, fill", "", "align 33%"));
setLayout(new MigLayout("fill, insets 0, gapx 10px", null, "align 33%"));
add(namesList, "grow");
@ -91,8 +91,8 @@ public class RenamePanel extends FileBotPanel {
matchButton.setMargin(new Insets(3, 14, 2, 14));
renameButton.setMargin(new Insets(6, 11, 2, 11));
add(matchButton, "cell 1 0, flowy, sizegroup button");
add(renameButton, "cell 1 0, gapy 30px, sizegroup button");
add(matchButton, "split 2, flowy, sizegroupx button");
add(renameButton, "gapy 30px, sizegroupx button");
add(filesList, "grow");

View File

@ -2,21 +2,15 @@
package net.sourceforge.filebot.ui.panel.subtitle;
import java.beans.PropertyChangeEvent;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingWorker.StateValue;
import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.web.SubtitleDescriptor;
import net.sourceforge.tuned.DownloadTask;
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
import org.jdesktop.beans.AbstractBean;
public class SubtitlePackage extends AbstractBean {
public class SubtitlePackage {
private final SubtitleDescriptor subtitleDescriptor;
@ -26,20 +20,22 @@ public class SubtitlePackage extends AbstractBean {
private final Language language;
private DownloadTask downloadTask;
private StateValue downloadState = StateValue.PENDING;
private float downloadProgress = 0;
private final DownloadTask downloadTask;
public SubtitlePackage(SubtitleDescriptor subtitleDescriptor) {
this.subtitleDescriptor = subtitleDescriptor;
this.language = new Language(subtitleDescriptor.getLanguageName());
language = new Language(subtitleDescriptor.getLanguageName());
downloadTask = subtitleDescriptor.createDownloadTask();
this.archiveType = ArchiveType.forName(subtitleDescriptor.getArchiveType());
this.archiveIcon = ResourceManager.getArchiveIcon(archiveType.getExtension());
archiveType = ArchiveType.forName(subtitleDescriptor.getArchiveType());
archiveIcon = ResourceManager.getArchiveIcon(archiveType.getExtension());
}
public SubtitleDescriptor getSubtitleDescriptor() {
return subtitleDescriptor;
}
@ -69,57 +65,8 @@ public class SubtitlePackage extends AbstractBean {
}
public synchronized void startDownload() {
if (downloadTask != null)
throw new IllegalStateException("Download has already been started");
downloadTask = subtitleDescriptor.createDownloadTask();
downloadTask.addPropertyChangeListener(new DownloadTaskPropertyChangeAdapter());
downloadTask.execute();
public DownloadTask getDownloadTask() {
return downloadTask;
}
public StateValue getDownloadState() {
return downloadState;
}
private void setDownloadState(StateValue downloadState) {
this.downloadState = downloadState;
firePropertyChange("downloadState", null, downloadState);
}
public float getDownloadProgress() {
return downloadProgress;
}
private void setDownloadProgress(float downloadProgress) {
this.downloadProgress = downloadProgress;
firePropertyChange("downloadProgress", null, downloadProgress);
}
private class DownloadTaskPropertyChangeAdapter extends SwingWorkerPropertyChangeAdapter {
@Override
public void started(PropertyChangeEvent evt) {
setDownloadState(StateValue.STARTED);
}
@Override
public void done(PropertyChangeEvent evt) {
setDownloadState(StateValue.DONE);
}
@Override
public void progress(PropertyChangeEvent evt) {
setDownloadProgress((Float) evt.getNewValue() / 100);
}
};
}

View File

@ -23,13 +23,13 @@ import net.sourceforge.tuned.ui.LabelProvider;
import net.sourceforge.tuned.ui.SimpleLabelProvider;
public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitlePackage, SubtitleDownloadPanel> {
public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitlePackage> {
public SubtitlePanel() {
super("Subtitles", ResourceManager.getIcon("panel.subtitle"));
getHistoryPanel().setColumnHeader(0, "Show / Movie");
getHistoryPanel().setColumnHeader(1, "Number of Subtitles");
historyPanel.setColumnHeader(0, "Show / Movie");
historyPanel.setColumnHeader(1, "Number of Subtitles");
// get preferences node that contains the history entries
Preferences historyNode = Preferences.systemNodeForPackage(getClass()).node("history");
@ -63,62 +63,54 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
@Override
protected SearchTask createSearchTask() {
SubtitleDownloadPanel panel = new SubtitleDownloadPanel();
protected SubtitleRequestProcessor createRequestProcessor() {
SubtitleClient client = searchTextField.getSelectButton().getSelectedValue();
String text = searchTextField.getText().trim();
//TODO language selection combobox
Locale language = Locale.ENGLISH;
return new SubtitleRequestProcessor(new SubtitleRequest(client, text, language));
}
private class SubtitleRequest extends Request {
private final Locale language;
public SubtitleRequest(SubtitleClient client, String searchText, Locale language) {
super(client, searchText);
this.language = language;
}
public Locale getLanguage() {
return language;
}
return new SubtitleSearchTask(getSearchField().getSelected(), getSearchField().getText(), panel);
}
@Override
protected FetchTask createFetchTask(SearchTask searchTask, SearchResult selectedSearchResult) {
return new SubtitleFetchTask(searchTask.getClient(), selectedSearchResult, searchTask.getTabPanel());
}
@Override
protected URI getLink(SubtitleClient client, SearchResult result) {
return client.getSubtitleListLink(result);
}
private class SubtitleSearchTask extends SearchTask {
private class SubtitleRequestProcessor extends RequestProcessor<SubtitleRequest> {
public SubtitleSearchTask(SubtitleClient client, String searchText, SubtitleDownloadPanel panel) {
super(client, searchText, panel);
public SubtitleRequestProcessor(SubtitleRequest request) {
super(request, new SubtitleDownloadPanel());
}
@Override
protected Collection<SearchResult> doInBackground() throws Exception {
return getClient().search(getSearchText());
public Collection<SearchResult> search() throws Exception {
return request.getClient().search(request.getSearchText());
}
@Override
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) throws Exception {
super.configureSelectDialog(selectDialog);
selectDialog.getHeaderLabel().setText("Select a Show / Movie:");
}
}
private class SubtitleFetchTask extends FetchTask {
public SubtitleFetchTask(SubtitleClient client, SearchResult searchResult, SubtitleDownloadPanel tabPanel) {
super(client, searchResult, tabPanel);
}
@Override
protected Collection<SubtitlePackage> fetch() throws Exception {
//TODO language combobox
Collection<SubtitleDescriptor> descriptors = getClient().getSubtitleList(getSearchResult(), Locale.ENGLISH);
ArrayList<SubtitlePackage> packages = new ArrayList<SubtitlePackage>();
public Collection<SubtitlePackage> fetch() throws Exception {
List<SubtitlePackage> packages = new ArrayList<SubtitlePackage>(20);
for (SubtitleDescriptor descriptor : descriptors) {
packages.add(new SubtitlePackage(descriptor));
for (SubtitleDescriptor subtitle : request.getClient().getSubtitleList(getSearchResult(), request.getLanguage())) {
packages.add(new SubtitlePackage(subtitle));
}
return packages;
@ -126,18 +118,37 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleClient, SubtitleP
@Override
protected void process(List<SubtitlePackage> elements) {
getTabPanel().getPackagePanel().getModel().addAll(elements);
public URI getLink() {
return request.getClient().getSubtitleListLink(getSearchResult(), request.getLanguage());
}
@Override
public String getStatusMessage() {
if (getCount() > 0)
return String.format("%d subtitles", getCount());
return "No subtitles found";
public void process(Collection<SubtitlePackage> episodes) {
//TODO subtitle tab ui
System.out.println(episodes);
}
@Override
public String getStatusMessage(Collection<SubtitlePackage> result) {
return (result.isEmpty()) ? "No subtitles found" : String.format("%d subtitles", result.size());
}
@Override
public String getTitle() {
// add additional information to default title
return String.format("%s [%s]", super.getTitle(), request.getLanguage().getDisplayName(Locale.ENGLISH));
}
@Override
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
super.configureSelectDialog(selectDialog);
selectDialog.getHeaderLabel().setText("Select a Show / Movie:");
}
}
}

View File

@ -55,7 +55,8 @@ public class OpenSubtitlesSubtitleClient implements SubtitleClient {
@Override
public URI getSubtitleListLink(SearchResult searchResult) {
public URI getSubtitleListLink(SearchResult searchResult, Locale language) {
//TODO provide link
return null;
}

View File

@ -138,7 +138,7 @@ public class SubsceneSubtitleClient implements SubtitleClient {
@Override
public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, Locale language) throws Exception {
URL subtitleListUrl = getSubtitleListLink(searchResult).toURL();
URL subtitleListUrl = getSubtitleListLink(searchResult, language).toURL();
String languageName = getLanguageName(language);
Integer languageFilter = getLanguageFilter(languageName);
@ -243,7 +243,7 @@ public class SubsceneSubtitleClient implements SubtitleClient {
@Override
public URI getSubtitleListLink(SearchResult searchResult) {
public URI getSubtitleListLink(SearchResult searchResult, Locale locale) {
return ((HyperLink) searchResult).toURI();
}

View File

@ -17,7 +17,7 @@ public interface SubtitleClient {
public Collection<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, Locale language) throws Exception;
public URI getSubtitleListLink(SearchResult searchResult);
public URI getSubtitleListLink(SearchResult searchResult, Locale language);
public String getName();

View File

@ -172,7 +172,7 @@ public class TVRageClient implements EpisodeListClient {
public List<Episode> getEpisodeList(int season) {
if (season > getTotalSeasons() || season < 0)
throw new IllegalArgumentException(String.format("%s only has %d seasons.", getName(), getTotalSeasons()));
throw new IndexOutOfBoundsException(String.format("%s has only %d season%s.", getName(), getTotalSeasons(), getTotalSeasons() != 1 ? "s" : ""));
List<Node> nodes = XPathUtil.selectNodes("//Season[@no='" + season + "']/episode", feed);
@ -188,7 +188,6 @@ public class TVRageClient implements EpisodeListClient {
return episodes;
}
}
}

View File

@ -16,7 +16,6 @@ import java.awt.event.ComponentEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Calendar;
import javax.swing.JComponent;
import javax.swing.Timer;
@ -35,13 +34,22 @@ public class ProgressIndicator extends JComponent {
private final Rectangle2D frame = new Rectangle2D.Double();
private final Ellipse2D circle = new Ellipse2D.Double();
private final Dimension baseSize = new Dimension(32, 32);
private Timer updateTimer = new Timer(20, new ActionListener() {
private double alpha = 0;
private double speed = 1.2;
private final Timer updateTimer = new Timer(20, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Timer timer = (Timer) e.getSource();
alpha += (timer.getDelay() * speed) / 1000;
if (alpha >= 1)
alpha -= Math.floor(alpha);
repaint();
}
});
@ -97,12 +105,10 @@ public class ProgressIndicator extends JComponent {
g2d.setStroke(stroke);
g2d.setPaint(progressShapeColor);
Calendar now = Calendar.getInstance();
// base rotation
g2d.rotate(getTheta(alpha, 1.0), frame.getCenterX(), frame.getCenterY());
double theta = getTheta(now.get(Calendar.MILLISECOND), now.getMaximum(Calendar.MILLISECOND));
g2d.rotate(theta, frame.getCenterX(), frame.getCenterY());
theta = getTheta(1, shapeCount);
double theta = getTheta(1, shapeCount);
for (int i = 0; i < shapeCount; i++) {
g2d.rotate(theta, frame.getCenterX(), frame.getCenterY());
@ -111,8 +117,8 @@ public class ProgressIndicator extends JComponent {
}
private double getTheta(int value, int max) {
return ((double) value / max) * 2 * Math.PI;
private double getTheta(double value, double max) {
return (value / max) * 2 * Math.PI;
}
@ -130,4 +136,14 @@ public class ProgressIndicator extends JComponent {
this.shapeCount = indeterminateShapeCount;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public double getSpeed() {
return speed;
}
}

View File

@ -17,6 +17,7 @@ import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.DefaultSingleSelectionModel;
import javax.swing.Icon;
import javax.swing.JButton;
@ -55,6 +56,7 @@ public class SelectButton<T> extends JButton {
setHorizontalAlignment(SwingConstants.CENTER);
setVerticalAlignment(SwingConstants.CENTER);
setBorder(BorderFactory.createLineBorder(new Color(0xA4A4A4), 1));
setPreferredSize(new Dimension(32, 22));
addActionListener(new OpenPopupOnClick());
@ -189,7 +191,7 @@ public class SelectButton<T> extends JButton {
popup.add(item);
}
popup.show(SelectButton.this, -4, getHeight() - 5);
popup.show(SelectButton.this, 0, getHeight() - 1);
}
}

View File

@ -2,70 +2,54 @@
package net.sourceforge.tuned.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.border.Border;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent;
import net.miginfocom.swing.MigLayout;
import net.sourceforge.filebot.ResourceManager;
public class SelectButtonTextField<T> extends JPanel {
public class SelectButtonTextField<T> extends JComponent {
private SelectButton<T> selectButton = new SelectButton<T>();
private ComboBoxTextField editor = new ComboBoxTextField();
private JComboBox editor = new JComboBox();
public SelectButtonTextField() {
setLayout(new BorderLayout(0, 0));
selectButton.addActionListener(textFieldFocusOnClick);
Color borderColor = new Color(0xA4A4A4);
editor.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 1, ((LineBorder) selectButton.getBorder()).getLineColor()));
Border lineBorder = BorderFactory.createLineBorder(borderColor, 1);
Border matteBorder = BorderFactory.createMatteBorder(1, 0, 1, 1, borderColor);
Border emptyBorder = BorderFactory.createEmptyBorder(0, 3, 0, 3);
setLayout(new MigLayout("nogrid, fill"));
add(selectButton, "h pref!, w pref!, sizegroupy this");
add(editor, "gap 0, w 195px!, sizegroupy this");
selectButton.setBorder(lineBorder);
editor.setBorder(BorderFactory.createCompoundBorder(matteBorder, emptyBorder));
editor.setUI(new TextFieldComboBoxUI());
add(editor, BorderLayout.CENTER);
add(selectButton, BorderLayout.WEST);
setPreferredSize(new Dimension(280, 22));
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift UP"), new SpinClientAction(-1));
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("shift DOWN"), new SpinClientAction(1));
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl UP"), new SpinClientAction(-1));
TunedUtil.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl DOWN"), new SpinClientAction(1));
}
/**
* Convenience method for <code>getEditor().getSelectedItem().toString()</code>
*/
public String getText() {
return editor.getText();
}
/**
* Convenience method for <code>getSelectButton().getSelectedValue()</code>
*/
public T getSelected() {
return getSelectButton().getSelectedValue();
return ((TextFieldComboBoxUI) editor.getUI()).getEditor().getText();
}
@ -103,45 +87,7 @@ public class SelectButtonTextField<T> extends JPanel {
}
private static class ComboBoxTextField extends JComboBox {
public ComboBoxTextField() {
setEditable(true);
super.setUI(new TextFieldComboBoxUI());
}
@Override
public void setUI(ComboBoxUI 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);
// }
}
}
private static class TextFieldComboBoxUI extends BasicComboBoxUI {
private class TextFieldComboBoxUI extends BasicComboBoxUI {
@Override
protected JButton createArrowButton() {
@ -160,9 +106,12 @@ public class SelectButtonTextField<T> extends JPanel {
@Override
protected void configureEditor() {
JTextComponent editor = getEditor();
editor.setEnabled(comboBox.isEnabled());
editor.setFocusable(comboBox.isFocusable());
editor.setFont(comboBox.getFont());
editor.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
editor.addFocusListener(createFocusListener());
}
@ -172,20 +121,42 @@ public class SelectButtonTextField<T> extends JPanel {
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);
// }
// };
// }
@Override
protected ComboPopup createPopup() {
return new BasicComboPopup(comboBox) {
@Override
public void show(Component invoker, int x, int y) {
super.show(invoker, x - selectButton.getWidth(), y);
}
@Override
protected Rectangle computePopupBounds(int px, int py, int pw, int ph) {
Rectangle bounds = super.computePopupBounds(px, py, pw, ph);
bounds.width += selectButton.getWidth();
return bounds;
}
};
}
@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

@ -88,7 +88,7 @@ public class SubsceneSubtitleClientTest {
@Test
public void getSubtitleListLink() throws Exception {
assertEquals(twinpeaksSearchResult.getURL().toString(), subscene.getSubtitleListLink(twinpeaksSearchResult).toURL().toString());
assertEquals(twinpeaksSearchResult.getURL().toString(), subscene.getSubtitleListLink(twinpeaksSearchResult, null).toURL().toString());
}
}