GP-628 - Dialogs - test of greatly simplified parenting code

This commit is contained in:
dragonmacher 2021-01-29 14:31:16 -05:00
parent d8166ff6ec
commit b956870432
13 changed files with 242 additions and 174 deletions

View file

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

View file

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

View file

@ -204,6 +204,7 @@ public class DualProgramTest extends DiffTestAdapter {
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram"); TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
pressButton(win, "OK"); pressButton(win, "OK");
waitForTasks(); waitForTasks();
win = waitForWindow("No Memory In Common"); win = waitForWindow("No Memory In Common");
assertNotNull(win); assertNotNull(win);
MultiLineLabel mll = findComponent(win, MultiLineLabel.class); 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 PROGRESS = "Progress";
private static final String DEFAULT = "No Progress"; private static final String DEFAULT = "No Progress";
protected JPanel rootPanel; private static int idCounter;
private int id = ++idCounter;
private boolean modal; private boolean modal;
private String title; private String title;
protected JPanel rootPanel;
private JPanel mainPanel; private JPanel mainPanel;
private JComponent workPanel; private JComponent workPanel;
private JPanel buttonPanel; private JPanel buttonPanel;
@ -189,6 +192,10 @@ public class DialogComponentProvider
// may be overridden by subclasses // may be overridden by subclasses
} }
public int getId() {
return id;
}
public JComponent getComponent() { public JComponent getComponent() {
return rootPanel; return rootPanel;
} }

View file

