diff --git a/source/net/filebot/ui/rename/EpisodeListMatcher.java b/source/net/filebot/ui/rename/EpisodeListMatcher.java index fa0c29ba..19123fb2 100644 --- a/source/net/filebot/ui/rename/EpisodeListMatcher.java +++ b/source/net/filebot/ui/rename/EpisodeListMatcher.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -231,7 +232,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { } // show selection dialog on EDT - RunnableFuture showSelectDialog = new FutureTask(() -> { + Callable showSelectDialog = () -> { 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))); @@ -261,7 +262,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { // selected value or null if the dialog was canceled by the user return selectDialog.getSelectedValue(); - }); + }; synchronized (selectionMemory) { if (selectionMemory.containsKey(query)) { @@ -277,14 +278,11 @@ class EpisodeListMatcher implements AutoCompleteMatcher { } // allow only one select dialog at a time - synchronized (INPUT_DIALOG_LOCK) { - SwingUtilities.invokeAndWait(showSelectDialog); - SearchResult userSelection = showSelectDialog.get(); + SearchResult userSelection = showInputDialog(showSelectDialog); - // remember selected value - selectionMemory.put(query, userSelection); - return userSelection; - } + // remember selected value + selectionMemory.put(query, userSelection); + return userSelection; } } diff --git a/source/net/filebot/ui/rename/MovieMatcher.java b/source/net/filebot/ui/rename/MovieMatcher.java index 168c9f70..c23eabc4 100644 --- a/source/net/filebot/ui/rename/MovieMatcher.java +++ b/source/net/filebot/ui/rename/MovieMatcher.java @@ -29,18 +29,16 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.FutureTask; -import java.util.concurrent.RunnableFuture; import java.util.logging.Level; import java.util.prefs.Preferences; import javax.swing.Action; import javax.swing.JLabel; -import javax.swing.SwingUtilities; import net.filebot.similarity.Match; import net.filebot.similarity.NameSimilarityMetric; @@ -340,7 +338,7 @@ class MovieMatcher implements AutoCompleteMatcher { } // show selection dialog on EDT - RunnableFuture showSelectDialog = new FutureTask(() -> { + Callable showSelectDialog = () -> { 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))); @@ -372,7 +370,7 @@ class MovieMatcher implements AutoCompleteMatcher { // selected value or null if the dialog was canceled by the user return selectDialog.getSelectedValue(); - }); + }; // allow only one select dialog at a time synchronized (selectionMemory) { @@ -389,13 +387,12 @@ class MovieMatcher implements AutoCompleteMatcher { return null; } - synchronized (INPUT_DIALOG_LOCK) { - SwingUtilities.invokeAndWait(showSelectDialog); + // allow only one select dialog at a time + Movie userSelection = showInputDialog(showSelectDialog); - // cache selected value - selectionMemory.put(selectionKey, showSelectDialog.get()); - return showSelectDialog.get(); - } + // cache selected value + selectionMemory.put(selectionKey, userSelection); + return userSelection; } } diff --git a/source/net/filebot/util/ui/SwingUI.java b/source/net/filebot/util/ui/SwingUI.java index 0c171dd8..3eefe662 100644 --- a/source/net/filebot/util/ui/SwingUI.java +++ b/source/net/filebot/util/ui/SwingUI.java @@ -31,7 +31,9 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.logging.Level; import java.util.regex.Pattern; @@ -226,7 +228,39 @@ public final class SwingUI { return (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0; } - public static List showMultiValueInputDialog(Object message, String initialValue, String title, Component parent) { + private static final ReentrantLock INPUT_DIALOG_LOCK = new ReentrantLock(true); // use fair lock to display dialogs in FIFO order + + public static T showInputDialog(Callable callable) throws Exception { + Object[] value = { null }; + + // display only one dialog at a time, one after another, in the correct sequence + INPUT_DIALOG_LOCK.lock(); + try { + if (SwingUtilities.isEventDispatchThread()) { + value[0] = callable.call(); + } else { + SwingUtilities.invokeAndWait(() -> { + try { + value[0] = callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + } finally { + INPUT_DIALOG_LOCK.unlock(); + } + + return (T) value[0]; + } + + public static String showInputDialog(Object message, String initialValue, String title, Component parent) throws Exception { + return showInputDialog(() -> { + return (String) JOptionPane.showInputDialog(parent, message, title, PLAIN_MESSAGE, null, null, initialValue); + }); + } + + public static List showMultiValueInputDialog(Object message, String initialValue, String title, Component parent) throws Exception { String input = showInputDialog(message, initialValue, title, parent); if (input == null || input.isEmpty()) { return emptyList(); @@ -252,23 +286,6 @@ public final class SwingUI { return singletonList(input); } - public static final Object INPUT_DIALOG_LOCK = new Object(); - - public static String showInputDialog(Object message, String initialValue, String title, Component parent) { - StringBuilder buffer = new StringBuilder(); - - synchronized (INPUT_DIALOG_LOCK) { - runOnEventDispatchThread(() -> { - Object value = JOptionPane.showInputDialog(parent, message, title, PLAIN_MESSAGE, null, null, initialValue); - if (value != null) { - buffer.append(value.toString().trim()); - } - }); - } - - return buffer.length() == 0 ? null : buffer.toString(); - } - public static Window getWindow(Object component) { if (component instanceof Window) return (Window) component;