Merge remote-tracking branch 'origin/GP-4853_dev747368_datatype_select_nodups'

This commit is contained in:
Ryan Kurtz 2024-11-07 11:47:11 -05:00
commit ad86d2d65a
12 changed files with 156 additions and 88 deletions

View File

@ -128,8 +128,8 @@ public class DataTypeTableCellEditor extends AbstractCellEditor
protected void init(int row, int column) { protected void init(int row, int column) {
updateService(); updateService();
editor = new DataTypeSelectionEditor(service, getAllowed(row, column)); editor = new DataTypeSelectionEditor(getPreferredDataTypeManager(row, column), service,
editor.setPreferredDataTypeManager(getPreferredDataTypeManager(row, column)); getAllowed(row, column));
editor.setTabCommitsEdit(true); editor.setTabCommitsEdit(true);
editor.setConsumeEnterKeyPress(false); editor.setConsumeEnterKeyPress(false);

View File

@ -255,8 +255,8 @@ public class BitFieldEditorPanel extends JPanel {
private JComponent createDataTypeChoiceEditor() { private JComponent createDataTypeChoiceEditor() {
dtChoiceEditor = dtChoiceEditor = new DataTypeSelectionEditor(composite.getDataTypeManager(), dtmService,
new DataTypeSelectionEditor(dtmService, AllowedDataTypes.BITFIELD_BASE_TYPE); AllowedDataTypes.BITFIELD_BASE_TYPE);
dtChoiceEditor.setConsumeEnterKeyPress(false); dtChoiceEditor.setConsumeEnterKeyPress(false);
dtChoiceEditor.setTabCommitsEdit(true); dtChoiceEditor.setTabCommitsEdit(true);
//dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager()); //dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager());

View File

@ -1201,13 +1201,12 @@ public abstract class CompositeEditorPanel extends JPanel
private void init() { private void init() {
Plugin plugin = provider.getPlugin(); Plugin plugin = provider.getPlugin();
final PluginTool tool = plugin.getTool(); PluginTool tool = plugin.getTool();
editor = new DataTypeSelectionEditor(tool, editor = new DataTypeSelectionEditor(model.getViewDataTypeManager(), tool,
bitfieldAllowed ? AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD bitfieldAllowed
? AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD
: AllowedDataTypes.SIZABLE_DYNAMIC); : AllowedDataTypes.SIZABLE_DYNAMIC);
editor.setTabCommitsEdit(true); editor.setTabCommitsEdit(true);
DataTypeManager originalDataTypeManager = model.getOriginalDataTypeManager();
editor.setPreferredDataTypeManager(originalDataTypeManager);
editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses
textField = editor.getDropDownTextField(); textField = editor.getDropDownTextField();

View File

@ -15,6 +15,9 @@
*/ */
package ghidra.app.plugin.core.datamgr.actions; package ghidra.app.plugin.core.datamgr.actions;
import java.util.Arrays;
import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.CellEditorListener; import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
@ -56,6 +59,11 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
} }
private JComponent createWorkPanel() { private JComponent createWorkPanel() {
List<DataTypeManager> managers = Arrays.stream(plugin.getDataTypeManagers())
.filter(dtm -> !(dtm instanceof BuiltInDataTypeManager))
.toList();
DataTypeManager defaultDTM = getDefaultDataTypeManager(managers);
JPanel panel = new JPanel(new PairLayout()); JPanel panel = new JPanel(new PairLayout());
// category info // category info
@ -68,7 +76,9 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
panel.add(nameTextField); panel.add(nameTextField);
// data type info // data type info
dataTypeEditor = new DataTypeSelectionEditor(plugin.getTool(), AllowedDataTypes.ALL); dataTypeEditor = new DataTypeSelectionEditor(
null, /* TODO: can't set default dtm for the data type selection field because the dialog allows switching between destination DTMs */
plugin.getTool(), AllowedDataTypes.ALL);
panel.add(new GLabel("Data type:")); panel.add(new GLabel("Data type:"));
panel.add(dataTypeEditor.getEditorComponent()); panel.add(dataTypeEditor.getEditorComponent());
@ -89,29 +99,8 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
dataTypeManagerBox = new GhidraComboBox<>(); dataTypeManagerBox = new GhidraComboBox<>();
dataTypeManagerBox dataTypeManagerBox
.setRenderer(GComboBoxCellRenderer.createDefaultTextRenderer(dtm -> dtm.getName())); .setRenderer(GComboBoxCellRenderer.createDefaultTextRenderer(dtm -> dtm.getName()));
dataTypeManagerBox.addToModel(managers);
DataTypeManager[] dataTypeManagers = plugin.getDataTypeManagers(); dataTypeManagerBox.setSelectedItem(defaultDTM);
for (DataTypeManager manager : dataTypeManagers) {
if (manager instanceof BuiltInDataTypeManager) {
continue; // can't add to built-in
}
dataTypeManagerBox.addToModel(manager);
}
Object itemToSelect = null;
// select the manager from where the dialog was created
Object lastPathComponent = selectedTreePath.getLastPathComponent();
if (lastPathComponent instanceof DataTypeTreeNode) {
DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) lastPathComponent;
ArchiveNode archiveNode = dataTypeTreeNode.getArchiveNode();
DataTypeManager manager = archiveNode.getArchive().getDataTypeManager();
if (dataTypeManagerBox.containsItem(manager)) {
itemToSelect = manager;
}
}
dataTypeManagerBox.setSelectedItem(itemToSelect);
panel.add(new GLabel("Archive:")); panel.add(new GLabel("Archive:"));
panel.add(dataTypeManagerBox); panel.add(dataTypeManagerBox);
@ -121,6 +110,19 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
return panel; return panel;
} }
private DataTypeManager getDefaultDataTypeManager(List<DataTypeManager> mgrs) {
// select the manager from where the dialog was created
Object lastPathComponent = selectedTreePath.getLastPathComponent();
if (lastPathComponent instanceof DataTypeTreeNode dataTypeTreeNode) {
ArchiveNode archiveNode = dataTypeTreeNode.getArchiveNode();
DataTypeManager manager = archiveNode.getArchive().getDataTypeManager();
if (mgrs.contains(manager)) {
return manager;
}
}
return null;
}
@Override @Override
protected void okCallback() { protected void okCallback() {
// are we valid? // are we valid?

View File

@ -759,8 +759,8 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
getSelectionModel().addListSelectionListener(selectionListener); getSelectionModel().addListSelectionListener(selectionListener);
// set the preferred viewport height smaller that the button panel, otherwise it is huge! // set the preferred viewport height smaller that the button panel, otherwise it is huge!
setPreferredScrollableViewportSize(new Dimension(600, 100)); setPreferredScrollableViewportSize(new Dimension(600, 100));
setDefaultEditor(DataType.class, setDefaultEditor(DataType.class, new ParameterDataTypeCellEditor(
new ParameterDataTypeCellEditor(FunctionEditorDialog.this, service)); FunctionEditorDialog.this, service, model.getProgram().getDataTypeManager()));
setDefaultRenderer(DataType.class, new ParameterDataTypeCellRenderer()); setDefaultRenderer(DataType.class, new ParameterDataTypeCellRenderer());
setDefaultEditor(VariableStorage.class, new StorageTableCellEditor(model)); setDefaultEditor(VariableStorage.class, new StorageTableCellEditor(model));
setDefaultRenderer(VariableStorage.class, new VariableStorageCellRenderer()); setDefaultRenderer(VariableStorage.class, new VariableStorageCellRenderer());

View File

@ -30,6 +30,7 @@ import docking.widgets.table.FocusableEditor;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.datatype.DataTypeSelectionEditor; import ghidra.app.util.datatype.DataTypeSelectionEditor;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.util.MessageType; import ghidra.util.MessageType;
import ghidra.util.data.DataTypeParser; import ghidra.util.data.DataTypeParser;
@ -43,27 +44,29 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor
private JPanel editorPanel; private JPanel editorPanel;
private DataTypeManagerService service; private DataTypeManagerService service;
private DialogComponentProvider dialog; private DialogComponentProvider dialog;
private DataTypeManager dtm;
ParameterDataTypeCellEditor(DialogComponentProvider dialog, DataTypeManagerService service) { ParameterDataTypeCellEditor(DialogComponentProvider dialog, DataTypeManagerService service,
DataTypeManager dtm) {
this.dialog = dialog; this.dialog = dialog;
this.service = service; this.service = service;
this.dtm = dtm;
} }
@Override @Override
public Component getTableCellEditorComponent(JTable table1, Object value, boolean isSelected, public Component getTableCellEditorComponent(JTable table1, Object value, boolean isSelected,
int row, int column) { int row, int column) {
init();
dt = (DataType) value; dt = (DataType) value;
init();
editor.setCellEditorValue(dt); editor.setCellEditorValue(dt);
return editorPanel; return editorPanel;
} }
private void init() { private void init() {
editor = new DataTypeSelectionEditor(service, DataTypeParser.AllowedDataTypes.ALL); editor = new DataTypeSelectionEditor(dtm, service, DataTypeParser.AllowedDataTypes.ALL);
editor.setTabCommitsEdit(true); editor.setTabCommitsEdit(true);
editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses

View File

@ -172,7 +172,8 @@ public class StorageAddressEditorDialog extends DialogComponentProvider
panel.add(new GLabel("Datatype: ")); panel.add(new GLabel("Datatype: "));
dataTypeEditor = new ParameterDataTypeCellEditor(this, service); dataTypeEditor =
new ParameterDataTypeCellEditor(this, service, model.getProgram().getDataTypeManager());
dataTypeEditor.addCellEditorListener(new CellEditorListener() { dataTypeEditor.addCellEditorListener(new CellEditorListener() {

View File

@ -23,11 +23,14 @@ import javax.swing.*;
import docking.widgets.DropDownSelectionTextField; import docking.widgets.DropDownSelectionTextField;
import docking.widgets.DropDownTextFieldDataModel; import docking.widgets.DropDownTextFieldDataModel;
import docking.widgets.list.GListCellRenderer; import docking.widgets.list.GListCellRenderer;
import ghidra.app.plugin.core.compositeeditor.CompositeViewerDataTypeManager;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.ToolTipUtils; import ghidra.app.util.ToolTipUtils;
import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
/** /**
@ -36,13 +39,25 @@ import ghidra.util.exception.AssertException;
*/ */
public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldDataModel<DataType> { public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldDataModel<DataType> {
private final DataTypeManager dtm; // preferred data type manager
private final DataTypeManagerService dataTypeService; private final DataTypeManagerService dataTypeService;
public DataTypeDropDownSelectionDataModel(ServiceProvider serviceProvider) { public DataTypeDropDownSelectionDataModel(ServiceProvider serviceProvider) {
this.dtm = null;
this.dataTypeService = getDataTypeService(serviceProvider); this.dataTypeService = getDataTypeService(serviceProvider);
} }
public DataTypeDropDownSelectionDataModel(DataTypeManagerService dataTypeService) { /**
* Creates a new instance.
*
* @param dtm the preferred {@link DataTypeManager}. Data types that are found in multiple
* data type managers will be pruned to just the ones already in the preferred data type
* manager.
* @param dataTypeService {@link DataTypeManagerService}
*/
public DataTypeDropDownSelectionDataModel(DataTypeManager dtm,
DataTypeManagerService dataTypeService) {
this.dtm = dtm;
this.dataTypeService = dataTypeService; this.dataTypeService = dataTypeService;
} }
@ -84,16 +99,57 @@ public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldData
* Remove any unwanted data type items, like arrays. * Remove any unwanted data type items, like arrays.
*/ */
private List<DataType> filterDataTypeList(List<DataType> dataTypeList) { private List<DataType> filterDataTypeList(List<DataType> dataTypeList) {
List<DataType> matchingList = new ArrayList<>(dataTypeList.size()); // build lookups for data types that are in the preferred dtm, but may have come from
for (DataType dataType : dataTypeList) { // another DTM. In the second step, duplicate data types will be omitted from the
if (!(dataType instanceof Array)) { // final results, in favor of the data type that is already in the preferred dtm.
matchingList.add(dataType); Set<UniversalID> preferredUIDs = new HashSet<>();
Set<Class<?>> preferredBuiltins = new HashSet<>();
for (DataType dt : dataTypeList) {
DataType baseDT = DataTypeUtilities.getBaseDataType(dt);
if (isFromPreferredDTM(baseDT)) {
if (baseDT instanceof BuiltInDataType) {
preferredBuiltins.add(baseDT.getClass());
}
else if (baseDT.getUniversalID() != null) {
preferredUIDs.add(baseDT.getUniversalID());
}
} }
} }
List<DataType> matchingList = new ArrayList<>(dataTypeList.size());
for (DataType dataType : dataTypeList) {
if (dataType instanceof Array) {
continue;
}
DataType baseDT = DataTypeUtilities.getBaseDataType(dataType);
if (dtm != null && !isFromPreferredDTM(baseDT)) {
if (baseDT instanceof BuiltInDataType &&
preferredBuiltins.contains(baseDT.getClass())) {
continue;
}
if (baseDT.getUniversalID() != null &&
preferredUIDs.contains(baseDT.getUniversalID())) {
continue;
}
}
matchingList.add(dataType);
}
return matchingList; return matchingList;
} }
private boolean isFromPreferredDTM(DataType dt) {
if (dtm != null) {
DataTypeManager altDTM = dtm instanceof CompositeViewerDataTypeManager compDTM
? compDTM.getOriginalDataTypeManager()
: null;
DataTypeManager dtDTM = dt.getDataTypeManager();
return dtDTM == dtm || dtDTM == altDTM;
}
return false;
}
@Override @Override
public int getIndexOfFirstMatchingEntry(List<DataType> data, String text) { public int getIndexOfFirstMatchingEntry(List<DataType> data, String text) {

View File

@ -66,7 +66,6 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
removeWorkPanel(); removeWorkPanel();
editor = createEditor(pluginTool, allowedTypes); editor = createEditor(pluginTool, allowedTypes);
editor.setPreferredDataTypeManager(dtm);
editor.setConsumeEnterKeyPress(false); // we want to handle Enter key presses editor.setConsumeEnterKeyPress(false); // we want to handle Enter key presses
editor.addCellEditorListener(new CellEditorListener() { editor.addCellEditorListener(new CellEditorListener() {
@Override @Override
@ -110,7 +109,7 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
protected DataTypeSelectionEditor createEditor(PluginTool tool, protected DataTypeSelectionEditor createEditor(PluginTool tool,
AllowedDataTypes allowedDataTypes) { AllowedDataTypes allowedDataTypes) {
return new DataTypeSelectionEditor(tool, allowedDataTypes); return new DataTypeSelectionEditor(dtm, tool, allowedDataTypes);
} }
protected JComponent createEditorPanel(DataTypeSelectionEditor dtEditor) { protected JComponent createEditorPanel(DataTypeSelectionEditor dtEditor) {

View File

@ -29,6 +29,7 @@ import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.data.DataTypeParser; import ghidra.util.data.DataTypeParser;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
/** /**
@ -68,14 +69,34 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
// optional path to initially select in the data type chooser tree // optional path to initially select in the data type chooser tree
private TreePath initiallySelectedTreePath; private TreePath initiallySelectedTreePath;
public DataTypeSelectionEditor(ServiceProvider serviceProvider, /**
* Creates a new instance.
*
* @param dtm the preferred {@link DataTypeManager}. Extra copies of data types that are
* already in the preferred DTM will be suppressed.
* @param serviceProvider {@link ServiceProvider}
* @param allowedDataTypes {@link AllowedDataTypes} option enum, controls what kind of
* data types that will be shown
*/
public DataTypeSelectionEditor(DataTypeManager dtm, ServiceProvider serviceProvider,
DataTypeParser.AllowedDataTypes allowedDataTypes) { DataTypeParser.AllowedDataTypes allowedDataTypes) {
this(serviceProvider.getService(DataTypeManagerService.class), allowedDataTypes); this(dtm, serviceProvider.getService(DataTypeManagerService.class), allowedDataTypes);
} }
public DataTypeSelectionEditor(DataTypeManagerService service, /**
* Creates a new instance.
*
* @param dtm the preferred {@link DataTypeManager}. Extra copies of data types that are
* already in the preferred DTM will be suppressed.
* @param service {@link DataTypeManagerService}
* @param allowedDataTypes {@link AllowedDataTypes} option enum, controls what kind of
* data types that will be shown
*/
public DataTypeSelectionEditor(DataTypeManager dtm, DataTypeManagerService service,
DataTypeParser.AllowedDataTypes allowedDataTypes) { DataTypeParser.AllowedDataTypes allowedDataTypes) {
this.dataTypeManager = dtm;
if (service == null) { if (service == null) {
throw new NullPointerException("DataTypeManagerService cannot be null"); throw new NullPointerException("DataTypeManagerService cannot be null");
} }
@ -86,19 +107,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
init(); init();
} }
/**
* Sets the {@link DataTypeManager} to use when the chooser is forced to parse the given
* data type text to resolve the data type. If the users chooses a type, then this value
* is not used. Note that setting this value does not restrict the parser to just the
* given value, but rather the given value is the preferred manager and is thus searched
* first.
*
* @param dataTypeManager the preferred data type manager
*/
public void setPreferredDataTypeManager(DataTypeManager dataTypeManager) {
this.dataTypeManager = dataTypeManager;
}
/** /**
* Sets whether this editor should consumer Enter key presses * Sets whether this editor should consumer Enter key presses
* @see DropDownSelectionTextField#setConsumeEnterKeyPress(boolean) * @see DropDownSelectionTextField#setConsumeEnterKeyPress(boolean)
@ -116,7 +124,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
private void init() { private void init() {
selectionField = createDropDownSelectionTextField( selectionField = createDropDownSelectionTextField(
new DataTypeDropDownSelectionDataModel(dataTypeManagerService)); new DataTypeDropDownSelectionDataModel(dataTypeManager, dataTypeManagerService));
selectionField.addCellEditorListener(new CellEditorListener() { selectionField.addCellEditorListener(new CellEditorListener() {
@Override @Override
public void editingCanceled(ChangeEvent e) { public void editingCanceled(ChangeEvent e) {

View File

@ -125,7 +125,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
@Override @Override
protected DataTypeSelectionEditor createEditor(PluginTool pluginTool, protected DataTypeSelectionEditor createEditor(PluginTool pluginTool,
AllowedDataTypes allowedDataTypes) { AllowedDataTypes allowedDataTypes) {
return new DataTypeSelectionEditor(pluginTool, allowedDataTypes) { return new DataTypeSelectionEditor(null, pluginTool, allowedDataTypes) {
@Override @Override
protected DropDownSelectionTextField<DataType> createDropDownSelectionTextField( protected DropDownSelectionTextField<DataType> createDropDownSelectionTextField(
@ -1207,8 +1207,8 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
new DefaultHighlighter.DefaultHighlightPainter(Palette.YELLOW)); new DefaultHighlighter.DefaultHighlightPainter(Palette.YELLOW));
JPanel editorPanel = new JPanel(new BorderLayout()); JPanel editorPanel = new JPanel(new BorderLayout());
DataTypeSelectionEditor editor = new DataTypeSelectionEditor(tool, AllowedDataTypes.ALL); DataTypeSelectionEditor editor =
editor.setPreferredDataTypeManager(program.getDataTypeManager()); new DataTypeSelectionEditor(program.getDataTypeManager(), tool, AllowedDataTypes.ALL);
editorPanel.add(panelUpdateField, BorderLayout.SOUTH); editorPanel.add(panelUpdateField, BorderLayout.SOUTH);
editorPanel.add(editor.getEditorComponent(), BorderLayout.NORTH); editorPanel.add(editor.getEditorComponent(), BorderLayout.NORTH);