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 278818798e..2bf6ccbf47 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 @@ -39,6 +39,7 @@ import ghidra.program.model.lang.Register; import ghidra.program.model.listing.Program; import ghidra.program.model.listing.ProgramContext; import ghidra.util.Msg; +import ghidra.util.Swing; class VarnodeLocationCellEditor extends AbstractCellEditor implements TableCellEditor, FocusableEditor { @@ -193,6 +194,9 @@ class VarnodeLocationCellEditor extends AbstractCellEditor registerEntryTextField = new DropDownSelectionTextField<>(registerModel); registerEntryTextField.setBorder(null); + // this allows us to show the matching list when there is no text in the editor + registerEntryTextField.setShowMatchingListOnEmptyText(true); + AtomicReference currentReg = new AtomicReference<>(); Address address = varnode.getAddress(); @@ -219,7 +223,12 @@ class VarnodeLocationCellEditor extends AbstractCellEditor registerEntryTextField.addActionListener(e -> stopCellEditing()); - registerEntryTextField.showMatchingList(); + // Note: need to do this later. At the time of construction, this text field is not yet + // showing. The text field has checks to avoid showing the list if it is not showing. By + // running later, this call will happen once the widget has been added to the table. + Swing.runLater(() -> { + registerEntryTextField.showMatchingList(); + }); return registerEntryTextField; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/IconPropertyEditor.java b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/IconPropertyEditor.java index f068b3bcec..9ccfd99661 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/IconPropertyEditor.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/IconPropertyEditor.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. @@ -151,14 +151,8 @@ public class IconPropertyEditor extends PropertyEditorSupport { private Component buildTopPanel() { JPanel panel = new JPanel(new BorderLayout()); dataModel = new IconDropDownDataModel(); - dropDown = new DropDownSelectionTextField<>(dataModel) { - protected List getMatchingData(String searchText) { - if (searchText.isBlank()) { - return ((IconDropDownDataModel) dataModel).getData(); - } - return super.getMatchingData(searchText); - } - }; + dropDown = new DropDownSelectionTextField<>(dataModel); + dropDown.setShowMatchingListOnEmptyText(true); dropDown.addDropDownSelectionChoiceListener(choiceListener); panel.add(dropDown, BorderLayout.CENTER); JButton browseButton = new BrowseButton(); 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 dbb7e4119c..9af89a2356 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/DropDownTextField.java @@ -91,6 +91,7 @@ public class DropDownTextField extends JTextField implements GComponent { private boolean consumeEnterKeyPress = true; // consume Enter presses by default private boolean ignoreEnterKeyPress = false; // do not ignore enter by default private boolean ignoreCaretChanges; + private boolean showMachingListOnEmptyText; // We use an update manager to buffer requests to update the matches. This allows us to be // more responsive when the user is attempting to type multiple characters @@ -354,6 +355,12 @@ public class DropDownTextField extends JTextField implements GComponent { return Collections.emptyList(); } + // By default we do not show the matches list is empty. This seems less noisy. Some + // clients would rather have empty text show all available choices. + if (searchText.isEmpty() && !showMachingListOnEmptyText) { + return Collections.emptyList(); + } + // get the sublist of matches--this may take a while Cursor previousCursor = getCursor(); try { @@ -377,8 +384,22 @@ public class DropDownTextField extends JTextField implements GComponent { * text. */ public void showMatchingList() { - String text = pendingTextUpdate != null ? pendingTextUpdate : getText(); - updateDisplayContents(text); + + // + // We temporarily enable this list to show for empty text, even if the text is not empty. + // This handles the default setting, which has this feature off. We can refactor this class + // to allow us to make a direct call instead of using this temporary setting. This seems + // simple enough for now. + // + boolean restore = showMachingListOnEmptyText; + try { + showMachingListOnEmptyText = true; + pendingTextUpdate = pendingTextUpdate != null ? pendingTextUpdate : getText(); + updateManager.updateNow(); + } + finally { + showMachingListOnEmptyText = restore; + } } /** @@ -414,6 +435,15 @@ public class DropDownTextField extends JTextField implements GComponent { this.ignoreEnterKeyPress = ignore; } + /** + * Allows this text field to show all potential matches when the text of the field is empty. + * The default is false. + * @param show true to allow the list to be shown + */ + public void setShowMatchingListOnEmptyText(boolean show) { + this.showMachingListOnEmptyText = show; + } + /** * Sets the height of the matching window. The default value is {@value #MIN_HEIGHT}. * diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/AbstractDropDownTextFieldTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/AbstractDropDownTextFieldTest.java index 5f8033d8b6..74707e8884 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/widgets/AbstractDropDownTextFieldTest.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/AbstractDropDownTextFieldTest.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. @@ -30,7 +30,6 @@ import org.junit.After; import org.junit.Before; import docking.test.AbstractDockingTest; -import ghidra.util.Msg; public abstract class AbstractDropDownTextFieldTest extends AbstractDockingTest { @@ -234,10 +233,10 @@ public abstract class AbstractDropDownTextFieldTest extends AbstractDockingTe protected void hideWindowPressKeyThenValidate(int keyCode) { JWindow matchingWindow = textField.getActiveMatchingWindow(); - matchingWindow.setVisible(false); + runSwing(() -> matchingWindow.setVisible(false)); waitForSwing(); - assertTrue("The completion window is showing after a call to setVisible(false).", - !matchingWindow.isShowing()); + assertFalse("The completion window is showing after a call to setVisible(false).", + matchingWindow.isShowing()); tpyeActionKey(keyCode); assertTrue("The completion window is not showing after being trigger by a navigation key.", matchingWindow.isShowing()); @@ -309,10 +308,6 @@ public abstract class AbstractDropDownTextFieldTest extends AbstractDockingTe return; } - if (!matchingWindow.isShowing()) { - Msg.debug(this, ""); - } - assertTrue("Window is not showing when it should be", matchingWindow.isShowing()); } diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/DropDownTextFieldTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/DropDownTextFieldTest.java index 662662e0de..c3cf8c9d2a 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/widgets/DropDownTextFieldTest.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/DropDownTextFieldTest.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. @@ -435,4 +435,42 @@ public class DropDownTextFieldTest extends AbstractDropDownTextFieldTest Dimension windowSize = matchingWindow.getSize(); assertEquals(newSize, windowSize.height); } + + @Test + public void testShowMatchingListOnEmptyText() { + + runSwing(() -> textField.setShowMatchingListOnEmptyText(false)); + + // insert some text and make sure the window is created + typeText("d", true); + assertMatchingWindowShowing(); + + // with this setting off, the list stays visible on empty text + clearText(); + assertMatchingWindowHidden(); + + // even with this setting off, this method should always force the list to show + showMatchingList(); + assertMatchingWindowShowing(); + + runSwing(() -> textField.setShowMatchingListOnEmptyText(true)); + + showMatchingList(); + assertMatchingWindowShowing(); + + typeText("d", true); + assertMatchingWindowShowing(); + + // with this setting on, the list stays visible on empty text + clearText(); + assertMatchingWindowShowing(); + + // with this setting on, this method should always force the list to show + showMatchingList(); + assertMatchingWindowShowing(); + } + + private void showMatchingList() { + runSwing(() -> textField.showMatchingList()); + } }