|
101 | 101 | import java.util.function.Supplier; |
102 | 102 | import java.util.regex.Matcher; |
103 | 103 | import java.util.regex.Pattern; |
| 104 | +import java.util.concurrent.Callable; |
104 | 105 | import java.util.concurrent.CancellationException; |
105 | 106 | import java.util.concurrent.CopyOnWriteArrayList; |
106 | 107 | import java.util.concurrent.ExecutionException; |
107 | 108 | import java.util.concurrent.Future; |
| 109 | +import java.util.concurrent.FutureTask; |
108 | 110 | import java.util.stream.Collectors; |
109 | 111 | import java.util.stream.IntStream; |
110 | 112 |
|
@@ -1585,25 +1587,58 @@ private Object showOpenDialog(final String title, final int selectionMode, final |
1585 | 1587 | return getOpenFileChooserResult(fileChooser); |
1586 | 1588 | } |
1587 | 1589 |
|
| 1590 | + /** |
| 1591 | + * Runs {@code c} on the Event Dispatch Thread (EDT) and returns its result. |
| 1592 | + * Runs immediately if already on the EDT, otherwise blocks until done. |
| 1593 | + * |
| 1594 | + * @param <T> the result type |
| 1595 | + * @param c the task to run |
| 1596 | + * @return the result of {@code c} |
| 1597 | + * @throws IllegalStateException if the calling thread was interrupted while |
| 1598 | + * waiting (its interrupt status is restored) |
| 1599 | + * @throws RuntimeException the exception thrown by {@code c}, if any |
| 1600 | + * (checked exceptions are wrapped) |
| 1601 | + */ |
| 1602 | + public static <T> T callOnEDT(final Callable<T> c) { |
| 1603 | + final FutureTask<T> ft = new FutureTask<>(c); |
| 1604 | + try { |
| 1605 | + if (SwingUtilities.isEventDispatchThread()) ft.run(); |
| 1606 | + else SwingUtilities.invokeAndWait(ft); |
| 1607 | + return ft.get(); |
| 1608 | + } catch (final InterruptedException e) { |
| 1609 | + Thread.currentThread().interrupt(); |
| 1610 | + throw new IllegalStateException("Interrupted while waiting for the EDT", e); |
| 1611 | + } catch (final ExecutionException | java.lang.reflect.InvocationTargetException e) { |
| 1612 | + final Throwable cause = (e.getCause() == null) ? e : e.getCause(); |
| 1613 | + if (cause instanceof RuntimeException) throw (RuntimeException) cause; |
| 1614 | + if (cause instanceof Error) throw (Error) cause; |
| 1615 | + throw new RuntimeException(cause); |
| 1616 | + } |
| 1617 | + } |
| 1618 | + |
| 1619 | + /** |
| 1620 | + * As {@link #callOnEDT(Callable)}, for tasks that do not return a value |
| 1621 | + * |
| 1622 | + * @param r the task to run |
| 1623 | + */ |
| 1624 | + public static void runOnEDT(final Runnable r) { |
| 1625 | + callOnEDT(() -> { |
| 1626 | + r.run(); |
| 1627 | + return null; |
| 1628 | + }); |
| 1629 | + } |
| 1630 | + |
1588 | 1631 | private Object showNativeOpenDialog(final String title, final int selectionMode, final boolean multiSelection, |
1589 | 1632 | final File preset, final boolean presetIsDirectory, final FileNameExtensionFilter filter) { |
1590 | 1633 | // Unlike JFileChooser.showOpenDialog(), SystemFileChooser.showOpenDialog() throws |
1591 | 1634 | // IllegalStateException if not called on the EDT |
1592 | | - if (!SwingUtilities.isEventDispatchThread()) { |
1593 | | - final Object[] result = new Object[1]; |
1594 | | - try { |
1595 | | - SwingUtilities.invokeAndWait(() -> result[0] = showNativeOpenDialogOnEDT( |
1596 | | - title, selectionMode, multiSelection, preset, presetIsDirectory, filter)); |
1597 | | - } catch (final InterruptedException e) { |
1598 | | - Thread.currentThread().interrupt(); |
1599 | | - return null; |
1600 | | - } catch (final java.lang.reflect.InvocationTargetException e) { |
1601 | | - SNTUtils.error("Native file dialog failed", e.getCause()); |
1602 | | - return null; |
1603 | | - } |
1604 | | - return result[0]; |
| 1635 | + try { |
| 1636 | + return callOnEDT(() -> showNativeOpenDialogOnEDT(title, selectionMode, multiSelection, preset, |
| 1637 | + presetIsDirectory, filter)); |
| 1638 | + } catch (final RuntimeException e) { |
| 1639 | + SNTUtils.error("Native file dialog failed", e); |
| 1640 | + return null; |
1605 | 1641 | } |
1606 | | - return showNativeOpenDialogOnEDT(title, selectionMode, multiSelection, preset, presetIsDirectory, filter); |
1607 | 1642 | } |
1608 | 1643 |
|
1609 | 1644 | private Object showNativeOpenDialogOnEDT(final String title, final int selectionMode, |
@@ -1643,20 +1678,12 @@ private Object showNativeOpenDialogOnEDT(final String title, final int selection |
1643 | 1678 | private File showNativeSaveDialog(final String title, final File preset, final FileNameExtensionFilter filter) { |
1644 | 1679 | // Unlike JFileChooser.showSaveDialog(), SystemFileChooser.showSaveDialog() throws |
1645 | 1680 | // IllegalStateException if not called on the EDT |
1646 | | - if (!SwingUtilities.isEventDispatchThread()) { |
1647 | | - final File[] result = new File[1]; |
1648 | | - try { |
1649 | | - SwingUtilities.invokeAndWait(() -> result[0] = showNativeSaveDialogOnEDT(title, preset, filter)); |
1650 | | - } catch (final InterruptedException e) { |
1651 | | - Thread.currentThread().interrupt(); |
1652 | | - return null; |
1653 | | - } catch (final java.lang.reflect.InvocationTargetException e) { |
1654 | | - SNTUtils.error("Native file dialog failed", e.getCause()); |
1655 | | - return null; |
1656 | | - } |
1657 | | - return result[0]; |
| 1681 | + try { |
| 1682 | + return callOnEDT(() -> showNativeSaveDialogOnEDT(title, preset, filter)); |
| 1683 | + } catch (final RuntimeException e) { |
| 1684 | + SNTUtils.error("Native file dialog failed", e); |
| 1685 | + return null; |
1658 | 1686 | } |
1659 | | - return showNativeSaveDialogOnEDT(title, preset, filter); |
1660 | 1687 | } |
1661 | 1688 |
|
1662 | 1689 | private File showNativeSaveDialogOnEDT(final String title, final File preset, |
|
0 commit comments