mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch 'origin/GP-1-dragonmacher-escape-confirmation'
This commit is contained in:
commit
a810c384f4
11 changed files with 227 additions and 64 deletions
|
@ -65,7 +65,6 @@ public class CreateInternalStructureAction extends CompositeEditorTableAction {
|
||||||
createStructure();
|
createStructure();
|
||||||
}
|
}
|
||||||
|
|
||||||
requestTableFocus();
|
|
||||||
Swing.runLater(() -> {
|
Swing.runLater(() -> {
|
||||||
provider.toFront();
|
provider.toFront();
|
||||||
provider.requestFocus();
|
provider.requestFocus();
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -32,7 +32,7 @@ public class AllHistoryAction extends ListingContextAction {
|
||||||
super("Show All History", owner);
|
super("Show All History", owner);
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
setMenuBarData(new MenuData(new String[] { ToolConstants.MENU_SEARCH, "Label History..." },
|
setMenuBarData(new MenuData(new String[] { ToolConstants.MENU_SEARCH, "Label History..." },
|
||||||
null, "Search"));
|
null, "search 1"));
|
||||||
|
|
||||||
setKeyBindingData(new KeyBindingData(KeyEvent.VK_H, 0));
|
setKeyBindingData(new KeyBindingData(KeyEvent.VK_H, 0));
|
||||||
addToWindowWhen(ListingActionContext.class);
|
addToWindowWhen(ListingActionContext.class);
|
||||||
|
|
|
@ -43,6 +43,10 @@ public enum Combiner {
|
||||||
this.function = function;
|
this.function = function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMerge() {
|
||||||
|
return this != REPLACE;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,8 +249,9 @@ public class MemoryMatchTableModel extends AddressBasedTableModel<MemoryMatch> {
|
||||||
byteString = HTMLUtilities.colorString(color, byteString);
|
byteString = HTMLUtilities.colorString(color, byteString);
|
||||||
}
|
}
|
||||||
b.append(byteString);
|
b.append(byteString);
|
||||||
if (i == max)
|
if (i == max) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
b.append(" ");
|
b.append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -274,6 +274,10 @@ class MemorySearchControlPanel extends JPanel {
|
||||||
searchButton.setSelectedStateByClientData(combiner);
|
searchButton.setSelectedStateByClientData(combiner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSearchCombiner(Combiner combiner) {
|
||||||
|
searchButton.setSelectedStateByClientData(combiner);
|
||||||
|
}
|
||||||
|
|
||||||
private void adjustLocationForCaretPosition(Point location) {
|
private void adjustLocationForCaretPosition(Point location) {
|
||||||
JTextField textField = searchInputField.getTextField();
|
JTextField textField = searchInputField.getTextField();
|
||||||
Caret caret = textField.getCaret();
|
Caret caret = textField.getCaret();
|
||||||
|
@ -460,5 +464,4 @@ class MemorySearchControlPanel extends JPanel {
|
||||||
Component getDefaultFocusComponent() {
|
Component getDefaultFocusComponent() {
|
||||||
return searchInputField;
|
return searchInputField;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,7 @@ public class MemorySearchPlugin extends Plugin implements MemorySearchService {
|
||||||
saveState.putBoolean(SHOW_OPTIONS_PANEL, showOptionsPanel);
|
saveState.putBoolean(SHOW_OPTIONS_PANEL, showOptionsPanel);
|
||||||
saveState.putBoolean(SHOW_SCAN_PANEL, showOptionsPanel);
|
saveState.putBoolean(SHOW_SCAN_PANEL, showOptionsPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// MemorySearchService methods
|
// MemorySearchService methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -205,10 +206,9 @@ public class MemorySearchPlugin extends Plugin implements MemorySearchService {
|
||||||
Msg.showWarn(this, null, "Search Failed!", "No valid start address!");
|
Msg.showWarn(this, null, "Search Failed!", "No valid start address!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemorySearcher searcher = new MemorySearcher(source, lastByteMatcher, addresses, 1);
|
MemorySearcher searcher = new MemorySearcher(source, lastByteMatcher, addresses, 1);
|
||||||
|
|
||||||
MemoryMatch match = searcher.findOnce(start, forward, monitor);
|
MemoryMatch match = searcher.findOnce(start, forward, monitor);
|
||||||
|
|
||||||
Swing.runLater(() -> navigateToMatch(match));
|
Swing.runLater(() -> navigateToMatch(match));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ import docking.action.ToggleDockingAction;
|
||||||
import docking.action.builder.ActionBuilder;
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.action.builder.ToggleActionBuilder;
|
import docking.action.builder.ToggleActionBuilder;
|
||||||
import docking.util.GGlassPaneMessage;
|
import docking.util.GGlassPaneMessage;
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
|
import docking.widgets.OptionDialogBuilder;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
import ghidra.app.context.NavigatableActionContext;
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
|
@ -38,6 +40,7 @@ import ghidra.app.nav.NavigatableRemovalListener;
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
|
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
|
||||||
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
||||||
|
import ghidra.features.base.memsearch.combiner.Combiner;
|
||||||
import ghidra.features.base.memsearch.matcher.ByteMatcher;
|
import ghidra.features.base.memsearch.matcher.ByteMatcher;
|
||||||
import ghidra.features.base.memsearch.scan.Scanner;
|
import ghidra.features.base.memsearch.scan.Scanner;
|
||||||
import ghidra.features.base.memsearch.searcher.*;
|
import ghidra.features.base.memsearch.searcher.*;
|
||||||
|
@ -145,7 +148,6 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||||
navigatable.addNavigatableListener(this);
|
navigatable.addNavigatableListener(this);
|
||||||
program.addCloseListener(this);
|
program.addCloseListener(this);
|
||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSearchInput(String input) {
|
public void setSearchInput(String input) {
|
||||||
|
@ -539,9 +541,43 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
super.actionPerformed(context);
|
super.actionPerformed(context);
|
||||||
updateSubTitle();
|
updateSubTitle();
|
||||||
|
resultsPanel.itemDeleted();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeComponent() {
|
||||||
|
doClose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doClose(boolean force) {
|
||||||
|
if (force) {
|
||||||
|
super.closeComponent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canClose()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.closeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canClose() {
|
||||||
|
boolean hasUserChanges = resultsPanel.hasUserChanges();
|
||||||
|
if (!hasUserChanges) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = "Close dialog and lost custom search results?";
|
||||||
|
OptionDialogBuilder builder = new OptionDialogBuilder("Close Results Window?", message);
|
||||||
|
int choice = builder.addOption("Yes")
|
||||||
|
.addCancel()
|
||||||
|
.setDefaultButton("Yes")
|
||||||
|
.setMessageType(OptionDialog.QUESTION_MESSAGE)
|
||||||
|
.show(resultsPanel);
|
||||||
|
|
||||||
|
return choice == OptionDialog.OPTION_ONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -551,6 +587,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispose() {
|
private void dispose() {
|
||||||
|
|
||||||
if (glassPaneMessage != null) {
|
if (glassPaneMessage != null) {
|
||||||
glassPaneMessage.hide();
|
glassPaneMessage.hide();
|
||||||
glassPaneMessage = null;
|
glassPaneMessage = null;
|
||||||
|
@ -583,12 +620,12 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void navigatableRemoved(Navigatable nav) {
|
public void navigatableRemoved(Navigatable nav) {
|
||||||
closeComponent();
|
doClose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void domainObjectClosed(DomainObject dobj) {
|
public void domainObjectClosed(DomainObject dobj) {
|
||||||
closeComponent();
|
doClose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Navigatable getNavigatable() {
|
Navigatable getNavigatable() {
|
||||||
|
@ -638,12 +675,20 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||||
return resultsPanel.getTableModel().getModelData();
|
return resultsPanel.getTableModel().getModelData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemorySearchResultsPanel getResultsPanel() {
|
||||||
|
return resultsPanel;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSettings(SearchSettings settings) {
|
public void setSettings(SearchSettings settings) {
|
||||||
String converted = searchPanel.convertInput(model.getSettings(), settings);
|
String converted = searchPanel.convertInput(model.getSettings(), settings);
|
||||||
model.setSettings(settings);
|
model.setSettings(settings);
|
||||||
searchPanel.setSearchInput(converted);
|
searchPanel.setSearchInput(converted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSearchCombiner(Combiner combiner) {
|
||||||
|
searchPanel.setSearchCombiner(combiner);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSearchSelection() {
|
public boolean isSearchSelection() {
|
||||||
return model.isSearchSelectionOnly();
|
return model.isSearchSelectionOnly();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ import ghidra.util.task.*;
|
||||||
* in a table. This panel also includes most of the search logic as it has direct access to the
|
* in a table. This panel also includes most of the search logic as it has direct access to the
|
||||||
* table for showing the results.
|
* table for showing the results.
|
||||||
*/
|
*/
|
||||||
class MemorySearchResultsPanel extends JPanel {
|
public class MemorySearchResultsPanel extends JPanel {
|
||||||
private GhidraThreadedTablePanel<MemoryMatch> threadedTablePanel;
|
private GhidraThreadedTablePanel<MemoryMatch> threadedTablePanel;
|
||||||
private GhidraTableFilterPanel<MemoryMatch> tableFilterPanel;
|
private GhidraTableFilterPanel<MemoryMatch> tableFilterPanel;
|
||||||
private GhidraTable table;
|
private GhidraTable table;
|
||||||
|
@ -49,6 +49,9 @@ class MemorySearchResultsPanel extends JPanel {
|
||||||
private MemorySearchProvider provider;
|
private MemorySearchProvider provider;
|
||||||
private SearchMarkers markers;
|
private SearchMarkers markers;
|
||||||
|
|
||||||
|
private boolean hasDeleted;
|
||||||
|
private boolean hasCombined;
|
||||||
|
|
||||||
MemorySearchResultsPanel(MemorySearchProvider provider, SearchMarkers markers) {
|
MemorySearchResultsPanel(MemorySearchProvider provider, SearchMarkers markers) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
|
@ -73,6 +76,14 @@ class MemorySearchResultsPanel extends JPanel {
|
||||||
markers.loadMarkers(provider.getTitle(), tableModel.getModelData());
|
markers.loadMarkers(provider.getTitle(), tableModel.getModelData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void itemDeleted() {
|
||||||
|
hasDeleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasUserChanges() {
|
||||||
|
return hasDeleted || hasCombined;
|
||||||
|
}
|
||||||
|
|
||||||
void providerActivated() {
|
void providerActivated() {
|
||||||
markers.makeActiveMarkerSet();
|
markers.makeActiveMarkerSet();
|
||||||
}
|
}
|
||||||
|
@ -109,7 +120,13 @@ class MemorySearchResultsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MemoryMatchTableLoader createLoader(MemorySearcher searcher, Combiner combiner) {
|
private MemoryMatchTableLoader createLoader(MemorySearcher searcher, Combiner combiner) {
|
||||||
if (hasResults()) {
|
if (!hasResults()) {
|
||||||
|
hasDeleted = false;
|
||||||
|
return new NewSearchTableLoader(searcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have existing results. Will they be merged?
|
||||||
|
if (combiner.isMerge()) {
|
||||||
|
|
||||||
// If we have existing results, the combiner determines how the new search results get
|
// If we have existing results, the combiner determines how the new search results get
|
||||||
// combined with the existing results.
|
// combined with the existing results.
|
||||||
|
@ -118,11 +135,14 @@ class MemorySearchResultsPanel extends JPanel {
|
||||||
// and only the new results are kept. In this case, it is preferred to use the same
|
// and only the new results are kept. In this case, it is preferred to use the same
|
||||||
// loader as if doing an initial search because you get incremental loading and also
|
// loader as if doing an initial search because you get incremental loading and also
|
||||||
// don't need to copy the existing results to feed to a combiner.
|
// don't need to copy the existing results to feed to a combiner.
|
||||||
if (combiner != Combiner.REPLACE) {
|
hasCombined = true;
|
||||||
List<MemoryMatch> previousResults = tableModel.getModelData();
|
List<MemoryMatch> previousResults = tableModel.getModelData();
|
||||||
return new CombinedMatchTableLoader(searcher, previousResults, combiner);
|
return new CombinedMatchTableLoader(searcher, previousResults, combiner);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have results, but we are going to replace them. A new load of data means any previous
|
||||||
|
// manual deletes are now irrelevant
|
||||||
|
hasDeleted = false;
|
||||||
return new NewSearchTableLoader(searcher);
|
return new NewSearchTableLoader(searcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +166,7 @@ class MemorySearchResultsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GhidraTable getTable() {
|
public GhidraTable getTable() {
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,17 @@ import java.util.List;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.fieldpanel.support.Highlight;
|
import docking.widgets.fieldpanel.support.Highlight;
|
||||||
|
import docking.widgets.table.GTable;
|
||||||
import ghidra.GhidraOptions;
|
import ghidra.GhidraOptions;
|
||||||
import ghidra.app.services.MarkerSet;
|
import ghidra.app.services.MarkerSet;
|
||||||
import ghidra.app.util.viewer.field.BytesFieldFactory;
|
import ghidra.app.util.viewer.field.BytesFieldFactory;
|
||||||
import ghidra.features.base.memsearch.bytesource.ProgramSearchRegion;
|
import ghidra.features.base.memsearch.bytesource.ProgramSearchRegion;
|
||||||
|
import ghidra.features.base.memsearch.combiner.Combiner;
|
||||||
import ghidra.features.base.memsearch.format.SearchFormat;
|
import ghidra.features.base.memsearch.format.SearchFormat;
|
||||||
|
import ghidra.features.base.memsearch.gui.MemorySearchResultsPanel;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -259,15 +264,13 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHexSearchAll2() throws Exception {
|
public void testHexSearchAll2() throws Exception {
|
||||||
// enter search string for multiple byte match
|
|
||||||
|
|
||||||
|
// enter search string for multiple byte match
|
||||||
setInput("ff 15");
|
setInput("ff 15");
|
||||||
performSearchAll();
|
performSearchAll();
|
||||||
|
|
||||||
waitForSearch(5);
|
waitForSearch(5);
|
||||||
|
|
||||||
List<Address> addrs = addrs(0x01002d1f, 0x01002d41, 0x01002d4a, 0x01002d5e, 0x010029bd);
|
List<Address> addrs = addrs(0x01002d1f, 0x01002d41, 0x01002d4a, 0x01002d5e, 0x010029bd);
|
||||||
|
|
||||||
checkMarkerSet(addrs);
|
checkMarkerSet(addrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,4 +555,113 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
||||||
|
|
||||||
assertEquals(addr(0x01002d0b), currentAddress());
|
assertEquals(addr(0x01002d0b), currentAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPromptToClose_NoChanges() {
|
||||||
|
|
||||||
|
search("ff 15", 5);
|
||||||
|
|
||||||
|
triggerEscape(searchProvider.getComponent());
|
||||||
|
assertProviderClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPromptToClose_DeletedRows() {
|
||||||
|
|
||||||
|
search("ff 15", 5);
|
||||||
|
|
||||||
|
deleteRow(0);
|
||||||
|
|
||||||
|
triggerEscape(searchProvider.getComponent());
|
||||||
|
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||||
|
pressButtonByText(dialog, "Yes");
|
||||||
|
assertProviderClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPromptToClose_DeletedRows_Cancel() {
|
||||||
|
|
||||||
|
search("ff 15", 5);
|
||||||
|
|
||||||
|
deleteRow(0);
|
||||||
|
|
||||||
|
triggerEscape(searchProvider.getComponent());
|
||||||
|
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||||
|
pressButtonByText(dialog, "No");
|
||||||
|
assertProviderVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPromptToClose_MergedData() {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Search then perform a new search that will be combined with the initial search results.
|
||||||
|
// This new merged search will trigger the requirement to prompt the user before closing,
|
||||||
|
// since the new set of data is non-trivial to create.
|
||||||
|
//
|
||||||
|
|
||||||
|
search("ff 15", 5);
|
||||||
|
|
||||||
|
// perform a new search and use the existing results
|
||||||
|
runSwing(() -> searchProvider.setSearchCombiner(Combiner.UNION));
|
||||||
|
search("8b f?", 9);
|
||||||
|
|
||||||
|
triggerEscape(searchProvider.getComponent());
|
||||||
|
OptionDialog confirmDialog = waitForDialogComponent(OptionDialog.class);
|
||||||
|
pressButtonByText(confirmDialog, "Yes");
|
||||||
|
assertProviderClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPromptToClose_DeletedRows_NewSearch() {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Search. Delete a row. This would trigger a prompt when closing the dialog. Perform a
|
||||||
|
// new search. This new non-merged search will clear the requirement to prompt the user
|
||||||
|
// before closing.
|
||||||
|
//
|
||||||
|
|
||||||
|
search("ff 15", 5);
|
||||||
|
|
||||||
|
deleteRow(0);
|
||||||
|
|
||||||
|
search("8b f?", 4);
|
||||||
|
|
||||||
|
triggerEscape(searchProvider.getComponent());
|
||||||
|
assertProviderClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertProviderVisible() {
|
||||||
|
assertTrue(runSwing(() -> searchProvider.isVisible()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertProviderClosed() {
|
||||||
|
assertFalse(runSwing(() -> searchProvider.isVisible()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void search(String input, int expectedMatchCount) {
|
||||||
|
setInput(input);
|
||||||
|
performSearchAll();
|
||||||
|
waitForSearch(expectedMatchCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteRow(int row) {
|
||||||
|
|
||||||
|
int resultCount = getResultCount();
|
||||||
|
runSwing(() -> {
|
||||||
|
|
||||||
|
MemorySearchResultsPanel panel = searchProvider.getResultsPanel();
|
||||||
|
GTable table = panel.getTable();
|
||||||
|
table.selectRow(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
DockingActionIf removeAction = getAction(memorySearchPlugin, "Remove Items");
|
||||||
|
performAction(removeAction);
|
||||||
|
assertEquals(resultCount - 1, getResultCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getResultCount() {
|
||||||
|
return runSwing(() -> searchProvider.getSearchResults().size());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,9 @@ import java.awt.Window;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.marker.MarkerManagerPlugin;
|
import ghidra.app.plugin.core.marker.MarkerManagerPlugin;
|
||||||
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
||||||
import ghidra.app.services.ProgramManager;
|
|
||||||
import ghidra.features.base.memsearch.gui.MemorySearchPlugin;
|
import ghidra.features.base.memsearch.gui.MemorySearchPlugin;
|
||||||
import ghidra.features.base.memsearch.gui.MemorySearchProvider;
|
import ghidra.features.base.memsearch.gui.MemorySearchProvider;
|
||||||
import ghidra.features.base.memsearch.mnemonic.MnemonicSearchPlugin;
|
import ghidra.features.base.memsearch.mnemonic.MnemonicSearchPlugin;
|
||||||
|
@ -35,7 +33,6 @@ import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramSelection;
|
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
|
@ -45,9 +42,9 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private ProgramDB program;
|
private ProgramDB program;
|
||||||
private MnemonicSearchPlugin plugin;
|
private MnemonicSearchPlugin plugin;
|
||||||
private DockingActionIf searchMnemonicOperandsNoConstAction;
|
private DockingActionIf includeOperandsExcludeConstAction;
|
||||||
private DockingActionIf searchMnemonicNoOperandsNoConstAction;
|
private DockingActionIf excludeOperandsAction;
|
||||||
private DockingActionIf searchMnemonicOperandsConstAction;
|
private DockingActionIf includeOperandsAction;
|
||||||
private CodeBrowserPlugin cb;
|
private CodeBrowserPlugin cb;
|
||||||
private MemorySearchProvider searchProvider;
|
private MemorySearchProvider searchProvider;
|
||||||
|
|
||||||
|
@ -63,16 +60,12 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
plugin = env.getPlugin(MnemonicSearchPlugin.class);
|
plugin = env.getPlugin(MnemonicSearchPlugin.class);
|
||||||
cb = env.getPlugin(CodeBrowserPlugin.class);
|
cb = env.getPlugin(CodeBrowserPlugin.class);
|
||||||
program = (ProgramDB) buildProgram();
|
program = (ProgramDB) buildProgram();
|
||||||
|
env.showTool(program);
|
||||||
|
|
||||||
ProgramManager pm = tool.getService(ProgramManager.class);
|
excludeOperandsAction = getAction(plugin, "Exclude Operands");
|
||||||
pm.openProgram(program.getDomainFile());
|
includeOperandsAction = getAction(plugin, "Include Operands");
|
||||||
|
includeOperandsExcludeConstAction =
|
||||||
searchMnemonicOperandsNoConstAction =
|
|
||||||
getAction(plugin, "Include Operands (except constants)");
|
getAction(plugin, "Include Operands (except constants)");
|
||||||
searchMnemonicNoOperandsNoConstAction = getAction(plugin, "Exclude Operands");
|
|
||||||
searchMnemonicOperandsConstAction = getAction(plugin, "Include Operands");
|
|
||||||
|
|
||||||
env.showTool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Program buildProgram() throws Exception {
|
private Program buildProgram() throws Exception {
|
||||||
|
@ -93,29 +86,19 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchMnemonicOperandsNoConst() {
|
public void testSearchMnemonicOperandsNoConst() {
|
||||||
ProgramSelection sel = new ProgramSelection(addr(0x01004062), addr(0x0100406a));
|
makeSelection(tool, program, addr(0x01004062), addr(0x0100406a));
|
||||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("Test", sel, program));
|
performAction(includeOperandsExcludeConstAction, cb.getProvider(), true);
|
||||||
|
|
||||||
performAction(searchMnemonicOperandsNoConstAction, cb.getProvider(), true);
|
|
||||||
searchProvider = waitForComponentProvider(MemorySearchProvider.class);
|
searchProvider = waitForComponentProvider(MemorySearchProvider.class);
|
||||||
|
|
||||||
assertNotNull(searchProvider);
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"01010101 10001011 11101100 10000001 11101100 ........ ........ ........ ........",
|
"01010101 10001011 11101100 10000001 11101100 ........ ........ ........ ........",
|
||||||
getInput());
|
getInput());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchMnemonicNoOperandsNoConst() {
|
public void testSearchMnemonicNoOperandsNoConst() {
|
||||||
ProgramSelection sel = new ProgramSelection(addr(0x01004062), addr(0x0100406a));
|
makeSelection(tool, program, addr(0x01004062), addr(0x0100406a));
|
||||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("Test", sel, program));
|
performAction(excludeOperandsAction, cb.getProvider(), true);
|
||||||
|
|
||||||
performAction(searchMnemonicNoOperandsNoConstAction, cb.getProvider(), true);
|
|
||||||
|
|
||||||
searchProvider = waitForComponentProvider(MemorySearchProvider.class);
|
searchProvider = waitForComponentProvider(MemorySearchProvider.class);
|
||||||
assertNotNull(searchProvider);
|
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"01010... 10001011 11...... 10000001 11101... ........ ........ ........ ........",
|
"01010... 10001011 11...... 10000001 11101... ........ ........ ........ ........",
|
||||||
getInput());
|
getInput());
|
||||||
|
@ -124,16 +107,10 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchMnemonicOperandsConst() {
|
public void testSearchMnemonicOperandsConst() {
|
||||||
ProgramSelection sel = new ProgramSelection(addr(0x01004062), addr(0x0100406a));
|
|
||||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("Test", sel, program));
|
|
||||||
|
|
||||||
performAction(searchMnemonicOperandsConstAction, cb.getProvider(), true);
|
|
||||||
|
|
||||||
performAction(searchMnemonicOperandsConstAction, cb.getProvider(), true);
|
|
||||||
|
|
||||||
|
makeSelection(tool, program, addr(0x01004062), addr(0x0100406a));
|
||||||
|
performAction(includeOperandsAction, cb.getProvider(), true);
|
||||||
searchProvider = waitForComponentProvider(MemorySearchProvider.class);
|
searchProvider = waitForComponentProvider(MemorySearchProvider.class);
|
||||||
assertNotNull(searchProvider);
|
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"01010101 10001011 11101100 10000001 11101100 00000100 00000001 00000000 00000000",
|
"01010101 10001011 11101100 10000001 11101100 00000100 00000001 00000000 00000000",
|
||||||
getInput());
|
getInput());
|
||||||
|
@ -142,7 +119,6 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
/**
|
/**
|
||||||
* Tests that when multiple regions are selected, the user is notified via
|
* Tests that when multiple regions are selected, the user is notified via
|
||||||
* pop-up that this is not acceptable.
|
* pop-up that this is not acceptable.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleSelection() {
|
public void testMultipleSelection() {
|
||||||
|
@ -158,7 +134,7 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
makeSelection(tool, program, addrSet);
|
makeSelection(tool, program, addrSet);
|
||||||
|
|
||||||
// Now invoke the menu option we want to test.
|
// Now invoke the menu option we want to test.
|
||||||
performAction(searchMnemonicOperandsConstAction, cb.getProvider(), false);
|
performAction(includeOperandsAction, cb.getProvider(), false);
|
||||||
|
|
||||||
// Here's the main assert: If the code recognizes that we have multiple selection, the
|
// Here's the main assert: If the code recognizes that we have multiple selection, the
|
||||||
// MemSearchDialog will NOT be displayed (an error message pops up instead). So verify that
|
// MemSearchDialog will NOT be displayed (an error message pops up instead). So verify that
|
||||||
|
@ -172,7 +148,7 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
return program.getMinAddress().getNewAddress(offset);
|
return program.getMinAddress().getNewAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getInput() {
|
private String getInput() {
|
||||||
return Swing.runNow(() -> searchProvider.getSearchInput());
|
return Swing.runNow(() -> searchProvider.getSearchInput());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -76,8 +76,11 @@ public class ProjectDataDeleteAction extends FrontendProjectTreeAction {
|
||||||
|
|
||||||
String message = getMessage(fileCount, files);
|
String message = getMessage(fileCount, files);
|
||||||
OptionDialogBuilder builder = new OptionDialogBuilder("Confirm Delete", message);
|
OptionDialogBuilder builder = new OptionDialogBuilder("Confirm Delete", message);
|
||||||
builder.addOption("OK").addCancel().setMessageType(OptionDialog.QUESTION_MESSAGE);
|
int choice = builder.addOption("OK")
|
||||||
return builder.show(parent) != OptionDialog.CANCEL_OPTION;
|
.addCancel()
|
||||||
|
.setMessageType(OptionDialog.QUESTION_MESSAGE)
|
||||||
|
.show(parent);
|
||||||
|
return choice != OptionDialog.CANCEL_OPTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMessage(int fileCount, Set<DomainFile> selectedFiles) {
|
private String getMessage(int fileCount, Set<DomainFile> selectedFiles) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue