Merge branch
'GP-5979_dragonmacher-search-memory-accessibility--SQUASHED' (Closes #8264)
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 18 KiB |
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.features.base.memsearch.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
@ -38,20 +37,30 @@ public class MemoryScanControlPanel extends JPanel {
|
|||
private JButton scanButton;
|
||||
|
||||
MemoryScanControlPanel(MemorySearchProvider provider) {
|
||||
super(new BorderLayout());
|
||||
super();
|
||||
|
||||
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
||||
|
||||
setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
|
||||
add(buildButtonPanel(), BorderLayout.CENTER);
|
||||
|
||||
scanButton = new JButton("Scan Values");
|
||||
scanButton.setMnemonic('V');
|
||||
scanButton.setEnabled(false);
|
||||
scanButton.setToolTipText("Refreshes byte values of current results and eliminates " +
|
||||
"those that don't meet the selected change criteria");
|
||||
|
||||
add(scanButton);
|
||||
add(Box.createHorizontalStrut(20));
|
||||
add(buildButtonPanel());
|
||||
|
||||
HelpService helpService = Help.getHelpService();
|
||||
helpService.registerHelp(this, new HelpLocation(HelpTopics.SEARCH, "Scan_Controls"));
|
||||
add(scanButton, BorderLayout.WEST);
|
||||
|
||||
scanButton.addActionListener(e -> provider.scan(selectedScanner));
|
||||
}
|
||||
|
||||
private JComponent buildButtonPanel() {
|
||||
JPanel panel = new JPanel(new FlowLayout());
|
||||
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
|
||||
ButtonGroup buttonGroup = new ButtonGroup();
|
||||
for (Scanner scanner : Scanner.values()) {
|
||||
GRadioButton button = new GRadioButton(scanner.getName());
|
||||
|
|
|
@ -24,14 +24,17 @@ import java.util.List;
|
|||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.menu.ButtonState;
|
||||
import docking.menu.MultiStateButton;
|
||||
import docking.widgets.PopupWindow;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.list.GComboBoxCellRenderer;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.features.base.memsearch.combiner.Combiner;
|
||||
|
@ -89,9 +92,11 @@ class MemorySearchControlPanel extends JPanel {
|
|||
searchButton = new MultiStateButton<Combiner>(initialSearchButtonStates);
|
||||
searchButton
|
||||
.setStateChangedListener(state -> model.setMatchCombiner(state.getClientData()));
|
||||
searchButton.setMnemonic('S');
|
||||
searchButton.addActionListener(e -> search());
|
||||
panel.add(searchButton, BorderLayout.WEST);
|
||||
selectionCheckbox = new JCheckBox("Selection Only");
|
||||
selectionCheckbox.setMnemonic('O');
|
||||
selectionCheckbox.setSelected(model.isSearchSelectionOnly());
|
||||
selectionCheckbox.setEnabled(model.hasSelection());
|
||||
selectionCheckbox
|
||||
|
@ -132,9 +137,10 @@ class MemorySearchControlPanel extends JPanel {
|
|||
if (!formatComboBox.getSelectedItem().equals(searchFormat)) {
|
||||
formatComboBox.setSelectedItem(searchFormat);
|
||||
}
|
||||
|
||||
selectionCheckbox.setSelected(model.isSearchSelectionOnly());
|
||||
selectionCheckbox.setEnabled(model.hasSelection());
|
||||
searchInputField.setToolTipText(searchFormat.getToolTip());
|
||||
searchInputField.setToolTipText("Search Text: " + searchFormat.getToolTip());
|
||||
|
||||
String text = searchInputField.getText();
|
||||
String convertedText = searchFormat.convertText(text, oldSettings, model.getSettings());
|
||||
|
@ -144,7 +150,31 @@ class MemorySearchControlPanel extends JPanel {
|
|||
}
|
||||
|
||||
private JComponent buildLeftSearchInputPanel() {
|
||||
createSearchInputField();
|
||||
|
||||
JPanel searchInputPanel = createSearchInputPanel();
|
||||
GLabel searchLabel = new GLabel("Search Text:");
|
||||
searchLabel.setDisplayedMnemonic('T');
|
||||
searchLabel.setLabelFor(searchInputField);
|
||||
|
||||
JLabel bytesLabel = new GLabel("Byte Sequence:", SwingConstants.RIGHT);
|
||||
bytesLabel.setToolTipText("The byte sequence that will be searched (if applicable)");
|
||||
JPanel bytesPanel = createBytesPanel();
|
||||
|
||||
JPanel panel = new JPanel(new PairLayout(2, 10));
|
||||
|
||||
// row 1
|
||||
panel.add(searchLabel);
|
||||
panel.add(searchInputPanel);
|
||||
|
||||
// row 2
|
||||
panel.add(bytesLabel);
|
||||
panel.add(bytesPanel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createBytesPanel() {
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
hexSearchSequenceField = new GDLabel();
|
||||
hexSearchSequenceField.setName("Hex Sequence Field");
|
||||
Border outerBorder = BorderFactory.createLoweredBevelBorder();
|
||||
|
@ -152,15 +182,21 @@ class MemorySearchControlPanel extends JPanel {
|
|||
Border border = BorderFactory.createCompoundBorder(outerBorder, innerBorder);
|
||||
hexSearchSequenceField.setBorder(border);
|
||||
|
||||
JPanel panel = new JPanel(new PairLayout(2, 10));
|
||||
panel.add(buildSearchFormatCombo());
|
||||
panel.add(searchInputField);
|
||||
JLabel byteSequenceLabel = new JLabel("Byte Sequence:", SwingConstants.RIGHT);
|
||||
byteSequenceLabel.setToolTipText(
|
||||
"This field shows the byte sequence that will be search (if applicable)");
|
||||
panel.add(hexSearchSequenceField, BorderLayout.CENTER);
|
||||
int spaceWidth = formatComboBox.getPreferredSize().width;
|
||||
panel.add(Box.createHorizontalStrut(spaceWidth), BorderLayout.EAST);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createSearchInputPanel() {
|
||||
|
||||
createSearchInputField();
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(searchInputField, BorderLayout.CENTER);
|
||||
panel.add(buildSearchFormatCombo(), BorderLayout.EAST);
|
||||
|
||||
panel.add(byteSequenceLabel);
|
||||
panel.add(hexSearchSequenceField);
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
@ -181,7 +217,7 @@ class MemorySearchControlPanel extends JPanel {
|
|||
updateCombo();
|
||||
searchInputField.setAutoCompleteEnabled(false); // this interferes with validation
|
||||
searchInputField.setEditable(true);
|
||||
searchInputField.setToolTipText(model.getSearchFormat().getToolTip());
|
||||
searchInputField.setToolTipText("Search Text: " + model.getSearchFormat().getToolTip());
|
||||
searchInputField.setDocument(new RestrictedInputDocument());
|
||||
searchInputField.addActionListener(ev -> search());
|
||||
JTextField searchTextField = searchInputField.getTextField();
|
||||
|
@ -220,11 +256,14 @@ class MemorySearchControlPanel extends JPanel {
|
|||
}
|
||||
|
||||
private JComponent buildSearchFormatCombo() {
|
||||
formatComboBox = new JComboBox<>(SearchFormat.ALL);
|
||||
formatComboBox = new GComboBox<>(SearchFormat.ALL);
|
||||
formatComboBox.setSelectedItem(model.getSearchFormat());
|
||||
formatComboBox.addItemListener(this::formatComboChanged);
|
||||
formatComboBox.setToolTipText("The selected format will determine how to " +
|
||||
"interpret text typed into the input field");
|
||||
formatComboBox.setToolTipText("Search Format: how to interpret search text");
|
||||
Border inside = formatComboBox.getBorder();
|
||||
CompoundBorder paddingBorder =
|
||||
BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0), inside);
|
||||
formatComboBox.setBorder(paddingBorder);
|
||||
|
||||
return formatComboBox;
|
||||
}
|
||||
|
@ -252,7 +291,7 @@ class MemorySearchControlPanel extends JPanel {
|
|||
currentMatcher = byteMatcher;
|
||||
String text = currentMatcher.getDescription();
|
||||
hexSearchSequenceField.setText(text);
|
||||
hexSearchSequenceField.setToolTipText(currentMatcher.getToolTip());
|
||||
hexSearchSequenceField.setToolTipText("Search as hex: " + currentMatcher.getToolTip());
|
||||
updateSearchButton();
|
||||
provider.setByteMatcher(byteMatcher);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import javax.swing.text.*;
|
|||
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.docking.util.LookAndFeelUtils;
|
||||
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
||||
|
@ -57,7 +58,7 @@ class MemorySearchOptionsPanel extends JPanel {
|
|||
super(new BorderLayout());
|
||||
this.model = model;
|
||||
|
||||
// if the look and feel is Nimbus, the spaceing it too big, so we use less spacing
|
||||
// if the look and feel is Nimbus, the spacing it too big, so we use less spacing
|
||||
// between elements.
|
||||
isNimbus = LookAndFeelUtils.isUsingNimbusUI();
|
||||
|
||||
|
@ -93,9 +94,22 @@ class MemorySearchOptionsPanel extends JPanel {
|
|||
JPanel panel = new JPanel(new VerticalLayout(3));
|
||||
panel.setBorder(createBorder("Search Region Filter"));
|
||||
|
||||
boolean accelerator = true;
|
||||
List<SearchRegion> choices = model.getMemoryRegionChoices();
|
||||
for (SearchRegion region : choices) {
|
||||
GCheckBox checkbox = new GCheckBox(region.getName());
|
||||
|
||||
if (accelerator) {
|
||||
// The text for the checkbox is dynamic. If the first letter is taken by a menu,
|
||||
// then the accelerator may not work. At the time of writing, the first letter of
|
||||
// the first option seems not to conflict with the other accelerators in the parent
|
||||
// dialog.
|
||||
String name = region.getName();
|
||||
char c = name.charAt(0);
|
||||
checkbox.setMnemonic(c);
|
||||
accelerator = false;
|
||||
}
|
||||
|
||||
checkbox.setToolTipText(region.getDescription());
|
||||
checkbox.setSelected(model.isSelectedRegion(region));
|
||||
checkbox.addItemListener(e -> model.selectRegion(region, checkbox.isSelected()));
|
||||
|
@ -109,13 +123,15 @@ class MemorySearchOptionsPanel extends JPanel {
|
|||
panel.setBorder(createBorder("Decimal Options"));
|
||||
|
||||
JPanel innerPanel = new JPanel(new PairLayout(5, 5));
|
||||
JLabel label = new JLabel("Size:");
|
||||
GLabel label = new GLabel("Size:");
|
||||
label.setDisplayedMnemonic('z');
|
||||
label.setToolTipText("Size of decimal values in bytes");
|
||||
innerPanel.add(label);
|
||||
|
||||
Integer[] decimalSizes = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 16 };
|
||||
int decimalByteSize = model.getDecimalByteSize();
|
||||
decimalByteSizeCombo = new GComboBox<>(decimalSizes);
|
||||
label.setLabelFor(decimalByteSizeCombo);
|
||||
decimalByteSizeCombo.setSelectedItem(decimalByteSize);
|
||||
decimalByteSizeCombo.addItemListener(this::byteSizeComboChanged);
|
||||
decimalByteSizeCombo.setToolTipText("Size of decimal values in bytes");
|
||||
|
@ -123,6 +139,7 @@ class MemorySearchOptionsPanel extends JPanel {
|
|||
panel.add(innerPanel);
|
||||
|
||||
decimalUnsignedCheckbox = new GCheckBox("Unsigned");
|
||||
decimalUnsignedCheckbox.setMnemonic('U');
|
||||
decimalUnsignedCheckbox.setToolTipText(
|
||||
"Sets whether decimal values should be interpreted as unsigned values");
|
||||
decimalUnsignedCheckbox.addActionListener(
|
||||
|
@ -145,8 +162,11 @@ class MemorySearchOptionsPanel extends JPanel {
|
|||
JPanel panel = new JPanel(new VerticalLayout(5));
|
||||
panel.setBorder(createBorder("Code Type Filter"));
|
||||
GCheckBox instructionsCheckBox = new GCheckBox("Instructions");
|
||||
instructionsCheckBox.setMnemonic('I');
|
||||
GCheckBox definedDataCheckBox = new GCheckBox("Defined Data");
|
||||
definedDataCheckBox.setMnemonic('D');
|
||||
GCheckBox undefinedDataCheckBox = new GCheckBox("Undefined Data");
|
||||
undefinedDataCheckBox.setMnemonic('U');
|
||||
instructionsCheckBox.setToolTipText(
|
||||
"If selected, include matches found in instructions");
|
||||
definedDataCheckBox.setToolTipText(
|
||||
|
@ -185,9 +205,15 @@ class MemorySearchOptionsPanel extends JPanel {
|
|||
alignField.setToolTipText(
|
||||
"Filters out matches whose address is not divisible by the alignment value");
|
||||
|
||||
panel.add(new JLabel("Endianess:"));
|
||||
GLabel endianessLabel = new GLabel("Endianess:");
|
||||
endianessLabel.setLabelFor(endianessCombo);
|
||||
endianessLabel.setDisplayedMnemonic('n');
|
||||
GLabel alignmentLabel = new GLabel("Alignment:");
|
||||
alignmentLabel.setDisplayedMnemonic('A');
|
||||
alignmentLabel.setLabelFor(alignField);
|
||||
panel.add(endianessLabel);
|
||||
panel.add(endianessCombo);
|
||||
panel.add(new JLabel("Alignment:"));
|
||||
panel.add(alignmentLabel);
|
||||
panel.add(alignField);
|
||||
|
||||
return panel;
|
||||
|
@ -215,19 +241,23 @@ class MemorySearchOptionsPanel extends JPanel {
|
|||
charsetCombo.setToolTipText("Character encoding for translating strings to bytes");
|
||||
|
||||
JPanel innerPanel = new JPanel(new PairLayout(5, 5));
|
||||
JLabel label = new JLabel("Encoding:");
|
||||
GLabel label = new GLabel("Encoding:");
|
||||
label.setDisplayedMnemonic('c');
|
||||
label.setLabelFor(charsetCombo);
|
||||
label.setToolTipText("Character encoding for translating strings to bytes");
|
||||
innerPanel.add(label);
|
||||
innerPanel.add(charsetCombo);
|
||||
panel.add(innerPanel);
|
||||
|
||||
caseSensitiveCheckbox = new GCheckBox("Case Sensitive");
|
||||
caseSensitiveCheckbox.setMnemonic('n');
|
||||
caseSensitiveCheckbox.setSelected(model.isCaseSensitive());
|
||||
caseSensitiveCheckbox.setToolTipText("Allows for case sensitive searching.");
|
||||
caseSensitiveCheckbox.addActionListener(
|
||||
e -> model.setCaseSensitive(caseSensitiveCheckbox.isSelected()));
|
||||
|
||||
escapeSequencesCheckbox = new GCheckBox("Escape Sequences");
|
||||
escapeSequencesCheckbox.setMnemonic('c');
|
||||
escapeSequencesCheckbox.setSelected(model.useEscapeSequences());
|
||||
escapeSequencesCheckbox.setToolTipText(
|
||||
"Allows specifying control characters using escape sequences " +
|
||||
|
|
|
@ -698,10 +698,18 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ActionContext createContext(Component sourceComponent, Object contextObject) {
|
||||
protected ActionContext createContext(Component focusedComponent, Object contextObject) {
|
||||
ActionContext context = new NavigatableActionContext(this, navigatable);
|
||||
context.setContextObject(contextObject);
|
||||
context.setSourceComponent(sourceComponent);
|
||||
|
||||
// the 'sourceComponent' will be the focused item if the focus owner is in our provider,
|
||||
// otherwise it will be the main component
|
||||
context.setSourceObject(focusedComponent);
|
||||
|
||||
// we make the source component be the table so that the 'activate filter' action works
|
||||
// from anywhere in this provider
|
||||
GhidraTable table = resultsPanel.getTable();
|
||||
context.setSourceComponent(table);
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|