diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm index 99da150e3c..3bc0f20110 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm @@ -359,7 +359,11 @@
  • To open a file in the tool that was specified as the "default," double click on the Program that you want to open, OR right mouse click on - the file and choose Open in Default Tool.
  • + the file and choose Open in Default Tool. Either a new tool will be launched + or an existing running tool will be reused based upon the Tool option setting (see + + Front-End Tool Options. +

    Open a File With a Specific Tool

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm b/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm index 85621a9a6e..ab8fecc578 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Tool/ToolOptions_Dialog.htm @@ -412,7 +412,16 @@ whether or not Ghidra automatically opens the previously loaded project on startup. - + + + Default Tool Launch Mode + + This controls + if a new or already running tool should be used during default launch. + Tool "reuse" mode will open selected file within a suitable running tool + if one can be identified, otherwise a new tool will be launched. + + Use diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java index c68b4c582d..5fd47fabea 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java @@ -316,18 +316,7 @@ public final class LanguageProviderPlugin extends Plugin implements ApplicationL try { SwingUtilities.invokeAndWait(() -> { ToolServices toolServices = tool.getToolServices(); - ToolTemplate defaultToolTemplate = toolServices.getDefaultToolTemplate(file); - if (defaultToolTemplate != null) { - String defaultToolName = defaultToolTemplate.getName(); - for (PluginTool t : toolServices.getRunningTools()) { - if (t.getName().equals(defaultToolName)) { - openTool = t; - break; - } - } - } - if (openTool == null || - !openTool.acceptDomainFiles(new DomainFile[] { file })) { + if (toolServices.launchDefaultTool(domainFile) == null) { Msg.showError(this, tool.getToolFrame(), "Failed to Open Program", "A suitable default tool could not found!"); } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/ContentHandler.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/ContentHandler.java index 9c48a7d8c6..f8406044b0 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/ContentHandler.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/ContentHandler.java @@ -185,8 +185,8 @@ public interface ContentHandler extends Extension Icon getIcon(); /** - * Returns the name of the default tool that should be used to open this content type. - * @return associated default tool for this content type + * Returns the name of the default tool/template that should be used to open this content type. + * @return associated default tool name for this content type */ String getDefaultToolName(); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndPlugin.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndPlugin.java index 0912d2a0c7..05f33daadc 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndPlugin.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndPlugin.java @@ -1102,14 +1102,9 @@ public class FrontEndPlugin extends Plugin } Project project = tool.getProject(); - final ToolServices toolServices = project.getToolServices(); - ToolTemplate defaultToolTemplate = toolServices.getDefaultToolTemplate(domainFile); - if (defaultToolTemplate != null) { - ToolButton button = toolBar.getToolButtonForToolConfig(defaultToolTemplate); - if (button != null) { - button.launchTool(domainFile); - return; - } + ToolServices toolServices = project.getToolServices(); + if (toolServices.launchDefaultTool(domainFile) != null) { + return; } Msg.showInfo(this, tool.getToolFrame(), "Cannot Find Tool", diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java index 49a8eed17f..c613a24d0a 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FrontEndTool.java @@ -87,6 +87,7 @@ import help.HelpService; * manner. */ public class FrontEndTool extends PluginTool implements OptionsChangeListener { + public static final String DEFAULT_TOOL_LAUNCH_MODE = "Default Tool Launch Mode"; public static final String AUTOMATICALLY_SAVE_TOOLS = "Automatically Save Tools"; private static final String USE_ALERT_ANIMATION_OPTION_NAME = "Use Notification Animation"; @@ -122,6 +123,8 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { private WeakSet listeners; private FrontEndPlugin plugin; + private DefaultLaunchMode defaultLaunchMode = DefaultLaunchMode.DEFAULT; + private ComponentProvider compProvider; private LogComponentProvider logProvider; @@ -315,11 +318,21 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { showComponentHeader(compProvider, false); } + /** + * Get the preferred default tool launch mode + * @return default tool launch mode + */ + public DefaultLaunchMode getDefaultLaunchMode() { + return defaultLaunchMode; + } + private void initFrontEndOptions() { ToolOptions options = getOptions(ToolConstants.TOOL_OPTIONS); HelpLocation help = new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Front_End_Tool_Options"); + options.registerOption(DEFAULT_TOOL_LAUNCH_MODE, DefaultLaunchMode.DEFAULT, help, + "Indicates if a new or already running tool should be used during default launch."); options.registerOption(AUTOMATICALLY_SAVE_TOOLS, true, help, "When enabled tools will be saved when they are closed"); options.registerOption(USE_ALERT_ANIMATION_OPTION_NAME, true, help, @@ -332,6 +345,8 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { options.registerOption(RESTORE_PREVIOUS_PROJECT_NAME, Boolean.TRUE, help, "Restore the previous project when Ghidra starts."); + defaultLaunchMode = options.getEnum(DEFAULT_TOOL_LAUNCH_MODE, defaultLaunchMode); + boolean autoSave = options.getBoolean(AUTOMATICALLY_SAVE_TOOLS, true); GhidraTool.autoSave = autoSave; @@ -350,6 +365,9 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener { @Override public void optionsChanged(ToolOptions options, String optionName, Object oldValue, Object newValue) { + if (DEFAULT_TOOL_LAUNCH_MODE.equals(optionName)) { + defaultLaunchMode = (DefaultLaunchMode) newValue; + } if (AUTOMATICALLY_SAVE_TOOLS.equals(optionName)) { GhidraTool.autoSave = (Boolean) newValue; } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java index 9b7b8aacaf..5937995c0c 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java @@ -67,7 +67,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable { private ToolTemplate template; private PluginTool associatedRunningTool; - private DefaultToolChangeListener toolChangeListener; private ToolServices toolServices; /** @@ -132,8 +131,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable { } if (!isRunningTool()) { - toolChangeListener = new ToolChangeListener(template); - toolServices.addDefaultToolChangeListener(toolChangeListener); setIcon(generateIcon()); } } @@ -503,8 +500,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable { } void dispose() { - toolServices.removeDefaultToolChangeListener(toolChangeListener); - plugin = null; template = null; associatedRunningTool = null; @@ -679,22 +674,6 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable { // Inner Classes //================================================================================================== - private class ToolChangeListener implements DefaultToolChangeListener { - private final ToolTemplate toolTemplate; - - public ToolChangeListener(ToolTemplate toolTemplate) { - this.toolTemplate = toolTemplate; - } - - @Override - public void defaultToolChanged(String oldName, String newName) { - String myName = toolTemplate.getName(); - if (myName.equals(oldName) || myName.equals(newName)) { - setIcon(generateIcon()); - } - } - } - private class ToolButtonDropTgtAdapter extends DropTgtAdapter { private boolean draggingOverValidDropTarget = false; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataOpenDefaultToolAction.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataOpenDefaultToolAction.java index 284ae2aa9d..506e2bb06f 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataOpenDefaultToolAction.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataOpenDefaultToolAction.java @@ -21,8 +21,8 @@ import java.util.List; import docking.action.KeyBindingData; import docking.action.MenuData; import ghidra.framework.main.AppInfo; -import ghidra.framework.main.datatable.ProjectDataContext; import ghidra.framework.main.datatable.FrontendProjectTreeAction; +import ghidra.framework.main.datatable.ProjectDataContext; import ghidra.framework.model.DomainFile; public class ProjectDataOpenDefaultToolAction extends FrontendProjectTreeAction { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DefaultToolChangeListener.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DefaultLaunchMode.java similarity index 56% rename from Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DefaultToolChangeListener.java rename to Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DefaultLaunchMode.java index f72c8e0d9d..f8cbbc1884 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DefaultToolChangeListener.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DefaultLaunchMode.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,28 @@ */ package ghidra.framework.model; -/** - * Listener that is notified when the default tool specification changes. - */ -public interface DefaultToolChangeListener { +import ghidra.framework.options.Options; + +/** + * {@link DefaultLaunchMode} provides an {@link Options} value which indicates how a default tool + * launch should be performed. + */ +public enum DefaultLaunchMode { + + REUSE_TOOL("Reuse acceptable running tool"), + NEW_TOOL("Launch new default tool"); + + public static DefaultLaunchMode DEFAULT = NEW_TOOL; + + private String str; + + private DefaultLaunchMode(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } - /** - * Notification that the default tool specification changed - * @param oldName name of the old default tool - * @param newName name of the new default tool - */ - void defaultToolChanged(String oldName, String newName); } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ToolServices.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ToolServices.java index 11b2dd12e6..20150baa2c 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ToolServices.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ToolServices.java @@ -119,6 +119,7 @@ public interface ToolServices { /** * Launch the default tool and open the specified domainFile. + * NOTE: running tool re-use is implementation dependent * @param domainFile the file to open * @return the launched tool. Null returned if a suitable default tool * for the file content type was not found. @@ -136,7 +137,8 @@ public interface ToolServices { /** * Launch the default tool and open the specified Ghidra URL resource. - * The tool choosen well be based upon the content type of the specified resource. + * The tool choosen will be based upon the content type of the specified resource. + * NOTE: running tool re-use is implementation dependent * @param ghidraUrl resource to be opened (see {@link GhidraURL}) * @return the launched tool. Null returned if a failure occurs while accessing the specified * resource or a suitable default tool for the file content type was not found. @@ -155,18 +157,6 @@ public interface ToolServices { */ public PluginTool launchToolWithURL(String toolName, URL ghidraUrl); - /** - * Add a listener that will be notified when the default tool specification changes - * @param listener the listener - */ - public void addDefaultToolChangeListener(DefaultToolChangeListener listener); - - /** - * Remove the listener - * @param listener the listener - */ - public void removeDefaultToolChangeListener(DefaultToolChangeListener listener); - /** * Return array of running tools * @return array of Tools diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/ToolServicesAdapter.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/ToolServicesAdapter.java index 91a3f4cf78..4890313862 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/ToolServicesAdapter.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/ToolServicesAdapter.java @@ -23,10 +23,6 @@ import ghidra.framework.model.*; public class ToolServicesAdapter implements ToolServices { - @Override - public void addDefaultToolChangeListener(DefaultToolChangeListener listener) { - } - @Override public boolean canAutoSave(PluginTool tool) { return true; @@ -97,11 +93,6 @@ public class ToolServicesAdapter implements ToolServices { return null; } - @Override - public void removeDefaultToolChangeListener(DefaultToolChangeListener listener) { - // override - } - @Override public void saveTool(PluginTool tool) { // override 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 77bd5fb2b1..04d1c1f036 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 @@ -18,6 +18,7 @@ package ghidra.framework.project.tool; import java.io.*; import java.net.URL; import java.util.*; +import java.util.function.Function; import org.jdom.Document; import org.jdom.output.XMLOutputter; @@ -26,6 +27,8 @@ import docking.widgets.OptionDialog; import docking.widgets.filechooser.GhidraFileChooser; import ghidra.framework.ToolUtils; import ghidra.framework.data.*; +import ghidra.framework.main.AppInfo; +import ghidra.framework.main.FrontEndTool; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginEvent; import ghidra.framework.plugintool.PluginTool; @@ -49,7 +52,6 @@ class ToolServicesImpl implements ToolServices { private ToolChest toolChest; private ToolManagerImpl toolManager; - private List listeners = new ArrayList<>(); private ToolChestChangeListener toolChestChangeListener; private Set> contentHandlers; @@ -187,22 +189,60 @@ class ToolServicesImpl implements ToolServices { matchingTool.firePluginEvent(event); } - @Override - public PluginTool launchDefaultTool(DomainFile domainFile) { - ToolTemplate template = getDefaultToolTemplate(domainFile); - if (template == null) { - return null; + private static DefaultLaunchMode getDefaultLaunchMode() { + DefaultLaunchMode defaultLaunchMode = DefaultLaunchMode.DEFAULT; + FrontEndTool frontEndTool = AppInfo.getFrontEndTool(); + if (frontEndTool != null) { + defaultLaunchMode = frontEndTool.getDefaultLaunchMode(); } + return defaultLaunchMode; + } + + private PluginTool defaultLaunch(ToolTemplate template, + Function openFunction) { + + DefaultLaunchMode defaultLaunchMode = getDefaultLaunchMode(); + if (defaultLaunchMode == DefaultLaunchMode.REUSE_TOOL) { + if (template != null) { + // attempt to reuse running tool with default name + String defaultToolName = template.getName(); + for (PluginTool tool : getRunningTools()) { + if (tool.getName().equals(defaultToolName) && openFunction.apply(tool)) { + return tool; + } + } + } + + // attempt to reuse any running tool + for (PluginTool tool : getRunningTools()) { + if (openFunction.apply(tool)) { + return tool; + } + } + } + + if (template == null) { + return null; // unable to launch new tool + } + Workspace workspace = toolManager.getActiveWorkspace(); PluginTool tool = workspace.runTool(template); if (tool == null) { - return null; + return null; // tool launch failed } tool.setVisible(true); - tool.acceptDomainFiles(new DomainFile[] { domainFile }); + openFunction.apply(tool); return tool; } + @Override + public PluginTool launchDefaultTool(DomainFile domainFile) { + ToolTemplate template = getDefaultToolTemplate(domainFile); + return defaultLaunch(template, t -> { + return t.acceptDomainFiles(new DomainFile[] { domainFile }); + }); + } + @Override public PluginTool launchTool(String toolName, DomainFile domainFile) { ToolTemplate template = findToolChestToolTemplate(toolName); @@ -228,17 +268,9 @@ class ToolServicesImpl implements ToolServices { return null; } ToolTemplate template = getDefaultToolTemplate(contentType); - if (template == null) { - return null; - } - Workspace workspace = toolManager.getActiveWorkspace(); - PluginTool tool = workspace.runTool(template); - if (tool == null) { - return null; - } - tool.setVisible(true); - tool.accept(ghidraUrl); - return tool; + return defaultLaunch(template, t -> { + return t.accept(ghidraUrl); + }); } @Override @@ -475,16 +507,6 @@ class ToolServicesImpl implements ToolServices { return contentHandlers; } - @Override - public void addDefaultToolChangeListener(DefaultToolChangeListener listener) { - listeners.add(listener); - } - - @Override - public void removeDefaultToolChangeListener(DefaultToolChangeListener listener) { - listeners.remove(listener); - } - private GhidraToolTemplate findToolChestToolTemplate(String toolName) { if (toolName != null) { return (GhidraToolTemplate) toolChest.getToolTemplate(toolName);