Merge remote-tracking branch 'origin/GP-3343_ghidragon_fontend_plugin_dispose--SQUASHED'

This commit is contained in:
Ryan Kurtz 2023-06-08 07:57:13 -04:00
commit 6e533802d4
18 changed files with 172 additions and 156 deletions

View file

@ -28,11 +28,11 @@ import ghidra.util.Msg;
import ghidra.util.Swing; import ghidra.util.Swing;
@DebuggerBotInfo( // @DebuggerBotInfo( //
description = "Show debugger interpreters", // description = "Show debugger interpreters", //
details = "Listens for new debuggers supporting the interpreter interface," + details = "Listens for new debuggers supporting the interpreter interface," +
" and when found, displays that interpeter.", // " and when found, displays that interpeter.", //
help = @HelpInfo(anchor = "show_interpreter"), // help = @HelpInfo(anchor = "show_interpreter"), //
enabledByDefault = true // enabledByDefault = true //
) )
public class ShowInterpreterDebuggerBot implements DebuggerBot { public class ShowInterpreterDebuggerBot implements DebuggerBot {
private DebuggerWorkflowServicePlugin plugin; private DebuggerWorkflowServicePlugin plugin;

View file

@ -26,8 +26,7 @@ import generic.util.WindowUtilities;
import ghidra.framework.data.DomainObjectMergeManager; import ghidra.framework.data.DomainObjectMergeManager;
import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFile;
import ghidra.framework.model.UndoableDomainObject; import ghidra.framework.model.UndoableDomainObject;
import ghidra.framework.plugintool.ModalPluginTool; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginException; import ghidra.framework.plugintool.util.PluginException;
import ghidra.program.model.listing.DomainObjectChangeSet; import ghidra.program.model.listing.DomainObjectChangeSet;
import ghidra.util.*; import ghidra.util.*;
@ -502,7 +501,7 @@ public abstract class MergeManager implements DomainObjectMergeManager {
if (mergePlugin != null) { if (mergePlugin != null) {
mergePlugin.dispose(); mergePlugin.dispose();
} }
mergeTool.exit(); // cleanup! PluginToolAccessUtils.dispose(mergeTool); // cleanup!
mergeTool = null; mergeTool = null;
}); });
} }

View file

@ -42,7 +42,7 @@ public class TestTool extends GhidraTool {
} }
Runnable r = () -> { Runnable r = () -> {
exit(); dispose();
if (getProject().getToolServices() != null) { if (getProject().getToolServices() != null) {
getProject().getToolServices().closeTool(TestTool.this); getProject().getToolServices().closeTool(TestTool.this);
} }

View file

@ -336,8 +336,8 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
saveSubordinateToolConfig(sourceTool); saveSubordinateToolConfig(sourceTool);
saveSubordinateToolConfig(destinationTool); saveSubordinateToolConfig(destinationTool);
pluginList.clear(); pluginList.clear();
sourceTool.exit(); PluginToolAccessUtils.dispose(sourceTool);
destinationTool.exit(); PluginToolAccessUtils.dispose(destinationTool);
sourceTool = null; sourceTool = null;
destinationTool = null; destinationTool = null;
} }

View file

