mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-508 fixing program actions enablement with regard to debugger program context not working
This commit is contained in:
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) {
|
||||
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;
|
||||
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 {
|
||||
|
||||
private static final String SAVE_GROUP = "DomainObjectSave";
|
||||
private static final String OPEN_GROUP = "DomainObjectOpen";
|
||||
static final String OPEN_GROUP = "DomainObjectOpen";
|
||||
private MultiProgramManager programMgr;
|
||||
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 OpenVersionedFileDialog openDialog;
|
||||
private boolean locked = false;
|
||||
|
@ -174,11 +166,11 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
Program openProgram = programMgr.getOpenProgram(ghidraURL);
|
||||
if (openProgram != null) {
|
||||
programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState);
|
||||
updateActions();
|
||||
if (openState == ProgramManager.OPEN_CURRENT) {
|
||||
gotoProgramRef(openProgram, ghidraURL.getRef());
|
||||
programMgr.saveLocation();
|
||||
}
|
||||
contextChanged();
|
||||
return openProgram;
|
||||
}
|
||||
|
||||
|
@ -215,7 +207,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
}
|
||||
|
||||
programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState);
|
||||
updateActions();
|
||||
contextChanged();
|
||||
openProgram.release(this);
|
||||
if (openState == ProgramManager.OPEN_CURRENT) {
|
||||
gotoProgramRef(openProgram, ghidraURL.getRef());
|
||||
|
@ -310,7 +302,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
|
||||
Program program = Swing.runNow(() -> {
|
||||
Program p = doOpenProgram(domainFile, version, state);
|
||||
updateActions();
|
||||
contextChanged();
|
||||
return p;
|
||||
});
|
||||
|
||||
|
@ -347,16 +339,16 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean closeOtherPrograms(final boolean ignoreChanges) {
|
||||
final Program[] otherPrograms = programMgr.getOtherPrograms();
|
||||
public boolean closeOtherPrograms(boolean ignoreChanges) {
|
||||
Program[] otherPrograms = programMgr.getOtherPrograms();
|
||||
Runnable r = () -> doCloseAllPrograms(otherPrograms, ignoreChanges);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
return programMgr.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeAllPrograms(final boolean ignoreChanges) {
|
||||
final Program[] openPrograms = programMgr.getAllPrograms();
|
||||
public boolean closeAllPrograms(boolean ignoreChanges) {
|
||||
Program[] openPrograms = programMgr.getAllPrograms();
|
||||
Runnable r = () -> doCloseAllPrograms(openPrograms, ignoreChanges);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
return programMgr.isEmpty();
|
||||
|
@ -396,11 +388,11 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
for (Program program : toRemove) {
|
||||
programMgr.removeProgram(program);
|
||||
}
|
||||
updateActions();
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeProgram(final Program program, final boolean ignoreChanges) {
|
||||
public boolean closeProgram(Program program, boolean ignoreChanges) {
|
||||
if (program == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -411,7 +403,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
if (ignoreChanges || program.isClosed() || programMgr.isPersistent(program) ||
|
||||
(tool.canCloseDomainObject(program) && programSaveMgr.canClose(program))) {
|
||||
programMgr.removeProgram(program);
|
||||
updateActions();
|
||||
contextChanged();
|
||||
}
|
||||
};
|
||||
SystemUtilities.runSwingNow(r);
|
||||
|
@ -434,16 +426,16 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
if (currentProgram != null) {
|
||||
programMgr.removeProgram(currentProgram);
|
||||
}
|
||||
updateActions();
|
||||
contextChanged();
|
||||
tool.setSubTitle("");
|
||||
tool.clearLastEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentProgram(final Program p) {
|
||||
public void setCurrentProgram(Program p) {
|
||||
Runnable r = () -> {
|
||||
programMgr.setCurrentProgram(p);
|
||||
updateActions();
|
||||
contextChanged();
|
||||
};
|
||||
SystemUtilities.runSwingNow(r);
|
||||
}
|
||||
|
@ -483,7 +475,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
if (state == ProgramManager.OPEN_CURRENT) {
|
||||
programMgr.saveLocation();
|
||||
}
|
||||
updateActions();
|
||||
contextChanged();
|
||||
};
|
||||
SystemUtilities.runSwingNow(r);
|
||||
}
|
||||
|
@ -496,7 +488,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
@Override
|
||||
protected boolean saveData() {
|
||||
boolean result = programSaveMgr.canCloseAll();
|
||||
updateActions();
|
||||
contextChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -513,100 +505,57 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
|
||||
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...")
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroup++))
|
||||
.keyBinding("ctrl O")
|
||||
.enabledWhen(c -> !locked)
|
||||
.onAction(c -> open())
|
||||
.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);
|
||||
|
||||
closeAction = new ActionBuilder("Close File", getName())
|
||||
.menuPath(ToolConstants.MENU_FILE, "&Close")
|
||||
.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())
|
||||
tool.addAction(new CloseProgramAction(this, OPEN_GROUP, subMenuGroup++));
|
||||
new ActionBuilder("Close Others", getName())
|
||||
.menuPath(ToolConstants.MENU_FILE, "Close &Others")
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroup++))
|
||||
.enabled(false)
|
||||
.withContext(ProgramActionContext.class)
|
||||
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
|
||||
.enabledWhen(c -> programMgr.hasProgram(c.getProgram()))
|
||||
.onAction(c -> closeOtherPrograms(false))
|
||||
.buildAndInstall(tool);
|
||||
|
||||
closeAllAction = new ActionBuilder("Close All", getName())
|
||||
DockingAction closeAllAction = new ActionBuilder("Close All", getName())
|
||||
.menuPath(ToolConstants.MENU_FILE, "Close &All")
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
|
||||
.withContext(ProgramActionContext.class)
|
||||
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroup++))
|
||||
.enabledWhen(c -> !programMgr.isEmpty())
|
||||
.onAction(c -> closeAllPrograms(false))
|
||||
.enabled(false)
|
||||
.buildAndInstall(tool);
|
||||
closeAllAction.addToWindowWhen(ProgramActionContext.class);
|
||||
|
||||
saveAction = new ActionBuilder("Save File", getName())
|
||||
.menuPath(ToolConstants.MENU_FILE, "Save File")
|
||||
.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);
|
||||
tool.addAction(new SaveProgramAction(this, SAVE_GROUP, subMenuGroup));
|
||||
tool.addAction(new SaveAsProgramAction(this, SAVE_GROUP, subMenuGroup));
|
||||
|
||||
saveAsAction = new ActionBuilder("Save As File", 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())
|
||||
DockingAction saveAllAction = new ActionBuilder("Save All Files", getName())
|
||||
.menuPath(ToolConstants.MENU_FILE, "Save All")
|
||||
.description("Save All Programs")
|
||||
.menuGroup(SAVE_GROUP, Integer.toString(subMenuGroupOrder++))
|
||||
.withContext(ProgramActionContext.class)
|
||||
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
|
||||
.menuGroup(SAVE_GROUP, Integer.toString(subMenuGroup++))
|
||||
.enabledWhen(c -> programMgr.hasUnsavedPrograms())
|
||||
.onAction(c -> programSaveMgr.saveChangedPrograms())
|
||||
.buildAndInstall(tool);
|
||||
saveAllAction.addToWindowWhen(ProgramActionContext.class);
|
||||
|
||||
optionsAction = new ActionBuilder("Program Options", getName())
|
||||
.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);
|
||||
tool.addAction(new ProgramOptionsAction(this));
|
||||
|
||||
undoAction = new UndoAction(tool, getName());
|
||||
redoAction = new RedoAction(tool, getName());
|
||||
undoAction = new UndoAction(this, tool);
|
||||
redoAction = new RedoAction(this, tool);
|
||||
tool.addAction(undoAction);
|
||||
tool.addAction(redoAction);
|
||||
}
|
||||
|
||||
private void showProgramOptions(final Program currentProgram) {
|
||||
void showProgramOptions(final Program currentProgram) {
|
||||
List<String> names = currentProgram.getOptionsNames();
|
||||
Options[] options = new Options[names.size()];
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
|
@ -664,64 +613,14 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void updateActions() {
|
||||
Program p = programMgr.getCurrentProgram();
|
||||
updateCloseAction(p);
|
||||
updateProgramOptionsAction(p);
|
||||
updateProgramActions();
|
||||
closeAllAction.setEnabled(p != null);
|
||||
optionsAction.setEnabled(p != null);
|
||||
Program[] programList = programMgr.getAllPrograms();
|
||||
closeOthersAction.setEnabled(programList.length > 1);
|
||||
void undoStackChanged() {
|
||||
undoAction.updateActionMenuName();
|
||||
redoAction.updateActionMenuName();
|
||||
tool.contextChanged(null);
|
||||
}
|
||||
|
||||
private void updateSaveAction(Program p) {
|
||||
if (p == 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);
|
||||
void contextChanged() {
|
||||
tool.contextChanged(null);
|
||||
}
|
||||
|
||||
private void open() {
|
||||
|
@ -746,7 +645,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
openDialog.addOkActionListener(listener);
|
||||
}
|
||||
tool.showDialog(openDialog);
|
||||
updateActions();
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
public void openPrograms(List<DomainFile> filesToOpen) {
|
||||
|
@ -792,27 +691,6 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
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.
|
||||
*/
|
||||
|
@ -870,7 +748,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
programMgr.setCurrentProgram(programs[0]);
|
||||
}
|
||||
}
|
||||
updateActions();
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1162,7 +1040,19 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||
@Override
|
||||
public void lockDown(boolean 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.tool.ToolConstants;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.context.ProgramContextAction;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.NavigationHistoryService;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -30,11 +28,14 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.util.*;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class RedoAction extends ProgramContextAction {
|
||||
/**
|
||||
* Action class for the "redo" action
|
||||
*/
|
||||
public class RedoAction extends AbstractProgramNameSwitchingAction {
|
||||
private final PluginTool tool;
|
||||
|
||||
public RedoAction(PluginTool tool, String owner) {
|
||||
super("Redo", owner);
|
||||
public RedoAction(ProgramManagerPlugin plugin, PluginTool tool) {
|
||||
super(plugin, "Redo", true);
|
||||
this.tool = tool;
|
||||
setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Redo"));
|
||||
String[] menuPath = { ToolConstants.MENU_EDIT, "&Redo" };
|
||||
|
@ -46,15 +47,10 @@ public class RedoAction extends ProgramContextAction {
|
|||
setToolBarData(new ToolBarData(icon, group));
|
||||
setKeyBindingData(new KeyBindingData("ctrl shift Z"));
|
||||
setDescription("Redo");
|
||||
setSupportsDefaultToolContext(true);
|
||||
|
||||
// we want this action to appear in all windows that can produce a program context
|
||||
addToWindowWhen(ProgramActionContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(ProgramActionContext programContext) {
|
||||
Program program = programContext.getProgram();
|
||||
protected void actionPerformed(Program program) {
|
||||
try {
|
||||
saveCurrentLocationToHistory();
|
||||
program.redo();
|
||||
|
@ -64,41 +60,30 @@ public class RedoAction extends ProgramContextAction {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the menu name of the action as the undo stack changes
|
||||
* <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) {
|
||||
void updateActionMenuName() {
|
||||
updateActionMenuName(lastContextProgram);
|
||||
}
|
||||
|
||||
if (program == null) {
|
||||
getMenuBarData().setMenuItemName("Redo ");
|
||||
setDescription("");
|
||||
setEnabled(false);
|
||||
}
|
||||
else if (program.canRedo()) {
|
||||
String programName = program.getDomainFile().getName();
|
||||
getMenuBarData().setMenuItemName("Redo " + programName);
|
||||
String tip = HTMLUtilities.toWrappedHTML(
|
||||
"Redo " + HTMLUtilities.escapeHTML(program.getRedoName()));
|
||||
setDescription(tip);
|
||||
setEnabled(true);
|
||||
}
|
||||
else {
|
||||
setDescription("Redo");
|
||||
setEnabled(false);
|
||||
void updateActionMenuName(Program program) {
|
||||
String actionName = "Redo " + (program == null ? "" : program.getDomainFile().getName());
|
||||
String description = actionName;
|
||||
|
||||
if (program != null && program.canRedo()) {
|
||||
description = HTMLUtilities
|
||||
.toWrappedHTML("Redo " + HTMLUtilities.escapeHTML(program.getRedoName()));
|
||||
}
|
||||
|
||||
getMenuBarData().setMenuItemName(actionName);
|
||||
setDescription(description);
|
||||
}
|
||||
|
||||
protected void programChanged(Program program) {
|
||||
updateActionMenuName(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForContext(ProgramActionContext context) {
|
||||
Program program = context.getProgram();
|
||||
return program.canRedo();
|
||||
protected boolean isEnabledForContext(Program program) {
|
||||
return program != null && program.canRedo();
|
||||
}
|
||||
|
||||
private void saveCurrentLocationToHistory() {
|
||||
|
@ -108,5 +93,4 @@ public class RedoAction extends ProgramContextAction {
|
|||
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.tool.ToolConstants;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.context.ProgramContextAction;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.NavigationHistoryService;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -30,11 +28,14 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.util.*;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class UndoAction extends ProgramContextAction {
|
||||
/**
|
||||
* Action class for the "Undo" action
|
||||
*/
|
||||
public class UndoAction extends AbstractProgramNameSwitchingAction {
|
||||
private final PluginTool tool;
|
||||
|
||||
public UndoAction(PluginTool tool, String owner) {
|
||||
super("Undo", owner);
|
||||
public UndoAction(ProgramManagerPlugin plugin, PluginTool tool) {
|
||||
super(plugin, "Undo", true);
|
||||
this.tool = tool;
|
||||
setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Undo"));
|
||||
String[] menuPath = { ToolConstants.MENU_EDIT, "&Undo" };
|
||||
|
@ -45,15 +46,10 @@ public class UndoAction extends ProgramContextAction {
|
|||
setToolBarData(new ToolBarData(icon, "Undo"));
|
||||
setDescription("Undo");
|
||||
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
|
||||
protected void actionPerformed(ProgramActionContext programContext) {
|
||||
Program program = programContext.getProgram();
|
||||
protected void actionPerformed(Program program) {
|
||||
try {
|
||||
saveCurrentLocationToHistory();
|
||||
program.undo();
|
||||
|
@ -71,39 +67,30 @@ public class UndoAction extends ProgramContextAction {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the menu name of the action as the undo stack changes
|
||||
* <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) {
|
||||
@Override
|
||||
protected void programChanged(Program program) {
|
||||
updateActionMenuName(program);
|
||||
}
|
||||
|
||||
void updateActionMenuName() {
|
||||
updateActionMenuName(lastContextProgram);
|
||||
}
|
||||
|
||||
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()) {
|
||||
String programName = program.getDomainFile().getName();
|
||||
getMenuBarData().setMenuItemName("Undo " + programName);
|
||||
String tip = HTMLUtilities.toWrappedHTML(
|
||||
"Undo " + HTMLUtilities.escapeHTML(program.getUndoName()));
|
||||
setDescription(tip);
|
||||
setEnabled(true);
|
||||
}
|
||||
else {
|
||||
setDescription("Undo");
|
||||
setEnabled(false);
|
||||
description = HTMLUtilities
|
||||
.toWrappedHTML("Undo " + HTMLUtilities.escapeHTML(program.getUndoName()));
|
||||
}
|
||||
|
||||
getMenuBarData().setMenuItemName(actionName);
|
||||
setDescription(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForContext(ProgramActionContext context) {
|
||||
Program program = context.getProgram();
|
||||
return program.canUndo();
|
||||
protected boolean isEnabledForContext(Program program) {
|
||||
return program != null && program.canUndo();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue