diff --git a/lib/swing-lib/src/main/java/org/triplea/swing/EventThreadJOptionPane.java b/lib/swing-lib/src/main/java/org/triplea/swing/EventThreadJOptionPane.java index bae2751b8a2..e69ead0310e 100644 --- a/lib/swing-lib/src/main/java/org/triplea/swing/EventThreadJOptionPane.java +++ b/lib/swing-lib/src/main/java/org/triplea/swing/EventThreadJOptionPane.java @@ -211,42 +211,66 @@ public enum ConfirmDialogType { * @return True if user confirms, false if user closes the confirmation dialog or selects no. */ public static boolean showConfirmDialog( - final @Nullable Component parentComponent, - final @Nullable Object message, - final @Nullable String title, - final ConfirmDialogType confirmDialogType) { - - // Construct a 'JDialog' the "hard" way through a JOptionPane so that we can - // set modal to be false. - final JOptionPane optionPane = - new JOptionPane( - message, JOptionPane.QUESTION_MESSAGE, confirmDialogType.optionTypeMagicNumber); - final JDialog dialog = optionPane.createDialog(parentComponent, title); - dialog.setAlwaysOnTop(true); - if (!SwingUtilities.isEventDispatchThread()) { - dialog.setModal(false); - } + final @Nullable Component parentComponent, + final @Nullable Object message, + final @Nullable String title, + final ConfirmDialogType confirmDialogType) { + // We want to construct a 'JDialog' the "hard" way through a JOptionPane so + // that we can set modal to be false. // Only modal dialogs are blocking. To mimic this, we use a latch to block once // the dialog is set to visible. We use a property listener to capture the users // confirmation choice and to unblock. final CountDownLatch latch = new CountDownLatch(1); final AtomicReference confirmation = new AtomicReference<>(); - optionPane.addPropertyChangeListener( - JOptionPane.VALUE_PROPERTY, - ignored -> { - final Object selectedValue = optionPane.getValue(); - confirmation.set(selectedValue != null && JOptionPane.OK_OPTION == (int) selectedValue); - latch.countDown(); - dialog.dispose(); - }); + // Swing components must be created in Swing event threads. If we are already + // in one, dialog creation is fairly easy. if (SwingUtilities.isEventDispatchThread()) { + final JOptionPane optionPane = + new JOptionPane( + message, JOptionPane.QUESTION_MESSAGE, + confirmDialogType.optionTypeMagicNumber); + final JDialog dialog = optionPane.createDialog(parentComponent, title); + dialog.setAlwaysOnTop(true); + + optionPane.addPropertyChangeListener( + JOptionPane.VALUE_PROPERTY, + ignored -> { + final Object selectedValue = optionPane.getValue(); + confirmation.set(selectedValue != null + && JOptionPane.OK_OPTION == (int) selectedValue); + latch.countDown(); + dialog.dispose(); + }); + // modal dialog being set to visible is blocking dialog.setVisible(true); + + // For non-Swing event threads, we must request our code be invoked and block manually } else { - // non modal dialog set to visible is not blocking and must be done on EDT - SwingUtilities.invokeLater(() -> dialog.setVisible(true)); + SwingUtilities.invokeLater(() -> { + final JOptionPane optionPane = + new JOptionPane(message, JOptionPane.QUESTION_MESSAGE, + confirmDialogType.optionTypeMagicNumber); + final JDialog dialog = optionPane.createDialog(parentComponent, title); + dialog.setAlwaysOnTop(true); + dialog.setModal(false); + + optionPane.addPropertyChangeListener( + JOptionPane.VALUE_PROPERTY, + ignored -> { + final Object selectedValue = optionPane.getValue(); + confirmation.set(selectedValue != null + && JOptionPane.OK_OPTION == (int) selectedValue); + latch.countDown(); + dialog.dispose(); + }); + + // non modal dialog set to visible is not blocking and must be done on EDT + dialog.setVisible(true); + }); + try { // start blocking, wait for the dialog property event to fire to clear this latch latch.await();