* 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:
parent
4c85cdce4a
commit
0bed877344
Binary file not shown.
Before Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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:");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue