mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 05:32:14 +00:00
Merge remote-tracking branch 'origin/GP-3521_ghidragon_allow_user_to_see_and_pick_older_undos_or_redos--SQUASHED'
This commit is contained in:
commit
d98ae48110
@ -188,8 +188,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
private void bookmarkTypeAdded(TraceBookmarkType type) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_BOOKMARK_TYPE_ADDED,
|
||||
null, null, type, null, null));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_BOOKMARK_TYPE_ADDED, null,
|
||||
null, type, null, null));
|
||||
}
|
||||
|
||||
private void bookmarkAdded(TraceAddressSpace space, TraceBookmark bm) {
|
||||
@ -248,14 +248,14 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
private void categoryAdded(long id, Category oldIsNull, Category added) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_CATEGORY_ADDED, null,
|
||||
null, null, oldIsNull, added));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_CATEGORY_ADDED, null, null,
|
||||
null, oldIsNull, added));
|
||||
}
|
||||
|
||||
private void categoryMoved(long id, CategoryPath oldPath, CategoryPath newPath) {
|
||||
Category category = getDataTypeManager().getCategory(id);
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_CATEGORY_MOVED, null,
|
||||
null, null, oldPath, category));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_CATEGORY_MOVED, null, null,
|
||||
null, oldPath, category));
|
||||
}
|
||||
|
||||
private void categoryRenamed(long id, String oldName, String newName) {
|
||||
@ -315,8 +315,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
|
||||
protected void fireCodeRemoved(DomainObjectEventQueues queues, Address min, Address max,
|
||||
TraceCodeUnit removed) {
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_CODE_REMOVED,
|
||||
min, max, null, removed, null));
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_CODE_REMOVED, min, max,
|
||||
null, removed, null));
|
||||
}
|
||||
|
||||
private void codeFragmentChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||
@ -352,14 +352,13 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
private void commentChanged(int docrType, TraceAddressSpace space,
|
||||
TraceAddressSnapRange range,
|
||||
String oldValue, String newValue) {
|
||||
TraceAddressSnapRange range, String oldValue, String newValue) {
|
||||
DomainObjectEventQueues queues = isVisible(space, range);
|
||||
if (queues == null) {
|
||||
return;
|
||||
}
|
||||
queues.fireEvent(new ProgramChangeRecord(docrType,
|
||||
range.getX1(), range.getX2(), null, oldValue, newValue));
|
||||
queues.fireEvent(new ProgramChangeRecord(docrType, range.getX1(), range.getX2(), null,
|
||||
oldValue, newValue));
|
||||
}
|
||||
|
||||
private void commentEolChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||
@ -438,14 +437,14 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
private void dataTypeChanged(long id, DataType oldIsNull, DataType changed) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_CHANGED,
|
||||
null, null, null, oldIsNull, changed));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_CHANGED, null,
|
||||
null, null, oldIsNull, changed));
|
||||
}
|
||||
|
||||
private void dataTypeReplaced(long id, DataTypePath oldPath, DataTypePath newPath) {
|
||||
DataType newType = getDataTypeManager().getDataType(id);
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_REPLACED,
|
||||
null, null, null, newPath, newType));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_REPLACED, null,
|
||||
null, null, newPath, newType));
|
||||
}
|
||||
|
||||
private void dataTypeMoved(long id, DataTypePath oldPath, DataTypePath newPath) {
|
||||
@ -457,13 +456,13 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
|
||||
private void dataTypeRenamed(long id, String oldName, String newName) {
|
||||
DataType dataType = getDataTypeManager().getDataType(id);
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_RENAMED,
|
||||
null, null, null, oldName, dataType));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_RENAMED, null,
|
||||
null, null, oldName, dataType));
|
||||
}
|
||||
|
||||
private void dataTypeDeleted(long id, DataTypePath oldPath, DataTypePath newIsNull) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_REMOVED,
|
||||
null, null, null, oldPath, newIsNull));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_DATA_TYPE_REMOVED, null,
|
||||
null, null, oldPath, newIsNull));
|
||||
}
|
||||
|
||||
private void gatherThunksTo(Collection<TraceFunctionSymbol> into,
|
||||
@ -553,18 +552,18 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
private void functionTagAdded(FunctionTag tag) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_CREATED,
|
||||
null, null, tag, null, null));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_CREATED, null,
|
||||
null, tag, null, null));
|
||||
}
|
||||
|
||||
private void functionTagChanged(FunctionTag tag) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_CHANGED,
|
||||
null, null, tag, null, null));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_CHANGED, null,
|
||||
null, tag, null, null));
|
||||
}
|
||||
|
||||
private void functionTagDeleted(FunctionTag tag) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_DELETED,
|
||||
null, null, tag, null, null));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_DELETED, null,
|
||||
null, tag, null, null));
|
||||
}
|
||||
|
||||
private void instructionFlowOverrideChanged(TraceAddressSpace space,
|
||||
@ -629,19 +628,19 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
boolean inOld = isRegionVisible(region, oldSpan);
|
||||
boolean inNew = isRegionVisible(region, newSpan);
|
||||
if (inOld && !inNew) {
|
||||
eventQueues.fireEvent(
|
||||
new ProgramChangeRecord(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED,
|
||||
region.getMinAddress(), region.getMaxAddress(), null, null, null));
|
||||
eventQueues
|
||||
.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED,
|
||||
region.getMinAddress(), region.getMaxAddress(), null, null, null));
|
||||
// NOTE: MemoryMapDB does this, too. Otherwise, CodeBrowserPlugin does not hear.
|
||||
eventQueues.fireEvent(
|
||||
new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
|
||||
eventQueues
|
||||
.fireEvent(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
|
||||
}
|
||||
if (!inOld && inNew) {
|
||||
eventQueues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_MEMORY_BLOCK_ADDED,
|
||||
region.getMinAddress(), region.getMaxAddress(), null, null, null));
|
||||
// NOTE: MemoryMapDB does this, too. Otherwise, CodeBrowserPlugin does not hear.
|
||||
eventQueues.fireEvent(
|
||||
new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
|
||||
eventQueues
|
||||
.fireEvent(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,8 +658,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
private void sourceArchiveAdded(UniversalID id) {
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_SOURCE_ARCHIVE_ADDED,
|
||||
null, null, id, null, null));
|
||||
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_SOURCE_ARCHIVE_ADDED, null,
|
||||
null, id, null, null));
|
||||
}
|
||||
|
||||
private void sourceArchiveChanged(UniversalID id) {
|
||||
@ -764,9 +763,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
return;
|
||||
}
|
||||
// Strange. This is fired as if by the reference rather than the symbol
|
||||
queues.fireEvent(
|
||||
new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_ASSOCIATION_ADDED,
|
||||
newRef.getFromAddress(), newRef.getFromAddress(), newRef, null, symbol));
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_ASSOCIATION_ADDED,
|
||||
newRef.getFromAddress(), newRef.getFromAddress(), newRef, null, symbol));
|
||||
}
|
||||
|
||||
private void symbolAssociationRemoved(TraceAddressSpace space, TraceSymbol symbol,
|
||||
@ -803,9 +801,9 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
fireSymbolRemoved(queues, symbol);
|
||||
if (symbol instanceof TraceFunctionSymbol) {
|
||||
TraceFunctionSymbol function = (TraceFunctionSymbol) symbol;
|
||||
queues.fireEvent(new ProgramChangeRecord(
|
||||
ChangeManager.DOCR_FUNCTION_REMOVED, function.getEntryPoint(),
|
||||
function.getEntryPoint(), function, function.getBody(), null));
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_REMOVED,
|
||||
function.getEntryPoint(), function.getEntryPoint(), function,
|
||||
function.getBody(), null));
|
||||
}
|
||||
}
|
||||
if (!inOld && inNew) {
|
||||
@ -834,9 +832,9 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
protected void fireSymbolRemoved(DomainObjectEventQueues queues, TraceSymbol symbol) {
|
||||
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_REMOVED,
|
||||
symbol.getAddress(), symbol.getAddress(), symbol, symbol.getName(),
|
||||
symbol.getID()));
|
||||
queues.fireEvent(
|
||||
new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_REMOVED, symbol.getAddress(),
|
||||
symbol.getAddress(), symbol, symbol.getName(), symbol.getID()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,8 +915,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
this.viewport = trace.createTimeViewport();
|
||||
this.viewport.setSnap(snap);
|
||||
|
||||
this.eventQueues =
|
||||
new DomainObjectEventQueues(this, TIME_INTERVAL, trace.getLock());
|
||||
this.eventQueues = new DomainObjectEventQueues(this, TIME_INTERVAL, trace.getLock());
|
||||
|
||||
this.regViewsByThread = new WeakValueHashMap<>();
|
||||
|
||||
@ -1574,6 +1571,16 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
return trace.getRedoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllUndoNames() {
|
||||
return trace.getAllUndoNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllRedoNames() {
|
||||
return trace.getAllRedoNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransactionListener(TransactionListener listener) {
|
||||
trace.addTransactionListener(listener);
|
||||
@ -1605,8 +1612,8 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
memory.updateChangeRegionBlockFlags(region);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeRegionBlockRange(TraceMemoryRegion region,
|
||||
AddressRange oldRange, AddressRange newRange) {
|
||||
public void updateMemoryChangeRegionBlockRange(TraceMemoryRegion region, AddressRange oldRange,
|
||||
AddressRange newRange) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
@ -1706,8 +1713,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
return RangeQueryOcclusion.super.occluded(cu, range, span);
|
||||
}
|
||||
byte[] memBytes = new byte[cu.getLength()];
|
||||
memSpace.getBytes(span.lmax(), cu.getMinAddress(),
|
||||
ByteBuffer.wrap(memBytes));
|
||||
memSpace.getBytes(span.lmax(), cu.getMinAddress(), ByteBuffer.wrap(memBytes));
|
||||
byte[] cuBytes;
|
||||
try {
|
||||
cuBytes = cu.getBytes();
|
||||
@ -1725,8 +1731,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
|
||||
@Override
|
||||
public Iterable<? extends TraceCodeUnit> query(AddressRange range, Lifespan span) {
|
||||
return definedUnits == null
|
||||
? Collections.emptyList()
|
||||
return definedUnits == null ? Collections.emptyList()
|
||||
: definedUnits.get(span.lmax(), range, true);
|
||||
}
|
||||
|
||||
@ -1779,8 +1784,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
public Iterable<? extends TraceFunctionSymbol> query(AddressRange range,
|
||||
Lifespan span) {
|
||||
// NB. No functions in register space!
|
||||
return functions.getIntersecting(Lifespan.at(span.lmax()), null, range,
|
||||
false);
|
||||
return functions.getIntersecting(Lifespan.at(span.lmax()), null, range, false);
|
||||
}
|
||||
|
||||
public boolean itemOccludes(AddressRange range, TraceFunctionSymbol f) {
|
||||
@ -1796,8 +1800,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
|
||||
protected boolean isFunctionVisible(TraceFunctionSymbol function, Lifespan lifespan) {
|
||||
AddressSetView body = function.getBody();
|
||||
AddressRange bodySpan =
|
||||
new AddressRangeImpl(body.getMinAddress(), body.getMaxAddress());
|
||||
AddressRange bodySpan = new AddressRangeImpl(body.getMinAddress(), body.getMaxAddress());
|
||||
return viewport.isCompletelyVisible(bodySpan, function.getLifespan(), function,
|
||||
getFunctionOcclusion(function));
|
||||
}
|
||||
@ -1823,8 +1826,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected DomainObjectEventQueues isSymbolVisible(TraceAddressSpace space,
|
||||
TraceSymbol symbol) {
|
||||
protected DomainObjectEventQueues isSymbolVisible(TraceAddressSpace space, TraceSymbol symbol) {
|
||||
// NB. Most symbols do not occlude each other
|
||||
DomainObjectEventQueues queues = getEventQueues(space);
|
||||
if (queues == null) {
|
||||
@ -1861,8 +1863,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
protected Occlusion<TraceMemoryRegion> regionOcclusion = new RangeQueryOcclusion<>() {
|
||||
@Override
|
||||
public Iterable<? extends TraceMemoryRegion> query(AddressRange range, Lifespan span) {
|
||||
return trace.getMemoryManager()
|
||||
.getRegionsIntersecting(Lifespan.at(span.lmax()), range);
|
||||
return trace.getMemoryManager().getRegionsIntersecting(Lifespan.at(span.lmax()), range);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1876,7 +1877,6 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
|
||||
protected boolean isRegionVisible(TraceMemoryRegion reg, Lifespan lifespan) {
|
||||
return viewport.isCompletelyVisible(reg.getRange(), lifespan, reg,
|
||||
regionOcclusion);
|
||||
return viewport.isCompletelyVisible(reg.getRange(), lifespan, reg, regionOcclusion);
|
||||
}
|
||||
}
|
||||
|
@ -613,6 +613,16 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
|
||||
return view.getRedoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllUndoNames() {
|
||||
return view.getAllUndoNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllRedoNames() {
|
||||
return view.getAllRedoNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransactionListener(TransactionListener listener) {
|
||||
view.addTransactionListener(listener);
|
||||
|
@ -36,6 +36,11 @@
|
||||
hovering the mouse over the <IMG src="icon.redo">button will display the name of the edit
|
||||
operation that would be "redone".</P>
|
||||
|
||||
<P><IMG src="help/shared/tip.png"> Also, pressing the drop-down arrow will show a list
|
||||
of all the undo/redo's that are available. Clicking on one of those will undo/redo that
|
||||
item and all the ones above it.</P>
|
||||
|
||||
|
||||
<P> </P>
|
||||
|
||||
<P> </P>
|
||||
|
@ -16,11 +16,14 @@
|
||||
package ghidra.app.plugin.core.progmgr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.menu.MultiActionDockingAction;
|
||||
import docking.tool.ToolConstants;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
@ -37,11 +40,12 @@ import ghidra.util.*;
|
||||
* Abstract base class for the undo and redo actions. These actions add a listener to the
|
||||
* current context program in order to know when to update their enabled state and description.
|
||||
*/
|
||||
public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
public abstract class AbstractUndoRedoAction extends MultiActionDockingAction {
|
||||
private PluginTool tool;
|
||||
private Program lastProgram;
|
||||
private Program activeProgram;
|
||||
private ProgramManagerPlugin plugin;
|
||||
private TransactionListener transactionListener;
|
||||
private HelpLocation helpLocation;
|
||||
|
||||
public AbstractUndoRedoAction(PluginTool tool, ProgramManagerPlugin plugin, String name,
|
||||
String iconId, String keyBinding, String subGroup) {
|
||||
@ -52,6 +56,8 @@ public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
|
||||
String[] menuPath = { ToolConstants.MENU_EDIT, "&" + name };
|
||||
Icon icon = new GIcon(iconId);
|
||||
helpLocation = new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, name);
|
||||
|
||||
String group = "Undo";
|
||||
|
||||
MenuData menuData = new MenuData(menuPath, icon, group);
|
||||
@ -60,7 +66,7 @@ public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
|
||||
setToolBarData(new ToolBarData(icon, group));
|
||||
setKeyBindingData(new KeyBindingData(keyBinding));
|
||||
setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, name));
|
||||
setHelpLocation(helpLocation);
|
||||
setDescription(name);
|
||||
|
||||
addToWindowWhen(ProgramActionContext.class);
|
||||
@ -68,7 +74,7 @@ public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
transactionListener = new ContextProgramTransactionListener();
|
||||
}
|
||||
|
||||
protected abstract void actionPerformed(Program program) throws IOException;
|
||||
protected abstract void doAction(Program program, int repeatCount) throws IOException;
|
||||
|
||||
protected abstract boolean canPerformAction(Program program);
|
||||
|
||||
@ -82,17 +88,21 @@ public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
// here to keep track of the current program context and add a listener
|
||||
// so that the action's name, description, and enablement is properly updated as the
|
||||
// user makes changes to the program.
|
||||
if (program != lastProgram) {
|
||||
removeTransactionListener(lastProgram);
|
||||
lastProgram = program;
|
||||
addTransactionListener(lastProgram);
|
||||
if (program != activeProgram) {
|
||||
removeTransactionListener(activeProgram);
|
||||
activeProgram = program;
|
||||
addTransactionListener(activeProgram);
|
||||
updateActionNameAndDescription();
|
||||
}
|
||||
return canPerformAction(lastProgram);
|
||||
return canPerformAction(activeProgram);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
executeAction(context, 1);
|
||||
}
|
||||
|
||||
private void executeAction(ActionContext context, int repeatCount) {
|
||||
Program program = getProgram(context);
|
||||
if (program == null) {
|
||||
return;
|
||||
@ -101,11 +111,30 @@ public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
saveCurrentLocationToHistory();
|
||||
|
||||
try {
|
||||
actionPerformed(program);
|
||||
doAction(program, repeatCount);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, null, null, e);
|
||||
Msg.showError(this, null, getName() + " Error",
|
||||
"Error occured while attempting " + getName() + "!", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract List<String> getDescriptions(Program program);
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getActionList(ActionContext context) {
|
||||
Program program = getProgram(context);
|
||||
List<String> descriptions = getDescriptions(program);
|
||||
List<DockingActionIf> actions = new ArrayList<>();
|
||||
|
||||
int repeatCount = 1;
|
||||
for (String string : descriptions) {
|
||||
actions.add(new RepeatedAction(string, repeatCount));
|
||||
repeatCount++;
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private Program getProgram(ActionContext context) {
|
||||
@ -129,24 +158,28 @@ public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
|
||||
private void updateAction() {
|
||||
updateActionNameAndDescription();
|
||||
setEnabled(canPerformAction(lastProgram));
|
||||
setEnabled(canPerformAction(activeProgram));
|
||||
}
|
||||
|
||||
private void updateActionNameAndDescription() {
|
||||
String actionName = getName();
|
||||
String description = actionName;
|
||||
String menuName = actionName;
|
||||
|
||||
if (lastProgram != null) {
|
||||
actionName += " " + lastProgram.getDomainFile().getName();
|
||||
if (activeProgram != null) {
|
||||
menuName = actionName + " " + activeProgram.getDomainFile().getName();
|
||||
description = actionName;
|
||||
}
|
||||
|
||||
if (canPerformAction(lastProgram)) {
|
||||
description = HTMLUtilities.toWrappedHTML(
|
||||
getName() + " " + HTMLUtilities.escapeHTML(getUndoRedoDescription(lastProgram)));
|
||||
if (canPerformAction(activeProgram)) {
|
||||
String programName = activeProgram.getDomainFile().getName();
|
||||
String undoRedoDescription = getUndoRedoDescription(activeProgram);
|
||||
String text = actionName + " " +
|
||||
HTMLUtilities.escapeHTML(undoRedoDescription + " (" + programName + ")");
|
||||
description = HTMLUtilities.toWrappedHTML(text);
|
||||
}
|
||||
|
||||
getMenuBarData().setMenuItemNamePlain(actionName);
|
||||
getMenuBarData().setMenuItemNamePlain(menuName);
|
||||
setDescription(description);
|
||||
}
|
||||
|
||||
@ -158,6 +191,30 @@ public abstract class AbstractUndoRedoAction extends DockingAction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action for repeating the undo/redo action multiple times to effectively undo/redo to
|
||||
* a transaction that is not at the top of the list of undo/redo items.
|
||||
*/
|
||||
private class RepeatedAction extends DockingAction {
|
||||
|
||||
private int repeatCount;
|
||||
|
||||
public RepeatedAction(String name, int repeatCount) {
|
||||
super(name, AbstractUndoRedoAction.this.getOwner());
|
||||
this.repeatCount = repeatCount;
|
||||
setHelpLocation(helpLocation);
|
||||
setMenuBarData(new MenuData(new String[] { name }));
|
||||
setEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
executeAction(context, repeatCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ContextProgramTransactionListener implements TransactionListener {
|
||||
|
||||
@Override
|
||||
|
@ -16,6 +16,7 @@
|
||||
package ghidra.app.plugin.core.progmgr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@ -31,8 +32,10 @@ public class RedoAction extends AbstractUndoRedoAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(Program program) throws IOException {
|
||||
program.redo();
|
||||
protected void doAction(Program program, int count) throws IOException {
|
||||
for (int i = 0; i < count; i++) {
|
||||
program.redo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -44,4 +47,9 @@ public class RedoAction extends AbstractUndoRedoAction {
|
||||
protected String getUndoRedoDescription(Program program) {
|
||||
return program.getRedoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getDescriptions(Program program) {
|
||||
return program.getAllRedoNames();
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package ghidra.app.plugin.core.progmgr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@ -31,8 +32,10 @@ public class UndoAction extends AbstractUndoRedoAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(Program program) throws IOException {
|
||||
program.undo();
|
||||
protected void doAction(Program program, int count) throws IOException {
|
||||
for (int i = 0; i < count; i++) {
|
||||
program.undo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -45,4 +48,9 @@ public class UndoAction extends AbstractUndoRedoAction {
|
||||
return program.getUndoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getDescriptions(Program program) {
|
||||
return program.getAllUndoNames();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -307,8 +307,8 @@ public class EmptyVTSession implements VTSession {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToPackedFile(File outputFile, TaskMonitor monitor) throws IOException,
|
||||
CancelledException {
|
||||
public void saveToPackedFile(File outputFile, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@ -372,6 +372,16 @@ public class EmptyVTSession implements VTSession {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllRedoNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllUndoNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTransactionListener(TransactionListener listener) {
|
||||
// do nothing
|
||||
|
@ -16,6 +16,7 @@
|
||||
package ghidra.framework.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import db.TerminatedTransactionException;
|
||||
import ghidra.framework.model.*;
|
||||
@ -222,14 +223,44 @@ abstract class AbstractTransactionManager {
|
||||
*/
|
||||
abstract int getUndoStackDepth();
|
||||
|
||||
/**
|
||||
* Returns true if there is at least one redo transaction to be redone.
|
||||
* @return true if there is at least one redo transaction to be redone
|
||||
*/
|
||||
abstract boolean canRedo();
|
||||
|
||||
/**
|
||||
* Returns true if there is at least one undo transaction to be undone.
|
||||
* @return true if there is at least one undo transaction to be undone
|
||||
*/
|
||||
abstract boolean canUndo();
|
||||
|
||||
/**
|
||||
* Returns the name of the next undo transaction (The most recent change).
|
||||
* @return the name of the next undo transaction (The most recent change)
|
||||
*/
|
||||
abstract String getRedoName();
|
||||
|
||||
/**
|
||||
* Returns the name of the next redo transaction (The most recent undo).
|
||||
* @return the name of the next redo transaction (The most recent undo)
|
||||
*/
|
||||
abstract String getUndoName();
|
||||
|
||||
/**
|
||||
* Returns the names of all undoable transactions in reverse chronological order. In other
|
||||
* words the transaction at the top of the list must be undone first.
|
||||
* @return the names of all undoable transactions in reverse chronological order
|
||||
*/
|
||||
abstract List<String> getAllUndoNames();
|
||||
|
||||
/**
|
||||
* Returns the names of all redoable transactions in chronological order. In other words
|
||||
* the transaction at the top of the list must be redone first.
|
||||
* @return the names of all redoable transactions in chronological order
|
||||
*/
|
||||
abstract List<String> getAllRedoNames();
|
||||
|
||||
abstract TransactionInfo getCurrentTransactionInfo();
|
||||
|
||||
final void redo() throws IOException {
|
||||
|
@ -427,6 +427,16 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return transactionMgr.getUndoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllUndoNames() {
|
||||
return transactionMgr.getAllUndoNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllRedoNames() {
|
||||
return transactionMgr.getAllRedoNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionInfo getCurrentTransactionInfo() {
|
||||
return transactionMgr.getCurrentTransactionInfo();
|
||||
|
@ -121,9 +121,6 @@ class DomainObjectDBTransaction implements TransactionInfo {
|
||||
return hasDBTransaction;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.framework.data.XTransaction#getID()
|
||||
*/
|
||||
@Override
|
||||
public long getID() {
|
||||
return id;
|
||||
@ -148,8 +145,8 @@ class DomainObjectDBTransaction implements TransactionInfo {
|
||||
throw new IllegalStateException("Transaction not found");
|
||||
}
|
||||
if (entry.status != Status.NOT_DONE) {
|
||||
throw new IllegalStateException("Attempted to end Transaction " + "more that once: " +
|
||||
entry.description);
|
||||
throw new IllegalStateException(
|
||||
"Attempted to end Transaction " + "more that once: " + entry.description);
|
||||
}
|
||||
entry.status = commit ? Status.COMMITTED : Status.ABORTED;
|
||||
if (!commit) {
|
||||
@ -196,28 +193,20 @@ class DomainObjectDBTransaction implements TransactionInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.framework.data.XTransaction#getDescription()
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
if (list.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
String description = "";
|
||||
for (TransactionEntry entry : list) {
|
||||
description = entry.description;
|
||||
String description = entry.description;
|
||||
if (description != null && description.length() != 0) {
|
||||
description = domainObject.getDomainFile().getName() + ": " + description;
|
||||
break;
|
||||
return description;
|
||||
}
|
||||
}
|
||||
return description;
|
||||
return "";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.framework.data.XTransaction#getOpenSubTransactions()
|
||||
*/
|
||||
@Override
|
||||
public ArrayList<String> getOpenSubTransactions() {
|
||||
ArrayList<String> subTxList = new ArrayList<String>();
|
||||
|
@ -16,7 +16,7 @@
|
||||
package ghidra.framework.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.TransactionInfo.Status;
|
||||
@ -27,10 +27,8 @@ import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
class DomainObjectTransactionManager extends AbstractTransactionManager {
|
||||
|
||||
private LinkedList<DomainObjectDBTransaction> undoList =
|
||||
new LinkedList<>();
|
||||
private LinkedList<DomainObjectDBTransaction> redoList =
|
||||
new LinkedList<>();
|
||||
private LinkedList<DomainObjectDBTransaction> undoList = new LinkedList<>();
|
||||
private LinkedList<DomainObjectDBTransaction> redoList = new LinkedList<>();
|
||||
|
||||
private WeakSet<TransactionListener> transactionListeners =
|
||||
WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||
@ -241,6 +239,25 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
List<String> getAllUndoNames() {
|
||||
return getDescriptions(undoList);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<String> getAllRedoNames() {
|
||||
return getDescriptions(redoList);
|
||||
}
|
||||
|
||||
private List<String> getDescriptions(List<DomainObjectDBTransaction> list) {
|
||||
List<String> descriptions = new ArrayList<>();
|
||||
for (DomainObjectDBTransaction tx : list) {
|
||||
descriptions.add(tx.getDescription());
|
||||
}
|
||||
Collections.reverse(descriptions);
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
TransactionInfo getCurrentTransactionInfo() {
|
||||
return transaction;
|
||||
|
@ -16,7 +16,7 @@
|
||||
package ghidra.framework.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.TransactionInfo.Status;
|
||||
@ -88,8 +88,8 @@ class SynchronizedTransactionManager extends AbstractTransactionManager {
|
||||
|
||||
synchronized void removeDomainObject(DomainObjectAdapterDB domainObj) throws LockException {
|
||||
if (getCurrentTransactionInfo() != null) {
|
||||
throw new LockException(
|
||||
"domain object has open transaction: " + getCurrentTransactionInfo().getDescription());
|
||||
throw new LockException("domain object has open transaction: " +
|
||||
getCurrentTransactionInfo().getDescription());
|
||||
}
|
||||
if (isLocked()) {
|
||||
throw new LockException("domain object is locked!");
|
||||
@ -265,6 +265,26 @@ class SynchronizedTransactionManager extends AbstractTransactionManager {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
List<String> getAllUndoNames() {
|
||||
List<String> descriptions = new ArrayList<>();
|
||||
for (SynchronizedTransaction tx : undoList) {
|
||||
descriptions.add(tx.getDescription());
|
||||
}
|
||||
Collections.reverse(descriptions);
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
List<String> getAllRedoNames() {
|
||||
List<String> descriptions = new ArrayList<>();
|
||||
for (SynchronizedTransaction tx : redoList) {
|
||||
descriptions.add(tx.getDescription());
|
||||
}
|
||||
Collections.reverse(descriptions);
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
TransactionInfo getCurrentTransactionInfo() {
|
||||
return transaction;
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,8 +15,8 @@
|
||||
*/
|
||||
package ghidra.framework.model;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Objects that implement Undoable have the ability to "remember" some number
|
||||
@ -59,14 +58,28 @@ public interface Undoable {
|
||||
void redo() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a description of the chanage that would be "undone".
|
||||
* Returns a description of the change that would be "undone".
|
||||
* @return a description of the change that would be "undone".
|
||||
*/
|
||||
String getUndoName();
|
||||
public String getUndoName();
|
||||
|
||||
/**
|
||||
* Returns a description of the change that would be "redone".
|
||||
* @return a description of the change that would be "redone".
|
||||
*/
|
||||
String getRedoName();
|
||||
public String getRedoName();
|
||||
|
||||
/**
|
||||
* Returns a list of the names of all current undo transactions
|
||||
* @return a list of the names of all current undo transactions
|
||||
*/
|
||||
public List<String> getAllUndoNames();
|
||||
|
||||
/**
|
||||
* Returns a list of the names of all current redo transactions
|
||||
* @return a list of the names of all current redo transactions
|
||||
*/
|
||||
public List<String> getAllRedoNames();
|
||||
|
||||
/**
|
||||
* Adds the given transaction listener to this domain object
|
||||
|
@ -303,6 +303,16 @@ public class StubProgram implements Program {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllUndoNames() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllRedoNames() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransactionListener(TransactionListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
Loading…
Reference in New Issue
Block a user