Merge remote-tracking branch 'origin/GP-628-dragonmacher-dialog-issues'

(fixes #2398, #2480)
This commit is contained in:
ghidra1 2021-02-16 13:02:28 -05:00
commit 0d01b72af9
29 changed files with 303 additions and 195 deletions

View file

@ -1344,7 +1344,7 @@ class StructureEditorModel extends CompEditorModel {
String title = "Specify the Structure's Name";
InputDialog nameStructureDialog =
new InputDialog(title, new String[] { "New Structure's Name: " },
new String[] { defaultName }, true, listener);
new String[] { defaultName }, listener);
provider.getPlugin().getTool().showDialog(nameStructureDialog);

View file

@ -138,7 +138,7 @@ public abstract class TagListPanel extends JPanel {
String comment = rowObject.getComment();
String[] labels = new String[] { "Name:", "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();
if (results == null || results.length != 2) {
return false;

View file

@ -28,7 +28,6 @@ import docking.widgets.label.GDLabel;
import generic.util.WindowUtilities;
import ghidra.framework.preferences.Preferences;
import ghidra.util.NumericUtilities;
import ghidra.util.SystemUtilities;
public class AskDialog<T> extends DialogComponentProvider {
public final static int STRING = 0;
@ -97,12 +96,13 @@ public class AskDialog<T> extends DialogComponentProvider {
panel.add(comboField, BorderLayout.CENTER);
}
setTransient(true);
addWorkPanel(panel);
addOKButton();
addCancelButton();
setDefaultButton(okButton);
setRememberSize(false);
SystemUtilities.runSwingNow(() -> DockingWindowManager.showDialog(parent, AskDialog.this));
DockingWindowManager.showDialog(parent, AskDialog.this);
}
private void saveCurrentDimensions() {

View file

@ -33,6 +33,7 @@ public class SelectLanguageDialog extends DialogComponentProvider {
languagePanel = new NewLanguagePanel();
setTransient(true);
addWorkPanel(languagePanel);
addOKButton();
addCancelButton();

View file

@ -203,7 +203,7 @@ public abstract class AbstractToolSavingTest extends AbstractGhidraHeadedIntegra
PluginTool[] tools = activeWorkspace.getTools();
List<PluginTool> pluginToolList = new ArrayList<>(tools.length);
for (PluginTool tool : tools) {
pluginToolList.add((PluginTool) tool);
pluginToolList.add(tool);
}
return pluginToolList;
}
@ -234,13 +234,9 @@ public abstract class AbstractToolSavingTest extends AbstractGhidraHeadedIntegra
protected Map<String, Object> getOptionsMap(PluginTool tool) {
Map<String, Object> map = new TreeMap<>();
Options[] options = tool.getOptions();
for (Options option : options) {
String optionsName = option.getName();
if (optionsName.equals("Key Bindings")) {
Msg.debug(this, "break");
}
List<String> optionNames = option.getOptionNames();
for (String name : optionNames) {
Object value = invokeInstanceMethod("getObject", option,
@ -277,7 +273,12 @@ public abstract class AbstractToolSavingTest extends AbstractGhidraHeadedIntegra
}
protected PluginTool launchTool(String toolName) {
return testEnv.launchTool(toolName, null);
PluginTool tool = testEnv.launchTool(toolName, null);
// There is some delayed options registration that causes sporadic test failures. Waiting
// for swing here seems to fix that.
waitForSwing();
return tool;
}
protected void dumpToolFile(String name) throws IOException {
@ -341,27 +342,14 @@ public abstract class AbstractToolSavingTest extends AbstractGhidraHeadedIntegra
JFrame toolFrame = tool.getToolFrame();
runSwing(() -> {
Msg.debug(this, "setting tool location to: " + point + "\n\ton window: " +
toolFrame.getTitle() + " (" + System.identityHashCode(toolFrame) + ")");
toolFrame.setLocation(point);
});
waitForSwing();
waitForCondition(() -> {
Msg.debug(this,
"\tattempt again - setting tool location to: " + point + "\n\ton window: " +
toolFrame.getTitle() + " (" + System.identityHashCode(toolFrame) + ")");
toolFrame.setLocation(point);
Msg.debug(this, "checking " + point + " against " + toolFrame.getLocation());
return point.equals(toolFrame.getLocation());
});
// debug
runSwing(() -> {
Msg.debug(this, "\ttool bounds now: " + toolFrame.getBounds());
});
}
protected void setToolSize(PluginTool tool, final Dimension dimension) {

View file

@ -85,7 +85,7 @@ public abstract class RenameTask {
String label = "Rename " + oldName + ":";
InputDialog renameVarDialog = new InputDialog( getTransactionName(),
new String[]{ label }, new String[]{ oldName }, true, listener );
new String[]{ label }, new String[]{ oldName }, listener );
tool.showDialog(renameVarDialog);

View file

@ -190,7 +190,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
ProgramLocation p1Loc = p2Loc;
if (p2LocationAddress.getAddressSpace().isOverlaySpace()) {
ProgramLocation equivalentP1Loc = DiffUtility
.getCompatibleProgramLocation(secondaryDiffProgram, p2Loc, primaryProgram);
.getCompatibleProgramLocation(secondaryDiffProgram, p2Loc, primaryProgram);
if (equivalentP1Loc != null) {
AddressSpace p2Space = p2LocationAddress.getAddressSpace();
AddressSpace p1Space = equivalentP1Loc.getAddress().getAddressSpace();
@ -465,7 +465,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
diffControl.setLocation(previousP1Location.getAddress());
}
ProgramLocation previousP1LocationAsP2 = DiffUtility
.getCompatibleProgramLocation(primaryProgram, location, secondaryDiffProgram);
.getCompatibleProgramLocation(primaryProgram, location, secondaryDiffProgram);
if (previousP1LocationAsP2 != null) {
diffListingPanel.setCursorPosition(previousP1LocationAsP2);
}
@ -804,8 +804,8 @@ public class ProgramDiffPlugin extends ProgramPlugin
AddressSet p2SelectionAsP1 =
DiffUtility.getCompatibleAddressSet(p2Selection, primaryProgram);
AddressSet p1ApplySet = p2SelectionAsP1.intersect(p1ViewAddrSet)
.subtract(addressesOnlyInP1)
.subtract(compatibleOnlyInP2);
.subtract(addressesOnlyInP1)
.subtract(compatibleOnlyInP2);
if (p1ApplySet.isEmpty()) {
Msg.showInfo(getClass(), tool.getToolFrame(), "Apply Differences",
(p2Selection.isEmpty()) ? "No diff selection in the current view."
@ -1882,7 +1882,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
}
catch (CancelledException e) {
// For now do nothing if user cancels, the Diff.
// For now do nothing if user cancels
}
monitor.setMessage("");

View file

@ -204,6 +204,7 @@ public class DualProgramTest extends DiffTestAdapter {
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
pressButton(win, "OK");
waitForTasks();
win = waitForWindow("No Memory In Common");
assertNotNull(win);
MultiLineLabel mll = findComponent(win, MultiLineLabel.class);

View file

@ -52,11 +52,14 @@ public class DialogComponentProvider
private static final String PROGRESS = "Progress";
private static final String DEFAULT = "No Progress";
protected JPanel rootPanel;
private static int idCounter;
private int id = ++idCounter;
private boolean modal;
private String title;
protected JPanel rootPanel;
private JPanel mainPanel;
private JComponent workPanel;
private JPanel buttonPanel;
@ -189,6 +192,10 @@ public class DialogComponentProvider
// may be overridden by subclasses
}
public int getId() {
return id;
}
public JComponent getComponent() {
return rootPanel;
}

View file

@ -38,6 +38,9 @@ import ghidra.util.bean.GGlassPane;
public class DockingDialog extends JDialog implements HelpDescriptor {
private static Component focusComponent; // allow only one scheduled focus component. See above.
private static Map<String, BoundsInfo> dialogBoundsMap =
LazyMap.lazyMap(new HashMap<>(), () -> new BoundsInfo());
private WindowListener windowAdapter;
private DialogComponentProvider component;
private boolean hasBeenFocused;
@ -47,11 +50,7 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
hasBeenFocused = true;
}
};
private static Map<String, BoundsInfo> dialogBoundsMap =
LazyMap.lazyMap(new HashMap<>(), () -> new BoundsInfo());
private DockingWindowManager owningWindowManager;
private WindowAdapter modalFixWindowAdapter;
/**

View file

@ -1664,7 +1664,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* @param dialogComponent the DialogComponentProvider object to be shown in a dialog
*/
public static void showDialog(DialogComponentProvider dialogComponent) {
showDialog(null, dialogComponent, (Component) null);
doShowDialog(dialogComponent, null);
}
/**
@ -1680,41 +1680,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
ComponentProvider centeredOnProvider) {
ComponentPlaceholder placeholder = getActivePlaceholder(centeredOnProvider);
Component c = null;
Window parent = null;
if (placeholder != null) {
parent = root.getWindow(placeholder);
c = placeholder.getComponent();
}
showDialog(parent, dialogComponent, c);
}
private static void doShowDialog(DialogComponentProvider provider, Component parent,
Component centeredOnComponent) {
Runnable r = () -> {
if (provider.isVisible()) {
provider.toFront();
return;
}
Window bestParent = getParentWindow(parent);
Component bestCenter = getCenterOnComponent(bestParent, centeredOnComponent);
// Make sure the window we have chosen to center over is related to the given parent.
// This prevents the oddness of a dialog that appears on the non-active screen.
bestParent = ensureParentHierarchy(bestParent, bestCenter);
DockingDialog dialog = DockingDialog.createDialog(bestParent, provider, bestCenter);
dialog.setVisible(true);
};
if (provider.isModal()) {
Swing.runNow(r);
}
else {
Swing.runIfSwingOrRunLater(r);
if (placeholder == null && centeredOnProvider != null) {
c = centeredOnProvider.getComponent();
}
doShowDialog(dialogComponent, c);
}
/**
@ -1731,69 +1700,129 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* @see #getParentWindow(Component) for parenting notes
*/
public static void showDialog(Component parent, DialogComponentProvider dialogComponent) {
doShowDialog(dialogComponent, parent, null);
doShowDialog(dialogComponent, parent);
}
/**
* Shows the dialog using the given parent window using the optional component for
* positioning
* positioning.
*
* @param parent the component whose window over which the given dialog will be shown
* <p>Warning: this method allows user to explicitly pass a parent window and component over
* which to be centered. There is no reason to use this method in the standard workflow.
* This method exists strictly to handle future unforeseen use cases. Use at your own
* risk of incorrectly parenting dialogs.
*
* @param parent the component whose window over which the given dialog will be shown; cannot
* be null
* @param dialogComponent the DialogComponentProvider object to be shown in a dialog
* @param centeredOnComponent the component over which the dialog will be centered if not null
* @param centeredOnComponent the component over which the dialog will be centered; cannot
* be null
*/
public static void showDialog(Window parent, DialogComponentProvider dialogComponent,
Component centeredOnComponent) {
Objects.requireNonNull(parent);
Objects.requireNonNull(centeredOnComponent);
doShowDialog(dialogComponent, parent, centeredOnComponent);
}
private static Window ensureParentHierarchy(Window parent, Component component) {
if (CollectionUtils.isAllNull(parent, component)) {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
return kfm.getActiveWindow();
}
private static void doShowDialog(DialogComponentProvider provider,
Component centeredOnComponent) {
if (SwingUtilities.isDescendingFrom(parent, component)) {
return parent;
}
Runnable r = () -> {
if (provider.isVisible()) {
provider.toFront();
return;
}
return getParentWindow(component);
Component bestCenter = getJavaActiveWindow();
Window bestParent = getParentWindow(bestCenter);
if (!provider.isModal()) {
bestParent = getBestNonModalParent(provider, bestParent);
}
//
// Note: prefer the active window; allow user's choice of center component when it is
// in the active window
//
if (centeredOnComponent != null &&
SwingUtilities.isDescendingFrom(centeredOnComponent, bestParent)) {
bestCenter = centeredOnComponent;
}
DockingDialog dialog = DockingDialog.createDialog(bestParent, provider, bestCenter);
dialog.setVisible(true);
};
if (provider.isModal()) {
Swing.runNow(r);
}
else {
Swing.runIfSwingOrRunLater(r);
}
}
private static Component getCenterOnComponent(Window parent, Component centeredOnComponent) {
private static Window getBestNonModalParent(DialogComponentProvider newProvider,
Window bestParent) {
/*
This method seeks to accomplish 2 goals:
1) find a suitable component over which to center, and
2) ensure that the chosen component is in the parent hierarchy
*/
Component bestComponent = centeredOnComponent;
if (SwingUtilities.isDescendingFrom(parent, bestComponent)) {
return bestComponent;
}
// by default, prefer to center over the active window
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Window activeWindow = kfm.getActiveWindow();
bestComponent = activeWindow;
if (SwingUtilities.isDescendingFrom(parent, bestComponent)) {
return bestComponent;
if (!(activeWindow instanceof DockingDialog)) {
return bestParent;
}
// The chosen component is not in the parent's hierarchy. See if there exists a
// non-transient parent window for that component.
Window newWindow = getParentWindow(parent);
if (newWindow != null) {
// the component is safe to use; the caller of this method will validate the component
// we return, updating the parent as needed
return bestComponent;
DockingDialog dialog = (DockingDialog) activeWindow;
if (!dialog.isModal()) {
// Note: See issue 'E' described in getParentWindow()
// If we parent to a non-modal progress dialog (which is odd), when it goes away, so
// to do we. Assume that non-modal dialogs in general are long lived. We shall only
// enforce parenting to modal dialogs as defined below in order to prevent blocking of
// the non-modal dialog.
return bestParent; // not modal; assume no issues
}
// We were unable to find a suitable parent for the 'best' component. Just return the
// parent as the thing over which to center.
return parent;
DialogComponentProvider activeProvider = dialog.getComponent();
if (activeProvider == null) {
return bestParent;
}
int activeId = activeProvider.getId();
int newId = newProvider.getId();
if (newId < activeId) {
// the new provider is actually older than the active window--do not parent the
// new provider to that window
return bestParent;
}
//
// The active window is modal. We must make it the non-modal dialog's parent to
// prevent blocking the non-modal.
//
return dialog;
}
private static void doShowDialog(DialogComponentProvider provider, Window parent,
Component centeredOnComponent) {
Runnable r = () -> {
if (provider.isVisible()) {
provider.toFront();
return;
}
DockingDialog dialog =
DockingDialog.createDialog(parent, provider, centeredOnComponent);
dialog.setVisible(true);
};
if (provider.isModal()) {
Swing.runNow(r);
}
else {
Swing.runIfSwingOrRunLater(r);
}
}
private static Window getParentWindow(Component parent) {
@ -1833,24 +1862,23 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
-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
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,
then the script progress dialog appears on top (which we do not want) and the
progress dialog goes away when the input dialog is closed.
E -A long-running API shows a non-modal progress dialog. This API then shows a
results dialog which is also non-modal. We do not want to parent the new dialog
to the original dialog, since it is a progress dialog that will go away.
For now, the easiest mental model to use is to always prefer the active window so
that a dialog will appear in the user's view. If we find a case where this is
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.
*/
//
// Due to Use Case 'C' above, prefer dialogs as parents, so that child non-modal dialogs
// do not get blocked by modal dialogs.
//
if (isNonTransientWindow(parent)) {
return (Window) parent;
}
*/
DockingWindowManager dwm = getActiveInstance();
Window defaultWindow = dwm != null ? dwm.getRootFrame() : null;
if (parent == null) {
Window w = getActiveNonTransientWindow();
return w == null ? defaultWindow : w;
@ -1862,7 +1890,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
// parent of 'c' if it itself is parented to a Frame. The issue is that
// Use Case 'C' above may not work correctly. If we find that to be the case,
// then we can try changing 'Frame' to 'Window' here.
if (c instanceof Frame && isNonTransientWindow(c)) {
if (c instanceof Window && isNonTransientWindow(c)) {
return (Window) c;
}
c = c.getParent();
@ -1887,12 +1915,21 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
return false; // we have seen this in testing
}
return !provider.isTransient();
return !provider.isModal() && !provider.isTransient();
}
if (c instanceof Dialog) {
return !((Dialog) c).isModal();
}
return (c instanceof Window);
}
private static Window getJavaActiveWindow() {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
return kfm.getActiveWindow();
}
private static Window getActiveNonTransientWindow() {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();

View file

@ -174,6 +174,7 @@ public class OptionDialog extends DialogComponentProvider {
protected OptionDialog(String title, String message, String option1, String option2,
int messageType, Icon icon, boolean addCancel) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, 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,
int messageType, Icon icon, boolean addCancel, String defaultButtonName) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, null);
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,
String option3, int messageType, Icon icon, boolean addCancel) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, 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,
DialogRememberOption savedDialogChoice, List<String> options, String defaultOption) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, savedDialogChoice);
buildButtons(options, addCancelButton, defaultOption);
}
@ -256,6 +260,7 @@ public class OptionDialog extends DialogComponentProvider {
private void buildMainPanel(String message, int messageType, Icon icon,
DialogRememberOption rememberOptionChoice) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

View file

@ -75,6 +75,7 @@ public abstract class AbstractNumberInputDialog extends DialogComponentProvider
}
this.max = max;
setTransient(true);
addWorkPanel(buildMainPanel(prompt, showAsHex));
addOKButton();
addCancelButton();

View file

@ -53,7 +53,7 @@ public class InputDialog extends DialogComponentProvider {
* @param label value to use for the label of the text field
*/
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
*/
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,
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
*/
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
*/
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 labels values to use for the labels of 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
*/
public InputDialog(String dialogTitle, String[] labels, String[] initialValues, boolean isModal,
public InputDialog(String dialogTitle, String[] labels, String[] initialValues,
InputDialogListener listener) {
super(dialogTitle, isModal, (listener != null) /* status */, true /* buttons */,
super(dialogTitle, true, (listener != null) /* status */, true /* buttons */,
false /* no tasks */);
this.listener = listener;
@ -152,8 +151,9 @@ public class InputDialog extends DialogComponentProvider {
// put the rest of the dialog together
inputLabels = labels;
this.initialValues = initialValues;
this.addOKButton();
this.addCancelButton();
setTransient(true);
addOKButton();
addCancelButton();
buildMainPanel();
if (initialValues != null && initialValues[0] != null && initialValues[0].length() > 0) {

View file

@ -55,10 +55,11 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
super(dialogTitle, true, false, true, false);
this.addOKButton();
this.addCancelButton();
this.setRememberSize(false);
this.setRememberLocation(false);
setTransient(true);
addOKButton();
addCancelButton();
setRememberSize(false);
setRememberLocation(false);
buildMainPanel(label, optionValues, initialValue, messageIcon);
setFocusComponent(combo);

View file

@ -43,6 +43,7 @@ public class MultiLineInputDialog extends DialogComponentProvider {
setFocusComponent(inputTextArea);
setTransient(true);
addOKButton();
addCancelButton();
}

View file

@ -148,6 +148,7 @@ public class MultiLineMessageDialog extends DialogComponentProvider {
workPanel.add(iconLabel, BorderLayout.WEST);
}
setTransient(true);
addWorkPanel(workPanel);
addOKButton();

View file

@ -36,6 +36,8 @@ public class ObjectChooserDialog<T> extends DialogComponentProvider {
this.objectClass = objectClass;
this.choosableObjects = choosableObjects;
this.methodsForColumns = methodsForColumns;
setTransient(true);
addWorkPanel(buildWorkPanel());
addOKButton();
addCancelButton();

View file

@ -55,8 +55,9 @@ public class ReadTextDialog extends DialogComponentProvider {
}
private void init(JPanel workPanelToInit) {
this.addWorkPanel(workPanelToInit);
this.addOKButton();
setTransient(true);
addWorkPanel(workPanelToInit);
addOKButton();
setRememberLocation(false);
setRememberSize(false);
}

View file

@ -52,6 +52,8 @@ public class SettingsDialog extends DialogComponentProvider {
if (help != null) {
setHelpLocation(help);
}
setTransient(true);
addWorkPanel(buildWorkPanel());
addDismissButton();

View file

@ -234,6 +234,7 @@ public class GhidraFileChooser extends DialogComponentProvider
super(TITLE, true, true, true, false);
this.parent = parent;
setTransient(true);
init(model);
loadRecentList();
loadOptions();

View file

@ -199,14 +199,14 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
String filterLabel) {
this.table = table;
buildPanel(filterLabel);
uniquePreferenceKey = createUniqueFilterPreferenceKey(table);
transformer = new DefaultRowFilterTransformer<>(tableModel, table.getColumnModel());
textFilterModel = installTableModel(tableModel);
buildPanel(filterLabel);
TableColumnModel columnModel = table.getColumnModel();
columnModel.addColumnModelListener(columnModelListener);
@ -478,7 +478,8 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
columnFilterDialog = new ColumnFilterDialog<>(this, table, tableModel);
}
else {
Msg.showError(this, this, "Column Filter Error", "This table contains no filterable columns!");
Msg.showError(this, this, "Column Filter Error",
"This table contains no filterable columns!");
return;
}

View file

@ -96,6 +96,7 @@ class BasicTaskMonitor implements TaskMonitor {
public void initialize(long maxValue) {
setMaximum(maxValue);
setProgress(0);
setIndeterminate(false);
}
@Override

View file

@ -55,13 +55,47 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
public final static int DEFAULT_WIDTH = 275;
/*
* Note: all paths of finishing should end up calling this runnable.
*
* Workflow:
*
* Dialog Close Button Pressed:
* -calls cancelCallback()
* -calls verifyCancel runnable
* -calls iternalCancel()
* -triggers taskProcessed()
* -calls closeDialog runnable
*
* Cancel Button Pressed:
* -(same as Dialog Close Button Pressed)
*
* Task Monitor Stop Button Pressed:
* -triggers taskProcessed()
* -calls closeDialog runnable
*
* Public API dispose() is Called:
* -calls iternalCancel()
* -triggers taskProcessed()
* -calls closeDialog runnable
*
* Task Monitor Cancelled by API:
* -triggers taskProcessed()
* -calls closeDialog runnable
*
* Task Finishes Normally:
* -triggers taskProcessed()
* -calls closeDialog runnable
*
*
*/
private Runnable closeDialog = () -> {
close();
dispose();
cleanup();
};
private Runnable verifyCancel = () -> {
if (promptToVerifyCancel()) {
cancel();
internalCancel();
}
};
@ -196,6 +230,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
@Override
protected void cancelCallback() {
// note: this is called from the cancel button and when the dialog close button is pressed
Swing.runLater(verifyCancel);
}
@ -211,11 +246,10 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
}
/**
* Called after the task has been executed
* Called after the task has been executed or when the task is cancelled
*/
public void taskProcessed() {
finished.countDown();
monitorComponent.notifyChangeListeners();
Swing.runLater(closeDialog);
}
@ -305,8 +339,14 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
}
}
/**
* Cancels the task and closes this dialog
*/
public void dispose() {
cancel();
internalCancel();
}
private void cleanup() {
showTimer.cancel();
messageUpdater.dispose();
}
@ -339,15 +379,21 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
@Override
public void initialize(long max) {
if (!supportsProgress) {
if (max <= 0) {
return;
}
if (!monitorComponent.isShowing()) {
installProgressMonitor();
monitorComponent.initialize(max);
if (!supportsProgress) {
supportsProgress = true;
setIndeterminate(false);
}
monitorComponent.initialize(max);
// Note: it is not clear why we only wish to show progress if the monitor is not
// visible. This seems wrong. If someone knows, please update this code.
//if (!monitorComponent.isShowing()) {
installProgressMonitor();
//}
}
@Override
@ -364,6 +410,17 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
public void setIndeterminate(boolean indeterminate) {
supportsProgress = !indeterminate;
monitorComponent.setIndeterminate(indeterminate);
// Assumption: if the client calls this method to show progress, then we should honor
// that request. If we find that nested monitor usage causes dialogs to incorrectly
// toggle monitors, then we need to update those clients to use a wrapping style
// monitor that prevents the behavior.
if (supportsProgress) {
installProgressMonitor();
}
else {
installActivityDisplay();
}
}
@Override
@ -378,11 +435,15 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
@Override
public synchronized void cancel() {
internalCancel();
}
private void internalCancel() {
if (monitorComponent.isCancelled()) {
return;
}
// Mark as cancelled, must be detected by task which should terminate
// and invoke setCompleted which will dismiss dialog.
// mark as cancelled; the task will terminate and the callback will dismiss this dialog
monitorComponent.cancel();
}

View file

@ -26,7 +26,7 @@ import javax.swing.*;
import docking.widgets.EmptyBorderButton;
import docking.widgets.OptionDialog;
import docking.widgets.label.GDHtmlLabel;
import ghidra.util.SystemUtilities;
import ghidra.util.Swing;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.CancelledException;
@ -208,7 +208,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
// a task dialog for fast background tasks.
//
isIndeterminate.set(indeterminate);
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
Swing.runIfSwingOrRunLater(() -> {
boolean newValue = isIndeterminate.get();
progressBar.setIndeterminate(newValue);
progressBar.setStringPainted(!newValue);
@ -219,7 +219,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
public synchronized void setCancelEnabled(boolean enable) {
if (cancelEnabled != enable) {
cancelEnabled = enable;
SystemUtilities.runSwingLater(updateCancelButtonRunnable);
Swing.runLater(updateCancelButtonRunnable);
}
}
@ -237,7 +237,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
isCancelled = true;
}
notifyChangeListeners();
notifyCancelListeners();
}
@Override
@ -286,7 +286,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
public synchronized void showProgress(boolean show) {
if (show != showingProgress) {
showingProgress = show;
SystemUtilities.runSwingLater(updateProgressPanelRunnable);
Swing.runLater(updateProgressPanelRunnable);
}
}
@ -298,7 +298,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
*/
public void setTaskName(String name) {
taskName = name;
SystemUtilities.runSwingLater(updateToolTipRunnable);
Swing.runLater(updateToolTipRunnable);
}
/**
@ -342,16 +342,16 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
showingIcon = visible;
};
SystemUtilities.runSwingNow(r);
Swing.runNow(r);
}
protected void notifyChangeListeners() {
protected void notifyCancelListeners() {
Runnable r = () -> {
for (CancelledListener mcl : listeners) {
mcl.cancelled();
}
};
SwingUtilities.invokeLater(r);
Swing.runLater(r);
}
private synchronized void startUpdateTimer() {
@ -493,7 +493,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
cancelButton.setName("CANCEL_TASK");
cancelButton.setPreferredSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
cancelButton.addActionListener(e -> SwingUtilities.invokeLater(shouldCancelRunnable));
cancelButton.addActionListener(e -> Swing.runLater(shouldCancelRunnable));
cancelButton.setFocusable(false);
cancelButton.setRolloverEnabled(true);

View file

@ -47,6 +47,8 @@ class TaskRunner {
void run() {
BasicTaskMonitor internalMonitor = new BasicTaskMonitor();
internalMonitor.setIndeterminate(!task.hasProgress());
internalMonitor.setCancelEnabled(task.canCancel());
WrappingTaskMonitor monitor = new WrappingTaskMonitor(internalMonitor);
startTaskThread(monitor);
showTaskDialog(monitor);

View file

@ -68,10 +68,16 @@ public class WrappingTaskMonitor implements TaskMonitor {
delegate.removeCancelledListener(l);
}
newDelegate.initialize(delegate.getMaximum());
if (delegate.isIndeterminate()) {
newDelegate.setIndeterminate(true);
}
else {
newDelegate.setIndeterminate(false);
newDelegate.initialize(delegate.getMaximum());
}
newDelegate.setProgress(delegate.getProgress());
newDelegate.setMessage(delegate.getMessage());
newDelegate.setIndeterminate(delegate.isIndeterminate());
newDelegate.setCancelEnabled(delegate.isCancelEnabled());
this.delegate = newDelegate;

View file

@ -1446,7 +1446,7 @@ public abstract class PluginTool extends AbstractDockingTool {
* @param centeredOnComponent the component on which to center the dialog.
*/
public void showDialog(DialogComponentProvider dialogComponent, Component centeredOnComponent) {
DockingWindowManager.showDialog(getToolFrame(), dialogComponent, centeredOnComponent);
DockingWindowManager.showDialog(centeredOnComponent, dialogComponent);
}
public Window getActiveWindow() {

View file

@ -40,12 +40,12 @@ import ghidra.program.model.util.StringPropertyMap;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
import ghidra.util.InvalidNameException;
import ghidra.util.task.TaskMonitorAdapter;
import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities;
public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTest {
private static final String PROJECT_NAME = "TestProject";
private static final String TEST_PROJECT_NAME = "TestProject";
private TestEnv env;
@ -64,11 +64,11 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
env = new TestEnv();
projectLocator = new ProjectLocator(getTestDirectoryPath(), PROJECT_NAME);
projectLocator = new ProjectLocator(getTestDirectoryPath(), TEST_PROJECT_NAME);
projectLocator.getMarkerFile().delete();
File projectDir = new File(getTestDirectoryPath(),
PROJECT_NAME + ProjectLocator.getProjectDirExtension());
TEST_PROJECT_NAME + ProjectLocator.getProjectDirExtension());
if (projectDir.isDirectory()) {
if (!FileUtilities.deleteDir(projectDir)) {
Assert.fail("Cleanup of old test project failed");
@ -78,7 +78,7 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
@After
public void tearDown() throws Exception {
closeAllWindowsAndFrames();
closeAllWindows();
env.dispose();
if (project != null) {
@ -114,11 +114,6 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
}
}
/**
* Create project
* @param filesystemVersion -1: mangled, 0... indexed version
* @throws Exception
*/
private void createProject(int filesystemVersion) throws Exception {
if (filesystemVersion < 0) {
@ -164,11 +159,6 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
return action;
}
/**
* Create and open project in front-end and popup Project Info dialog
* @param filesystemVersion -1: mangled, 0... indexed version
* @throws Exception
*/
private void openProjectAndInfo(int filesystemVersion) throws Exception {
frontEndTool = env.getFrontEndTool();
@ -197,8 +187,7 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
assertNotNull(action);
performAction(action, true);
dialog = waitForDialogComponent(frontEndTool.getToolFrame(), ProjectInfoDialog.class, 2000);
assertNotNull(dialog);
dialog = waitForDialogComponent(ProjectInfoDialog.class);
}
private DomainFolder getFolder(String folderPath, boolean create) throws IOException {
@ -239,13 +228,13 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
programUserData.endTransaction(txId);
}
df = getFolder(folderPath, true).createFile(name, p, TaskMonitorAdapter.DUMMY_MONITOR);
df = getFolder(folderPath, true).createFile(name, p, TaskMonitor.DUMMY);
}
finally {
p.release(this);
}
df.addToVersionControl("Initial", true, TaskMonitorAdapter.DUMMY_MONITOR);
df.addToVersionControl("Initial", true, TaskMonitor.DUMMY);
return df;
}
@ -266,7 +255,7 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
assertEquals("Initial", versionHistory[0].getComment());
Program p =
(Program) df.getDomainObject(this, false, false, TaskMonitorAdapter.DUMMY_MONITOR);
(Program) df.getDomainObject(this, false, false, TaskMonitor.DUMMY);
try {
AddressFactory addressFactory = p.getAddressFactory();
ProgramUserData programUserData = p.getProgramUserData();
@ -303,7 +292,7 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
JLabel nameLabel = (JLabel) findComponentByName(dialog.getComponent(), "Project Name");
assertNotNull(nameLabel);
assertEquals(PROJECT_NAME, nameLabel.getText());
assertEquals(TEST_PROJECT_NAME, nameLabel.getText());
}
private void doStorageConvert(String buttonText) {
@ -314,7 +303,7 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
pressButton(button, false);
JDialog confirmDialog =
waitForJDialog(null, "Confirm Convert/Upgrade Project Storage", 2000);
waitForJDialog("Confirm Convert/Upgrade Project Storage");
assertNotNull("Expected convert confirm dialog", confirmDialog);
JButton confirmButton = findButtonByText(confirmDialog, "Convert");
@ -322,7 +311,7 @@ public class ProjectInfoFilesystemTest extends AbstractGhidraHeadedIntegrationTe
waitForPostedSwingRunnables();
dialog = waitForDialogComponent(frontEndTool.getToolFrame(), ProjectInfoDialog.class, 5000);
dialog = waitForDialogComponent(ProjectInfoDialog.class);
assertNotNull("Expected to find Project Info dialog after conversion completed", dialog);
ProjectManager projectManager = env.getProjectManager();