mirror of
https://github.com/mitb-archive/filebot
synced 2024-12-24 08:48:51 -05:00
Make Episode/Movie selection dialog more pretty
This commit is contained in:
parent
5fc3a90159
commit
2fac737052
@ -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() {
|
||||||
|
@ -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:");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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>");
|
||||||
}
|
}
|
||||||
|
if (message != null) {
|
||||||
html.append(message);
|
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));
|
||||||
}
|
}
|
||||||
|
@ -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>");
|
||||||
|
if (message != null) {
|
||||||
html.append(message);
|
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
|
||||||
|
@ -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);
|
||||||
|
@ -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:");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user