diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java index 6a086b0e49..176b7e00b6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java @@ -192,6 +192,11 @@ public class DefaultDataTypeManagerService implements DataTypeManagerService { throw new UnsupportedOperationException(); } + @Override + public List getSelectedDatatypes() { + throw new UnsupportedOperationException(); + } + @Override public void setRecentlyUsed(DataType dt) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java index ef439d43cd..1ae2fe61fc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java @@ -612,6 +612,14 @@ public class DataTypeManagerPlugin extends ProgramPlugin } } + @Override + public List getSelectedDatatypes() { + if (provider.isVisible()) { + return provider.getSelectedDataTypes(); + } + return Collections.emptyList(); + } + @Override public void setRecentlyUsed(DataType dt) { dataTypeManagerHandler.setRecentlyUsedDataType(dt); 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 206d3aa35f..d89903da13 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 @@ -780,6 +780,25 @@ public class DataTypesProvider extends ComponentProviderAdapter { contextChanged(); } + /** + * Returns a list of all the data types selected in the data types tree + * @return a list of all the data types selected in the data types tree + */ + public List getSelectedDataTypes() { + List selectedDataTypes = new ArrayList<>(); + DataTypeArchiveGTree gTree = getGTree(); + for (TreePath path : gTree.getSelectionPaths()) { + Object node = path.getLastPathComponent(); + if (node instanceof DataTypeNode) { + DataType dataType = ((DataTypeNode) node).getDataType(); + if (dataType != null) { + selectedDataTypes.add(dataType); + } + } + } + return selectedDataTypes; + } + // this is a callback from the action--we need this to prevent callbacks, as the other // version of this method will try to get the method, which will lazily created it, which // will trigger a callback... diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeManagerService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeManagerService.java index fce2133d8b..d9e38f26a3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeManagerService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeManagerService.java @@ -151,6 +151,12 @@ public interface DataTypeManagerService extends DataTypeQueryService { */ public void setDataTypeSelected(DataType dataType); + /** + * Returns the list of data types that are currently selected in the data types tree + * @return the list of data types that are currently selected in the data types tree + */ + public List getSelectedDatatypes(); + /** * Shows the user a dialog that allows them to choose a data type from a tree of all available * data types. 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 237be63780..a1e7d003f8 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 @@ -52,6 +52,7 @@ import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.plugin.core.function.AbstractEditFunctionSignatureDialog; import ghidra.app.plugin.core.programtree.ProgramTreePlugin; +import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.ProgramManager; import ghidra.app.util.datatype.DataTypeSelectionEditor; import ghidra.framework.options.ToolOptions; @@ -900,9 +901,40 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe assertMatchingStructures(resultsProvider, "Structure_0x10", "Structure_0x20"); } + @Test + public void testGetSelectedDatatypesFromService() { + DataTypeManagerService dataTypeManagerService = + tool.getService(DataTypeManagerService.class); + + assertEquals(0, dataTypeManagerService.getSelectedDatatypes().size()); + + CategoryPath path = new CategoryPath("/MISC"); + DataType dt1 = program.getDataTypeManager().getDataType(path, "ArrayStruct"); + DataType dt2 = program.getDataTypeManager().getDataType(path, "ArrayUnion"); + + selectDataTypes(dt1, dt2); + + List selectedDatatypes = dataTypeManagerService.getSelectedDatatypes(); + assertEquals(2, selectedDatatypes.size()); + assertTrue(selectedDatatypes.contains(dt1)); + assertTrue(selectedDatatypes.contains(dt2)); + } + //================================================================================================== // Private methods //================================================================================================== + private void selectDataTypes(DataType dt1, DataType dt2) { + String catName1 = dt1.getCategoryPath().getName(); // assumes path is only 1 level + CategoryNode cat1 = (CategoryNode) programNode.getChild(catName1); + DataTypeNode node1 = cat1.getNode(dt1); + + String catName2 = dt2.getCategoryPath().getName(); // assumes path is only 1 level + CategoryNode cat2 = (CategoryNode) programNode.getChild(catName2); + DataTypeNode node2 = cat2.getNode(dt2); + + tree.setSelectedNodes(node1, node2); + waitForTree(tree); + } private void createStructureWithOffset_0x4() { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java b/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java index a906497d48..730109c1f9 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java @@ -121,6 +121,11 @@ public class TestDoubleDataTypeManagerService implements DataTypeManagerService throw new UnsupportedOperationException(); } + @Override + public List getSelectedDatatypes() { + throw new UnsupportedOperationException(); + } + @Override public DataType getDataType(TreePath selectedPath) { throw new UnsupportedOperationException();