diff --git a/source/net/sourceforge/filebot/resources/loading.gif b/source/net/sourceforge/filebot/resources/loading.gif deleted file mode 100644 index 570115e5..00000000 Binary files a/source/net/sourceforge/filebot/resources/loading.gif and /dev/null differ diff --git a/source/net/sourceforge/filebot/resources/tab.loading.gif b/source/net/sourceforge/filebot/resources/tab.loading.gif deleted file mode 100644 index 6558e315..00000000 Binary files a/source/net/sourceforge/filebot/resources/tab.loading.gif and /dev/null differ diff --git a/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java b/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java index d71d94a0..ef93fd90 100644 --- a/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java +++ b/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java @@ -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 extends FileBotPanel { +public abstract class AbstractSearchPanel 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 searchField = new SelectButtonTextField(); + protected final HistoryPanel historyPanel = new HistoryPanel(); - private final EventList searchHistory = new BasicEventList(); + protected final SelectButtonTextField searchTextField = new SelectButtonTextField(); + + private EventList searchHistory = new BasicEventList(); 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 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 extends Fi protected abstract LabelProvider 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 getSearchHistory() { @@ -122,13 +104,20 @@ public abstract class AbstractSearchPanel extends Fi } - public HistoryPanel getHistoryPanel() { - return historyPanel; - } - - - public SelectButtonTextField 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 extends Fi return; } - SearchTask searchTask = createSearchTask(); - searchTask.addPropertyChangeListener(new SearchTaskListener()); - - searchTask.execute(); + search(createRequestProcessor()); } }; - protected abstract class SearchTask extends SwingWorker, 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 doInBackground() throws Exception; + public S getClient() { + return client; + } - protected SearchResult chooseSearchResult() throws Exception { + public String getSearchText() { + return searchText; + } + + } + + + protected abstract class RequestProcessor { + + protected final R request; + + private FileBotTab tab; + + private SearchResult searchResult = null; + + private long duration = 0; + + + public RequestProcessor(R request, JComponent component) { + this.request = request; + this.tab = new FileBotTab(component); + } + + + public abstract Collection search() throws Exception; + + + public abstract Collection fetch() throws Exception; + + + public abstract void process(Collection 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 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 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 selectDialog = new SelectDialog(window, get()); + SelectDialog selectDialog = new SelectDialog(window, searchResults); configureSelectDialog(selectDialog); @@ -196,146 +244,8 @@ public abstract class AbstractSearchPanel extends Fi } - protected void configureSelectDialog(SelectDialog 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 tab; - - - @Override - public void started(PropertyChangeEvent evt) { - SearchTask task = (SearchTask) evt.getSource(); - - tab = new FileBotTab(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 { - - 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 results = fetch(); - - for (E result : results) { - publish(result); - count++; - } - - duration = System.currentTimeMillis() - start; - - return null; - } - - - protected abstract Collection fetch() throws Exception; - - - @Override - protected abstract void process(List elements); - - - public abstract String getStatusMessage(); - - - public S getClient() { - return client; - } - - - public SearchResult getSearchResult() { - return searchResult; - } - - - public T getTabPanel() { - return tabPanel; + protected void configureSelectDialog(SelectDialog selectDialog) { + selectDialog.setIconImage(TunedUtil.getImage(searchTextField.getSelectButton().getLabelProvider().getIcon(request.getClient()))); } @@ -343,87 +253,126 @@ public abstract class AbstractSearchPanel extends Fi return duration; } + } + - public int getCount() { - return count; + private class SearchTask extends SwingWorker, Void> { + + private final RequestProcessor requestProcessor; + + + public SearchTask(RequestProcessor requestProcessor) { + this.requestProcessor = requestProcessor; + } + + + @Override + protected Collection 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, Void> { - private final FileBotTab tab; - - private CancelAction cancelOnClose; + private final RequestProcessor requestProcessor; - public FetchTaskListener(FileBotTab 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 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 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); - } - - } - } diff --git a/source/net/sourceforge/filebot/ui/FileBotTab.java b/source/net/sourceforge/filebot/ui/FileBotTab.java index b7499454..34b3164c 100644 --- a/source/net/sourceforge/filebot/ui/FileBotTab.java +++ b/source/net/sourceforge/filebot/ui/FileBotTab.java @@ -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 extends JComponent { @@ -20,17 +18,14 @@ public class FileBotTab 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 extends JComponent { public void setLoading(boolean loading) { tabComponent.setLoading(loading); - loadingOverlayPane.setOverlayVisible(loading); } private final ActionListener closeAction = new ActionListener() { diff --git a/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java b/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java index 13c4a1f0..127e8752 100644 --- a/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java +++ b/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java @@ -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 { private SeasonSpinnerModel seasonSpinnerModel = new SeasonSpinnerModel(); - private SelectButtonTextField searchField; - public EpisodeListPanel() { super("Episodes", ResourceManager.getIcon("panel.episodelist")); - setLayout(new BorderLayout()); - - searchField = new SelectButtonTextField(); - - 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 createSearchEngines() { List engines = new ArrayList(3); @@ -138,61 +80,54 @@ public class EpisodeListPanel extends FileBotPanel { } + @Override protected LabelProvider 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 FileExportHandler of the currently selected tab */ + @SuppressWarnings("unchecked") private FileExportHandler getExportHandler() { try { - FileBotList list = (FileBotList) tabbedPane.getSelectedComponent(); + EpisodeListTab list = ((FileBotTab) 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, 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 doInBackground() throws Exception { - return client.search(query); + public int getSeason() { + return season; } } - private class SearchTaskListener extends SwingWorkerPropertyChangeAdapter { + private class EpisodeListRequestProcessor extends RequestProcessor { - private FileBotTab> 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 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 select = new SelectDialog(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 search() throws Exception { + return request.getClient().search(request.getSearchText()); } + + + @Override + public Collection 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 episodes) { + // set a proper title for the export handler before adding episodes + getComponent().setTitle(getTitle()); + + getComponent().getModel().addAll(episodes); + } + + + @Override + public String getStatusMessage(Collection 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 selectDialog) { + super.configureSelectDialog(selectDialog); + selectDialog.getHeaderLabel().setText("Select a Show:"); + } + } - private class FetchEpisodeListTaskListener extends SwingWorkerPropertyChangeAdapter { + private static class EpisodeListTab extends FileBotList { - private FileBotTab> episodeList; - - - public FetchEpisodeListTaskListener(FileBotTab> 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 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); - } - } } } diff --git a/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListTab.java b/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListTab.java deleted file mode 100644 index 2e8e0ca6..00000000 --- a/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListTab.java +++ /dev/null @@ -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> { - - public EpisodeListTab() { - super(new FileBotList()); - - // set export handler for episode list - getComponent().setExportHandler(new FileBotListExportHandler(getComponent())); - - // allow removal of episode list entries - getComponent().getRemoveAction().setEnabled(true); - } - -} diff --git a/source/net/sourceforge/filebot/ui/panel/episodelist/FetchEpisodeListTask.java b/source/net/sourceforge/filebot/ui/panel/episodelist/FetchEpisodeListTask.java deleted file mode 100644 index 3c590058..00000000 --- a/source/net/sourceforge/filebot/ui/panel/episodelist/FetchEpisodeListTask.java +++ /dev/null @@ -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, 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 doInBackground() throws Exception { - long start = System.currentTimeMillis(); - - List list = new ArrayList(); - - 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; - } - -} diff --git a/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerEditor.java b/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerEditor.java index 7e70860e..a48c00f2 100644 --- a/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerEditor.java +++ b/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerEditor.java @@ -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)); diff --git a/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerModel.java b/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerModel.java index 31c5bae7..c6a87368 100644 --- a/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerModel.java +++ b/source/net/sourceforge/filebot/ui/panel/episodelist/SeasonSpinnerModel.java @@ -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); + } } } diff --git a/source/net/sourceforge/filebot/ui/panel/list/ListPanel.java b/source/net/sourceforge/filebot/ui/panel/list/ListPanel.java index 23548f0e..fc9e9656 100644 --- a/source/net/sourceforge/filebot/ui/panel/list/ListPanel.java +++ b/source/net/sourceforge/filebot/ui/panel/list/ListPanel.java @@ -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"); diff --git a/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java b/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java index b1efe38c..410a7c0d 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java @@ -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"); diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java index ec372476..ea92b04e 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java @@ -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); - } - }; - } diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java index 1fdca5b6..95fb6a21 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java @@ -23,13 +23,13 @@ import net.sourceforge.tuned.ui.LabelProvider; import net.sourceforge.tuned.ui.SimpleLabelProvider; -public class SubtitlePanel extends AbstractSearchPanel { +public class SubtitlePanel extends AbstractSearchPanel { 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 { - public SubtitleSearchTask(SubtitleClient client, String searchText, SubtitleDownloadPanel panel) { - super(client, searchText, panel); + public SubtitleRequestProcessor(SubtitleRequest request) { + super(request, new SubtitleDownloadPanel()); } @Override - protected Collection doInBackground() throws Exception { - return getClient().search(getSearchText()); + public Collection search() throws Exception { + return request.getClient().search(request.getSearchText()); } @Override - protected void configureSelectDialog(SelectDialog 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 fetch() throws Exception { - //TODO language combobox - Collection descriptors = getClient().getSubtitleList(getSearchResult(), Locale.ENGLISH); - ArrayList packages = new ArrayList(); + public Collection fetch() throws Exception { + List packages = new ArrayList(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 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 episodes) { + //TODO subtitle tab ui + System.out.println(episodes); } + + + @Override + public String getStatusMessage(Collection 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 selectDialog) { + super.configureSelectDialog(selectDialog); + selectDialog.getHeaderLabel().setText("Select a Show / Movie:"); + } + } } diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleClient.java b/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleClient.java index 2bb06dac..ba27dd74 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleClient.java @@ -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; } diff --git a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java index 455abeae..34b4588f 100644 --- a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java @@ -138,7 +138,7 @@ public class SubsceneSubtitleClient implements SubtitleClient { @Override public List 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(); } diff --git a/source/net/sourceforge/filebot/web/SubtitleClient.java b/source/net/sourceforge/filebot/web/SubtitleClient.java index 3dcab6bd..0c6e1231 100644 --- a/source/net/sourceforge/filebot/web/SubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SubtitleClient.java @@ -17,7 +17,7 @@ public interface SubtitleClient { public Collection getSubtitleList(SearchResult searchResult, Locale language) throws Exception; - public URI getSubtitleListLink(SearchResult searchResult); + public URI getSubtitleListLink(SearchResult searchResult, Locale language); public String getName(); diff --git a/source/net/sourceforge/filebot/web/TVRageClient.java b/source/net/sourceforge/filebot/web/TVRageClient.java index 412fcfc1..1a96bb9a 100644 --- a/source/net/sourceforge/filebot/web/TVRageClient.java +++ b/source/net/sourceforge/filebot/web/TVRageClient.java @@ -172,7 +172,7 @@ public class TVRageClient implements EpisodeListClient { public List 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 nodes = XPathUtil.selectNodes("//Season[@no='" + season + "']/episode", feed); @@ -188,7 +188,6 @@ public class TVRageClient implements EpisodeListClient { return episodes; } - } } diff --git a/source/net/sourceforge/tuned/ui/ProgressIndicator.java b/source/net/sourceforge/tuned/ui/ProgressIndicator.java index 98efcdd0..d11eebae 100644 --- a/source/net/sourceforge/tuned/ui/ProgressIndicator.java +++ b/source/net/sourceforge/tuned/ui/ProgressIndicator.java @@ -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; + } + } diff --git a/source/net/sourceforge/tuned/ui/SelectButton.java b/source/net/sourceforge/tuned/ui/SelectButton.java index e3c7a6a7..0f0ae2b0 100644 --- a/source/net/sourceforge/tuned/ui/SelectButton.java +++ b/source/net/sourceforge/tuned/ui/SelectButton.java @@ -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 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 extends JButton { popup.add(item); } - popup.show(SelectButton.this, -4, getHeight() - 5); + popup.show(SelectButton.this, 0, getHeight() - 1); } } diff --git a/source/net/sourceforge/tuned/ui/SelectButtonTextField.java b/source/net/sourceforge/tuned/ui/SelectButtonTextField.java index a59205bd..6ef5d973 100644 --- a/source/net/sourceforge/tuned/ui/SelectButtonTextField.java +++ b/source/net/sourceforge/tuned/ui/SelectButtonTextField.java @@ -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 extends JPanel { +public class SelectButtonTextField extends JComponent { private SelectButton selectButton = new SelectButton(); - 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 getEditor().getSelectedItem().toString() - */ public String getText() { - return editor.getText(); - } - - - /** - * Convenience method for getSelectButton().getSelectedValue() - */ - public T getSelected() { - return getSelectButton().getSelectedValue(); + return ((TextFieldComboBoxUI) editor.getUI()).getEditor().getText(); } @@ -103,45 +87,7 @@ public class SelectButtonTextField 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 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 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); + } + }; + } } diff --git a/test/net/sourceforge/filebot/web/SubsceneSubtitleClientTest.java b/test/net/sourceforge/filebot/web/SubsceneSubtitleClientTest.java index 0bf97eae..7ed796b7 100644 --- a/test/net/sourceforge/filebot/web/SubsceneSubtitleClientTest.java +++ b/test/net/sourceforge/filebot/web/SubsceneSubtitleClientTest.java @@ -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()); } }