* AutoCompleteSupport now works with the full movie/series/anime index in the back
* update movie/series/anime index files and exclude bad entries
This commit is contained in:
parent
94ceccf966
commit
f466546788
|
@ -76,6 +76,7 @@ def getNamePermutations(names) {
|
|||
res = res.findResults{ fn(it) }
|
||||
}
|
||||
out += res
|
||||
out = out.findAll{ it.length() >= 2 && !(it =~ /^[a-z]/) && it =~ /^[.\p{L}\p{Digit}]/ } // MUST START WITH UNICODE LETTER
|
||||
out = out.unique{ it.toLowerCase().normalizePunctuation() }.findAll{ it.length() > 0 }.toList()
|
||||
out = out.size() <= 4 ? out : out.subList(0, 4)
|
||||
return out
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.Map.Entry;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sourceforge.filebot.Language;
|
||||
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
|
||||
|
@ -169,6 +171,10 @@ public class ArgumentBean {
|
|||
return new Locale(lang);
|
||||
}
|
||||
|
||||
public Language getLanguage() {
|
||||
return Language.findLanguage(lang);
|
||||
}
|
||||
|
||||
public Level getLogLevel() {
|
||||
return Level.parse(log.toUpperCase());
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@ import java.awt.Dimension;
|
|||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -32,13 +33,11 @@ import javax.swing.event.ChangeListener;
|
|||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.similarity.SeriesNameMatcher;
|
||||
import net.sourceforge.filebot.web.SearchResult;
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
import net.sourceforge.tuned.ListChangeSynchronizer;
|
||||
import net.sourceforge.tuned.ui.LabelProvider;
|
||||
import net.sourceforge.tuned.ui.SelectButton;
|
||||
import ca.odell.glazedlists.BasicEventList;
|
||||
import ca.odell.glazedlists.EventList;
|
||||
import ca.odell.glazedlists.matchers.TextMatcherEditor;
|
||||
import ca.odell.glazedlists.swing.AutoCompleteSupport;
|
||||
|
||||
|
@ -52,7 +51,7 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
|
||||
protected final SelectButtonTextField<S> searchTextField = new SelectButtonTextField<S>();
|
||||
|
||||
protected final EventList<String> searchHistory = createSearchHistory();
|
||||
protected final BasicEventList<String> searchHistory = new BasicEventList<String>(100000);
|
||||
|
||||
public AbstractSearchPanel() {
|
||||
historyPanel.setColumnHeader(2, "Duration");
|
||||
|
@ -74,6 +73,35 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
searchTextField.getSelectButton().setModel(Arrays.asList(getSearchEngines()));
|
||||
searchTextField.getSelectButton().setLabelProvider(getSearchEngineLabelProvider());
|
||||
|
||||
searchTextField.getSelectButton().addPropertyChangeListener(SelectButton.SELECTED_VALUE, new PropertyChangeListener() {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
new SwingWorker<Collection<String>, Void>() {
|
||||
|
||||
private final S engine = searchTextField.getSelectButton().getSelectedValue();
|
||||
|
||||
@Override
|
||||
protected Collection<String> doInBackground() throws Exception {
|
||||
return getHistory(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
if (engine == searchTextField.getSelectButton().getSelectedValue()) {
|
||||
try {
|
||||
searchHistory.clear();
|
||||
searchHistory.addAll(get());
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// restore selected subtitle client
|
||||
searchTextField.getSelectButton().setSelectedIndex(Integer.parseInt(getSettings().get("engine.selected", "0")));
|
||||
|
@ -91,10 +119,18 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
}
|
||||
});
|
||||
|
||||
AutoCompleteSupport.install(searchTextField.getEditor(), searchHistory).setFilterMode(TextMatcherEditor.CONTAINS);
|
||||
// high-performance auto-completion
|
||||
AutoCompleteSupport<String> acs = AutoCompleteSupport.install(searchTextField.getEditor(), searchHistory);
|
||||
acs.setTextMatchingStrategy(TextMatcherEditor.IDENTICAL_STRATEGY);
|
||||
acs.setFilterMode(TextMatcherEditor.CONTAINS);
|
||||
acs.setCorrectsCase(true);
|
||||
acs.setStrict(false);
|
||||
|
||||
installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), searchAction);
|
||||
}
|
||||
|
||||
protected abstract Collection<String> getHistory(S engine) throws Exception;
|
||||
|
||||
protected abstract S[] getSearchEngines();
|
||||
|
||||
protected abstract LabelProvider<S> getSearchEngineLabelProvider();
|
||||
|
@ -118,23 +154,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
new SearchTask(requestProcessor).execute();
|
||||
}
|
||||
|
||||
protected EventList<String> createSearchHistory() {
|
||||
// create in-memory history
|
||||
BasicEventList<String> history = new BasicEventList<String>();
|
||||
|
||||
// get the preferences node that contains the history entries
|
||||
// and get a StringList that read and writes directly from and to the preferences
|
||||
List<String> persistentHistory = getSettings().node("history").asList();
|
||||
|
||||
// add history from the preferences to the current in-memory history (for completion)
|
||||
history.addAll(persistentHistory);
|
||||
|
||||
// perform all insert/add/remove operations on the in-memory history on the preferences node as well
|
||||
ListChangeSynchronizer.syncEventListToList(history, persistentHistory);
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
private final AbstractAction searchAction = new AbstractAction("Find", ResourceManager.getIcon("action.find")) {
|
||||
|
||||
@Override
|
||||
|
@ -200,12 +219,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
// set search result
|
||||
requestProcessor.setSearchResult(selectedSearchResult);
|
||||
|
||||
String historyEntry = requestProcessor.getHistoryEntry();
|
||||
|
||||
if (historyEntry != null && !searchHistory.contains(historyEntry)) {
|
||||
searchHistory.add(historyEntry);
|
||||
}
|
||||
|
||||
tab.setTitle(requestProcessor.getTitle());
|
||||
|
||||
// fetch elements of the selected search result
|
||||
|
@ -330,14 +343,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
return request.getSearchText();
|
||||
}
|
||||
|
||||
public String getHistoryEntry() {
|
||||
SeriesNameMatcher nameMatcher = new SeriesNameMatcher();
|
||||
|
||||
// the common word sequence of query and search result
|
||||
// common name will maintain the exact word characters (incl. case) of the first argument
|
||||
return nameMatcher.matchByFirstCommonWordSequence(searchResult.getName(), request.getSearchText());
|
||||
}
|
||||
|
||||
public Icon getIcon() {
|
||||
return null;
|
||||
}
|
||||
|
@ -362,7 +367,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
|||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package net.sourceforge.filebot.ui;
|
||||
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
@ -34,185 +32,170 @@ import net.sourceforge.filebot.ResourceManager;
|
|||
import net.sourceforge.tuned.ui.SelectButton;
|
||||
import net.sourceforge.tuned.ui.TunedUtilities;
|
||||
|
||||
|
||||
public class SelectButtonTextField<T> extends JComponent {
|
||||
|
||||
|
||||
private SelectButton<T> selectButton = new SelectButton<T>();
|
||||
|
||||
|
||||
private JComboBox editor = new JComboBox();
|
||||
|
||||
|
||||
|
||||
public SelectButtonTextField() {
|
||||
selectButton.addActionListener(textFieldFocusOnClick);
|
||||
|
||||
|
||||
editor.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 1, ((LineBorder) selectButton.getBorder()).getLineColor()));
|
||||
|
||||
|
||||
setLayout(new MigLayout("nogrid, fill"));
|
||||
add(selectButton, "h pref!, w pref!, sizegroupy this");
|
||||
add(editor, "gap 0, w 195px!, sizegroupy this");
|
||||
|
||||
|
||||
editor.setRenderer(new CompletionCellRenderer());
|
||||
editor.setPrototypeDisplayValue("X");
|
||||
editor.setUI(new TextFieldComboBoxUI());
|
||||
|
||||
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.CTRL_MASK), new SpinClientAction(-1));
|
||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.CTRL_MASK), new SpinClientAction(1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getText() {
|
||||
return ((TextFieldComboBoxUI) editor.getUI()).getEditor().getText();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public JComboBox getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SelectButton<T> getSelectButton() {
|
||||
return selectButton;
|
||||
}
|
||||
|
||||
|
||||
private final ActionListener textFieldFocusOnClick = new ActionListener() {
|
||||
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getEditor().requestFocus();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
private class SpinClientAction extends AbstractAction {
|
||||
|
||||
|
||||
private int spin;
|
||||
|
||||
|
||||
|
||||
public SpinClientAction(int spin) {
|
||||
super(String.format("Spin%+d", spin));
|
||||
this.spin = spin;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
selectButton.spinValue(spin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class CompletionCellRenderer extends DefaultListCellRenderer {
|
||||
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
|
||||
|
||||
setBorder(new EmptyBorder(1, 4, 1, 4));
|
||||
|
||||
|
||||
String highlightText = SelectButtonTextField.this.getText().substring(0, ((TextFieldComboBoxUI) editor.getUI()).getEditor().getSelectionStart());
|
||||
|
||||
|
||||
// highlight the matching sequence
|
||||
Matcher matcher = Pattern.compile(highlightText, Pattern.LITERAL | Pattern.CASE_INSENSITIVE).matcher(value.toString());
|
||||
|
||||
|
||||
// use no-break, because we really don't want line-wrapping in our table cells
|
||||
StringBuffer htmlText = new StringBuffer("<html><nobr>");
|
||||
|
||||
|
||||
if (matcher.find()) {
|
||||
matcher.appendReplacement(htmlText, "<span style='font-weight: bold; text-decoration: underline;'>$0</span>");
|
||||
}
|
||||
|
||||
|
||||
matcher.appendTail(htmlText);
|
||||
|
||||
|
||||
htmlText.append("</nobr></html>");
|
||||
|
||||
|
||||
setText(htmlText.toString());
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class TextFieldComboBoxUI extends BasicComboBoxUI {
|
||||
|
||||
|
||||
@Override
|
||||
protected JButton createArrowButton() {
|
||||
return new JButton(ResourceManager.getIcon("action.list"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void configureArrowButton() {
|
||||
super.configureArrowButton();
|
||||
|
||||
|
||||
arrowButton.setContentAreaFilled(false);
|
||||
arrowButton.setFocusable(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@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());
|
||||
|
||||
|
||||
editor.getDocument().addDocumentListener(new DocumentListener() {
|
||||
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
popup.getList().repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
popup.getList().repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
popup.getList().repaint();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
popup.getList().setPrototypeCellValue("X");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public JTextComponent getEditor() {
|
||||
return (JTextComponent) editor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@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.
|
||||
*/
|
||||
|
@ -224,7 +207,7 @@ public class SelectButtonTextField<T> extends JComponent {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -13,10 +13,12 @@ import java.beans.PropertyChangeListener;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Icon;
|
||||
|
@ -26,13 +28,15 @@ import javax.swing.JSpinner;
|
|||
import javax.swing.KeyStroke;
|
||||
|
||||
import net.sourceforge.filebot.Analytics;
|
||||
import net.sourceforge.filebot.Language;
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.WebServices;
|
||||
import net.sourceforge.filebot.media.MediaDetection;
|
||||
import net.sourceforge.filebot.similarity.Normalization;
|
||||
import net.sourceforge.filebot.ui.AbstractSearchPanel;
|
||||
import net.sourceforge.filebot.ui.FileBotList;
|
||||
import net.sourceforge.filebot.ui.FileBotListExportHandler;
|
||||
import net.sourceforge.filebot.ui.FileBotTab;
|
||||
import net.sourceforge.filebot.Language;
|
||||
import net.sourceforge.filebot.ui.LanguageComboBox;
|
||||
import net.sourceforge.filebot.ui.SelectDialog;
|
||||
import net.sourceforge.filebot.ui.transfer.ArrayTransferable;
|
||||
|
@ -77,6 +81,18 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
|||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK), new SpinSeasonAction(-1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getHistory(EpisodeListProvider engine) throws Exception {
|
||||
final List<String> names = new ArrayList<String>(100000);
|
||||
final SearchResult[] index = (engine == WebServices.AniDB) ? MediaDetection.releaseInfo.getAnidbIndex() : MediaDetection.releaseInfo.getTheTVDBIndex();
|
||||
for (SearchResult it : index) {
|
||||
for (String n : it.getEffectiveNames()) {
|
||||
names.add(Normalization.removeTrailingBrackets(n));
|
||||
}
|
||||
}
|
||||
return new TreeSet<String>(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EpisodeListProvider[] getSearchEngines() {
|
||||
return WebServices.getEpisodeListProviders();
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.net.URI;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
|
@ -32,13 +33,16 @@ import javax.swing.JTextField;
|
|||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.Language;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.WebServices;
|
||||
import net.sourceforge.filebot.media.MediaDetection;
|
||||
import net.sourceforge.filebot.similarity.Normalization;
|
||||
import net.sourceforge.filebot.ui.AbstractSearchPanel;
|
||||
import net.sourceforge.filebot.Language;
|
||||
import net.sourceforge.filebot.ui.LanguageComboBox;
|
||||
import net.sourceforge.filebot.ui.SelectDialog;
|
||||
import net.sourceforge.filebot.web.Movie;
|
||||
import net.sourceforge.filebot.web.OpenSubtitlesClient;
|
||||
import net.sourceforge.filebot.web.SearchResult;
|
||||
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
||||
|
@ -138,6 +142,19 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
|||
}
|
||||
};
|
||||
|
||||
protected Collection<String> getHistory(SubtitleProvider engine) throws Exception {
|
||||
final List<String> names = new ArrayList<String>(200000);
|
||||
for (Movie it : MediaDetection.releaseInfo.getMovieList()) {
|
||||
names.addAll(it.getEffectiveNamesWithoutYear());
|
||||
}
|
||||
for (SearchResult it : MediaDetection.releaseInfo.getTheTVDBIndex()) {
|
||||
for (String n : it.getEffectiveNames()) {
|
||||
names.add(Normalization.removeTrailingBrackets(n));
|
||||
}
|
||||
}
|
||||
return new TreeSet<String>(names);
|
||||
};
|
||||
|
||||
@Override
|
||||
protected SubtitleProvider[] getSearchEngines() {
|
||||
return WebServices.getSubtitleProviders();
|
||||
|
|
|
@ -155,7 +155,7 @@ public class AnidbClient extends AbstractEpisodeListProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* This method is (and must be!) overridden by {@link WebServices.AnidbClientWithLocalSearch} to use our own anime index from sourceforge (as to not abuse anidb servers)
|
||||
* This method is (and must be!) overridden by WebServices.AnidbClientWithLocalSearch to use our own anime index from sourceforge (as to not abuse anidb servers)
|
||||
*/
|
||||
public synchronized List<AnidbSearchResult> getAnimeTitles() throws Exception {
|
||||
URL url = new URL("http", host, "/api/anime-titles.dat.gz");
|
||||
|
@ -176,6 +176,11 @@ public class AnidbClient extends AbstractEpisodeListProvider {
|
|||
languageOrder.add("en");
|
||||
languageOrder.add("ja");
|
||||
|
||||
List<String> typeOrder = new ArrayList<String>();
|
||||
typeOrder.add("1");
|
||||
typeOrder.add("4");
|
||||
typeOrder.add("2");
|
||||
|
||||
// fetch data
|
||||
Map<Integer, List<Object[]>> entriesByAnime = new HashMap<Integer, List<Object[]>>(65536);
|
||||
|
||||
|
@ -190,7 +195,7 @@ public class AnidbClient extends AbstractEpisodeListProvider {
|
|||
String language = matcher.group(3);
|
||||
String title = matcher.group(4);
|
||||
|
||||
if (aid > 0 && title.length() > 0 && languageOrder.contains(language)) {
|
||||
if (aid > 0 && title.length() > 0 && typeOrder.contains(type) && languageOrder.contains(language)) {
|
||||
List<Object[]> names = entriesByAnime.get(aid);
|
||||
if (names == null) {
|
||||
names = new ArrayList<Object[]>();
|
||||
|
@ -200,7 +205,7 @@ public class AnidbClient extends AbstractEpisodeListProvider {
|
|||
// resolve HTML entities
|
||||
title = Jsoup.parse(title).text();
|
||||
|
||||
names.add(new Object[] { Integer.parseInt(type), languageOrder.indexOf(language), title });
|
||||
names.add(new Object[] { typeOrder.indexOf(type), languageOrder.indexOf(language), title });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package net.sourceforge.tuned.ui;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
|
@ -29,82 +27,72 @@ import javax.swing.JPopupMenu;
|
|||
import javax.swing.SingleSelectionModel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
|
||||
public class SelectButton<T> extends JButton {
|
||||
|
||||
|
||||
public static final String SELECTED_VALUE = "selected value";
|
||||
|
||||
|
||||
private final Color beginColor = new Color(0xF0EEE4);
|
||||
private final Color endColor = new Color(0xE0DED4);
|
||||
|
||||
|
||||
private final Color beginColorHover = beginColor;
|
||||
private final Color endColorHover = new Color(0xD8D7CD);
|
||||
|
||||
|
||||
private final SelectIcon selectIcon = new SelectIcon();
|
||||
|
||||
|
||||
private List<T> model = Collections.emptyList();
|
||||
private SingleSelectionModel selectionModel = new DefaultSingleSelectionModel();
|
||||
|
||||
|
||||
private LabelProvider<T> labelProvider = new NullLabelProvider<T>();
|
||||
|
||||
|
||||
private boolean hover = false;
|
||||
|
||||
|
||||
public SelectButton() {
|
||||
setContentAreaFilled(false);
|
||||
setFocusable(false);
|
||||
|
||||
|
||||
super.setIcon(selectIcon);
|
||||
|
||||
|
||||
setHorizontalAlignment(SwingConstants.CENTER);
|
||||
setVerticalAlignment(SwingConstants.CENTER);
|
||||
|
||||
|
||||
setBorder(BorderFactory.createLineBorder(new Color(0xA4A4A4), 1));
|
||||
setPreferredSize(new Dimension(32, 22));
|
||||
|
||||
|
||||
addActionListener(new OpenPopupOnClick());
|
||||
}
|
||||
|
||||
|
||||
public void setModel(Collection<T> model) {
|
||||
this.model = new ArrayList<T>(model);
|
||||
|
||||
setSelectedIndex(0);
|
||||
}
|
||||
|
||||
|
||||
public LabelProvider<T> getLabelProvider() {
|
||||
return labelProvider;
|
||||
}
|
||||
|
||||
|
||||
public void setLabelProvider(LabelProvider<T> labelProvider) {
|
||||
this.labelProvider = labelProvider;
|
||||
|
||||
|
||||
// update icon
|
||||
this.setIcon(labelProvider.getIcon(getSelectedValue()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setIcon(Icon icon) {
|
||||
selectIcon.setInnerIcon(icon);
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
public void setSelectedValue(T value) {
|
||||
setSelectedIndex(model.indexOf(value));
|
||||
}
|
||||
|
||||
|
||||
public T getSelectedValue() {
|
||||
if (!selectionModel.isSelected())
|
||||
return null;
|
||||
|
||||
|
||||
return model.get(selectionModel.getSelectedIndex());
|
||||
}
|
||||
|
||||
|
||||
public void setSelectedIndex(int i) {
|
||||
if (i < 0 || i >= model.size()) {
|
||||
|
@ -112,171 +100,150 @@ public class SelectButton<T> extends JButton {
|
|||
setIcon(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (i != selectionModel.getSelectedIndex()) {
|
||||
selectionModel.setSelectedIndex(i);
|
||||
|
||||
T value = model.get(i);
|
||||
|
||||
setIcon(labelProvider.getIcon(value));
|
||||
|
||||
firePropertyChange(SELECTED_VALUE, null, value);
|
||||
}
|
||||
|
||||
selectionModel.setSelectedIndex(i);
|
||||
T value = model.get(i);
|
||||
setIcon(labelProvider.getIcon(value));
|
||||
firePropertyChange(SELECTED_VALUE, null, value);
|
||||
}
|
||||
|
||||
|
||||
public int getSelectedIndex() {
|
||||
return selectionModel.getSelectedIndex();
|
||||
}
|
||||
|
||||
|
||||
public SingleSelectionModel getSelectionModel() {
|
||||
return selectionModel;
|
||||
}
|
||||
|
||||
|
||||
public void spinValue(int spin) {
|
||||
int size = model.size();
|
||||
|
||||
|
||||
spin = spin % size;
|
||||
|
||||
|
||||
int next = getSelectedIndex() + spin;
|
||||
|
||||
|
||||
if (next < 0)
|
||||
next += size;
|
||||
else if (next >= size)
|
||||
next -= size;
|
||||
|
||||
|
||||
setSelectedIndex(next);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
Rectangle bounds = new Rectangle(getSize());
|
||||
|
||||
|
||||
if (hover)
|
||||
g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(bounds, beginColorHover, endColorHover));
|
||||
else
|
||||
g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(bounds, beginColor, endColor));
|
||||
|
||||
|
||||
g2d.fill(bounds);
|
||||
|
||||
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void processMouseEvent(MouseEvent e) {
|
||||
switch (e.getID()) {
|
||||
case MouseEvent.MOUSE_ENTERED:
|
||||
hover = true;
|
||||
repaint();
|
||||
break;
|
||||
case MouseEvent.MOUSE_EXITED:
|
||||
hover = false;
|
||||
repaint();
|
||||
break;
|
||||
case MouseEvent.MOUSE_ENTERED:
|
||||
hover = true;
|
||||
repaint();
|
||||
break;
|
||||
case MouseEvent.MOUSE_EXITED:
|
||||
hover = false;
|
||||
repaint();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
super.processMouseEvent(e);
|
||||
}
|
||||
|
||||
|
||||
private class OpenPopupOnClick implements ActionListener {
|
||||
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
|
||||
|
||||
for (T value : model) {
|
||||
SelectPopupMenuItem item = new SelectPopupMenuItem(labelProvider.getText(value), labelProvider.getIcon(value), value);
|
||||
|
||||
|
||||
if (value == getSelectedValue())
|
||||
item.setSelected(true);
|
||||
|
||||
|
||||
popup.add(item);
|
||||
}
|
||||
|
||||
|
||||
popup.show(SelectButton.this, 0, getHeight() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class SelectPopupMenuItem extends JMenuItem implements ActionListener {
|
||||
|
||||
|
||||
private final T value;
|
||||
|
||||
|
||||
public SelectPopupMenuItem(String text, Icon icon, T value) {
|
||||
super(text, icon);
|
||||
|
||||
|
||||
this.value = value;
|
||||
|
||||
|
||||
setMargin(new Insets(3, 0, 3, 0));
|
||||
addActionListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setSelected(boolean selected) {
|
||||
setFont(getFont().deriveFont(selected ? Font.BOLD : Font.PLAIN));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
setSelectedValue(value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class SelectIcon implements Icon {
|
||||
|
||||
|
||||
private final GeneralPath arrow;
|
||||
|
||||
|
||||
private Icon icon;
|
||||
|
||||
|
||||
public SelectIcon() {
|
||||
arrow = new GeneralPath(Path2D.WIND_EVEN_ODD, 3);
|
||||
int x = 25;
|
||||
int y = 10;
|
||||
|
||||
|
||||
arrow.moveTo(x - 2, y);
|
||||
arrow.lineTo(x, y + 3);
|
||||
arrow.lineTo(x + 3, y);
|
||||
arrow.lineTo(x - 2, y);
|
||||
}
|
||||
|
||||
|
||||
public void setInnerIcon(Icon icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
|
||||
|
||||
if (icon != null) {
|
||||
icon.paintIcon(c, g2d, 4, 3);
|
||||
}
|
||||
|
||||
|
||||
g2d.setPaint(Color.BLACK);
|
||||
g2d.fill(arrow);
|
||||
}
|
||||
|
||||
|
||||
public int getIconWidth() {
|
||||
return 30;
|
||||
}
|
||||
|
||||
|
||||
public int getIconHeight() {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
^Video$
|
||||
^VIDEO_TS$
|
||||
^Videos$
|
||||
^volume[0-9]?$
|
||||
^volume\D?\d?$
|
||||
^Volumes$
|
||||
^watch$
|
||||
^www$
|
||||
|
|
Loading…
Reference in New Issue