1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-12-23 16:28:51 -05:00

Make Episode/Movie selection dialog more pretty

This commit is contained in:
Reinhard Pointner 2016-04-09 20:58:37 +00:00
parent 5fc3a90159
commit 2fac737052
6 changed files with 56 additions and 37 deletions

View File

@ -31,17 +31,17 @@ import net.miginfocom.swing.MigLayout;
public class SelectDialog<T> extends JDialog { public class SelectDialog<T> extends JDialog {
private JLabel headerLabel = new JLabel(); private JLabel messageLabel = new JLabel();
private JCheckBox autoRepeatCheckBox = new JCheckBox(); private JCheckBox autoRepeatCheckBox = new JCheckBox();
private JList<T> list; private JList<T> list;
private String command = null; private String command = null;
public SelectDialog(Component parent, Collection<? extends T> options) { public SelectDialog(Component parent, Collection<? extends T> options) {
this(parent, options, false, false); this(parent, options, false, false, null);
} }
public SelectDialog(Component parent, Collection<? extends T> options, boolean autoRepeatEnabled, boolean autoRepeatSelected) { public SelectDialog(Component parent, Collection<? extends T> options, boolean autoRepeatEnabled, boolean autoRepeatSelected, JComponent header) {
super(getWindow(parent), "Select", ModalityType.DOCUMENT_MODAL); super(getWindow(parent), "Select", ModalityType.DOCUMENT_MODAL);
setDefaultCloseOperation(DISPOSE_ON_CLOSE); setDefaultCloseOperation(DISPOSE_ON_CLOSE);
@ -69,9 +69,12 @@ public class SelectDialog<T> extends JDialog {
list.addMouseListener(mouseListener); list.addMouseListener(mouseListener);
JComponent c = (JComponent) getContentPane(); JComponent c = (JComponent) getContentPane();
c.setLayout(new MigLayout("insets 1.5mm 1.5mm 2.7mm 1.5mm, nogrid, fill", "", "[pref!][fill][pref!]")); c.setLayout(new MigLayout("insets 1.5mm 1.5mm 2.7mm 1.5mm, nogrid, fill", "", header == null ? "[pref!][fill][pref!]" : "[pref!][pref!][fill][pref!]"));
c.add(headerLabel, "wmin 150px, growx, wrap"); if (header != null) {
c.add(header, "wmin 150px, growx, wrap");
}
c.add(messageLabel, "wmin 150px, growx, wrap");
c.add(new JScrollPane(list), "wmin 150px, hmin 150px, grow, wrap 2mm"); c.add(new JScrollPane(list), "wmin 150px, hmin 150px, grow, wrap 2mm");
c.add(new JButton(selectAction), "align center, id select"); c.add(new JButton(selectAction), "align center, id select");
@ -122,8 +125,8 @@ public class SelectDialog<T> extends JDialog {
return html.toString(); return html.toString();
} }
public JLabel getHeaderLabel() { public JLabel getMessageLabel() {
return headerLabel; return messageLabel;
} }
public JCheckBox getAutoRepeatCheckBox() { public JCheckBox getAutoRepeatCheckBox() {

View File

@ -224,7 +224,7 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
@Override @Override
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) { protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
super.configureSelectDialog(selectDialog); super.configureSelectDialog(selectDialog);
selectDialog.getHeaderLabel().setText("Select a Show:"); selectDialog.getMessageLabel().setText("Select a Show:");
} }
} }

View File

@ -3,6 +3,7 @@ package net.filebot.ui.rename;
import static java.util.Collections.*; import static java.util.Collections.*;
import static java.util.Comparator.*; import static java.util.Comparator.*;
import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.*;
import static javax.swing.BorderFactory.*;
import static net.filebot.MediaTypes.*; import static net.filebot.MediaTypes.*;
import static net.filebot.Settings.*; import static net.filebot.Settings.*;
import static net.filebot.WebServices.*; import static net.filebot.WebServices.*;
@ -35,6 +36,7 @@ import java.util.prefs.Preferences;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import net.filebot.Cache; import net.filebot.Cache;
@ -62,13 +64,13 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
return Cache.getCache("selection_" + provider.getName(), CacheType.Persistent).cast(SearchResult.class); return Cache.getCache("selection_" + provider.getName(), CacheType.Persistent).cast(SearchResult.class);
} }
protected SearchResult selectSearchResult(List<File> files, String query, List<SearchResult> searchResults, Map<String, SearchResult> selectionMemory, boolean autodetection, Component parent) throws Exception { protected SearchResult selectSearchResult(List<File> files, String query, List<SearchResult> options, Map<String, SearchResult> selectionMemory, boolean autodetection, Component parent) throws Exception {
if (searchResults.size() == 1) { if (options.size() == 1) {
return searchResults.get(0); return options.get(0);
} }
// auto-select most probable search result // auto-select most probable search result
List<SearchResult> probableMatches = getProbableMatches(query, searchResults, true, true); List<SearchResult> probableMatches = getProbableMatches(query, options, true, true);
// auto-select first and only probable search result // auto-select first and only probable search result
if (probableMatches.size() == 1) { if (probableMatches.size() == 1) {
@ -77,11 +79,14 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
// show selection dialog on EDT // show selection dialog on EDT
RunnableFuture<SearchResult> showSelectDialog = new FutureTask<SearchResult>(() -> { RunnableFuture<SearchResult> showSelectDialog = new FutureTask<SearchResult>(() -> {
JLabel header = new JLabel(getQueryInputMessage("Failed to identify some of the following files:", null, getFilesForQuery(files, query)));
header.setBorder(createCompoundBorder(createTitledBorder(""), createEmptyBorder(3, 3, 3, 3)));
// multiple results have been found, user must select one // multiple results have been found, user must select one
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(parent, searchResults, true, false); SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(parent, options, true, false, header);
selectDialog.setTitle(provider.getName()); selectDialog.setTitle(provider.getName());
selectDialog.getHeaderLabel().setText(getQueryInputMessage(String.format("Select best match for \"<b>%s</b>\":", query), getFilesForQuery(files, query))); selectDialog.getMessageLabel().setText("<html>Select best match for \"<b>" + query + "</b>\":</html>");
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore"); selectDialog.getCancelAction().putValue(Action.NAME, "Skip");
selectDialog.pack(); selectDialog.pack();
// show dialog // show dialog
@ -137,9 +142,10 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
List<Future<List<Episode>>> tasks = querySet.stream().map(q -> { List<Future<List<Episode>>> tasks = querySet.stream().map(q -> {
return requestThreadPool.submit(() -> { return requestThreadPool.submit(() -> {
// select search result // select search result
List<SearchResult> results = provider.search(q, locale); List<SearchResult> options = provider.search(q, locale);
if (results.size() > 0) {
SearchResult selectedSearchResult = selectSearchResult(files, q, results, selectionMemory, autodetection, parent); if (options.size() > 0) {
SearchResult selectedSearchResult = selectSearchResult(files, q, options, selectionMemory, autodetection, parent);
if (selectedSearchResult != null) { if (selectedSearchResult != null) {
return provider.getEpisodeList(selectedSearchResult, sortOrder, locale); return provider.getEpisodeList(selectedSearchResult, sortOrder, locale);
} }
@ -237,7 +243,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
} }
public List<Match<File, ?>> matchEpisodeSet(List<File> files, Collection<String> queries, SortOrder sortOrder, boolean strict, Locale locale, boolean autodetection, Map<String, SearchResult> selectionMemory, Map<String, List<String>> inputMemory, Component parent) throws Exception { public List<Match<File, ?>> matchEpisodeSet(List<File> files, Collection<String> queries, SortOrder sortOrder, boolean strict, Locale locale, boolean autodetection, Map<String, SearchResult> selectionMemory, Map<String, List<String>> inputMemory, Component parent) throws Exception {
Set<Episode> episodes = emptySet(); Collection<Episode> episodes = emptySet();
// detect series name and fetch episode list // detect series name and fetch episode list
if (autodetection) { if (autodetection) {
@ -250,13 +256,12 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
// require user input if auto-detection has failed or has been disabled // require user input if auto-detection has failed or has been disabled
if (episodes.isEmpty() && !strict) { if (episodes.isEmpty() && !strict) {
List<String> detectedSeriesNames = detectSeriesNames(files, anime, locale); List<String> detectedSeriesNames = detectSeriesNames(files, anime, locale);
String parentPathHint = normalizePathSeparators(getRelativePathTail(files.get(0).getParentFile(), 2).getPath());
String suggestion = detectedSeriesNames.size() > 0 ? join(detectedSeriesNames, "; ") : normalizePunctuation(getName(files.get(0))); String suggestion = detectedSeriesNames.size() > 0 ? join(detectedSeriesNames, "; ") : normalizePunctuation(getName(files.get(0)));
synchronized (inputMemory) { synchronized (inputMemory) {
List<String> input = inputMemory.get(suggestion); List<String> input = inputMemory.get(suggestion);
if (input == null || suggestion == null || suggestion.isEmpty()) { if (input == null || suggestion == null || suggestion.isEmpty()) {
input = showMultiValueInputDialog(getQueryInputMessage("Enter series name:", files), suggestion, parentPathHint, parent); input = showMultiValueInputDialog(getQueryInputMessage("Please identify the following files:", "Enter series name:", files), suggestion, provider.getName(), parent);
inputMemory.put(suggestion, input); inputMemory.put(suggestion, input);
} }
@ -292,14 +297,15 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
return selection.size() > 0 ? selection : files; return selection.size() > 0 ? selection : files;
} }
protected String getQueryInputMessage(String message, Collection<File> files) throws Exception { protected String getQueryInputMessage(String header, String message, Collection<File> files) throws Exception {
List<File> selection = files.stream().sorted(comparing(File::length).reversed()).limit(5).collect(toList()); List<File> selection = files.stream().sorted(comparing(File::length).reversed()).limit(5).collect(toList());
StringBuilder html = new StringBuilder(512); StringBuilder html = new StringBuilder(512);
html.append("<html>"); html.append("<html>");
if (selection.size() > 0) { if (selection.size() > 0) {
html.append("Failed to identify some of the following files:").append("<br>"); if (header != null) {
html.append(header).append("<br>");
}
for (File file : sortByUniquePath(selection)) { for (File file : sortByUniquePath(selection)) {
html.append("<nobr>"); html.append("<nobr>");
html.append(""); html.append("");
@ -313,14 +319,14 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
html.append("</nobr>"); html.append("</nobr>");
html.append("<br>"); html.append("<br>");
} }
if (selection.size() < files.size()) { if (selection.size() < files.size()) {
html.append("").append("").append("<br>"); html.append("").append("").append("<br>");
} }
html.append("<br>"); html.append("<br>");
} }
html.append(message); if (message != null) {
html.append(message);
}
html.append("</html>"); html.append("</html>");
return html.toString(); return html.toString();
} }
@ -331,7 +337,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>(); List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();
if (input.size() > 0) { if (input.size() > 0) {
Set<Episode> episodes = fetchEpisodeSet(emptyList(), input, sortOrder, locale, new HashMap<String, SearchResult>(), false, parent); Collection<Episode> episodes = fetchEpisodeSet(emptyList(), input, sortOrder, locale, new HashMap<String, SearchResult>(), false, parent);
for (Episode it : episodes) { for (Episode it : episodes) {
matches.add(new Match<File, Episode>(null, it)); matches.add(new Match<File, Episode>(null, it));
} }

View File

@ -3,6 +3,7 @@ package net.filebot.ui.rename;
import static java.util.Collections.*; import static java.util.Collections.*;
import static java.util.Comparator.*; import static java.util.Comparator.*;
import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.*;
import static javax.swing.BorderFactory.*;
import static net.filebot.Logging.*; import static net.filebot.Logging.*;
import static net.filebot.MediaTypes.*; import static net.filebot.MediaTypes.*;
import static net.filebot.Settings.*; import static net.filebot.Settings.*;
@ -38,6 +39,7 @@ import java.util.logging.Level;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import net.filebot.similarity.Match; import net.filebot.similarity.Match;
@ -229,7 +231,7 @@ class MovieMatcher implements AutoCompleteMatcher {
if (input == null || suggestion == null || suggestion.isEmpty()) { if (input == null || suggestion == null || suggestion.isEmpty()) {
File movieFolder = guessMovieFolder(movieFile); File movieFolder = guessMovieFolder(movieFile);
input = showInputDialog(getQueryInputMessage("Enter movie name:", movieFile), suggestion != null && suggestion.length() > 0 ? suggestion : getName(movieFile), movieFolder == null ? movieFile.getName() : String.join(" / ", movieFolder.getName(), movieFile.getName()), parent); input = showInputDialog(getQueryInputMessage("Please identify the following files:", "Enter movie name:", movieFile), suggestion != null && suggestion.length() > 0 ? suggestion : getName(movieFile), movieFolder == null ? movieFile.getName() : String.join(" / ", movieFolder.getName(), movieFile.getName()), parent);
inputMemory.put(suggestion, input); inputMemory.put(suggestion, input);
} }
@ -245,10 +247,12 @@ class MovieMatcher implements AutoCompleteMatcher {
return options.isEmpty() ? null : selectMovie(movieFile, strict, null, options, autoSelectionMode, selectionMemory, parent); return options.isEmpty() ? null : selectMovie(movieFile, strict, null, options, autoSelectionMode, selectionMemory, parent);
} }
protected String getQueryInputMessage(String message, File file) throws Exception { protected String getQueryInputMessage(String header, String message, File file) throws Exception {
StringBuilder html = new StringBuilder(512); StringBuilder html = new StringBuilder(512);
html.append("<html>"); html.append("<html>");
html.append("Failed to identify some of the following files:").append("<br>"); if (header != null) {
html.append(header).append("<br>");
}
html.append("<nobr>"); html.append("<nobr>");
html.append(""); html.append("");
@ -263,7 +267,9 @@ class MovieMatcher implements AutoCompleteMatcher {
html.append("<br>"); html.append("<br>");
html.append("<br>"); html.append("<br>");
html.append(message); if (message != null) {
html.append(message);
}
html.append("</html>"); html.append("</html>");
return html.toString(); return html.toString();
} }
@ -342,12 +348,16 @@ class MovieMatcher implements AutoCompleteMatcher {
// show selection dialog on EDT // show selection dialog on EDT
RunnableFuture<Movie> showSelectDialog = new FutureTask<Movie>(() -> { RunnableFuture<Movie> showSelectDialog = new FutureTask<Movie>(() -> {
String query = fileQuery.length() >= 2 || folderQuery.length() <= 2 ? fileQuery : folderQuery;
JLabel header = new JLabel(getQueryInputMessage("Failed to identify some of the following files:", null, movieFile));
header.setBorder(createCompoundBorder(createTitledBorder(""), createEmptyBorder(3, 3, 3, 3)));
// multiple results have been found, user must select one // multiple results have been found, user must select one
SelectDialog<Movie> selectDialog = new SelectDialog<Movie>(parent, options, true, false); SelectDialog<Movie> selectDialog = new SelectDialog<Movie>(parent, options, true, false, header);
selectDialog.setTitle(service.getName()); selectDialog.setTitle(service.getName());
selectDialog.getHeaderLabel().setText(getQueryInputMessage(String.format("Select best match for \"<b>%s</b>\":", fileQuery.length() >= 2 || folderQuery.length() <= 2 ? fileQuery : folderQuery), movieFile)); selectDialog.getMessageLabel().setText("<html>Select best match for \"<b>" + query + "</b>\":</html>");
selectDialog.getCancelAction().putValue(Action.NAME, AutoSelection.Skip.toString()); selectDialog.getCancelAction().putValue(Action.NAME, "Skip");
selectDialog.pack(); selectDialog.pack();
// show dialog // show dialog

View File

@ -278,7 +278,7 @@ public class SfvPanel extends JComponent {
} }
}; };
selectDialog.getHeaderLabel().setText("Select checksum column:"); selectDialog.getMessageLabel().setText("Select checksum column:");
selectDialog.pack(); selectDialog.pack();
selectDialog.setLocationRelativeTo(SfvPanel.this); selectDialog.setLocationRelativeTo(SfvPanel.this);
selectDialog.setVisible(true); selectDialog.setVisible(true);

View File

@ -329,7 +329,7 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
@Override @Override
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) { protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
super.configureSelectDialog(selectDialog); super.configureSelectDialog(selectDialog);
selectDialog.getHeaderLabel().setText("Select a Show / Movie:"); selectDialog.getMessageLabel().setText("Select a Show / Movie:");
} }
} }