mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-13 21:52:59 +00:00
Merge remote-tracking branch
'origin/GP-1685-dragonmacher-decompiler-find-text' into patch (Closes #3890)
This commit is contained in:
commit
ebc9a89860
|
@ -19,7 +19,6 @@ import java.awt.BorderLayout;
|
|||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
|
@ -39,28 +38,28 @@ class AnnotationHandlerDialog extends DialogComponentProvider {
|
|||
private AnnotationHandler handler;
|
||||
|
||||
private boolean success;
|
||||
|
||||
|
||||
AnnotationHandlerDialog(List<AnnotationHandler> handlerList) {
|
||||
super("Export Format");
|
||||
this.handlerList = handlerList;
|
||||
|
||||
|
||||
addWorkPanel(create());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setOkEnabled(true);
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DATA_MANAGER, "Export_To"));
|
||||
setRememberSize( false );
|
||||
setRememberSize(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
protected void cancelCallback() {
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
Object [] objs = handlerComboBox.getSelectedObjects();
|
||||
protected void okCallback() {
|
||||
Object[] objs = handlerComboBox.getSelectedObjects();
|
||||
if (objs != null && objs.length > 0) {
|
||||
handler = (AnnotationHandler) objs[0];
|
||||
}
|
||||
|
@ -70,9 +69,10 @@ class AnnotationHandlerDialog extends DialogComponentProvider {
|
|||
|
||||
JPanel create() {
|
||||
JPanel outerPanel = new JPanel(new BorderLayout());
|
||||
|
||||
handlerComboBox = new GhidraComboBox<>(new Vector<AnnotationHandler>(handlerList));
|
||||
|
||||
handlerComboBox = new GhidraComboBox<>(handlerList);
|
||||
handlerComboBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
okCallback();
|
||||
}
|
||||
|
@ -80,8 +80,12 @@ class AnnotationHandlerDialog extends DialogComponentProvider {
|
|||
outerPanel.add(handlerComboBox, BorderLayout.NORTH);
|
||||
return outerPanel;
|
||||
}
|
||||
|
||||
public AnnotationHandler getHandler() { return handler; }
|
||||
|
||||
public boolean wasSuccessful() { return success; }
|
||||
|
||||
public AnnotationHandler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public boolean wasSuccessful() {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
|||
private Component buildFormatChooser() {
|
||||
|
||||
List<Exporter> exporters = getApplicableExporters();
|
||||
comboBox = new GhidraComboBox<>(new Vector<>(exporters));
|
||||
comboBox = new GhidraComboBox<>(exporters);
|
||||
|
||||
Exporter defaultExporter = getDefaultExporter(exporters);
|
||||
if (defaultExporter != null) {
|
||||
|
|
|
@ -260,7 +260,7 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
set.add(loader);
|
||||
}
|
||||
}
|
||||
loaderComboBox = new GhidraComboBox<>(new Vector<>(set));
|
||||
loaderComboBox = new GhidraComboBox<>(set);
|
||||
loaderComboBox.addItemListener(e -> selectedLoaderChanged());
|
||||
loaderComboBox.setEnterKeyForwarding(true);
|
||||
loaderComboBox.setRenderer(
|
||||
|
|
|
@ -45,14 +45,13 @@ public class FindAction extends AbstractDecompilerAction {
|
|||
if (findDialog == null) {
|
||||
findDialog =
|
||||
new FindDialog("Decompiler Find Text", new DecompilerSearcher(decompilerPanel)) {
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
// clear the search results when the dialog is closed
|
||||
decompilerPanel.setSearchResults(null);
|
||||
}
|
||||
};
|
||||
findDialog
|
||||
.setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionFind"));
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
// clear the search results when the dialog is closed
|
||||
decompilerPanel.setSearchResults(null);
|
||||
}
|
||||
};
|
||||
findDialog.setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionFind"));
|
||||
}
|
||||
return findDialog;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package docking.widgets;
|
|||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
|
@ -149,7 +150,7 @@ public class FindDialog extends DialogComponentProvider {
|
|||
SearchLocation searchLocation =
|
||||
searcher.search(searchText, cursorPosition, forward, useRegex);
|
||||
|
||||
//
|
||||
//
|
||||
// First, just search in the current direction.
|
||||
//
|
||||
if (searchLocation != null) {
|
||||
|
@ -157,7 +158,7 @@ public class FindDialog extends DialogComponentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Did not find the text in the current direction. Wrap and try one more time.
|
||||
//
|
||||
String wrapMessage;
|
||||
|
@ -177,9 +178,9 @@ public class FindDialog extends DialogComponentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// At this point, we wrapped our search and did *not* find a match. This can only
|
||||
// happen if there is no matching text anywhere in the document, as after wrapping
|
||||
//
|
||||
// At this point, we wrapped our search and did *not* find a match. This can only
|
||||
// happen if there is no matching text anywhere in the document, as after wrapping
|
||||
// will will again find the previous match, if it exists.
|
||||
//
|
||||
notifyUser("Not found");
|
||||
|
@ -218,9 +219,15 @@ public class FindDialog extends DialogComponentProvider {
|
|||
|
||||
public void setSearchText(String text) {
|
||||
String searchText = text == null ? textField.getText() : text;
|
||||
textField.setText(searchText);
|
||||
textField.setSelectionStart(0);
|
||||
textField.setSelectionEnd(searchText.length());
|
||||
comboBox.setSelectedItem(searchText);
|
||||
}
|
||||
|
||||
public String getSearchText() {
|
||||
return textField.getText();
|
||||
}
|
||||
|
||||
public void setHistory(List<String> history) {
|
||||
history.forEach(comboBox::addToModel);
|
||||
}
|
||||
|
||||
private void storeSearchText(String text) {
|
||||
|
|
|
@ -31,30 +31,32 @@ import docking.widgets.GComponent;
|
|||
/**
|
||||
* GhidraComboBox adds the following features:
|
||||
*
|
||||
* 1) ActionListeners are only invoked when the <Enter> key
|
||||
* is pressed within the text-field of the combo-box.
|
||||
* In normal JComboBox case, the ActionListeners are notified
|
||||
* when an item is selected from the list.
|
||||
* <p>
|
||||
* 1) ActionListeners are only invoked when the <Enter> key is pressed within the text-field
|
||||
* of the combo-box. In normal JComboBox case, the ActionListeners are notified when an item is
|
||||
* selected from the list.
|
||||
*
|
||||
* 2) Adds the auto-completion feature. As a user
|
||||
* types in the field, the combo box suggest the nearest matching
|
||||
* entry in the combo box model.
|
||||
* <p>
|
||||
* 2) Adds the auto-completion feature. As a user types in the field, the combo box suggest the
|
||||
* nearest matching entry in the combo box model.
|
||||
*
|
||||
* <p>
|
||||
* It also fixes the following bug:
|
||||
*
|
||||
* A normal JComboBox has a problem (feature?)
|
||||
* that if you have a dialog with a button
|
||||
* and JComboBox and you edit the comboText field and
|
||||
* then hit the button, the button sometimes does not work.
|
||||
* <p>
|
||||
* A normal JComboBox has a problem (feature?) that if you have a dialog with a button and
|
||||
* JComboBox and you edit the comboText field and then hit the button, the button sometimes does
|
||||
* not work.
|
||||
*
|
||||
* When the combobox loses focus,
|
||||
* and its text has changed, it generates an actionPerformed event as
|
||||
* though the user pressed <Enter> in the combo text field. This
|
||||
* has a bizarre effect if you have added an actionPerformed listener
|
||||
* to the combobox and in your callback you adjust the enablement state
|
||||
* of the button that you pressed (which caused the text field to lose
|
||||
* focus) in that you end up changing the button's internal state(by calling
|
||||
* setEnabled(true or false)) in the middle of the button press.
|
||||
* <p>
|
||||
* When the combobox loses focus, and its text has changed, it generates an actionPerformed event
|
||||
* as though the user pressed <Enter> in the combo text field. This has a bizarre effect if
|
||||
* you have added an actionPerformed listener to the combobox and in your callback you adjust the
|
||||
* enablement state of the button that you pressed (which caused the text field to lose focus) in
|
||||
* that you end up changing the button's internal state(by calling setEnabled(true or false)) in
|
||||
* the middle of the button press.
|
||||
*
|
||||
* @param <E> the item type
|
||||
*/
|
||||
public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
private ArrayList<ActionListener> listeners = new ArrayList<>();
|
||||
|
@ -74,17 +76,16 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
|||
|
||||
/**
|
||||
* Construct a new GhidraComboBox using the given model.
|
||||
* @see javax.swing.JComboBox#JComboBox(ComboBoxModel)
|
||||
* @param model the model
|
||||
*/
|
||||
public GhidraComboBox(ComboBoxModel<E> aModel) {
|
||||
super(aModel);
|
||||
public GhidraComboBox(ComboBoxModel<E> model) {
|
||||
super(model);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new GhidraComboBox and populate a default model
|
||||
* with the given items.
|
||||
* @see javax.swing.JComboBox#JComboBox(Object[])
|
||||
* Construct a new GhidraComboBox and populate a default model with the given items.
|
||||
* @param items the items
|
||||
*/
|
||||
public GhidraComboBox(E[] items) {
|
||||
super(items);
|
||||
|
@ -92,12 +93,11 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct a new GhidraComboBox and populate a default model with
|
||||
* the given Vector of items.
|
||||
* @see javax.swing.JComboBox#JComboBox(Vector)
|
||||
* Construct a new GhidraComboBox and populate a default model with the given items.
|
||||
* @param items the items
|
||||
*/
|
||||
public GhidraComboBox(Vector<E> items) {
|
||||
super(items);
|
||||
public GhidraComboBox(Collection<E> items) {
|
||||
super(new Vector<>(items));
|
||||
init();
|
||||
}
|
||||
|
||||
|
@ -155,16 +155,16 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
|||
}
|
||||
|
||||
/**
|
||||
* HACK ALERT: By default, the JComboBoxUI forwards the <Enter> key actions to the root pane
|
||||
* of the JComboBox's container (which is used primarily by any installed 'default button').
|
||||
* The problem is that the forwarding does not happen always. In the case that the <Enter>
|
||||
* key will trigger a selection in the combo box, the action is NOT forwarded.
|
||||
* HACK ALERT: By default, the JComboBoxUI forwards the <Enter> key actions to the root
|
||||
* pane of the JComboBox's container (which is used primarily by any installed 'default
|
||||
* button'). The problem is that the forwarding does not happen always. In the case that the
|
||||
* <Enter> key will trigger a selection in the combo box, the action is NOT forwarded.
|
||||
* <p>
|
||||
* By default Ghidra disables the forwarding altogether, since most users of
|
||||
* By default Ghidra disables the forwarding altogether, since most users of
|
||||
* {@link GhidraComboBox} will add an action listener to handle <Enter> actions.
|
||||
* <p>
|
||||
* To re-enable the default behavior, set the <code>forwardEnter</code> value to true.
|
||||
*
|
||||
*
|
||||
* @param forwardEnter true to enable default <Enter> key handling.
|
||||
*/
|
||||
public void setEnterKeyForwarding(boolean forwardEnter) {
|
||||
|
@ -211,14 +211,17 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
|||
|
||||
/**
|
||||
* A fix for the following series of events:
|
||||
* -The user selects an item
|
||||
* -The user deletes the text
|
||||
* -setSelectedItem(Object) method is called with the same item
|
||||
* <ol>
|
||||
* <li>The user selects an item</li>
|
||||
* <li>The user deletes the text</li>
|
||||
* <li>setSelectedItem(Object) method is called with the same item</li>
|
||||
* </ol>
|
||||
*
|
||||
* In that above series of steps, the text will still be empty, as the user deleted it *and*
|
||||
* the call to setSelectedItem(Object) had no effect because the base class assumed that the
|
||||
* item is already selected.
|
||||
* item is already selected.
|
||||
*
|
||||
* <p>
|
||||
* This method exists to make sure, in that case, that the text of the field matches the
|
||||
* selected item.
|
||||
*/
|
||||
|
@ -243,9 +246,6 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all entries in the drop down list
|
||||
*/
|
||||
public void clearModel() {
|
||||
DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel();
|
||||
model.removeAllElements();
|
||||
|
@ -325,15 +325,15 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
|||
}
|
||||
|
||||
/**
|
||||
* Custom Document the valid user input on the fly.
|
||||
* Custom Document to perform matching of items as the user types
|
||||
*/
|
||||
public class InterceptedInputDocument extends DefaultStyledDocument {
|
||||
|
||||
private boolean automated = false;
|
||||
|
||||
/**
|
||||
* Called before new user input is inserted into the entry text field. The super
|
||||
* method is called if the input is accepted.
|
||||
* Called before new user input is inserted into the entry text field. The super method is
|
||||
* called if the input is accepted.
|
||||
*/
|
||||
@Override
|
||||
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/* ###
|
||||
* 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 docking.widgets;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class FindDialogTest {
|
||||
|
||||
@Test
|
||||
public void testSetSelectedValueDoesNotTriggerMatch() {
|
||||
FindDialogSearcher searcher = new DummySearcher();
|
||||
FindDialog findDialog = new FindDialog("Title", searcher);
|
||||
findDialog.setHistory(List.of("search1"));
|
||||
|
||||
String searchText = "search"; // a prefix of an existing history entry
|
||||
findDialog.setSearchText(searchText);
|
||||
assertEquals(searchText, AbstractGenericTest.runSwing(() -> findDialog.getSearchText()));
|
||||
}
|
||||
|
||||
private class DummySearcher implements FindDialogSearcher {
|
||||
|
||||
@Override
|
||||
public CursorPosition getCursorPosition() {
|
||||
return new CursorPosition(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCursorPosition(CursorPosition position) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorPosition getStart() {
|
||||
return new CursorPosition(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorPosition getEnd() {
|
||||
return new CursorPosition(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void highlightSearchResults(SearchLocation location) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchLocation search(String text, CursorPosition cursorPosition,
|
||||
boolean searchForward, boolean useRegex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue