GP-508 fixing program actions enablement with regard to debugger program context not working

This commit is contained in:
ghidravore
2021-10-28 17:51:27 -04:00
parent 569cbc61fc
commit aee233c4d2
9 changed files with 460 additions and 249 deletions
@@ -0,0 +1,99 @@
/* ###
* 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.app.plugin.core.progmgr;
import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.app.context.ProgramActionContext;
import ghidra.program.model.listing.Program;
/**
* Abstract base class for program actions that change their menu name depending on the the active
* program. There are two types of actions that extend this class; those that only work
* on programs that are managed by Ghidra, and those that can work on any program even those
* whose life cycles are managed by individual plugins.
*/
public abstract class AbstractProgramNameSwitchingAction extends DockingAction {
protected ProgramManagerPlugin plugin;
protected Program lastContextProgram;
private boolean requiresManagedProgram;
/**
* Constructor
* @param plugin the ProgramManagerPlugin (i.e. the global Ghidra manager for programs)
* @param name the name of the action
* @param requiresManagedProgram true if the action is only used on globally managed
* programs
*/
public AbstractProgramNameSwitchingAction(ProgramManagerPlugin plugin, String name,
boolean requiresManagedProgram) {
super(name, plugin.getName());
this.plugin = plugin;
this.requiresManagedProgram = requiresManagedProgram;
addToWindowWhen(ProgramActionContext.class);
}
@Override
public boolean isValidContext(ActionContext context) {
Program program = getProgram(context);
if (program != lastContextProgram) {
lastContextProgram = program;
programChanged(program);
}
return true;
}
@Override
public final boolean isEnabledForContext(ActionContext context) {
return isEnabledForContext(getProgram(context));
}
protected boolean isEnabledForContext(Program program) {
return program != null;
}
@Override
public void actionPerformed(ActionContext context) {
Program program = getProgram(context);
if (program != null) {
actionPerformed(program);
}
}
protected abstract void actionPerformed(Program program);
protected abstract void programChanged(Program program);
/**
* Gets the program for the given context. If this actions requires the program
* to be globally managed, then it will only use the context program if it is
* managed; otherwise it will return the global current program.
* @param context the action context from which to get the program
* @return the appropriate program to use for this action.
*/
protected Program getProgram(ActionContext context) {
if (context instanceof ProgramActionContext) {
Program program = ((ProgramActionContext) context).getProgram();
if (plugin.isManaged(program) || !requiresManagedProgram) {
return program;
}
// otherwise, just return the global current program.
}
return plugin.getCurrentProgram();
}
}
@@ -0,0 +1,56 @@
/* ###
* 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.app.plugin.core.progmgr;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import docking.tool.ToolConstants;
import ghidra.program.model.listing.Program;
import ghidra.util.HTMLUtilities;
/**
* Action class for the "Close Program" action
*/
public class CloseProgramAction extends AbstractProgramNameSwitchingAction {
public CloseProgramAction(ProgramManagerPlugin plugin, String group, int subGroup) {
super(plugin, "Close File", true);
MenuData menuData = new MenuData(new String[] { ToolConstants.MENU_FILE, "&Close" });
menuData.setMenuGroup(group);
menuData.setMenuSubGroup(Integer.toString(subGroup));
setMenuBarData(menuData);
setKeyBindingData(new KeyBindingData("ctrl o"));
}
@Override
protected void programChanged(Program program) {
if (program == null) {
getMenuBarData().setMenuItemName("&Close");
setDescription("Close Program");
}
else {
String programName = "'" + program.getDomainFile().getName() + "'";
getMenuBarData().setMenuItemName("&Close " + programName);
setDescription("<html>Close " + HTMLUtilities.escapeHTML(programName));
}
}
@Override
public void actionPerformed(Program program) {
plugin.closeProgram(program, false);
}
}
@@ -64,7 +64,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
if (tool == null) { if (tool == null) {
return; // we have been disposed return; // we have been disposed
} }
plugin.updateProgramActions(); plugin.undoStackChanged();
}; };
} }
@@ -501,4 +501,32 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
int openVersion = openFile.isReadOnly() ? openFile.getVersion() : -1; int openVersion = openFile.isReadOnly() ? openFile.getVersion() : -1;
return version == openVersion; return version == openVersion;
} }
/**
* Returns true if this ProgramManager is managing the given program
* @param program the program to check
* @return true if this ProgramManager is managing the given programs
*/
public boolean hasProgram(Program program) {
return programMap.containsKey(program);
}
/**
* Returns true if there is at least one program that has unsaved changes.
* @return true if there is at least one program that has unsaved changes.
*/
public boolean hasUnsavedPrograms() {
// first check the current program as that is the one most likely to have changes
Program currentProgram = getCurrentProgram();
if (currentProgram != null && currentProgram.isChanged()) {
return true;
}
// look at all the open programs to see if any have changes
for (ProgramInfo programInfo : openProgramList) {
if (programInfo.program.isChanged()) {
return true;
}
}
return false;
}
} }
@@ -81,17 +81,9 @@ import ghidra.util.task.TaskLauncher;
public class ProgramManagerPlugin extends Plugin implements ProgramManager { public class ProgramManagerPlugin extends Plugin implements ProgramManager {
private static final String SAVE_GROUP = "DomainObjectSave"; private static final String SAVE_GROUP = "DomainObjectSave";
private static final String OPEN_GROUP = "DomainObjectOpen"; static final String OPEN_GROUP = "DomainObjectOpen";
private MultiProgramManager programMgr; private MultiProgramManager programMgr;
private ProgramSaveManager programSaveMgr; private ProgramSaveManager programSaveMgr;
private DockingAction openAction;
private DockingAction saveAllAction;
private DockingAction closeAction;
private DockingAction saveAction;
private DockingAction saveAsAction;
private DockingAction optionsAction;
private DockingAction closeOthersAction;
private DockingAction closeAllAction;
private int transactionID = -1; private int transactionID = -1;
private OpenVersionedFileDialog openDialog; private OpenVersionedFileDialog openDialog;
private boolean locked = false; private boolean locked = false;
@@ -174,11 +166,11 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
Program openProgram = programMgr.getOpenProgram(ghidraURL); Program openProgram = programMgr.getOpenProgram(ghidraURL);
if (openProgram != null) { if (openProgram != null) {
programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState); programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState);
updateActions();
if (openState == ProgramManager.OPEN_CURRENT) { if (openState == ProgramManager.OPEN_CURRENT) {
gotoProgramRef(openProgram, ghidraURL.getRef()); gotoProgramRef(openProgram, ghidraURL.getRef());
programMgr.saveLocation(); programMgr.saveLocation();
} }
contextChanged();
return openProgram; return openProgram;
} }
@@ -215,7 +207,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
} }
programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState); programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState);
updateActions(); contextChanged();
openProgram.release(this); openProgram.release(this);
if (openState == ProgramManager.OPEN_CURRENT) { if (openState == ProgramManager.OPEN_CURRENT) {
gotoProgramRef(openProgram, ghidraURL.getRef()); gotoProgramRef(openProgram, ghidraURL.getRef());
@@ -310,7 +302,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
Program program = Swing.runNow(() -> { Program program = Swing.runNow(() -> {
Program p = doOpenProgram(domainFile, version, state); Program p = doOpenProgram(domainFile, version, state);
updateActions(); contextChanged();
return p; return p;
}); });
@@ -347,16 +339,16 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
} }
@Override @Override
public boolean closeOtherPrograms(final boolean ignoreChanges) { public boolean closeOtherPrograms(boolean ignoreChanges) {
final Program[] otherPrograms = programMgr.getOtherPrograms(); Program[] otherPrograms = programMgr.getOtherPrograms();
Runnable r = () -> doCloseAllPrograms(otherPrograms, ignoreChanges); Runnable r = () -> doCloseAllPrograms(otherPrograms, ignoreChanges);
SystemUtilities.runSwingNow(r); SystemUtilities.runSwingNow(r);
return programMgr.isEmpty(); return programMgr.isEmpty();
} }
@Override @Override
public boolean closeAllPrograms(final boolean ignoreChanges) { public boolean closeAllPrograms(boolean ignoreChanges) {
final Program[] openPrograms = programMgr.getAllPrograms(); Program[] openPrograms = programMgr.getAllPrograms();
Runnable r = () -> doCloseAllPrograms(openPrograms, ignoreChanges); Runnable r = () -> doCloseAllPrograms(openPrograms, ignoreChanges);
SystemUtilities.runSwingNow(r); SystemUtilities.runSwingNow(r);
return programMgr.isEmpty(); return programMgr.isEmpty();
@@ -396,11 +388,11 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
for (Program program : toRemove) { for (Program program : toRemove) {
programMgr.removeProgram(program); programMgr.removeProgram(program);
} }
updateActions(); contextChanged();
} }
@Override @Override
public boolean closeProgram(final Program program, final boolean ignoreChanges) { public boolean closeProgram(Program program, boolean ignoreChanges) {
if (program == null) { if (program == null) {
return false; return false;
} }
@@ -411,7 +403,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
if (ignoreChanges || program.isClosed() || programMgr.isPersistent(program) || if (ignoreChanges || program.isClosed() || programMgr.isPersistent(program) ||
(tool.canCloseDomainObject(program) && programSaveMgr.canClose(program))) { (tool.canCloseDomainObject(program) && programSaveMgr.canClose(program))) {
programMgr.removeProgram(program); programMgr.removeProgram(program);
updateActions(); contextChanged();
} }
}; };
SystemUtilities.runSwingNow(r); SystemUtilities.runSwingNow(r);
@@ -434,16 +426,16 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
if (currentProgram != null) { if (currentProgram != null) {
programMgr.removeProgram(currentProgram); programMgr.removeProgram(currentProgram);
} }
updateActions(); contextChanged();
tool.setSubTitle(""); tool.setSubTitle("");
tool.clearLastEvents(); tool.clearLastEvents();
} }
@Override @Override
public void setCurrentProgram(final Program p) { public void setCurrentProgram(Program p) {
Runnable r = () -> { Runnable r = () -> {
programMgr.setCurrentProgram(p); programMgr.setCurrentProgram(p);
updateActions(); contextChanged();
}; };
SystemUtilities.runSwingNow(r); SystemUtilities.runSwingNow(r);
} }
@@ -483,7 +475,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
if (state == ProgramManager.OPEN_CURRENT) { if (state == ProgramManager.OPEN_CURRENT) {
programMgr.saveLocation(); programMgr.saveLocation();
} }
updateActions(); contextChanged();
}; };
SystemUtilities.runSwingNow(r); SystemUtilities.runSwingNow(r);
} }
@@ -496,7 +488,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
@Override @Override
protected boolean saveData() { protected boolean saveData() {
boolean result = programSaveMgr.canCloseAll(); boolean result = programSaveMgr.canCloseAll();
updateActions(); contextChanged();
return result; return result;
} }
@@ -513,100 +505,57 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
private void createActions() { private void createActions() {
int subMenuGroupOrder = 1; int subMenuGroup = 1;
openAction = new ActionBuilder("Open File", getName()) DockingAction openAction = new ActionBuilder("Open File", getName())
.menuPath(ToolConstants.MENU_FILE, "&Open...") .menuPath(ToolConstants.MENU_FILE, "&Open...")
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++)) .menuGroup(OPEN_GROUP, Integer.toString(subMenuGroup++))
.keyBinding("ctrl O") .keyBinding("ctrl O")
.enabledWhen(c -> !locked)
.onAction(c -> open()) .onAction(c -> open())
.buildAndInstall(tool); .buildAndInstall(tool);
// .withContext(ProgramActionContext.class)
// .inWindow(ActionBuilder.When.CONTEXT_MATCHES)
// openAction doesn't really use a context, but we want it to be in windows that
// have providers that use programs.
openAction.addToWindowWhen(ProgramActionContext.class); openAction.addToWindowWhen(ProgramActionContext.class);
closeAction = new ActionBuilder("Close File", getName()) tool.addAction(new CloseProgramAction(this, OPEN_GROUP, subMenuGroup++));
.menuPath(ToolConstants.MENU_FILE, "&Close") new ActionBuilder("Close Others", getName())
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
.withContext(ProgramActionContext.class)
.supportsDefaultToolContext(true)
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
.onAction(c -> closeProgram(c.getProgram(), false))
.keyBinding("ctrl W")
.buildAndInstall(tool);
closeOthersAction = new ActionBuilder("Close Others", getName())
.menuPath(ToolConstants.MENU_FILE, "Close &Others") .menuPath(ToolConstants.MENU_FILE, "Close &Others")
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++)) .menuGroup(OPEN_GROUP, Integer.toString(subMenuGroup++))
.enabled(false) .enabled(false)
.withContext(ProgramActionContext.class) .withContext(ProgramActionContext.class)
.inWindow(ActionBuilder.When.CONTEXT_MATCHES) .inWindow(ActionBuilder.When.CONTEXT_MATCHES)
.enabledWhen(c -> programMgr.hasProgram(c.getProgram()))
.onAction(c -> closeOtherPrograms(false)) .onAction(c -> closeOtherPrograms(false))
.buildAndInstall(tool); .buildAndInstall(tool);
closeAllAction = new ActionBuilder("Close All", getName()) DockingAction closeAllAction = new ActionBuilder("Close All", getName())
.menuPath(ToolConstants.MENU_FILE, "Close &All") .menuPath(ToolConstants.MENU_FILE, "Close &All")
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++)) .menuGroup(OPEN_GROUP, Integer.toString(subMenuGroup++))
.withContext(ProgramActionContext.class) .enabledWhen(c -> !programMgr.isEmpty())
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
.onAction(c -> closeAllPrograms(false)) .onAction(c -> closeAllPrograms(false))
.enabled(false)
.buildAndInstall(tool); .buildAndInstall(tool);
closeAllAction.addToWindowWhen(ProgramActionContext.class);
saveAction = new ActionBuilder("Save File", getName()) tool.addAction(new SaveProgramAction(this, SAVE_GROUP, subMenuGroup));
.menuPath(ToolConstants.MENU_FILE, "Save File") tool.addAction(new SaveAsProgramAction(this, SAVE_GROUP, subMenuGroup));
.description("Save Program")
.menuGroup(SAVE_GROUP, Integer.toString(subMenuGroupOrder++))
.menuIcon(null)
.toolBarIcon("images/disk.png")
.toolBarGroup(ToolConstants.TOOLBAR_GROUP_ONE)
.keyBinding("ctrl S")
.withContext(ProgramActionContext.class)
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
.supportsDefaultToolContext(true)
.enabledWhen(c -> c.getProgram() != null && c.getProgram().isChanged())
.onAction(c -> programSaveMgr.saveProgram(c.getProgram()))
.buildAndInstall(tool);
saveAsAction = new ActionBuilder("Save As File", getName()) DockingAction saveAllAction = new ActionBuilder("Save All Files", getName())
.menuPath(ToolConstants.MENU_FILE, "Save &As...")
.menuGroup(SAVE_GROUP, Integer.toString(subMenuGroupOrder++))
.withContext(ProgramActionContext.class)
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
.supportsDefaultToolContext(true)
.onAction(c -> programSaveMgr.saveAs(c.getProgram()))
.buildAndInstall(tool);
saveAllAction = new ActionBuilder("Save All Files", getName())
.menuPath(ToolConstants.MENU_FILE, "Save All") .menuPath(ToolConstants.MENU_FILE, "Save All")
.description("Save All Programs") .description("Save All Programs")
.menuGroup(SAVE_GROUP, Integer.toString(subMenuGroupOrder++)) .menuGroup(SAVE_GROUP, Integer.toString(subMenuGroup++))
.withContext(ProgramActionContext.class) .enabledWhen(c -> programMgr.hasUnsavedPrograms())
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
.onAction(c -> programSaveMgr.saveChangedPrograms()) .onAction(c -> programSaveMgr.saveChangedPrograms())
.buildAndInstall(tool); .buildAndInstall(tool);
saveAllAction.addToWindowWhen(ProgramActionContext.class);
optionsAction = new ActionBuilder("Program Options", getName()) tool.addAction(new ProgramOptionsAction(this));
.menuPath(ToolConstants.MENU_EDIT, "P&rogram Options...")
.description("Edit Options for current program")
.menuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP,
ToolConstants.TOOL_OPTIONS_MENU_GROUP + "b")
.withContext(ProgramActionContext.class)
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
.supportsDefaultToolContext(true)
.onAction(c -> showProgramOptions(c.getProgram()))
.buildAndInstall(tool);
undoAction = new UndoAction(tool, getName()); undoAction = new UndoAction(this, tool);
redoAction = new RedoAction(tool, getName()); redoAction = new RedoAction(this, tool);
tool.addAction(undoAction); tool.addAction(undoAction);
tool.addAction(redoAction); tool.addAction(redoAction);
} }
private void showProgramOptions(final Program currentProgram) { void showProgramOptions(final Program currentProgram) {
List<String> names = currentProgram.getOptionsNames(); List<String> names = currentProgram.getOptionsNames();
Options[] options = new Options[names.size()]; Options[] options = new Options[names.size()];
for (int i = 0; i < names.size(); i++) { for (int i = 0; i < names.size(); i++) {
@@ -664,64 +613,14 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
} }
} }
private void updateActions() { void undoStackChanged() {
Program p = programMgr.getCurrentProgram(); undoAction.updateActionMenuName();
updateCloseAction(p); redoAction.updateActionMenuName();
updateProgramOptionsAction(p);
updateProgramActions();
closeAllAction.setEnabled(p != null);
optionsAction.setEnabled(p != null);
Program[] programList = programMgr.getAllPrograms();
closeOthersAction.setEnabled(programList.length > 1);
tool.contextChanged(null); tool.contextChanged(null);
} }
private void updateSaveAction(Program p) { void contextChanged() {
if (p == null) { tool.contextChanged(null);
saveAction.getMenuBarData().setMenuItemName("&Save");
saveAction.setDescription("Save Program");
saveAction.setEnabled(false);
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
saveAction.getMenuBarData().setMenuItemName("&Save " + programName);
saveAction.setDescription("Save " + programName);
saveAction.setEnabled(p.isChanged());
}
}
private void updateSaveAsAction(Program p) {
if (p == null) {
saveAsAction.getMenuBarData().setMenuItemName("Save &As...");
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
saveAsAction.getMenuBarData().setMenuItemName("Save " + programName + " &As...");
}
}
private void updateProgramOptionsAction(Program p) {
if (p == null) {
optionsAction.getMenuBarData().setMenuItemName("Program Options");
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
optionsAction.getMenuBarData().setMenuItemName("Options for " + programName + "...");
}
optionsAction.setEnabled(p != null);
}
private void updateCloseAction(Program p) {
if (p == null) {
closeAction.getMenuBarData().setMenuItemName("&Close");
closeAction.setDescription("Close Program");
}
else {
String programName = "'" + p.getDomainFile().getName() + "'";
closeAction.getMenuBarData().setMenuItemName("&Close " + programName);
closeAction.setDescription("<html>Close " + HTMLUtilities.escapeHTML(programName));
}
closeAction.setEnabled(p != null);
} }
private void open() { private void open() {
@@ -746,7 +645,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
openDialog.addOkActionListener(listener); openDialog.addOkActionListener(listener);
} }
tool.showDialog(openDialog); tool.showDialog(openDialog);
updateActions(); contextChanged();
} }
public void openPrograms(List<DomainFile> filesToOpen) { public void openPrograms(List<DomainFile> filesToOpen) {
@@ -792,27 +691,6 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
return openProgram; return openProgram;
} }
void updateProgramActions() {
updateSaveAllAction();
Program p = getCurrentProgram();
updateSaveAction(getCurrentProgram());
updateSaveAsAction(getCurrentProgram());
undoAction.update(p);
redoAction.update(p);
}
private void updateSaveAllAction() {
boolean saveAllEnable = false;
Program[] programList = programMgr.getAllPrograms();
for (Program element : programList) {
if (element.isChanged()) {
saveAllEnable = true;
break;
}
}
saveAllAction.setEnabled(saveAllEnable);
}
/** /**
* Write out my data state. * Write out my data state.
*/ */
@@ -870,7 +748,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
programMgr.setCurrentProgram(programs[0]); programMgr.setCurrentProgram(programs[0]);
} }
} }
updateActions(); contextChanged();
} }
@Override @Override
@@ -1162,7 +1040,19 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
@Override @Override
public void lockDown(boolean state) { public void lockDown(boolean state) {
locked = state; locked = state;
openAction.setEnabled(!state); contextChanged();
}
public boolean isManaged(Program program) {
return programMgr.contains(program);
}
public void saveProgram(Program program) {
programSaveMgr.saveProgram(program);
}
public void saveProgramAs(Program program) {
programSaveMgr.saveAs(program);
} }
} }
@@ -0,0 +1,52 @@
/* ###
* 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.app.plugin.core.progmgr;
import docking.action.MenuData;
import docking.tool.ToolConstants;
import ghidra.program.model.listing.Program;
/**
* Action class for the "Edit Program Options" action
*/
public class ProgramOptionsAction extends AbstractProgramNameSwitchingAction {
public ProgramOptionsAction(ProgramManagerPlugin plugin) {
super(plugin, "Program Options", true);
MenuData menuData =
new MenuData(new String[] { ToolConstants.MENU_EDIT, "P&rogram Options..." });
menuData.setMenuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP);
menuData.setMenuSubGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP + "b");
setMenuBarData(menuData);
}
@Override
protected void programChanged(Program program) {
if (program == null) {
getMenuBarData().setMenuItemName("Program Options");
}
else {
String programName = "'" + program.getDomainFile().getName() + "'";
getMenuBarData().setMenuItemName("Options for " + programName + "...");
}
}
@Override
public void actionPerformed(Program program) {
plugin.showProgramOptions(program);
}
}
@@ -21,8 +21,6 @@ import javax.swing.Icon;
import docking.action.*; import docking.action.*;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.app.services.NavigationHistoryService; import ghidra.app.services.NavigationHistoryService;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@@ -30,11 +28,14 @@ import ghidra.program.model.listing.Program;
import ghidra.util.*; import ghidra.util.*;
import resources.ResourceManager; import resources.ResourceManager;
public class RedoAction extends ProgramContextAction { /**
* Action class for the "redo" action
*/
public class RedoAction extends AbstractProgramNameSwitchingAction {
private final PluginTool tool; private final PluginTool tool;
public RedoAction(PluginTool tool, String owner) { public RedoAction(ProgramManagerPlugin plugin, PluginTool tool) {
super("Redo", owner); super(plugin, "Redo", true);
this.tool = tool; this.tool = tool;
setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Redo")); setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Redo"));
String[] menuPath = { ToolConstants.MENU_EDIT, "&Redo" }; String[] menuPath = { ToolConstants.MENU_EDIT, "&Redo" };
@@ -46,15 +47,10 @@ public class RedoAction extends ProgramContextAction {
setToolBarData(new ToolBarData(icon, group)); setToolBarData(new ToolBarData(icon, group));
setKeyBindingData(new KeyBindingData("ctrl shift Z")); setKeyBindingData(new KeyBindingData("ctrl shift Z"));
setDescription("Redo"); setDescription("Redo");
setSupportsDefaultToolContext(true);
// we want this action to appear in all windows that can produce a program context
addToWindowWhen(ProgramActionContext.class);
} }
@Override @Override
protected void actionPerformed(ProgramActionContext programContext) { protected void actionPerformed(Program program) {
Program program = programContext.getProgram();
try { try {
saveCurrentLocationToHistory(); saveCurrentLocationToHistory();
program.redo(); program.redo();
@@ -64,41 +60,30 @@ public class RedoAction extends ProgramContextAction {
} }
} }
/** void updateActionMenuName() {
* updates the menu name of the action as the undo stack changes updateActionMenuName(lastContextProgram);
* <P> }
* NOTE: currently, we must manage the enablement explicitly
* because contextChanged is not called for data changes. Ideally, the enablement
* would be handled by the context, but for now it doesn't work
*
* @param program the program
*/
public void update(Program program) {
if (program == null) { void updateActionMenuName(Program program) {
getMenuBarData().setMenuItemName("Redo "); String actionName = "Redo " + (program == null ? "" : program.getDomainFile().getName());
setDescription(""); String description = actionName;
setEnabled(false);
} if (program != null && program.canRedo()) {
else if (program.canRedo()) { description = HTMLUtilities
String programName = program.getDomainFile().getName(); .toWrappedHTML("Redo " + HTMLUtilities.escapeHTML(program.getRedoName()));
getMenuBarData().setMenuItemName("Redo " + programName);
String tip = HTMLUtilities.toWrappedHTML(
"Redo " + HTMLUtilities.escapeHTML(program.getRedoName()));
setDescription(tip);
setEnabled(true);
}
else {
setDescription("Redo");
setEnabled(false);
} }
getMenuBarData().setMenuItemName(actionName);
setDescription(description);
}
protected void programChanged(Program program) {
updateActionMenuName(program);
} }
@Override @Override
protected boolean isEnabledForContext(ProgramActionContext context) { protected boolean isEnabledForContext(Program program) {
Program program = context.getProgram(); return program != null && program.canRedo();
return program.canRedo();
} }
private void saveCurrentLocationToHistory() { private void saveCurrentLocationToHistory() {
@@ -108,5 +93,4 @@ public class RedoAction extends ProgramContextAction {
historyService.addNewLocation(goToService.getDefaultNavigatable()); historyService.addNewLocation(goToService.getDefaultNavigatable());
} }
} }
} }
@@ -0,0 +1,51 @@
/* ###
* 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.app.plugin.core.progmgr;
import docking.action.MenuData;
import docking.tool.ToolConstants;
import ghidra.program.model.listing.Program;
/**
* Action class for the "Save As" action
*/
public class SaveAsProgramAction extends AbstractProgramNameSwitchingAction {
public SaveAsProgramAction(ProgramManagerPlugin plugin, String group, int subGroup) {
super(plugin, "Save As File", true);
MenuData menuData = new MenuData(new String[] { ToolConstants.MENU_FILE, "Save &As..." });
menuData.setMenuGroup(group);
menuData.setMenuSubGroup(Integer.toString(subGroup));
setMenuBarData(menuData);
}
@Override
protected void programChanged(Program program) {
if (program == null) {
getMenuBarData().setMenuItemName("Save &As...");
}
else {
String programName = "'" + program.getDomainFile().getName() + "'";
getMenuBarData().setMenuItemName("Save " + programName + " &As...");
}
}
@Override
public void actionPerformed(Program program) {
plugin.saveProgramAs(program);
}
}
@@ -0,0 +1,64 @@
/* ###
* 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.app.plugin.core.progmgr;
import javax.swing.ImageIcon;
import docking.action.*;
import docking.tool.ToolConstants;
import ghidra.program.model.listing.Program;
import resources.ResourceManager;
/**
* Action class for the "Save Program" action
*/
public class SaveProgramAction extends AbstractProgramNameSwitchingAction {
public SaveProgramAction(ProgramManagerPlugin plugin, String group, int subGroup) {
super(plugin, "Save File", true);
MenuData menuData = new MenuData(new String[] { ToolConstants.MENU_FILE, "Save File" });
menuData.setMenuGroup(group);
menuData.setMenuSubGroup(Integer.toString(subGroup));
setMenuBarData(menuData);
ImageIcon icon = ResourceManager.loadImage("images/disk.png");
setToolBarData(new ToolBarData(icon, ToolConstants.TOOLBAR_GROUP_ONE));
setKeyBindingData(new KeyBindingData("ctrl S"));
}
@Override
protected void programChanged(Program program) {
if (program == null) {
getMenuBarData().setMenuItemName("&Save");
setDescription("Save Program");
}
else {
String programName = "'" + program.getDomainFile().getName() + "'";
getMenuBarData().setMenuItemName("&Save " + programName);
setDescription("Save " + programName);
}
}
@Override
public boolean isEnabledForContext(Program program) {
return program != null && program.isChanged();
}
@Override
public void actionPerformed(Program program) {
plugin.saveProgram(program);
}
}
@@ -21,8 +21,6 @@ import javax.swing.Icon;
import docking.action.*; import docking.action.*;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.app.services.NavigationHistoryService; import ghidra.app.services.NavigationHistoryService;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@@ -30,11 +28,14 @@ import ghidra.program.model.listing.Program;
import ghidra.util.*; import ghidra.util.*;
import resources.ResourceManager; import resources.ResourceManager;
public class UndoAction extends ProgramContextAction { /**
* Action class for the "Undo" action
*/
public class UndoAction extends AbstractProgramNameSwitchingAction {
private final PluginTool tool; private final PluginTool tool;
public UndoAction(PluginTool tool, String owner) { public UndoAction(ProgramManagerPlugin plugin, PluginTool tool) {
super("Undo", owner); super(plugin, "Undo", true);
this.tool = tool; this.tool = tool;
setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Undo")); setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Undo"));
String[] menuPath = { ToolConstants.MENU_EDIT, "&Undo" }; String[] menuPath = { ToolConstants.MENU_EDIT, "&Undo" };
@@ -45,15 +46,10 @@ public class UndoAction extends ProgramContextAction {
setToolBarData(new ToolBarData(icon, "Undo")); setToolBarData(new ToolBarData(icon, "Undo"));
setDescription("Undo"); setDescription("Undo");
setKeyBindingData(new KeyBindingData("ctrl Z")); setKeyBindingData(new KeyBindingData("ctrl Z"));
setSupportsDefaultToolContext(true);
// we want this action to appear in all windows that can produce a program context
addToWindowWhen(ProgramActionContext.class);
} }
@Override @Override
protected void actionPerformed(ProgramActionContext programContext) { protected void actionPerformed(Program program) {
Program program = programContext.getProgram();
try { try {
saveCurrentLocationToHistory(); saveCurrentLocationToHistory();
program.undo(); program.undo();
@@ -71,39 +67,30 @@ public class UndoAction extends ProgramContextAction {
} }
} }
/** @Override
* updates the menu name of the action as the undo stack changes protected void programChanged(Program program) {
* <P> updateActionMenuName(program);
* NOTE: currently, we must manage the enablement explicitly }
* because contextChanged is not called for data changes. Ideally, the enablement
* would be handled by the context, but for now it doesn't work void updateActionMenuName() {
* updateActionMenuName(lastContextProgram);
* @param program the program }
*/
public void update(Program program) { void updateActionMenuName(Program program) {
String actionName = "Undo " + (program == null ? "" : program.getDomainFile().getName());
String description = actionName;
if (program == null) {
getMenuBarData().setMenuItemName("Undo ");
setDescription("");
setEnabled(false);
}
if (program != null && program.canUndo()) { if (program != null && program.canUndo()) {
String programName = program.getDomainFile().getName(); description = HTMLUtilities
getMenuBarData().setMenuItemName("Undo " + programName); .toWrappedHTML("Undo " + HTMLUtilities.escapeHTML(program.getUndoName()));
String tip = HTMLUtilities.toWrappedHTML(
"Undo " + HTMLUtilities.escapeHTML(program.getUndoName()));
setDescription(tip);
setEnabled(true);
}
else {
setDescription("Undo");
setEnabled(false);
} }
getMenuBarData().setMenuItemName(actionName);
setDescription(description);
} }
@Override @Override
protected boolean isEnabledForContext(ProgramActionContext context) { protected boolean isEnabledForContext(Program program) {
Program program = context.getProgram(); return program != null && program.canUndo();
return program.canUndo();
} }
} }