diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java index bac13d4cbd..1db688f854 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java @@ -207,27 +207,29 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager, Opti return program; } - private Program doOpenProgramSwing(ProgramLocator programLocator, int state) { + private Program doOpenProgramSwing(ProgramLocator locator, int state) { // see if already open - Program program = programMgr.getOpenProgram(programLocator); + Program program = programMgr.getOpenProgram(locator); if (program != null) { - showProgram(program, programLocator, state); + showProgram(program, locator, state); return program; } + // see if cached - program = programCache.get(programLocator); + program = programCache.get(locator); if (program != null) { - programMgr.addProgram(program, programLocator, state); + programMgr.addProgram(program, locator, state); return program; } + // ok, then open it - OpenProgramTask task = new OpenProgramTask(programLocator, this); + OpenProgramTask task = new OpenProgramTask(locator, this); new TaskLauncher(task, tool.getToolFrame()); - OpenProgramRequest openProgramReq = task.getOpenProgram(); - if (openProgramReq != null) { - program = openProgramReq.getProgram(); - programMgr.addProgram(program, programLocator, state); - openProgramReq.release(); + OpenProgramRequest request = task.getOpenProgram(); + if (request != null) { + program = request.getProgram(); + programMgr.addProgram(program, locator, state); + request.release(); return program; } return null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java index aaa9952172..06c170e1d8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java @@ -584,7 +584,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc * the screen. In that case, when this parameter is true, then the given location * will be placed in the center of the screen; when the parameter is false, then the * screen will be scrolled only enough to show the cursor. - * @return true if succussful + * @return true if successful */ public boolean goTo(ProgramLocation loc, boolean centerWhenNotVisible) { diff --git a/Ghidra/Features/GhidraGo/src/main/java/ghidra/app/plugin/core/go/GhidraGoPlugin.java b/Ghidra/Features/GhidraGo/src/main/java/ghidra/app/plugin/core/go/GhidraGoPlugin.java index 70d8647115..8b0f488cda 100644 --- a/Ghidra/Features/GhidraGo/src/main/java/ghidra/app/plugin/core/go/GhidraGoPlugin.java +++ b/Ghidra/Features/GhidraGo/src/main/java/ghidra/app/plugin/core/go/GhidraGoPlugin.java @@ -21,14 +21,13 @@ import java.net.URL; import ghidra.app.CorePluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.core.go.ipc.GhidraGoListener; -import ghidra.framework.main.AppInfo; -import ghidra.framework.main.ApplicationLevelOnlyPlugin; +import ghidra.framework.main.*; import ghidra.framework.model.ToolServices; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.protocol.ghidra.GhidraURL; import ghidra.util.Msg; -import ghidra.util.SystemUtilities; +import ghidra.util.Swing; //@formatter:off @PluginInfo( @@ -88,18 +87,19 @@ public class GhidraGoPlugin extends Plugin implements ApplicationLevelOnlyPlugin */ private void processGhidraURL(URL ghidraURL) { - Msg.info(GhidraGoPlugin.class, "GhidraGo processing " + ghidraURL); + Msg.info(this, "GhidraGo processing " + ghidraURL); try { - Msg.info(GhidraGoPlugin.class, + Msg.info(this, "Accepting the resource at " + GhidraURL.getProjectURL(ghidraURL)); - SystemUtilities.runSwingNow(() -> { - AppInfo.getFrontEndTool().toFront(); - AppInfo.getFrontEndTool().getToolServices().launchDefaultToolWithURL(ghidraURL); + Swing.runNow(() -> { + FrontEndTool frontEnd = AppInfo.getFrontEndTool(); + frontEnd.toFront(); + frontEnd.getToolServices().launchDefaultToolWithURL(ghidraURL); }); } catch (IllegalArgumentException e) { - Msg.showError(GhidraGoPlugin.class, null, "GhidraGo Unable to process GhidraURL", + Msg.showError(this, null, "GhidraGo Unable to process GhidraURL", "GhidraGo could not process " + ghidraURL, e); } } diff --git a/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java b/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java index 6088dedd9d..978131edd5 100644 --- a/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java +++ b/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java @@ -1911,6 +1911,7 @@ public class BufferMgr { int bufCount = 0; for (int id = 0; id < indexCnt; id++) { monitor.checkCancelled(); + monitor.setProgress(id); BufferNode node = getCachedBufferNode(id); if (node != null) { // check nod which resides in cache @@ -1929,6 +1930,8 @@ public class BufferMgr { } } + monitor.initialize(indexCnt); + // write/update all non-empty buffers try (OutputBlockStream out = LocalBufferFile.getOutputBlockStream(outFile, bufCount)) { for (int id = 0; id < indexCnt; id++) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java index fd384dac45..1eb818676e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java @@ -654,6 +654,7 @@ public class DropDownTextField extends JTextField implements GComponent { * signalling to use the clicked item. When pressing Enter, they may have been typing and * ignoring the list, so we have to do some validation. */ + @SuppressWarnings("unchecked") // for the cast to T private void setTextFromListOnEnterPress() { Object selectedItem = list.getSelectedValue(); if (selectedItem == null) { diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.java index f4dfde9d95..a092395443 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.java @@ -16,15 +16,14 @@ package ghidra.util.task; /** - * Similar to a {@link Runnable} except the {@link #monitoredRun(TaskMonitor) run} - * method is given a monitor to report progress and check for cancellation. + * Similar to a {@link Runnable} except the {@link #monitoredRun(TaskMonitor) run} method is given a + * monitor to report progress and check for cancellation. */ public interface MonitoredRunnable { - - /** - * Similar to a runnable except the run method is given a monitor - * to report progress and check for cancellation. - * @param monitor the TaskMonitor to use. + + /** + * Runs this runnable, given a monitor to report progress and check for cancellation. + * @param monitor the monitor. */ void monitoredRun(TaskMonitor monitor); } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java index 9e140f8d88..4a32026162 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java @@ -266,7 +266,6 @@ class ToolServicesImpl implements ToolServices { Workspace workspace = toolManager.getActiveWorkspace(); PluginTool tool = workspace.runTool(template); if (tool != null) { - tool.setVisible(true); tool.accept(ghidraUrl); } return tool; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/WorkspaceImpl.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/WorkspaceImpl.java index 6cbd69914d..5f4f5c59c7 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/WorkspaceImpl.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/WorkspaceImpl.java @@ -18,6 +18,8 @@ package ghidra.framework.project.tool; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; import org.jdom.Element; @@ -25,7 +27,9 @@ import ghidra.framework.model.ToolTemplate; import ghidra.framework.model.Workspace; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginToolAccessUtils; +import ghidra.util.Swing; import ghidra.util.exception.DuplicateNameException; +import ghidra.util.task.*; /** * WorkspaceImpl @@ -76,16 +80,42 @@ class WorkspaceImpl implements Workspace { @Override public PluginTool runTool(ToolTemplate template) { - PluginTool tool = toolManager.getTool(this, template); - if (tool != null) { - tool.setVisible(true); - runningTools.add(tool); + // + // Clients that launch a tool would like to have it ready to use when returned from this + // method. For the tool to be ready, it must be created, made visible and fully + // initialized. That process needs to happen on the Swing thread and can be slow. Since + // we may be called on the Swing thread, we use a TaskLauncher, which will show a modal + // dialog. This allows any pending Swing events, including any buffered events (like + // painting and attaching to a parent hierarchy) to be processed by the dialog's secondary + // Swing queue before returning control back to the caller of this method. + // + return launchSwing("Launching Tool", () -> { + PluginTool tool = toolManager.getTool(this, template); + if (tool != null) { + tool.setVisible(true); + runningTools.add(tool); - // alert the tool manager that we have changed - toolManager.setWorkspaceChanged(this); - toolManager.fireToolAddedEvent(this, tool); - } - return tool; + toolManager.setWorkspaceChanged(this); + toolManager.fireToolAddedEvent(this, tool); + } + return tool; + }); + } + + // This method could instead become TaskLauncher.launchSwing(). It seems too niche for general + // use though. + private T launchSwing(String title, Supplier supplier) { + AtomicReference ref = new AtomicReference<>(); + Task t = new Task(title, false, false, true) { + @Override + public void run(TaskMonitor monitor) { + ref.set(Swing.runNow(supplier)); + } + }; + + int delay = 0; + new TaskLauncher(t, null, delay); + return ref.get(); } @Override