@ -38,6 +38,9 @@ import ghidra.util.bean.GGlassPane;
public class DockingDialog extends JDialog implements HelpDescriptor { public class DockingDialog extends JDialog implements HelpDescriptor {
private static Component focusComponent; // allow only one scheduled focus component. See above. 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 WindowListener windowAdapter;
private DialogComponentProvider component; private DialogComponentProvider component;
private boolean hasBeenFocused; private boolean hasBeenFocused;
@ -47,11 +50,7 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
hasBeenFocused = true; hasBeenFocused = true;
} }
}; };
private static Map<String, BoundsInfo> dialogBoundsMap =
LazyMap.lazyMap(new HashMap<>(), () -> new BoundsInfo());
private DockingWindowManager owningWindowManager; private DockingWindowManager owningWindowManager;
private WindowAdapter modalFixWindowAdapter; 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 * @param dialogComponent the DialogComponentProvider object to be shown in a dialog
*/ */
public static void showDialog(DialogComponentProvider dialogComponent) { 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) { ComponentProvider centeredOnProvider) {
ComponentPlaceholder placeholder = getActivePlaceholder(centeredOnProvider); ComponentPlaceholder placeholder = getActivePlaceholder(centeredOnProvider);
Component c = null; Component c = null;
Window parent = null; if (placeholder == null && centeredOnProvider != null) {
if (placeholder != null) { c = centeredOnProvider.getComponent();
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);
} }
doShowDialog(dialogComponent, c);
} }
/** /**
@ -1731,73 +1700,129 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* @see #getParentWindow(Component) for parenting notes * @see #getParentWindow(Component) for parenting notes
*/ */
public static void showDialog(Component parent, DialogComponentProvider dialogComponent) { 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 * Shows the dialog using the given parent window using the optional component for
* positioning * positioning.
*
* <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 * @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 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, public static void showDialog(Window parent, DialogComponentProvider dialogComponent,
Component centeredOnComponent) { Component centeredOnComponent) {
Objects.requireNonNull(parent);
Objects.requireNonNull(centeredOnComponent);
doShowDialog(dialogComponent, parent, centeredOnComponent); doShowDialog(dialogComponent, parent, centeredOnComponent);
} }
private static Window ensureParentHierarchy(Window parent, Component component) { private static void doShowDialog(DialogComponentProvider provider,
if (CollectionUtils.isAllNull(parent, component)) { Component centeredOnComponent) {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
return kfm.getActiveWindow();
}
if (SwingUtilities.isDescendingFrom(parent, component)) { Runnable r = () -> {
return parent; 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) {
/* KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
This method seeks to accomplish 2 goals: Window activeWindow = kfm.getActiveWindow();
1) find a suitable component over which to center, and if (!(activeWindow instanceof DockingDialog)) {
2) ensure that the chosen component is in the parent hierarchy return bestParent;
*/
if (SwingUtilities.isDescendingFrom(parent, centeredOnComponent)) {
return centeredOnComponent;
} }
// DockingDialog dialog = (DockingDialog) activeWindow;
// By default, prefer to center over the active window if (!dialog.isModal()) {
//
Window activeWindow = getActiveNonTransientWindow(); // Note: See issue 'E' described in getParentWindow()
if (SwingUtilities.isDescendingFrom(parent, activeWindow)) { // 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
// Have an active, visible, non-transient window, which may be another dialog. // enforce parenting to modal dialogs as defined below in order to prevent blocking of
// We prefer this to be the parent. // the non-modal dialog.
//
return activeWindow; return bestParent; // not modal; assume no issues
}
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 chosen component is not in the parent's hierarchy. See if there exists a // The active window is modal. We must make it the non-modal dialog's parent to
// non-transient parent window for that component. // prevent blocking the non-modal.
// //
Window newWindow = getParentWindow(centeredOnComponent); return dialog;
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 centeredOnComponent;
}
// We were unable to find a suitable parent for the 'best' component. Just return the private static void doShowDialog(DialogComponentProvider provider, Window parent,
// parent as the thing over which to center. Component centeredOnComponent) {
return parent;
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) { private static Window getParentWindow(Component parent) {
@ -1841,6 +1866,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
dialog is shown. If the non-modal dialog is parented to the modal input dialog, 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 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. 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 non-transient For now, the easiest mental model to use is to always prefer the active non-transient
@ -1849,17 +1877,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
*/ */
//
// 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(); DockingWindowManager dwm = getActiveInstance();
Window defaultWindow = dwm != null ? dwm.getRootFrame() : null; Window defaultWindow = dwm != null ? dwm.getRootFrame() : null;
if (parent == null) { if (parent == null) {
Window w = getActiveNonTransientWindow(); Window w = getActiveNonTransientWindow();
return w == null ? defaultWindow : w; return w == null ? defaultWindow : w;
@ -1871,7 +1890,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
// parent of 'c' if it itself is parented to a Frame. The issue is that // 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, // 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. // then we can try changing 'Frame' to 'Window' here.
if (c instanceof Frame && isNonTransientWindow(c)) { if (c instanceof Window && isNonTransientWindow(c)) {
return (Window) c; return (Window) c;
} }
c = c.getParent(); c = c.getParent();
@ -1896,12 +1915,21 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
return false; // we have seen this in testing 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); return (c instanceof Window);
} }
private static Window getJavaActiveWindow() {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
return kfm.getActiveWindow();
}
private static Window getActiveNonTransientWindow() { private static Window getActiveNonTransientWindow() {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();

View file

@ -199,14 +199,14 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
String filterLabel) { String filterLabel) {
this.table = table; this.table = table;
buildPanel(filterLabel);
uniquePreferenceKey = createUniqueFilterPreferenceKey(table); uniquePreferenceKey = createUniqueFilterPreferenceKey(table);
transformer = new DefaultRowFilterTransformer<>(tableModel, table.getColumnModel()); transformer = new DefaultRowFilterTransformer<>(tableModel, table.getColumnModel());
textFilterModel = installTableModel(tableModel); textFilterModel = installTableModel(tableModel);
buildPanel(filterLabel);
TableColumnModel columnModel = table.getColumnModel(); TableColumnModel columnModel = table.getColumnModel();
columnModel.addColumnModelListener(columnModelListener); columnModel.addColumnModelListener(columnModelListener);
@ -478,10 +478,11 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
columnFilterDialog = new ColumnFilterDialog<>(this, table, tableModel); columnFilterDialog = new ColumnFilterDialog<>(this, table, tableModel);
} }
else { 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; return;
} }
} }
columnFilterDialog.setCloseCallback(() -> { columnFilterDialog.setCloseCallback(() -> {

View file

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

View file

@ -55,13 +55,47 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
public final static int DEFAULT_WIDTH = 275; 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 = () -> { private Runnable closeDialog = () -> {
close(); close();
dispose(); cleanup();
}; };
private Runnable verifyCancel = () -> { private Runnable verifyCancel = () -> {
if (promptToVerifyCancel()) { if (promptToVerifyCancel()) {
cancel(); internalCancel();
} }
}; };
@ -196,6 +230,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
@Override @Override
protected void cancelCallback() { protected void cancelCallback() {
// note: this is called from the cancel button and when the dialog close button is pressed
Swing.runLater(verifyCancel); 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() { public void taskProcessed() {
finished.countDown(); finished.countDown();
monitorComponent.notifyChangeListeners();
Swing.runLater(closeDialog); Swing.runLater(closeDialog);
} }
@ -305,8 +339,14 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
} }
} }
/**
* Cancels the task and closes this dialog
*/
public void dispose() { public void dispose() {
cancel(); internalCancel();
}
private void cleanup() {
showTimer.cancel(); showTimer.cancel();
messageUpdater.dispose(); messageUpdater.dispose();
} }
@ -339,17 +379,21 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
@Override @Override
public void initialize(long max) { public void initialize(long max) {
monitorComponent.initialize(max); if (max <= 0) {
if (!supportsProgress) {
return; return;
} }
if (!monitorComponent.isShowing()) { monitorComponent.initialize(max);
// Note: it is not clear why we only wish to show progress if the monitor is not if (!supportsProgress) {
// visible. This seems wrong. If someone knows, please update this code. supportsProgress = true;
installProgressMonitor(); setIndeterminate(false);
} }
// 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 @Override
@ -391,11 +435,15 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
@Override @Override
public synchronized void cancel() { public synchronized void cancel() {
internalCancel();
}
private void internalCancel() {
if (monitorComponent.isCancelled()) { if (monitorComponent.isCancelled()) {
return; 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(); monitorComponent.cancel();
} }

View file

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

View file

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

View file

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

View file

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