@ -46,7 +46,7 @@ public class AppInfo {
public static void exitGhidra() { public static void exitGhidra() {
assertFrontEndRunning(); assertFrontEndRunning();
tool.exit(); tool.close(); // closing the front end tool will exit the application
} }
private static void assertFrontEndRunning() { private static void assertFrontEndRunning() {

View file

@ -35,6 +35,7 @@ import ghidra.framework.client.RepositoryAdapter;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.PluginToolAccessUtils;
import ghidra.framework.store.LockException; import ghidra.framework.store.LockException;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.exception.NotFoundException; import ghidra.util.exception.NotFoundException;
@ -123,7 +124,7 @@ class FileActionManager {
closeProjectAction = new DockingAction("Close Project", plugin.getName()) { closeProjectAction = new DockingAction("Close Project", plugin.getName()) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
closeProject(false); //not exiting closeProject(false); // not exiting
} }
}; };
closeProjectAction.setEnabled(false); closeProjectAction.setEnabled(false);
@ -187,7 +188,7 @@ class FileActionManager {
// if all is well and we already have an active project, close it // if all is well and we already have an active project, close it
Project activeProject = plugin.getActiveProject(); Project activeProject = plugin.getActiveProject();
if (activeProject != null) { if (activeProject != null) {
if (!closeProject(false)) { // false -->not exiting if (!closeProject(false)) { // false --> not exiting
return; // user canceled return; // user canceled
} }
} }
@ -428,7 +429,7 @@ class FileActionManager {
// check for any changes since last saved // check for any changes since last saved
PluginTool[] runningTools = activeProject.getToolManager().getRunningTools(); PluginTool[] runningTools = activeProject.getToolManager().getRunningTools();
for (PluginTool runningTool : runningTools) { for (PluginTool runningTool : runningTools) {
if (!runningTool.canClose(isExiting)) { if (!PluginToolAccessUtils.canClose(runningTool)) {
return false; return false;
} }
} }

View file

@ -445,23 +445,7 @@ public class FrontEndPlugin extends Plugin
projectDataPanel.readDataState(saveState); projectDataPanel.readDataState(saveState);
} }
/** boolean closeActiveProject() {
* Exit the Ghidra application; the parameter indicates whether
* the user should be prompted to save the project that is about
* to be closed
*/
void exitGhidra() {
boolean okToExit = closeActiveProject();
if (okToExit) {
System.exit(0);
}
else if (!tool.isVisible()) {
tool.setVisible(true);
}
}
private boolean closeActiveProject() {
if (activeProject == null) { if (activeProject == null) {
return true; return true;
} }
@ -470,7 +454,7 @@ public class FrontEndPlugin extends Plugin
} }
catch (Exception e) { catch (Exception e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); // Keep this. Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); // Keep this.
int result = OptionDialog.showOptionDialog(tool.getToolFrame(), "Close Project Failed", int result = OptionDialog.showOptionDialog(null, "Close Project Failed",
"Error Description: [ " + e + " ]" + "\n" + "Error Description: [ " + e + " ]" + "\n" +
"=====> Do you wish to exit Ghidra, possibly losing changes? <=====", "=====> Do you wish to exit Ghidra, possibly losing changes? <=====",
"Exit Ghidra (Possibly Lose Changes)", OptionDialog.ERROR_MESSAGE); "Exit Ghidra (Possibly Lose Changes)", OptionDialog.ERROR_MESSAGE);

View file

@ -166,12 +166,17 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener {
} }
@Override @Override
protected void dispose() { public void dispose() {
super.dispose(); super.dispose();
if (logProvider != null) { if (logProvider != null) {
logProvider.dispose(); logProvider.dispose();
} }
shutdown();
}
protected void shutdown() {
System.exit(0);
} }
private void ensureSize() { private void ensureSize() {
@ -392,13 +397,8 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener {
} }
@Override @Override
public void exit() { public boolean canClose() {
plugin.exitGhidra(); return super.canClose() && plugin.closeActiveProject();
}
@Override
public void close() {
close(true);
} }
/** /**

View file

@ -38,6 +38,7 @@ import ghidra.framework.data.ConvertFileSystem;
import ghidra.framework.data.TransientDataManager; import ghidra.framework.data.TransientDataManager;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.PluginToolAccessUtils;
import ghidra.framework.remote.User; import ghidra.framework.remote.User;
import ghidra.framework.store.local.*; import ghidra.framework.store.local.*;
import ghidra.util.*; import ghidra.util.*;
@ -95,9 +96,9 @@ public class ProjectInfoDialog extends DialogComponentProvider {
connectionButton.setContentAreaFilled(false); connectionButton.setContentAreaFilled(false);
connectionButton.setSelected(isConnected); connectionButton.setSelected(isConnected);
connectionButton.setBorder( connectionButton
isConnected ? BorderFactory.createBevelBorder(BevelBorder.LOWERED) .setBorder(isConnected ? BorderFactory.createBevelBorder(BevelBorder.LOWERED)
: BorderFactory.createBevelBorder(BevelBorder.RAISED)); : BorderFactory.createBevelBorder(BevelBorder.RAISED));
updateConnectButtonToolTip(); updateConnectButtonToolTip();
if (isConnected) { if (isConnected) {
try { try {
@ -181,8 +182,8 @@ public class ProjectInfoDialog extends DialogComponentProvider {
String toolTipForChange = "Change server information or specify another repository."; String toolTipForChange = "Change server information or specify another repository.";
String toolTipForConvert = "Convert project to be a shared project."; String toolTipForConvert = "Convert project to be a shared project.";
changeConvertButton.setToolTipText( changeConvertButton
repository != null ? toolTipForChange : toolTipForConvert); .setToolTipText(repository != null ? toolTipForChange : toolTipForConvert);
Class<? extends LocalFileSystem> fsClass = project.getProjectData().getLocalStorageClass(); Class<? extends LocalFileSystem> fsClass = project.getProjectData().getLocalStorageClass();
String convertStorageButtonLabel = null; String convertStorageButtonLabel = null;
@ -198,8 +199,8 @@ public class ProjectInfoDialog extends DialogComponentProvider {
convertStorageButton.addActionListener(e -> convertToIndexedFilesystem()); convertStorageButton.addActionListener(e -> convertToIndexedFilesystem());
help.registerHelp(changeConvertButton, help.registerHelp(changeConvertButton,
new HelpLocation(GenericHelpTopics.FRONT_END, "Convert_Project_Storage")); new HelpLocation(GenericHelpTopics.FRONT_END, "Convert_Project_Storage"));
convertStorageButton.setToolTipText( convertStorageButton
"Convert/Upgrade project storage to latest Indexed Filesystem"); .setToolTipText("Convert/Upgrade project storage to latest Indexed Filesystem");
} }
JPanel p = new JPanel(new FlowLayout()); JPanel p = new JPanel(new FlowLayout());
@ -260,9 +261,9 @@ public class ProjectInfoDialog extends DialogComponentProvider {
connectionButton.setName("Connect Button"); connectionButton.setName("Connect Button");
connectionButton.setContentAreaFilled(false); connectionButton.setContentAreaFilled(false);
connectionButton.setSelected(isConnected); connectionButton.setSelected(isConnected);
connectionButton.setBorder( connectionButton
isConnected ? BorderFactory.createBevelBorder(BevelBorder.LOWERED) .setBorder(isConnected ? BorderFactory.createBevelBorder(BevelBorder.LOWERED)
: BorderFactory.createBevelBorder(BevelBorder.RAISED)); : BorderFactory.createBevelBorder(BevelBorder.RAISED));
updateConnectButtonToolTip(); updateConnectButtonToolTip();
HelpService help = Help.getHelpService(); HelpService help = Help.getHelpService();
help.registerHelp(connectionButton, help.registerHelp(connectionButton,
@ -343,8 +344,7 @@ public class ProjectInfoDialog extends DialogComponentProvider {
private void updateSharedProjectInfo() { private void updateSharedProjectInfo() {
int openCount = getOpenFileCount(); int openCount = getOpenFileCount();
if (openCount != 0) { if (openCount != 0) {
Msg.showInfo(getClass(), getComponent(), Msg.showInfo(getClass(), getComponent(), "Cannot Change Project Info with Open Files",
"Cannot Change Project Info with Open Files",
"Found " + openCount + " open project file(s).\n" + "Found " + openCount + " open project file(s).\n" +
"Before your project info can be updated, you must\n" + "Before your project info can be updated, you must\n" +
"close all open project files and tools."); "close all open project files and tools.");
@ -392,7 +392,7 @@ public class ProjectInfoDialog extends DialogComponentProvider {
private boolean checkToolsClose() { private boolean checkToolsClose() {
PluginTool[] runningTools = project.getToolManager().getRunningTools(); PluginTool[] runningTools = project.getToolManager().getRunningTools();
for (PluginTool runningTool : runningTools) { for (PluginTool runningTool : runningTools) {
if (!runningTool.canClose(false)) { if (!PluginToolAccessUtils.canClose(runningTool)) {
return false; return false;
} }
runningTool.close(); runningTool.close();
@ -448,8 +448,7 @@ public class ProjectInfoDialog extends DialogComponentProvider {
int openCount = getOpenFileCount(); int openCount = getOpenFileCount();
if (openCount != 0) { if (openCount != 0) {
Msg.showInfo(getClass(), getComponent(), Msg.showInfo(getClass(), getComponent(), "Cannot Convert Project with Open Files",
"Cannot Convert Project with Open Files",
"Found " + openCount + " open project file(s).\n" + "Found " + openCount + " open project file(s).\n" +
"Before your project can be converted, you must close\n" + "Before your project can be converted, you must close\n" +
"all open project files and tools."); "all open project files and tools.");

View file

@ -126,8 +126,8 @@ public class SaveDataDialog extends DialogComponentProvider {
} }
} }
if (list.size() > 0) { if (list.size() > 0) {
DomainFile[] deleteFiles = new DomainFile[list.size()]; DomainFile[] saveFiles = new DomainFile[list.size()];
SaveTask task = new SaveTask(list.toArray(deleteFiles)); SaveTask task = new SaveTask(list.toArray(saveFiles));
new TaskLauncher(task, getComponent()); new TaskLauncher(task, getComponent());
} }
else { else {

View file

@ -28,6 +28,12 @@ public class TestFrontEndTool extends FrontEndTool {
@Override @Override
public void close() { public void close() {
setVisible(false); // overridden to not ask to save
dispose();
}
@Override
protected void shutdown() {
// let test environment bring the system down
} }
} }

View file

@ -492,10 +492,6 @@ public abstract class PluginTool extends AbstractDockingTool {
return eventMgr.hasToolListeners(); return eventMgr.hasToolListeners();
} }
public void exit() {
dispose();
}
protected void dispose() { protected void dispose() {
isDisposed = true; isDisposed = true;
@ -525,6 +521,7 @@ public abstract class PluginTool extends AbstractDockingTool {
disposeManagers(); disposeManagers();
winMgr.dispose(); winMgr.dispose();
toolServices.closeTool(this);
} }
private void disposeManagers() { private void disposeManagers() {
@ -1148,46 +1145,80 @@ public abstract class PluginTool extends AbstractDockingTool {
} }
/** /**
* Close this tool: * Closes this tool, possibly with input from the user. The following conditions are checked
* and can prompt the user for more info and allow them to cancel the close.
* <OL> * <OL>
* <LI>if there are no tasks running. * <LI>Running tasks. Closing with running tasks could lead to data loss.
* <LI>resolve the state of any plugins so they can be closed. * <LI>Plugins get asked if they can be closed. They may prompt the user to resolve
* <LI>Prompt the user to save any changes. * some plugin specific state.
* <LI>close all associated plugins (this closes the domain object if one is open). * <LI>The user is prompted to save any data changes.
* <LI>pop up dialog to save the configuration if it has changed. * <LI>Tools are saved, possibly asking the user to resolve any conflicts caused by
* <LI>notify the project tool services that this tool is going away. * changing multiple instances of the same tool in different ways.
* <LI>If all the above conditions passed, the tool is closed and disposed.
* </OL> * </OL>
*/ */
@Override @Override
public void close() { public void close() {
close(false); if (canClose()) {
dispose();
}
} }
protected void close(boolean isExiting) { protected boolean canClose() {
if (canClose(isExiting) && pluginMgr.saveData()) { if (isBusy()) {
doClose(); return false;
} }
if (!canClosePlugins()) {
return false;
}
if (!pluginMgr.saveData()) {
return false;
}
return doSaveTool();
} }
/** /**
* Close this tool: * Normally, tools are not allowed to close while tasks are running in that tool as it
* <OL> * could leave the application in an unstable state. Tools that exit the application
* <LI>if there are no tasks running. * (such as the FrontEndTool) can override this so that the user can terminate running tasks
* <LI>close all associated plugins (this closes the domain object if one is open). * and not have that prevent exiting the application.
* <LI>pop up dialog to save the configuration if it has changed; * @return whether the user is allowed to terminate tasks so that the tool can be closed.
* <LI>notify the project tool services that this tool is going away.
* </OL>
*/ */
private void doClose() { protected boolean allowTerminatingTasksWhenClosing() {
return false;
if (!doSaveTool()) {
return; // if cancelled, don't close
}
exit();
toolServices.closeTool(this);
} }
/**
* Checks if this tool's plugins are in a state to be closed.
* @return true if all the plugins in the tool can be closed without further user input.
*/
protected boolean canClosePlugins() {
return pluginMgr.canClose();
}
/**
* Checks if this tool has running tasks, with optionally giving the user an
* opportunity to cancel them.
*
* @return true if this tool has running tasks
*/
protected boolean isBusy() {
if (taskMgr.isBusy()) {
int result = OptionDialog.showYesNoDialog(getToolFrame(), "Tool Busy Executing Task",
"The tool is busy performing a background task.\n If you continue the" +
" task may be terminated and some work may be lost!\n\nContinue anyway?");
if (result != OptionDialog.YES_OPTION) {
return true;
}
taskMgr.stop(false);
}
return false;
}
/**
* Returns true if this tool needs saving
* @return true if this tool needs saving
*/
public boolean shouldSave() { public boolean shouldSave() {
return hasConfigChanged(); // ignore the window layout changes return hasConfigChanged(); // ignore the window layout changes
} }
@ -1224,41 +1255,6 @@ public abstract class PluginTool extends AbstractDockingTool {
return true; return true;
} }
/**
* Can this tool be closed?
* <br>Note: This forces plugins to terminate any tasks they have running and
* apply any unsaved data to domain objects or files. If they can't do
* this or the user cancels then this returns false.
*
* @param isExiting whether the tool is exiting
* @return false if this tool has tasks in progress or can't be closed
* since the user has unfinished/unsaved changes.
*/
public boolean canClose(boolean isExiting) {
if (taskMgr.isBusy()) {
if (isExiting) {
int result = OptionDialog.showYesNoDialog(getToolFrame(),
"Tool Busy Executing Task",
"The tool is busy performing a background task.\n If you continue the" +
" task may be terminated and some work may be lost!\n\nContinue anyway?");
if (result == OptionDialog.NO_OPTION) {
return false;
}
taskMgr.stop(false);
}
else {
beep();
Msg.showInfo(getClass(), getToolFrame(), "Tool Busy",
"You must stop all background tasks before tool may close.");
return false;
}
}
if (!pluginMgr.canClose()) {
return false;
}
return true;
}
/** /**
* Can the domain object be closed? * Can the domain object be closed?
* <br>Note: This forces plugins to terminate any tasks they have running for the * <br>Note: This forces plugins to terminate any tasks they have running for the

View file

@ -0,0 +1,48 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.framework.plugintool;
/**
* Utility class to provide access to non-public methods on PluginTool. There are a number of
* methods that internal classes need access to but we don't want on the public interface of
* PluginTool.This is a stopgap approach until we clean up the package structure for tool related
* classes and interfaces. This class should only be used by internal tool manager classes.
*/
public class PluginToolAccessUtils {
private PluginToolAccessUtils() {
// Can't be constructed
}
/**
* Disposes the tool.
* @param tool the tool to dispose
*/
public static void dispose(PluginTool tool) {
tool.dispose();
}
/**
* Returns true if the tool can be closed. Note this does not handle any data saving. It only
* checks that there are no tasks running and the plugins can be closed.
* @param tool the tool to close
* @return true if the tool can be closed
*/
public static boolean canClose(PluginTool tool) {
return !tool.isBusy() && tool.canClosePlugins();
}
}

