mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-12-01 08:31:52 +00:00
GP-1188 fixed analysis panel to not use 'last used' analysis profile when accessed via program options versus analysis action. Also added check if you made changes and then change the combo
This commit is contained in:
parent
f839d34fa9
commit
b34bfecf6c
@ -56,7 +56,7 @@ public class AnalysisOptionsDialog extends DialogComponentProvider
|
||||
super("Analysis Options");
|
||||
setHelpLocation(new HelpLocation("AutoAnalysisPlugin", "AnalysisOptions"));
|
||||
panel = new AnalysisPanel(programs, editorStateFactory, this);
|
||||
|
||||
panel.setToLastUsedAnalysisOptionsIfProgramNotAnalyzed();
|
||||
addWorkPanel(panel);
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.analysis;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.beans.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -86,9 +87,15 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
private EditorStateFactory editorStateFactory;
|
||||
|
||||
private JPanel noOptionsPanel;
|
||||
private GhidraComboBox<Options> defaultOptionsCombo;
|
||||
private GhidraComboBox<Options> optionsComboBox;
|
||||
private JButton deleteButton;
|
||||
|
||||
private Options[] optionConfigurationChoices;
|
||||
|
||||
private ItemListener optionsComboBoxListener = this::optionsComboBoxChanged;
|
||||
|
||||
private FileOptions currentNonDefaults;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -207,14 +214,14 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
private Component buildOptionsComboBoxPanel() {
|
||||
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
||||
|
||||
Options[] defaultOptionsArray = getDefaultOptionsArray();
|
||||
defaultOptionsCombo = new GhidraComboBox<>(defaultOptionsArray);
|
||||
selectedOptions = findOptions(defaultOptionsArray, getLastUsedDefaultOptionsName());
|
||||
defaultOptionsCombo.setSelectedItem(selectedOptions);
|
||||
defaultOptionsCombo.addItemListener(this::analysisComboChanged);
|
||||
Dimension preferredSize = defaultOptionsCombo.getPreferredSize();
|
||||
defaultOptionsCombo.setPreferredSize(new Dimension(200, preferredSize.height));
|
||||
panel.add(defaultOptionsCombo);
|
||||
optionConfigurationChoices = loadPossibleOptionsChoicesForComboBox();
|
||||
optionsComboBox = new GhidraComboBox<>(optionConfigurationChoices);
|
||||
selectedOptions = currentProgramOptions;
|
||||
optionsComboBox.setSelectedItem(selectedOptions);
|
||||
optionsComboBox.addItemListener(optionsComboBoxListener);
|
||||
Dimension preferredSize = optionsComboBox.getPreferredSize();
|
||||
optionsComboBox.setPreferredSize(new Dimension(200, preferredSize.height));
|
||||
panel.add(optionsComboBox);
|
||||
|
||||
deleteButton = new JButton("Delete");
|
||||
deleteButton.addActionListener(e -> deleteSelectedOptionsConfiguration());
|
||||
@ -350,17 +357,18 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
"Overwrite existing configuration file: " + saveName + " ?", "Overwrite")) {
|
||||
return;
|
||||
}
|
||||
FileOptions saved = saveCurrentOptions();
|
||||
FileOptions currentOptions = getCurrentOptionsAsFileOptions();
|
||||
try {
|
||||
saved.save(saveFile);
|
||||
reloadOptionsCombo(saved);
|
||||
currentOptions.save(saveFile);
|
||||
currentNonDefaults = currentOptions;
|
||||
reloadOptionsCombo(currentOptions);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(this, "Error saving default options", e);
|
||||
}
|
||||
}
|
||||
|
||||
private FileOptions saveCurrentOptions() {
|
||||
private FileOptions getCurrentOptionsAsFileOptions() {
|
||||
FileOptions saveTo = new FileOptions("");
|
||||
List<AnalyzerEnablementState> analyzerStates = model.getModelData();
|
||||
for (AnalyzerEnablementState analyzerState : analyzerStates) {
|
||||
@ -395,17 +403,18 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
editorState.loadFrom(selectedOptions);
|
||||
}
|
||||
updateDeleteButton();
|
||||
currentNonDefaults = getCurrentOptionsAsFileOptions();
|
||||
}
|
||||
|
||||
private void reloadOptionsCombo(Options newDefaultOptions) {
|
||||
Options[] defaultOptionsArray = getDefaultOptionsArray();
|
||||
defaultOptionsCombo.setModel(new DefaultComboBoxModel<Options>(defaultOptionsArray));
|
||||
Options selected = findOptions(defaultOptionsArray, newDefaultOptions.getName());
|
||||
defaultOptionsCombo.setSelectedItem(selected);
|
||||
optionConfigurationChoices = loadPossibleOptionsChoicesForComboBox();
|
||||
optionsComboBox.setModel(new DefaultComboBoxModel<Options>(optionConfigurationChoices));
|
||||
Options selected = findOptionsByName(newDefaultOptions.getName());
|
||||
optionsComboBox.setSelectedItem(selected);
|
||||
}
|
||||
|
||||
private Options findOptions(Options[] defaultOptionsArray, String name) {
|
||||
for (Options fileOptions : defaultOptionsArray) {
|
||||
private Options findOptionsByName(String name) {
|
||||
for (Options fileOptions : optionConfigurationChoices) {
|
||||
if (fileOptions.getName().equals(name)) {
|
||||
return fileOptions;
|
||||
}
|
||||
@ -539,6 +548,13 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
copyOptionsToAllPrograms();
|
||||
currentProgramOptions = getNonDefaultProgramOptions();
|
||||
reloadOptionsCombo(currentProgramOptions);
|
||||
|
||||
// save off preference (unless it is the current program options, then don't save it)
|
||||
if (selectedOptions != currentProgramOptions) {
|
||||
Preferences.setProperty(LAST_USED_OPTIONS_CONFIG,
|
||||
selectedOptions.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void copyOptionsToAllPrograms() {
|
||||
@ -684,23 +700,12 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
private String getLastUsedDefaultOptionsName() {
|
||||
// if the program has non-default options or has been analyzed use its
|
||||
// current settings initially
|
||||
if (isAnalyzed() || !currentProgramOptions.getOptionNames().isEmpty()) {
|
||||
return currentProgramOptions.getName();
|
||||
}
|
||||
// Otherwise, use the last used analysis options configuration
|
||||
return Preferences.getProperty(LAST_USED_OPTIONS_CONFIG,
|
||||
STANDARD_DEFAULT_OPTIONS.getName());
|
||||
}
|
||||
|
||||
private boolean isAnalyzed() {
|
||||
Options options = programs.get(0).getOptions(Program.PROGRAM_INFO);
|
||||
return options.getBoolean(Program.ANALYZED, false);
|
||||
}
|
||||
|
||||
private Options[] getDefaultOptionsArray() {
|
||||
private Options[] loadPossibleOptionsChoicesForComboBox() {
|
||||
List<Options> savedDefaultsList = getSavedOptionsObjects();
|
||||
Options[] optionsArray = new FileOptions[savedDefaultsList.size() + 2]; // 2 standard configurations always present
|
||||
optionsArray[CURRENT_PROGRAM_OPTIONS_CHOICE_INDEX] = currentProgramOptions;
|
||||
@ -712,10 +717,9 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
private String[] getSavedChoices() {
|
||||
Options[] defaultOptionsArray = getDefaultOptionsArray();
|
||||
List<String> list = new ArrayList<>();
|
||||
for (int i = 2; i < defaultOptionsArray.length; i++) {
|
||||
list.add(defaultOptionsArray[i].getName());
|
||||
for (int i = 2; i < optionConfigurationChoices.length; i++) {
|
||||
list.add(optionConfigurationChoices[i].getName());
|
||||
}
|
||||
String[] a = new String[list.size()];
|
||||
list.toArray(a);
|
||||
@ -804,20 +808,57 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
|
||||
|
||||
}
|
||||
|
||||
private void analysisComboChanged(ItemEvent e) {
|
||||
private void optionsComboBoxChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
selectedOptions = (FileOptions) defaultOptionsCombo.getSelectedItem();
|
||||
if (!checkOkToChange()) {
|
||||
optionsComboBox.removeItemListener(optionsComboBoxListener);
|
||||
optionsComboBox.setSelectedItem(selectedOptions);
|
||||
optionsComboBox.addItemListener(optionsComboBoxListener);
|
||||
return;
|
||||
}
|
||||
selectedOptions = (FileOptions) optionsComboBox.getSelectedItem();
|
||||
updateDeleteButton();
|
||||
loadCurrentOptionsIntoEditors();
|
||||
// save off preference (unless it is the current program options, then don't save it)
|
||||
if (selectedOptions != currentProgramOptions) {
|
||||
Preferences.setProperty(LAST_USED_OPTIONS_CONFIG,
|
||||
selectedOptions.getName());
|
||||
}
|
||||
propertyChangeListener.propertyChange(
|
||||
new PropertyChangeEvent(this, GhidraOptions.APPLY_ENABLED, null,
|
||||
hasChangedValues()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkOkToChange() {
|
||||
FileOptions current = getCurrentOptionsAsFileOptions();
|
||||
if (Options.hasSameOptionsAndValues(current, currentNonDefaults)) {
|
||||
return true;
|
||||
}
|
||||
int result = OptionDialog.showYesNoDialog(this, "Loose Changes?",
|
||||
"You have made changes from the current options set. If you change\n" +
|
||||
"the current option set, those changes will be lost.\n" +
|
||||
"Do you want to proceed?");
|
||||
return result == OptionDialog.YES_OPTION;
|
||||
}
|
||||
|
||||
private void updateDeleteButton() {
|
||||
deleteButton.setEnabled(isUserConfiguration(selectedOptions));
|
||||
}
|
||||
|
||||
public void setToLastUsedAnalysisOptionsIfProgramNotAnalyzed() {
|
||||
// if already analyzed, get out
|
||||
if (isAnalyzed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if any analysis options are non default, it means the user previously saved
|
||||
// some options, so don't use last save profile
|
||||
if (!getNonDefaultProgramOptions().getOptionNames().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, use the last used analysis options configuration
|
||||
String optionsName = Preferences.getProperty(LAST_USED_OPTIONS_CONFIG,
|
||||
STANDARD_DEFAULT_OPTIONS.getName());
|
||||
Options lastUsed = findOptionsByName(optionsName);
|
||||
if (lastUsed != null) {
|
||||
optionsComboBox.setSelectedItem(lastUsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,8 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertTrue(isAnalyzerEnabled("Stack"));
|
||||
assertTrue(isAnalyzerEnabled("Reference"));
|
||||
assertTrue(isAnalyzerEnabled("ASCII Strings"));
|
||||
|
||||
close(optionsDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -119,6 +121,8 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertFalse(isAnalyzerEnabled("Stack"));
|
||||
assertFalse(isAnalyzerEnabled("Reference"));
|
||||
assertFalse(isAnalyzerEnabled("ASCII Strings"));
|
||||
|
||||
close(optionsDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -140,6 +144,8 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertTrue(isAnalyzerEnabled("Stack"));
|
||||
assertTrue(isAnalyzerEnabled("Reference"));
|
||||
assertTrue(isAnalyzerEnabled("ASCII Strings"));
|
||||
|
||||
close(optionsDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -154,6 +160,7 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
assertComboboxEquals("foo");
|
||||
|
||||
close(optionsDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -166,6 +173,8 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
confirmDelete();
|
||||
|
||||
assertComboboxEquals("Current Program Options");
|
||||
|
||||
close(optionsDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -190,6 +199,7 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertTrue(isAnalyzerEnabled("Reference"));
|
||||
assertFalse(isAnalyzerEnabled("ASCII Strings"));
|
||||
|
||||
close(optionsDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -212,6 +222,7 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
pressButtonByText(optionsDialog, "Cancel", false);
|
||||
OptionDialog yesNoDialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(yesNoDialog.getComponent(), "No");
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(isAnalyzerEnabledInProgramOptions("Stack"));
|
||||
assertTrue(isAnalyzerEnabledInProgramOptions("Reference"));
|
||||
@ -260,7 +271,7 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
AnalysisPanel panel = (AnalysisPanel) getInstanceField("panel", optionsDialog);
|
||||
@SuppressWarnings("unchecked")
|
||||
GhidraComboBox<Options> combo =
|
||||
(GhidraComboBox<Options>) getInstanceField("defaultOptionsCombo", panel);
|
||||
(GhidraComboBox<Options>) getInstanceField("optionsComboBox", panel);
|
||||
assertEquals(name, ((Options) combo.getSelectedItem()).getName());
|
||||
}
|
||||
|
||||
@ -269,7 +280,7 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
AnalysisPanel panel = (AnalysisPanel) getInstanceField("panel", optionsDialog);
|
||||
@SuppressWarnings("unchecked")
|
||||
GhidraComboBox<Options> combo =
|
||||
(GhidraComboBox<Options>) getInstanceField("defaultOptionsCombo", panel);
|
||||
(GhidraComboBox<Options>) getInstanceField("optionsComboBox", panel);
|
||||
ComboBoxModel<Options> model = combo.getModel();
|
||||
for (int i = 0; i < model.getSize(); i++) {
|
||||
Options elementAt = model.getElementAt(i);
|
||||
|
@ -19,8 +19,7 @@ import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
@ -517,4 +516,29 @@ public interface Options {
|
||||
*/
|
||||
public abstract String getDefaultValueAsString(String optionName);
|
||||
|
||||
/**
|
||||
* Returns true if the two options objects have the same set of options and values
|
||||
* @param options1 the first options object to test
|
||||
* @param options2 the second options object to test
|
||||
* @return true if the two options objects have the same set of options and values
|
||||
*/
|
||||
public static boolean hasSameOptionsAndValues(Options options1, Options options2) {
|
||||
List<String> leafOptionNames1 = options1.getOptionNames();
|
||||
List<String> leafOptionNames2 = options2.getOptionNames();
|
||||
Collections.sort(leafOptionNames1);
|
||||
Collections.sort(leafOptionNames2);
|
||||
|
||||
if (!leafOptionNames1.equals(leafOptionNames2)) {
|
||||
return false;
|
||||
}
|
||||
for (String optionName : leafOptionNames1) {
|
||||
Object value1 = options1.getObject(optionName, null);
|
||||
Object value2 = options2.getObject(optionName, null);
|
||||
if (!Objects.equals(value1, value2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.framework.options;
|
||||
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.beans.PropertyEditor;
|
||||
@ -26,6 +23,8 @@ import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
public class SubOptions implements Options {
|
||||
|
||||
private AbstractOptions options;
|
||||
@ -369,5 +368,4 @@ public class SubOptions implements Options {
|
||||
Set<String> leaves = AbstractOptions.getLeaves(optionPaths);
|
||||
return new ArrayList<String>(leaves);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user