Merge remote-tracking branch

'origin/GP-4577-dragonmacher-enum-editor-find-uses--SQUASHED'
(Closes #6475)
This commit is contained in:
Ryan Kurtz 2024-05-22 12:26:50 -04:00
commit 1371bb50c9
11 changed files with 223 additions and 89 deletions

View File

@ -21,7 +21,6 @@ import javax.swing.*;
import docking.action.*;
import docking.widgets.table.GTable;
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation;
@ -34,8 +33,6 @@ import ghidra.util.HelpLocation;
*/
abstract public class CompositeEditorTableAction extends DockingAction implements EditorAction {
private static final String PREFIX = DataTypeEditorManager.EDIT_ACTION_PREFIX;
protected CompositeEditorProvider provider;
protected CompositeEditorModel model;
protected String tooltip;
@ -57,8 +54,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group,
String[] popupPath, String[] menuPath, Icon icon) {
super(PREFIX + name, provider.plugin.getName(),
KeyBindingType.SHARED);
super(name, provider.plugin.getName(), KeyBindingType.SHARED);
init(provider);
if (menuPath != null) {
setMenuBarData(new MenuData(menuPath, icon, group));
@ -114,11 +110,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
abstract public void adjustEnablement();
public String getHelpName() {
String actionName = getName();
if (actionName.startsWith(PREFIX)) {
actionName = actionName.substring(PREFIX.length());
}
return actionName;
return getName();
}
@Override

View File

@ -24,17 +24,15 @@ import ghidra.program.model.data.DataTypeComponent;
import ghidra.util.*;
/**
* An action to show references to the field in the currently selected editor row
* An action to show references to the field in the currently selected editor row.
*/
public class FindReferencesToField extends CompositeEditorTableAction {
public class FindReferencesToStructureFieldAction extends CompositeEditorTableAction {
public final static String ACTION_NAME = "Find Uses of";
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
private final static String ACTION_NAME = "Find Uses of";
private final static String DESCRIPTION = "Find uses of field in the selected row";
private static String[] popupPath = new String[] { ACTION_NAME };
public FindReferencesToField(CompositeEditorProvider provider) {
super(provider, ACTION_NAME, GROUP_NAME, popupPath, null, null);
public FindReferencesToStructureFieldAction(CompositeEditorProvider provider) {
super(provider, ACTION_NAME, BASIC_ACTION_GROUP, new String[] { ACTION_NAME }, null, null);
setDescription(DESCRIPTION);
adjustEnablement();
setHelpLocation(new HelpLocation(HelpTopics.FIND_REFERENCES, "Data_Types"));
@ -46,8 +44,8 @@ public class FindReferencesToField extends CompositeEditorTableAction {
FindAppliedDataTypesService service = tool.getService(FindAppliedDataTypesService.class);
if (service == null) {
Msg.showError(this, null, "Missing Plugin",
"The FindAppliedDataTypesService is not installed.\n" +
"Please add the plugin implementing this service.");
"The %s is not installed.\nPlease add the plugin implementing this service."
.formatted(FindAppliedDataTypesService.class.getSimpleName()));
return;
}

View File

@ -64,7 +64,7 @@ public class StructureEditorProvider extends CompositeEditorProvider {
new DeleteAction(this),
new PointerAction(this),
new ArrayAction(this),
new FindReferencesToField(this),
new FindReferencesToStructureFieldAction(this),
new UnpackageAction(this),
new EditComponentAction(this),
new EditFieldAction(this),

View File

@ -212,7 +212,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
// ZVeryLast group
addLocalAction(new FindReferencesToDataTypeAction(plugin)); // DataType
addLocalAction(new FindReferencesToFieldAction(plugin)); // DataType
addLocalAction(new FindReferencesToFieldByNameOrOffsetAction(plugin)); // DataType
addLocalAction(new FindBaseDataTypeAction(plugin)); // DataType
addLocalAction(new DisplayTypeAsGraphAction(plugin));

View File

@ -0,0 +1,79 @@
/* ###
* 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.datamgr.actions;
import docking.ActionContext;
import docking.action.*;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.services.FieldMatcher;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.data.DataType;
import ghidra.util.*;
public abstract class AbstractFindReferencesToFieldAction extends DockingAction {
// The base action name will be used to create the menu item by appending the field name
public static final String BASE_ACTION_NAME = "Find Uses of";
private Plugin plugin;
public AbstractFindReferencesToFieldAction(Plugin plugin) {
super(BASE_ACTION_NAME, plugin.getName(), KeyBindingType.SHARED);
this.plugin = plugin;
String menuGroup = "ZVeryLast"; // it's own group; on the bottom
setPopupMenuData(new MenuData(new String[] { "Find Uses of Field..." }, null, menuGroup));
setHelpLocation(new HelpLocation("LocationReferencesPlugin", "Data_Types"));
}
protected abstract DataTypeAndFields getSelectedType(ActionContext context);
protected abstract FieldMatcher createFieldMatcher(DataTypeAndFields typeAndFields);
@Override
public boolean isEnabledForContext(ActionContext context) {
return getSelectedType(context) != null;
}
@Override
public void actionPerformed(ActionContext context) {
ServiceProvider serviceProvider = plugin.getTool();
FindAppliedDataTypesService service =
serviceProvider.getService(FindAppliedDataTypesService.class);
if (service == null) {
Msg.showError(this, null, "Missing Plugin",
"The %s is not installed.\nPlease add the plugin implementing this service."
.formatted(FindAppliedDataTypesService.class.getSimpleName()));
return;
}
DataTypeAndFields typeAndFields = getSelectedType(context);
FieldMatcher fieldMatcher = createFieldMatcher(typeAndFields);
if (fieldMatcher == null) {
return; // user cancelled
}
DataType dt = fieldMatcher.getDataType();
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher));
}
public record DataTypeAndFields(DataType dataType, String[] fieldNames) {
// record
}
}

View File

@ -23,106 +23,80 @@ import javax.swing.tree.TreePath;
import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.services.FieldMatcher;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.*;
import ghidra.util.NumericUtilities;
public class FindReferencesToFieldAction extends DockingAction {
/**
* An action that can be used on a {@link Composite} or {@link Enum} to find references to a field
* by name or offset.
*/
public class FindReferencesToFieldByNameOrOffsetAction extends AbstractFindReferencesToFieldAction {
private final DataTypeManagerPlugin plugin;
public FindReferencesToFieldAction(DataTypeManagerPlugin plugin) {
super("Find Uses of Field", plugin.getName());
this.plugin = plugin;
String menuGroup = "ZVeryLast"; // it's own group; on the bottom
setPopupMenuData(new MenuData(new String[] { "Find Uses of Field..." }, null, menuGroup));
setHelpLocation(new HelpLocation("LocationReferencesPlugin", "Data_Types"));
setEnabled(true);
public FindReferencesToFieldByNameOrOffsetAction(Plugin plugin) {
super(plugin);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
protected DataTypeAndFields getSelectedType(ActionContext context) {
if (!(context instanceof DataTypesActionContext)) {
return false;
return null;
}
Object contextObject = context.getContextObject();
GTree gtree = (GTree) contextObject;
TreePath[] selectionPaths = gtree.getSelectionPaths();
if (selectionPaths.length != 1) {
return false;
return null;
}
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
if (!(node instanceof DataTypeNode)) {
return false;
return null;
}
DataTypeNode dtNode = (DataTypeNode) node;
DataType dataType = dtNode.getDataType();
return dataType instanceof Composite || dataType instanceof Enum;
DataType dt = dtNode.getDataType();
if (!(dt instanceof Composite || dt instanceof Enum)) {
return null;
}
String[] fields = null;
if (dt instanceof Composite) {
fields = getCompositeFieldNames((Composite) dt);
}
else if (dt instanceof Enum) {
fields = ((Enum) dt).getNames();
}
return new DataTypeAndFields(dt, fields);
}
@Override
public void actionPerformed(ActionContext context) {
GTree gTree = (GTree) context.getContextObject();
TreePath[] selectionPaths = gTree.getSelectionPaths();
final DataTypeNode dataTypeNode = (DataTypeNode) selectionPaths[0].getLastPathComponent();
PluginTool tool = plugin.getTool();
FindAppliedDataTypesService service = tool.getService(FindAppliedDataTypesService.class);
if (service == null) {
Msg.showError(this, null, "Missing Plugin",
"The FindAppliedDataTypesService is not installed.\n" +
"Please add the plugin implementing this service.");
return;
}
DataType dt = dataTypeNode.getDataType();
String[] choices = null;
if (dt instanceof Composite) {
choices = getCompisiteFieldNames((Composite) dt);
}
else if (dt instanceof Enum) {
choices = ((Enum) dt).getNames();
}
protected FieldMatcher createFieldMatcher(DataTypeAndFields typeAndFields) {
DataType dt = typeAndFields.dataType();
String message = "Find uses of '" + dt.getName() + "' field by name or offset";
String userChoice = OptionDialog.showEditableInputChoiceDialog(null, "Choose Field",
message, choices, null, OptionDialog.QUESTION_MESSAGE);
message, typeAndFields.fieldNames(), null, OptionDialog.QUESTION_MESSAGE);
if (userChoice == null) {
return;
return null; // cancelled
}
FieldMatcher fieldMatcher;
Long longChoice = parseInt(userChoice);
if (longChoice != null) {
fieldMatcher = new FieldMatcher(dt, longChoice.intValue());
return new FieldMatcher(dt, longChoice.intValue());
}
else {
fieldMatcher = new FieldMatcher(dt, userChoice);
}
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher));
return new FieldMatcher(dt, userChoice);
}
private Long parseInt(String s) {
return NumericUtilities.parseNumber(s, null);
}
private String[] getCompisiteFieldNames(Composite composite) {
private String[] getCompositeFieldNames(Composite composite) {
DataTypeComponent[] components = composite.getDefinedComponents();
List<String> names = new ArrayList<>();
for (DataTypeComponent dataTypeComponent : components) {
@ -139,4 +113,7 @@ public class FindReferencesToFieldAction extends DockingAction {
return names.toArray(String[]::new);
}
private Long parseInt(String s) {
return NumericUtilities.parseNumber(s, null);
}
}

View File

@ -22,6 +22,7 @@ import docking.actions.DockingToolActions;
import docking.actions.SharedDockingActionPlaceholder;
import ghidra.app.plugin.core.compositeeditor.*;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.actions.AbstractFindReferencesToFieldAction;
import ghidra.app.plugin.core.function.AbstractEditFunctionSignatureDialog;
import ghidra.framework.model.DomainObject;
import ghidra.framework.plugintool.PluginTool;
@ -145,7 +146,7 @@ public class DataTypeEditorManager implements EditorListener {
registerAction(DeleteAction.ACTION_NAME);
registerAction(PointerAction.ACTION_NAME);
registerAction(ArrayAction.ACTION_NAME);
registerAction(FindReferencesToField.ACTION_NAME);
registerAction(AbstractFindReferencesToFieldAction.BASE_ACTION_NAME);
registerAction(UnpackageAction.ACTION_NAME);
registerAction(EditComponentAction.ACTION_NAME);
registerAction(EditFieldAction.ACTION_NAME);

View File

@ -300,9 +300,8 @@ class EnumEditorPanel extends JPanel {
table.setRowHeight(table.getRowHeight() + 4);
table.setDefaultEditor(String.class, new EnumStringCellEditor());
table.getColumnModel()
.getColumn(EnumTableModel.VALUE_COL)
.setCellEditor(
new EnumLongCellEditor());
.getColumn(EnumTableModel.VALUE_COL)
.setCellEditor(new EnumLongCellEditor());
table.setDefaultRenderer(String.class, new GTableCellRenderer());
table.setDefaultRenderer(Long.class, new EnumValueRenderer());
add(createInfoPanel(), BorderLayout.SOUTH);
@ -483,6 +482,16 @@ class EnumEditorPanel extends JPanel {
showValuesAsHex = showHex;
tableModel.fireTableDataChanged();
}
String getSelectedFieldName() {
int row = table.getSelectedRow();
if (row < 0) {
return null;
}
EnumEntry enumEntry = tableModel.getRowObject(row);
return enumEntry.getName();
}
//==================================================================================================
// Inner Classes
//==================================================================================================

View File

@ -246,13 +246,21 @@ public class EnumEditorProvider extends ComponentProviderAdapter
tool.setStatusInfo(msg);
}
@Override
public DataTypeManager getDataTypeManager() {
return dataTypeManager;
}
String getCategoryText() {
return dataTypeManager.getName() + originalCategoryPath;
}
@Override
public DataTypeManager getDataTypeManager() {
return dataTypeManager;
Enum getEnum() {
return originalEnum;
}
String getSelectedFieldName() {
return editorPanel.getSelectedFieldName();
}
//==================================================================================================
@ -317,10 +325,13 @@ public class EnumEditorProvider extends ComponentProviderAdapter
showEnumAction.setToolBarData(
new ToolBarData(new GIcon("icon.plugin.enum.editor.home"), thirdGroup));
FindReferencesToEnumFieldAction findReferencesAction = new FindReferencesToEnumFieldAction(plugin);
tool.addLocalAction(this, applyAction);
tool.addLocalAction(this, addAction);
tool.addLocalAction(this, deleteAction);
tool.addLocalAction(this, showEnumAction);
tool.addLocalAction(this, findReferencesAction);
}
private boolean applyChanges() {

View File

@ -0,0 +1,67 @@
/* ###
* 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.datamgr.editor;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.MenuData;
import ghidra.app.plugin.core.datamgr.actions.AbstractFindReferencesToFieldAction;
import ghidra.app.services.FieldMatcher;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.data.DataType;
/**
* Finds references to a member of an enum.
*/
public class FindReferencesToEnumFieldAction extends AbstractFindReferencesToFieldAction {
public FindReferencesToEnumFieldAction(Plugin plugin) {
super(plugin);
}
@Override
protected DataTypeAndFields getSelectedType(ActionContext context) {
ComponentProvider provider = context.getComponentProvider();
if (!(provider instanceof EnumEditorProvider enumProvider)) {
return null;
}
String fieldName = enumProvider.getSelectedFieldName();
if (fieldName == null) {
return null;
}
updateMenuName(fieldName);
DataType dt = enumProvider.getEnum();
return new DataTypeAndFields(dt, new String[] { fieldName });
}
@Override
protected FieldMatcher createFieldMatcher(DataTypeAndFields typeAndFields) {
DataType dt = typeAndFields.dataType();
String field = typeAndFields.fieldNames()[0];
return new FieldMatcher(dt, field);
}
private void updateMenuName(String name) {
String menuName = BASE_ACTION_NAME + ' ' + name;
MenuData data = getPopupMenuData().cloneData();
data.setMenuPath(new String[] { menuName });
setPopupMenuData(data);
}
}

View File

@ -21,7 +21,7 @@ import docking.tool.ToolConstants;
/**
* A marker interface to signal that the implementing action serves as an action that should
* not be itself used in the tool, but should only be used to register and manager keybindings.
* not itself be used in the tool, but should only be used to register and manage keybindings.
*
*
* <p>This action is merely a tool by which transient components can ensure that their actions