diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonView.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonView.java index 090a4fceac..d4555b53ab 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonView.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonView.java @@ -18,8 +18,7 @@ package ghidra.features.base.codecompare.listing; import static ghidra.util.datastruct.Duo.Side.*; import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import javax.swing.Icon; import javax.swing.JComponent; @@ -37,8 +36,8 @@ import ghidra.app.plugin.core.functioncompare.actions.*; import ghidra.app.util.ListingHighlightProvider; import ghidra.app.util.viewer.format.*; import ghidra.app.util.viewer.listingpanel.*; -import ghidra.features.base.codecompare.panel.CodeComparisonViewActionContext; import ghidra.features.base.codecompare.panel.CodeComparisonView; +import ghidra.features.base.codecompare.panel.CodeComparisonViewActionContext; import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; @@ -63,6 +62,7 @@ public class ListingCodeComparisonView extends CodeComparisonView implements FormatModelListener, OptionsChangeListener { public static final String NAME = "Listing View"; + private static final String FORMAT_KEY = "FIELD_FORMAT"; private static final String DIFF_NAVIGATE_GROUP = "A2_DiffNavigate"; //@formatter:off @@ -79,6 +79,8 @@ public class ListingCodeComparisonView ALL, UNMATCHED, DIFF } + private SaveState defaultSaveState; + private SaveState saveState; private ListingCodeComparisonOptions comparisonOptions; private Duo displays; @@ -112,6 +114,9 @@ public class ListingCodeComparisonView listingDiff = buildListingDiff(); displays = buildListingDisplays(); + + buildDefaultSaveState(); + buildPanel(); createActions(); @@ -139,6 +144,63 @@ public class ListingCodeComparisonView return new Duo<>(leftDisplay, rightDisplay); } + private void buildDefaultSaveState() { + SaveState ss = new SaveState(); + saveFormat(ss); + defaultSaveState = ss; + } + + @Override + public void setSaveState(SaveState ss) { + + this.saveState = ss; + + if (hasStateChanges()) { + loadFormat(ss); + } + + } + + private boolean hasStateChanges() { + if (!saveState.isEmpty()) { + return !equals(saveState, defaultSaveState); + } + return false; + } + + private boolean equals(SaveState state1, SaveState state2) { + String s1 = state1.toString(); + String s2 = state2.toString(); + return Objects.equals(s1, s2); + } + + private void changeRightToMatchLeftFormat(FieldFormatModel model) { + SaveState formatState = new SaveState(); + displays.get(LEFT).getFormatManager().saveState(formatState); + displays.get(RIGHT).getFormatManager().readState(formatState); + + saveFormat(saveState); + tool.setConfigChanged(true); + } + + private void loadFormat(SaveState ss) { + + SaveState formatState = ss.getSaveState(FORMAT_KEY); + if (formatState == null) { + return; + } + + displays.get(LEFT).getFormatManager().readState(formatState); + displays.get(RIGHT).getFormatManager().readState(formatState); + } + + private void saveFormat(SaveState ss) { + SaveState formatState = new SaveState(); + FormatManager format = displays.get(LEFT).getFormatManager(); + format.saveState(formatState); + ss.putSaveState(FORMAT_KEY, formatState); + } + @Override public JComponent getComparisonComponent(Side side) { return displays.get(side).getListingPanel(); @@ -590,12 +652,6 @@ public class ListingCodeComparisonView comparisonOptions.loadOptions(options); } - private void changeRightToMatchLeftFormat(FieldFormatModel model) { - SaveState saveState = new SaveState(); - displays.get(LEFT).getFormatManager().saveState(saveState); - displays.get(RIGHT).getFormatManager().readState(saveState); - } - private void updateProgramViews() { displays.get(LEFT).setProgramView(getProgram(LEFT), getAddresses(LEFT), "listing1"); displays.get(RIGHT).setProgramView(getProgram(RIGHT), getAddresses(RIGHT), "listing2"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonPanel.java index a362c9d9d2..304a5db2bd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonPanel.java @@ -49,12 +49,11 @@ import help.HelpService; * A panel for displaying {@link Function functions} side-by-side for comparison purposes */ public class FunctionComparisonPanel extends JPanel implements ChangeListener { - private static final String ORIENTATION_PROPERTY_NAME = "ORIENTATION"; - private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonView.NAME; - private static final String COMPARISON_VIEW_DISPLAYED = "COMPARISON_VIEW_DISPLAYED"; - private static final String CODE_COMPARISON_LOCK_SCROLLING_TOGETHER = - "CODE_COMPARISON_LOCK_SCROLLING_TOGETHER"; + private static final String DEFAULT_VIEW = ListingCodeComparisonView.NAME; + private static final String KEY_ACTIVE_VIEW = "ACTIVE_VIEW"; + private static final String KEY_SCROLL_LOCK = "SCROLL_LOCK"; + private static final String KEY_ORIENTATION = "ORIENTATION"; private static final String HELP_TOPIC = "FunctionComparison"; @@ -67,7 +66,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { private static final String DUAL_SCROLLING_HELP_TOPIC = "FunctionComparison"; private JTabbedPane tabbedPane; - private Map tabNameToComponentMap; + private Map tabComponentsByName; private List codeComparisonViews; private ToggleScrollLockAction toggleScrollLockAction; private boolean syncScrolling = false; @@ -89,10 +88,16 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { state.addUpdateCallback(this::comparisonStateUpdated); codeComparisonViews = getCodeComparisonViews(tool, owner); - tabNameToComponentMap = new HashMap<>(); + tabComponentsByName = new HashMap<>(); createMainPanel(); createActions(owner); setScrollingSyncState(true); + + // reload saved state; add the listener after we are fully finished build so we do not save + // any default state + readPanelState(); + tabbedPane.addChangeListener(this); + HelpService help = Help.getHelpService(); help.registerHelp(this, new HelpLocation(HELP_TOPIC, "Function Comparison")); } @@ -237,9 +242,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { * @param name name of view to set as the current tab * @return true if the named view was found in the view map */ - public boolean setCurrentTabbedComponent(String name) { - - JComponent component = tabNameToComponentMap.get(name); + public boolean setActiveView(String name) { + JComponent component = tabComponentsByName.get(name); if (component != null) { if (tabbedPane.getSelectedComponent() == component) { tabChanged(); @@ -254,7 +258,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { * * @return the tab name, or null if there is nothing selected */ - public String getCurrentComponentName() { + public String getActiveViewName() { int selectedIndex = tabbedPane.getSelectedIndex(); if (selectedIndex >= 0) { return tabbedPane.getTitleAt(selectedIndex); @@ -262,14 +266,6 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { return null; } - /** - * Get the number of views in the tabbed pane - * @return the number of views in the tabbed pane - */ - int getNumberOfTabbedComponents() { - return tabNameToComponentMap.size(); - } - /** * Remove all views in the tabbed pane */ @@ -288,7 +284,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { } } - public CodeComparisonView getCodeComparisonView(String name) { + public CodeComparisonView getView(String name) { for (CodeComparisonView view : codeComparisonViews) { if (name.equals(view.getName())) { return view; @@ -297,21 +293,12 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { return null; } - public void selectComparisonView(String name) { - for (CodeComparisonView view : codeComparisonViews) { - if (name.equals(view.getName())) { - tabbedPane.setSelectedComponent(view); - } - } - } - /** * Create the main tabbed panel */ private void createMainPanel() { tabbedPane = new JTabbedPane(); - tabbedPane.addChangeListener(this); setLayout(new BorderLayout()); add(tabbedPane, BorderLayout.CENTER); @@ -319,7 +306,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { for (CodeComparisonView view : codeComparisonViews) { tabbedPane.add(view.getName(), view); - tabNameToComponentMap.put(view.getName(), view); + tabComponentsByName.put(view.getName(), view); } } @@ -356,32 +343,34 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { private void readPanelState() { SaveState panelState = state.getPanelState(); - String currentTabView = - panelState.getString(COMPARISON_VIEW_DISPLAYED, DEFAULT_CODE_COMPARISON_VIEW); - setCurrentTabbedComponent(currentTabView); - setScrollingSyncState( - panelState.getBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true)); + String activeView = panelState.getString(KEY_ACTIVE_VIEW, DEFAULT_VIEW); + setActiveView(activeView); + + boolean scrollLock = panelState.getBoolean(KEY_SCROLL_LOCK, true); + setScrollingSyncState(scrollLock); for (CodeComparisonView view : codeComparisonViews) { - String key = view.getName() + ORIENTATION_PROPERTY_NAME; + String key = view.getName() + KEY_ORIENTATION; view.setSideBySide(panelState.getBoolean(key, true)); } } private void writeTabState() { - String currentComponentName = getCurrentComponentName(); - if (currentComponentName == null) { - return; + + String viewName = getActiveViewName(); + if (viewName == null) { + return; // null can happen during tabbed pane disposal } SaveState panelState = state.getPanelState(); - panelState.putString(COMPARISON_VIEW_DISPLAYED, getCurrentComponentName()); + panelState.putString(KEY_ACTIVE_VIEW, getActiveViewName()); + state.setChanged(); } private void writeScrollState() { SaveState panelState = state.getPanelState(); - panelState.putBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced()); + panelState.putBoolean(KEY_SCROLL_LOCK, isScrollingSynced()); state.setChanged(); } @@ -389,7 +378,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener { SaveState panelState = state.getPanelState(); for (CodeComparisonView view : codeComparisonViews) { - String key = view.getName() + ORIENTATION_PROPERTY_NAME; + String key = view.getName() + KEY_ORIENTATION; boolean sideBySide = view.isSideBySide(); panelState.putBoolean(key, sideBySide); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonState.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonState.java index cabed0aefc..6981d0c3eb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonState.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonState.java @@ -31,8 +31,11 @@ public class FunctionComparisonState { private static final String PROVIDER_SAVE_STATE_NAME = "FunctionComparison"; + // generic panel state that applies to the top-level panel, such as divider location private SaveState panelState = new SaveState(); - private CodeComparisonViewState comparisonState = new CodeComparisonViewState(); + + // view-specific state that is managed by each discovered view + private CodeComparisonViewState viewState = new CodeComparisonViewState(); private PluginTool tool; @@ -55,7 +58,7 @@ public class FunctionComparisonState { * @return the state */ public CodeComparisonViewState getViewState() { - return comparisonState; + return viewState; } /** @@ -67,7 +70,7 @@ public class FunctionComparisonState { public void writeConfigState(SaveState saveState) { saveState.putSaveState(PROVIDER_SAVE_STATE_NAME, panelState); - comparisonState.writeConfigState(saveState); + viewState.writeConfigState(saveState); } public void readConfigState(SaveState saveState) { @@ -76,7 +79,7 @@ public class FunctionComparisonState { panelState = restoredPanelState; } - comparisonState.readConfigState(saveState); + viewState.readConfigState(saveState); updateCallbacks.forEach(Callback::call); } diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/plugin/FunctionComparisonProvider.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/plugin/FunctionComparisonProvider.java index fd90ed84fb..9827fe4389 100644 --- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/plugin/FunctionComparisonProvider.java +++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/plugin/FunctionComparisonProvider.java @@ -354,12 +354,12 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter } } - public CodeComparisonView getCodeComparisonView(String name) { - return functionComparisonPanel.getCodeComparisonView(name); + public CodeComparisonView getView(String name) { + return functionComparisonPanel.getView(name); } - public void selectComparisonView(String name) { - functionComparisonPanel.selectComparisonView(name); + public void setActiveView(String name) { + functionComparisonPanel.setActiveView(name); } private void dispose() { diff --git a/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/decompile/AbstractDualDecompilerTest.java b/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/decompile/AbstractDualDecompilerTest.java index c43eacb0ab..6a9bd5292d 100644 --- a/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/decompile/AbstractDualDecompilerTest.java +++ b/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/decompile/AbstractDualDecompilerTest.java @@ -75,7 +75,7 @@ public abstract class AbstractDualDecompilerTest extends AbstractGhidraHeadedInt protected void setActivePanel(FunctionComparisonProvider provider, CodeComparisonView comparisonProvider) { runSwing( - () -> provider.getComponent().setCurrentTabbedComponent(comparisonProvider.getName())); + () -> provider.getComponent().setActiveView(comparisonProvider.getName())); waitForSwing(); } diff --git a/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/plugin/CompareFunctionsProviderTest.java b/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/plugin/CompareFunctionsProviderTest.java index 2bf372db1d..72e22ea431 100644 --- a/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/plugin/CompareFunctionsProviderTest.java +++ b/Ghidra/Features/CodeCompare/src/test/java/ghidra/features/codecompare/plugin/CompareFunctionsProviderTest.java @@ -294,7 +294,7 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio private void setActivePanel(FunctionComparisonProvider provider, CodeComparisonView comparisonProvider) { runSwing( - () -> provider.getComponent().setCurrentTabbedComponent(comparisonProvider.getName())); + () -> provider.getComponent().setActiveView(comparisonProvider.getName())); waitForSwing(); } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java index 341f64863b..a9de9bdded 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java @@ -375,7 +375,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter functionComparisonPanel = fcService.createComparisonViewer(); addSpecificCodeComparisonActions(); - functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonView.NAME); + functionComparisonPanel.setActiveView(ListingCodeComparisonView.NAME); functionComparisonPanel.setTitlePrefixes("Source:", "Destination:"); comparisonSplitPane = diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java index ddf32aae83..d1af28507d 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java @@ -159,7 +159,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter functionComparisonPanel = fcService.createComparisonViewer(); addSpecificCodeComparisonActions(); - functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonView.NAME); + functionComparisonPanel.setActiveView(ListingCodeComparisonView.NAME); functionComparisonPanel.getAccessibleContext().setAccessibleName("Function Comparison"); functionComparisonPanel.setTitlePrefixes("Source:", "Destination:"); ListingCodeComparisonView dualListingProvider = diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionComparisonScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionComparisonScreenShots.java index 87fcc6e420..79150f436e 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionComparisonScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionComparisonScreenShots.java @@ -101,7 +101,7 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator { waitForComponentProvider(FunctionComparisonProvider.class); FunctionComparisonPanel functionComparisonPanel = provider.getComponent(); runSwing(() -> { - functionComparisonPanel.setCurrentTabbedComponent("Listing View"); + functionComparisonPanel.setActiveView("Listing View"); ListingCodeComparisonView dualListing = (ListingCodeComparisonView) functionComparisonPanel.getDisplayedView(); ListingPanel leftPanel = dualListing.getListingPanel(LEFT); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsDecompilerViewTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsDecompilerViewTest.java index da63e5e774..34bf788af3 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsDecompilerViewTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsDecompilerViewTest.java @@ -79,7 +79,7 @@ public class CompareFunctionsDecompilerViewTest extends AbstractGhidraHeadedInte checkFunctions(provider, LEFT, fun1, fun1, fun2); DecompilerCodeComparisonView comparisonProvider = (DecompilerCodeComparisonView) provider - .getCodeComparisonView(DecompilerCodeComparisonView.NAME); + .getView(DecompilerCodeComparisonView.NAME); waitForDecompiler(comparisonProvider); assertHasLines(comparisonProvider.getLeftPanel(), 28); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsFunctionGraphViewTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsFunctionGraphViewTest.java index de550e05e2..cbcd5b1156 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsFunctionGraphViewTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsFunctionGraphViewTest.java @@ -500,13 +500,13 @@ public class CompareFunctionsFunctionGraphViewTest extends AbstractGhidraHeadedI } private void selectFgPanel(FunctionComparisonProvider provider) { - runSwing(() -> provider.selectComparisonView(FunctionGraphCodeComparisonView.NAME)); + runSwing(() -> provider.setActiveView(FunctionGraphCodeComparisonView.NAME)); } private FunctionGraphCodeComparisonView getFgComparisonProvider( FunctionComparisonProvider provider) { return runSwing(() -> (FunctionGraphCodeComparisonView) provider - .getCodeComparisonView(FunctionGraphCodeComparisonView.NAME)); + .getView(FunctionGraphCodeComparisonView.NAME)); } private void waitForFunctionGraph(FunctionGraphCodeComparisonView panel) {