GP-5327 - Create Structure Dialog - New text category box & existing

categories chooser
This commit is contained in:
ghidravision
2025-03-06 18:54:59 +00:00
committed by dragonmacher
parent 795d92cb1a
commit 8234bfb14a
13 changed files with 974 additions and 170 deletions
@@ -93,6 +93,11 @@ public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService
throw new UnsupportedOperationException();
}
@Override
public CategoryPath getCategoryPath(TreePath selectedPath) {
throw new UnsupportedOperationException();
}
@Override
public List<DataType> getFavorites() {
throw new UnsupportedOperationException();
@@ -111,6 +116,11 @@ public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService
return dataTypes;
}
@Override
public List<CategoryPath> getSortedCategoryPathList() {
throw new UnsupportedOperationException();
}
@Override
public void removeDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) {
throw new UnsupportedOperationException();
@@ -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.
@@ -40,7 +40,6 @@ class CreateStructureAction extends ListingContextAction {
new String[] { "Data", "Create Structure..." };
private DataPlugin plugin;
private CreateStructureDialog createStructureDialog;
public CreateStructureAction(DataPlugin plugin) {
super("Create Structure", plugin.getName());
@@ -49,25 +48,12 @@ class CreateStructureAction extends ListingContextAction {
setKeyBindingData(new KeyBindingData(KeyEvent.VK_OPEN_BRACKET, InputEvent.SHIFT_DOWN_MASK));
this.plugin = plugin;
setEnabled(true);
createStructureDialog = new CreateStructureDialog(plugin.getTool());
}
@Override
public void dispose() {
super.dispose();
createStructureDialog.dispose();
}
/**
* Method called when the action is invoked.
*/
@Override
public void actionPerformed(ListingActionContext programActionContext) {
Program program = programActionContext.getProgram();
ProgramSelection sel = programActionContext.getSelection();
if (sel != null && !sel.isEmpty()) {
InteriorSelection interiorSel = sel.getInteriorSelection();
if (interiorSel != null) {
@@ -113,8 +99,8 @@ class CreateStructureAction extends ListingContextAction {
return;
}
Structure userChoice =
createStructureDialog.showCreateStructureDialog(program, tempStructure);
CreateStructureDialog dialog = new CreateStructureDialog(plugin.getTool());
Structure userChoice = dialog.showCreateStructureDialog(program, tempStructure);
if (userChoice != null) {
CreateStructureInStructureCmd cmd = new CreateStructureInStructureCmd(userChoice,
@@ -162,8 +148,9 @@ class CreateStructureAction extends ListingContextAction {
return;
}
CreateStructureDialog dialog = new CreateStructureDialog(plugin.getTool());
Structure userChoice =
createStructureDialog.showCreateStructureDialog(program, tempStructure);
dialog.showCreateStructureDialog(program, tempStructure);
// exit if the user cancels the operation
if (userChoice != null) {
@@ -594,6 +594,18 @@ public class DataTypeManagerPlugin extends ProgramPlugin
return dialog.getSelectedDataType();
}
@Override
public CategoryPath getCategoryPath(TreePath selectedPath) {
DataTypeChooserDialog dialog = new DataTypeChooserDialog(this);
dialog.setCategorySelectionMode(true);
if (selectedPath != null) {
dialog.setSelectedPath(selectedPath);
}
tool.showDialog(dialog);
return dialog.getSelectedCategoryPath();
}
@Override
public DataTypeManager[] getDataTypeManagers() {
return dataTypeManagerHandler.getDataTypeManagers();
@@ -664,6 +676,11 @@ public class DataTypeManagerPlugin extends ProgramPlugin
return dataTypeManagerHandler.getDataTypeIndexer().getSortedDataTypeList();
}
@Override
public List<CategoryPath> getSortedCategoryPathList() {
return dataTypeManagerHandler.getDataTypeIndexer().getSortedCategoryPathList();
}
@Override
public void setDataTypeSelected(DataType dataType) {
if (provider.isVisible()) {
@@ -23,10 +23,11 @@ import ghidra.program.model.data.*;
import ghidra.util.task.*;
/**
* A class that stores a sorted list of all the {@link DataType} objects in the current data type
* manager plugin. This class does its work lazily such that no work is done until
* {@link #getSortedDataTypeList()} is called. Even when that method is called no work will be
* done if the state of the data types in the system hasn't changed.
* A class that stores a sorted list of all the {@link DataType} and a list of all the
* {@link CategoryPath} objects in the current data type manager plugin. This class does its work
* lazily such that no work is done until {@link #getSortedDataTypeList()} is called. Even when that
* method is called no work will be done if the state of the data types in the system hasn't
* changed.
*/
public class DataTypeIndexer {
private List<DataTypeManager> dataTypeManagers = new ArrayList<>();
@@ -34,6 +35,7 @@ public class DataTypeIndexer {
private DataTypeIndexUpdateListener listener = new DataTypeIndexUpdateListener();
private volatile boolean isStale = true;
private List<CategoryPath> categoryPathList = Collections.emptyList();
// Note: synchronizing here prevents concurrent mod issues with the managers list
public synchronized void addDataTypeManager(DataTypeManager dataTypeManager) {
@@ -79,6 +81,18 @@ public class DataTypeIndexer {
return Collections.unmodifiableList(newList);
}
/**
* Returns a list of the unique Category Paths ({@link CategoryPath}) as utilized by the
* data types open in the current tool.
*
* @return a list of the {@link CategoryPath} associated with the data types open in the
* current tool.
*/
public List<CategoryPath> getSortedCategoryPathList() {
updateDataTypeList(); // the category list is quietly updated in the background
return categoryPathList;
}
private List<DataType> updateDataTypeList() {
if (!isStale) {
return dataTypeList;
@@ -95,8 +109,9 @@ public class DataTypeIndexer {
task.run(TaskMonitor.DUMMY);
}
List<DataType> newList = task.getList();
return newList;
List<DataType> newDataTypeList = task.getList();
categoryPathList = task.getCategoryPathList();
return newDataTypeList;
}
// Note: purposefully not synchronized for speed
@@ -107,6 +122,7 @@ public class DataTypeIndexer {
// is possible that once marked stale, we may never have another request for this data
// again.
dataTypeList = Collections.emptyList();
categoryPathList = Collections.emptyList();
}
//==================================================================================================
@@ -155,7 +171,8 @@ public class DataTypeIndexer {
private class IndexerTask extends Task {
private List<DataType> list = new ArrayList<>();
private List<DataType> dataTypes = new ArrayList<>();
private List<CategoryPath> categories;
IndexerTask() {
super("Data Type Indexer Task", false, true, true);
@@ -169,15 +186,32 @@ public class DataTypeIndexer {
for (DataTypeManager dataTypeManager : dataTypeManagers) {
monitor.setMessage("Searching " + dataTypeManager.getName());
dataTypeManager.getAllDataTypes(list);
dataTypeManager.getAllDataTypes(dataTypes);
monitor.incrementProgress(1);
}
Collections.sort(list, new CaseInsensitiveDataTypeComparator());
Collections.sort(dataTypes, new CaseInsensitiveDataTypeComparator());
populateCategoryList(dataTypes);
}
private void populateCategoryList(List<DataType> dataTypes) {
Set<CategoryPath> set = new HashSet<>();
for (DataType dt : dataTypes) {
CategoryPath path = dt.getCategoryPath();
set.add(path);
}
categories = new ArrayList<>(set);
Collections.sort(categories);
}
List<DataType> getList() {
return list;
return dataTypes;
}
List<CategoryPath> getCategoryPathList() {
return categories;
}
}
@@ -266,4 +300,5 @@ public class DataTypeIndexer {
markStale();
}
}
}
@@ -34,10 +34,9 @@ import docking.widgets.filter.TextFilterStrategy;
import docking.widgets.label.GLabel;
import docking.widgets.tree.*;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.app.util.datatype.DataTypeSelectionDialog;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -49,8 +48,12 @@ import ghidra.util.task.TaskMonitor;
public class DataTypeChooserDialog extends DialogComponentProvider {
private DataTypeArchiveGTree tree;
private DataType selectedDataType;
private CategoryPath selectedCategoryPath;
private GLabel messageLabel;
boolean isFilterEditable;
private boolean isFilterEditable;
private boolean categorySelectionMode;
public DataTypeChooserDialog(DataTypeManagerPlugin plugin) {
super("Data Type Chooser", true, true, true, false);
@@ -60,7 +63,7 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
tree.setEditable(false);
tree.updateFilterForChoosingDataType();
tree.addGTreeSelectionListener(e -> setOkEnabled(getSelectedNode() != null));
tree.addGTreeSelectionListener(e -> setOkEnabled(isValidNodeSelected()));
tree.addMouseListener(new MouseAdapter() {
@Override
@@ -69,7 +72,18 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
return;
}
DataTypeNode selectedNode = getSelectedNode();
if (categorySelectionMode) {
CategoryPath path = getCurrentCategoryPath();
if (path == null) {
return;
}
selectedCategoryPath = path;
close();
return;
}
DataTypeNode selectedNode = getSelectedDtNode();
if (selectedNode == null) {
return;
}
@@ -86,18 +100,48 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
setOkEnabled(false);
}
private DataTypeNode getSelectedNode() {
/**
* Signals that this chooser is intended to pick {@link CategoryPath}s instead of data types.
* @param categorySelectionMode true to pick category paths
*/
public void setCategorySelectionMode(boolean categorySelectionMode) {
this.categorySelectionMode = categorySelectionMode;
}
private boolean isValidNodeSelected() {
TreePath[] selectionPath = tree.getSelectionPaths();
if (selectionPath.length != 1) {
return false;
}
GTreeNode node = (GTreeNode) selectionPath[0].getLastPathComponent();
return node instanceof DataTypeTreeNode;
}
private DataTypeNode getSelectedDtNode() {
TreePath[] selectionPath = tree.getSelectionPaths();
if (selectionPath.length != 1) {
return null;
}
GTreeNode node = (GTreeNode) selectionPath[0].getLastPathComponent();
if (!(node instanceof DataTypeNode)) {
if (node instanceof DataTypeNode dtNode) {
return dtNode;
}
return null;
}
private CategoryNode getSelectedCategoryNode() {
TreePath[] selectionPath = tree.getSelectionPaths();
if (selectionPath.length != 1) {
return null;
}
return (DataTypeNode) node;
GTreeNode node = (GTreeNode) selectionPath[0].getLastPathComponent();
if (node instanceof CategoryNode catNode) {
return catNode;
}
return null;
}
@Override
@@ -119,12 +163,36 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
@Override
protected void okCallback() {
// can't be null since we control button enablement
DataTypeNode dataTypeNode = getSelectedNode();
selectedDataType = dataTypeNode.getDataType();
if (categorySelectionMode) {
selectedCategoryPath = getCurrentCategoryPath();
}
else {
DataTypeNode dtNode = getSelectedDtNode();
selectedDataType = dtNode.getDataType();
}
close();
}
private CategoryPath getCurrentCategoryPath() {
DataTypeNode dtNode = getSelectedDtNode();
// the user may have picked a data type node or a category node
if (dtNode != null) {
return dtNode.getDataType().getCategoryPath();
}
CategoryNode categoryNode = getSelectedCategoryNode();
if (categoryNode != null) {
Category category = categoryNode.getCategory();
return category.getCategoryPath();
}
return null;
}
/**
* A convenience method to show this dialog with the following configuration:
* <ul>
@@ -207,6 +275,10 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
tree.setFilterProvider(provider);
}
public CategoryPath getSelectedCategoryPath() {
return selectedCategoryPath;
}
public DataType getSelectedDataType() {
return selectedDataType;
}
@@ -137,6 +137,15 @@ public interface DataTypeManagerService extends DataTypeQueryService, DataTypeAr
*/
public DataType getDataType(TreePath selectedPath);
/**
* Shows the user a dialog that allows them to choose a category path from a tree of all
* available categories.
*
* @param selectedPath An optional tree path to select in the tree
* @return A category path chosen by the user
*/
public CategoryPath getCategoryPath(TreePath selectedPath);
/**
* Examines all enum dataTypes for items that match the given value. Returns a list of Strings
* that might make sense for the given value.
@@ -40,6 +40,18 @@ public interface DataTypeQueryService {
*/
public List<DataType> getSortedDataTypeList();
/**
* Prompts the user for a data type. The optional filter text will be used to filter the tree
* of available types.
* Gets the sorted list of all category paths known by this service via its owned
* DataTypeManagers. This method can be called frequently, as the underlying data is indexed
* and only updated as changes are made. The sorting of the list is done using the
* natural sort of the {@link CategoryPath} objects.
*
* @return the sorted list of known category paths.
*/
public List<CategoryPath> getSortedCategoryPathList();
/**
* This method simply calls {@link #promptForDataType(String)}
* @deprecated use {@link #promptForDataType(String)}
@@ -49,8 +61,10 @@ public interface DataTypeQueryService {
public DataType getDataType(String filterText);
/**
* Prompts the user for a data type. The optional filter text will be used to filter the tree
* of available types.
* Obtain the preferred datatype which corresponds to the specified
* datatype specified by filterText. A tool-based service provider
* may prompt the user to select a datatype if more than one possibility
* exists.
*
* @param filterText If not null, this text filters the visible data types to only show those
* that start with the given text
@@ -0,0 +1,466 @@
/* ###
* 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.util.datatype;
import java.awt.Component;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.TreePath;
import docking.widgets.DropDownSelectionTextField;
import docking.widgets.DropDownTextFieldDataModel;
import docking.widgets.button.BrowseButton;
import docking.widgets.list.GListCellRenderer;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.data.CategoryPath;
import ghidra.util.exception.AssertException;
/**
* An editor that is used to show the {@link DropDownSelectionTextField} for the entering of
* category paths by name and offers the user of a completion window. This editor also provides a
* browse button that when pressed will show a data type tree so that the user may browse a tree
* of known category paths.
* <p>
* <u>Stand Alone Usage</u><br>
* In order to use this component directly you need to call {@link #getEditorComponent()}. This
* will give you a Component for editing.
* <p>
* In order to know when changes are made to the component you need to add a DocumentListener
* via the {@link #addDocumentListener(DocumentListener)} method. The added listener will be
* notified as the user enters text into the editor's text field.
*/
public class CategoryPathSelectionEditor extends AbstractCellEditor {
private JPanel editorPanel;
private DropDownSelectionTextField<CategoryPath> selectionField;
private JButton browseButton;
private DataTypeManagerService dataTypeManagerService;
private KeyAdapter keyListener;
private NavigationDirection navigationDirection;
// optional path to initially select in the data type chooser tree
private TreePath initiallySelectedTreePath;
/**
* Creates a new instance.
*
* @param serviceProvider {@link ServiceProvider}
*/
public CategoryPathSelectionEditor(ServiceProvider serviceProvider) {
this.dataTypeManagerService = serviceProvider.getService(DataTypeManagerService.class);
if (this.dataTypeManagerService == null) {
throw new NullPointerException("DataTypeManagerService cannot be null");
}
init();
}
/**
* Sets whether this editor should consumer Enter key presses
* @see DropDownSelectionTextField#setConsumeEnterKeyPress(boolean)
*
* @param consume true to consume
*/
public void setConsumeEnterKeyPress(boolean consume) {
selectionField.setConsumeEnterKeyPress(consume);
}
protected DropDownSelectionTextField<CategoryPath> createDropDownSelectionTextField(
CategoryPathDropDownSelectionDataModel model) {
return new DropDownSelectionTextField<>(model);
}
private void init() {
selectionField = createDropDownSelectionTextField(
new CategoryPathDropDownSelectionDataModel(dataTypeManagerService));
selectionField.addCellEditorListener(new CellEditorListener() {
@Override
public void editingCanceled(ChangeEvent e) {
fireEditingCanceled();
navigationDirection = null;
}
@Override
public void editingStopped(ChangeEvent e) {
fireEditingStopped();
navigationDirection = null;
}
});
selectionField.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent event) {
selectionField.setEnabled(true);
selectionField.requestFocus();
}
});
selectionField.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
browseButton = new BrowseButton();
browseButton.setToolTipText("Browse Existing Category Paths");
browseButton.addActionListener(e -> showBrowser());
editorPanel = new JPanel();
editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.X_AXIS));
editorPanel.add(selectionField);
editorPanel.add(Box.createHorizontalStrut(5));
editorPanel.add(browseButton);
keyListener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_TAB) {
if (e.isShiftDown()) {
navigationDirection = NavigationDirection.BACKWARD;
}
else {
navigationDirection = NavigationDirection.FORWARD;
}
fireEditingStopped();
e.consume();
}
}
};
}
/**
* Retrieve the value in the cell.
* @return categoryPath of the selected value from the drop-down
*/
@Override
public CategoryPath getCellEditorValue() {
return selectionField.getSelectedValue();
}
/**
* If a path was selected from the drop-down list, it is already
* well-formed and cannot be null.
* @return the selected category path as CategoryPath
*/
public CategoryPath getCellEditorValueAsCategoryPath() {
return selectionField.getSelectedValue();
}
/**
* Returns the text value of the editor's text field.
* @return the text value of the editor's text field.
*/
public String getCellEditorValueAsText() {
return selectionField.getText();
}
/**
* Returns the component that allows the user to edit.
* @return the component that allows the user to edit.
*/
public JComponent getEditorComponent() {
return editorPanel;
}
/**
* Retrieve the dropdown text field that holds the category path collection.
* @return CategoryPath dropdown selection text field object
*/
public DropDownSelectionTextField<CategoryPath> getDropDownTextField() {
return selectionField;
}
/**
* The browse button which opens a menu with the Category Path collection from the data manager.
* @return browseButton
*/
public JButton getBrowseButton() {
return browseButton;
}
/**
* Sets the initially selected node in the data type tree that the user can choose to
* show.
*
* @param path The path to set
*/
public void setDefaultSelectedTreePath(TreePath path) {
this.initiallySelectedTreePath = path;
}
/**
* Place focus on the selectionField.
*/
public void requestFocus() {
selectionField.requestFocus();
}
/**
* Highlights the text of the cell editor.
*/
void selectCellEditorValue() {
selectionField.selectAll();
}
/**
* Sets the cell editor value as the entered String text.
* @param text String input
*/
public void setCellEditorValueAsText(String text) {
selectionField.setText(text);
navigationDirection = null;
}
/**
* Sets the value to be edited on this cell editor.
*
* @param path The data type which is to be edited.
*/
public void setCellEditorValue(CategoryPath path) {
selectionField.setSelectedValue(path);
navigationDirection = null;
}
/**
* Adds a document listener to the text field editing component of this editor so that users
* can be notified when the text contents of the editor change.
* @param listener the listener to add.
*/
public void addDocumentListener(DocumentListener listener) {
selectionField.getDocument().addDocumentListener(listener);
}
/**
* Removes a previously added document listener.
* @param listener the listener to remove.
*/
public void removeDocumentListener(DocumentListener listener) {
selectionField.getDocument().removeDocumentListener(listener);
}
/**
* Add the provided FocusListener to the selectionField.
* @param listener FocusListener
*/
public void addFocusListener(FocusListener listener) {
selectionField.addFocusListener(listener);
}
/**
* Remove the provided FocusListener from the selectionField.
* @param listener FocusListener
*/
public void removeFocusListener(FocusListener listener) {
selectionField.removeFocusListener(listener);
}
/**
* Toggle Tab key commits an edit. Sets the traversal key enabled field of the selectionField.
* @param doesCommit Boolean
*/
public void setTabCommitsEdit(boolean doesCommit) {
selectionField.setFocusTraversalKeysEnabled(!doesCommit);
removeKeyListener(keyListener); // always remove to prevent multiple additions
if (doesCommit) {
addKeyListener(keyListener);
}
}
/**
* Returns the direction of the user triggered navigation; null if the user did not trigger
* navigation out of this component.
* @return the direction
*/
public NavigationDirection getNavigationDirection() {
return navigationDirection;
}
private void addKeyListener(KeyListener listener) {
selectionField.addKeyListener(listener);
}
private void removeKeyListener(KeyListener listener) {
selectionField.removeKeyListener(listener);
}
private void showBrowser() {
CategoryPath path = dataTypeManagerService.getCategoryPath(initiallySelectedTreePath);
if (path != null) {
setCellEditorValue(path);
selectionField.requestFocus();
}
}
/**
* Enable or disable the Category Path Text Field.
* @param createStructureByName Boolean
*/
public void setEnabled(boolean createStructureByName) {
selectionField.setEnabled(createStructureByName);
}
/**
* Determine whether the Category Path Text Field is enabled.
* @return isEnabled boolean
*/
public boolean isEnabled() {
return selectionField.isEnabled();
}
/**
* CategoryPathDropDownSelectionDataModel class handles the display and selection of a
* Category Path.
*/
private class CategoryPathDropDownSelectionDataModel
implements DropDownTextFieldDataModel<CategoryPath> {
private List<CategoryPath> data;
private Comparator<Object> searchComparator = new CategoryPathComparator();
/**
* Creates a new instance.
*
* @param dataTypeService {@link DataTypeManagerService}
*/
public CategoryPathDropDownSelectionDataModel(DataTypeManagerService dataTypeService) {
data = dataTypeService.getSortedCategoryPathList();
}
@Override
public ListCellRenderer<CategoryPath> getListRenderer() {
return new CategoryPathDropDownRenderer();
}
/**
* Description of the CategoryPath is the display text of the path as a string.
* @param categoryPath CategoryPath
* @return String representation of the Category Path
*/
@Override
public String getDescription(CategoryPath categoryPath) {
return getDisplayText(categoryPath);
}
/**
* Retrieve the CategoryPath string representation.
*
* @param categoryPath CategoryPath
* @return String representation of the Category Path
*/
@Override
public String getDisplayText(CategoryPath categoryPath) {
return categoryPath.getPath();
}
/**
* Support for the filtering mechanism on the collection of Category Paths in the Data Manager.
*
* @param searchText String entered text
* @return filtered list of Category Paths
*/
@Override
public List<CategoryPath> getMatchingData(String searchText) {
if (searchText == null || searchText.length() == 0) {
return Collections.emptyList();
}
char END_CHAR = '\uffff';
String searchTextStart = searchText;
String searchTextEnd = searchText + END_CHAR;
int startIndex = Collections.binarySearch(data, searchTextStart, searchComparator);
int endIndex = Collections.binarySearch(data, searchTextEnd, searchComparator);
// the binary search returns a negative, incremented position if there is no match in the
// list for the given search
if (startIndex < 0) {
startIndex = -startIndex - 1;
}
if (endIndex < 0) {
endIndex = -endIndex - 1;
}
return data.subList(startIndex, endIndex);
}
/**
* Identify index of first matching CategoryPath from entered text string.
* @param dataCollection list of Category Paths
* @param text search string
* @return int index of first match
*/
@Override
public int getIndexOfFirstMatchingEntry(List<CategoryPath> dataCollection, String text) {
int lastPreferredMatchIndex = -1;
for (int i = 0; i < data.size(); i++) {
CategoryPath dataType = data.get(i);
String dataTypeName = dataType.getName();
dataTypeName = dataTypeName.replaceAll(" ", "");
if (dataTypeName.equals(text)) {
// an exact match is the best possible match!
return i;
}
if (dataTypeName.equalsIgnoreCase(text)) {
// keep going, but remember this location, in case we don't find any more matches
lastPreferredMatchIndex = i;
}
else {
// we've encountered a non-matching entry--nothing left to search
return lastPreferredMatchIndex;
}
}
return -1; // we only get here when the list is empty
}
private class CategoryPathComparator implements Comparator<Object> {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof CategoryPath && o2 instanceof String) {
CategoryPath path = (CategoryPath) o1;
return path.getName().compareToIgnoreCase(((String) o2));
}
throw new AssertException(
"CategoryPathCompartor used to compare files against a String key!");
}
}
private class CategoryPathDropDownRenderer extends GListCellRenderer<CategoryPath> {
@Override
protected String getItemText(CategoryPath path) {
return path.getPath();
}
@Override
public Component getListCellRendererComponent(JList<? extends CategoryPath> list,
CategoryPath value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
return this;
}
}
}
}
@@ -22,7 +22,7 @@ import javax.swing.JPanel;
import javax.swing.event.*;
import docking.DialogComponentProvider;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.util.HelpLocation;
@@ -36,17 +36,17 @@ import ghidra.util.data.DataTypeParser.AllowedDataTypes;
public class DataTypeSelectionDialog extends DialogComponentProvider {
private DataTypeSelectionEditor editor;
private PluginTool pluginTool;
private ServiceProvider serviceProvider;
private DataType userChoice;
private int maxSize = -1;
private DataTypeManager dtm;
private final AllowedDataTypes allowedTypes;
public DataTypeSelectionDialog(PluginTool pluginTool, DataTypeManager dtm, int maxSize,
DataTypeParser.AllowedDataTypes allowedTypes) {
public DataTypeSelectionDialog(ServiceProvider serviceProvider, DataTypeManager dtm,
int maxSize, DataTypeParser.AllowedDataTypes allowedTypes) {
super("Data Type Chooser Dialog", true, true, true, false);
this.pluginTool = pluginTool;
this.serviceProvider = serviceProvider;
this.dtm = dtm;
this.maxSize = maxSize;
this.allowedTypes = allowedTypes;
@@ -65,7 +65,7 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
private void buildEditor() {
removeWorkPanel();
editor = createEditor(pluginTool, allowedTypes);
editor = createEditor(serviceProvider, allowedTypes);
editor.setConsumeEnterKeyPress(false); // we want to handle Enter key presses
editor.addCellEditorListener(new CellEditorListener() {
@Override
@@ -108,9 +108,9 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
rootPanel.validate();
}
protected DataTypeSelectionEditor createEditor(PluginTool tool,
protected DataTypeSelectionEditor createEditor(ServiceProvider sp,
AllowedDataTypes allowedDataTypes) {
return new DataTypeSelectionEditor(dtm, tool, allowedDataTypes);
return new DataTypeSelectionEditor(dtm, sp, allowedDataTypes);
}
protected JComponent createEditorPanel(DataTypeSelectionEditor dtEditor) {
@@ -352,6 +352,11 @@ public class FunctionSignatureParser {
return service.getSortedDataTypeList();
}
@Override
public List<CategoryPath> getSortedCategoryPathList() {
return service.getSortedCategoryPathList();
}
@Override
public DataType getDataType(String filterText) {
return promptForDataType(filterText);
@@ -56,6 +56,7 @@ import ghidra.app.services.DataTypeManagerService;
import ghidra.app.services.ProgramManager;
import ghidra.framework.Application;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.data.*;
@@ -123,9 +124,9 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
dialog = new DataTypeSelectionDialog(tool, program.getDataTypeManager(), -1,
AllowedDataTypes.ALL) {
@Override
protected DataTypeSelectionEditor createEditor(PluginTool pluginTool,
protected DataTypeSelectionEditor createEditor(ServiceProvider sp,
AllowedDataTypes allowedDataTypes) {
return new DataTypeSelectionEditor(null, pluginTool, allowedDataTypes) {
return new DataTypeSelectionEditor(null, sp, allowedDataTypes) {
@Override
protected DropDownSelectionTextField<DataType> createDropDownSelectionTextField(
@@ -50,7 +50,7 @@ public class TestDoubleDataTypeManagerService implements DataTypeManagerService
}
@Override
public DataType getDataType(String filterText) {
public List<CategoryPath> getSortedCategoryPathList() {
throw new UnsupportedOperationException();
}
@@ -161,11 +161,21 @@ public class TestDoubleDataTypeManagerService implements DataTypeManagerService
throw new UnsupportedOperationException();
}
@Override
public DataType getDataType(String filterText) {
throw new UnsupportedOperationException();
}
@Override
public DataType getDataType(TreePath selectedPath) {
throw new UnsupportedOperationException();
}
@Override
public CategoryPath getCategoryPath(TreePath selectedPath) {
throw new UnsupportedOperationException();
}
@Override
public Set<String> getPossibleEquateNames(long value) {
throw new UnsupportedOperationException();