View file

@ -303,8 +303,7 @@ public class DefaultProject implements Project {
ProjectFileManager projectData = (ProjectFileManager) c.getProjectData(); ProjectFileManager projectData = (ProjectFileManager) c.getProjectData();
if (projectData == null) { if (projectData == null) {
throw new IOException( throw new IOException(
"Failed to view specified project/repository: " + "Failed to view specified project/repository: " + GhidraURL.getDisplayString(url));
GhidraURL.getDisplayString(url));
} }
url = projectData.getProjectLocator().getURL(); // transform to repository root URL url = projectData.getProjectLocator().getURL(); // transform to repository root URL
@ -423,7 +422,6 @@ public class DefaultProject implements Project {
try { try {
if (toolManager != null) { if (toolManager != null) {
toolManager.close();
toolManager.dispose(); toolManager.dispose();
} }
if (projectManager != null) { if (projectManager != null) {

View file

@ -167,12 +167,12 @@ public class GhidraTool extends PluginTool {
} }
@Override @Override
public void exit() { public void dispose() {
if (fileOpenDropHandler != null) { if (fileOpenDropHandler != null) {
fileOpenDropHandler.dispose(); fileOpenDropHandler.dispose();
fileOpenDropHandler = null; fileOpenDropHandler = null;
} }
super.exit(); super.dispose();
} }
private void addCloseAction() { private void addCloseAction() {

View file

@ -375,16 +375,6 @@ public class ToolManagerImpl implements ToolManager, PropertyChangeListener {
return ((changedWorkspaces.size() > 0) || activeWorkspaceChanged); return ((changedWorkspaces.size() > 0) || activeWorkspaceChanged);
} }
/**
* Close all running tools in the project.
*/
public void close() {
for (Workspace element : workspaces) {
WorkspaceImpl w = (WorkspaceImpl) element;
w.close();
}
}
/** /**
* Save the tools that are opened and changed, that will be brought back up when the project * Save the tools that are opened and changed, that will be brought back up when the project
* is reopened * is reopened
@ -445,6 +435,10 @@ public class ToolManagerImpl implements ToolManager, PropertyChangeListener {
} }
public void dispose() { public void dispose() {
for (Workspace element : workspaces) {
WorkspaceImpl w = (WorkspaceImpl) element;
w.dispose();
}
toolServices.dispose(); toolServices.dispose();
} }

View file

@ -15,13 +15,16 @@
*/ */
package ghidra.framework.project.tool; package ghidra.framework.project.tool;
import java.util.*; import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.jdom.Element; import org.jdom.Element;
import ghidra.framework.model.ToolTemplate; import ghidra.framework.model.ToolTemplate;
import ghidra.framework.model.Workspace; import ghidra.framework.model.Workspace;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.PluginToolAccessUtils;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
/** /**
@ -31,11 +34,10 @@ import ghidra.util.exception.DuplicateNameException;
* *
*/ */
class WorkspaceImpl implements Workspace { class WorkspaceImpl implements Workspace {
private final static int TYPICAL_NUM_RUNNING_TOOLS = 5;
private String name; private String name;
private ToolManagerImpl toolManager; private ToolManagerImpl toolManager;
private Set<PluginTool> runningTools = new HashSet<PluginTool>(TYPICAL_NUM_RUNNING_TOOLS); private Set<PluginTool> runningTools = new CopyOnWriteArraySet<>();
private boolean isActive; private boolean isActive;
WorkspaceImpl(String name, ToolManagerImpl toolManager) { WorkspaceImpl(String name, ToolManagerImpl toolManager) {
@ -223,14 +225,9 @@ class WorkspaceImpl implements Workspace {
* Close all running tools; called from the close() method in * Close all running tools; called from the close() method in
* ToolManagerImpl which is called from the Project's close() * ToolManagerImpl which is called from the Project's close()
*/ */
void close() { void dispose() {
for (PluginTool tool : runningTools) { for (PluginTool tool : runningTools) {
try { PluginToolAccessUtils.dispose(tool);
tool.exit();
}
finally {
toolManager.toolRemoved(this, tool);
}
} }
runningTools.clear(); runningTools.clear();
} }

View file

@ -64,17 +64,11 @@ public class DummyTool extends PluginTool {
name = typeName; name = typeName;
} }
@Override
public void exit() {
//do nothing
}
@Override @Override
public void close() { public void close() {
if (project != null) { if (project != null) {
project.getToolServices().closeTool(this); project.getToolServices().closeTool(this);
} }
} }
@Override @Override
@ -266,7 +260,7 @@ public class DummyTool extends PluginTool {
} }
@Override @Override
public boolean canClose(boolean isExiting) { public boolean canClose() {
return true; return true;
} }