From b490ac5e635667a1d14678cb9a2134bf14211bb7 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:22:05 -0400 Subject: [PATCH] GP-7075 - Structure Editor - Added actions to jump to the next row with a defined data type --- .../DataTypeEditors/StructureEditor.htm | 15 ++++ .../compositeeditor/CompositeEditorPanel.java | 72 +++++++++++++++++-- .../CompositeEditorProvider.java | 4 ++ .../NextPrevDefinedComponentAction.java | 53 ++++++++++++++ .../ShowDataTypeInTreeAction.java | 3 - .../StructureEditorProvider.java | 2 + 6 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/NextPrevDefinedComponentAction.java diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm index 510a020979..2b0c9c4cf0 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm @@ -1202,6 +1202,21 @@ + +

Go To Next / Previous Defined Type

+ +
+

+ Go to Next Defined Type and Go to Previous Defined Type are + available from the popup menu. They allow you to jump to the next row that has a + defined data type. This is useful when dealing with structures that have long runs of + undfined types used for padding. If you invoke the action on a row that already has a + defined data type, then the action will first find the next row with an undefined type and + start the search from there. +

+
+ +

Showing a Component's Data Type Category

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 72d9fb9b50..f475d55194 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 @@ -942,25 +942,83 @@ public abstract class CompositeEditorPanel isUndefined(dtc); + int undefinedRow = findNextMatchingDtc(currentRow, forward, isUndefined); + int startRow = undefinedRow + (forward ? 1 : -1); + int n = model.getRowCount(); + if (startRow >= n) { + return null; + } + + DtcMatcher isDefined = dtc -> !isUndefined(dtc); + return findNextMatchingDtc(startRow, forward, isDefined); + } + + private int findNextMatchingDtc(int row, boolean forward, DtcMatcher matcher) { + + int start = row; + int end = forward ? model.getRowCount() : -1; + int direction = forward ? 1 : -1; + for (int i = start; i != end; i += direction) { + DataTypeComponent dtc = model.getComponent(i); + if (matcher.matches(dtc)) { + return i; + } + } + return -1; + } + + // just a nicer predicate + private interface DtcMatcher { + public boolean matches(DataTypeComponent dtc); + } + + private boolean isUndefined(DataTypeComponent dtc) { + if (dtc == null) { + return true; + } + + DataType dt = dtc.getDataType(); + return Undefined.isUndefined(dt); + } + + private void goToRow(int row) { + table.getSelectionModel().setSelectionInterval(row, row); + Rectangle cellRect = table.getCellRect(row, 0, true); + table.scrollRectToVisible(cellRect); + } + void search(String searchText, boolean forward) { searchText = searchText.toLowerCase(); Integer row = forward ? findForward(searchText) : findBackward(searchText); if (row != null) { - table.getSelectionModel().setSelectionInterval(row, row); - Rectangle cellRect = table.getCellRect(row, 0, true); - table.scrollRectToVisible(cellRect); + goToRow(row); } - } private Integer findForward(String text) { @@ -1085,6 +1143,10 @@ public abstract class CompositeEditorPanel provider, boolean forward) { + super(provider, getName(forward)); + + this.forward = forward; + + MenuData data = new MenuData(new String[] { getName(forward) }); + data.setMenuGroup(BASIC_ACTION_GROUP + "_2"); // put below the basic action group + setPopupMenuData(data); + + setKeyBindingData(new KeyBindingData(forward ? "Control Down" : "Control Up")); + + setHelpLocation( + new HelpLocation(provider.getHelpTopic(), "Structure_Editor_Go_To_Next_Defined")); + } + + @Override + public void actionPerformed(ActionContext context) { + provider.goToNextDefinedRow(forward); + } + + private static String getName(boolean forward) { + return "Go to " + (forward ? "Next" : "Previous") + " Defined Type"; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java index 2e6ed2bed1..5a6110b163 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java @@ -18,7 +18,6 @@ package ghidra.app.plugin.core.compositeeditor; import javax.swing.Icon; import docking.ActionContext; -import docking.action.ToolBarData; import generic.theme.GIcon; import ghidra.app.services.DataTypeManagerService; import ghidra.program.model.data.*; @@ -37,8 +36,6 @@ public class ShowDataTypeInTreeAction extends CompositeEditorTableAction { public ShowDataTypeInTreeAction(CompositeEditorProvider provider) { super(provider, ACTION_NAME, TOOLBAR_GROUP, null /*popupPath*/, null /*menuPath*/, ICON); - - setToolBarData(new ToolBarData(ICON, TOOLBAR_GROUP)); } @Override 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 a69b9c6f51..c227ef4ee8 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 @@ -85,6 +85,8 @@ public class StructureEditorProvider new AddBitFieldAction(this), new EditBitFieldAction(this), new ShowDataTypeInTreeAction(this), + new NextPrevDefinedComponentAction(this, true), + new NextPrevDefinedComponentAction(this, false), // new ViewBitFieldAction(this) };