mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-20 10:02:13 +08:00
Merge remote-tracking branch 'origin/GP-4719_ghidra1_StandaloneDTMUndoRedo--SQUASHED'
This commit is contained in:
@@ -899,4 +899,10 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||
super.close();
|
||||
objectManager.waitWbWorkers();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void domainObjectRestored() {
|
||||
super.domainObjectRestored();
|
||||
dataTypeManager.notifyRestored();
|
||||
}
|
||||
}
|
||||
|
||||
+16
-6
@@ -92,14 +92,24 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
||||
throw new IllegalStateException(
|
||||
"Can't apply edits without a data type or data type manager.");
|
||||
}
|
||||
int transactionID = originalDTM.startTransaction("Edit " + getCompositeName());
|
||||
boolean originalDtExists = originalDTM.contains(originalDt);
|
||||
boolean renamed = false;
|
||||
if (originalDtExists) {
|
||||
String origName = originalDt.getName();
|
||||
String editName = getCompositeName();
|
||||
renamed = !origName.equals(editName);
|
||||
}
|
||||
String action = originalDtExists ? "Edit" : "Create";
|
||||
if (renamed) {
|
||||
action += "/Rename";
|
||||
}
|
||||
String type = (originalDt instanceof Union) ? " Union " : " Structure ";
|
||||
int transactionID = originalDTM.startTransaction(action + type + getCompositeName());
|
||||
try {
|
||||
if (originalDTM.contains(originalDt)) {
|
||||
|
||||
if (originalDtExists) {
|
||||
// Update the original structure.
|
||||
String origName = originalDt.getName();
|
||||
String editName = getCompositeName();
|
||||
if (!origName.equals(editName)) {
|
||||
if (renamed) {
|
||||
String editName = getCompositeName();
|
||||
try {
|
||||
originalDt.setName(editName);
|
||||
}
|
||||
|
||||
+12
-12
@@ -545,20 +545,21 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
}
|
||||
}
|
||||
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
public void dataTypeManagerRestored() {
|
||||
DataTypeManager originalDTM = model.getOriginalDataTypeManager();
|
||||
if (originalDTM == null) {
|
||||
// editor unloaded
|
||||
return;
|
||||
}
|
||||
boolean reload = true;
|
||||
String objectType = "domain object";
|
||||
if (domainObject instanceof Program) {
|
||||
objectType = "program";
|
||||
String objectType;
|
||||
if (originalDTM instanceof ProgramBasedDataTypeManager) {
|
||||
objectType = "Program";
|
||||
}
|
||||
else if (domainObject instanceof DataTypeArchive) {
|
||||
objectType = "data type archive";
|
||||
else {
|
||||
objectType = "Archive";
|
||||
}
|
||||
String archiveName = originalDTM.getName();
|
||||
DataType dt = originalDTM.getDataType(model.getCompositeID());
|
||||
if (dt instanceof Composite) {
|
||||
Composite composite = (Composite) dt;
|
||||
@@ -570,10 +571,9 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
Composite originalDt = model.getOriginalComposite();
|
||||
if (originalDt == null) {
|
||||
provider.show();
|
||||
String info =
|
||||
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may no longer exist outside the editor.";
|
||||
Msg.showWarn(this, this, "Program Restored", info);
|
||||
String info = "The " + objectType + " \"" + archiveName + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may no longer exist outside the editor.";
|
||||
Msg.showWarn(this, this, objectType + " Restored", info);
|
||||
return;
|
||||
}
|
||||
else if (originalDt.isDeleted()) {
|
||||
@@ -586,8 +586,8 @@ public abstract class CompositeEditorPanel extends JPanel
|
||||
// The user has modified the structure so prompt for whether or
|
||||
// not to reload the structure.
|
||||
String question =
|
||||
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
|
||||
model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"Discard edits & reload the " + model.getTypeName() + "?";
|
||||
String title = "Reload " + model.getTypeName() + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
|
||||
|
||||
+2
-3
@@ -261,9 +261,8 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter
|
||||
return editorModel.hasChanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
editorPanel.domainObjectRestored(domainObject);
|
||||
public void dataTypeManagerRestored() {
|
||||
editorPanel.dataTypeManagerRestored();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+35
-6
@@ -24,7 +24,7 @@ import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
||||
|
||||
|
||||
/**
|
||||
* The data type manager for original composite data type being edited.
|
||||
* This is where the edited datatype will be written back to.
|
||||
@@ -43,7 +43,7 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
||||
public CompositeViewerDataTypeManager(String rootName, Composite originalComposite) {
|
||||
super(rootName, originalComposite.getDataTypeManager().getDataOrganization());
|
||||
this.originalComposite = originalComposite;
|
||||
transactionID = startTransaction("");
|
||||
transactionID = super.startTransaction("");
|
||||
originalDTM = originalComposite.getDataTypeManager();
|
||||
|
||||
ProgramArchitecture arch = originalDTM.getProgramArchitecture();
|
||||
@@ -68,11 +68,11 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
endTransaction(transactionID, true);
|
||||
public void close() {
|
||||
super.endTransaction(transactionID, true);
|
||||
super.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@link DataTypeManager} associated with the original composite datatype being edited.
|
||||
* @return original datatype manager
|
||||
@@ -82,7 +82,7 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArchiveType getType() {
|
||||
public ArchiveType getType() {
|
||||
return originalDTM.getType();
|
||||
}
|
||||
|
||||
@@ -103,4 +103,33 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
||||
return super.resolve(dataType, handler);
|
||||
}
|
||||
|
||||
//
|
||||
// Transaction support has been disabled since a single open transaction is maintained
|
||||
// until this DTM is closed.
|
||||
//
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public int startTransaction(String description) {
|
||||
// ignore - not yet supported
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction(int txId, boolean commit) {
|
||||
// ignore - not yet supported
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public boolean canUndo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public boolean canRedo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-4
@@ -959,6 +959,16 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
// Don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
provider.dataTypeManagerRestored();
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Helper methods for CategoryChangeListener methods.
|
||||
//=================================================================================================
|
||||
@@ -1354,8 +1364,4 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
||||
return viewComposite.isPackingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
|
||||
-7
@@ -47,13 +47,6 @@ public interface EditorProvider {
|
||||
*/
|
||||
public DataTypeManager getDataTypeManager();
|
||||
|
||||
/**
|
||||
* Notification that the data type manager domain object (program or data type archive) was
|
||||
* restored.
|
||||
* @param domainObject the program or data type archive that was restored.
|
||||
*/
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject);
|
||||
|
||||
/**
|
||||
* Return whether this editor is editing the data type with the given path.
|
||||
* @param dtPath path of a data type
|
||||
|
||||
+2
-1
@@ -1264,7 +1264,8 @@ class StructureEditorModel extends CompEditorModel {
|
||||
private DataType createDataTypeInOriginalDTM(StructureDataType structureDataType) {
|
||||
boolean commit = false;
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
int transactionID = originalDTM.startTransaction("Creating " + structureDataType.getName());
|
||||
int transactionID =
|
||||
originalDTM.startTransaction("Create structure " + structureDataType.getName());
|
||||
try {
|
||||
DataType addedDataType =
|
||||
originalDTM.addDataType(structureDataType, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
|
||||
+2
-1
@@ -290,7 +290,8 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||
DataTypeManagerDomainObject domainObject = (DataTypeManagerDomainObject) source;
|
||||
provider.domainObjectRestored(domainObject);
|
||||
dataTypePropertyManager.domainObjectRestored(domainObject);
|
||||
editorManager.domainObjectRestored(domainObject);
|
||||
// NOTE: each editor that cares about a restored DataTypeManager must establish
|
||||
// a DataTypeManagerChangeListener and will be notified via the restored method.
|
||||
}
|
||||
}
|
||||
else if (event.contains(DomainObjectEvent.RENAMED)) {
|
||||
|
||||
+11
-12
@@ -99,7 +99,7 @@ public class DataTypeSynchronizer {
|
||||
}
|
||||
|
||||
private static void update(DataTypeManager refDTM, DataType sourceDT) {
|
||||
int transactionID = refDTM.startTransaction("Update Datatype");
|
||||
int transactionID = refDTM.startTransaction("Update Datatype " + sourceDT.getName());
|
||||
try {
|
||||
updateAssumingTransactionsOpen(refDTM, sourceDT);
|
||||
}
|
||||
@@ -184,8 +184,7 @@ public class DataTypeSynchronizer {
|
||||
}
|
||||
|
||||
public void markSynchronized() {
|
||||
int transactionID =
|
||||
dataTypeManager.startTransaction("Clear dirty flag for data type manager.");
|
||||
int transactionID = dataTypeManager.startTransaction("Clear Dirty Flag");
|
||||
try {
|
||||
sourceArchive.setDirtyFlag(false);
|
||||
sourceArchive.setLastSyncTime(sourceDTM.getLastChangeTimeForMyManager());
|
||||
@@ -457,8 +456,8 @@ public class DataTypeSynchronizer {
|
||||
return;
|
||||
}
|
||||
|
||||
int transactionID = dataTypeManager
|
||||
.startTransaction("re-sync '" + sourceArchive.getName() + "' data types");
|
||||
int transactionID =
|
||||
dataTypeManager.startTransaction("Sync '" + sourceArchive.getName() + "' data types");
|
||||
try {
|
||||
reSyncOutOfSyncInTimeOnlyDataTypes();
|
||||
fixSyncForDifferingDataTypes();
|
||||
@@ -525,7 +524,7 @@ public class DataTypeSynchronizer {
|
||||
|
||||
private void autoUpdateDataTypesThatHaveNoRealChanges(
|
||||
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
|
||||
int transactionID = dataTypeManager.startTransaction("auto sync datatypes");
|
||||
int transactionID = dataTypeManager.startTransaction("Sync datatypes");
|
||||
try {
|
||||
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
|
||||
dataTypeSyncInfo.syncTimes();
|
||||
@@ -539,16 +538,16 @@ public class DataTypeSynchronizer {
|
||||
}
|
||||
}
|
||||
|
||||
public void performBulkOperation(String actionName, List<DataTypeSyncInfo> selectedList,
|
||||
public void performBulkOperation(String actionName, List<DataTypeSyncInfo> selectedList,
|
||||
ExceptionalConsumer<DataTypeSyncInfo, CancelledException> infoApplier,
|
||||
Consumer<List<DataTypeSyncInfo>> handleOutOfSync,
|
||||
boolean sourceRequiresTransaction) throws CancelledException {
|
||||
Consumer<List<DataTypeSyncInfo>> handleOutOfSync, boolean sourceRequiresTransaction)
|
||||
throws CancelledException {
|
||||
if (sourceDTM == null) {
|
||||
throw new RuntimeException("Source archive required");
|
||||
}
|
||||
|
||||
int sourceTransactionId = sourceRequiresTransaction ?
|
||||
sourceDTM.startTransaction(actionName) : 0;
|
||||
|
||||
int sourceTransactionId =
|
||||
sourceRequiresTransaction ? sourceDTM.startTransaction(actionName) : 0;
|
||||
int transactionID = dataTypeManager.startTransaction(actionName);
|
||||
try {
|
||||
for (DataTypeSyncInfo info : selectedList) {
|
||||
|
||||
+2
@@ -181,6 +181,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
// FileEdit group
|
||||
addLocalAction(new LockArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new UnlockArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new UndoArchiveTransactionAction(plugin)); // Archive
|
||||
addLocalAction(new RedoArchiveTransactionAction(plugin)); // Archive
|
||||
|
||||
// Arch group
|
||||
addLocalAction(new SetArchiveArchitectureAction(plugin)); // Archive
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ abstract class AbstractTypeDefAction extends DockingAction {
|
||||
private DataType createNewTypeDef(Component parentComponent, TypeDef typedef,
|
||||
CategoryPath categoryPath, DataTypeManager dataTypeManager) {
|
||||
DataType newdt = null;
|
||||
int transactionID = dataTypeManager.startTransaction("Create Typedef");
|
||||
int transactionID = dataTypeManager.startTransaction("Create Typedef " + typedef.getName());
|
||||
try {
|
||||
newdt = dataTypeManager.addDataType(typedef, plugin.getConflictHandler());
|
||||
}
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/* ###
|
||||
* 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.datamgr.actions;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager;
|
||||
|
||||
public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAction {
|
||||
|
||||
private String actionName; // Undo / Redo
|
||||
|
||||
/**
|
||||
* Construct Undo/Redo action
|
||||
* @param actionName "Undo" or "Redo" action name
|
||||
* @param plugin {@link DataTypeManagerPlugin}
|
||||
*/
|
||||
public AbstractUndoRedoArchiveTransactionAction(String actionName,
|
||||
DataTypeManagerPlugin plugin) {
|
||||
super(actionName + " Archive Change", plugin.getName());
|
||||
this.actionName = actionName;
|
||||
setPopupMenuData(getMenuData(null));
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
private MenuData getMenuData(String txName) {
|
||||
String name = actionName + " Change";
|
||||
if (!StringUtils.isEmpty(txName)) {
|
||||
name += ": " + txName;
|
||||
}
|
||||
return new MenuData(new String[] { name }, null, "FileEdit");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
return getModifiableProjectOrFileDTM(selectionPaths) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the corresponding undo/redo can be performed
|
||||
* @param dtm archive datatype manager
|
||||
* @return true if action can be performed on archive
|
||||
*/
|
||||
abstract protected boolean canExecute(StandAloneDataTypeManager dtm);
|
||||
|
||||
/**
|
||||
* Determine the next undo/redo transaction name
|
||||
* @param dtm archive datatype manager
|
||||
* @return next undo/redo transaction name
|
||||
*/
|
||||
abstract protected String getNextName(StandAloneDataTypeManager dtm);
|
||||
|
||||
/**
|
||||
* Execute the undo/redo operation on the specified archive datatype manager.
|
||||
* @param dtm archive datatype manager
|
||||
*/
|
||||
abstract protected void execute(StandAloneDataTypeManager dtm);
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
|
||||
if (dtm != null && canExecute(dtm)) {
|
||||
setPopupMenuData(getMenuData(getNextName(dtm)));
|
||||
return true;
|
||||
}
|
||||
setPopupMenuData(getMenuData(null));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
|
||||
if (dtm != null && canExecute(dtm)) {
|
||||
execute(dtm);
|
||||
}
|
||||
}
|
||||
|
||||
private TreePath[] getSelectionPaths(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gtree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||
return selectionPaths;
|
||||
}
|
||||
|
||||
private StandAloneDataTypeManager getModifiableProjectOrFileDTM(TreePath[] selectionPaths) {
|
||||
// only valid if single file or project archive node is selected
|
||||
if (selectionPaths.length != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TreePath path = selectionPaths[0];
|
||||
if (path.getPathCount() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GTreeNode node = (GTreeNode) path.getPathComponent(1);
|
||||
if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.isModifiable()) {
|
||||
DataTypeManager dtm = archiveNode.getArchive().getDataTypeManager();
|
||||
if (dtm instanceof StandAloneDataTypeManager archiveDtm) {
|
||||
return archiveDtm;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -26,8 +26,7 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.program.model.data.Category;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.InvalidNameException;
|
||||
|
||||
public class CreateCategoryAction extends DockingAction {
|
||||
@@ -90,10 +89,10 @@ public class CreateCategoryAction extends DockingAction {
|
||||
Archive archive = archiveNode.getArchive();
|
||||
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
||||
|
||||
String newNodeName = null;
|
||||
int transactionID = dataTypeManager.startTransaction("Create Category");
|
||||
String newNodeName = getUniqueCategoryName(category);
|
||||
String path = category.toString() + newNodeName;
|
||||
int transactionID = dataTypeManager.startTransaction("Create " + path);
|
||||
try {
|
||||
newNodeName = getUniqueCategoryName(category);
|
||||
category.createCategory(newNodeName);
|
||||
}
|
||||
catch (InvalidNameException ie) {
|
||||
|
||||
+2
-1
@@ -78,7 +78,8 @@ public class CreatePointerAction extends DockingAction {
|
||||
|
||||
private DataType createNewDataType(Component parentComponent, DataType dataType,
|
||||
CategoryPath categoryPath, DataTypeManager dataTypeManager) {
|
||||
int transactionID = dataTypeManager.startTransaction("Create Typedef");
|
||||
int transactionID =
|
||||
dataTypeManager.startTransaction("Create Pointer " + dataType.getName());
|
||||
try {
|
||||
return dataTypeManager.addDataType(dataType, plugin.getConflictHandler());
|
||||
}
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ public class Pack1DataTypeAction extends DockingAction {
|
||||
boolean commit = false;
|
||||
try {
|
||||
// start a transaction
|
||||
transactionID = dataTypeManager.startTransaction("pack of " + dataType.getName());
|
||||
transactionID = dataTypeManager.startTransaction("Pack(1) " + dataType.getName());
|
||||
packDataType(dataType);
|
||||
commit = true;
|
||||
}
|
||||
|
||||
+5
-6
@@ -29,7 +29,6 @@ import ghidra.util.Msg;
|
||||
|
||||
public class PackDataTypeAction extends DockingAction {
|
||||
|
||||
|
||||
public PackDataTypeAction(DataTypeManagerPlugin plugin) {
|
||||
super("Pack Data Type", plugin.getName());
|
||||
setPopupMenuData(new MenuData(new String[] { "Pack (default)" }, "Edit"));
|
||||
@@ -100,20 +99,20 @@ public class PackDataTypeAction extends DockingAction {
|
||||
private void alignDataType(DataType dataType, DataOrganization dataOrganization) {
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
if (dataTypeManager == null) {
|
||||
Msg.error(this, "Can't align data type " + dataType.getName() +
|
||||
" without a data type manager.");
|
||||
Msg.error(this,
|
||||
"Can't align data type " + dataType.getName() + " without a data type manager.");
|
||||
return;
|
||||
}
|
||||
if (!(dataType instanceof Structure)) {
|
||||
Msg.error(this, "Can't align data type " + dataType.getName() +
|
||||
". It's not a structure.");
|
||||
Msg.error(this,
|
||||
"Can't align data type " + dataType.getName() + ". It's not a structure.");
|
||||
return;
|
||||
}
|
||||
int transactionID = -1;
|
||||
boolean commit = false;
|
||||
try {
|
||||
// start a transaction
|
||||
transactionID = dataTypeManager.startTransaction("align " + dataType.getName());
|
||||
transactionID = dataTypeManager.startTransaction("Pack " + dataType.getName());
|
||||
((Structure) dataType).setPackingEnabled(true);
|
||||
commit = true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ public class PackSizeDataTypeAction extends DockingAction {
|
||||
try {
|
||||
// start a transaction
|
||||
transactionID =
|
||||
dataTypeManager.startTransaction("pack(" + packSize + ") of " + dataType.getName());
|
||||
dataTypeManager.startTransaction("Pack(" + packSize + ") " + dataType.getName());
|
||||
packDataType(dataType, packSize);
|
||||
commit = true;
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/* ###
|
||||
* 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.datamgr.actions;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager;
|
||||
|
||||
public class RedoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction {
|
||||
|
||||
public RedoArchiveTransactionAction(DataTypeManagerPlugin plugin) {
|
||||
super("Redo", plugin);
|
||||
// Key-bind disabled by default to activation context concerns
|
||||
//setKeyBindingData(new KeyBindingData("ctrl shift Z"));
|
||||
setDescription("Redo last undone change made to data type archive");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canExecute(StandAloneDataTypeManager dtm) {
|
||||
return dtm.canRedo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNextName(StandAloneDataTypeManager dtm) {
|
||||
return dtm.getRedoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(StandAloneDataTypeManager dtm) {
|
||||
dtm.redo();
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/* ###
|
||||
* 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.datamgr.actions;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager;
|
||||
|
||||
public class UndoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction {
|
||||
|
||||
public UndoArchiveTransactionAction(DataTypeManagerPlugin plugin) {
|
||||
super("Undo", plugin);
|
||||
// Key-bind disabled by default to activation context concerns
|
||||
//setKeyBindingData(new KeyBindingData("ctrl Z"));
|
||||
setDescription("Undo last change made to data type archive");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canExecute(StandAloneDataTypeManager dtm) {
|
||||
return dtm.canUndo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNextName(StandAloneDataTypeManager dtm) {
|
||||
return dtm.getUndoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(StandAloneDataTypeManager dtm) {
|
||||
dtm.undo();
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -282,7 +282,8 @@ public class AssociateDataTypeAction extends DockingAction {
|
||||
}
|
||||
|
||||
boolean noErrors = false;
|
||||
int tx = dtm.startTransaction("Create Category");
|
||||
String path = archive.getName() + categoryPath;
|
||||
int tx = dtm.startTransaction("Create " + path);
|
||||
try {
|
||||
category = dtm.createCategory(categoryPath);
|
||||
noErrors = true;
|
||||
|
||||
+6
-5
@@ -131,7 +131,8 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
monitor.setMessage("Finding out-of-sync types");
|
||||
List<DataTypeSyncInfo> outOfSynchDataTypes = synchronizer.findOutOfSynchDataTypes();
|
||||
|
||||
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer, synchronizer.findOutOfSynchDataTypes());
|
||||
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer,
|
||||
synchronizer.findOutOfSynchDataTypes());
|
||||
if (outOfSynchDataTypes.isEmpty()) {
|
||||
showNoDataTypesToSyncMessage();
|
||||
return;
|
||||
@@ -194,20 +195,20 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
protected void processSelectedDataTypes(DataTypeSynchronizer synchronizer,
|
||||
List<DataTypeSyncInfo> selectedList, List<DataTypeSyncInfo> outOfSynchDataTypes,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
|
||||
synchronizer.performBulkOperation(getName(), selectedList, info -> {
|
||||
monitor.checkCancelled();
|
||||
monitor.setMessage("Syncing " + info.getName());
|
||||
applyOperation(info);
|
||||
outOfSynchDataTypes.remove(info);
|
||||
monitor.incrementProgress(1);
|
||||
|
||||
|
||||
}, outOfSyncList -> {
|
||||
// dataTypeChanged can cause other related data types to become updated
|
||||
// and their times will appear out of sync. So clean up any that actually
|
||||
// are the same.
|
||||
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer, outOfSyncList);
|
||||
|
||||
|
||||
}, requiresArchiveOpenForEditing());
|
||||
}
|
||||
|
||||
@@ -326,7 +327,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
||||
private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer,
|
||||
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
|
||||
|
||||
int transactionID = dtm.startTransaction("Auto-sync data types");
|
||||
int transactionID = dtm.startTransaction("Sync data types");
|
||||
try {
|
||||
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
|
||||
dataTypeSyncInfo.syncTimes();
|
||||
|
||||
+5
@@ -220,5 +220,10 @@ public class DataTypeIndexer {
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
markStale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
markStale();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -640,7 +640,6 @@ public class DataTypeManagerHandler {
|
||||
|
||||
void dataTypeManagerChanged(FileArchive archive, DataTypeManager oldManager,
|
||||
DataTypeManager newManager) {
|
||||
|
||||
oldManager.removeDataTypeManagerListener(listenerDelegate);
|
||||
newManager.addDataTypeManagerListener(listenerDelegate);
|
||||
dataTypeIndexer.removeDataTypeManager(oldManager);
|
||||
@@ -1239,6 +1238,13 @@ public class DataTypeManagerHandler {
|
||||
listener.programArchitectureChanged(dataTypeManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) {
|
||||
listener.restored(dataTypeManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1412,8 +1418,7 @@ public class DataTypeManagerHandler {
|
||||
}
|
||||
|
||||
private DataTreeDialog getSaveDialog() {
|
||||
DataTreeDialog dialog =
|
||||
new DataTreeDialog(null, "Save As", SAVE, createArchiveFileFilter);
|
||||
DataTreeDialog dialog = new DataTreeDialog(null, "Save As", SAVE, createArchiveFileFilter);
|
||||
|
||||
ActionListener listener = event -> {
|
||||
DomainFolder folder = dialog.getDomainFolder();
|
||||
|
||||
+6
@@ -300,6 +300,12 @@ public class FileArchive implements Archive {
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
setChanged(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
archiveManager.dataTypeManagerChanged(FileArchive.this, dataTypeManager,
|
||||
dataTypeManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-1
@@ -29,7 +29,7 @@ public class ProjectArchive implements DomainFileArchive {
|
||||
|
||||
private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.project.closed");
|
||||
private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.project.open");
|
||||
|
||||
|
||||
private DataTypeArchive dataTypeArchive;
|
||||
private DomainFile sourceDomainFile;
|
||||
private DataTypeManagerChangeListener categoryListener; // hold on to since it is stored in a weak set
|
||||
@@ -67,6 +67,7 @@ public class ProjectArchive implements DomainFileArchive {
|
||||
return -1; // Project Archives appear between the ProgramArchive and FileArchives.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExclusiveAccess() {
|
||||
return dataTypeArchive.hasExclusiveAccess();
|
||||
}
|
||||
@@ -202,5 +203,10 @@ public class ProjectArchive implements DomainFileArchive {
|
||||
public void programArchitectureChanged(DataTypeManager dtm) {
|
||||
fireStateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dtm) {
|
||||
fireStateChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-28
@@ -390,34 +390,6 @@ public class DataTypeEditorManager implements EditorListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
// Create a copy of the list since restore may remove an editor from the original list.
|
||||
ArrayList<EditorProvider> list = new ArrayList<>(editorList);
|
||||
// notify the editors
|
||||
for (EditorProvider editor : list) {
|
||||
DataTypeManager dataTypeManager = editor.getDataTypeManager();
|
||||
DataTypeManager programDataTypeManager = domainObject.getDataTypeManager();
|
||||
if (dataTypeManager == programDataTypeManager) {
|
||||
/*
|
||||
|
||||
It is not clear why this check was added. It seem reasonable to always let the
|
||||
editor know about the event. With this code enabled, editors with new, unsaved
|
||||
types will be closed.
|
||||
|
||||
DataTypePath dtPath = editor.getDtPath();
|
||||
CategoryPath categoryPath = dtPath.getCategoryPath();
|
||||
String name = dtPath.getDataTypeName();
|
||||
DataType dataType = programDataTypeManager.getDataType(categoryPath, name);
|
||||
if (dataType == null || dataType.isDeleted()) {
|
||||
dismissEditor(editor);
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
editor.domainObjectRestored(domainObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the specified data type is being edited for the indicated category, this gets that editor.
|
||||
* @param dataType the data type
|
||||
|
||||
+26
-14
@@ -36,6 +36,7 @@ import docking.widgets.textfield.GValidatedTextField.ValidationMessageListener;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.*;
|
||||
@@ -120,30 +121,40 @@ class EnumEditorPanel extends JPanel {
|
||||
});
|
||||
}
|
||||
|
||||
void domainObjectRestored(DataTypeManagerDomainObject domainObject, EnumDataType enuum) {
|
||||
void domainObjectRestored(EnumDataType enuum, boolean exists) {
|
||||
|
||||
stopCellEditing();
|
||||
this.originalEnumDT = enuum;
|
||||
this.editedEnumDT = (EnumDataType) enuum.copy(enuum.getDataTypeManager());
|
||||
DataTypeManager objectDataTypeManager = domainObject.getDataTypeManager();
|
||||
DataTypeManager providerDataTypeManager = provider.getDataTypeManager();
|
||||
if (objectDataTypeManager != providerDataTypeManager) {
|
||||
return; // The editor isn't associated with the restored domain object.
|
||||
}
|
||||
|
||||
DataTypeManager enumDtMgr = enuum.getDataTypeManager();
|
||||
String objectType = "domain object";
|
||||
if (domainObject instanceof Program) {
|
||||
if (enumDtMgr instanceof ProgramBasedDataTypeManager) {
|
||||
objectType = "program";
|
||||
}
|
||||
else if (domainObject instanceof DataTypeArchive) {
|
||||
else {
|
||||
objectType = "data type archive";
|
||||
}
|
||||
String archiveName = enumDtMgr.getName();
|
||||
this.originalEnumDT = enuum;
|
||||
|
||||
if (tableModel.hasChanges()) {
|
||||
if (!exists) {
|
||||
if (OptionDialog.showOptionNoCancelDialog(this, "Close Enum Editor?",
|
||||
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
|
||||
enuum.getDisplayName() + "\" may no longer exist outside the editor.\n" +
|
||||
"Do you want to close editor?",
|
||||
"Close", "Continue Edit",
|
||||
OptionDialog.WARNING_MESSAGE) == OptionDialog.OPTION_ONE) {
|
||||
provider.dispose();
|
||||
}
|
||||
else {
|
||||
provider.stateChanged(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (exists && tableModel.hasChanges()) {
|
||||
if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, "Reload Enum Editor?",
|
||||
"The " + objectType + " \"" + objectDataTypeManager.getName() +
|
||||
"\" has been restored.\n" + "\"" + tableModel.getEnum().getDisplayName() +
|
||||
"\" may have changed outside this editor.\n" +
|
||||
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
|
||||
enuum.getDisplayName() + "\" may have changed outside this editor.\n" +
|
||||
"Do you want to discard edits and reload the Enum?") == OptionDialog.OPTION_TWO) {
|
||||
|
||||
// 'No'; do not discard
|
||||
@@ -153,6 +164,7 @@ class EnumEditorPanel extends JPanel {
|
||||
}
|
||||
|
||||
// reload the enum
|
||||
this.editedEnumDT = (EnumDataType) enuum.copy(enuum.getDataTypeManager());
|
||||
setFieldInfo(editedEnumDT);
|
||||
tableModel.setEnum(editedEnumDT, false);
|
||||
}
|
||||
|
||||
+47
-33
@@ -107,6 +107,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
}
|
||||
originalCategoryPath = categoryPath;
|
||||
originalEnum = enumDT;
|
||||
|
||||
originalEnumName = enumDT.getDisplayName();
|
||||
dataTypeManager = enumDTM;
|
||||
|
||||
@@ -213,25 +214,6 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
return editorPanel.needsSave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
|
||||
if (originalEnumID == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Enum enuum = (Enum) dataTypeManager.getDataType(originalEnumID);
|
||||
if (enuum != null) {
|
||||
EnumDataType dt = (EnumDataType) enuum.copy(dataTypeManager);
|
||||
originalEnumName = dt.getDisplayName();
|
||||
updateTitle(dt);
|
||||
Category category = dataTypeManager.getCategory(enuum.getCategoryPath());
|
||||
originalCategoryPath = category.getCategoryPath();
|
||||
editorPanel.domainObjectRestored(domainObject, dt);
|
||||
}
|
||||
tool.setStatusInfo("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransient() {
|
||||
return true;
|
||||
@@ -348,11 +330,21 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
setStatusMessage("Empty enum is not allowed");
|
||||
return false;
|
||||
}
|
||||
int txID = startTransaction();
|
||||
|
||||
boolean originalDtExists = dataTypeManager.contains(originalEnum);
|
||||
boolean renamed = false;
|
||||
if (originalDtExists) {
|
||||
String editorName = editorPanel.getEnumName().trim();
|
||||
renamed = !originalEnumName.equals(editorName);
|
||||
}
|
||||
String action = originalDtExists ? "Edit" : "Create";
|
||||
if (renamed) {
|
||||
action += "/Rename";
|
||||
}
|
||||
int txID = dataTypeManager.startTransaction(action + " Enum " + editedEnum.getName());
|
||||
try {
|
||||
DataTypeManager dtm = editedEnum.getDataTypeManager();
|
||||
boolean userSaved = resolveEquateConflicts(editedEnum, dtm);
|
||||
|
||||
boolean userSaved = resolveEquateConflicts(editedEnum);
|
||||
if (!userSaved) {
|
||||
return false;
|
||||
}
|
||||
@@ -364,11 +356,12 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
newEnuum.replaceWith(editedEnum);
|
||||
|
||||
originalEnum = newEnuum;
|
||||
originalEnumID = dataTypeManager.getID(newEnuum);
|
||||
editorPanel.setEnum((EnumDataType) newEnuum.copy(dataTypeManager));
|
||||
applyAction.setEnabled(hasChanges());
|
||||
}
|
||||
finally {
|
||||
endTransaction(txID);
|
||||
dataTypeManager.endTransaction(txID, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -381,10 +374,9 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
/**
|
||||
* Checks to see if the new changes to the enum will affect equates based off of it.
|
||||
* @param editedEnum the enum to check for conflicts with
|
||||
* @param dtm the data type manager that this enum lies within
|
||||
* @return true if the enum should save its changes; otherwise, false
|
||||
*/
|
||||
private boolean resolveEquateConflicts(Enum editedEnum, DataTypeManager dtm) {
|
||||
private boolean resolveEquateConflicts(Enum editedEnum) {
|
||||
|
||||
Program program = plugin.getProgram();
|
||||
if (program == null) {
|
||||
@@ -500,14 +492,6 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
}
|
||||
}
|
||||
|
||||
private int startTransaction() {
|
||||
return dataTypeManager.startTransaction("Edit Enum");
|
||||
}
|
||||
|
||||
private void endTransaction(int transID) {
|
||||
dataTypeManager.endTransaction(transID, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user if the editor has unsaved changes. Saves the changes if
|
||||
* the user indicates to do so.
|
||||
@@ -692,6 +676,36 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dtm) {
|
||||
if (originalEnumID <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeManager originalDTM = originalEnum.getDataTypeManager();
|
||||
DataType dt = originalDTM.getDataType(originalEnumID);
|
||||
|
||||
boolean exists = false;
|
||||
if (dt instanceof Enum) {
|
||||
originalEnum = (Enum) dt;
|
||||
exists = true;
|
||||
}
|
||||
else {
|
||||
// original enum no longer exists
|
||||
originalEnumID = -1;
|
||||
EnumDataType enuum = editorPanel.getEnum();
|
||||
originalEnum = new EnumDataType(enuum.getCategoryPath(), enuum.getName(),
|
||||
enuum.getLength(), originalDTM);
|
||||
}
|
||||
|
||||
originalEnumName = originalEnum.getDisplayName();
|
||||
updateTitle(originalEnum);
|
||||
originalCategoryPath = originalEnum.getCategoryPath();
|
||||
|
||||
editorPanel.domainObjectRestored((EnumDataType) originalEnum.copy(originalDTM), exists);
|
||||
tool.setStatusInfo("");
|
||||
}
|
||||
|
||||
private boolean isMyCategory(DataTypePath path) {
|
||||
CategoryPath parentPath = path.getCategoryPath();
|
||||
return parentPath.equals(originalCategoryPath);
|
||||
|
||||
+8
@@ -480,5 +480,13 @@ public class ArchiveNode extends CategoryNode {
|
||||
unloadChildren();
|
||||
nodeChangedUpdater.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager manager) {
|
||||
// need to force all cached datatype tooltips to be cleared
|
||||
// due to potential changes (e.g., undo/redo)
|
||||
unloadChildren();
|
||||
nodeChangedUpdater.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -253,18 +253,20 @@ public class CategoryNode extends DataTypeTreeNode {
|
||||
|
||||
@Override
|
||||
public void valueChanged(Object newValue) {
|
||||
int transactionID = category.getDataTypeManager().startTransaction("rename");
|
||||
String newName = newValue.toString();
|
||||
int transactionID =
|
||||
category.getDataTypeManager().startTransaction("Rename Category " + newName);
|
||||
try {
|
||||
category.setName(newValue.toString());
|
||||
category.setName(newName);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.showError(getClass(), null, "Rename Failed",
|
||||
"Category by the name " + newValue + " already exists in this category.");
|
||||
"Category by the name " + newName + " already exists in this category.");
|
||||
}
|
||||
catch (InvalidNameException exc) {
|
||||
String msg = exc.getMessage();
|
||||
if (msg == null) {
|
||||
msg = "Invalid name specified: " + newValue;
|
||||
msg = "Invalid name specified: " + newName;
|
||||
}
|
||||
Msg.showError(getClass(), null, "Invalid name specified", exc.getMessage());
|
||||
}
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ public class DataTypeNode extends DataTypeTreeNode {
|
||||
return;
|
||||
}
|
||||
|
||||
int transactionID = dataType.getDataTypeManager().startTransaction("rename");
|
||||
int transactionID = dataType.getDataTypeManager().startTransaction("Rename DataType");
|
||||
|
||||
try {
|
||||
dataType.setName(newName);
|
||||
|
||||
+3
-3
@@ -182,7 +182,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
|
||||
|
||||
DataTypeManager newDtm = createLayeredDataTypeManager();
|
||||
|
||||
int transactionId = newDtm.startTransaction("add datatypes");
|
||||
int transactionId = newDtm.startTransaction("Add Datatypes");
|
||||
try {
|
||||
Iterator<DataType> allDataTypes = dataTypeManager.getAllDataTypes();
|
||||
while (allDataTypes.hasNext()) {
|
||||
@@ -343,7 +343,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
int transactionID = dataTypeManager.startTransaction("Add dataType");
|
||||
int transactionID = dataTypeManager.startTransaction("Add " + dt.getName());
|
||||
try {
|
||||
DataType resolvedDt = dataTypeManager.resolve(dt, null);
|
||||
model.add(resolvedDt);
|
||||
@@ -354,7 +354,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
|
||||
}
|
||||
|
||||
private void removeDataType(DataType dt) {
|
||||
int transactionID = dataTypeManager.startTransaction("Remove dataType");
|
||||
int transactionID = dataTypeManager.startTransaction("Remove " + dt.getName());
|
||||
try {
|
||||
model.removeAll(dt);
|
||||
|
||||
|
||||
+6
-11
@@ -24,7 +24,8 @@ import javax.swing.*;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorPanel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
@@ -273,15 +274,9 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
public void dataTypeManagerRestored() {
|
||||
boolean reload = true;
|
||||
String objectType = "domain object";
|
||||
if (domainObject instanceof Program) {
|
||||
objectType = "program";
|
||||
}
|
||||
else if (domainObject instanceof DataTypeArchive) {
|
||||
objectType = "data type archive";
|
||||
}
|
||||
String objectType = "program";
|
||||
DataTypeManager dtm = ((StackEditorModel) model).getOriginalDataTypeManager();
|
||||
Composite originalDt = ((StackEditorModel) model).getOriginalComposite();
|
||||
if (originalDt instanceof StackFrameDataType) {
|
||||
@@ -306,8 +301,8 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
// The user has modified the structure so prompt for whether or
|
||||
// not to reload the structure.
|
||||
String question =
|
||||
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"The " + objectType + " \"" + dtm.getName() + "\" has been restored.\n" + "\"" +
|
||||
model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"Discard edits & reload the " + name + " Editor?";
|
||||
String title = "Reload " + name + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
|
||||
|
||||
+5
-12
@@ -23,7 +23,8 @@ import ghidra.app.plugin.core.compositeeditor.*;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
import ghidra.program.model.data.DataTypePath;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
@@ -144,12 +145,6 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
|
||||
return actionMgr.getAllActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
refreshName();
|
||||
editorPanel.domainObjectRestored(domainObject);
|
||||
}
|
||||
|
||||
private void refreshName() {
|
||||
StackFrameDataType origDt = (StackFrameDataType) stackModel.getOriginalComposite();
|
||||
StackFrameDataType viewDt = stackModel.getViewComposite();
|
||||
@@ -187,11 +182,9 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
|
||||
DomainObjectChangeRecord rec = event.getChangeRecord(i);
|
||||
EventType eventType = rec.getEventType();
|
||||
if (eventType == DomainObjectEvent.RESTORED) {
|
||||
Object source = event.getSource();
|
||||
if (source instanceof Program) {
|
||||
Program restoredProgram = (Program) source;
|
||||
domainObjectRestored(restoredProgram);
|
||||
}
|
||||
refreshName();
|
||||
// NOTE: editorPanel should be notified of restored datatype manager via the
|
||||
// CompositeViewerModel's DataTypeManagerChangeListener restored method
|
||||
return;
|
||||
}
|
||||
if (eventType instanceof ProgramEvent type) {
|
||||
|
||||
+12
-24
@@ -460,7 +460,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the golang version
|
||||
* @return {@link GoVer}
|
||||
@@ -842,9 +841,8 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
// gdt data base. This method only leaves the target gdt filename + ".step1" in the db.
|
||||
File tmpGDTFile = new File(gdtFile.getParentFile(), gdtFile.getName() + ".step1.gdt");
|
||||
FileDataTypeManager tmpFdtm = FileDataTypeManager.createFileArchive(tmpGDTFile);
|
||||
int tx = -1;
|
||||
int tx = tmpFdtm.startTransaction("Import");
|
||||
try {
|
||||
tx = tmpFdtm.startTransaction("Import");
|
||||
tmpFdtm.addDataTypes(registeredStructDTs, DataTypeConflictHandler.DEFAULT_HANDLER,
|
||||
monitor);
|
||||
if (runtimeFuncSnapshot) {
|
||||
@@ -879,17 +877,14 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
Msg.error(this, "Error when exporting types to file: %s".formatted(gdtFile), e);
|
||||
}
|
||||
finally {
|
||||
if (tx != -1) {
|
||||
tmpFdtm.endTransaction(tx, true);
|
||||
}
|
||||
tmpFdtm.endTransaction(tx, true);
|
||||
}
|
||||
|
||||
tmpFdtm.save();
|
||||
|
||||
FileDataTypeManager fdtm = FileDataTypeManager.createFileArchive(gdtFile);
|
||||
tx = -1;
|
||||
tx = fdtm.startTransaction("Import");
|
||||
try {
|
||||
tx = fdtm.startTransaction("Import");
|
||||
tmpFdtm.getAllDataTypes()
|
||||
.forEachRemaining(
|
||||
dt -> fdtm.addDataType(dt, DataTypeConflictHandler.DEFAULT_HANDLER));
|
||||
@@ -898,9 +893,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (tx != -1) {
|
||||
fdtm.endTransaction(tx, true);
|
||||
}
|
||||
fdtm.endTransaction(tx, true);
|
||||
}
|
||||
|
||||
fdtm.save();
|
||||
@@ -926,7 +919,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
return existingDT;
|
||||
}
|
||||
|
||||
|
||||
private List<DataType> createBootstrapFuncDefs(DataTypeManager destDTM, CategoryPath destCP,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
List<Function> funcs = getAllFunctions().stream()
|
||||
@@ -959,7 +951,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
private void moveAllDataTypesTo(DataTypeManager dtm, CategoryPath srcCP, CategoryPath destCP)
|
||||
throws DuplicateNameException, DataTypeDependencyException, InvalidNameException {
|
||||
Category srcCat = dtm.getCategory(srcCP);
|
||||
@@ -1182,7 +1173,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
.map(Entry::getKey)
|
||||
.collect(toSet());
|
||||
typeDupCount.clear();
|
||||
|
||||
|
||||
for (GoType goType : goTypes.values()) {
|
||||
String typeName = goType.getNameWithPackageString();
|
||||
if (dupedTypeNames.contains(typeName)) {
|
||||
@@ -1270,10 +1261,11 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
|
||||
StructureContext<T> structContext = getStructureContextOfInstance(structInstance);
|
||||
String fallbackName = defaultValue;
|
||||
fallbackName = fallbackName == null && structContext != null
|
||||
? "%s_%x".formatted(structContext.getMappingInfo().getStructureName(),
|
||||
structContext.getStructureStart())
|
||||
: "invalid_object";
|
||||
fallbackName =
|
||||
fallbackName == null && structContext != null
|
||||
? "%s_%x".formatted(structContext.getMappingInfo().getStructureName(),
|
||||
structContext.getStructureStart())
|
||||
: "invalid_object";
|
||||
return GoName.createFakeInstance(fallbackName);
|
||||
}
|
||||
|
||||
@@ -1296,7 +1288,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
return "unknown_type_%x".formatted(offset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link GoType} corresponding to an offset that is relative to the controlling
|
||||
* GoModuledata's typesOffset.
|
||||
@@ -1441,15 +1432,13 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
|
||||
private AddressRange getPclntabSearchRange() {
|
||||
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "rdata");
|
||||
return memBlock != null
|
||||
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
return memBlock != null ? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
: null;
|
||||
}
|
||||
|
||||
private AddressRange getModuledataSearchRange() {
|
||||
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "data");
|
||||
return memBlock != null
|
||||
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
return memBlock != null ? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -1489,7 +1478,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public Symbol getGoSymbol(String symbolName) {
|
||||
return getGoSymbol(program, symbolName);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class DataTypeCleaner implements Closeable {
|
||||
this.targetDtm = targetDtm;
|
||||
this.retainExistingComposites = retainExistingComposites;
|
||||
this.cleanerDtm = new StandAloneDataTypeManager("CleanerDTM");
|
||||
txId = cleanerDtm.startTransaction("CleanerTx");
|
||||
txId = cleanerDtm.startTransaction("Clean Datatypes");
|
||||
|
||||
ProgramArchitecture arch = targetDtm.getProgramArchitecture();
|
||||
if (arch != null) {
|
||||
|
||||
-15
@@ -40,7 +40,6 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog;
|
||||
import ghidra.app.plugin.core.stackeditor.StackEditorModel;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginException;
|
||||
@@ -488,20 +487,6 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration
|
||||
program.endTransaction(txId, saveChanges);
|
||||
}
|
||||
|
||||
protected class RestoreListener implements DomainObjectListener {
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent event) {
|
||||
if (event.contains(DomainObjectEvent.RESTORED)) {
|
||||
Object source = event.getSource();
|
||||
if (source instanceof DataTypeManagerDomainObject) {
|
||||
DataTypeManagerDomainObject restoredDomainObject =
|
||||
(DataTypeManagerDomainObject) source;
|
||||
provider.domainObjectRestored(restoredDomainObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class StatusListener extends CompositeEditorModelAdapter {
|
||||
String status = null;
|
||||
boolean beep = false;
|
||||
|
||||
+24
-46
@@ -108,11 +108,9 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
@@ -165,7 +163,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +170,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
// This should close the edit session.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedDt() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
@@ -197,7 +193,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
final Structure myS1Structure = s1Struct;
|
||||
|
||||
init(myS1Structure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the structure.
|
||||
runSwingLater(() -> {
|
||||
@@ -232,7 +227,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +234,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
// program so it goes away. This should close the edit session.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedDtComp() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
@@ -268,7 +261,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
assertTrue(s2.isEquivalent(myS2Structure));
|
||||
|
||||
init(myS2Structure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the structure.
|
||||
runSwing(() -> {
|
||||
@@ -303,7 +295,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,14 +302,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
// so it goes away. The editor stays since the structure existed previously, but editor reloads.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedComponentDtYes() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure myStruct = new StructureDataType("myStruct", 0);
|
||||
myStruct.add(new WordDataType());
|
||||
|
||||
init(emptyStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Add the data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
@@ -371,7 +360,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,14 +367,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
// so it goes away. The editor stays since the structure existed previously, but doesn't reload.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedComponentDtNo() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure myStruct = new StructureDataType("myStruct", 0);
|
||||
myStruct.add(new WordDataType());
|
||||
|
||||
init(emptyStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Add the data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
@@ -438,45 +424,37 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testUnModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
try {
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 4, 5 });
|
||||
deleteAction.actionPerformed(new DefaultActionContext());
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
}
|
||||
finally {
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 4, 5 });
|
||||
deleteAction.actionPerformed(new DefaultActionContext());
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-4
@@ -31,8 +31,7 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
public class StructureEditorUnlockedActions5Test
|
||||
extends AbstractStructureEditorTest {
|
||||
public class StructureEditorUnlockedActions5Test extends AbstractStructureEditorTest {
|
||||
|
||||
@Test
|
||||
public void testApplyDuplicateName() throws Exception {
|
||||
@@ -614,14 +613,14 @@ public class StructureEditorUnlockedActions5Test
|
||||
undo(program, false);
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
runSwing(() -> provider.domainObjectRestored(program), true);
|
||||
runSwing(() -> provider.dataTypeManagerRestored(), true);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals("myStruct", model.getCompositeName());
|
||||
redo(program, false);
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
runSwing(() -> provider.domainObjectRestored(program), true);
|
||||
runSwing(() -> provider.dataTypeManagerRestored(), true);
|
||||
waitForSwing();
|
||||
assertEquals("myStruct2", model.getCompositeName());
|
||||
|
||||
|
||||
+22
-32
@@ -83,11 +83,9 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
|
||||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
@@ -138,47 +136,39 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
|
||||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testUnModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
try {
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
delete(4, 5);
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
delete(4, 5);
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
assertFalse(complexUnion.isEquivalent(model.viewComposite));
|
||||
waitForTasks();
|
||||
assertFalse(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
}
|
||||
finally {
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
/* ###
|
||||
* 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.datamgr.editor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.DefaultActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.*;
|
||||
|
||||
/**
|
||||
* {@link AbstractEnumEditorUndoRedoTest} contains tests which should be applied to the various
|
||||
* {@link DataTypeManager} implementations which are responsible for setting {@code dtm} during
|
||||
* the setUp phase.
|
||||
*/
|
||||
public abstract class AbstractEnumEditorUndoRedoTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
protected Program program;
|
||||
protected DataTypeManagerPlugin plugin;
|
||||
protected PluginTool tool;
|
||||
protected TestEnv env;
|
||||
|
||||
protected DataTypeManager dtm; // must be set by test implementation during setUp
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
ToyProgramBuilder builder = new ToyProgramBuilder("notepad", true);
|
||||
builder.addCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
|
||||
program = builder.getProgram();
|
||||
|
||||
env = new TestEnv();
|
||||
tool = env.showTool(program);
|
||||
tool.addPlugin(DataTypeManagerPlugin.class.getName());
|
||||
plugin = getPlugin(tool, DataTypeManagerPlugin.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedo() throws Exception {
|
||||
|
||||
Enum enumDt = editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
// delete a row
|
||||
table.setRowSelectionInterval(0, 0);
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getDeleteAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
applyChanges(true);
|
||||
assertNull(enumDt.getName(0));
|
||||
|
||||
// undo
|
||||
undo(true);
|
||||
assertEquals("Red", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
|
||||
//redo
|
||||
redo(true);
|
||||
assertEquals("Pink", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRemoval() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
assertFalse(model.hasChanges());
|
||||
|
||||
undo(true); // will remove enum from DTM
|
||||
|
||||
DataType dt = dtm.getDataType("/Category1/Colors");
|
||||
assertNull(dt);
|
||||
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
assertEquals("Close Enum Editor?", d.getTitle());
|
||||
|
||||
JButton button = findButtonByText(d.getComponent(), "Continue Edit");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(panel.needsSave());
|
||||
|
||||
DockingActionIf applyAction = getApplyAction();
|
||||
assertTrue(applyAction.isEnabled());
|
||||
|
||||
applyChanges(true);
|
||||
|
||||
dt = dtm.getDataType("/Category1/Colors");
|
||||
assertNotNull(dt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoYes() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
int origRowCount = model.getRowCount();
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// yes to reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "Yes");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(origRowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoNo() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
int rowCount = model.getRowCount();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// not to not reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "No");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(rowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private EnumEditorPanel findEditorPanel(Window w) {
|
||||
Window[] windows = w.getOwnedWindows();
|
||||
for (Window window : windows) {
|
||||
if (window.isVisible() && JDialog.class.isAssignableFrom(window.getClass())) {
|
||||
Container c =
|
||||
findContainer(((JDialog) window).getContentPane(), EnumEditorPanel.class);
|
||||
if (c != null) {
|
||||
return (EnumEditorPanel) c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Container findContainer(Container parent, Class<?> theClass) {
|
||||
Component[] c = parent.getComponents();
|
||||
for (Component element : c) {
|
||||
if (theClass.isAssignableFrom(element.getClass())) {
|
||||
return (Container) element;
|
||||
}
|
||||
if (element instanceof Container) {
|
||||
Container container = findContainer((Container) element, theClass);
|
||||
if (container != null) {
|
||||
return container;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void applyChanges(boolean doWait) throws Exception {
|
||||
|
||||
DockingActionIf applyAction = getApplyAction();
|
||||
assertTrue(applyAction.isEnabled());
|
||||
Runnable r = () -> applyAction.actionPerformed(new DefaultActionContext());
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
dtm.flushEvents();
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
|
||||
}
|
||||
|
||||
private DockingActionIf getAddAction() {
|
||||
return getAction(plugin, "Add Enum Value");
|
||||
}
|
||||
|
||||
private DockingActionIf getApplyAction() {
|
||||
return getAction(plugin, "Apply Enum Changes");
|
||||
}
|
||||
|
||||
private DockingActionIf getDeleteAction() {
|
||||
return getAction(plugin, "Delete Enum Value");
|
||||
}
|
||||
|
||||
private Enum editSampleEnum() {
|
||||
|
||||
AtomicReference<Enum> enumRef = new AtomicReference<>();
|
||||
|
||||
dtm.withTransaction("Create Test Enum", () -> {
|
||||
|
||||
Category cat = dtm.createCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
|
||||
|
||||
Enum enumm = new EnumDataType("Colors", 1);
|
||||
enumm.add("Red", 0);
|
||||
enumm.add("Green", 0x10);
|
||||
enumm.add("Blue", 0x20);
|
||||
enumm.add("Purple", 5);
|
||||
enumm.add("Turquoise", 0x22);
|
||||
enumm.add("Pink", 2);
|
||||
enumm.setDescription("This is a set of Colors");
|
||||
|
||||
Enum enumDt = (Enum) cat.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
enumRef.set(enumDt);
|
||||
|
||||
dtm.flushEvents();
|
||||
waitForSwing();
|
||||
|
||||
runSwingLater(() -> plugin.edit(enumDt));
|
||||
});
|
||||
|
||||
waitForSwing();
|
||||
return enumRef.get();
|
||||
|
||||
}
|
||||
|
||||
private void undo(boolean doWait) throws Exception {
|
||||
Runnable r = () -> {
|
||||
try {
|
||||
undo();
|
||||
dtm.flushEvents();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
};
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void redo(boolean doWait) throws Exception {
|
||||
Runnable r = () -> {
|
||||
try {
|
||||
redo();
|
||||
dtm.flushEvents();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
};
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
abstract void undo() throws IOException;
|
||||
|
||||
abstract void redo() throws IOException;
|
||||
}
|
||||
-123
@@ -668,110 +668,6 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedo() throws Exception {
|
||||
|
||||
Enum enumDt = editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
// delete a row
|
||||
table.setRowSelectionInterval(0, 0);
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getDeleteAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
applyChanges(true);
|
||||
assertNull(enumDt.getName(0));
|
||||
// undo
|
||||
undo(program);
|
||||
assertEquals("Red", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
|
||||
//redo
|
||||
redo(program);
|
||||
assertEquals("Pink", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoYes() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
int origRowCount = model.getRowCount();
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// yes to reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "Yes");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(origRowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoNo() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
int rowCount = model.getRowCount();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// not to not reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "No");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(rowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
@@ -936,23 +832,4 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
return enumDt;
|
||||
}
|
||||
|
||||
private void undo(boolean doWait) throws Exception {
|
||||
Runnable r = () -> {
|
||||
try {
|
||||
program.undo();
|
||||
program.flushEvents();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
};
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/* ###
|
||||
* 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.datamgr.editor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
|
||||
public class FileArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
|
||||
|
||||
private File tempGdt;
|
||||
private Archive fileArchive;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
tempGdt = createTempFileForTest(".gdt");
|
||||
tempGdt.delete();
|
||||
|
||||
fileArchive = plugin.getDataTypeManagerHandler().createArchive(tempGdt);
|
||||
|
||||
assertTrue(fileArchive.isModifiable());
|
||||
|
||||
dtm = fileArchive.getDataTypeManager();
|
||||
}
|
||||
|
||||
@After
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
if (fileArchive != null) {
|
||||
plugin.getDataTypeManagerHandler().closeArchive(fileArchive);
|
||||
tempGdt.delete();
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
void undo() throws IOException {
|
||||
FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager();
|
||||
fileDtm.undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
void redo() throws IOException {
|
||||
FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager();
|
||||
fileDtm.redo();
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/* ###
|
||||
* 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.datamgr.editor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
public class ProgramEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
dtm = program.getDataTypeManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
void undo() throws IOException {
|
||||
program.undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
void redo() throws IOException {
|
||||
program.redo();
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/* ###
|
||||
* 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.datamgr.editor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.program.database.DataTypeArchiveDB;
|
||||
|
||||
public class ProjectArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
|
||||
|
||||
Archive projectArchive;
|
||||
DataTypeArchiveDB dataTypeArchiveDB;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
|
||||
|
||||
dataTypeArchiveDB = new DataTypeArchiveDB(rootFolder, "Test", tool);
|
||||
|
||||
projectArchive = plugin.getDataTypeManagerHandler().openArchive(dataTypeArchiveDB);
|
||||
|
||||
assertTrue(projectArchive.isModifiable());
|
||||
|
||||
dtm = dataTypeArchiveDB.getDataTypeManager();
|
||||
}
|
||||
|
||||
@After
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
if (projectArchive != null) {
|
||||
plugin.getDataTypeManagerHandler().closeArchive(projectArchive);
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
void undo() throws IOException {
|
||||
dataTypeArchiveDB.undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
void redo() throws IOException {
|
||||
dataTypeArchiveDB.redo();
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -22,8 +22,6 @@ import javax.swing.JTextField;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.program.model.data.DataTypeManagerDomainObject;
|
||||
import ghidra.program.model.data.Pointer;
|
||||
|
||||
public class PositiveStackEditorProviderTest extends AbstractStackEditorTest {
|
||||
@@ -113,39 +111,4 @@ public class PositiveStackEditorProviderTest extends AbstractStackEditorTest {
|
||||
assertEquals(0x4, stackModel.getParameterSize());
|
||||
}
|
||||
|
||||
// public void testIncreasePosReturnAddrOffset() throws Exception {
|
||||
// init(SIMPLE_STACK);
|
||||
// assertEquals(0x20, stackModel.getFrameSize());
|
||||
// assertEquals(0x0, stackModel.getReturnAddressOffset());
|
||||
// assertEquals(0x12, stackModel.getLocalSize());
|
||||
// assertEquals(-0x8, stackModel.getParameterOffset());
|
||||
// assertEquals(0x7, stackModel.getParameterSize());
|
||||
// }
|
||||
//
|
||||
// public void testDecreasePosReturnAddrOffset() throws Exception {
|
||||
// init(SIMPLE_STACK);
|
||||
// assertEquals(0x20, stackModel.getFrameSize());
|
||||
// assertEquals(0x0, stackModel.getReturnAddressOffset());
|
||||
// assertEquals(0x12, stackModel.getLocalSize());
|
||||
// assertEquals(-0x8, stackModel.getParameterOffset());
|
||||
// assertEquals(0x7, stackModel.getParameterSize());
|
||||
// }
|
||||
|
||||
protected class RestoreListener implements DomainObjectListener {
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObjectListener#domainObjectChanged(ghidra.framework.model.DomainObjectChangedEvent)
|
||||
*/
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent event) {
|
||||
if (event.contains(DomainObjectEvent.RESTORED)) {
|
||||
Object source = event.getSource();
|
||||
if (source instanceof DataTypeManagerDomainObject) {
|
||||
DataTypeManagerDomainObject restoredDomainObject =
|
||||
(DataTypeManagerDomainObject) source;
|
||||
provider.domainObjectRestored(restoredDomainObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -1119,6 +1119,11 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care for now
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
// don't care for now
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomDataType extends StructureDataType {
|
||||
|
||||
@@ -910,5 +910,10 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -476,13 +476,21 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter implemen
|
||||
@Override
|
||||
public void invalidate() {
|
||||
clearCache(false);
|
||||
super.invalidate();
|
||||
super.invalidate(); // fires RESTORED event
|
||||
}
|
||||
|
||||
protected void clearCache(boolean all) {
|
||||
options.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this domain object has been restored to a completely different state due
|
||||
* to a transaction undo/redo/rollback or a database merge operation.
|
||||
*/
|
||||
protected void domainObjectRestored() {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean canSave() {
|
||||
DomainFile df = getDomainFile();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user