GP-628 - Dialogs - fixed issue of non-modal dialog appearing over modal

dialog; marked many input dialogs as transient
This commit is contained in:
dragonmacher
2021-01-28 15:50:51 -05:00
parent e983784753
commit a20d77a27b
16 changed files with 67 additions and 42 deletions
@@ -1344,7 +1344,7 @@ class StructureEditorModel extends CompEditorModel {
String title = "Specify the Structure's Name"; String title = "Specify the Structure's Name";
InputDialog nameStructureDialog = InputDialog nameStructureDialog =
new InputDialog(title, new String[] { "New Structure's Name: " }, new InputDialog(title, new String[] { "New Structure's Name: " },
new String[] { defaultName }, true, listener); new String[] { defaultName }, listener);
provider.getPlugin().getTool().showDialog(nameStructureDialog); provider.getPlugin().getTool().showDialog(nameStructureDialog);
@@ -138,7 +138,7 @@ public abstract class TagListPanel extends JPanel {
String comment = rowObject.getComment(); String comment = rowObject.getComment();
String[] labels = new String[] { "Name:", "Comment:" }; String[] labels = new String[] { "Name:", "Comment:" };
String[] init = new String[] { tagName, comment }; String[] init = new String[] { tagName, comment };
InputDialog dialog = new InputDialog("Edit Tag", labels, init, true, d -> { InputDialog dialog = new InputDialog("Edit Tag", labels, init, d -> {
String[] results = d.getValues(); String[] results = d.getValues();
if (results == null || results.length != 2) { if (results == null || results.length != 2) {
return false; return false;
@@ -28,7 +28,6 @@ import docking.widgets.label.GDLabel;
import generic.util.WindowUtilities; import generic.util.WindowUtilities;
import ghidra.framework.preferences.Preferences; import ghidra.framework.preferences.Preferences;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
import ghidra.util.SystemUtilities;
public class AskDialog<T> extends DialogComponentProvider { public class AskDialog<T> extends DialogComponentProvider {
public final static int STRING = 0; public final static int STRING = 0;
@@ -97,12 +96,13 @@ public class AskDialog<T> extends DialogComponentProvider {
panel.add(comboField, BorderLayout.CENTER); panel.add(comboField, BorderLayout.CENTER);
} }
setTransient(true);
addWorkPanel(panel); addWorkPanel(panel);
addOKButton(); addOKButton();
addCancelButton(); addCancelButton();
setDefaultButton(okButton); setDefaultButton(okButton);
setRememberSize(false); setRememberSize(false);
SystemUtilities.runSwingNow(() -> DockingWindowManager.showDialog(parent, AskDialog.this)); DockingWindowManager.showDialog(parent, AskDialog.this);
} }
private void saveCurrentDimensions() { private void saveCurrentDimensions() {
@@ -33,6 +33,7 @@ public class SelectLanguageDialog extends DialogComponentProvider {
languagePanel = new NewLanguagePanel(); languagePanel = new NewLanguagePanel();
setTransient(true);
addWorkPanel(languagePanel); addWorkPanel(languagePanel);
addOKButton(); addOKButton();
addCancelButton(); addCancelButton();
@@ -85,7 +85,7 @@ public abstract class RenameTask {
String label = "Rename " + oldName + ":"; String label = "Rename " + oldName + ":";
InputDialog renameVarDialog = new InputDialog( getTransactionName(), InputDialog renameVarDialog = new InputDialog( getTransactionName(),
new String[]{ label }, new String[]{ oldName }, true, listener ); new String[]{ label }, new String[]{ oldName }, listener );
tool.showDialog(renameVarDialog); tool.showDialog(renameVarDialog);
@@ -1766,41 +1766,45 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
This method seeks to accomplish 2 goals: This method seeks to accomplish 2 goals:
1) find a suitable component over which to center, and 1) find a suitable component over which to center, and
2) ensure that the chosen component is in the parent hierarchy 2) ensure that the chosen component is in the parent hierarchy
*/ */
Component bestComponent = centeredOnComponent; if (SwingUtilities.isDescendingFrom(parent, centeredOnComponent)) {
if (SwingUtilities.isDescendingFrom(parent, bestComponent)) { return centeredOnComponent;
return bestComponent;
} }
// by default, prefer to center over the active window //
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); // By default, prefer to center over the active window
Window activeWindow = kfm.getActiveWindow(); //
bestComponent = activeWindow; Window activeWindow = getActiveNonTransientWindow();
if (SwingUtilities.isDescendingFrom(parent, bestComponent)) { if (SwingUtilities.isDescendingFrom(parent, activeWindow)) {
return bestComponent; //
// Have an active, visible, non-transient window, which may be another dialog.
// We prefer this to be the parent.
//
return activeWindow;
} }
//
// The chosen component is not in the parent's hierarchy. See if there exists a // The chosen component is not in the parent's hierarchy. See if there exists a
// non-transient parent window for that component. // non-transient parent window for that component.
Window newWindow = getParentWindow(parent); //
Window newWindow = getParentWindow(centeredOnComponent);
if (newWindow != null) { if (newWindow != null) {
// the component is safe to use; the caller of this method will validate the component // the component is safe to use; the caller of this method will validate the component
// we return, updating the parent as needed // we return, updating the parent as needed
return bestComponent; return centeredOnComponent;
} }
// We were unable to find a suitable parent for the 'best' component. Just return the // We were unable to find a suitable parent for the 'best' component. Just return the
// parent as the thing over which to center. // parent as the thing over which to center.
return parent; return parent;
} }
private static Window getParentWindow(Component parent) { private static Window getParentWindow(Component parent) {
/* /*
Note: Which window should be the parent of the dialog when the user does not specify? Note: Which window should be the parent of the dialog when the user does not specify?
Some use cases; a dialog is shown from: Some use cases; a dialog is shown from:
1) A toolbar action 1) A toolbar action
2) A component provider's code 2) A component provider's code
@@ -1808,7 +1812,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
4) A background thread 4) A background thread
5) The help window 5) The help window
6) A modal password dialog appears over the splash screen 6) A modal password dialog appears over the splash screen
It seems like the parent should be the active window for 1-2. It seems like the parent should be the active window for 1-2.
Case 3 should probably use the window of the dialog provider. Case 3 should probably use the window of the dialog provider.
Case 4 should probably use the main tool frame, since the user may be Case 4 should probably use the main tool frame, since the user may be
@@ -1816,12 +1820,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
active window, we can default to the tool's frame. active window, we can default to the tool's frame.
Case 5 should use the help window. Case 5 should use the help window.
Case 6 should use the splash screen as the parent. Case 6 should use the splash screen as the parent.
We have not yet solidified how we should parent. This documentation is meant to We have not yet solidified how we should parent. This documentation is meant to
move us towards clarity as we find Use Cases that don't make sense. (Once we move us towards clarity as we find Use Cases that don't make sense. (Once we
finalize our understanding, we should update the javadoc to list exactly where finalize our understanding, we should update the javadoc to list exactly where
the given Dialog Component will be shown.) the given Dialog Component will be shown.)
Use Case Use Case
A -The user presses an action on a toolbar from a window on screen 1, while the A -The user presses an action on a toolbar from a window on screen 1, while the
main tool frame is on screen 2. We want the popup window to appear on screen main tool frame is on screen 2. We want the popup window to appear on screen
@@ -1833,11 +1837,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
-modal - Java handles this correctly, allowing the new dialog to be used -modal - Java handles this correctly, allowing the new dialog to be used
-non-modal - Java prevents the non-modal from being editing if not parented -non-modal - Java prevents the non-modal from being editing if not parented
correctly correctly
D -The user runs a script that shows an input dialog before the non-modal script
dialog is shown. If the non-modal dialog is parented to the modal input dialog,
For now, the easiest mental model to use is to always prefer the active window so then the script progress dialog appears on top (which we do not want) and the
that a dialog will appear in the user's view. If we find a case where this is progress dialog goes away when the input dialog is closed.
For now, the easiest mental model to use is to always prefer the active non-transient
window so that a dialog will appear in the user's view. If we find a case where this is
not desired, then document it here. not desired, then document it here.
*/ */
// //
@@ -174,6 +174,7 @@ public class OptionDialog extends DialogComponentProvider {
protected OptionDialog(String title, String message, String option1, String option2, protected OptionDialog(String title, String message, String option1, String option2,
int messageType, Icon icon, boolean addCancel) { int messageType, Icon icon, boolean addCancel) {
super(title, true, false, true, false); super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, null); buildMainPanel(message, messageType, icon, null);
buildButtons(toList(option1, option2), addCancel, null); buildButtons(toList(option1, option2), addCancel, null);
} }
@@ -194,6 +195,7 @@ public class OptionDialog extends DialogComponentProvider {
protected OptionDialog(String title, String message, String option1, String option2, protected OptionDialog(String title, String message, String option1, String option2,
int messageType, Icon icon, boolean addCancel, String defaultButtonName) { int messageType, Icon icon, boolean addCancel, String defaultButtonName) {
super(title, true, false, true, false); super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, null); buildMainPanel(message, messageType, icon, null);
buildButtons(toList(option1, option2), addCancel, defaultButtonName); buildButtons(toList(option1, option2), addCancel, defaultButtonName);
} }
@@ -233,6 +235,7 @@ public class OptionDialog extends DialogComponentProvider {
protected OptionDialog(String title, String message, String option1, String option2, protected OptionDialog(String title, String message, String option1, String option2,
String option3, int messageType, Icon icon, boolean addCancel) { String option3, int messageType, Icon icon, boolean addCancel) {
super(title, true, false, true, false); super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, null); buildMainPanel(message, messageType, icon, null);
buildButtons(toList(option1, option2, option3), addCancel, null); buildButtons(toList(option1, option2, option3), addCancel, null);
} }
@@ -240,6 +243,7 @@ public class OptionDialog extends DialogComponentProvider {
OptionDialog(String title, String message, int messageType, Icon icon, boolean addCancelButton, OptionDialog(String title, String message, int messageType, Icon icon, boolean addCancelButton,
DialogRememberOption savedDialogChoice, List<String> options, String defaultOption) { DialogRememberOption savedDialogChoice, List<String> options, String defaultOption) {
super(title, true, false, true, false); super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, savedDialogChoice); buildMainPanel(message, messageType, icon, savedDialogChoice);
buildButtons(options, addCancelButton, defaultOption); buildButtons(options, addCancelButton, defaultOption);
} }
@@ -256,6 +260,7 @@ public class OptionDialog extends DialogComponentProvider {
private void buildMainPanel(String message, int messageType, Icon icon, private void buildMainPanel(String message, int messageType, Icon icon,
DialogRememberOption rememberOptionChoice) { DialogRememberOption rememberOptionChoice) {
JPanel panel = new JPanel(new BorderLayout()); JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
@@ -75,6 +75,7 @@ public abstract class AbstractNumberInputDialog extends DialogComponentProvider
} }
this.max = max; this.max = max;
setTransient(true);
addWorkPanel(buildMainPanel(prompt, showAsHex)); addWorkPanel(buildMainPanel(prompt, showAsHex));
addOKButton(); addOKButton();
addCancelButton(); addCancelButton();
@@ -53,7 +53,7 @@ public class InputDialog extends DialogComponentProvider {
* @param label value to use for the label of the text field * @param label value to use for the label of the text field
*/ */
public InputDialog(String dialogTitle, String label) { public InputDialog(String dialogTitle, String label) {
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, true, null); this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, null);
} }
/** /**
@@ -68,7 +68,7 @@ public class InputDialog extends DialogComponentProvider {
* @param initialValue initial value to use for the text field * @param initialValue initial value to use for the text field
*/ */
public InputDialog(String dialogTitle, String label, String initialValue) { public InputDialog(String dialogTitle, String label, String initialValue) {
this(dialogTitle, new String[] { label }, new String[] { initialValue }, true, null); this(dialogTitle, new String[] { label }, new String[] { initialValue }, null);
} }
/** /**
@@ -85,7 +85,7 @@ public class InputDialog extends DialogComponentProvider {
*/ */
public InputDialog(String dialogTitle, String label, String initialValue, public InputDialog(String dialogTitle, String label, String initialValue,
InputDialogListener listener) { InputDialogListener listener) {
this(dialogTitle, new String[] { label }, new String[] { initialValue }, true, listener); this(dialogTitle, new String[] { label }, new String[] { initialValue }, listener);
} }
/** /**
@@ -101,7 +101,7 @@ public class InputDialog extends DialogComponentProvider {
* @param isModal whether or not the dialog is to be modal * @param isModal whether or not the dialog is to be modal
*/ */
public InputDialog(String dialogTitle, String label, String initialValue, boolean isModal) { public InputDialog(String dialogTitle, String label, String initialValue, boolean isModal) {
this(dialogTitle, new String[] { label }, new String[] { initialValue }, isModal, null); this(dialogTitle, new String[] { label }, new String[] { initialValue }, null);
} }
/** /**
@@ -116,7 +116,7 @@ public class InputDialog extends DialogComponentProvider {
* @param initialValues initial values to use for the text fields * @param initialValues initial values to use for the text fields
*/ */
public InputDialog(String dialogTitle, String[] labels, String[] initialValues) { public InputDialog(String dialogTitle, String[] labels, String[] initialValues) {
this(dialogTitle, labels, initialValues, true, null); this(dialogTitle, labels, initialValues, null);
} }
/** /**
@@ -129,12 +129,11 @@ public class InputDialog extends DialogComponentProvider {
* @param dialogTitle used as the name of the dialog's title bar * @param dialogTitle used as the name of the dialog's title bar
* @param labels values to use for the labels of the text fields * @param labels values to use for the labels of the text fields
* @param initialValues initial values to use for the text fields * @param initialValues initial values to use for the text fields
* @param isModal whether or not the dialog is to be modal
* @param listener listener that is called when the OK button is hit * @param listener listener that is called when the OK button is hit
*/ */
public InputDialog(String dialogTitle, String[] labels, String[] initialValues, boolean isModal, public InputDialog(String dialogTitle, String[] labels, String[] initialValues,
InputDialogListener listener) { InputDialogListener listener) {
super(dialogTitle, isModal, (listener != null) /* status */, true /* buttons */, super(dialogTitle, true, (listener != null) /* status */, true /* buttons */,
false /* no tasks */); false /* no tasks */);
this.listener = listener; this.listener = listener;
@@ -152,8 +151,9 @@ public class InputDialog extends DialogComponentProvider {
// put the rest of the dialog together // put the rest of the dialog together
inputLabels = labels; inputLabels = labels;
this.initialValues = initialValues; this.initialValues = initialValues;
this.addOKButton(); setTransient(true);
this.addCancelButton(); addOKButton();
addCancelButton();
buildMainPanel(); buildMainPanel();
if (initialValues != null && initialValues[0] != null && initialValues[0].length() > 0) { if (initialValues != null && initialValues[0] != null && initialValues[0].length() > 0) {
@@ -55,10 +55,11 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
super(dialogTitle, true, false, true, false); super(dialogTitle, true, false, true, false);
this.addOKButton(); setTransient(true);
this.addCancelButton(); addOKButton();
this.setRememberSize(false); addCancelButton();
this.setRememberLocation(false); setRememberSize(false);
setRememberLocation(false);
buildMainPanel(label, optionValues, initialValue, messageIcon); buildMainPanel(label, optionValues, initialValue, messageIcon);
setFocusComponent(combo); setFocusComponent(combo);
@@ -43,6 +43,7 @@ public class MultiLineInputDialog extends DialogComponentProvider {
setFocusComponent(inputTextArea); setFocusComponent(inputTextArea);
setTransient(true);
addOKButton(); addOKButton();
addCancelButton(); addCancelButton();
} }
@@ -148,6 +148,7 @@ public class MultiLineMessageDialog extends DialogComponentProvider {
workPanel.add(iconLabel, BorderLayout.WEST); workPanel.add(iconLabel, BorderLayout.WEST);
} }
setTransient(true);
addWorkPanel(workPanel); addWorkPanel(workPanel);
addOKButton(); addOKButton();
@@ -36,6 +36,8 @@ public class ObjectChooserDialog<T> extends DialogComponentProvider {
this.objectClass = objectClass; this.objectClass = objectClass;
this.choosableObjects = choosableObjects; this.choosableObjects = choosableObjects;
this.methodsForColumns = methodsForColumns; this.methodsForColumns = methodsForColumns;
setTransient(true);
addWorkPanel(buildWorkPanel()); addWorkPanel(buildWorkPanel());
addOKButton(); addOKButton();
addCancelButton(); addCancelButton();
@@ -55,8 +55,9 @@ public class ReadTextDialog extends DialogComponentProvider {
} }
private void init(JPanel workPanelToInit) { private void init(JPanel workPanelToInit) {
this.addWorkPanel(workPanelToInit); setTransient(true);
this.addOKButton(); addWorkPanel(workPanelToInit);
addOKButton();
setRememberLocation(false); setRememberLocation(false);
setRememberSize(false); setRememberSize(false);
} }
@@ -52,6 +52,8 @@ public class SettingsDialog extends DialogComponentProvider {
if (help != null) { if (help != null) {
setHelpLocation(help); setHelpLocation(help);
} }
setTransient(true);
addWorkPanel(buildWorkPanel()); addWorkPanel(buildWorkPanel());
addDismissButton(); addDismissButton();
@@ -234,6 +234,7 @@ public class GhidraFileChooser extends DialogComponentProvider
super(TITLE, true, true, true, false); super(TITLE, true, true, true, false);
this.parent = parent; this.parent = parent;
setTransient(true);
init(model); init(model);
loadRecentList(); loadRecentList();
loadOptions(); loadOptions();