mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-1826 - Scripting - Created a Script Quick Launch dialog.
This commit is contained in:
parent
b6181be1e4
commit
2a5e6f45b8
24 changed files with 923 additions and 214 deletions
|
@ -26,7 +26,8 @@ public class DefaultDropDownSelectionDataModel<T> implements DropDownTextFieldDa
|
|||
private static final char END_CHAR = '\uffff';
|
||||
|
||||
protected List<T> data;
|
||||
private ObjectStringComparator comparator;
|
||||
|
||||
private Comparator<Object> comparator;
|
||||
private DataToStringConverter<T> searchConverter;
|
||||
private DataToStringConverter<T> descriptionConverter;
|
||||
private ListCellRenderer<T> renderer =
|
||||
|
@ -75,7 +76,7 @@ public class DefaultDropDownSelectionDataModel<T> implements DropDownTextFieldDa
|
|||
public int getIndexOfFirstMatchingEntry(List<T> list, String text) {
|
||||
// The data are sorted such that lower-case is before upper-case and smaller length
|
||||
// matches come before longer matches. If we ever find a case-sensitive exact match,
|
||||
// use that. Otherwise, keep looking for a case-insensitve exact match. The
|
||||
// use that. Otherwise, keep looking for a case-insensitive exact match. The
|
||||
// case-insensitive match is preferred over a non-matching item. Once we get to a
|
||||
// non-matching item, we can quit.
|
||||
int lastPreferredMatchIndex = -1;
|
||||
|
|
|
@ -28,18 +28,18 @@ import ghidra.util.datastruct.WeakDataStructureFactory;
|
|||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
/**
|
||||
* Extension of the {@link DropDownSelectionTextField} that allows multiple items to
|
||||
* be selected.
|
||||
* Extension of the {@link DropDownSelectionTextField} that allows multiple items to be selected.
|
||||
* <p>
|
||||
* Note that multiple selection introduces some display complications that are not an
|
||||
* issue with single selection. Namely:
|
||||
* Note that multiple selection introduces some display complications that are not an issue with
|
||||
* single selection. Namely:
|
||||
* <ul>
|
||||
* <li>how do you display multiple selected items in the preview pane</li>
|
||||
* <li>how do you display those same items in the drop down text field</li>
|
||||
* </ul>
|
||||
* The solution here is to:
|
||||
* <ul>
|
||||
* <li>let the preview panel operate normally; it will simply display the preview text for whatever was last selected</li>
|
||||
* <li>let the preview panel operate normally; it will simply display the preview text for whatever
|
||||
* was last selected</li>
|
||||
* <li>display all selected items in the drop down text field as a comma-delimited list</li>
|
||||
* </ul>
|
||||
*
|
||||
|
@ -74,7 +74,8 @@ public class DropDownMultiSelectionTextField<T> extends DropDownSelectionTextFie
|
|||
@Override
|
||||
public void addDropDownSelectionChoiceListener(DropDownSelectionChoiceListener<T> listener) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Please use the flavor of this method that takes a DropDownMultiSelectionChoiceListener instance.");
|
||||
"Please use the flavor of this method that takes a " +
|
||||
DropDownMultiSelectionChoiceListener.class.getSimpleName() + " instance.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,8 +161,8 @@ public class DropDownMultiSelectionTextField<T> extends DropDownSelectionTextFie
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a string representing all items selected in the pulldown. If multiple
|
||||
* items are selected, they will be comma-delimited.
|
||||
* Returns a string representing all items selected in the drop-down. If multiple items are
|
||||
* selected, they will be comma-delimited.
|
||||
*
|
||||
* @return the comma-delimited selection
|
||||
*/
|
||||
|
@ -197,9 +198,8 @@ public class DropDownMultiSelectionTextField<T> extends DropDownSelectionTextFie
|
|||
}
|
||||
|
||||
/**
|
||||
* Listener for the preview panel which is kicked whenever a selection has been
|
||||
* made in the drop down. This will prompt the preview panel to change what it
|
||||
* displays.
|
||||
* Listener for the preview panel which is kicked whenever a selection has been made in the
|
||||
* drop down. This will prompt the preview panel to change what it displays.
|
||||
*/
|
||||
private class PreviewListener implements ListSelectionListener {
|
||||
|
||||
|
|
|
@ -37,14 +37,12 @@ import ghidra.util.task.SwingUpdateManager;
|
|||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A text field that handles comparing text typed by the user to the list of objects
|
||||
* and then presenting potential matches in a drop down window. The items in this window
|
||||
* cannot be selected.
|
||||
* A text field that handles comparing text typed by the user to the list of objects and then
|
||||
* presenting potential matches in a drop down window. The items in this window cannot be selected.
|
||||
*
|
||||
* <P>This class will fire {@link #fireEditingStopped()} and {@link #fireEditingCancelled()}
|
||||
* events when the user makes a choice by pressing the ENTER key, thus allowing the client
|
||||
* code to use this class similar in fashion to a property editor. This behavior can be
|
||||
* configured to:
|
||||
* <P>This class will fire {@link #fireEditingStopped()} and {@link #fireEditingCancelled()} events
|
||||
* when the user makes a choice by pressing the ENTER key, thus allowing the client code to use
|
||||
* this class similar in fashion to a property editor. This behavior can be configured to:
|
||||
* <UL>
|
||||
* <LI>Not consume the ENTER key press (it consumes by default), allowing the parent container
|
||||
* to process the event (see {@link #setConsumeEnterKeyPress(boolean)}
|
||||
|
@ -53,8 +51,8 @@ import util.CollectionUtils;
|
|||
* </LI>
|
||||
* </UL>
|
||||
*
|
||||
* <p>This class is subclassed to not only have the matching behavior, but to also allow for
|
||||
* user selections.
|
||||
* <p>This class is subclassed to not only have the matching behavior, but to also allow for user
|
||||
* selections.
|
||||
*
|
||||
* @param <T> The type of object that this model manipulates
|
||||
*/
|
||||
|
@ -91,17 +89,16 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
private boolean ignoreEnterKeyPress = false; // do not ignore enter by default
|
||||
private boolean ignoreCaretChanges;
|
||||
|
||||
// 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
|
||||
// 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
|
||||
private String pendingTextUpdate;
|
||||
private SwingUpdateManager updateManager;
|
||||
|
||||
/**
|
||||
* The text that was used to generate the current list of matches. This can be different
|
||||
* than the text of this text field, as the user can move the cursor around, which will
|
||||
* change the list of matches. Also, we can set the value of the text field as the user
|
||||
* arrows through the list, which will change the contents of the text field, but not the
|
||||
* list of matches.
|
||||
* The text that was used to generate the current list of matches. This can be different than
|
||||
* the text of this text field, as the user can move the cursor around, which will change the
|
||||
* list of matches. Also, we can set the value of the text field as the user arrows through
|
||||
* the list, which will change the contents of the text field, but not the list of matches.
|
||||
*/
|
||||
private String currentMatchingText;
|
||||
|
||||
|
@ -121,8 +118,8 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
*
|
||||
* @param dataModel provides element storage and search capabilities to this component.
|
||||
* @param updateMinDelay suggestion list refresh delay, triggered after search results have
|
||||
* changed. Too low a value may cause an inconsistent view as filtering tasks complete; too high
|
||||
* a value delivers an unresponsive user experience.
|
||||
* changed. Too low a value may cause an inconsistent view as filtering tasks complete; too
|
||||
* high a value delivers an unresponsive user experience.
|
||||
*/
|
||||
public DropDownTextField(DropDownTextFieldDataModel<T> dataModel, int updateMinDelay) {
|
||||
super(30);
|
||||
|
@ -151,7 +148,6 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
}
|
||||
|
||||
protected ListSelectionModel createListSelectionModel() {
|
||||
|
||||
return new NoSelectionAllowedListSelectionModel();
|
||||
}
|
||||
|
||||
|
@ -218,8 +214,8 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
}
|
||||
|
||||
// O.K., if we are consuming key presses, then we only want to do so when the selection
|
||||
// window is showing. This will close the selection window and not send the Enter event
|
||||
// up to our parent component.
|
||||
// window is showing. This will close the selection window and not send the Enter event up
|
||||
// to our parent component.
|
||||
boolean listShowing = isMatchingListShowing();
|
||||
if (consumeEnterKeyPress) {
|
||||
if (listShowing) {
|
||||
|
@ -252,12 +248,11 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
|
||||
private void validateChosenItemAgainstText(boolean isListShowing) {
|
||||
//
|
||||
// If the text differs from that of the chosen item, then the implication is the user
|
||||
// has changed the text after the last time an item was chosen and after the drop-down
|
||||
// list was closed (if they haven't changed the text, then it will have been set to
|
||||
// the value of the currently selected item). The user will do this if they
|
||||
// want a new item that is not in the list, but the new item starts with the same
|
||||
// value as something that is in the list (SCR 7659).
|
||||
// If the text differs from that of the chosen item, then the implication is the user has
|
||||
// changed the text after the last time an item was chosen and after the drop-down list was
|
||||
// closed (if they haven't changed the text, then it will have been set to the value of the
|
||||
// currently selected item). The user will do this if they want a new item that is not in
|
||||
// the list, but the new item starts with the same value as something that is in the list.
|
||||
//
|
||||
if (selectedValue == null) {
|
||||
return; // nothing to validate
|
||||
|
@ -408,7 +403,7 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
});
|
||||
|
||||
// adjust the display based upon the list contents
|
||||
if (data.size() == 0) {
|
||||
if (data.isEmpty()) {
|
||||
updateDisplayLocation(false);
|
||||
return;
|
||||
}
|
||||
|
@ -455,28 +450,30 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
|
||||
/**
|
||||
* When true, this field will not pass Enter key press events up to it's parent <b>when the
|
||||
* drop-down selection window is open</b>. However, an Enter key press will still be
|
||||
* "unconsumed" when the drop-down window is not open. When set to false, this
|
||||
* method will always pass the Enter key press up to it's parent.
|
||||
* drop-down selection window is open</b>. However, an Enter key press will still be
|
||||
* "unconsumed" when the drop-down window is not open. When set to false, this method will
|
||||
* always pass the Enter key press up to it's parent.
|
||||
*
|
||||
* <P>The default is true. Clients will set this to false when they wish to respond to an
|
||||
* Enter event. For example, a dialog may want to close itself on an Enter key press, even
|
||||
* when the drop-down selection text field is still open. Contrastingly, when this field
|
||||
* is embedded inside of a larger editor, like a multi-editor field dialog,
|
||||
* the Enter key press should simply
|
||||
* trigger the drop-down window to close and the editing to stop, but should not trigger the
|
||||
* when the drop-down selection text field is still open. Contrastingly, when this field is
|
||||
* embedded inside of a larger editor, like a multi-editor field dialog, the Enter key press
|
||||
* should simply trigger the drop-down window to close and the editing to stop, but should not
|
||||
* trigger the overall dialog to close.
|
||||
* @param consume true to consume
|
||||
*
|
||||
*/
|
||||
public void setConsumeEnterKeyPress(boolean consume) {
|
||||
this.consumeEnterKeyPress = consume;
|
||||
}
|
||||
|
||||
/**
|
||||
* True signals to do nothing when the user presses Enter. The default is to respond
|
||||
* to the Enter key, using any existing selection to set this field's
|
||||
* {@link #getSelectedValue() selected value}.
|
||||
* True signals to do nothing when the user presses Enter. The default is to respond to the
|
||||
* Enter key, using any existing selection to set this field's {@link #getSelectedValue()
|
||||
* selected value}.
|
||||
*
|
||||
* <P>This can be set to true to allow clients to show drop-down matches without allowing
|
||||
* the user to select them, triggering the window to be closed.
|
||||
* <P>This can be set to true to allow clients to show drop-down matches without allowing the
|
||||
* user to select them, triggering the window to be closed.
|
||||
*
|
||||
* @param ignore true to ignore Enter presses; false is the default
|
||||
*/
|
||||
|
@ -505,8 +502,10 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
* the text field.
|
||||
*
|
||||
* <P>Note: the listener is stored in a {@link WeakDataStructureFactory weak data structure},
|
||||
* so you must maintain a reference to the listener you pass in--anonymous
|
||||
* classes or lambdas will not work.
|
||||
* so you must maintain a reference to the listener you pass in--anonymous classes or lambdas
|
||||
* will not work.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void addDropDownSelectionChoiceListener(DropDownSelectionChoiceListener<T> listener) {
|
||||
choiceListeners.add(listener);
|
||||
|
@ -645,12 +644,10 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
}
|
||||
|
||||
/**
|
||||
* This is more complicated that responding to the user mouse click. When clicked, the user
|
||||
* is signalling to use the clicked item. When pressing Enter, they may have been typing
|
||||
* and ignoring the list, so we have to do some validation.
|
||||
* This is more complicated that responding to the user mouse click. When clicked, the user is
|
||||
* signalling to use the clicked item. When pressing Enter, they may have been typing and
|
||||
* ignoring the list, so we have to do some validation.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// we know the cast is safe because we put the items in the list
|
||||
private void setTextFromListOnEnterPress() {
|
||||
Object selectedItem = list.getSelectedValue();
|
||||
if (selectedItem == null) {
|
||||
|
@ -658,15 +655,21 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
}
|
||||
|
||||
String textFieldText = getText();
|
||||
String listItemText = dataModel.getDisplayText((T) selectedItem);
|
||||
if (!StringUtilities.startsWithIgnoreCase(listItemText, textFieldText)) {
|
||||
if (!shouldReplaceTextFieldTextWithSelectedItem(textFieldText, selectedValue)) {
|
||||
// The selected item text does not start with the text in the text field, which
|
||||
// implies the user has added or changed text and the list has not yet been updated.
|
||||
return;
|
||||
}
|
||||
|
||||
setTextFromList();
|
||||
}
|
||||
|
||||
protected boolean shouldReplaceTextFieldTextWithSelectedItem(String textFieldText,
|
||||
T selectedItem) {
|
||||
String listItemText = dataModel.getDisplayText(selectedItem);
|
||||
return StringUtilities.startsWithIgnoreCase(listItemText, textFieldText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user's selection or null if the user has not made a selection.
|
||||
* <p>
|
||||
|
@ -680,12 +683,12 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the current selection on this text field. This will store the provided value
|
||||
* and set the text of the text field to be the name of that value. If the given value
|
||||
* is null, then the text of this field will be cleared.
|
||||
* Sets the current selection on this text field. This will store the provided value and set
|
||||
* the text of the text field to be the name of that value. If the given value is null, then
|
||||
* the text of this field will be cleared.
|
||||
*
|
||||
* @param value The value that is to be the current selection or null to clear the
|
||||
* selected value of this text field.
|
||||
* @param value The value that is to be the current selection or null to clear the selected
|
||||
* value of this text field.
|
||||
*/
|
||||
public void setSelectedValue(T value) {
|
||||
storeSelectedValue(value);
|
||||
|
@ -733,9 +736,9 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
|||
windowVisibilityListener = Objects.requireNonNull(l);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
//=================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
//=================================================================================================
|
||||
|
||||
private class HideWindowFocusListener extends FocusAdapter {
|
||||
@Override
|
||||
|
|
|
@ -41,8 +41,8 @@ public interface DropDownTextFieldDataModel<T> {
|
|||
* data sets that do not allow duplicates, this is simply the index of the item that matches
|
||||
* the text in the list. For items that allow duplicates, the is the index of the first match.
|
||||
*
|
||||
* @param data the list to search
|
||||
* @param text the text to match against the items in the list
|
||||
* @param data the list to search.
|
||||
* @param text the text to match against the items in the list.
|
||||
* @return the index in the given list of the first item that matches the given text.
|
||||
*/
|
||||
public int getIndexOfFirstMatchingEntry(List<T> data, String text);
|
||||
|
@ -50,18 +50,23 @@ public interface DropDownTextFieldDataModel<T> {
|
|||
/**
|
||||
* Returns the renderer to be used to paint the contents of the list returned by
|
||||
* {@link #getMatchingData(String)}.
|
||||
* @return the renderer.
|
||||
*/
|
||||
public ListCellRenderer<T> getListRenderer();
|
||||
|
||||
/**
|
||||
* Returns a description for this item that gives that will be displayed along side of the
|
||||
* {@link DropDownSelectionTextField}'s matching window.
|
||||
* @param value the value.
|
||||
* @return the description.
|
||||
*/
|
||||
public String getDescription(T value);
|
||||
|
||||
/**
|
||||
* Returns the text for the given item that will be entered into the
|
||||
* {@link DropDownSelectionTextField} when the user makes a selection.
|
||||
* @param value the value.
|
||||
* @return the description.
|
||||
*/
|
||||
public String getDisplayText(T value);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,15 @@
|
|||
*/
|
||||
package ghidra.util.datastruct;
|
||||
|
||||
import generic.concurrent.ConcurrentListenerSet;
|
||||
|
||||
/**
|
||||
* Factory for creating containers to use in various threading environments
|
||||
*
|
||||
* Other non-weak listeners:
|
||||
* <ul>
|
||||
* <li>{@link ConcurrentListenerSet}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class WeakDataStructureFactory {
|
||||
|
||||
|
@ -26,7 +33,7 @@ public class WeakDataStructureFactory {
|
|||
* @return a new WeakSet
|
||||
*/
|
||||
public static <T> WeakSet<T> createSingleThreadAccessWeakSet() {
|
||||
return new ThreadUnsafeWeakSet<T>();
|
||||
return new ThreadUnsafeWeakSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +43,7 @@ public class WeakDataStructureFactory {
|
|||
* @see CopyOnReadWeakSet
|
||||
*/
|
||||
public static <T> WeakSet<T> createCopyOnReadWeakSet() {
|
||||
return new CopyOnReadWeakSet<T>();
|
||||
return new CopyOnReadWeakSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,6 +53,6 @@ public class WeakDataStructureFactory {
|
|||
* @see CopyOnWriteWeakSet
|
||||
*/
|
||||
public static <T> WeakSet<T> createCopyOnWriteWeakSet() {
|
||||
return new CopyOnWriteWeakSet<T>();
|
||||
return new CopyOnWriteWeakSet<>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -378,11 +378,11 @@ public class UserSearchUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Escapes all regex characters with the '\' character, except for those in the given
|
||||
* exclusion array.
|
||||
* Escapes all regex characters with the '\' character, except for those in the given exclusion
|
||||
* array.
|
||||
*
|
||||
* @param input
|
||||
* The input string to be escaped
|
||||
* @param input The input string to be escaped
|
||||
* @param doNotEscape characters that should not be escaped
|
||||
* @return A new regex string with special characters escaped.
|
||||
*/
|
||||
// note: 'package' for testing
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue