Skip to content

Commit

Permalink
Fix #16485: Modal dialogs may occasionally be behind another dialog
Browse files Browse the repository at this point in the history
From the [https://docs.oracle.com/en/java/javase/21/docs/api/java.desktop/java/awt/doc-files/Modality.html Modality] javadocs:
> * keeps its Z-order below the modal dialog that blocks it
> **Warning! ** Some window managers allow users to change the window Z-order in an arbitrary way — in that case the last requirement may not be met. 

The workaround is to set the blocking dialog as always on top.

git-svn-id: https://josm.openstreetmap.de/svn/trunk@18849 0c6e7542-c601-0410-84e7-c038aed88b3b
  • Loading branch information
taylor.smock committed Oct 3, 2023
1 parent 9220733 commit 0ca6f72
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 12 deletions.
48 changes: 40 additions & 8 deletions src/org/openstreetmap/josm/gui/ConditionalOptionPaneUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.openstreetmap.josm.tools.I18n.tr;

import java.awt.Component;
import java.awt.Dialog;
import java.awt.GridBagLayout;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -14,6 +15,8 @@
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
import org.openstreetmap.josm.spi.preferences.Config;
Expand All @@ -23,7 +26,7 @@
/**
* ConditionalOptionPaneUtil provides static utility methods for displaying modal message dialogs
* which can be enabled/disabled by the user.
*
* <p>
* They wrap the methods provided by {@link JOptionPane}. Within JOSM you should use these
* methods rather than the bare methods from {@link JOptionPane} because the methods provided
* by ConditionalOptionPaneUtil ensure that a dialog window is always on top and isn't hidden by one of the
Expand Down Expand Up @@ -92,13 +95,13 @@ public static void endBulkOperation(final String prefKey) {
* Displays an confirmation dialog with some option buttons given by <code>optionType</code>.
* It is always on top even if there are other open windows like detached dialogs,
* relation editors, history browsers and the like.
*
* <p>
* Set <code>optionType</code> to {@link JOptionPane#YES_NO_OPTION} for a dialog with a YES and
* a NO button.
* <p>
* Set <code>optionType</code> to {@link JOptionPane#YES_NO_CANCEL_OPTION} for a dialog with a YES,
* a NO and a CANCEL button
*
* <p>
* Returns one of the constants JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
* JOptionPane.CANCEL_OPTION or JOptionPane.CLOSED_OPTION depending on the action chosen by
* the user.
Expand Down Expand Up @@ -132,13 +135,13 @@ public static int showOptionDialog(String preferenceKey, Component parent, Objec
* Displays a confirmation dialog with some option buttons given by <code>optionType</code>.
* It is always on top even if there are other open windows like detached dialogs,
* relation editors, history browsers and the like.
*
* <p>
* Set <code>optionType</code> to {@link JOptionPane#YES_NO_OPTION} for a dialog with a YES and
* a NO button.
* <p>
* Set <code>optionType</code> to {@link JOptionPane#YES_NO_CANCEL_OPTION} for a dialog with a YES,
* a NO and a CANCEL button
*
* <p>
* Replies true, if the selected option is equal to <code>trueOption</code>, otherwise false.
* Replies true, if the dialog is not displayed because the respective preference option
* <code>preferenceKey</code> is set to false and the user has previously chosen
Expand Down Expand Up @@ -180,7 +183,7 @@ private static boolean isYesOrNo(int returnCode) {
* Displays an message in modal dialog with an OK button. Makes sure the dialog
* is always on top even if there are other open windows like detached dialogs,
* relation editors, history browsers and the like.
*
* <p>
* If there is a preference with key <code>preferenceKey</code> and value <code>false</code>
* the dialog is not show.
*
Expand Down Expand Up @@ -283,6 +286,35 @@ public static class MessagePanel extends JPanel {
add(cbShowImmediateDialog, GBC.eol());
}
add(cbStandard, GBC.eol());

this.addAncestorListener(new AncestorListener() {
boolean wasAlwaysOnTop;
@Override
public void ancestorAdded(AncestorEvent event) {
if (event.getAncestor() instanceof Dialog) {
Dialog dialog = (Dialog) event.getAncestor();
wasAlwaysOnTop = dialog.isAlwaysOnTop();
if (dialog.isVisible() && dialog.isModal()) {
dialog.setAlwaysOnTop(true);
}
}
}

@Override
public void ancestorRemoved(AncestorEvent event) {
if (event.getAncestor() instanceof Dialog) {
Dialog dialog = (Dialog) event.getAncestor();
if (dialog.isVisible() && dialog.isModal()) {
dialog.setAlwaysOnTop(wasAlwaysOnTop);
}
}
}

@Override
public void ancestorMoved(AncestorEvent event) {
// Do nothing
}
});
}

NotShowAgain getNotShowAgain() {
Expand Down
3 changes: 3 additions & 0 deletions src/org/openstreetmap/josm/gui/ExtendedDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@ public void setVisible(boolean visible) {
rememberWindowGeometry(new WindowGeometry(this));
}
}
if (visible && isModal()) {
this.setAlwaysOnTop(true);
}
super.setVisible(visible);

if (!visible && disposeOnClose) {
Expand Down
11 changes: 7 additions & 4 deletions src/org/openstreetmap/josm/gui/HelpAwareOptionPane.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,11 @@ public void actionPerformed(ActionEvent e) {
* the dialog includes a "Help" button and launches the help browser if the user presses F1. If the
* user clicks on the "Help" button the option dialog remains open and JOSM launches the help
* browser.
*
* <p>
* <code>helpTopic</code> is the trailing part of a JOSM online help URL, i.e. the part after the leading
* <code>https://josm.openstreetmap.de/wiki/Help</code>. It should start with a leading '/' and it
* {@code https://josm.openstreetmap.de/wiki/Help}. It should start with a leading '/' and it
* may include an anchor after a '#'.
*
* <p>
* <strong>Examples</strong>
* <ul>
* <li>/Dialogs/RelationEditor</li>
Expand Down Expand Up @@ -350,6 +350,9 @@ public void actionPerformed(ActionEvent e) {
if (helpTopic != null) {
HelpUtil.setHelpContext(dialog.getRootPane(), helpTopic);
}
if (dialog.isModal()) {
dialog.setAlwaysOnTop(true);
}
dialog.setVisible(true);
}

Expand All @@ -371,7 +374,7 @@ public static int showOptionDialog(Component parentComponent, Object msg, String
/**
* Run it in Event Dispatch Thread.
* This version does not return anything, so it is more like {@code showMessageDialog}.
*
* <p>
* It can be used, when you need to show a message dialog from a worker thread,
* e.g. from {@code PleaseWaitRunnable}.
*
Expand Down

0 comments on commit 0ca6f72

Please sign in to comment.