diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/Data.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/Data.htm index 5d9100e00c..240fd2e5e5 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/Data.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/Data.htm @@ -1376,6 +1376,10 @@
If a default field (a field with an undefined
datatype (??)) is named or given a comment, the datatype will be set to undefined1 if
no specific datatype is set. This is because undefined fields are not stored and therefore
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/images/EditFieldDialog.png b/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/images/EditFieldDialog.png
index 47f90f76f2..e0123004a4 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/images/EditFieldDialog.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataPlugin/images/EditFieldDialog.png differ
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java
index 8d0df057a2..aa577f5fb9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java
@@ -109,6 +109,8 @@ public class DataPlugin extends Plugin implements DataService {
private DataTypeManagerChangeListenerAdapter adapter;
private SwingUpdateManager favoritesUpdateManager;
+ private boolean addAddress;
+ private boolean addDate;
public DataPlugin(PluginTool tool) {
super(tool);
@@ -855,8 +857,10 @@ public class DataPlugin extends Plugin implements DataService {
DataTypeComponent component = DataTypeUtils.getDataTypeComponent(program, address, path);
if (component != null) {
EditDataFieldDialog dialog =
- new EditDataFieldDialog(tool, dtmService, location, component);
+ new EditDataFieldDialog(tool, dtmService, location, component, addAddress, addDate);
tool.showDialog(dialog);
+ addAddress = dialog.isAddAddressSelected();
+ addDate = dialog.isAddDateSelected();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/EditDataFieldDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/EditDataFieldDialog.java
index e9f4450e2b..9de0f6046e 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/EditDataFieldDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/EditDataFieldDialog.java
@@ -16,6 +16,9 @@
package ghidra.app.plugin.core.data;
import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.util.Date;
import javax.swing.*;
@@ -32,8 +35,7 @@ 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.*;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.layout.PairLayout;
@@ -51,6 +53,8 @@ public class EditDataFieldDialog extends DialogComponentProvider {
private DataType newDataType;
private ProgramLocation programLocation;
private DataTypeManagerService dtmService;
+ private JCheckBox addressCheckBox;
+ private JCheckBox dateCheckBox;
/**
* Constructor
@@ -58,9 +62,12 @@ public class EditDataFieldDialog extends DialogComponentProvider {
* @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
+ * @param addDate selects the addDate checkbox
+ * @param addAddress selects the addDate checkbox
*/
public EditDataFieldDialog(PluginTool tool, DataTypeManagerService dtmService,
- ProgramLocation location, DataTypeComponent dataTypeComponent) {
+ ProgramLocation location, DataTypeComponent dataTypeComponent, boolean addAddress,
+ boolean addDate) {
super("Edit Field Dialog", true, true, true, false);
this.tool = tool;
this.dtmService = dtmService;
@@ -69,7 +76,7 @@ public class EditDataFieldDialog extends DialogComponentProvider {
setTitle(generateTitle());
addWorkPanel(buildMainPanel());
- initializeFields();
+ initializeFields(addAddress, addDate);
setFocusComponent(nameField);
setHelpLocation(new HelpLocation("DataPlugin", "Edit_Field_Dialog"));
@@ -135,14 +142,43 @@ public class EditDataFieldDialog extends DialogComponentProvider {
updateDataTypeTextField();
}
- private void initializeFields() {
+ /**
+ * Returns true if the Add Address checkbox is selected.
+ * @return true if the Add Address checkbox is selected
+ */
+ public boolean isAddAddressSelected() {
+ return addressCheckBox.isSelected();
+ }
+
+ /**
+ * Returns true if the Add Date checkbox is selected.
+ * @return true if the Add Address checkbox is selected
+ */
+ public boolean isAddDateSelected() {
+ return dateCheckBox.isSelected();
+ }
+
+ private void initializeFields(boolean addAddress, boolean addDate) {
String name = component.getFieldName();
if (StringUtils.isBlank(name)) {
name = "";
}
nameField.setText(name);
- commentField.setText(component.getComment());
+ String comment = component.getComment();
+ if (comment == null) {
+ comment = "";
+ }
+ commentField.setText(comment);
dataTypeTextField.setText(component.getDataType().getDisplayName());
+
+ if (addAddress) {
+ addressCheckBox.setSelected(true);
+ addTextToComment(getCurrentAddressString());
+ }
+ if (addDate) {
+ dateCheckBox.setSelected(true);
+ addTextToComment(getTodaysDate());
+ }
}
@Override
@@ -202,6 +238,27 @@ public class EditDataFieldDialog extends DialogComponentProvider {
}
private JPanel buildMainPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(buildNameValuePanel(), BorderLayout.NORTH);
+ panel.add(buildCheckboxPanel(), BorderLayout.SOUTH);
+ return panel;
+ }
+
+ private JPanel buildCheckboxPanel() {
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 30, 0));
+
+ addressCheckBox = new JCheckBox("Add Current Address");
+ addressCheckBox.addActionListener(this::addressCheckBoxChanged);
+
+ dateCheckBox = new JCheckBox("Add Today's Date");
+ dateCheckBox.addActionListener(this::dateCheckBoxChanged);
+
+ panel.add(addressCheckBox);
+ panel.add(dateCheckBox);
+ return panel;
+ }
+
+ private JPanel buildNameValuePanel() {
JPanel panel = new JPanel(new PairLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
@@ -214,10 +271,10 @@ public class EditDataFieldDialog extends DialogComponentProvider {
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());
+ panel.add(new JLabel("Comment:", SwingConstants.LEFT));
+ panel.add(commentField);
return panel;
}
@@ -256,6 +313,58 @@ public class EditDataFieldDialog extends DialogComponentProvider {
return "Edit " + compositeName + ", Field " + component.getOrdinal();
}
+ private void dateCheckBoxChanged(ActionEvent e) {
+ String today = getTodaysDate();
+ if (dateCheckBox.isSelected()) {
+ addTextToComment(today);
+ }
+ else {
+ removeTextFromComment(today);
+ }
+ }
+
+ private void addressCheckBoxChanged(ActionEvent e) {
+ String address = getCurrentAddressString();
+ if (addressCheckBox.isSelected()) {
+ addTextToComment(address);
+ }
+ else {
+ removeTextFromComment(address);
+ }
+ }
+
+ private void removeTextFromComment(String text) {
+ String comment = commentField.getText().trim();
+ int index = comment.indexOf(text);
+ if (index < 0) {
+ return;
+ }
+
+ // remove the given text and any spaces that follow it.
+ comment = comment.replaceAll(text + "\\s*", "");
+ commentField.setText(comment.trim());
+ }
+
+ private String getTodaysDate() {
+ return DateUtils.formatCompactDate(new Date());
+ }
+
+ private String getCurrentAddressString() {
+ return programLocation.getAddress().toString();
+ }
+
+ private void addTextToComment(String text) {
+ String comment = commentField.getText().trim();
+ if (comment.contains(text)) {
+ return;
+ }
+ if (!comment.isBlank()) {
+ comment += " ";
+ }
+ comment += text;
+ commentField.setText(comment.trim());
+ }
+
public String getDataTypeText() {
return dataTypeTextField.getText();
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/EditFieldDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/EditFieldDialogTest.java
index adcd62d4d1..a450111d7a 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/EditFieldDialogTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/EditFieldDialogTest.java
@@ -17,6 +17,8 @@ package ghidra.app.plugin.core.data;
import static org.junit.Assert.*;
+import java.util.Date;
+
import org.junit.*;
import docking.action.DockingActionIf;
@@ -27,6 +29,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.test.*;
+import ghidra.util.DateUtils;
public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env;
@@ -162,6 +165,49 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals("byte", structure.getComponent(1).getDataType().getDisplayName());
}
+ @Test
+ public void testAddAddressCheckbox() {
+ goTo(0x101);
+ showFieldEditDialog();
+ assertEquals("", getCommentText());
+ pressButtonByText(dialog.getComponent(), "Add Current Address");
+ assertEquals("00000101", getCommentText());
+
+ pressOk();
+ waitForTasks();
+ assertEquals("00000101", structure.getComponent(1).getComment());
+
+ showFieldEditDialog();
+ assertEquals("00000101", getCommentText());
+ pressButtonByText(dialog.getComponent(), "Add Current Address");
+ assertEquals("", getCommentText());
+
+ pressOk();
+ assertNull(structure.getComponent(1).getComment());
+ }
+
+ @Test
+ public void testAddDateCheckbox() {
+ String today = DateUtils.formatCompactDate(new Date());
+ goTo(0x101);
+ showFieldEditDialog();
+ assertEquals("", getCommentText());
+ pressButtonByText(dialog.getComponent(), "Add Today's Date");
+ assertEquals(today, getCommentText());
+
+ pressOk();
+ waitForTasks();
+ assertEquals(today, structure.getComponent(1).getComment());
+
+ showFieldEditDialog();
+ assertEquals(today, getCommentText());
+ pressButtonByText(dialog.getComponent(), "Add Today's Date");
+ assertEquals("", getCommentText());
+
+ pressOk();
+ assertNull(structure.getComponent(1).getComment());
+ }
+
private boolean isDialogVisible() {
return runSwing(() -> dialog.isVisible());
}
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/DateUtils.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/DateUtils.java
index 942979a615..997a4fd906 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/DateUtils.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/DateUtils.java
@@ -4,9 +4,9 @@
* 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.
@@ -31,6 +31,7 @@ public class DateUtils {
/** Example: Oct 31, 2019 03:24 PM */
private static final String DATE_TIME_FORMAT_STRING = "MMM dd, yyyy hh:mm a";
private static final String DATE_FORMAT_STRING = "MM/dd/yyyy";
+ private static final String COMPACT_DATE_FORMAT_STRING = "MM/dd/yy";
private static final String TIME_FORMAT_STRING = "h:mm";
private static final DateTimeFormatter DATE_TIME_FORMATTER =
@@ -39,6 +40,8 @@ public class DateUtils {
DateTimeFormatter.ofPattern(DATE_FORMAT_STRING);
private static final DateTimeFormatter TIME_FORMATTER =
DateTimeFormatter.ofPattern(TIME_FORMAT_STRING);
+ private static final DateTimeFormatter COMPACT_DATE_FORMATTER =
+ DateTimeFormatter.ofPattern(COMPACT_DATE_FORMAT_STRING);
public static final long MS_PER_SEC = 1000;
public static final long MS_PER_MIN = MS_PER_SEC * 60;
@@ -227,6 +230,16 @@ public class DateUtils {
return DATE_FORMATTER.format(toLocalDate(date));
}
+ /**
+ * Formats the given date into a compact date string (mm/dd/yy).
+ *
+ * @param date the date to format
+ * @return the date string
+ */
+ public static String formatCompactDate(Date date) {
+ return COMPACT_DATE_FORMATTER.format(toLocalDate(date));
+ }
+
/**
* Formats the given date into a string that contains the date and time. This is in
* contrast to {@link #formatDate(Date)}, which only returns a date string.
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java
index 65d0427666..aa733c0e8c 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java
@@ -15,11 +15,16 @@
*/
package help.screenshot;
+import java.util.Set;
+import java.util.stream.Collectors;
+
import javax.swing.JRadioButton;
import org.junit.Test;
-import docking.DialogComponentProvider;
+import docking.*;
+import docking.action.DockingActionIf;
+import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.util.table.GhidraTable;
public class DataPluginScreenShots extends GhidraScreenShotGenerator {
@@ -75,7 +80,10 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
@Test
public void testDefaultSettings() {
positionListingTop(0x40d3a4);
- performAction("Default Data Settings", "DataPlugin", false);
+ ComponentProvider componentProvider = getProvider(CodeViewerProvider.class);
+ ActionContext actionContext = componentProvider.getActionContext(null);
+ DockingActionIf action = getAction("Default Settings", actionContext);
+ performAction(action, actionContext, false);
captureDialog();
}
@@ -86,4 +94,29 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
captureDialog();
}
+ private DockingActionIf getAction(String name, ActionContext context) {
+ Set