From 2cefdb76888c19e482d46dfa966b2e6a89bfca05 Mon Sep 17 00:00:00 2001
From: dragonmacher <48328597+dragonmacher@users.noreply.github.com>
Date: Fri, 28 Jan 2022 15:59:35 -0500
Subject: [PATCH] GP-1627 - Data Types - Updated the Paste Action to work when
pasting ont a data type node in the Data Types provider
---
Ghidra/Features/Base/certification.manifest | 1 -
.../data_type_manager_description.htm | 34 +-
.../data_type_manager_window.html | 68 ++-
.../core/datamgr/DataTypesProvider.java | 4 +-
.../core/datamgr/actions/PasteAction.java | 42 +-
.../actions/ReplaceDataTypeAction.java | 138 +++++
.../archive/DataTypeManagerHandler.java | 74 +--
.../tree/DataTypeDragNDropHandler.java | 75 ++-
.../core/datamgr/tree/DataTypeNode.java | 8 +-
.../core/datamgr/tree/DataTypeTreeNode.java | 1 +
.../util/DataTypeTreeCopyMoveTask.java | 176 +------
.../datamgr/DataTypeCopyMoveDragTest.java | 480 +++++++++++-------
.../datamgr/DataTypeManagerPluginTest.java | 7 +-
.../ghidra/program/model/data/Category.java | 81 +--
14 files changed, 687 insertions(+), 502 deletions(-)
create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java
diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest
index 916c438c64..ec24544237 100644
--- a/Ghidra/Features/Base/certification.manifest
+++ b/Ghidra/Features/Base/certification.manifest
@@ -899,7 +899,6 @@ src/main/resources/help/Dummy_HelpSet.hs||GHIDRA||reviewed||END|
src/main/resources/help/empty.htm||GHIDRA||||END|
src/main/resources/images/2leftarrow.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
src/main/resources/images/2rightarrow.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
-src/main/resources/images/ArmedMarkSelection.png||GHIDRA||||END|
src/main/resources/images/Array.png||GHIDRA||||END|
src/main/resources/images/B.gif||GHIDRA||||END|
src/main/resources/images/BookShelf.png||GHIDRA||||END|
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm
index 01daa93f42..0f16964181 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm
@@ -18,13 +18,6 @@
program. Allowing the user to build libraries of data types and to share them between programs,
projects, and different users is a long term goal for Ghidra.
- Prior to Ghidra 4.3, sharing data types even between programs in the same project was very
- difficult. (users would have to drag data types, one at a time, from one program or archive to
- another to propagate changes to data types). As of Ghidra 4.3, significant progress has been
- made to make sharing data types easier, but the inherent complexity of this problem requires
- users to understand a number of basic concepts to take advantage of the new features.
-
-
Topics
@@ -1008,7 +1001,7 @@
Rename the data type that you are dragging to have ".conflict" appended to
it to make a unique name.
- Replace the existing data type with the one you are dragging (or, pasting);
+ Replace the existing data type with the new one;
this means any use of the existing data types is replaced with the new
data type; the existing data type is deleted.
@@ -1023,30 +1016,9 @@
A data type can be replaced by another data type. This means that every occurrence of
the original data type in a program is replaced by the new data type and the original
- data type is deleted. There are two ways to replace a data type.
+ data type is deleted. To replace a data type, right-click on the type to be replaced
+ and select the Replace Data Type... action.
-
-
-
- 1. Drag-N-Drop |
-
- Click on the replacement data type and drag it
- onto the data type to be replaced. |
-
-
-
- 2. Copy/Paste |
-
- Right-click on the replacement data type to be
- moved and select the Cut action. Next, right-click on the data type
- to be replaced and select the Paste action. |
-
-
-
-
-
-
- Either way, a confirmation dialog will appear.
Setting Favorite Data Types
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_window.html b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_window.html
index 7e70c4580f..0a67847708 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_window.html
+++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_window.html
@@ -524,6 +524,21 @@
Miscellaneous Actions
+
+ Copy
+
+
+ The Copy action can be be used to
+ copy
+ selected data types and/or
+ copy selected categories.
+ The Copy action only primes the selected nodes to be copied. The
+ Paste action must be used to complete the copy. Any other
+ Cut or Copy action will cancel the previousCopy
+ .
+
+
+
Cut
@@ -538,27 +553,31 @@
"data_type_manager_description.htm#ReplaceDataType">replace one data type for another.
- Copy
+ Delete
- The Copy action can be be used to
- copy
- selected data types and/or
- copy selected categories.
- The Copy action only primes the selected nodes to be copied. The
- Paste action must be used to complete the copy. Any other
- Cut or Copy action will cancel the previousCopy
- .
-
+ The Delete action is used to
+ delete data
+ types and/or
+ delete categories. A confirmation dialog
+ will appear before actually deleting the selected data types and categories.
+
Paste
- ThePaste action is used to complete a move or copy operation as
- initiated by either the Copy or Cut action. The node that
- is selected will be the destination for whatever nodes where selected when the
- Copy or Paste was invoked.
+ The Paste action is used to complete a move or copy operation as
+ initiated by either the Copy or Cut action. The category
+ node (or the parent category of a data type node) that is selected will be the
+ destination for whatever nodes where selected when the Copy or
+ Paste was invoked.
+
+
+ Any conflicts encountered while pasting will be resolved according to the current
+ conflict resolution mode.
+
+
Rename
@@ -571,26 +590,29 @@
type.
- Delete
+
+ Replace Data Type...
- The Delete action is used to
- delete data
- types and/or
- delete categories. A confirmation dialog
- will appear before actually deleting the selected data types and categories.
+ The Replace Data Type... action is used to
+ replace a
+ selected data type and all occurrences in the program.
+
+
+
- Collapse All
+
+
+
+ Collapse All
To collapse all open nodes in a sub-tree, right-click on the root node of the
sub-tree and select the Collapse All action. If this action is invoked
via the local toolbar, then the entire tree is collapsed.
-
-
Expand All
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java
index d89903da13..d7f8745fa9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java
@@ -148,6 +148,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
addLocalAction(new CutAction(plugin));
addLocalAction(new CopyAction(plugin));
addLocalAction(new PasteAction(plugin));
+ addLocalAction(new ReplaceDataTypeAction(plugin));
addLocalAction(new DeleteAction(plugin));
addLocalAction(new DeleteArchiveAction(plugin));
addLocalAction(new RenameAction(plugin));
@@ -181,8 +182,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
addLocalAction(new FindDataTypesBySizeAction(plugin, "2"));
addLocalAction(new FindStructuresByOffsetAction(plugin, "3"));
addLocalAction(new FindStructuresBySizeAction(plugin, "4"));
- includeDataMembersInSearchAction =
- new IncludeDataTypesInFilterAction(plugin, this, "5");
+ includeDataMembersInSearchAction = new IncludeDataTypesInFilterAction(plugin, this, "5");
addLocalAction(includeDataMembersInSearchAction);
addLocalAction(new ApplyFunctionDataTypesAction(plugin)); // Tree
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java
index 90c77c2ada..2a74d5f445 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java
@@ -48,9 +48,9 @@ public class PasteAction extends DockingAction {
this.clipboard = plugin.getClipboard();
this.tool = plugin.getTool();
setPopupMenuData(new MenuData(new String[] { "Paste" }, "Edit"));
- setKeyBindingData(new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V,
- InputEvent.CTRL_DOWN_MASK), KeyBindingPrecedence.ActionMapLevel));
- setEnabled(true);
+ setKeyBindingData(
+ new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
+ KeyBindingPrecedence.ActionMapLevel));
}
@Override
@@ -65,7 +65,7 @@ public class PasteAction extends DockingAction {
@Override
public boolean isEnabledForContext(ActionContext context) {
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
- if (!(node instanceof CategoryNode) || !((CategoryNode) node).isEnabled()) {
+ if (node == null) {
return false;
}
List transferNodeList = getNodesFromClipboard();
@@ -96,7 +96,7 @@ public class PasteAction extends DockingAction {
return false;
}
- if (invalidCutNodes(destinationNode, transferNodeList)) {
+ if (hasInvalidCutNodes(destinationNode, transferNodeList)) {
return false; // cut nodes that cannot be pasted here
}
@@ -109,13 +109,14 @@ public class PasteAction extends DockingAction {
}
DataFlavor[] flavors = handler.getSupportedDataFlavors(transferNodeList);
- return handler.isDropSiteOk(destinationNode, flavors, DnDConstants.ACTION_COPY);
+ return handler.isValidDataTypeDestination(destinationNode, flavors,
+ DnDConstants.ACTION_COPY);
}
- private boolean invalidCutNodes(DataTypeTreeNode destinationNode, List nodeList) {
+ private boolean hasInvalidCutNodes(DataTypeTreeNode destinationNode, List nodeList) {
DataTypeTreeNode node = (DataTypeTreeNode) nodeList.get(0);
if (!node.isCut()) {
- return false; // hasn't been cut, no problemo
+ return false; // hasn't been cut, no problem
}
// can't cut nodes from one archive and paste into another
@@ -142,28 +143,37 @@ public class PasteAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
- GTree gTree = (GTree) context.getContextObject();
-
- TreePath[] selectionPaths = gTree.getSelectionPaths();
- GTreeNode destinationNode = (GTreeNode) selectionPaths[0].getLastPathComponent();
List nodeList = getNodesFromClipboard();
if (nodeList.isEmpty()) {
return;
}
+
+ GTree gTree = (GTree) context.getContextObject();
+ TreePath[] selectionPaths = gTree.getSelectionPaths();
+ CategoryNode destinationNode = getDropTargetNode(selectionPaths);
DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) nodeList.get(0);
if (dataTypeTreeNode.isCut()) { // clear cut nodes on paste operation
clipboard.setContents(null, null);
}
ActionType actionType = getActionType(dataTypeTreeNode);
- DataTypeTreeCopyMoveTask task =
- new DataTypeTreeCopyMoveTask(destinationNode, nodeList, actionType,
- (DataTypeArchiveGTree) gTree,
- plugin.getConflictHandler());
+ DataTypeTreeCopyMoveTask task = new DataTypeTreeCopyMoveTask(destinationNode, nodeList,
+ actionType, (DataTypeArchiveGTree) gTree, plugin.getConflictHandler());
tool.execute(task, 250);
}
+ private CategoryNode getDropTargetNode(TreePath[] selectionPaths) {
+
+ // clients can drop/paste onto a category or archive
+ GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
+ if (node instanceof CategoryNode) {
+ return (CategoryNode) node;
+ }
+
+ return (CategoryNode) node.getParent();
+ }
+
private ActionType getActionType(DataTypeTreeNode pasteNode) {
if (pasteNode.isCut()) {
return ActionType.MOVE;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java
new file mode 100644
index 0000000000..fbb4e47625
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java
@@ -0,0 +1,138 @@
+/* ###
+ * 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 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.archive.Archive;
+import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
+import ghidra.app.plugin.core.datamgr.tree.*;
+import ghidra.app.util.datatype.DataTypeSelectionDialog;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.model.data.*;
+import ghidra.util.Msg;
+import ghidra.util.data.DataTypeParser.AllowedDataTypes;
+
+/**
+ * Replace the selected data type with the chosen data type
+ */
+public class ReplaceDataTypeAction extends DockingAction {
+
+ private DataTypeManagerPlugin plugin;
+
+ public ReplaceDataTypeAction(DataTypeManagerPlugin plugin) {
+ super("Replace Data Type", plugin.getName());
+
+ this.plugin = plugin;
+ setPopupMenuData(new MenuData(new String[] { "Replace Data Type..." }, "Edit"));
+ }
+
+ @Override
+ public boolean isAddToPopup(ActionContext context) {
+ DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
+ if (node instanceof BuiltInArchiveNode) {
+ return false;
+ }
+ return (node != null);
+ }
+
+ @Override
+ public boolean isEnabledForContext(ActionContext context) {
+ DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
+ if (node == null) {
+ return false;
+ }
+
+ if (!(node instanceof DataTypeNode)) {
+ return false;
+ }
+ return node.isModifiable();
+ }
+
+ private DataTypeTreeNode getSelectedDataTypeTreeNode(ActionContext context) {
+ if (!(context instanceof DataTypesActionContext)) {
+ return null;
+ }
+
+ GTree gTree = (GTree) context.getContextObject();
+ TreePath[] selectionPaths = gTree.getSelectionPaths();
+ if (selectionPaths == null || selectionPaths.length == 0) {
+ return null;
+ }
+
+ if (selectionPaths.length > 1) {
+ return null;
+ }
+
+ DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent();
+ return node;
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+
+ PluginTool tool = plugin.getTool();
+ int noSizeRestriction = -1;
+ DataTypeSelectionDialog selectionDialog = new DataTypeSelectionDialog(tool,
+ plugin.getProgram().getDataTypeManager(), noSizeRestriction, AllowedDataTypes.ALL);
+ tool.showDialog(selectionDialog);
+ DataType newDt = selectionDialog.getUserChosenDataType();
+ if (newDt == null) {
+ return; // cancelled
+ }
+
+ DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
+
+ DataTypeManagerHandler dtmHandler = plugin.getDataTypeManagerHandler();
+ DataTypeManager dtm = newDt.getDataTypeManager();
+ Archive sourceArchive = dtmHandler.getArchive(dtm);
+ Archive destinationArchive = findArchive(node);
+
+ DataType oldDt = ((DataTypeNode) node).getDataType();
+ if (sourceArchive != destinationArchive) {
+ oldDt = oldDt.clone(oldDt.getDataTypeManager());
+ }
+
+ int txId = dtm.startTransaction("Replace Data Type");
+ try {
+ dtm.replaceDataType(oldDt, newDt, true);
+ }
+ catch (DataTypeDependencyException e) {
+ Msg.showError(this, null, "Replace Failed", "Replace failed. Existing type " + newDt +
+ "; replacment type " + oldDt + ". " + e.getMessage());
+ }
+ finally {
+ dtm.endTransaction(txId, true);
+ }
+ }
+
+ private Archive findArchive(GTreeNode node) {
+ while (node != null) {
+ if (node instanceof ArchiveNode) {
+ return ((ArchiveNode) node).getArchive();
+ }
+ node = node.getParent();
+ }
+ return null;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java
index 8fdf767d83..f005ca919b 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java
@@ -585,8 +585,8 @@ public class DataTypeManagerHandler {
dataTypeManager.updateSourceArchiveName(existingDataTypeManager.getUniversalID(),
existingDataTypeManager.getName());
- int existingTxID = existingDataTypeManager.startTransaction(
- "Update Data Type Source Archive Name");
+ int existingTxID = existingDataTypeManager
+ .startTransaction("Update Data Type Source Archive Name");
try {
existingDataTypeManager.updateSourceArchiveName(
dataTypeManager.getUniversalID(), dataTypeManager.getName());
@@ -686,7 +686,7 @@ public class DataTypeManagerHandler {
Msg.info(this, "Closed archive: '" + archive.getName() + "'");
}
- private Archive getArchive(DataTypeManager dtm) {
+ public Archive getArchive(DataTypeManager dtm) {
for (Archive archive : openArchives) {
DataTypeManager dataTypeManager = archive.getDataTypeManager();
if (dataTypeManager.equals(dtm)) {
@@ -814,43 +814,43 @@ public class DataTypeManagerHandler {
private void initializeFavorites() {
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(TerminatedStringDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(TerminatedUnicodeDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(LongDoubleDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(UnsignedIntegerDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(UnsignedLongDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
- builtInDataTypesManager.setFavorite(
- builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
+ builtInDataTypesManager
+ .setFavorite(builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
}
@@ -900,7 +900,7 @@ public class DataTypeManagerHandler {
}
saveState.putStrings(RECENT_NAMES, getSaveableArchiveNames(recentMenuList));
- // update the initialArchives list so that future checks on that list do not trigger a
+ // update the initialArchives list so that future checks on that list do not trigger a
// state change
initiallyOpenedFileArchiveNames = getOpenFileArchiveNames(openArchives);
}
@@ -957,11 +957,11 @@ public class DataTypeManagerHandler {
/**
* Determine if we can remember the specified project archive using a simple project path
- * (e.g., we can't remember specific versions).
+ * (e.g., we can't remember specific versions).
* @param pa project archive
- * @param activeProjectOnly if true pa must be contained within the
+ * @param activeProjectOnly if true pa must be contained within the
* active project to be remembered.
- * @return return project path which can be remembered or null
+ * @return return project path which can be remembered or null
*/
public String getProjectPathname(ProjectArchive pa, boolean activeProjectOnly) {
// Project archives are always opened by a user.
@@ -1227,7 +1227,7 @@ public class DataTypeManagerHandler {
}
/**
- * Signals to this manager to save the knowledge of all currently opened archives and to mark
+ * Signals to this manager to save the knowledge of all currently opened archives and to mark
* the tool as dirty (changed) if the current open archives are not the same as those that
* were initially opened.
*/
@@ -1428,8 +1428,8 @@ public class DataTypeManagerHandler {
new DataTreeDialog(null, "Save As", DataTreeDialog.SAVE, domainFileFilter);
dataTreeSaveDialog.addOkActionListener(listener);
- dataTreeSaveDialog.setHelpLocation(
- new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
+ dataTreeSaveDialog
+ .setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
}
return dataTreeSaveDialog;
}
@@ -1614,8 +1614,8 @@ public class DataTypeManagerHandler {
@Override
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
- if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
- file.getContentType())) {
+ if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
+ .equals(file.getContentType())) {
return;
}
Iterator archiveIter = openArchives.iterator();
@@ -1658,8 +1658,8 @@ public class DataTypeManagerHandler {
@Override
public void domainFileRenamed(DomainFile file, String oldName) {
- if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
- file.getContentType())) {
+ if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
+ .equals(file.getContentType())) {
return;
}
String newName = file.getName();
@@ -1726,7 +1726,7 @@ public class DataTypeManagerHandler {
/**
* Provides an exception handler for a failed attempt to open an datatype archive file.
- * This method will display exception information to the user and/or log.
+ * This method will display exception information to the user and/or log.
* @param plugin datatype manager plugin
* @param archiveFile archive file resource being opened
* @param t throwable
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java
index 1dbeca3bc4..4da07d673f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java
@@ -37,11 +37,11 @@ import ghidra.util.task.Task;
public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
private static DataFlavor localDataTypeTreeFlavor = createLocalTreeNodeFlavor();
- public static DataFlavor[] allSupportedFlavors = { DataTypeTransferable.localDataTypeFlavor,
- localDataTypeTreeFlavor };
+ public static DataFlavor[] allSupportedFlavors =
+ { DataTypeTransferable.localDataTypeFlavor, localDataTypeTreeFlavor };
- public static DataFlavor[] builtinFlavors = { DataTypeTransferable.localBuiltinDataTypeFlavor,
- localDataTypeTreeFlavor };
+ public static DataFlavor[] builtinFlavors =
+ { DataTypeTransferable.localBuiltinDataTypeFlavor, localDataTypeTreeFlavor };
public static DataFlavor[] restrictedFlavors = { localDataTypeTreeFlavor };
@@ -52,8 +52,9 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
// create a data flavor that is an List of GTreeNodes
private static DataFlavor createLocalTreeNodeFlavor() {
try {
- return new GenericDataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
- "; class=java.util.List", "Local list of Drag/Drop DataType Tree objects");
+ return new GenericDataFlavor(
+ DataFlavor.javaJVMLocalObjectMimeType + "; class=java.util.List",
+ "Local list of Drag/Drop DataType Tree objects");
}
catch (Exception e) {
Msg.showError(DataTypeDragNDropHandler.class, null, null, null, e);
@@ -75,12 +76,12 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
if (list.contains(destinationNode)) { // don't allow drop on dragged nodes.
return;
}
+
+ CategoryNode updatedDestinationNode = getDropTargetNode(destinationNode);
ActionType actionType =
dropAction == DnDConstants.ACTION_COPY ? ActionType.COPY : ActionType.MOVE;
- Task task =
- new DataTypeTreeCopyMoveTask(destinationNode, list, actionType,
- (DataTypeArchiveGTree) tree,
- plugin.getConflictHandler());
+ Task task = new DataTypeTreeCopyMoveTask(updatedDestinationNode, list, actionType,
+ (DataTypeArchiveGTree) tree, plugin.getConflictHandler());
plugin.getTool().execute(task, 250);
}
catch (UnsupportedFlavorException e) {
@@ -91,6 +92,16 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
}
}
+ private CategoryNode getDropTargetNode(GTreeNode node) {
+
+ // clients can drop/paste onto a category or archive
+ if (node instanceof CategoryNode) {
+ return (CategoryNode) node;
+ }
+
+ return (CategoryNode) node.getParent();
+ }
+
@Override
public DataFlavor[] getSupportedDataFlavors(List draggedNodes) {
// single, datatype node supports both datatype dragging *and* local tree dragging
@@ -150,12 +161,38 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
@Override
public boolean isDropSiteOk(GTreeNode destinationNode, DataFlavor[] flavors, int dropAction) {
- // can't drop on the root node
+
+ if (!isValidDataTypeDestination(destinationNode, flavors, dropAction)) {
+ return false;
+ }
+
+ GTreeNode updatedDestinationNode = getDropTargetNode(destinationNode);
+
+ if (isDroppingBuiltin(flavors)) {
+ if (!isValidBuiltinDropSite(updatedDestinationNode)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Verifies the given destination node can accept the given drop/copy/paste action and content
+ * flavors.
+ * @param destinationNode the node accepting the action
+ * @param flavors the supported flavors of the action
+ * @param dropAction the actual action see {@link DnDConstants}
+ * @return true if valid
+ */
+ public boolean isValidDataTypeDestination(GTreeNode destinationNode, DataFlavor[] flavors,
+ int dropAction) {
+ // can't drop/paste on the root node
if (destinationNode == null || destinationNode.getParent() == null) {
return false;
}
- // can only drop nodes from other dataTypetrees
+ // can only drop/paste nodes from other dataType trees
if (!containsFlavor(flavors, localDataTypeTreeFlavor)) {
return false;
}
@@ -167,20 +204,6 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
return false;
}
- // only a single datatype node can be dropped on a datatype node.
- if (destinationNode instanceof DataTypeNode) {
- if (!containsFlavor(flavors, DataTypeTransferable.localDataTypeFlavor) &&
- !containsFlavor(flavors, DataTypeTransferable.localBuiltinDataTypeFlavor)) {
- return false;
- }
- }
-
- if (isDroppingBuiltin(flavors)) {
- if (!isValidBuiltinDropSite(destinationNode)) {
- return false;
- }
- }
-
return true;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java
index 0f8a6b1c13..c82ac41815 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java
@@ -204,11 +204,7 @@ public class DataTypeNode extends DataTypeTreeNode {
@Override
public boolean canPaste(List pastedNodes) {
- if (pastedNodes.size() != 1) {
- return false;
- }
- GTreeNode pastedNode = pastedNodes.get(0);
- return pastedNode instanceof DataTypeNode;
+ return isModifiable();
}
@Override
@@ -252,7 +248,7 @@ public class DataTypeNode extends DataTypeTreeNode {
@Override
public String getDisplayText() {
- // note: we have to check the name each time, as the optional underlying
+ // note: we have to check the name each time, as the optional underlying
// source archive may have changed.
String currentDisplayText = getCurrentDisplayText();
if (!displayText.equals(currentDisplayText)) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java
index 182ebc471b..4f15b1e1bc 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java
@@ -34,6 +34,7 @@ public abstract class DataTypeTreeNode extends GTreeLazyNode {
/**
* Returns true if this nodes handles paste operations
+ * @param pastedNodes the nodes to be pasted
* @return true if this nodes handles paste operations
*/
public abstract boolean canPaste(List pastedNodes);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java
index 7e1d03ae77..f554d1df7d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java
@@ -22,8 +22,6 @@ import java.util.regex.Pattern;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeState;
-import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
-import ghidra.app.plugin.core.datamgr.DataTypeSynchronizer;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
import ghidra.app.plugin.core.datamgr.tree.*;
@@ -39,7 +37,7 @@ import ghidra.util.task.TaskMonitor;
public class DataTypeTreeCopyMoveTask extends Task {
// If the total number of nodes is small, we won't need to collapse the tree before deleting
- // the nodes to avoid excess tree events. This number is very arbitrary. This number is
+ // the nodes to avoid excess tree events. This number is very arbitrary. This number is
// used to compare the number of dragged nodes, which may include categories whose child
// count is not reflected in this number. This could mean that thousands of nodes will be
// processed, but the actual drag count could be much less.
@@ -50,7 +48,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
private DataTypeArchiveGTree gTree;
- private GTreeNode destinationNode;
+ private CategoryNode destinationNode;
private List droppedNodes;
private Archive sourceArchive;
private Archive destinationArchive;
@@ -64,7 +62,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
super("Drag/Drop", true, true, true);
}
- public DataTypeTreeCopyMoveTask(GTreeNode destinationNode, List droppedNodeList,
+ public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List droppedNodeList,
ActionType actionType, DataTypeArchiveGTree gTree,
DataTypeConflictHandler conflictHandler) {
super("Drag/Drop", true, true, true);
@@ -100,10 +98,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
//
- // Note: we collapse the node before performing this work because there is a
+ // Note: we collapse the node before performing this work because there is a
// potential for a large number of events to be generated. Further, if the
// given archive node has many children (like 10s of thousands), then the
- // copious events generated herein could lock the UI. By closing the node,
+ // copious events generated herein could lock the UI. By closing the node,
// the tree is not invalidating/validating its cache as a result of these
// events.
//
@@ -118,7 +116,6 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
doCopy(monitor);
-
}
catch (CancelledException e) {
return; // nothing to report
@@ -161,12 +158,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
DataTypeManager dtm = destinationArchive.getDataTypeManager();
int txId = dtm.startTransaction("Copy/Move Category/DataType");
try {
- if (destinationNode instanceof DataTypeNode) {
- dragNodeToDataType();
- }
- else {
- dragNodesToCategory(monitor);
- }
+ dragNodesToCategory(monitor);
}
finally {
dtm.endTransaction(txId, true);
@@ -189,8 +181,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
}
- private void associateDataTypes(TaskMonitor monitor)
- throws CancelledException {
+ private void associateDataTypes(TaskMonitor monitor) throws CancelledException {
if (!promptToAssociateTypes(monitor)) {
return;
@@ -347,22 +338,19 @@ public class DataTypeTreeCopyMoveTask extends Task {
DataTypeManager nodeDtm = dataType.getDataTypeManager();
boolean sameManager = (dtm == nodeDtm);
DataType newDt = !sameManager ? dataType.clone(nodeDtm) : dataType.copy(nodeDtm);
- if (sameManager &&
- newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
+ if (sameManager && newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
renameAsCopy(toCategory, newDt);
}
DataType resolvedDt = toCategory.addDataType(newDt, conflictHandler);
if (resolvedDt instanceof Pointer || resolvedDt instanceof Array ||
- resolvedDt instanceof BuiltInDataType ||
- resolvedDt instanceof MissingBuiltInDataType) {
+ resolvedDt instanceof BuiltInDataType || resolvedDt instanceof MissingBuiltInDataType) {
return;
}
if (!resolvedDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
- errors.add(
- "Data type copy failed. Another copy of this data type already exists at " +
- resolvedDt.getPathName());
+ errors.add("Data type copy failed. Another copy of this data type already exists at " +
+ resolvedDt.getPathName());
}
}
@@ -417,17 +405,15 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
}
- private void moveCategory(Category toCategory, Category category,
- TaskMonitor monitor) {
+ private void moveCategory(Category toCategory, Category category, TaskMonitor monitor) {
if (category.getParent() == toCategory) { // moving to same place
return;
}
try {
CategoryPath path = toCategory.getCategoryPath();
if (path.isAncestorOrSelf(category.getCategoryPath())) {
- errors.add(
- "Cannot move a parent node onto a child node. Moving " + category + " to " +
- toCategory);
+ errors.add("Cannot move a parent node onto a child node. Moving " + category +
+ " to " + toCategory);
return;
}
@@ -441,30 +427,25 @@ public class DataTypeTreeCopyMoveTask extends Task {
private void moveDataType(Category toCategory, DataType dataType) {
if (dataType.getCategoryPath().equals(toCategory.getCategoryPath())) {
- errors.add(
- "Move failed. DataType is already in this category. Category " + toCategory +
- "; Data type: " + dataType);
+ errors.add("Move failed. DataType is already in this category. Category " +
+ toCategory + "; Data type: " + dataType);
return;
}
try {
toCategory.moveDataType(dataType, conflictHandler);
}
catch (DataTypeDependencyException e) {
- errors.add(
- "Move failed. DataType is already in this category. Category " + toCategory +
- "; Data type: " + dataType + ". " + e.getMessage());
+ errors.add("Move failed. DataType is already in this category. Category " +
+ toCategory + "; Data type: " + dataType + ". " + e.getMessage());
}
}
- private void copyCategory(Category toCategory, Category category,
- TaskMonitor monitor) {
+ private void copyCategory(Category toCategory, Category category, TaskMonitor monitor) {
CategoryPath toPath = toCategory.getCategoryPath();
- boolean sameManager =
- (toCategory.getDataTypeManager() == category.getDataTypeManager());
+ boolean sameManager = (toCategory.getDataTypeManager() == category.getDataTypeManager());
if (sameManager && toPath.isAncestorOrSelf(category.getCategoryPath())) {
- errors.add("Copy failed. " +
- "Cannot copy a parent node onto a child node. Moving " + category + " to " +
- toCategory);
+ errors.add("Copy failed. " + "Cannot copy a parent node onto a child node. Moving " +
+ category + " to " + toCategory);
return;
}
toCategory.copyCategory(category, conflictHandler, monitor);
@@ -481,32 +462,11 @@ public class DataTypeTreeCopyMoveTask extends Task {
"Expected node to be either an ArchiveNode or CategoryNode but was " + node.getClass());
}
- private boolean isAssociatedEitherWay(DataType dt1, DataType dt2) {
- return isAssociated(dt1, dt2) || isAssociated(dt2, dt1);
- }
-
- private boolean isAssociated(DataType sourceDt, DataType destinationDt) {
- UniversalID destinationID = destinationDt.getUniversalID();
- if (destinationID == null || !destinationID.equals(sourceDt.getUniversalID())) {
- return false;
- }
- if (!haveSameSourceArchive(sourceDt, destinationDt)) {
- return false;
- }
- return isLocal(sourceDt);
- }
-
- private boolean haveSameSourceArchive(DataType dt1, DataType dt2) {
- SourceArchive s1 = dt1.getSourceArchive();
- SourceArchive s2 = dt2.getSourceArchive();
- return s1.getSourceArchiveID().equals(s2.getSourceArchiveID());
- }
-
/**
* Returns true if the given data type's source archive is the same as it's current data
- * type manager. This is false if copying a new type from the program to an
- * external archive.
- *
+ * type manager. This is false if copying a new type from the program to an
+ * external archive.
+ *
* @param dt the type
* @return true if the given type already lives in its source archive
*/
@@ -516,96 +476,12 @@ public class DataTypeTreeCopyMoveTask extends Task {
return sourceId.equals(dtmId);
}
- private void dragNodeToDataType() {
- DataType destinationDt = ((DataTypeNode) destinationNode).getDataType();
-
- // there must be exactly one and it must be a dataTypeNode, because of isValidDropSite()
- GTreeNode node = droppedNodes.get(0);
- DataType replacementDt = ((DataTypeNode) node).getDataType();
- if (sourceArchive != destinationArchive) {
- if (isAssociatedEitherWay(replacementDt, destinationDt)) {
- handleAssociatedType(destinationDt, replacementDt);
- return;
- }
-
- replacementDt = replacementDt.clone(replacementDt.getDataTypeManager());
- }
- else if (actionType == ActionType.COPY) { // Copy within a single data type manager.
- replacementDt = replacementDt.copy(replacementDt.getDataTypeManager());
- }
-
- replaceDataType(destinationDt, replacementDt);
- }
-
- private void handleAssociatedType(DataType destinationDt, DataType replacementDt) {
- if (isLocal(destinationDt)) {
- DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(replacementDt,
- destinationDt.getDataTypeManager());
- if (!syncInfo.canCommit()) {
- Msg.showInfo(getClass(), gTree, "Commit Data Type",
- "No changes to commit");
- }
- // destination data-type is local to an archive
- else if (confirmCommit()) {
- // if the destination dataType is local to its dataTypeManager
- // then we are committing.
- DataTypeSynchronizer.commit(destinationDt.getDataTypeManager(), replacementDt);
- }
- }
- else { // else we are updating
- DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(destinationDt,
- replacementDt.getDataTypeManager());
- if (!syncInfo.canUpdate()) {
- Msg.showInfo(getClass(), gTree, "Update Data Type", "No changes to copy");
- }
- else if (confirmUpdate()) {
- DataTypeSynchronizer.update(destinationDt.getDataTypeManager(), replacementDt);
- }
- }
- }
-
- private boolean confirmCommit() {
- return confirm("Commit Data Type?",
- "Do you want to commit the changes to this data type back to the source Archive? \n" +
- "(Warning: any changes in the source archive will be overwritten.)");
- }
-
- private boolean confirmUpdate() {
- return confirm("Update Data Type?",
- "Do you want to update this data type with the changes in the source Archive?\n" +
- "(Warning: any local changes will be overwritten.)");
- }
-
- private boolean confirm(String title, String message) {
- int choice = OptionDialog.showYesNoDialog(gTree, title, message);
- return choice == OptionDialog.YES_OPTION;
- }
-
private int askToAssociateDataTypes() {
return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?",
"Do you want to associate local datatypes with the target archive?");
}
- private void replaceDataType(DataType existingDt, DataType replacementDt) {
-
- int choice =
- OptionDialog.showYesNoDialog(gTree, "Replace Data Type?", "Replace " +
- existingDt.getPathName() + "\nwith " + replacementDt.getPathName() +
- "?");
-
- if (choice == OptionDialog.YES_OPTION) {
- try {
- DataTypeManager dtMgr = existingDt.getDataTypeManager();
- dtMgr.replaceDataType(existingDt, replacementDt, true);
- }
- catch (DataTypeDependencyException e) {
- errors.add("Replace failed. Existing type " + existingDt + "; replacment type " +
- replacementDt + ". " + e.getMessage());
- }
- }
- }
-
- // filters out nodes with categories in their path
+ // filters out nodes with categories in their path
private void filterRedundantNodes() {
Set nodeSet = new HashSet<>(droppedNodes);
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java
index 773b40a78f..ee06e38cd7 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java
@@ -22,20 +22,21 @@ import java.awt.dnd.DnDConstants;
import java.util.ArrayList;
import java.util.List;
-import javax.swing.JButton;
+import javax.swing.JTextField;
import org.junit.*;
import docking.action.DockingActionIf;
import docking.menu.ActionState;
-import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeDragNDropHandler;
import docking.widgets.tree.support.GTreeNodeTransferable;
+import ghidra.app.context.ProgramActionContext;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.datamgr.actions.ConflictHandlerModesAction;
import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.app.services.ProgramManager;
+import ghidra.app.util.datatype.DataTypeSelectionDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
@@ -57,7 +58,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
private ProgramDB program;
private DataTypeManagerPlugin plugin;
private DataTypesProvider provider;
+ private DockingActionIf pasteAction;
private ConflictHandlerModesAction conflictHandlerModesAction;
+ private ProgramActionContext treeContext;
private DataTypeArchiveGTree tree;
private ArchiveRootNode archiveRootNode;
private ArchiveNode programNode;
@@ -89,6 +92,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
assertNotNull("Did not successfully wait for the program node to load", programNode);
tool.showComponentProvider(provider, true);
+
+ pasteAction = getAction(plugin, "Paste");
+ treeContext = new DataTypesActionContext(provider, program, tree, null);
}
private ProgramDB buildProgram() throws Exception {
@@ -164,7 +170,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
- public void testConflictCopyInProgram() throws Exception {
+ public void testCopyPasteToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@@ -194,7 +200,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
- public void testConflictCopyReplace() throws Exception {
+ public void testCopyPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@@ -213,7 +219,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
- public void testConflictCopyUseExisting() throws Exception {
+ public void testCopyPasteToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@@ -234,11 +240,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
assertEquals(originalDataType, newDataTypeNode.getDataType());
structureNode = (DataTypeNode) category3Node.getChild(structName);
- assertTrue(!originalDataType.isEquivalent(structureNode.getDataType()));
+ assertFalse(originalDataType.isEquivalent(structureNode.getDataType()));
}
@Test
- public void testConflictPasteMoveRename() throws Exception {
+ public void testCutPasteToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@@ -256,7 +262,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
- public void testConflictDragMoveRename() throws Exception {
+ public void testDragMoveToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@@ -269,7 +275,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
// move/drag ArrayStruct to MISC
- dragNodeToNode(structureNode, miscNode);
+ moveDragNodeToNode(structureNode, miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
assertNotNull(node);
@@ -277,7 +283,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
- public void testConflictDragCopyRename() throws Exception {
+ public void testDragCopyToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@@ -291,7 +297,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
// copy/drag ArrayStruct to MISC
- copyNodeToNode(structureNode, miscNode);
+ copyDragNodeToNode(structureNode, miscNode);
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
category3Node = (CategoryNode) tree.getViewNode(category3Node);
@@ -305,7 +311,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
- public void testConflictDragCopyReplace() throws Exception {
+ public void testDragCopyToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@@ -314,13 +320,13 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
- DataType origDt = structureNode.getDataType();
+ DataType originalDt = structureNode.getDataType();
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
// copy/drag ArrayStruct to MISC
- copyNodeToNode(structureNode, miscNode);
+ copyDragNodeToNode(structureNode, miscNode);
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
category3Node = (CategoryNode) tree.getViewNode(category3Node);
@@ -329,11 +335,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertEquals(category3Node, structureNode.getParent());
- assertTrue(origDt.isEquivalent(structureNode.getDataType()));
+ assertTrue(originalDt.isEquivalent(structureNode.getDataType()));
}
@Test
- public void testConflictDragCopyUseExisting() throws Exception {
+ public void testDragCopyToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@@ -341,23 +347,23 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
- DataType origDt = structureNode.getDataType();
+ DataType originalDt = structureNode.getDataType();
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
// copy/drag ArrayStruct to MISC
- copyNodeToNode(structureNode, miscNode);
+ copyDragNodeToNode(structureNode, miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
structureNode = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(structureNode);
- assertTrue(!origDt.isEquivalent(structureNode.getDataType()));
+ assertFalse(originalDt.isEquivalent(structureNode.getDataType()));
}
@Test
- public void testConflictPasteMoveReplace() throws Exception {
+ public void testCutPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@@ -368,18 +374,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
- DataType origDt = node.getDataType();
+ DataType originalDt = node.getDataType();
// move ArrayStruct to MISC
cutPasteSelectedNodeToNode("MISC");
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
- assertTrue(!origDt.equals(node.getDataType()));
+ assertFalse(originalDt.equals(node.getDataType()));
}
@Test
- public void testConflictPasteMoveUseExisting() throws Exception {
+ public void testCutPasteToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@@ -390,18 +396,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
- DataType origDt = node.getDataType();
+ DataType originalDt = node.getDataType();
cutPasteSelectedNodeToNode("MISC");
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
- assertTrue(origDt.equals(node.getDataType()));
+ assertTrue(originalDt.equals(node.getDataType()));
assertNull(structureNode.getParent());
}
@Test
- public void testConflictDragMoveReplace() throws Exception {
+ public void testDragMoveToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@@ -414,21 +420,21 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
- DataType origDt = node.getDataType();
+ DataType originalDt = node.getDataType();
// move/drag ArrayStruct to MISC
- dragNodeToNode(structureNode, miscNode);
+ moveDragNodeToNode(structureNode, miscNode);
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertNull(structureNode.getParent());
assertNotNull(node);
- assertTrue(!origDt.equals(node.getDataType()));
+ assertFalse(originalDt.equals(node.getDataType()));
}
@Test
- public void testConflictDragMoveUseExisting() throws Exception {
+ public void testDragMoveToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@@ -440,21 +446,24 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
- DataType origDt = node.getDataType();
+ DataType originalDt = node.getDataType();
// move/drag ArrayStruct to MISC
- dragNodeToNode(structureNode, miscNode);
+ moveDragNodeToNode(structureNode, miscNode);
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertNull(structureNode.getParent());
assertNotNull(node);
- assertTrue(origDt.equals(node.getDataType()));
+ assertTrue(originalDt.equals(node.getDataType()));
}
@Test
- public void testReplaceDataTypeYes() throws Exception {
+ public void testDragMoveToDataType_Replace() throws Exception {
+
+ enableReplaceExistingConflictHandler();
+
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
Structure structure = (Structure) structureNode.getDataType();
@@ -465,9 +474,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
// drag/move ArrayStruct to MISC/ArrayStruct
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
- dragNodeToNode(structureNode, miscStructureNode);
-
- pressButtonOnOptionDialog("Yes");
+ moveDragNodeToNode(structureNode, miscStructureNode);
assertNull(structureNode.getParent());
assertNull(category3Node.getChild(structName));
@@ -491,122 +498,38 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
- public void testReplaceDataTypeNo() throws Exception {
- String structName = "ArrayStruct";
- DataTypeNode structureNode = createAndSelectStructure(structName);
- Structure structure = (Structure) structureNode.getDataType();
- CategoryNode category3Node = (CategoryNode) structureNode.getParent();
-
- CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
- expandNode(miscNode);
-
- // drag/move ArrayStruct to MISC/ArrayStruct
- DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
- dragNodeToNode(structureNode, miscStructureNode);
-
- pressButtonOnOptionDialog("No");
-
- structureNode = (DataTypeNode) tree.getViewNode(structureNode);
- assertNotNull(structureNode.getParent());
-
- category3Node = (CategoryNode) tree.getViewNode(category3Node);
- assertNotNull(category3Node.getChild("ArrayStruct"));
-
- miscNode = (CategoryNode) tree.getViewNode(miscNode);
- DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
- assertTrue(!structure.isEquivalent(node.getDataType()));
- }
-
- @Test
- public void testReplaceDTSameParentYes() throws Exception {
+ public void testDragMoveToDataType_SameParent() throws Exception {
// drag/drop a data type onto another data type
- // get Option dialog, and choose "Yes"
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
- dragNodeToNode(unionNode, structureNode);
+ setErrorsExpected(true);
+ moveDragNodeToNode(unionNode, structureNode);
+ setErrorsExpected(false);
- pressButtonOnOptionDialog("Yes");
-
- assertNotNull(miscNode.getChild("ArrayUnion"));
- assertNull(miscNode.getChild("ArrayStruct"));
+ // can't move a data type into its same category
+ waitForWindow("Encountered Errors Copying/Moving");
}
@Test
- public void testReplaceDTSameParentNo() throws Exception {
- // drag/drop a data type onto another data type
- // get Option dialog, and choose "Yes"
- CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
- expandNode(miscNode);
+ public void testDragCopyToDataType() throws Exception {
- DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
- DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
+ enableRenameConflictHandler();
- dragNodeToNode(unionNode, structureNode);
-
- pressButtonOnOptionDialog("No");
-
- assertNotNull(miscNode.getChild("ArrayUnion"));
- assertNotNull(miscNode.getChild("ArrayStruct"));
- }
-
- @Test
- public void testCopyReplaceDataTypeNo() throws Exception {
- // drag/copy a data type onto another data type
- // get Option dialog, and choose "No"
- // cause a conflict
- // in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
- // drag/move ArrayStruct to MISC/ArrayStruct
- CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
- expandNode(miscNode);
-
- DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
- DataType origDt = miscStructureNode.getDataType();
-
- copyNodeToNode(structureNode, miscStructureNode);
-
- pressButtonOnOptionDialog("No");
-
- structureNode = (DataTypeNode) tree.getViewNode(structureNode);
- assertNotNull(structureNode.getParent());
-
- category3Node = (CategoryNode) tree.getViewNode(category3Node);
- assertNotNull(category3Node.getChild("ArrayStruct"));
-
- miscNode = (CategoryNode) tree.getViewNode(miscNode);
- DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
- assertEquals(origDt, node.getDataType());
- }
-
- @Test
- public void testCopyReplaceDataTypeYes() throws Exception {
- // drag/copy a data type onto another data type
- // get Option dialog, and choose "Yes"
- // cause a conflict
- // in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
- String structName = "ArrayStruct";
- DataTypeNode structureNode = createAndSelectStructure(structName);
- Structure structure = (Structure) structureNode.getDataType();
- CategoryNode category3Node = (CategoryNode) structureNode.getParent();
-
// drag/move ArrayStruct to MISC/ArrayStruct
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
- DataType miscStructure = miscStructureNode.getDataType();
-
- copyNodeToNode(structureNode, miscStructureNode);
-
- pressButtonOnOptionDialog("Yes");
+ copyDragNodeToNode(structureNode, miscStructureNode);
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
assertNotNull(structureNode.getParent());
@@ -615,26 +538,251 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
assertNotNull(category3Node.getChild(structName));
miscNode = (CategoryNode) tree.getViewNode(miscNode);
- DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
- assertTrue(structure.isEquivalent(node.getDataType()));
+ DataTypeNode newNode =
+ (DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
+ assertNotNull(newNode);
+ assertNotNull(structureNode.getParent());
+ }
- undo();
+ @Test
+ public void testCopyPasteToCategory() {
- miscNode = (CategoryNode) programNode.getChild(miscName);
- node = (DataTypeNode) miscNode.getChild(structName);
- assertTrue(miscStructure.isEquivalent(node.getDataType()));
+ String miscName = "MISC";
+ CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
+ expandNode(miscNode);
- redo();
+ String dtName = "ArrayStruct";
+ DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
+ selectNode(miscStructureNode);
- miscNode = (CategoryNode) programNode.getChild(miscName);
- node = (DataTypeNode) miscNode.getChild(structName);
- assertTrue(structure.isEquivalent(node.getDataType()));
+ DockingActionIf copyAction = getAction(plugin, "Copy");
+ assertTrue(copyAction.isEnabledForContext(treeContext));
+ assertFalse(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(copyAction, tree);
+
+ selectNode(miscNode);
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+ DataTypeTestUtils.performAction(pasteAction, tree);
+ GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
+ assertNotNull(newNode);
+
+ selectNode(miscNode);
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+ DataTypeTestUtils.performAction(pasteAction, tree);
+ newNode = miscNode.getChild("Copy_2_of_" + dtName);
+ assertNotNull(newNode);
+ }
+
+ @Test
+ public void testCopyPasteToDataType_SameType() throws Exception {
+
+ String miscName = "MISC";
+ CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
+ expandNode(miscNode);
+
+ String dtName = "ArrayStruct";
+ DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
+ selectNode(miscStructureNode);
+
+ DockingActionIf copyAction = getAction(plugin, "Copy");
+ assertTrue(copyAction.isEnabledForContext(treeContext));
+ assertFalse(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(copyAction, tree);
+
+ selectNode(miscStructureNode);
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+ DataTypeTestUtils.performAction(pasteAction, tree);
+ GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
+ assertNotNull(newNode);
+
+ selectNode(miscStructureNode);
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(pasteAction, tree);
+
+ newNode = miscNode.getChild("Copy_2_of_" + dtName);
+ assertNotNull(newNode);
+ }
+
+ @Test
+ public void testCopyPasteToDataType_DifferentType() throws Exception {
+
+ String miscName = "MISC";
+ CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
+ expandNode(miscNode);
+
+ String dtName = "ArrayStruct";
+ DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
+ selectNode(miscStructureNode);
+
+ DockingActionIf copyAction = getAction(plugin, "Copy");
+ assertTrue(copyAction.isEnabledForContext(treeContext));
+ assertFalse(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(copyAction, tree);
+
+ DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
+ selectNode(miscUnionNode);
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(pasteAction, tree);
+
+ GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
+ assertNotNull(newNode);
+ }
+
+ @Test
+ public void testCopyPasteToDataType_DifferentType_SameName_RenameConflictHanlder()
+ throws Exception {
+
+ enableRenameConflictHandler();
+
+ String existingDtName = "ArrayUnion";
+
+ DataTypeNode intStructureNode =
+ (DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
+ rename(intStructureNode, existingDtName);
+ intStructureNode =
+ (DataTypeNode) getNotepadNode("Category1/Category2/Category3/" + existingDtName);
+ selectNode(intStructureNode);
+
+ DockingActionIf copyAction = getAction(plugin, "Copy");
+ assertTrue(copyAction.isEnabledForContext(treeContext));
+ assertFalse(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(copyAction, tree);
+
+ String miscName = "MISC";
+ CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
+ expandNode(miscNode);
+ DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild(existingDtName);
+ selectNode(miscUnionNode);
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(pasteAction, tree);
+ waitForTree();
+
+ GTreeNode newNode = miscNode.getChild(existingDtName + DataType.CONFLICT_SUFFIX);
+ assertNotNull(newNode);
+ }
+
+ @Test
+ public void testCopyPasteToDataType_FromDifferentCategory() throws Exception {
+
+ String miscName = "MISC";
+ CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
+ expandNode(miscNode);
+
+ DataTypeNode intStructureNode =
+ (DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
+ selectNode(intStructureNode);
+
+ DockingActionIf copyAction = getAction(plugin, "Copy");
+ assertTrue(copyAction.isEnabledForContext(treeContext));
+ assertFalse(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(copyAction, tree);
+
+ String dtName = "ArrayStruct";
+ DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
+ selectNode(miscStructureNode);
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(pasteAction, tree);
+
+ GTreeNode newNode = miscNode.getChild("IntStruct");
+ assertNotNull(newNode);
+ }
+
+ @Test
+ public void testCopyPasteToDataType_MultipleDataTypes() {
+
+ String miscName = "MISC";
+ CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
+ expandNode(miscNode);
+
+ String dtName1 = "ArrayStruct";
+ DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName1);
+ String dtName2 = "ArrayUnion";
+ DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild(dtName2);
+ selectNodes(miscStructureNode, miscUnionNode);
+
+ DockingActionIf copyAction = getAction(plugin, "Copy");
+ assertTrue(copyAction.isEnabledForContext(treeContext));
+ assertFalse(pasteAction.isEnabledForContext(treeContext));
+
+ DataTypeTestUtils.performAction(copyAction, tree);
+
+ DataTypeNode intStructureNode =
+ (DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
+ selectNode(intStructureNode);
+
+ assertTrue(pasteAction.isEnabledForContext(treeContext));
+ DataTypeTestUtils.performAction(pasteAction, tree);
+
+ GTreeNode newNode = miscNode.getChild(dtName1);
+ assertNotNull(newNode);
+ newNode = miscNode.getChild(dtName2);
+ assertNotNull(newNode);
+ }
+
+ @Test
+ public void testReplaceAction() {
+
+ String miscName = "MISC";
+ CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
+ expandNode(miscNode);
+
+ String originalDtName = "ArrayStruct";
+ DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(originalDtName);
+ selectNode(miscStructureNode);
+
+ DockingActionIf replaceAction = getAction(plugin, "Replace Data Type");
+ assertTrue(replaceAction.isEnabledForContext(treeContext));
+ DataTypeTestUtils.performAction(replaceAction, tree, false);
+
+ String newDtName = "IntStruct";
+ chooseDataType(newDtName);
+
+ DataTypeNode updatedNode = (DataTypeNode) miscNode.getChild(newDtName);
+ assertNotNull(updatedNode);
}
//==================================================================================================
-// Private Refactored Methods
+// Private Methods
//==================================================================================================
+ private void chooseDataType(String dtName) {
+
+ DataTypeSelectionDialog chooser = waitForDialogComponent(DataTypeSelectionDialog.class);
+
+ JTextField tf = findComponent(chooser, JTextField.class);
+ triggerText(tf, dtName);
+
+ pressButtonByText(chooser, "OK");
+ waitForTasks();
+ }
+
+ private GTreeNode getNotepadNode(String path) {
+
+ GTreeNode last = programNode;
+ String[] names = path.split("/");
+ for (String name : names) {
+ last = last.getChild(name);
+ }
+
+ return last;
+ }
+
+ private void rename(DataTypeNode node, String newName) throws Exception {
+
+ DataType dt = node.getDataType();
+ tx(program, () -> dt.setName(newName));
+ waitForProgram();
+ }
+
/**
* In the program, rename Category1/Category2/Category3/IntStruct to
*/
@@ -649,9 +797,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
DataTypeNode structureNode = (DataTypeNode) category3Node.getChild("IntStruct");
Structure structure = (Structure) structureNode.getDataType();
- int transactionID = program.startTransaction("test");
- structure.setName(structureName);
- program.endTransaction(transactionID, true);
+ tx(program, () -> structure.setName(structureName));
waitForProgram();
structureNode = (DataTypeNode) category3Node.getChild(structureName);
@@ -659,9 +805,6 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
return structureNode;
}
- /**
- * Copies the currently selected node to the node by the given name.
- */
private CategoryNode copyPasteSelectedNodeToNode(String toNodeName) throws Exception {
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabled());
@@ -671,10 +814,8 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
selectNode(miscNode);
- executeOnSwingWithoutBlocking(() -> {
- DockingActionIf pasteAction = getAction(plugin, "Paste");
- DataTypeTestUtils.performAction(pasteAction, tree);
- });
+ runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
+ waitForTasks();
return miscNode;
}
@@ -686,40 +827,30 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
selectNode(miscNode);
- executeOnSwingWithoutBlocking(() -> {
- DockingActionIf pasteAction = getAction(plugin, "Paste");
- DataTypeTestUtils.performAction(pasteAction, tree);
- });
+ runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
+ waitForTasks();
return miscNode;
}
- private void pressButtonOnOptionDialog(String buttonName) throws Exception {
- OptionDialog d = waitForDialogComponent(OptionDialog.class);
- JButton button = findButtonByText(d, buttonName);
- assertNotNull(button);
- pressButton(button);
- waitForProgram();
- }
-
- private void dragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
+ private void moveDragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
List dropList = new ArrayList<>();
dropList.add(fromNode);
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
- runSwing(
- () -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE), false);
- waitForSwing();
+ runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE),
+ false);
+ waitForTasks();
}
- private void copyNodeToNode(GTreeNode fromNode, final GTreeNode toNode) throws Exception {
+ private void copyDragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) throws Exception {
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
List dropList = new ArrayList<>();
dropList.add(fromNode);
- final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
-
- runSwing(
- () -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY), false);
+ Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
+ runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY),
+ false);
+ waitForTasks();
}
//==================================================================================================
@@ -735,6 +866,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
waitForTree();
}
+ private void selectNodes(GTreeNode... nodes) {
+ tree.setSelectedNodes(nodes);
+ waitForTree();
+ }
+
private void waitForTree() {
waitForTree(tree);
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java
index a1e7d003f8..3fe94ff5c6 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java
@@ -113,6 +113,9 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
treeContext = new DataTypesActionContext(provider, program, tree, null);
removeDistractingPlugins();
+
+ cutAction = getAction(plugin, "Copy");
+ pasteAction = getAction(plugin, "Paste");
}
private void removeDistractingPlugins() {
@@ -589,8 +592,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
selectNode(myStructNode);
DockingActionIf copyAction = getAction(plugin, "Copy");
- cutAction = getAction(plugin, "Cut");
- pasteAction = getAction(plugin, "Paste");
assertTrue(cutAction.isEnabledForContext(treeContext));
assertTrue(copyAction.isEnabledForContext(treeContext));
@@ -616,8 +617,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
selectNode(cat2Node);
DockingActionIf copyAction = getAction(plugin, "Copy");
- cutAction = getAction(plugin, "Cut");
- pasteAction = getAction(plugin, "Paste");
assertTrue(cutAction.isEnabledForContext(treeContext));
assertTrue(copyAction.isEnabledForContext(treeContext));
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Category.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Category.java
index 9e3684b225..894fea044e 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Category.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Category.java
@@ -25,28 +25,31 @@ import ghidra.util.task.TaskMonitor;
* Each data type resides in a given a category.
*/
public interface Category extends Comparable {
+
/**
* Get the name of this category.
+ * @return the name.
*/
public abstract String getName();
/**
* Sets the name of this category.
* @param name the new name for this category
- * @throws DuplicateNameException if another category exists in the same parent with the same name;
+ * @throws DuplicateNameException if another category exists in the same parent with the same
+ * name
* @throws InvalidNameException if the name is not an acceptable name.
*/
public abstract void setName(String name) throws DuplicateNameException, InvalidNameException;
/**
* Get all categories in this category.
- * @return zero-length array if there are no categories
+ * @return zero-length array if there are no categories.
*/
public abstract Category[] getCategories();
/**
* Get all data types in this category.
- * @return zero-length array if there are no data types
+ * @return zero-length array if there are no data types.
*/
public abstract DataType[] getDataTypes();
@@ -55,11 +58,12 @@ public interface Category extends Comparable {
* The base name of a name is the first part of the string up to where the first ".conflict"
* occurs. In other words, finds all data types whose name matches the given name once
* any conflict suffixes have been removed from both the given name and the data types
- * that are being scanned.
- * @param name the name for which to get conflict related data types in this category. Note: the
- * name that is passed in will be normalized to its base name, so you may pass in names with .conflict
- * appended as a convenience.
- * @return a list of data types that have the same base name as the base name of the given name
+ * that are being scanned.
+ * @param name the name for which to get conflict related data types in this category. Note:
+ * the name that is passed in will be normalized to its base name, so you may pass in names
+ * with .conflict appended as a convenience.
+ * @return a list of data types that have the same base name as the base name of the given
+ * name.
*/
public abstract List getDataTypesByBaseName(String name);
@@ -73,8 +77,8 @@ public interface Category extends Comparable {
/**
* Get a category with the given name.
- * @param name the name of the category
- * @return null if there is no category by this name
+ * @param name the name of the category.
+ * @return null if there is no category by this name.
*/
public abstract Category getCategory(String name);
@@ -86,39 +90,41 @@ public interface Category extends Comparable {
/**
* Get a data type with the given name.
- * @param name the name of the data type
- * @return null if there is no data type by this name
+ * @param name the name of the data type.
+ * @return null if there is no data type by this name.
*/
public abstract DataType getDataType(String name);
/**
- * Create a category with the given name; if category already exists, then
- * return that category.
- * @param name the category name
- * @throws InvalidNameException if name has invalid characters
+ * Create a category with the given name; if category already exists, then return that
+ * category.
+ * @param name the category name.
+ * @return the category.
+ * @throws InvalidNameException if name has invalid characters.
*/
public abstract Category createCategory(String name) throws InvalidNameException;
/**
* Remove the named category from this category.
- * @param name the name of the category to remove
- * @param monitor the task monitor
- * @return true if the category was removed
+ * @param name the name of the category to remove.
+ * @param monitor the task monitor.
+ * @return true if the category was removed.
*/
public abstract boolean removeCategory(String name, TaskMonitor monitor);
/**
* Remove the named category from this category, IFF it is empty.
- * @param name the name of the category to remove
- * @param monitor the task monitor
- * @return true if the category was removed
+ * @param name the name of the category to remove.
+ * @param monitor the task monitor.
+ * @return true if the category was removed.
*/
public abstract boolean removeEmptyCategory(String name, TaskMonitor monitor);
/**
- * Move the given category to this category; category is removed from
- * its original parent category.
- * @param category the category to move
+ * Move the given category to this category; category is removed from its original parent
+ * category.
+ * @param category the category to move.
+ * @param monitor the monitor.
* @throws DuplicateNameException if this category already contains a
* category or data type with the same name as the category param.
*/
@@ -126,15 +132,18 @@ public interface Category extends Comparable {
throws DuplicateNameException;
/**
- * Make a new subcategory from the given category.
- * @param category the category to copy into this category
- * @return category that is added to this category
+ * Make a new sub-category from the given category.
+ * @param category the category to copy into this category.
+ * @param handler the handler to call if there is a data type conflict.
+ * @param monitor the monitor.
+ * @return category that is added to this category.
*/
public abstract Category copyCategory(Category category, DataTypeConflictHandler handler,
TaskMonitor monitor);
/**
* Return this category's parent; return null if this is the root category.
+ * @return the category.
*/
public abstract Category getParent();
@@ -146,33 +155,36 @@ public interface Category extends Comparable {
/**
* Get the fully qualified name for this category.
+ * @return the name.
*/
public abstract String getCategoryPathName();
/**
* Get the root category.
+ * @return the category.
*/
public abstract Category getRoot();
/**
* Get the data type manager associated with this category.
+ * @return the manager.
*/
public abstract DataTypeManager getDataTypeManager();
/**
- * Move a data type into this category
+ * Move a data type into this category.
*
- * @param type data type to be moved
- * @param handler the handler to call if there is a data type conflict
- * @throws DataTypeDependencyException
+ * @param type data type to be moved.
+ * @param handler the handler to call if there is a data type conflict.
+ * @throws DataTypeDependencyException if a disallowed dependency is created during the move.
*/
public abstract void moveDataType(DataType type, DataTypeConflictHandler handler)
throws DataTypeDependencyException;
/**
- * Remove a datatype from this category
+ * Remove a datatype from this category.
*
- * @param type data type to be removed
+ * @param type data type to be removed.
* @param monitor monitor of progress in case operation takes a long time.
* @return true if the data type was found in this category and successfully removed.
*/
@@ -180,6 +192,7 @@ public interface Category extends Comparable {
/**
* Get the ID for this category.
+ * @return the ID.
*/
public long getID();
}