mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-31 14:05:56 +08:00
GP-5327 - Fixed sizing issues with the data type and category editors
This commit is contained in:
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 17 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
@@ -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.
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.cmd.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
@@ -73,9 +72,6 @@ public class CreateStructureCmd extends AbstractCreateStructureCmd {
|
||||
structureDataLength = structure.getLength();
|
||||
}
|
||||
|
||||
/*
|
||||
* @see AbstractCreateStructureCmd#createStructure(Address, Program)
|
||||
*/
|
||||
@Override
|
||||
Structure createStructure(Address address, Program program) {
|
||||
|
||||
@@ -87,9 +83,6 @@ public class CreateStructureCmd extends AbstractCreateStructureCmd {
|
||||
return structure;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see AbstractCreateStructureCmd#initializeStructureData(Program, Structure)
|
||||
*/
|
||||
@Override
|
||||
DataType initializeStructureData(Program program, Structure localStructure) {
|
||||
|
||||
@@ -101,11 +94,11 @@ public class CreateStructureCmd extends AbstractCreateStructureCmd {
|
||||
}
|
||||
catch (AddressOverflowException e1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't create structure because length exceeds address " + "space" +
|
||||
"Can't create structure because length exceeds address space" +
|
||||
structureDataLength);
|
||||
}
|
||||
ReferenceManager refMgr = program.getReferenceManager();
|
||||
Reference[] refs = findExistingRefs(refMgr, program.getAddressFactory(),
|
||||
List<Reference> refs = findExistingRefs(refMgr, program.getAddressFactory(),
|
||||
getStructureAddress(), endAddress);
|
||||
listing.clearCodeUnits(getStructureAddress(), endAddress, false);
|
||||
|
||||
@@ -123,20 +116,20 @@ public class CreateStructureCmd extends AbstractCreateStructureCmd {
|
||||
return data.getDataType();
|
||||
}
|
||||
|
||||
private Reference[] findExistingRefs(ReferenceManager refMgr, AddressFactory af, Address start,
|
||||
private List<Reference> findExistingRefs(ReferenceManager refMgr, AddressFactory af,
|
||||
Address start,
|
||||
Address end) {
|
||||
ArrayList<Reference> list = new ArrayList<Reference>();
|
||||
List<Reference> list = new ArrayList<Reference>();
|
||||
AddressIterator it = refMgr.getReferenceSourceIterator(new AddressSet(start, end), true);
|
||||
while (it.hasNext()) {
|
||||
Address addr = it.next();
|
||||
Reference[] refs = refMgr.getReferencesFrom(addr);
|
||||
Collections.addAll(list, refs);
|
||||
}
|
||||
Reference[] refList = new Reference[list.size()];
|
||||
return list.toArray(refList);
|
||||
return list;
|
||||
}
|
||||
|
||||
private void addRefs(Program p, ReferenceManager refMgr, Reference[] refs) {
|
||||
private void addRefs(Program p, ReferenceManager refMgr, List<Reference> refs) {
|
||||
for (Reference ref : refs) {
|
||||
refMgr.addReference(ref);
|
||||
}
|
||||
|
||||
+4
-19
@@ -1158,8 +1158,6 @@ public abstract class CompositeEditorPanel<T extends Composite, M extends Compos
|
||||
private int maxLength;
|
||||
private boolean bitfieldAllowed;
|
||||
|
||||
private JPanel editorPanel;
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable table1, Object value,
|
||||
boolean isSelected, int row, int column) {
|
||||
@@ -1178,7 +1176,7 @@ public abstract class CompositeEditorPanel<T extends Composite, M extends Compos
|
||||
|
||||
editor.setCellEditorValue(dt);
|
||||
|
||||
return editorPanel;
|
||||
return editor.getEditorComponent();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
@@ -1193,7 +1191,7 @@ public abstract class CompositeEditorPanel<T extends Composite, M extends Compos
|
||||
editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses
|
||||
|
||||
textField = editor.getDropDownTextField();
|
||||
textField.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
|
||||
|
||||
editor.addCellEditorListener(new CellEditorListener() {
|
||||
@Override
|
||||
public void editingCanceled(ChangeEvent e) {
|
||||
@@ -1206,17 +1204,8 @@ public abstract class CompositeEditorPanel<T extends Composite, M extends Compos
|
||||
}
|
||||
});
|
||||
|
||||
// force a small button for the table's cell editor
|
||||
JButton dataTypeChooserButton = new JButton("...") {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = 15;
|
||||
return preferredSize;
|
||||
}
|
||||
};
|
||||
|
||||
dataTypeChooserButton.addActionListener(e -> Swing.runLater(() -> stopEdit(tool)));
|
||||
JButton browseButton = editor.getBrowseButton();
|
||||
browseButton.addActionListener(e -> Swing.runLater(() -> stopEdit(tool)));
|
||||
|
||||
textField.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
@@ -1226,10 +1215,6 @@ public abstract class CompositeEditorPanel<T extends Composite, M extends Compos
|
||||
}
|
||||
});
|
||||
|
||||
editorPanel = new JPanel();
|
||||
editorPanel.setLayout(new BorderLayout());
|
||||
editorPanel.add(textField, BorderLayout.CENTER);
|
||||
editorPanel.add(dataTypeChooserButton, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
private void stopEdit(PluginTool tool) {
|
||||
|
||||
+2
-2
@@ -99,7 +99,7 @@ class CreateStructureAction extends ListingContextAction {
|
||||
return;
|
||||
}
|
||||
|
||||
CreateStructureDialog dialog = new CreateStructureDialog(plugin.getTool());
|
||||
CreateStructureDialog dialog = new CreateStructureDialog(plugin.getTool(), program);
|
||||
Structure userChoice = dialog.showCreateStructureDialog(program, tempStructure);
|
||||
|
||||
if (userChoice != null) {
|
||||
@@ -148,7 +148,7 @@ class CreateStructureAction extends ListingContextAction {
|
||||
return;
|
||||
}
|
||||
|
||||
CreateStructureDialog dialog = new CreateStructureDialog(plugin.getTool());
|
||||
CreateStructureDialog dialog = new CreateStructureDialog(plugin.getTool(), program);
|
||||
Structure userChoice =
|
||||
dialog.showCreateStructureDialog(program, tempStructure);
|
||||
|
||||
|
||||
+111
-124
@@ -54,7 +54,7 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
private static final String EXISITING_STRUCTURE_STATUS_PREFIX = "Using existing structure: ";
|
||||
|
||||
private static final String STRUCTURE_COLUMN_NAME = "Structure";
|
||||
private static final String PATH_COLUMN_NAME = "Path";
|
||||
private static final String CATEGORY_COLUMN_NAME = "Category";
|
||||
|
||||
private JTextField nameTextField;
|
||||
private CategoryPathSelectionEditor categoryPathEditor;
|
||||
@@ -74,11 +74,13 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
* Creates a new dialog with the given parent.
|
||||
*
|
||||
* @param tool The current tool that this dialog needs to access services.
|
||||
* @param program the current program
|
||||
*/
|
||||
public CreateStructureDialog(PluginTool tool) {
|
||||
public CreateStructureDialog(PluginTool tool, Program program) {
|
||||
super("Create Structure", true, true, true, false);
|
||||
|
||||
pluginTool = tool;
|
||||
this.pluginTool = tool;
|
||||
this.currentProgram = program;
|
||||
setHelpLocation(new HelpLocation("DataPlugin", "Create_Structure_Dialog"));
|
||||
|
||||
addWorkPanel(buildMainPanel());
|
||||
@@ -159,20 +161,15 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
|
||||
JLabel nameLabel = new JLabel("Name: ");
|
||||
|
||||
nameTextField = new JTextField() {
|
||||
// make sure our height doesn't stretch
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
Dimension d = super.getMaximumSize();
|
||||
d.height = getPreferredSize().height;
|
||||
return d;
|
||||
}
|
||||
};
|
||||
nameTextField = new JTextField();
|
||||
nameTextField.setName("StructureName");
|
||||
nameTextField.getAccessibleContext().setAccessibleName("Name");
|
||||
|
||||
// Allow user to click on the text field to re-activate "create new" panel without forcing
|
||||
// a click on the radio button
|
||||
nameTextField.addMouseListener(new MouseAdapter() {
|
||||
nameTextField.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent event) {
|
||||
public void focusGained(FocusEvent e) {
|
||||
createNewStructButton.setSelected(true);
|
||||
updateEnablement();
|
||||
}
|
||||
@@ -224,45 +221,13 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
|
||||
private void buildCategoryPathEditor() {
|
||||
categoryPathEditor = new CategoryPathSelectionEditor(pluginTool);
|
||||
categoryPathEditor.getEditorComponent()
|
||||
.getAccessibleContext()
|
||||
.setAccessibleName("Category");
|
||||
// make sure the "Category: " text field size matches the "Name: " text field size
|
||||
categoryPathEditor.getEditorComponent().setMaximumSize(nameTextField.getMaximumSize());
|
||||
categoryPathEditor.addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent event) {
|
||||
updateStatus(event.getDocument());
|
||||
}
|
||||
JComponent editorComponent = categoryPathEditor.getEditorComponent();
|
||||
editorComponent.getAccessibleContext().setAccessibleName("Category");
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent event) {
|
||||
updateStatus(event.getDocument());
|
||||
}
|
||||
categoryPathEditor.setCellEditorValue(CategoryPath.ROOT);
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent event) {
|
||||
updateStatus(event.getDocument());
|
||||
}
|
||||
|
||||
private void updateStatus(Document document) {
|
||||
try {
|
||||
String text = document.getText(0, document.getLength());
|
||||
if (StringUtils.isBlank(text)) {
|
||||
updateStatusText(true, null);
|
||||
}
|
||||
else {
|
||||
updateStatusText(true, "Using category: " + text);
|
||||
}
|
||||
}
|
||||
catch (BadLocationException ble) {
|
||||
// nothing we can do here
|
||||
}
|
||||
}
|
||||
});
|
||||
// Allow the user to re-activate the "new struct" panel without forcing toggle click. Use
|
||||
// FocusListener because @CategoryPathSelectionEditor.java already contains a mouse listener
|
||||
// and would override this one.
|
||||
// Allow user to click on the text field to re-activate "create new" panel without forcing
|
||||
// a click on the radio button
|
||||
categoryPathEditor.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
@@ -285,6 +250,16 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
updateEnablement();
|
||||
}
|
||||
});
|
||||
ListSelectionModel selectionModel = table.getSelectionModel();
|
||||
selectionModel.addListSelectionListener(e -> {
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (useExistingStructButton.isSelected()) {
|
||||
setOkEnabled(table.getSelectedRowCount() > 0);
|
||||
}
|
||||
});
|
||||
|
||||
filterPanel = new GhidraTableFilterPanel<>(table, structureTableModel) {
|
||||
// make sure our height doesn't stretch
|
||||
@@ -527,6 +502,18 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
JTextField getNameField() {
|
||||
return nameTextField;
|
||||
}
|
||||
|
||||
JTable getTable() {
|
||||
return matchingStructuresTable;
|
||||
}
|
||||
|
||||
CategoryPathSelectionEditor getCategoryEditor() {
|
||||
return categoryPathEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog that allows the user to create a new structure.
|
||||
* <p>
|
||||
@@ -577,34 +564,58 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
|
||||
if (nameTextField.isEnabled()) {
|
||||
// just use the name set by the user
|
||||
String nameText = nameTextField.getText();
|
||||
|
||||
if (!setCategoryPath()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
currentStructure.setName(nameText);
|
||||
}
|
||||
catch (InvalidNameException ine) {
|
||||
setStatusText(ine.getMessage());
|
||||
return;
|
||||
}
|
||||
catch (DuplicateNameException dne) {
|
||||
setStatusText(dne.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (useExistingStructButton.isSelected()) {
|
||||
// get the selected object in the table
|
||||
currentStructure = getSelectedStructure();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
// just use the name set by the user
|
||||
String nameText = nameTextField.getText();
|
||||
try {
|
||||
currentStructure.setName(nameText);
|
||||
}
|
||||
catch (InvalidNameException ine) {
|
||||
setStatusText(ine.getMessage());
|
||||
return;
|
||||
}
|
||||
catch (DuplicateNameException dne) {
|
||||
setStatusText(dne.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!setCategoryPath()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
private boolean validateName() {
|
||||
// Use the current name and category path to see if there is already an existing name. This
|
||||
// allows us to avoid a conflict.
|
||||
ProgramBasedDataTypeManager dtm = currentProgram.getDataTypeManager();
|
||||
CategoryPath path = currentStructure.getCategoryPath();
|
||||
Category category = dtm.getCategory(path);
|
||||
if (category == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String nameText = currentStructure.getName();
|
||||
DataType existingDt = category.getDataType(nameText);
|
||||
if (existingDt != null) {
|
||||
setStatusText("Name already exists: " + nameText, MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Structure getSelectedStructure() {
|
||||
int row = matchingStructuresTable.getSelectedRow();
|
||||
if (row < 0) {
|
||||
@@ -616,66 +627,43 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
|
||||
private boolean setCategoryPath() {
|
||||
|
||||
try {
|
||||
doSetCategoryPath();
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
setStatusText(e.getMessage(), MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doSetCategoryPath() throws DuplicateNameException {
|
||||
CategoryPath path = categoryPathEditor.getCellEditorValue();
|
||||
// First see if a category from the list was chosen and make sure the user didn't modify it.
|
||||
// If they did, path needs to be parsed separately.
|
||||
if (path != null && path.getPath().equals(categoryPathEditor.getCellEditorValueAsText())) {
|
||||
try {
|
||||
currentStructure.setCategoryPath(path);
|
||||
}
|
||||
catch (DuplicateNameException dne) {
|
||||
setStatusText(dne.getMessage(), MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
String editorValue = categoryPathEditor.getCellEditorValueAsText();
|
||||
if (path != null && path.getPath().equals(editorValue)) {
|
||||
currentStructure.setCategoryPath(path);
|
||||
return;
|
||||
}
|
||||
|
||||
String categoryText = categoryPathEditor.getCellEditorValueAsText();
|
||||
// Selecting/entering a category is optional; root is default
|
||||
if (!categoryText.isBlank()) {
|
||||
try {
|
||||
CategoryPath parsedPath = parseEnteredCategoryPath(categoryText);
|
||||
currentStructure.setCategoryPath(parsedPath);
|
||||
}
|
||||
catch (DuplicateNameException dne) {
|
||||
setStatusText(dne.getMessage(), MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
if (!editorValue.isBlank()) {
|
||||
CategoryPath parsedPath = parseEnteredCategoryPath(editorValue);
|
||||
currentStructure.setCategoryPath(parsedPath);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
currentStructure.setCategoryPath(CategoryPath.ROOT);
|
||||
}
|
||||
catch (DuplicateNameException dne) {
|
||||
setStatusText(dne.getMessage(), MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
currentStructure.setCategoryPath(CategoryPath.ROOT);
|
||||
}
|
||||
|
||||
private CategoryPath parseEnteredCategoryPath(String categoryText) {
|
||||
// entering a leading slash is optional, path is still generated accordingly
|
||||
if (categoryText.startsWith(CategoryPath.DELIMITER_STRING)) {
|
||||
return generateCategoryPath(categoryText.substring(1));
|
||||
return new CategoryPath(categoryText);
|
||||
}
|
||||
return generateCategoryPath(categoryText);
|
||||
}
|
||||
|
||||
private CategoryPath generateCategoryPath(String categoryText) {
|
||||
if (!categoryText.contains(CategoryPath.DELIMITER_STRING)) {
|
||||
return new CategoryPath(CategoryPath.ROOT, categoryText);
|
||||
}
|
||||
|
||||
// Additional slashes need parsed as branch(es) and final leaf
|
||||
List<String> parts = split(categoryText);
|
||||
return new CategoryPath(CategoryPath.ROOT, parts);
|
||||
}
|
||||
|
||||
private List<String> split(String categoryText) {
|
||||
List<String> parts = new ArrayList<String>(
|
||||
Arrays.asList(categoryText.split(CategoryPath.DELIMITER_STRING)));
|
||||
return parts;
|
||||
return new CategoryPath(CategoryPath.DELIMITER_STRING + categoryText);
|
||||
}
|
||||
|
||||
// a table model that is used to allow for the easy updating of the table with new List data
|
||||
@@ -707,7 +695,7 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
case 0:
|
||||
return STRUCTURE_COLUMN_NAME;
|
||||
case 1:
|
||||
return PATH_COLUMN_NAME;
|
||||
return CATEGORY_COLUMN_NAME;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -735,8 +723,7 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
case 1: {
|
||||
Structure structure = t.getStructure();
|
||||
CategoryPath path = structure.getCategoryPath();
|
||||
String name = structure.getName();
|
||||
return path.toString() + '/' + name;
|
||||
return path.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -779,7 +766,7 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
}
|
||||
|
||||
// we need this renderer in order to create nice tool tip text values
|
||||
class StructureCellRenderer extends GTableCellRenderer {
|
||||
private class StructureCellRenderer extends GTableCellRenderer {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
@@ -799,7 +786,7 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
|
||||
renderer.setToolTipText(ToolTipUtils.getToolTipText(structure));
|
||||
}
|
||||
}
|
||||
else if (PATH_COLUMN_NAME.equals(columnName)) {
|
||||
else if (CATEGORY_COLUMN_NAME.equals(columnName)) {
|
||||
if (value != null) {
|
||||
renderer.setToolTipText(value.toString());
|
||||
}
|
||||
|
||||
+1
@@ -598,6 +598,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||
public CategoryPath getCategoryPath(TreePath selectedPath) {
|
||||
DataTypeChooserDialog dialog = new DataTypeChooserDialog(this);
|
||||
dialog.setCategorySelectionMode(true);
|
||||
dialog.setShowProgramArchiveOnly(true);
|
||||
|
||||
if (selectedPath != null) {
|
||||
dialog.setSelectedPath(selectedPath);
|
||||
|
||||
+46
-13
@@ -169,10 +169,37 @@ public class DataTypeIndexer {
|
||||
}
|
||||
}
|
||||
|
||||
private class CaseInsensitiveCategoryComparator implements Comparator<CategoryPath> {
|
||||
|
||||
@Override
|
||||
public int compare(CategoryPath cp1, CategoryPath cp2) {
|
||||
|
||||
String name1 = cp1.getName();
|
||||
String name2 = cp2.getName();
|
||||
|
||||
int result = name1.compareToIgnoreCase(name2);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = name1.compareTo(name2);
|
||||
if (result != 0) {
|
||||
// let equivalent names be sorted by case ('-' for lower-case first)
|
||||
return -result;
|
||||
}
|
||||
|
||||
// if the names are the same, then sort by full path
|
||||
String p1 = cp1.getPath();
|
||||
String p2 = cp2.getPath();
|
||||
return p1.compareToIgnoreCase(p2);
|
||||
}
|
||||
}
|
||||
|
||||
private class IndexerTask extends Task {
|
||||
|
||||
private List<DataType> dataTypes = new ArrayList<>();
|
||||
private List<CategoryPath> categories;
|
||||
private List<CategoryPath> categories = new ArrayList<>();
|
||||
private Set<CategoryPath> categorySet = new HashSet<>();
|
||||
|
||||
IndexerTask() {
|
||||
super("Data Type Indexer Task", false, true, true);
|
||||
@@ -184,26 +211,32 @@ public class DataTypeIndexer {
|
||||
monitor.initialize(dataTypeManagers.size());
|
||||
monitor.setMessage("Preparing to index data types...");
|
||||
|
||||
for (DataTypeManager dataTypeManager : dataTypeManagers) {
|
||||
monitor.setMessage("Searching " + dataTypeManager.getName());
|
||||
dataTypeManager.getAllDataTypes(dataTypes);
|
||||
for (DataTypeManager dtm : dataTypeManagers) {
|
||||
monitor.setMessage("Searching " + dtm.getName());
|
||||
dtm.getAllDataTypes(dataTypes);
|
||||
|
||||
Category root = dtm.getRootCategory();
|
||||
populateCategories(root);
|
||||
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
Collections.sort(dataTypes, new CaseInsensitiveDataTypeComparator());
|
||||
populateCategoryList(dataTypes);
|
||||
|
||||
categories.addAll(categorySet);
|
||||
Collections.sort(categories, new CaseInsensitiveCategoryComparator());
|
||||
}
|
||||
|
||||
private void populateCategoryList(List<DataType> dataTypes) {
|
||||
private void populateCategories(Category parent) {
|
||||
|
||||
Set<CategoryPath> set = new HashSet<>();
|
||||
for (DataType dt : dataTypes) {
|
||||
CategoryPath path = dt.getCategoryPath();
|
||||
set.add(path);
|
||||
categorySet.add(parent.getCategoryPath());
|
||||
Category[] children = parent.getCategories();
|
||||
for (Category category : children) {
|
||||
CategoryPath path = category.getCategoryPath();
|
||||
categorySet.add(path);
|
||||
|
||||
populateCategories(category);
|
||||
}
|
||||
|
||||
categories = new ArrayList<>(set);
|
||||
Collections.sort(categories);
|
||||
}
|
||||
|
||||
List<DataType> getList() {
|
||||
|
||||
+8
@@ -674,6 +674,13 @@ public class DataTypeManagerHandler {
|
||||
return builtInDataTypesManager;
|
||||
}
|
||||
|
||||
public DataTypeManager getProgramDataTypeManager() {
|
||||
if (programArchive != null) {
|
||||
return programArchive.getDataTypeManager();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public DataTypeIndexer getDataTypeIndexer() {
|
||||
return dataTypeIndexer;
|
||||
}
|
||||
@@ -1810,4 +1817,5 @@ public class DataTypeManagerHandler {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+39
-15
@@ -34,8 +34,15 @@ public class ArchiveRootNode extends DataTypeTreeNode {
|
||||
|
||||
private DtFilterState dtFilterState = new DtFilterState();
|
||||
|
||||
ArchiveRootNode(DataTypeManagerHandler archiveManager) {
|
||||
private boolean programDtmOnly;
|
||||
|
||||
public ArchiveRootNode(DataTypeManagerHandler archiveManager) {
|
||||
this(archiveManager, false);
|
||||
}
|
||||
|
||||
public ArchiveRootNode(DataTypeManagerHandler archiveManager, boolean programDtmOnly) {
|
||||
this.archiveManager = archiveManager;
|
||||
this.programDtmOnly = programDtmOnly;
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -93,21 +100,29 @@ public class ArchiveRootNode extends DataTypeTreeNode {
|
||||
}
|
||||
|
||||
// a factory method to isolate non-OO inheritance checks
|
||||
private static final GTreeNode createArchiveNode(Archive archive, DtFilterState dtFilterState) {
|
||||
private final GTreeNode createArchiveNode(Archive archive, DtFilterState filterState) {
|
||||
|
||||
if (programDtmOnly) {
|
||||
if (archive instanceof ProgramArchive) {
|
||||
return new ProgramArchiveNode((ProgramArchive) archive, filterState);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (archive instanceof FileArchive) {
|
||||
return new FileArchiveNode((FileArchive) archive, dtFilterState);
|
||||
return new FileArchiveNode((FileArchive) archive, filterState);
|
||||
}
|
||||
else if (archive instanceof ProjectArchive) {
|
||||
return new ProjectArchiveNode((ProjectArchive) archive, dtFilterState);
|
||||
return new ProjectArchiveNode((ProjectArchive) archive, filterState);
|
||||
}
|
||||
else if (archive instanceof InvalidFileArchive) {
|
||||
return new InvalidArchiveNode((InvalidFileArchive) archive);
|
||||
}
|
||||
else if (archive instanceof ProgramArchive) {
|
||||
return new ProgramArchiveNode((ProgramArchive) archive, dtFilterState);
|
||||
return new ProgramArchiveNode((ProgramArchive) archive, filterState);
|
||||
}
|
||||
else if (archive instanceof BuiltInArchive) {
|
||||
return new BuiltInArchiveNode((BuiltInArchive) archive, dtFilterState);
|
||||
return new BuiltInArchiveNode((BuiltInArchive) archive, filterState);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -178,7 +193,10 @@ public class ArchiveRootNode extends DataTypeTreeNode {
|
||||
public List<GTreeNode> generateChildren() {
|
||||
List<GTreeNode> list = new ArrayList<>();
|
||||
for (Archive element : archiveManager.getAllArchives()) {
|
||||
list.add(createArchiveNode(element, dtFilterState));
|
||||
GTreeNode node = createArchiveNode(element, dtFilterState);
|
||||
if (node != null) {
|
||||
list.add(node);
|
||||
}
|
||||
}
|
||||
Collections.sort(list);
|
||||
return list;
|
||||
@@ -221,15 +239,21 @@ public class ArchiveRootNode extends DataTypeTreeNode {
|
||||
|
||||
@Override
|
||||
public void archiveOpened(Archive archive) {
|
||||
if (isLoaded()) {
|
||||
GTreeNode node = createArchiveNode(archive, dtFilterState);
|
||||
List<GTreeNode> allChildrenList = getChildren();
|
||||
int index = Collections.binarySearch(allChildrenList, node);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
}
|
||||
addNode(index, node);
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GTreeNode node = createArchiveNode(archive, dtFilterState);
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<GTreeNode> allChildrenList = getChildren();
|
||||
int index = Collections.binarySearch(allChildrenList, node);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
}
|
||||
addNode(index, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+34
-3
@@ -31,12 +31,14 @@ import docking.DialogComponentProvider;
|
||||
import docking.Tool;
|
||||
import docking.widgets.filter.FilterOptions;
|
||||
import docking.widgets.filter.TextFilterStrategy;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.tree.*;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.app.util.datatype.DataTypeSelectionDialog;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@@ -46,17 +48,20 @@ import ghidra.util.task.TaskMonitor;
|
||||
* {@link DataTypeSelectionDialog} utility widget.
|
||||
*/
|
||||
public class DataTypeChooserDialog extends DialogComponentProvider {
|
||||
|
||||
private DataTypeManagerPlugin plugin;
|
||||
private DataTypeArchiveGTree tree;
|
||||
private DataType selectedDataType;
|
||||
private CategoryPath selectedCategoryPath;
|
||||
|
||||
private GLabel messageLabel;
|
||||
private GDLabel messageLabel;
|
||||
private boolean isFilterEditable;
|
||||
|
||||
private boolean categorySelectionMode;
|
||||
|
||||
public DataTypeChooserDialog(DataTypeManagerPlugin plugin) {
|
||||
super("Data Type Chooser", true, true, true, false);
|
||||
this.plugin = plugin;
|
||||
|
||||
tree = new DataTypeArchiveGTree(plugin);
|
||||
|
||||
@@ -98,6 +103,8 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setOkEnabled(false);
|
||||
|
||||
setHelpLocation(new HelpLocation("DataTypeEditors", "browse"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,6 +113,29 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
|
||||
*/
|
||||
public void setCategorySelectionMode(boolean categorySelectionMode) {
|
||||
this.categorySelectionMode = categorySelectionMode;
|
||||
if (categorySelectionMode) {
|
||||
setTitle("Category Chooser");
|
||||
messageLabel.setText("Choose a category:");
|
||||
}
|
||||
else {
|
||||
setTitle("Data Type Chooser");
|
||||
messageLabel.setText("Choose a data type:");
|
||||
}
|
||||
}
|
||||
|
||||
public void setShowProgramArchiveOnly(boolean programOnly) {
|
||||
DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler();
|
||||
if (programOnly) {
|
||||
DataTypeManager programDtm = handler.getProgramDataTypeManager();
|
||||
if (programDtm != null) {
|
||||
ArchiveRootNode root = new ArchiveRootNode(handler, true);
|
||||
tree.setRootNode(root);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ArchiveRootNode root = new ArchiveRootNode(handler);
|
||||
tree.setRootNode(root);
|
||||
}
|
||||
|
||||
private boolean isValidNodeSelected() {
|
||||
@@ -152,7 +182,7 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
|
||||
|
||||
private JComponent createWorkPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
messageLabel = new GLabel("Choose the data type you wish to use.");
|
||||
messageLabel = new GDLabel("Choose the data type you wish to use.");
|
||||
messageLabel.setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 2));
|
||||
messageLabel.getAccessibleContext().setAccessibleName("Message");
|
||||
panel.add(messageLabel, BorderLayout.NORTH);
|
||||
@@ -335,4 +365,5 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-23
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.function.editor;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.util.EventObject;
|
||||
|
||||
@@ -32,16 +32,15 @@ import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.util.MessageType;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.data.DataTypeParser;
|
||||
|
||||
class ParameterDataTypeCellEditor extends AbstractCellEditor
|
||||
implements TableCellEditor, FocusableEditor {
|
||||
private DataTypeSelectionEditor editor;
|
||||
private DropDownSelectionTextField<DataType> textField;
|
||||
private JButton dataTypeChooserButton;
|
||||
private DataType dt;
|
||||
|
||||
private JPanel editorPanel;
|
||||
private DataTypeManagerService service;
|
||||
private DialogComponentProvider dialog;
|
||||
private DataTypeManager dtm;
|
||||
@@ -62,7 +61,7 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor
|
||||
|
||||
editor.setCellEditorValue(dt);
|
||||
|
||||
return editorPanel;
|
||||
return editor.getEditorComponent();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
@@ -83,17 +82,8 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor
|
||||
}
|
||||
});
|
||||
|
||||
// force a small button for the table's cell editor
|
||||
dataTypeChooserButton = new JButton("...") {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = 15;
|
||||
return preferredSize;
|
||||
}
|
||||
};
|
||||
|
||||
dataTypeChooserButton.addActionListener(e -> SwingUtilities.invokeLater(() -> {
|
||||
JButton browseButton = editor.getBrowseButton();
|
||||
browseButton.addActionListener(e -> Swing.runLater(() -> {
|
||||
DataType dataType = service.getDataType((String) null);
|
||||
if (dataType != null) {
|
||||
editor.setCellEditorValue(dataType);
|
||||
@@ -104,18 +94,13 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor
|
||||
}
|
||||
}));
|
||||
|
||||
FocusAdapter focusListener = new FocusAdapter() {
|
||||
textField.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
textField.selectAll();
|
||||
textField.removeFocusListener(this);
|
||||
}
|
||||
};
|
||||
textField.addFocusListener(focusListener);
|
||||
|
||||
editorPanel = new JPanel(new BorderLayout());
|
||||
editorPanel.add(textField, BorderLayout.CENTER);
|
||||
editorPanel.add(dataTypeChooserButton, BorderLayout.EAST);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,7 +121,7 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor
|
||||
* be returned if getTableCellEditorComponent method has not yet been invoked.
|
||||
*/
|
||||
public JButton getChooserButton() {
|
||||
return dataTypeChooserButton;
|
||||
return editor.getBrowseButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+73
-72
@@ -15,11 +15,14 @@
|
||||
*/
|
||||
package ghidra.app.util.datatype;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
@@ -30,7 +33,6 @@ 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
|
||||
@@ -92,6 +94,8 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
||||
private void init() {
|
||||
selectionField = createDropDownSelectionTextField(
|
||||
new CategoryPathDropDownSelectionDataModel(dataTypeManagerService));
|
||||
selectionField.setName("CategoryPath");
|
||||
selectionField.getAccessibleContext().setAccessibleName("Category");
|
||||
selectionField.addCellEditorListener(new CellEditorListener() {
|
||||
@Override
|
||||
public void editingCanceled(ChangeEvent e) {
|
||||
@@ -113,16 +117,12 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
||||
selectionField.requestFocus();
|
||||
}
|
||||
});
|
||||
selectionField.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
|
||||
browseButton = new BrowseButton();
|
||||
browseButton.setToolTipText("Browse Existing Category Paths");
|
||||
browseButton.addActionListener(e -> showBrowser());
|
||||
|
||||
JPanel browsePanel = buildBrowsePanel();
|
||||
editorPanel = new JPanel();
|
||||
editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.X_AXIS));
|
||||
editorPanel.add(selectionField);
|
||||
editorPanel.add(Box.createHorizontalStrut(5));
|
||||
editorPanel.add(browseButton);
|
||||
editorPanel.add(browsePanel);
|
||||
|
||||
keyListener = new KeyAdapter() {
|
||||
|
||||
@@ -144,6 +144,60 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
||||
};
|
||||
}
|
||||
|
||||
private JPanel buildBrowsePanel() {
|
||||
|
||||
// We override the various sizes to make sure the button does not get too big or too small,
|
||||
// which changes depending upon the theme being used.
|
||||
JPanel browsePanel = new JPanel() {
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
int width = getBestWidth();
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = Math.min(width, preferredSize.width);
|
||||
return preferredSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
int width = getBestWidth();
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = Math.min(width, preferredSize.width);
|
||||
return preferredSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
int width = getBestWidth();
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = Math.min(width, preferredSize.width);
|
||||
return preferredSize;
|
||||
}
|
||||
|
||||
private int getBestWidth() {
|
||||
Font f = getFont();
|
||||
FontMetrics fm = getFontMetrics(f);
|
||||
int width = fm.stringWidth(" . . . ");
|
||||
return width;
|
||||
}
|
||||
};
|
||||
|
||||
browsePanel.setLayout(new BorderLayout());
|
||||
browsePanel.setOpaque(false);
|
||||
|
||||
// Space the button so that it pops out visually. This was chosen by trial-and-error and
|
||||
// looks reasonable on all themes.
|
||||
Border empty = BorderFactory.createEmptyBorder(2, 2, 1, 1);
|
||||
browsePanel.setBorder(empty);
|
||||
|
||||
browseButton = new BrowseButton();
|
||||
browseButton.setToolTipText("Browse Existing Category Paths");
|
||||
browseButton.addActionListener(e -> showBrowser());
|
||||
browsePanel.add(browseButton);
|
||||
|
||||
return browsePanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value in the cell.
|
||||
* @return categoryPath of the selected value from the drop-down
|
||||
@@ -179,8 +233,8 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the dropdown text field that holds the category path collection.
|
||||
* @return CategoryPath dropdown selection text field object
|
||||
* Retrieve the drop-down text field that holds the category path collection.
|
||||
* @return CategoryPath drop-down selection text field object
|
||||
*/
|
||||
public DropDownSelectionTextField<CategoryPath> getDropDownTextField() {
|
||||
return selectionField;
|
||||
@@ -333,13 +387,6 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -349,66 +396,32 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
||||
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);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
List<CategoryPath> results = new ArrayList<>();
|
||||
for (CategoryPath path : data) {
|
||||
String pathString = path.getPath();
|
||||
if (pathString.contains(searchText)) {
|
||||
results.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (endIndex < 0) {
|
||||
endIndex = -endIndex - 1;
|
||||
}
|
||||
|
||||
return data.subList(startIndex, endIndex);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -434,18 +447,6 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
||||
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
|
||||
|
||||
+60
-5
@@ -15,9 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.util.datatype;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
@@ -141,15 +143,13 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
|
||||
|
||||
selectionField.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
|
||||
|
||||
browseButton = new BrowseButton();
|
||||
browseButton.setToolTipText("Browse the Data Manager");
|
||||
browseButton.addActionListener(e -> showDataTypeBrowser());
|
||||
JPanel browsePanel = buildBrowsePanel();
|
||||
|
||||
editorPanel = new JPanel();
|
||||
editorPanel.setOpaque(false);
|
||||
editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.X_AXIS));
|
||||
editorPanel.add(selectionField);
|
||||
editorPanel.add(Box.createHorizontalStrut(5));
|
||||
editorPanel.add(browseButton);
|
||||
editorPanel.add(browsePanel);
|
||||
|
||||
keyListener = new KeyAdapter() {
|
||||
|
||||
@@ -171,6 +171,61 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
|
||||
};
|
||||
}
|
||||
|
||||
private JPanel buildBrowsePanel() {
|
||||
|
||||
// We override the various sizes to make sure the button does not get too big or too small,
|
||||
// which changes depending upon the theme being used.
|
||||
JPanel browsePanel = new JPanel() {
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
int width = getBestWidth();
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = Math.min(width, preferredSize.width);
|
||||
return preferredSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
int width = getBestWidth();
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = Math.min(width, preferredSize.width);
|
||||
return preferredSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
int width = getBestWidth();
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = Math.min(width, preferredSize.width);
|
||||
return preferredSize;
|
||||
}
|
||||
|
||||
private int getBestWidth() {
|
||||
Font f = getFont();
|
||||
FontMetrics fm = getFontMetrics(f);
|
||||
int width = fm.stringWidth(" . . . ");
|
||||
return width;
|
||||
}
|
||||
};
|
||||
|
||||
browsePanel.setLayout(new BorderLayout());
|
||||
browsePanel.setOpaque(false);
|
||||
|
||||
// Space the button so that it pops out visually. This was chosen by trial-and-error and
|
||||
// looks reasonable on all themes.
|
||||
Border empty = BorderFactory.createEmptyBorder(2, 2, 1, 1);
|
||||
browsePanel.setBorder(empty);
|
||||
|
||||
browseButton = new BrowseButton();
|
||||
browseButton.setToolTipText("Browse the Data Manager");
|
||||
browseButton.addActionListener(e -> showDataTypeBrowser());
|
||||
|
||||
browsePanel.add(browseButton);
|
||||
|
||||
return browsePanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return selectionField.getSelectedValue();
|
||||
|
||||
@@ -1001,7 +1001,10 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
|
||||
public void selectRow(final JTable table, final int rowIndex) {
|
||||
waitForTable(table);
|
||||
|
||||
runSwing(() -> table.setRowSelectionInterval(rowIndex, rowIndex));
|
||||
runSwing(() -> {
|
||||
table.setRowSelectionInterval(rowIndex, rowIndex);
|
||||
table.requestFocus();
|
||||
});
|
||||
waitForTable(table);
|
||||
}
|
||||
|
||||
|
||||
@@ -974,7 +974,7 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
||||
fireUserChoiceMade(selectedItem);
|
||||
}
|
||||
|
||||
class PreviewListener implements ListSelectionListener {
|
||||
private class PreviewListener implements ListSelectionListener {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting()) {
|
||||
|
||||
@@ -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.
|
||||
@@ -55,5 +55,7 @@ public class BrowseButton extends JButton {
|
||||
setIcon(ICON);
|
||||
setName(NAME);
|
||||
setToolTipText(TOOLTIP_TEXT);
|
||||
|
||||
getAccessibleContext().setAccessibleName("Browse");
|
||||
}
|
||||
}
|
||||
|
||||
+18
-1
@@ -19,6 +19,9 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -66,7 +69,21 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
GhidraTable table = (GhidraTable) getInstanceField("matchingStructuresTable", dialog);
|
||||
selectRow(table, 2);
|
||||
|
||||
captureDialog(500, 400);
|
||||
shrinkCategoryColumn(table);
|
||||
|
||||
captureDialog(600, 500);
|
||||
}
|
||||
|
||||
private void shrinkCategoryColumn(JTable table) {
|
||||
|
||||
runSwing(() -> {
|
||||
TableColumnModel columnModel = table.getColumnModel();
|
||||
int columnIndex = columnModel.getColumnIndex("Category");
|
||||
TableColumn column = columnModel.getColumn(columnIndex);
|
||||
int size = 150;
|
||||
column.setPreferredWidth(size);
|
||||
column.setMaxWidth(size);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user