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 9a94fa0600..b61c97d2ab 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 @@ -54,6 +54,11 @@ public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService throw new UnsupportedOperationException(); } + @Override + public void edit(Structure structure, String fieldName) { + throw new UnsupportedOperationException(); + } + @Override public DataType getDataType(String filterText) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java index 5f3610b61b..1c18c828c5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java @@ -20,8 +20,7 @@ import java.awt.datatransfer.DataFlavor; import java.awt.dnd.*; import java.awt.event.*; import java.math.BigInteger; -import java.util.Arrays; -import java.util.EventObject; +import java.util.*; import java.util.List; import javax.swing.*; @@ -218,6 +217,47 @@ public abstract class CompositeEditorPanel extends JPanel } } + /** + * Select the field by the given name in this panel's table. + * + * @param fieldName the field name + */ + public void selectField(String fieldName) { + + if (!model.isLoaded()) { + return; // disposed; not sure if this can happen + } + + // Find the given field by name in the current editor, which, if edited, may not match the + // original data type. If the user has renamed the field, but not saved the editor, then + // we may not find the field. + int row = findRowForFieldName(fieldName); + if (row == -1) { + return; + } + + table.getSelectionModel().setSelectionInterval(row, row); + } + + private int findRowForFieldName(String fieldName) { + int n = model.getRowCount(); + for (int row = 0; row < n; row++) { + + DataTypeComponent dtc = model.getComponent(row); + if (dtc != null) { + String dtcFieldName = dtc.getFieldName(); + if (Objects.equals(fieldName, dtcFieldName)) { + return row; + } + String defaultName = dtc.getDefaultFieldName(); + if (Objects.equals(fieldName, defaultName)) { + return row; + } + } + } + return -1; + } + protected void cancelCellEditing() { TableCellEditor cellEditor = table.getCellEditor(); if (cellEditor != null) { @@ -1416,5 +1456,4 @@ public abstract class CompositeEditorPanel extends JPanel KeyBindingUtils.clearKeyBinding(this, keyStroke); } } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java index 8da484a980..488dc5c516 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java @@ -89,4 +89,10 @@ public class StructureEditorProvider extends CompositeEditorProvider { public String getHelpTopic() { return "DataTypeEditors"; } + + public void selectField(String fieldName) { + if (fieldName != null) { + editorPanel.selectField(fieldName); + } + } } 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 9823ebb551..89c6d03c56 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 @@ -492,20 +492,14 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override public void edit(DataType dt) { - DataTypeManager dataTypeManager = dt.getDataTypeManager(); - if (dataTypeManager == null) { - throw new IllegalArgumentException( - "DataType " + dt.getPathName() + " has no DataTypeManager! Make sure the " + - "given DataType has been resolved by a DataTypeManager"); - } - CategoryPath categoryPath = dt.getCategoryPath(); - if (categoryPath == null) { - throw new IllegalArgumentException( - "DataType " + dt.getName() + " has no category path!"); - } editorManager.edit(dt); } + @Override + public void edit(Structure dt, String fieldName) { + editorManager.edit(dt, fieldName); + } + @Override public DataTypeManager getBuiltInDataTypesManager() { return dataTypeManagerHandler.getBuiltInDataTypesManager(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java index b637f1cd99..ec8772bff8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java @@ -97,17 +97,8 @@ public class DataTypeEditorManager implements EditorListener { */ public void edit(DataType dataType) { - DataTypeManager dataTypeManager = dataType.getDataTypeManager(); - if (dataTypeManager == null) { - throw new IllegalArgumentException( - "Datatype " + dataType.getName() + " doesn't have a data type manager specified."); - } - - EditorProvider editor = getEditor(dataType); + EditorProvider editor = reuseExistingEditor(dataType); if (editor != null) { - ComponentProvider componentProvider = editor.getComponentProvider(); - plugin.getTool().showComponentProvider(componentProvider, true); - componentProvider.toFront(); return; } @@ -124,13 +115,58 @@ public class DataTypeEditorManager implements EditorListener { else if (dataType instanceof FunctionDefinition) { editFunctionSignature((FunctionDefinition) dataType); } - if (editor == null) { + + if (editor != null) { + editor.addEditorListener(this); + editorList.add(editor); + } + } + + /** + * Displays a data type editor for editing the given Structure. If the structure is is already + * being edited then it is brought to the front. Otherwise, a new editor is created and + * displayed. + * @param structure the structure. + * @param fieldName the optional name of the field to select in the editor. + */ + public void edit(Structure structure, String fieldName) { + + StructureEditorProvider editor = (StructureEditorProvider) getEditor(structure); + if (editor != null) { + reuseExistingEditor(structure); + editor.selectField(fieldName); return; } + + editor = new StructureEditorProvider(plugin, structure, + showStructureNumbersInHex()); + editor.selectField(fieldName); editor.addEditorListener(this); editorList.add(editor); } + private EditorProvider reuseExistingEditor(DataType dataType) { + DataTypeManager dataTypeManager = dataType.getDataTypeManager(); + if (dataTypeManager == null) { + throw new IllegalArgumentException( + "Datatype " + dataType.getName() + " doesn't have a data type manager specified."); + } + + CategoryPath categoryPath = dataType.getCategoryPath(); + if (categoryPath == null) { + throw new IllegalArgumentException( + "DataType " + dataType.getName() + " has no category path!"); + } + + EditorProvider editor = getEditor(dataType); + if (editor != null) { + ComponentProvider componentProvider = editor.getComponentProvider(); + plugin.getTool().showComponentProvider(componentProvider, true); + componentProvider.toFront(); + } + return editor; + } + private void installEditorActions() { // composite editor actions 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 7aa7a901b3..fd82185ffe 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 @@ -22,8 +22,7 @@ import javax.swing.tree.TreePath; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.framework.plugintool.ServiceInfo; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeManagerChangeListener; +import ghidra.program.model.data.*; import ghidra.util.HelpLocation; /** @@ -88,14 +87,24 @@ public interface DataTypeManagerService extends DataTypeQueryService, DataTypeAr public boolean isEditable(DataType dt); /** - * Pop up an editor dialog for the given data type. + * Pop up an editor window for the given data type. * - * @param dt data type that either a Structure or a Union; built in types cannot be edited + * @param dt the data type; built in types cannot be edited * @throws IllegalArgumentException if the given has not been resolved by a DataTypeManager; - * in other words, if {@link DataType#getDataTypeManager()} returns null. + * in other words, if {@link DataType#getDataTypeManager()} returns null */ public void edit(DataType dt); + /** + * Pop up an editor window for the given structure. + * + * @param structure the structure + * @param fieldName the optional structure field name to select in the editor window + * @throws IllegalArgumentException if the given has not been resolved by a DataTypeManager; + * in other words, if {@link DataType#getDataTypeManager()} returns null + */ + public void edit(Structure structure, String fieldName); + /** * Selects the given data type in the display of data types. A null dataType * value will clear the current selection. 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 5cd9480377..263885c832 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 @@ -99,6 +99,11 @@ public class TestDoubleDataTypeManagerService implements DataTypeManagerService throw new UnsupportedOperationException(); } + @Override + public void edit(Structure structure, String fieldName) { + throw new UnsupportedOperationException(); + } + @Override public void closeArchive(DataTypeManager dtm) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/EditDataTypeAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/EditDataTypeAction.java index 3fc4f2f5b7..5af9519f30 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/EditDataTypeAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/EditDataTypeAction.java @@ -17,14 +17,16 @@ package ghidra.app.plugin.core.decompile.actions; import docking.ActionContext; import docking.action.MenuData; +import ghidra.app.decompiler.ClangFieldToken; +import ghidra.app.decompiler.ClangToken; +import ghidra.app.decompiler.component.DecompilerPanel; import ghidra.app.decompiler.component.DecompilerUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.HelpTopics; import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Function; import ghidra.util.HelpLocation; import ghidra.util.UndefinedFunction; @@ -74,9 +76,31 @@ public class EditDataTypeAction extends AbstractDecompilerAction { if (baseDtDTM != dataTypeManager) { baseDataType = baseDataType.clone(dataTypeManager); } - final DataTypeManagerService service = + + DataTypeManagerService service = context.getTool().getService(DataTypeManagerService.class); - service.edit(baseDataType); + + if (dataType instanceof Structure structure) { + editStructure(service, structure, context); + } + else { + service.edit(baseDataType); + } } + private void editStructure(DataTypeManagerService service, Structure structure, + DecompilerActionContext context) { + + DecompilerPanel decompilerPanel = context.getDecompilerPanel(); + ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor(); + if (tokenAtCursor instanceof ClangFieldToken) { + String fieldName = tokenAtCursor.getText(); + if (fieldName != null) { + service.edit(structure, fieldName); + return; + } + } + + service.edit(structure); + } }