diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/MoveBlockTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/MoveBlockTask.java index d3ea2b928d..b19732b2ef 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/MoveBlockTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/MoveBlockTask.java @@ -32,8 +32,9 @@ public class MoveBlockTask extends ProgramTask { private Address currentStart; private Address newStart; private MoveBlockListener listener; - private boolean wasCancelled; - private boolean status; + private boolean cancelled; + private String statusMessage; + private boolean success; /** * Creates a background command for moving memory blocks. The memory block @@ -62,58 +63,53 @@ public class MoveBlockTask extends ProgramTask { Memory mem = program.getMemory(); MemoryBlock block = mem.getBlock(currentStart); monitor.setMessage("Moving Memory Block ..."); - String msg = ""; + statusMessage = ""; Throwable cause = null; try { mem.moveBlock(block, newStart, monitor); if (monitor.isCancelled()) { - wasCancelled = true; + cancelled = true; } else { - status = true; + success = true; listener.moveBlockCompleted(this); return; } } catch (OutOfMemoryError e) { - msg = "Insufficient memory to complete operation"; + statusMessage = "Insufficient memory to complete operation"; cause = e; } - catch (NotFoundException exc) { - msg = "Memory block not found"; - cause = exc; + catch (NotFoundException e) { + statusMessage = "Memory block not found"; + cause = e; } - catch (MemoryConflictException exc) { - msg = exc.getMessage(); - cause = exc; - } - catch (MemoryBlockException exc) { - msg = exc.getMessage(); - cause = exc; - } - catch (IllegalArgumentException e) { - msg = e.getMessage(); + catch (MemoryConflictException | MemoryBlockException | IllegalArgumentException e) { + statusMessage = e.getMessage(); cause = e; } catch (Throwable t) { Msg.error(this, "Unexpected Exception: " + t.getMessage(), t); - msg = t.getMessage(); - if (msg == null) { - msg = t.toString(); + statusMessage = t.getMessage(); + if (statusMessage == null) { + statusMessage = t.toString(); } cause = t; } - monitor.setMessage(msg); listener.moveBlockCompleted(this); - throw new RollbackException(msg, cause); + throw new RollbackException(statusMessage, cause); } public boolean isCancelled() { - return wasCancelled; + return cancelled; } - public boolean getStatus() { - return status; + public boolean wasSuccessful() { + return success; + } + + public String getStatusMessage() { + return statusMessage; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java index 5053f21bd5..571c1266b8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java @@ -29,9 +29,9 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressFactory; import ghidra.util.HelpLocation; +import ghidra.util.Swing; import ghidra.util.layout.PairLayout; -import ghidra.util.task.BackgroundThreadTaskLauncher; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskBuilder; /** * Dialog that uses a model to validate the fields for moving a block of memory. @@ -49,15 +49,6 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc private MoveBlockModel model; private PluginTool tool; - /** - * Constructor for MoveBlockDialog. - * - * @param dialog - * @param title - * @param modal - * @param includeStatus - * @param includeButtons - */ MoveBlockDialog(MoveBlockModel model, PluginTool tool) { super("Move Memory Block"); this.model = model; @@ -69,33 +60,22 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc addCancelButton(); } - /** - * @see ghidra.app.plugin.contrib.memory.MoveBlockListener#moveBlockCompleted(boolean, - * java.lang.String) - */ @Override - public void moveBlockCompleted(final MoveBlockTask cmd) { - Runnable r = () -> { - if (cmd.getStatus()) { + public void moveBlockCompleted(MoveBlockTask task) { + + setCursor(Cursor.getDefaultCursor()); + boolean success = task.wasSuccessful(); + setOkEnabled(success); + setStatusText(task.getStatusMessage()); + + Swing.runLater(() -> { + if (success) { close(); model.dispose(); } - else { - setCursor(Cursor.getDefaultCursor()); - setOkEnabled(false); - if (cmd.isCancelled()) { - tool.setStatusInfo(getStatusText()); - close(); - model.dispose(); - } - } - }; - SwingUtilities.invokeLater(r); + }); } - /** - * @see ghidra.app.plugin.contrib.memory.MoveBlockListener#stateChanged() - */ @Override public void stateChanged() { setOkEnabled(false); @@ -138,18 +118,20 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc setOkEnabled(false); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(model.makeTask()); - launcher.run(new TaskMonitorAdapter() { - @Override - public void setMessage(String message) { - setStatusText(message); - } - }); + MoveBlockTask task = model.makeTask(); + + //@formatter:off + TaskBuilder.withTask(task) + .setParent(this.getComponent()) + .launchModal() + ; + //@formatter:on } @Override protected void cancelCallback() { close(); + model.dispose(); } private JPanel buildMainPanel() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockModel.java index dab01e3f9b..6aaf1ee7a8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockModel.java @@ -17,15 +17,10 @@ package ghidra.app.plugin.core.memory; import ghidra.app.cmd.memory.MoveBlockListener; import ghidra.app.cmd.memory.MoveBlockTask; -import ghidra.framework.model.DomainObject; -import ghidra.framework.model.DomainObjectChangedEvent; -import ghidra.framework.model.DomainObjectListener; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressFactory; -import ghidra.program.model.address.AddressOverflowException; +import ghidra.framework.model.*; +import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; -import ghidra.util.task.Task; /** * Model for moving a memory block; this class does validation of the new start @@ -172,11 +167,11 @@ class MoveBlockModel implements DomainObjectListener { } /** - * Start the task that will move the block. + * Create the task that will move the block * - * @param delay number of ms to delay until the progress dialog is displayed + * @return the new task */ - Task makeTask() { + MoveBlockTask makeTask() { return new MoveBlockTask(program, block.getStart(), newStartAddr, listener); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java index 72721fd9a5..72f409165b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java @@ -30,7 +30,7 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; -import ghidra.util.task.*; +import ghidra.util.task.TaskBuilder; public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest implements MoveBlockListener { @@ -42,7 +42,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest private MemoryBlock block; private volatile boolean moveCompleted; - private volatile boolean status; + private volatile boolean success; private volatile String errMsg; private Program buildProgram1(String programName) throws Exception { @@ -134,7 +134,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest // wait until the we get the move complete notification waitForCondition(() -> moveCompleted && notepad.canLock()); - assertTrue("Error message= [" + errMsg + "], ", status); + assertTrue("Error message= [" + errMsg + "], ", success); } @Test @@ -145,7 +145,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest // wait until the we get the move complete notification waitForCondition(() -> moveCompleted && notepad.canLock()); - assertTrue("Error message= [" + errMsg + "], ", status); + assertTrue("Error message= [" + errMsg + "], ", success); } @Test @@ -177,7 +177,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest waitForCondition(() -> moveCompleted && x8051.canLock()); setErrorsExpected(false); - assertFalse("Error message= [" + errMsg + "], ", status); + assertFalse("Error message= [" + errMsg + "], ", success); } @Test @@ -242,15 +242,10 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest assertNotNull(errMsg); } - private void launch(Task task) { + private void launch(MoveBlockTask task) { - BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task); - launcher.run(new TaskMonitorAdapter() { - @Override - public void setMessage(String message) { - errMsg = message; - } - }); + TaskBuilder.withTask(task).launchModal(); + errMsg = task.getStatusMessage(); } private Address getNotepadAddr(int offset) { @@ -265,7 +260,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest @Override public void moveBlockCompleted(MoveBlockTask cmd) { moveCompleted = true; - this.status = cmd.getStatus(); + this.success = cmd.wasSuccessful(); } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/DataLoadingConstraintEditor.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/DataLoadingConstraintEditor.java index 3c2c9c1529..2f1fd7c31c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/DataLoadingConstraintEditor.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/DataLoadingConstraintEditor.java @@ -221,8 +221,8 @@ public abstract class DataLoadingConstraintEditor extends AbstractColumnConst reloadDataButton.setVisible(false); Task task = new LoadDataTask(); task.addTaskListener(this); - BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task); - launcher.run(taskMonitorComponent); + + TaskBuilder.withTask(task).launchInBackground(taskMonitorComponent); } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/BackgroundThreadTaskLauncher.java b/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/BackgroundThreadTaskLauncher.java index 4ca176904b..81a19ab4ee 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/BackgroundThreadTaskLauncher.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/BackgroundThreadTaskLauncher.java @@ -30,15 +30,15 @@ import ghidra.util.TaskUtilities; * *

See {@link TaskLauncher}. */ -public class BackgroundThreadTaskLauncher { +class BackgroundThreadTaskLauncher { private Task task; - public BackgroundThreadTaskLauncher(Task task) { + BackgroundThreadTaskLauncher(Task task) { this.task = task; } - public void run(TaskMonitor monitor) { + void run(TaskMonitor monitor) { // add the task here, so we can track it before it is actually started by the thread TaskUtilities.addTrackedTask(task, monitor); @@ -49,5 +49,6 @@ public class BackgroundThreadTaskLauncher { Thread.currentThread().setName(name); task.monitoredRun(monitor); }); + } } diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/TaskBuilder.java b/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/TaskBuilder.java index e0b44d6abe..01f9c548c3 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/TaskBuilder.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/util/task/TaskBuilder.java @@ -255,6 +255,22 @@ public class TaskBuilder { new TaskLauncher(t, parent, delay, dialogWidth); } + /** + * Runs the task in a background thread with the given monitor that cannot be null. This + * is a special case for clients that already have a task monitor widget in their UI and + * they wish to let it show the progress of the given task while not blocking the Swing + * thread. + * + * @param monitor the task monitor; may not be null + */ + public void launchInBackground(TaskMonitor monitor) { + // validate(); // not needed since we are in the background + Objects.requireNonNull(monitor); + BackgroundThreadTaskLauncher launcher = + new BackgroundThreadTaskLauncher(new TaskBuilderTask(false)); + launcher.run(monitor); + } + private void validate() { if (title == null) { throw new NullPointerException("Task title cannot be null");