diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/RegisterDropDownSelectionDataModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/RegisterDropDownSelectionDataModel.java new file mode 100644 index 0000000000..dedef9c5f9 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/RegisterDropDownSelectionDataModel.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.function.editor; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.ListCellRenderer; + +import docking.widgets.DropDownSelectionTextField; +import docking.widgets.DropDownTextFieldDataModel; +import docking.widgets.list.GListCellRenderer; +import ghidra.program.model.lang.Register; + +/** + * The data model for {@link DropDownSelectionTextField} that allows the text field to work with + * {@link Register}s. + */ +public class RegisterDropDownSelectionDataModel implements DropDownTextFieldDataModel { + + private List registers; + + public RegisterDropDownSelectionDataModel(List registers) { + this.registers = registers; + } + + @Override + public ListCellRenderer getListRenderer() { + return new GListCellRenderer(); + } + + @Override + public String getDescription(Register value) { + return null; + } + + @Override + public String getDisplayText(Register value) { + return value.getName(); + } + + @Override + public List getMatchingData(String searchText) { + + if (searchText == null || searchText.length() == 0) { + return registers; + } + + searchText = searchText.toLowerCase(); + + List regList = new ArrayList<>(); + for (Register reg : registers) { + String regName = reg.getName().toLowerCase(); + if (regName.startsWith(searchText)) { + regList.add(reg); + } + } + return regList; + } + + @Override + public int getIndexOfFirstMatchingEntry(List data, String searchText) { + + String lcSearchText = searchText.toLowerCase(); + int len = data.size(); + for (int i = 0; i < len; i++) { + String name = data.get(i).getName(); + String lcName = name.toLowerCase(); + if (lcName.startsWith(lcSearchText)) { + return i; + } + } + return 0; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/StorageTableCellEditor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/StorageTableCellEditor.java index 66449ad8d3..88003ae3b2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/StorageTableCellEditor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/StorageTableCellEditor.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -58,6 +58,7 @@ class StorageTableCellEditor extends AbstractCellEditor implements TableCellEdit JTextField field = new JTextField(stringValue); field.setBackground(getUneditableForegroundColor(isSelected)); field.setEditable(false); + field.setBorder(null); ParameterTableModel tableModel = (ParameterTableModel) table.getModel(); FunctionVariableData rowData = tableModel.getRowObject(row); final StorageAddressEditorDialog dialog = new StorageAddressEditorDialog(model.getProgram(), diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java index 89bcb26a78..278818798e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/VarnodeLocationCellEditor.java @@ -19,13 +19,16 @@ import java.awt.Component; import java.awt.event.MouseEvent; import java.math.BigInteger; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import javax.swing.*; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; -import docking.widgets.combobox.GhidraComboBox; +import org.apache.commons.lang3.StringUtils; + +import docking.widgets.DropDownSelectionTextField; import docking.widgets.table.FocusableEditor; import docking.widgets.textfield.IntegerTextField; import generic.theme.GThemeDefaults.Colors.Palette; @@ -42,18 +45,16 @@ class VarnodeLocationCellEditor extends AbstractCellEditor private Program program; private VarnodeType type; private Component editorComponent; - private GhidraComboBox combo; + private DropDownSelectionTextField registerEntryTextField; private AddressInput addressInput; private IntegerTextField offsetInput; private Comparator registerWrapperComparator = (r1, r2) -> r1.toString().compareToIgnoreCase(r2.toString()); private VarnodeInfo currentVarnode; - private int maxRegisterSize; VarnodeLocationCellEditor(StorageAddressModel model) { this.program = model.getProgram(); - this.maxRegisterSize = program.getDefaultPointerSize(); } @Override @@ -68,13 +69,13 @@ class VarnodeLocationCellEditor extends AbstractCellEditor public boolean stopCellEditing() { switch (type) { case Register: - Object selectedObj = combo.getSelectedItem(); - if (selectedObj instanceof String) { - if (program.getRegister((String) selectedObj) == null) { + String regName = registerEntryTextField.getText().trim(); + if (program.getRegister(regName) == null) { + if (!StringUtils.isBlank(regName)) { Msg.showError(this, editorComponent, "Invalid Register", - "Register does not exist: " + selectedObj); - return false; + "Register does not exist: " + regName); } + return false; } break; @@ -102,7 +103,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor public Object getCellEditorValue() { switch (type) { case Register: - return combo.getSelectedItem(); + return registerEntryTextField.getText(); case Stack: BigInteger value = offsetInput.getValue(); @@ -175,47 +176,51 @@ class VarnodeLocationCellEditor extends AbstractCellEditor private Component createRegisterCombo(VarnodeInfo varnode) { ProgramContext programContext = program.getProgramContext(); - List validItems = new ArrayList<>(programContext.getRegisters()); + List registers = new ArrayList<>(programContext.getRegisters()); - for (Iterator iter = validItems.iterator(); iter.hasNext();) { + for (Iterator iter = registers.iterator(); iter.hasNext();) { Register register = iter.next(); if (register.isProcessorContext() || register.isHidden()) { iter.remove(); } } - Collections.sort(validItems, registerWrapperComparator); - Register[] registers = validItems.toArray(new Register[validItems.size()]); + Collections.sort(registers, registerWrapperComparator); + //Register[] registers = validItems.toArray(new Register[validItems.size()]); + + RegisterDropDownSelectionDataModel registerModel = + new RegisterDropDownSelectionDataModel(registers); + registerEntryTextField = new DropDownSelectionTextField<>(registerModel); + registerEntryTextField.setBorder(null); + + AtomicReference currentReg = new AtomicReference<>(); - combo = new GhidraComboBox<>(registers); - combo.setEditable(false); - combo.setEnterKeyForwarding(true); Address address = varnode.getAddress(); if (address != null && varnode.getSize() != null) { Register register = program.getRegister(address, varnode.getSize()); - combo.setSelectedItem(register); + if (register != null) { + currentReg.set(register); + registerEntryTextField.setText(register.getName()); + } } - combo.addPopupMenuListener(new PopupMenuListener() { + registerEntryTextField.addCellEditorListener(new CellEditorListener() { @Override - public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - // ignore - } - - @Override - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + public void editingStopped(ChangeEvent e) { stopCellEditing(); } @Override - public void popupMenuCanceled(PopupMenuEvent e) { - // ignore + public void editingCanceled(ChangeEvent e) { + cancelCellEditing(); } }); - combo.addActionListener(e -> stopCellEditing()); + registerEntryTextField.addActionListener(e -> stopCellEditing()); - return combo; + registerEntryTextField.showMatchingList(); + + return registerEntryTextField; } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java index ea71d17b22..dbb7e4119c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java @@ -350,7 +350,7 @@ public class DropDownTextField extends JTextField implements GComponent { // for testing so that we can override, otherwise would be private protected List getMatchingData(String searchText) { - if (searchText == null || searchText.length() == 0) { + if (searchText == null) { return Collections.emptyList(); } @@ -372,6 +372,15 @@ public class DropDownTextField extends JTextField implements GComponent { return matchingWindow.isShowing(); } + /** + * Shows the matching list. This can be used to show all data when the user has not typed any + * text. + */ + public void showMatchingList() { + String text = pendingTextUpdate != null ? pendingTextUpdate : getText(); + updateDisplayContents(text); + } + /** * When true, this field will not pass Enter key press events up to it's parent when the * drop-down selection window is open. However, an Enter key press will still be