Merge remote-tracking branch

'origin/GP-5326_ghidragon_edit_structure_field--SQUASHED' (Closes #7407)
This commit is contained in:
Ryan Kurtz 2025-03-12 12:59:39 -04:00
commit 928fd09692
15 changed files with 672 additions and 137 deletions

View file

@ -308,6 +308,7 @@ src/main/help/help/topics/DataPlugin/images/CreateStructureDialog.png||GHIDRA|||
src/main/help/help/topics/DataPlugin/images/CreateStructureDialogWithTableSelection.png||GHIDRA||||END|
src/main/help/help/topics/DataPlugin/images/DataSelectionSettings.png||GHIDRA||||END|
src/main/help/help/topics/DataPlugin/images/DefaultSettings.png||GHIDRA||||END|
src/main/help/help/topics/DataPlugin/images/EditFieldDialog.png||GHIDRA||||END|
src/main/help/help/topics/DataPlugin/images/InstanceSettings.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/DataTypeSelectionDialog.htm||GHIDRA||||END|
src/main/help/help/topics/DataTypeEditors/EnumEditor.htm||GHIDRA||||END|

View file

@ -970,21 +970,14 @@
quickly changing the name of a single member:</P>
<OL>
<LI>Right mouse click on the field name of a structure member in the Code Browser</LI>
<LI>Right mouse click on a structure member in the Listing</LI>
<LI>Choose the <B>Data</B><IMG src="help/shared/arrow.gif"> <B>Rename Field</B>
option</LI>
<LI>Choose the <B>Data</B><IMG src="help/shared/arrow.gif"> <B>Edit Field</B>
action to bring the up the <A href=
"#Edit_Field_Dialog">Edit Field Dialog</A> </LI>
<LI>Enter a new name in the <B>Rename Data Field</B> dialog</LI>
</OL>
<BLOCKQUOTE>
<P><IMG src="help/shared/note.png">The&nbsp; "Field Name" field must be added to the
"Open Data" tab in the Code Browser <A href=
"../CodeBrowserPlugin/Browser_Field_Formatter.htm">header</A> in order for the
data structure field names to show up in the Code Browser.</P>
</BLOCKQUOTE>
<P>The second way is more useful for changing the names of multiple members:</P>
<OL>
@ -996,10 +989,6 @@
<LI>Edit the field name for the structure member</LI>
</OL>
<BLOCKQUOTE>
<P><IMG src="help/shared/note.png"> You cannot set the field name of undefined
member</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
</BLOCKQUOTE>
@ -1374,6 +1363,27 @@
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="Edit_Field"></A>Quick Editing of a Structure or Union Field</H2>
<BLOCKQUOTE>
<P>As a convenience, a structure or union field can be edited directly from the listing without
bringing up the entire structure or union editor. To edit a field, click anywhere on the line
displaying that field in the listing and then right click and select <B>Data</B><IMG
src="help/shared/arrow.gif"> <B>Edit Field</B> from the popup context menu.</P>
<H3><A name="Edit_Field_Dialog"></A>Edit Field Dialog</H3>
<P align="center"><IMG src="images/EditFieldDialog.png" alt=""> &nbsp;</P>
<UL>
<LI><B>Field Name</B>: The name of the structure or union field can be changed here.</LI>
<LI><B>Comment</B>: The comment for the field can be entered or changed here.</LI>
<LI><B>DataType</B>: The data can be changed here. The text field is read only so you must
press the ... button to bring up the datatype chooser to change the datatype.</LI>
<P><IMG src="help/shared/note.png"> If a default field (a field with an undefined
datatype (??)) is named or given a comment, the datatype will be set to <B>undefined1</B> if
no specific datatype is set. This is because undefined fields are not stored and therefore
can't have an associated name or comment.</P>
</BLOCKQUOTE>
<P class="providedbyplugin">Provided By: <I>Data</I> Plugin</P>
<P class="relatedtopic">Related Topics:</P>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -34,6 +34,7 @@ import ghidra.app.plugin.core.compositeeditor.*;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.services.DataService;
import ghidra.app.services.DataTypeManagerService;
import ghidra.docking.settings.SettingsDefinition;
@ -100,7 +101,6 @@ public class DataPlugin extends Plugin implements DataService {
private DockingAction editDataTypeAction;
private CreateStructureAction createStructureAction;
private CreateArrayAction createArrayAction;
private RenameDataFieldAction renameDataFieldAction;
private List<DataAction> favoriteActions = new ArrayList<>();
@ -113,7 +113,7 @@ public class DataPlugin extends Plugin implements DataService {
public DataPlugin(PluginTool tool) {
super(tool);
addActions();
createActions();
favoritesUpdateManager = new SwingUpdateManager(1000, 30000, () -> updateFavoriteActions());
}
@ -135,7 +135,7 @@ public class DataPlugin extends Plugin implements DataService {
/**
* Add actions
*/
private void addActions() {
private void createActions() {
recentlyUsedAction = new RecentlyUsedAction(this);
recentlyUsedAction.setEnabled(false);
@ -147,12 +147,17 @@ public class DataPlugin extends Plugin implements DataService {
createArrayAction = new CreateArrayAction(this);
tool.addAction(createArrayAction);
renameDataFieldAction = new RenameDataFieldAction(this);
tool.addAction(renameDataFieldAction);
pointerAction = new PointerDataAction(this);
tool.addAction(pointerAction);
new ActionBuilder("Edit Field", getName())
.popupMenuPath("Data", "Edit Field")
.keyBinding("ctrl shift E")
.withContext(ListingActionContext.class)
.enabledWhen(this::canEditField)
.onAction(this::editField)
.buildAndInstall(tool);
// Data instance settings action based upon data selection in listing
new ActionBuilder("Data Settings", getName()).sharedKeyBinding()
.popupMenuPath(DATA_SETTINGS_POPUP_PATH)
@ -832,4 +837,27 @@ public class DataPlugin extends Plugin implements DataService {
return true;
}
public DataType pickDataType() {
return dtmService.getDataType("");
}
private boolean canEditField(ListingActionContext context) {
ProgramLocation location = context.getLocation();
int[] componentPath = location.getComponentPath();
return componentPath != null && componentPath.length > 0;
}
private void editField(ListingActionContext context) {
Program program = context.getProgram();
ProgramLocation location = context.getLocation();
Address address = location.getAddress();
int[] path = location.getComponentPath();
DataTypeComponent component = DataTypeUtils.getDataTypeComponent(program, address, path);
if (component != null) {
EditDataFieldDialog dialog =
new EditDataFieldDialog(tool, dtmService, location, component);
tool.showDialog(dialog);
}
}
}

View file

@ -0,0 +1,305 @@
/* ###
* 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.data;
import java.awt.BorderLayout;
import javax.swing.*;
import org.apache.commons.lang3.StringUtils;
import docking.DialogComponentProvider;
import docking.widgets.button.BrowseButton;
import ghidra.app.cmd.data.CreateDataInStructureCmd;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.cmd.Command;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.MessageType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.layout.PairLayout;
/**
* Dialog for editing the name, comment, and datatype for a structure or union field.
*/
public class EditDataFieldDialog extends DialogComponentProvider {
private JTextField nameField;
private JTextField commentField;
private JTextField dataTypeTextField;
private DataTypeComponent component;
private PluginTool tool;
private DataType newDataType;
private ProgramLocation programLocation;
private DataTypeManagerService dtmService;
/**
* Constructor
* @param tool The tool hosting this dialog
* @param dtmService the DataTypeManagerService used for choosing datatypes
* @param location the location of the field being edited
* @param dataTypeComponent the component of the field being edited
*/
public EditDataFieldDialog(PluginTool tool, DataTypeManagerService dtmService,
ProgramLocation location, DataTypeComponent dataTypeComponent) {
super("Edit Field Dialog", true, true, true, false);
this.tool = tool;
this.dtmService = dtmService;
this.programLocation = location;
this.component = dataTypeComponent;
setTitle(generateTitle());
addWorkPanel(buildMainPanel());
initializeFields();
setFocusComponent(nameField);
setHelpLocation(new HelpLocation("DataPlugin", "Edit_Field_Dialog"));
addOKButton();
addCancelButton();
}
@Override
public void dispose() {
super.dispose();
programLocation = null;
component = null;
tool = null;
}
/**
* Returns the pending new datatype to change to.
* @return the pending new datatype to change to
*/
public DataType getNewDataType() {
return newDataType != null ? newDataType : new Undefined1DataType();
}
/**
* Returns the text currently in the text field for the field name.
* @return the text currently in the text field for the field name
*/
public String getNameText() {
return nameField.getText();
}
/**
* Sets the dialog's name text field to the given text.
* @param newName the text to put into the name text field
*/
public void setNameText(String newName) {
nameField.setText(newName);
}
/**
* Returns the text currently in the text field for the field comment.
* @return the text currently in the text field for the field commment
*/
public String getCommentText() {
return commentField.getText();
}
/**
* Sets the dialog's comment text field to the given text.
* @param newComment the text to put into the comment text field
*/
public void setCommentText(String newComment) {
commentField.setText(newComment);
}
/**
* Sets the pending new datatype and updates the datatype text field to the name of that
* datatype.
* @param dataType the new pending datatype
*/
public void setDataType(DataType dataType) {
newDataType = dataType;
updateDataTypeTextField();
}
private void initializeFields() {
String name = component.getFieldName();
if (StringUtils.isBlank(name)) {
name = component.getDefaultFieldName();
}
nameField.setText(name);
commentField.setText(component.getComment());
dataTypeTextField.setText(component.getDataType().getDisplayName());
}
@Override
protected void okCallback() {
if (updateComponent()) {
close();
programLocation = null;
}
}
private boolean updateComponent() {
if (!hasChanges()) {
return true;
}
Command<Program> cmd = new UpdateDataComponentCommand();
if (!tool.execute(cmd, programLocation.getProgram())) {
setStatusText(cmd.getStatusMsg(), MessageType.ERROR);
return false;
}
return true;
}
private boolean hasChanges() {
return hasNameChange() || hasCommentChange() || hasDataTypeChange();
}
private boolean hasCommentChange() {
String newComment = getNewFieldComment();
if (StringUtils.isBlank(newComment) && StringUtils.isBlank(component.getComment())) {
return false;
}
return !newComment.equals(component.getComment());
}
boolean hasDataTypeChange() {
return newDataType != null && !newDataType.equals(component.getDataType());
}
boolean hasNameChange() {
String newName = getNewFieldName();
if (newName.equals(component.getFieldName())) {
return false;
}
if (newName.equals(component.getDefaultFieldName())) {
return false;
}
return true;
}
private String getNewFieldName() {
return nameField.getText().trim();
}
private String getNewFieldComment() {
return commentField.getText().trim();
}
private JPanel buildMainPanel() {
JPanel panel = new JPanel(new PairLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
nameField = new JTextField(20);
nameField.setEditable(true);
nameField.addActionListener(e -> okCallback());
commentField = new JTextField(20);
commentField.setEditable(true);
commentField.addActionListener(e -> okCallback());
panel.add(new JLabel("Field Name:", SwingConstants.LEFT));
panel.add(nameField);
panel.add(new JLabel("Comment:", SwingConstants.LEFT));
panel.add(commentField);
panel.add(new JLabel("Datatype:", SwingConstants.LEFT));
panel.add(buildDataTypeChooserPanel());
return panel;
}
private JPanel buildDataTypeChooserPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 0));
dataTypeTextField = new JTextField();
dataTypeTextField.setEditable(false);
BrowseButton browseButton = new BrowseButton();
browseButton.setToolTipText("Browse the Data Manager");
browseButton.addActionListener(e -> showDataTypeBrowser());
panel.add(dataTypeTextField, BorderLayout.CENTER);
panel.add(browseButton, BorderLayout.EAST);
return panel;
}
private void showDataTypeBrowser() {
newDataType = dtmService.getDataType("");
updateDataTypeTextField();
}
private void updateDataTypeTextField() {
if (newDataType != null) {
dataTypeTextField.setText(newDataType.getDisplayName());
}
else {
dataTypeTextField.setText(component.getDataType().getDisplayName());
}
}
private String generateTitle() {
DataType parent = component.getParent();
String compositeName = parent.getName();
return "Edit " + compositeName + ", Field " + component.getOrdinal();
}
public String getDataTypeText() {
return dataTypeTextField.getText();
}
private class UpdateDataComponentCommand implements Command<Program> {
private String statusMessage = null;
@Override
public boolean applyTo(Program program) {
if (component.isUndefined() || hasDataTypeChange()) {
DataType dt = getNewDataType();
Address address = programLocation.getAddress();
int[] path = programLocation.getComponentPath();
Command<Program> cmd = new CreateDataInStructureCmd(address, path, dt, false);
if (!cmd.applyTo(program)) {
statusMessage = cmd.getStatusMsg();
return false;
}
component = DataTypeUtils.getDataTypeComponent(program, address, path);
}
if (hasNameChange()) {
try {
component.setFieldName(getNewFieldName());
}
catch (DuplicateNameException e) {
statusMessage = "Duplicate field name";
return false;
}
}
if (hasCommentChange()) {
component.setComment(getNewFieldComment());
}
return true;
}
@Override
public String getStatusMsg() {
return statusMessage;
}
@Override
public String getName() {
return "Update Structure Field";
}
}
}

View file

@ -1,89 +0,0 @@
/* ###
* 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.data;
import java.awt.event.KeyEvent;
import docking.action.KeyBindingData;
import docking.action.MenuData;
import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ListingContextAction;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.util.FieldNameFieldLocation;
import ghidra.program.util.ProgramLocation;
/**
* Base class for comment actions to edit and delete comments.
*/
class RenameDataFieldAction extends ListingContextAction {
private DataPlugin plugin;
public RenameDataFieldAction(DataPlugin plugin) {
super("Rename Data Field", plugin.getName());
setPopupMenuData(
new MenuData(
new String[] { "Data", "Rename Field" }, null, "BasicData"));
setKeyBindingData(new KeyBindingData(
KeyEvent.VK_N, 0));
this.plugin = plugin;
setEnabled(true);
}
@Override
protected void actionPerformed(ListingActionContext context) {
ListingActionContext programActionContext =
(ListingActionContext) context.getContextObject();
PluginTool tool = plugin.getTool();
Program program = programActionContext.getProgram();
ProgramLocation loc = programActionContext.getLocation();
Data data = program.getListing().getDataContaining(loc.getAddress());
DataType type = data.getDataType();
if (type instanceof Composite) {
Composite comp = (Composite) type;
int[] compPath = loc.getComponentPath();
for (int i = 0; i < compPath.length - 1; i++) {
DataTypeComponent subComp = comp.getComponent(compPath[i]);
type = subComp.getDataType();
if (type instanceof Composite) {
comp = (Composite) type;
}
else {
return;
}
}
Data instance = data.getComponent(compPath);
DataTypeComponent subComp = comp.getComponent(compPath[compPath.length - 1]);
RenameDataFieldDialog dialog = new RenameDataFieldDialog(plugin);
dialog.setDataComponent(program, subComp, instance.getFieldName());
tool.showDialog(dialog);
}
}
@Override
protected boolean isEnabledForContext(ListingActionContext context) {
return (context.getLocation() instanceof FieldNameFieldLocation);
}
}

View file

@ -24,8 +24,12 @@ import javax.swing.Icon;
import generic.theme.GColor;
import generic.theme.GIcon;
import ghidra.app.services.DataTypeQueryService;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import resources.MultiIcon;
@ -469,6 +473,30 @@ public class DataTypeUtils {
return index;
}
/**
* Finds the DataTypeComponent at an address and component path in a program.
* @param program the program to look for a datatype component
* @param address the address to look for a datatype component
* @param componentPath the component path (an array of indexes into hierarchy of nested
* datatypes)
* @return The datatype component at that address and component path or null if there is
* none at that location.
*/
public static DataTypeComponent getDataTypeComponent(Program program, Address address,
int[] componentPath) {
Data data = program.getListing().getDataContaining(address);
DataType dt = data.getDataType();
DataTypeComponent comp = null;
for (int i = 0; i < componentPath.length; i++) {
if (!(dt instanceof Composite composite)) {
return null;
}
comp = composite.getComponent(componentPath[i]);
dt = comp.getDataType();
}
return comp;
}
// finds the index of the first element in the given list--this is used in conjunction with
// the binary search, which doesn't produce the desired results when searching lists with
// duplicates

View file

@ -383,7 +383,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList
new ActionBuilder("Search Text", getName())
.menuPath("&Search", "Program &Text...")
.menuGroup("search", subGroup)
.keyBinding("ctrl shift E")
.keyBinding("ctrl F")
.description(DESCRIPTION)
.helpLocation(new HelpLocation(HelpTopics.SEARCH, "Search Text"))
.withContext(NavigatableActionContext.class, true)

View file

@ -0,0 +1,232 @@
/* ###
* 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.data;
import static org.junit.Assert.*;
import org.junit.*;
import docking.action.DockingActionIf;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.test.*;
public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env;
private PluginTool tool;
private Program program;
private DockingActionIf editFieldAction;
private EditDataFieldDialog dialog;
private CodeBrowserPlugin codeBrowser;
private Structure structure;
@Before
public void setUp() throws Exception {
env = new TestEnv();
tool = env.getTool();
tool.addPlugin(DataPlugin.class.getName());
tool.addPlugin(CodeBrowserPlugin.class.getName());
DataPlugin plugin = getPlugin(tool, DataPlugin.class);
codeBrowser = env.getPlugin(CodeBrowserPlugin.class);
program = buildProgram();
env.open(program);
env.showTool();
editFieldAction = getAction(plugin, "Edit Field");
Data dataAt = program.getListing().getDataAt(addr(0x100));
structure = (Structure) dataAt.getDataType();
codeBrowser.toggleOpen(dataAt);
waitForSwing();
}
private Program buildProgram() throws Exception {
ToyProgramBuilder builder = new ToyProgramBuilder("Test", true);
builder.createMemory("Test", "0", 1000);
StructureDataType struct = new StructureDataType("TestStruct", 4);
struct.add(new WordDataType(), "count", "This is the count field");
struct.add(new WordDataType(), "color", "This is the color field");
builder.applyDataType("0x100", struct);
return builder.getProgram();
}
@After
public void tearDown() throws Exception {
env.dispose();
}
@Test
public void testEditDefinedFieldName() {
goTo(0x104);
showFieldEditDialog();
assertEquals("count", structure.getComponent(4).getFieldName());
assertEquals("count", getNameText());
setNameText("weight");
pressOk();
waitForTasks();
assertEquals("weight", structure.getComponent(4).getFieldName());
}
@Test
public void testEditDefinedFieldComment() {
goTo(0x104);
showFieldEditDialog();
assertEquals("This is the count field", structure.getComponent(4).getComment());
assertEquals("This is the count field", getCommentText());
setCommentText("This is the weight field");
pressOk();
waitForTasks();
assertEquals("This is the weight field", structure.getComponent(4).getComment());
}
@Test
public void testEditDefinedFieldDataType() {
goTo(0x104);
showFieldEditDialog();
assertEquals("word", structure.getComponent(4).getDataType().getDisplayName());
assertEquals("word", getDataTypeText());
setDataType(new CharDataType());
pressOk();
waitForTasks();
assertFalse(isDialogVisible());
assertEquals("char", structure.getComponent(4).getDataType().getDisplayName());
}
@Test
public void testEditUndefinedFieldName() {
goTo(0x101);
showFieldEditDialog();
assertNull(structure.getComponent(1).getFieldName());
assertEquals("field1_0x1", getNameText());
setNameText("abc");
pressOk();
waitForTasks();
assertEquals("abc", structure.getComponent(1).getFieldName());
assertEquals("undefined1", structure.getComponent(1).getDataType().getDisplayName());
}
@Test
public void testEditUndefinedComment() {
goTo(0x101);
showFieldEditDialog();
assertNull(structure.getComponent(1).getComment());
assertEquals("", getCommentText());
setCommentText("comment");
pressOk();
waitForTasks();
assertEquals("comment", structure.getComponent(1).getComment());
assertEquals("undefined1", structure.getComponent(1).getDataType().getDisplayName());
}
@Test
public void testEditUndefinedDataType() {
goTo(0x101);
showFieldEditDialog();
assertNull(structure.getComponent(1).getComment());
assertEquals("undefined", getDataTypeText());
setDataType(new ByteDataType());
pressOk();
waitForTasks();
assertEquals("byte", structure.getComponent(1).getDataType().getDisplayName());
}
@Test
public void testRenameToDuplicateNameError() {
goTo(0x104);
showFieldEditDialog();
assertEquals("count", structure.getComponent(4).getFieldName());
assertEquals("count", getNameText());
setNameText("color");
pressOk();
waitForTasks();
assertTrue(isDialogVisible());
assertEquals("Duplicate field name", getDialogStatusText());
assertEquals("count", structure.getComponent(4).getFieldName());
}
private boolean isDialogVisible() {
return runSwing(() -> dialog.isVisible());
}
private void showFieldEditDialog() {
performAction(editFieldAction, false);
dialog = waitForDialogComponent(EditDataFieldDialog.class);
}
private void goTo(long addressOffset) {
Address address = addr(addressOffset);
codeBrowser.goToField(address, "Address", 0, 0);
waitForSwing();
}
private Address addr(long offset) {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
}
private void pressOk() {
runSwing(() -> dialog.okCallback());
}
private String getNameText() {
return runSwing(() -> dialog.getNameText());
}
private void setNameText(String newName) {
runSwing(() -> dialog.setNameText(newName));
}
private String getCommentText() {
return runSwing(() -> dialog.getCommentText());
}
private void setCommentText(String newComment) {
runSwing(() -> dialog.setCommentText(newComment));
}
private String getDataTypeText() {
return runSwing(() -> dialog.getDataTypeText());
}
private void setDataType(DataType dataType) {
runSwing(() -> dialog.setDataType(dataType));
}
private String getDialogStatusText() {
return runSwing(() -> dialog.getStatusText());
}
}

View file

@ -455,11 +455,8 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
return InternalDataTypeComponent.toString(this);
}
/**
* Determine if component is an undefined filler component
* @return true if undefined filler component, else false
*/
boolean isUndefined() {
@Override
public boolean isUndefined() {
return record == null && cachedDataType == null;
}

View file

@ -146,6 +146,11 @@ public class AlignedStructureInspector extends AlignedStructurePacker {
this.dataType = dataType;
}
@Override
public boolean isUndefined() {
return component.isUndefined();
}
}
/**

View file

@ -173,4 +173,10 @@ public interface DataTypeComponent {
return false;
}
/**
* Returns true if this this component is not defined. It is just a placeholder.
* @return true if this this component is not defined. It is just a placeholder.
*/
public boolean isUndefined();
}

View file

@ -334,11 +334,8 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
dataType = dt;
}
/**
* Determine if component is an undefined filler component
* @return true if undefined filler component, else false
*/
boolean isUndefined() {
@Override
public boolean isUndefined() {
return dataType == DataType.DEFAULT;
}

View file

@ -195,4 +195,9 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl
return s1.equals(s2);
}
@Override
public boolean isUndefined() {
return dataType == DataType.DEFAULT;
}
}

View file

@ -39,6 +39,16 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
captureDialog(500, 400);
}
@Test
public void testEditFieldDialog() {
positionListingTop(0x400080);
positionCursor(0x400080, "+");
leftClickCursor();
positionListingTop(0x4000a4);
performAction("Edit Field", "DataPlugin", false);
captureDialog();
}
@Test
public void testCreateStructureDialogWithTableSelection() {
positionListingTop(0x40d3a4);