diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java
index df6f4267f5..17bb7b6d48 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java
@@ -852,16 +852,6 @@ public abstract class AbstractCodeBrowserPlugin
ex
return getAddressIndexMap().getAddress(index);
}
- @Override
- public void formatModelAdded(FieldFormatModel model) {
- // uninterested
- }
-
- @Override
- public void formatModelRemoved(FieldFormatModel model) {
- // uninterested
- }
-
@Override
public void formatModelChanged(FieldFormatModel model) {
tool.setConfigChanged(true);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonData.java
deleted file mode 100644
index eeb9f4c944..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonData.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.plugin.core.functioncompare;
-
-import ghidra.program.model.address.AddressSet;
-import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.listing.*;
-
-/**
- * Defines the information being displayed in the left or right panels
- * of a {@link FunctionComparisonPanel}, which can display either
- * {@link Function functions}, {@link Data data}, or specified
- * {@link AddressSet address sets}. At any given time, only one of the
- * Function or Data attributes may be set; the other will be
- * set to null.
- */
-class FunctionComparisonData {
-
- protected Program program;
- protected Function function;
- protected Data data;
- protected AddressSetView addressSet = new AddressSet();
-
- /**
- * Returns the program for this model
- *
- * @return the program, or null if not set
- */
- public Program getProgram() {
- return program;
- }
-
- /**
- * Sets the program for this model
- *
- * @param program the program to set
- */
- public void setProgram(Program program) {
- this.program = program;
- }
-
- /**
- * Returns the function for this model
- *
- * @return the function, or null if not set
- */
- public Function getFunction() {
- return function;
- }
-
- /**
- * Sets the function for this model
- *
- * @param function the function to set
- */
- public void setFunction(Function function) {
- if (function == null) {
- clear();
- return;
- }
- this.function = function;
- this.data = null;
- this.program = function.getProgram();
- this.addressSet = function.getBody();
- }
-
- /**
- * Returns the data for this model
- *
- * @return the data, or null if not set
- */
- public Data getData() {
- return data;
- }
-
- /**
- * Sets the data for this model
- *
- * @param data the data to set
- */
- public void setData(Data data) {
- if (data == null) {
- clear();
- return;
- }
- this.data = data;
- this.function = null;
- this.program = data.getProgram();
- this.addressSet = new AddressSet(data.getMinAddress(), data.getMaxAddress());
- }
-
- /**
- * Returns the address set for this model
- *
- * @return the address set, or null if not set
- */
- public AddressSetView getAddressSet() {
- return addressSet;
- }
-
- /**
- * Sets the address for this model
- *
- * @param addressSet the addressSet to set
- */
- public void setAddressSet(AddressSetView addressSet) {
- this.addressSet = addressSet;
- this.data = null;
- this.function = null;
- }
-
- /**
- * Returns true if the data being managed by this model is of type
- * {@link Data}
- *
- * @return true if this model is set to display {@link Data}
- */
- public boolean isData() {
- return data != null;
- }
-
- /**
- * Returns true if the data being managed by this model is of type
- * {@link Function}
- *
- * @return true if this model is set to display a {@link Function}
- */
- public boolean isFunction() {
- return function != null;
- }
-
- /**
- * Returns true if this class holds no function, data or address set
- * information
- *
- * @return true if this class holds no function, data or address set
- * information
- */
- public boolean isEmpty() {
- return function == null && data == null && addressSet == null;
- }
-
- /**
- * Resets all fields in this model to a nominal state
- */
- public void clear() {
- this.function = null;
- this.data = null;
- this.addressSet = new AddressSet();
- this.program = null;
- }
-
- public String toString() {
- String str = "";
-
- if (function != null) {
- str = function.getName();
- }
- else if (data != null) {
- str = data.getAddress().toString();
- }
- else if (addressSet != null) {
- str = addressSet.toString();
- }
- else {
- str = "none";
- }
-
- return str;
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPanel.java
index 7ddc827b98..f97615bc02 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPanel.java
@@ -15,6 +15,9 @@
*/
package ghidra.app.plugin.core.functioncompare;
+import static ghidra.app.util.viewer.util.ComparisonData.*;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import java.awt.*;
import java.awt.event.MouseEvent;
import java.lang.reflect.Constructor;
@@ -29,11 +32,10 @@ import javax.swing.event.ChangeListener;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.*;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.tabbedpane.DockingTabRenderer;
import generic.theme.GIcon;
import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel;
-import ghidra.app.util.viewer.util.CodeComparisonPanel;
+import ghidra.app.util.viewer.util.*;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.PluginTool;
@@ -43,6 +45,7 @@ import ghidra.program.model.listing.*;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
+import ghidra.util.datastruct.Duo;
import help.Help;
import help.HelpService;
@@ -54,11 +57,9 @@ import help.HelpService;
* be compared, use a {@link MultiFunctionComparisonPanel}
*/
public class FunctionComparisonPanel extends JPanel implements ChangeListener {
+ private static final String ORIENTATION_PROPERTY_NAME = "ORIENTATION";
- private FunctionComparisonData leftComparisonData;
- private FunctionComparisonData rightComparisonData;
-
- private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonPanel.TITLE;
+ private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonPanel.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";
@@ -78,26 +79,22 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
private Map tabNameToComponentMap;
protected PluginTool tool;
protected ComponentProviderAdapter provider;
- private List> codeComparisonPanels;
+ private List codeComparisonPanels;
private ToggleScrollLockAction toggleScrollLockAction;
private boolean syncScrolling = false;
+ private Duo comparisonData = new Duo();
+
/**
* Constructor
*
* @param provider the GUI provider that includes this panel
* @param tool the tool containing this panel
- * @param leftFunction the function displayed in the left side of the panel
- * @param rightFunction the function displayed in the right side of the panel
*/
- public FunctionComparisonPanel(ComponentProviderAdapter provider, PluginTool tool,
- Function leftFunction, Function rightFunction) {
+ public FunctionComparisonPanel(ComponentProviderAdapter provider, PluginTool tool) {
this.provider = provider;
this.tool = tool;
- this.leftComparisonData = new FunctionComparisonData();
- this.rightComparisonData = new FunctionComparisonData();
- this.leftComparisonData.setFunction(leftFunction);
- this.rightComparisonData.setFunction(rightFunction);
+ this.comparisonData = new Duo<>(EMPTY, EMPTY);
this.codeComparisonPanels = getCodeComparisonPanels();
tabNameToComponentMap = new HashMap<>();
createMainPanel();
@@ -113,15 +110,11 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @param rightFunction The function for the right side of the panel
*/
public void loadFunctions(Function leftFunction, Function rightFunction) {
- leftComparisonData.setFunction(leftFunction);
- rightComparisonData.setFunction(rightFunction);
-
- CodeComparisonPanel extends FieldPanelCoordinator> activePanel =
- getActiveComparisonPanel();
- if (activePanel != null) {
- activePanel.loadFunctions(leftComparisonData.getFunction(),
- rightComparisonData.getFunction());
- }
+ ComparisonData left =
+ leftFunction == null ? EMPTY : new FunctionComparisonData(leftFunction);
+ ComparisonData right =
+ rightFunction == null ? EMPTY : new FunctionComparisonData(rightFunction);
+ loadComparisons(left, right);
}
/**
@@ -131,14 +124,19 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @param rightData The data for the right side of the panel
*/
public void loadData(Data leftData, Data rightData) {
- leftComparisonData.setData(leftData);
- rightComparisonData.setData(rightData);
+ ComparisonData left = new DataComparisonData(leftData, rightData.getLength());
+ ComparisonData right = new DataComparisonData(rightData, leftData.getLength());
+ loadComparisons(left, right);
+ }
- CodeComparisonPanel extends FieldPanelCoordinator> activePanel =
- getActiveComparisonPanel();
+ public void loadComparisons(ComparisonData left, ComparisonData right) {
+ comparisonData = new Duo<>(left, right);
+
+ CodeComparisonPanel activePanel = getActiveComparisonPanel();
if (activePanel != null) {
- activePanel.loadData(leftComparisonData.getData(), rightComparisonData.getData());
+ activePanel.loadComparisons(left, right);
}
+
}
/**
@@ -154,17 +152,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
*/
public void loadAddresses(Program leftProgram, Program rightProgram,
AddressSetView leftAddresses, AddressSetView rightAddresses) {
- leftComparisonData.setAddressSet(leftAddresses);
- rightComparisonData.setAddressSet(rightAddresses);
- leftComparisonData.setProgram(leftProgram);
- rightComparisonData.setProgram(rightProgram);
- CodeComparisonPanel extends FieldPanelCoordinator> activePanel =
- getActiveComparisonPanel();
- if (activePanel != null) {
- activePanel.loadAddresses(leftComparisonData.getProgram(),
- rightComparisonData.getProgram(), leftComparisonData.getAddressSet(),
- rightComparisonData.getAddressSet());
- }
+ ComparisonData left = new AddressSetComparisonData(leftProgram, leftAddresses);
+ ComparisonData right = new AddressSetComparisonData(rightProgram, rightAddresses);
+ loadComparisons(left, right);
}
/**
@@ -183,35 +173,23 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @return the description
*/
public String getDescription() {
- Function leftFunc = leftComparisonData.getFunction();
- Function rightFunc = rightComparisonData.getFunction();
- Data leftData = leftComparisonData.getData();
- Data rightData = rightComparisonData.getData();
+ String leftShort = comparisonData.get(LEFT).getShortDescription();
+ String rightShort = comparisonData.get(LEFT).getShortDescription();
- if (leftFunc != null && rightFunc != null) {
- return leftFunc.getName(true) + " & " + rightFunc.getName(true);
- }
- if (leftData != null && rightData != null) {
- return leftData.getDataType().getName() + " & " + rightData.getDataType().getName();
- }
-
- // Otherwise give a simple description for address sets
- return "Nothing selected";
+ return leftShort + " & " + rightShort;
}
/**
* Clear both sides of this panel
*/
public void clear() {
- leftComparisonData.clear();
- rightComparisonData.clear();
+ comparisonData = new Duo<>(EMPTY, EMPTY);
// Setting the addresses to be displayed to null effectively clears
// the display
- CodeComparisonPanel extends FieldPanelCoordinator> activePanel =
- getActiveComparisonPanel();
+ CodeComparisonPanel activePanel = getActiveComparisonPanel();
if (activePanel != null) {
- activePanel.loadAddresses(null, null, null, null);
+ activePanel.clearComparisons();
}
}
@@ -222,7 +200,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @return true if the comparison window has no information to display
*/
public boolean isEmpty() {
- return leftComparisonData.isEmpty() || rightComparisonData.isEmpty();
+ return comparisonData.get(LEFT).isEmpty() || comparisonData.get(RIGHT).isEmpty();
}
/**
@@ -232,10 +210,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @return the comparison panel or null
*/
public ListingCodeComparisonPanel getDualListingPanel() {
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
- JComponent component = codeComparisonPanel.getComponent();
- if (component instanceof ListingCodeComparisonPanel) {
- return (ListingCodeComparisonPanel) component;
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
+ if (codeComparisonPanel instanceof ListingCodeComparisonPanel listingPanel) {
+ return listingPanel;
}
}
return null;
@@ -300,11 +277,26 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
tabbedPane.removeAll();
setVisible(false);
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
codeComparisonPanel.dispose();
}
}
+ void programClosed(Program program) {
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
+ codeComparisonPanel.programClosed(program);
+ }
+ }
+
+ CodeComparisonPanel getCodeComparisonPanelByName(String name) {
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
+ if (name.equals(codeComparisonPanel.getName())) {
+ return codeComparisonPanel;
+ }
+ }
+ return null;
+ }
+
/**
* Create the main tabbed panel
*/
@@ -317,12 +309,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
add(tabbedPane, BorderLayout.CENTER);
setPreferredSize(new Dimension(200, 300));
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
- codeComparisonPanel.loadFunctions(leftComparisonData.getFunction(),
- rightComparisonData.getFunction());
- JComponent component = codeComparisonPanel.getComponent();
- tabbedPane.add(codeComparisonPanel.getTitle(), component);
- tabNameToComponentMap.put(codeComparisonPanel.getTitle(), component);
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
+ tabbedPane.add(codeComparisonPanel.getName(), codeComparisonPanel);
+ tabNameToComponentMap.put(codeComparisonPanel.getName(), codeComparisonPanel);
}
}
@@ -331,24 +320,11 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* the appropriate data to be compared.
*/
private void tabChanged() {
- CodeComparisonPanel extends FieldPanelCoordinator> activePanel =
- getActiveComparisonPanel();
+ CodeComparisonPanel activePanel = getActiveComparisonPanel();
if (activePanel == null) {
return; // initializing
}
-
- if (leftComparisonData.isFunction() || rightComparisonData.isFunction()) {
- activePanel.loadFunctions(leftComparisonData.getFunction(),
- rightComparisonData.getFunction());
- }
- else if (leftComparisonData.isData() || rightComparisonData.isData()) {
- activePanel.loadData(leftComparisonData.getData(), rightComparisonData.getData());
- }
- else {
- activePanel.loadAddresses(leftComparisonData.getProgram(),
- rightComparisonData.getProgram(), leftComparisonData.getAddressSet(),
- rightComparisonData.getAddressSet());
- }
+ activePanel.loadComparisons(comparisonData.get(LEFT), comparisonData.get(RIGHT));
}
/**
@@ -357,100 +333,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @return the currently selected comparison panel, or null if nothing
* selected
*/
- private CodeComparisonPanel extends FieldPanelCoordinator> getActiveComparisonPanel() {
- JComponent c = (JComponent) tabbedPane.getSelectedComponent();
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
- JComponent component = codeComparisonPanel.getComponent();
- if (c == component) {
- return codeComparisonPanel;
- }
- }
-
- return null;
- }
-
- /**
- * Returns the comparison data object for the left panel
- *
- * @return the comparison data object for the left panel
- */
- public FunctionComparisonData getLeftComparisonData() {
- return leftComparisonData;
- }
-
- /**
- * Returns the comparison data object for the right panel
- *
- * @return the comparison data object for the right panel
- */
- public FunctionComparisonData getRightComparisonData() {
- return rightComparisonData;
- }
-
- /**
- * Gets the function currently displayed in the left side of this panel
- *
- * @return the left function or null
- */
- public Function getLeftFunction() {
- return leftComparisonData.getFunction();
- }
-
- /**
- * Sets the function to display in the left side of this panel
- *
- * @param function the function to display
- */
- protected void setLeftFunction(Function function) {
- loadFunctions(function, rightComparisonData.getFunction());
- }
-
- /**
- * Gets the function currently displayed in the right side of this panel
- *
- * @return the right function or null
- */
- public Function getRightFunction() {
- return rightComparisonData.getFunction();
- }
-
- /**
- * Sets the function to display in the right side of this panel
- *
- * @param function the function to display
- */
- protected void setRightFunction(Function function) {
- loadFunctions(leftComparisonData.getFunction(), function);
- }
-
- /**
- * Gets the data displayed in the left side of this panel
- *
- * @return the left data or null
- */
- public Data getLeftData() {
- return leftComparisonData.getData();
- }
-
- /**
- * Gets the data displayed in the right side of this panel
- *
- * @return the right data
- */
- public Data getRightData() {
- return rightComparisonData.getData();
- }
-
- /**
- * Enables/disables mouse navigation for all the CodeComparisonPanels
- * displayed by this panel
- *
- * @param enabled true to enable mouse navigation in the panels
- */
- public void setMouseNavigationEnabled(boolean enabled) {
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
- codeComparisonPanel.setMouseNavigationEnabled(enabled);
- }
+ private CodeComparisonPanel getActiveComparisonPanel() {
+ return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
}
/**
@@ -466,9 +350,10 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
setCurrentTabbedComponent(currentTabView);
setScrollingSyncState(
saveState.getBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true));
- ListingCodeComparisonPanel dualListingPanel = getDualListingPanel();
- if (dualListingPanel != null) {
- dualListingPanel.readConfigState(prefix, saveState);
+
+ for (CodeComparisonPanel panel : codeComparisonPanels) {
+ String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
+ panel.setSideBySide(saveState.getBoolean(key, true));
}
}
@@ -485,10 +370,13 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
saveState.putString(prefix + COMPARISON_VIEW_DISPLAYED, getCurrentComponentName());
}
saveState.putBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced());
- ListingCodeComparisonPanel dualListingPanel = getDualListingPanel();
- if (dualListingPanel != null) {
- dualListingPanel.writeConfigState(prefix, saveState);
+
+ for (CodeComparisonPanel panel : codeComparisonPanels) {
+ String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
+ boolean sideBySide = panel.isSideBySide();
+ saveState.putBoolean(key, sideBySide);
}
+
}
/**
@@ -499,18 +387,18 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
*/
public DockingAction[] getCodeComparisonActions() {
ArrayList dockingActionList = new ArrayList<>();
+
// Get actions for this functionComparisonPanel
DockingAction[] functionComparisonActions = getActions();
for (DockingAction dockingAction : functionComparisonActions) {
dockingActionList.add(dockingAction);
}
+
// Get actions for each CodeComparisonPanel
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
- DockingAction[] actions = codeComparisonPanel.getActions();
- for (DockingAction dockingAction : actions) {
- dockingActionList.add(dockingAction);
- }
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
+ dockingActionList.addAll(codeComparisonPanel.getActions());
}
+
return dockingActionList.toArray(new DockingAction[dockingActionList.size()]);
}
@@ -524,8 +412,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
public void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix) {
Component[] components = tabbedPane.getComponents();
for (Component component : components) {
- if (component instanceof CodeComparisonPanel>) {
- ((CodeComparisonPanel>) component).setTitlePrefixes(leftTitlePrefix,
+ if (component instanceof CodeComparisonPanel) {
+ ((CodeComparisonPanel) component).setTitlePrefixes(leftTitlePrefix,
rightTitlePrefix);
}
}
@@ -539,34 +427,10 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
* @return the action context
*/
public ActionContext getActionContext(MouseEvent event, ComponentProvider componentProvider) {
- Object source = (event != null) ? event.getSource() : null;
- Component sourceComponent = (source instanceof Component) ? (Component) source : null;
- ListingCodeComparisonPanel dualListingPanel = getDualListingPanel();
- // Is the action being taken on the dual listing.
- if (dualListingPanel != null && dualListingPanel.isAncestorOf(sourceComponent)) {
- return dualListingPanel.getActionContext(event, componentProvider);
- }
-
- CodeComparisonPanel extends FieldPanelCoordinator> activePanel =
- getFocusedComparisonPanel();
+ CodeComparisonPanel activePanel = getDisplayedPanel();
if (activePanel != null) {
return activePanel.getActionContext(componentProvider, event);
}
-
- return null;
- }
-
- private CodeComparisonPanel extends FieldPanelCoordinator> getFocusedComparisonPanel() {
- Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
- if (focused != null) {
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
- JComponent component = codeComparisonPanel.getComponent();
- boolean isParent = SwingUtilities.isDescendingFrom(focused, component);
- if (isParent) {
- return codeComparisonPanel;
- }
- }
- }
return null;
}
@@ -594,8 +458,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
toggleScrollLockAction.setToolBarData(new ToolBarData(
syncScrolling ? SYNC_SCROLLING_ICON : UNSYNC_SCROLLING_ICON, SCROLLING_GROUP));
// Notify each comparison panel of the scrolling sync state.
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
- codeComparisonPanel.setScrollingSyncState(syncScrolling);
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
+ codeComparisonPanel.setSynchronizedScrolling(syncScrolling);
}
this.syncScrolling = syncScrolling;
}
@@ -605,17 +469,17 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
*
* @return the current panel or null.
*/
- public CodeComparisonPanel extends FieldPanelCoordinator> getDisplayedPanel() {
+ public CodeComparisonPanel getDisplayedPanel() {
int selectedIndex = tabbedPane.getSelectedIndex();
Component component = tabbedPane.getComponentAt(selectedIndex);
- return (CodeComparisonPanel>) component;
+ return (CodeComparisonPanel) component;
}
/**
* Updates the enablement for all actions provided by each panel
*/
public void updateActionEnablement() {
- for (CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel : codeComparisonPanels) {
+ for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
codeComparisonPanel.updateActionEnablement();
}
}
@@ -625,8 +489,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
*
* @return null if there is no code comparison panel
*/
- CodeComparisonPanel extends FieldPanelCoordinator> getCurrentComponent() {
- return (CodeComparisonPanel>) tabbedPane.getSelectedComponent();
+ CodeComparisonPanel getCurrentComponent() {
+ return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
}
/**
@@ -677,7 +541,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
}
}
- public List> getComparisonPanels() {
+ public List getComparisonPanels() {
return codeComparisonPanels;
}
@@ -686,17 +550,17 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
*
* @return the CodeComparisonPanels which are extension points
*/
- private List> getCodeComparisonPanels() {
+ private List getCodeComparisonPanels() {
if (codeComparisonPanels == null) {
codeComparisonPanels = createAllPossibleCodeComparisonPanels();
- codeComparisonPanels.sort((p1, p2) -> p1.getTitle().compareTo(p2.getTitle()));
+ codeComparisonPanels.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
}
return codeComparisonPanels;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
- private ArrayList> createAllPossibleCodeComparisonPanels() {
- ArrayList> instances =
+ private ArrayList createAllPossibleCodeComparisonPanels() {
+ ArrayList instances =
new ArrayList<>();
List> classes =
ClassSearcher.getClasses(CodeComparisonPanel.class);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPlugin.java
index 33abb8bb5d..15df0fc755 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonPlugin.java
@@ -19,19 +19,13 @@ import java.util.Set;
import java.util.function.Supplier;
import ghidra.app.CorePluginPackage;
-import ghidra.app.events.ProgramActivatedPluginEvent;
-import ghidra.app.events.ProgramClosedPluginEvent;
-import ghidra.app.events.ProgramSelectionPluginEvent;
+import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsAction;
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsFromListingAction;
import ghidra.app.services.FunctionComparisonService;
-import ghidra.framework.model.DomainObjectChangeRecord;
-import ghidra.framework.model.DomainObjectChangedEvent;
-import ghidra.framework.model.DomainObjectEvent;
-import ghidra.framework.model.DomainObjectListener;
-import ghidra.framework.model.EventType;
+import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
@@ -117,7 +111,7 @@ public class FunctionComparisonPlugin extends ProgramPlugin
EventType eventType = doRecord.getEventType();
if (eventType == DomainObjectEvent.RESTORED) {
- functionComparisonManager.domainObjectRestored(ev);
+ functionComparisonManager.domainObjectRestored((Program) ev.getSource());
}
else if (eventType == ProgramEvent.FUNCTION_REMOVED) {
ProgramChangeRecord rec = (ProgramChangeRecord) ev.getChangeRecord(i);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java
index 98d6b99dc6..11c5087db0 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java
@@ -15,6 +15,8 @@
*/
package ghidra.app.plugin.core.functioncompare;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import java.awt.event.MouseEvent;
import java.util.*;
@@ -23,7 +25,6 @@ import docking.Tool;
import docking.action.DockingAction;
import docking.action.DockingActionIf;
import docking.actions.PopupActionProvider;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.services.FunctionComparisonModel;
import ghidra.app.services.FunctionComparisonService;
import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel;
@@ -87,7 +88,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
@Override
public FunctionComparisonPanel getComponent() {
if (functionComparisonPanel == null) {
- functionComparisonPanel = new FunctionComparisonPanel(this, tool, null, null);
+ functionComparisonPanel = new FunctionComparisonPanel(this, tool);
}
return functionComparisonPanel;
}
@@ -107,19 +108,14 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
buff.append(getName() + "\n");
buff.append("Tab Text: ");
buff.append(getTabText() + "\n");
- Function leftFunction = functionComparisonPanel.getLeftFunction();
- String leftName = (leftFunction != null) ? leftFunction.getName() : "No Function";
- buff.append("Function 1: " + leftName + "\n");
- Function rightFunction = functionComparisonPanel.getRightFunction();
- String rightName = (rightFunction != null) ? rightFunction.getName() : "No Function";
- buff.append("Function 2: " + rightName + "\n");
+ buff.append(functionComparisonPanel.getDescription());
buff.append("tool = " + tool + "\n");
return buff.toString();
}
@Override
public ActionContext getActionContext(MouseEvent event) {
- CodeComparisonPanel extends FieldPanelCoordinator> currentComponent =
+ CodeComparisonPanel currentComponent =
functionComparisonPanel.getCurrentComponent();
return currentComponent.getActionContext(this, event);
}
@@ -145,7 +141,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
ListingCodeComparisonPanel dualListingPanel =
functionComparisonPanel.getDualListingPanel();
if (dualListingPanel != null) {
- ListingPanel leftPanel = dualListingPanel.getLeftPanel();
+ ListingPanel leftPanel = dualListingPanel.getListingPanel(LEFT);
return leftPanel.getHeaderActions(getName());
}
}
@@ -178,6 +174,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
* @param program the program being closed
*/
public void programClosed(Program program) {
+ functionComparisonPanel.programClosed(program);
model.removeFunctions(program);
closeIfEmpty();
}
@@ -210,7 +207,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
* @param program the program that was restored (undo/redo)
*/
public void programRestored(Program program) {
- CodeComparisonPanel extends FieldPanelCoordinator> comparePanel =
+ CodeComparisonPanel comparePanel =
functionComparisonPanel.getCurrentComponent();
comparePanel.programRestored(program);
}
@@ -283,6 +280,10 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
this.closeListener = Callback.dummyIfNull(closeListener);
}
+ public CodeComparisonPanel getCodeComparisonPanelByName(String name) {
+ return functionComparisonPanel.getCodeComparisonPanelByName(name);
+ }
+
public void dispose() {
functionComparisonPanel.dispose();
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProviderManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProviderManager.java
index 6bbd6e60ba..8b233fc7f4 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProviderManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProviderManager.java
@@ -20,7 +20,6 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import docking.ComponentProviderActivationListener;
-import ghidra.framework.model.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
@@ -225,20 +224,10 @@ public class FunctionComparisonProviderManager implements FunctionComparisonProv
* will notify all the function comparison providers. This allows them to
* refresh if they are showing a function from the program
*
- * @param ev the object changed event
+ * @param program the program that was restored
*/
- public void domainObjectRestored(DomainObjectChangedEvent ev) {
- for (DomainObjectChangeRecord domainObjectChangeRecord : ev) {
- EventType eventType = domainObjectChangeRecord.getEventType();
- if (eventType != DomainObjectEvent.RESTORED) {
- return;
- }
- Object source = ev.getSource();
- if (source instanceof Program) {
- Program program = (Program) source;
- providers.stream().forEach(p -> p.programRestored(program));
- }
- }
+ public void domainObjectRestored(Program program) {
+ providers.stream().forEach(p -> p.programRestored(program));
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonPanel.java
index 939d9dc7f3..22b7f3c0e9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonPanel.java
@@ -15,6 +15,8 @@
*/
package ghidra.app.plugin.core.functioncompare;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
@@ -23,11 +25,11 @@ import java.util.Set;
import javax.swing.*;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.services.FunctionComparisonModel;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
+import ghidra.util.datastruct.Duo.Side;
import help.Help;
import help.HelpService;
@@ -67,7 +69,7 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
* @param tool the active plugin tool
*/
public MultiFunctionComparisonPanel(MultiFunctionComparisonProvider provider, PluginTool tool) {
- super(provider, tool, null, null);
+ super(provider, tool);
JPanel choicePanel = new JPanel(new GridLayout(1, 2));
choicePanel.add(createSourcePanel());
@@ -77,7 +79,7 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
// For the multi-panels we don't need to show the title of each
// comparison panel because the name of the function/data being shown
// is already visible in the combo box
- getComparisonPanels().forEach(p -> p.setShowTitles(false));
+ getComparisonPanels().forEach(p -> p.setShowDataTitles(false));
setPreferredSize(new Dimension(1200, 600));
}
@@ -99,7 +101,6 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
// Fire a notification to update the UI state; without this the
// actions would not be properly enabled/disabled
tool.contextChanged(provider);
- tool.setStatusInfo("function comparisons updated");
}
/**
@@ -108,10 +109,14 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
* @return the focused component
*/
public JComboBox getFocusedComponent() {
- CodeComparisonPanel extends FieldPanelCoordinator> currentComponent =
- getCurrentComponent();
- boolean sourceHasFocus = currentComponent.leftPanelHasFocus();
- return sourceHasFocus ? sourceFunctionsCB : targetFunctionsCB;
+ CodeComparisonPanel currentComponent = getCurrentComponent();
+ Side side = currentComponent.getActiveSide();
+ return side == LEFT ? sourceFunctionsCB : targetFunctionsCB;
+ }
+
+ public Side getFocusedSide() {
+ CodeComparisonPanel currentComponent = getCurrentComponent();
+ return currentComponent.getActiveSide();
}
/**
@@ -260,9 +265,6 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
return;
}
- Function selected = (Function) sourceFunctionsCBModel.getSelectedItem();
- loadFunctions(selected, null);
-
// Each time a source function is selected we need
// to load the targets associated with it
reloadTargetList((Function) sourceFunctionsCBModel.getSelectedItem());
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/NavigateToFunctionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/NavigateToFunctionAction.java
index 6b2b3168d0..0efece36a5 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/NavigateToFunctionAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/NavigateToFunctionAction.java
@@ -15,21 +15,27 @@
*/
package ghidra.app.plugin.core.functioncompare.actions;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import java.awt.event.*;
import java.util.List;
import javax.swing.Icon;
+import javax.swing.JComboBox;
+import docking.ActionContext;
import docking.action.ToggleDockingAction;
import docking.action.ToolBarData;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
-import ghidra.app.plugin.core.functioncompare.*;
+import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonPanel;
+import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonProvider;
import ghidra.app.services.GoToService;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
-import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
import ghidra.util.HTMLUtilities;
import ghidra.util.HelpLocation;
+import ghidra.util.datastruct.Duo.Side;
import resources.Icons;
/**
@@ -50,6 +56,8 @@ public class NavigateToFunctionAction extends ToggleDockingAction {
private static final Icon NAV_FUNCTION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
+ private MultiFunctionComparisonPanel comparisonPanel;
+
/**
* Constructor
*
@@ -57,6 +65,7 @@ public class NavigateToFunctionAction extends ToggleDockingAction {
*/
public NavigateToFunctionAction(MultiFunctionComparisonProvider provider) {
super("Navigate To Selected Function", provider.getName());
+ comparisonPanel = (MultiFunctionComparisonPanel) provider.getComponent();
goToService = provider.getTool().getService(GoToService.class);
@@ -70,8 +79,15 @@ public class NavigateToFunctionAction extends ToggleDockingAction {
setHelpLocation(
new HelpLocation(MultiFunctionComparisonPanel.HELP_TOPIC, "Navigate_To_Function"));
- addFocusListeners(provider);
- addChangeListeners(provider);
+ addFocusListeners();
+ addChangeListeners();
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+ JComboBox combo = comparisonPanel.getFocusedComponent();
+ Function f = (Function) combo.getSelectedItem();
+ goToService.goTo(f.getEntryPoint(), f.getProgram());
}
/**
@@ -79,108 +95,75 @@ public class NavigateToFunctionAction extends ToggleDockingAction {
* comparison provider. When a new function is selected, a GoTo event
* is generated for the entry point of the function.
*
- * @param provider the function comparison provider
*/
- private void addChangeListeners(MultiFunctionComparisonProvider provider) {
- MultiFunctionComparisonPanel panel = (MultiFunctionComparisonPanel) provider.getComponent();
+ private void addChangeListeners() {
+ JComboBox sourceCombo = comparisonPanel.getSourceComponent();
+ JComboBox targetCombo = comparisonPanel.getTargetComponent();
+ sourceCombo.addItemListener(new PanelItemListener(LEFT));
+ targetCombo.addItemListener(new PanelItemListener(RIGHT));
- panel.getSourceComponent().addItemListener(new ItemListener() {
- @Override
- public void itemStateChanged(ItemEvent e) {
- if (e.getStateChange() != ItemEvent.SELECTED) {
- return;
- }
-
- if (panel.getFocusedComponent() != panel.getSourceComponent()) {
- return;
- }
-
- if (NavigateToFunctionAction.this.isSelected()) {
- Function f = (Function) panel.getSourceComponent().getSelectedItem();
- goToService.goTo(f.getEntryPoint(), f.getProgram());
- }
- }
- });
-
- panel.getTargetComponent().addItemListener(new ItemListener() {
- @Override
- public void itemStateChanged(ItemEvent e) {
- if (e.getStateChange() != ItemEvent.SELECTED) {
- return;
- }
-
- if (panel.getFocusedComponent() != panel.getTargetComponent()) {
- return;
- }
-
- if (NavigateToFunctionAction.this.isSelected()) {
- Function f = (Function) panel.getTargetComponent().getSelectedItem();
- goToService.goTo(f.getEntryPoint(), f.getProgram());
- }
- }
- });
}
/**
* Adds a listener to each panel in the function comparison provider,
* triggered when focus has been changed. If focused is gained in a panel,
* a GoTo event is issued containing the function start address.
- *
- * @param provider the function comparison provider
*/
- private void addFocusListeners(MultiFunctionComparisonProvider provider) {
+ private void addFocusListeners() {
+ List panels = comparisonPanel.getComparisonPanels();
- FunctionComparisonPanel mainPanel = provider.getComponent();
- List> panels =
- mainPanel.getComparisonPanels();
+ for (CodeComparisonPanel panel : panels) {
+ panel.getComparisonComponent(LEFT)
+ .addFocusListener(new PanelFocusListener(panel, Side.LEFT));
+ panel.getComparisonComponent(RIGHT)
+ .addFocusListener(new PanelFocusListener(panel, Side.RIGHT));
+ }
+ }
- for (CodeComparisonPanel extends FieldPanelCoordinator> panel : panels) {
+ private class PanelItemListener implements ItemListener {
+ private Side side;
- panel.getRightFieldPanel().addFocusListener(new FocusAdapter() {
+ PanelItemListener(Side side) {
+ this.side = side;
+ }
- @Override
- public void focusGained(FocusEvent e) {
- if (NavigateToFunctionAction.this.isSelected()) {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() != ItemEvent.SELECTED) {
+ return;
+ }
+ if (comparisonPanel.getFocusedSide() != side) {
+ return;
+ }
- Address addr = null;
+ if (isSelected()) {
+ JComboBox> combo = (JComboBox>) e.getSource();
+ Function f = (Function) combo.getSelectedItem();
+ goToService.goTo(f.getEntryPoint(), f.getProgram());
+ }
+ }
- if (panel.getRightFunction() != null) {
- addr = panel.getRightFunction().getBody().getMinAddress();
- }
- else if (panel.getRightData() != null) {
- addr = panel.getRightData().getAddress();
- }
- else if (panel.getRightAddresses() != null) {
- addr = panel.getRightAddresses().getMinAddress();
- }
+ }
- goToService.goTo(addr, panel.getRightProgram());
- }
- }
- });
+ private class PanelFocusListener extends FocusAdapter {
+ private CodeComparisonPanel panel;
+ private Side side;
- panel.getLeftFieldPanel().addFocusListener(new FocusAdapter() {
+ PanelFocusListener(CodeComparisonPanel panel, Side side) {
+ this.panel = panel;
+ this.side = side;
+ }
- @Override
- public void focusGained(FocusEvent e) {
- if (NavigateToFunctionAction.this.isSelected()) {
- Address addr = null;
-
- if (panel.getLeftFunction() != null) {
- addr = panel.getLeftFunction().getBody().getMinAddress();
- }
- else if (panel.getLeftData() != null) {
- addr = panel.getLeftData().getAddress();
- }
- else if (panel.getLeftAddresses() != null) {
- addr = panel.getLeftAddresses().getMinAddress();
- }
-
- goToService.goTo(addr, panel.getLeftProgram());
- }
- }
-
- });
+ @Override
+ public void focusGained(FocusEvent e) {
+ if (!isSelected()) {
+ return;
+ }
+ Program program = panel.getProgram(side);
+ AddressSetView addresses = panel.getAddresses(side);
+ if (program != null && addresses != null && !addresses.isEmpty()) {
+ goToService.goTo(addresses.getMinAddress(), program);
+ }
}
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java
index 426719096f..170cfc6635 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java
@@ -107,6 +107,11 @@ public class MarkerManager implements MarkerService {
Gui.addThemeListener(themeListener);
}
+ public void clearAll() {
+ programMarkersByGroup.clear();
+ markerSetCache.clear();
+ }
+
private void themeChanged(ThemeEvent e) {
if (e instanceof ColorChangedThemeEvent) {
markerSetCache.clearColors();
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FieldHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FieldHeader.java
index 1c0c58e06b..51febf1049 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FieldHeader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FieldHeader.java
@@ -40,15 +40,6 @@ public class FieldHeader extends JTabbedPane implements ChangeListener {
private FormatManager formatManager;
private FormatModelListener formatListener = new FormatModelListener() {
- @Override
- public void formatModelAdded(FieldFormatModel formatModel) {
- createTabs();
- }
-
- @Override
- public void formatModelRemoved(FieldFormatModel formatModel) {
- createTabs();
- }
@Override
public void formatModelChanged(FieldFormatModel formatModel) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java
index ff085e412a..4e3e47e354 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java
@@ -159,7 +159,9 @@ public class FormatManager implements OptionsChangeListener {
* @param listener the listener to be added
*/
public void addFormatModelListener(FormatModelListener listener) {
- formatListeners.add(listener);
+ if (listener != null) {
+ formatListeners.add(listener);
+ }
}
/**
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatModelListener.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatModelListener.java
index fb1ef9a745..cbbfdcc2a7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatModelListener.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatModelListener.java
@@ -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.
@@ -20,16 +19,26 @@ package ghidra.app.util.viewer.format;
* Interface for listeners to format model changes.
*/
public interface FormatModelListener {
+
/**
- * Notifies that a new format model was added to the format manager.
- * @param model the new model.
+ * Format model added. Not used.
+ * @param model the model that was added
+ * @deprecated not used
*/
- void formatModelAdded(FieldFormatModel model);
+ @Deprecated(since = "11.2", forRemoval = true)
+ default void formatModelAdded(FieldFormatModel model) {
+ // not used
+ }
+
/**
- * Notifies that a format model was removed.
- * @param model the model that was removed.
+ * Format model removed. Not used.
+ * @param model the model that was added
+ * @deprecated not used
*/
- void formatModelRemoved(FieldFormatModel model);
+ @Deprecated(since = "11.2", forRemoval = true)
+ default void formatModelRemoved(FieldFormatModel model) {
+ // not used
+ }
/**
* Notifies that the given format model was changed.
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingActionContext.java
index b1e61b956f..a8a3e6287c 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingActionContext.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingActionContext.java
@@ -16,57 +16,32 @@
package ghidra.app.util.viewer.listingpanel;
import docking.ComponentProvider;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.util.viewer.util.CodeComparisonActionContext;
-import ghidra.app.util.viewer.util.CodeComparisonPanel;
-import ghidra.program.model.listing.Function;
-// Note: If you want to get the typical actions for things like comments, labels, bookmarks, etc.
-// that are available in the CodeBrowser then change this to extend ListingActionContext.
-// This currently extends NavigatableActionContext so that it does NOT get the typical
-// CodeBrowser Listing actions.
/**
* Action context for a ListingCodeComparisonPanel.
*/
public class DualListingActionContext extends CodeComparisonActionContext {
- private CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel = null;
+ private ListingCodeComparisonPanel codeComparisonPanel = null;
/**
* Constructor for a dual listing's action context.
* @param provider the provider that uses this action context.
+ * @param panel the ListingCodeComparisonPanel that generated this context
*/
- public DualListingActionContext(ComponentProvider provider) {
- super(provider);
+ public DualListingActionContext(ComponentProvider provider, ListingCodeComparisonPanel panel) {
+ super(provider, panel, panel.getActiveListingPanel().getFieldPanel());
+ this.codeComparisonPanel = panel;
}
/**
- * Sets the CodeComparisonPanel associated with this context.
- * @param codeComparisonPanel the code comparison panel
+ * Returns the {@link ListingCodeComparisonPanel} that generated this context
+ * @return the listing comparison panel that generated this context
*/
- public void setCodeComparisonPanel(
- CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel) {
- this.codeComparisonPanel = codeComparisonPanel;
- }
-
@Override
- public CodeComparisonPanel extends FieldPanelCoordinator> getCodeComparisonPanel() {
+ public ListingCodeComparisonPanel getCodeComparisonPanel() {
return codeComparisonPanel;
}
- @Override
- public Function getSourceFunction() {
- boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
-
- return leftHasFocus ? codeComparisonPanel.getRightFunction()
- : codeComparisonPanel.getLeftFunction();
- }
-
- @Override
- public Function getTargetFunction() {
- boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
-
- return leftHasFocus ? codeComparisonPanel.getLeftFunction()
- : codeComparisonPanel.getRightFunction();
- }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingFieldPanelCoordinator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingFieldPanelCoordinator.java
deleted file mode 100644
index 139d062add..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingFieldPanelCoordinator.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.util.viewer.listingpanel;
-
-import ghidra.program.util.ProgramLocation;
-import docking.widgets.fieldpanel.listener.ViewListener;
-
-/**
- * Coordinates the locations between the left and right sides of a dual listing panel.
- */
-public interface DualListingFieldPanelCoordinator extends ViewListener {
-
- /**
- * Method that gets called when the location changes in the left side's program listing.
- * @param leftLocation the new location in the left side.
- */
- public void leftLocationChanged(ProgramLocation leftLocation);
-
- /**
- * Method that gets called when the location changes in the right side's program listing.
- * @param rightLocation the new location in the right side.
- */
- public void rightLocationChanged(ProgramLocation rightLocation);
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingGoToService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingGoToService.java
index de2aa3b298..51cb42ade0 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingGoToService.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingGoToService.java
@@ -22,6 +22,7 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.ExternalLocation;
import ghidra.program.util.ProgramLocation;
+import ghidra.util.datastruct.Duo.Side;
import ghidra.util.task.TaskMonitor;
/**
@@ -32,7 +33,7 @@ import ghidra.util.task.TaskMonitor;
class DualListingGoToService implements GoToService {
private ListingCodeComparisonPanel dualListing;
- private boolean isLeftSide;
+ private Side side;
private GoToOverrideService overrideService;
private GoToService goToService;
@@ -41,13 +42,13 @@ class DualListingGoToService implements GoToService {
* @param goToService the GoToService that this overrides and that can be used when the
* GoToService methods don't pertain specifically to the left or right listing panel.
* @param dualListing the dual listing panel
- * @param isLeftSide true means this GoToService is for the left listing panel of the dual listing.
+ * @param side the LEFT or RIGHT side
*/
DualListingGoToService(GoToService goToService, ListingCodeComparisonPanel dualListing,
- boolean isLeftSide) {
+ Side side) {
this.goToService = goToService;
this.dualListing = dualListing;
- this.isLeftSide = isLeftSide;
+ this.side = side;
}
@Override
@@ -92,8 +93,7 @@ class DualListingGoToService implements GoToService {
if (addr == null) {
return false;
}
- AddressSetView addresses =
- isLeftSide ? dualListing.getLeftAddresses() : dualListing.getRightAddresses();
+ AddressSetView addresses = dualListing.getAddresses(side);
if (!addresses.contains(addr)) {
dualListing.setStatusInfo(
"\"" + addr.toString() + "\" is outside the current listing's view.");
@@ -112,8 +112,7 @@ class DualListingGoToService implements GoToService {
return false;
}
- ListingPanel listingPanel =
- (isLeftSide) ? dualListing.getLeftPanel() : dualListing.getRightPanel();
+ ListingPanel listingPanel = dualListing.getListingPanel(side);
return listingPanel.goTo(loc);
}
@@ -124,8 +123,7 @@ class DualListingGoToService implements GoToService {
return false;
}
- ListingPanel listingPanel =
- (isLeftSide) ? dualListing.getLeftPanel() : dualListing.getRightPanel();
+ ListingPanel listingPanel = dualListing.getListingPanel(side);
return listingPanel.goTo(addr);
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingNavigator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingNavigator.java
index 5a585056ba..adf74a6757 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingNavigator.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingNavigator.java
@@ -36,15 +36,14 @@ class DualListingNavigator implements Navigatable {
/**
* Constructor for a dual listing navigator.
- * @param dualListingPanel the dual listing whose left or right listing panel is to be controlled.
- * @param isLeftSide true indicates that this navigator is for the left side listing.
+ * @param listingPanel the dual listing whose left or right listing panel is to be controlled.
+ * @param goToService which side LEFT or RIGHT
* false means it's for the right side listing.
*/
- DualListingNavigator(ListingCodeComparisonPanel dualListingPanel, boolean isLeftSide) {
+ DualListingNavigator(ListingPanel listingPanel, GoToService goToService) {
- this.listingPanel =
- isLeftSide ? dualListingPanel.getLeftPanel() : dualListingPanel.getRightPanel();
- this.goToService = dualListingPanel.getGoToService(isLeftSide);
+ this.listingPanel = listingPanel;
+ this.goToService = goToService;
id = UniversalIdGenerator.nextID().getValue();
}
@@ -152,7 +151,8 @@ class DualListingNavigator implements Navigatable {
}
@Override
- public void removeHighlightProvider(ListingHighlightProvider highlightProvider, Program program) {
+ public void removeHighlightProvider(ListingHighlightProvider highlightProvider,
+ Program program) {
// currently unsupported
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingServiceProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingServiceProvider.java
index c93b6e24e8..4a80ad1891 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingServiceProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/DualListingServiceProvider.java
@@ -18,6 +18,7 @@ package ghidra.app.util.viewer.listingpanel;
import ghidra.app.services.GoToService;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.util.ServiceListener;
+import ghidra.util.datastruct.Duo.Side;
/**
* This provides services, but overrides and implements its own goTo for one of the listing
@@ -27,22 +28,21 @@ import ghidra.framework.plugintool.util.ServiceListener;
* the dual listing code comparison panel.
*/
class DualListingServiceProvider implements ServiceProvider {
-
+
private ServiceProvider serviceProvider;
private DualListingGoToService dualListingGoToService;
-
+
/**
* Constructor for a DualListingServiceProvider.
* @param serviceProvider the service provider to use for acquiring services other than goTo.
* @param panel the dual listing code comparison panel.
- * @param isLeftPanel true indicates this is the left listing panel of the dual panel.
- * false indicates the right panel.
+ * @param side LEFT or RIGHT
*/
DualListingServiceProvider(ServiceProvider serviceProvider, ListingCodeComparisonPanel panel,
- boolean isLeftPanel) {
+ Side side) {
this.serviceProvider = serviceProvider;
GoToService goToService = serviceProvider.getService(GoToService.class);
- this.dualListingGoToService = new DualListingGoToService(goToService, panel, isLeftPanel);
+ this.dualListingGoToService = new DualListingGoToService(goToService, panel, side);
}
@Override
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/LinearAddressCorrelation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/LinearAddressCorrelation.java
new file mode 100644
index 0000000000..21c1d823ba
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/LinearAddressCorrelation.java
@@ -0,0 +1,96 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.listingpanel;
+
+import static ghidra.util.datastruct.Duo.Side.*;
+
+import ghidra.app.util.viewer.util.ComparisonData;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.*;
+import ghidra.program.util.ListingAddressCorrelation;
+import ghidra.util.datastruct.Duo;
+import ghidra.util.datastruct.Duo.Side;
+
+/**
+ * Creates an address correlation with a simplistic correlation where each address correlates based
+ * on an offset from the address set's minimum address.
+ */
+public class LinearAddressCorrelation implements ListingAddressCorrelation {
+ private Duo comparisonData;
+
+ public LinearAddressCorrelation(Duo comparisonData) {
+ this.comparisonData = comparisonData;
+ }
+
+ @Override
+ public Program getProgram(Side side) {
+ return comparisonData.get(LEFT).getProgram();
+ }
+
+ @Override
+ public AddressSetView getAddresses(Side side) {
+ return comparisonData.get(LEFT).getAddressSet();
+ }
+
+ @Override
+ public Function getFunction(Side side) {
+ return null;
+ }
+
+ @Override
+ public Address getAddress(Side side, Address otherAddress) {
+ Side otherSide = side.otherSide();
+ if (!isValidAddress(otherSide, otherAddress) || !isCodeUnitStart(otherSide, otherAddress)) {
+ return null;
+ }
+ AddressSetView otherSet = comparisonData.get(otherSide).getAddressSet();
+ Address minOtherAddress = otherSet.getMinAddress();
+ long offset = otherAddress.subtract(minOtherAddress);
+ Address minAddress = comparisonData.get(side).getAddressSet().getMinAddress();
+ Address address = minAddress.addWrap(offset);
+ if (!isValidAddress(side, address)) {
+ return null;
+ }
+ return normalizeToCodeUnitStart(side, address);
+ }
+
+ private boolean isValidAddress(Side side, Address address) {
+ AddressSetView addresses = comparisonData.get(side).getAddressSet();
+ return addresses.contains(address);
+ }
+
+ private boolean isCodeUnitStart(Side side, Address address) {
+ Listing listing = getListing(side);
+ CodeUnit cu = listing.getCodeUnitAt(address);
+ return cu != null;
+ }
+
+ private Address normalizeToCodeUnitStart(Side side, Address address) {
+ Listing listing = getListing(side);
+ CodeUnit cu = listing.getCodeUnitContaining(address);
+ Address minAddress = cu.getMinAddress();
+ if (isValidAddress(side, minAddress)) {
+ return minAddress;
+ }
+ return null;
+ }
+
+ private Listing getListing(Side side) {
+ return comparisonData.get(side).getProgram().getListing();
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java
index f7350c454b..0bcbdbaa7f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCodeComparisonPanel.java
@@ -15,79 +15,56 @@
*/
package ghidra.app.util.viewer.listingpanel;
-import java.awt.*;
-import java.awt.event.*;
+import static ghidra.util.datastruct.Duo.Side.*;
+
+import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
-import javax.swing.*;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
+import javax.swing.Icon;
+import javax.swing.JComponent;
import docking.*;
-import docking.action.*;
-import docking.menu.ActionState;
+import docking.action.DockingAction;
+import docking.action.ToggleDockingAction;
+import docking.action.builder.*;
import docking.menu.MultiStateDockingAction;
import docking.options.OptionsService;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.FieldPanel;
-import docking.widgets.fieldpanel.field.Field;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
-import docking.widgets.fieldpanel.listener.FieldLocationListener;
-import docking.widgets.fieldpanel.support.FieldLocation;
-import docking.widgets.fieldpanel.support.ViewerPosition;
import generic.theme.GIcon;
-import generic.theme.GThemeDefaults.Colors.Palette;
-import ghidra.GhidraOptions;
-import ghidra.app.nav.Navigatable;
-import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
-import ghidra.app.plugin.core.codebrowser.hover.*;
import ghidra.app.plugin.core.functioncompare.actions.*;
-import ghidra.app.plugin.core.marker.MarkerManager;
-import ghidra.app.services.*;
import ghidra.app.util.ListingHighlightProvider;
-import ghidra.app.util.SymbolPath;
import ghidra.app.util.viewer.format.*;
-import ghidra.app.util.viewer.util.*;
+import ghidra.app.util.viewer.util.CodeComparisonPanel;
+import ghidra.app.util.viewer.util.CodeComparisonPanelActionContext;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginTool;
-import ghidra.framework.plugintool.ServiceProviderDecorator;
import ghidra.program.model.address.*;
import ghidra.program.model.correlate.HashedFunctionAddressCorrelation;
-import ghidra.program.model.listing.*;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
-import ghidra.program.model.mem.MemoryBlock;
-import ghidra.program.model.symbol.*;
import ghidra.program.util.*;
-import ghidra.util.*;
+import ghidra.util.HelpLocation;
+import ghidra.util.Msg;
+import ghidra.util.datastruct.Duo;
+import ghidra.util.datastruct.Duo.Side;
+import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import help.Help;
-import help.HelpService;
/**
* Panel that displays two listings for comparison.
*/
public class ListingCodeComparisonPanel
- extends CodeComparisonPanel implements
- FormatModelListener, CodeFormatService, ListingDiffChangeListener, OptionsChangeListener {
-
- private static final Color FG_COLOR_TITLE = Palette.DARK_GRAY;
-
- private static final String DUAL_LISTING_HEADER_SHOWING = "DUAL_LISTING_HEADER_SHOWING";
- private static final String DUAL_LISTING_SIDE_BY_SIDE = "DUAL_LISTING_SIDE_BY_SIDE";
- public static final String NAME = "DualListing";
- public static final String TITLE = "Listing View";
-
- protected static final HelpService help = Help.getHelpService();
-
- private static final String DUAL_LISTING_HELP_TOPIC = "FunctionComparison";
- private static final String DUAL_LISTING_ACTION_GROUP = NAME;
+ extends CodeComparisonPanel implements
+ FormatModelListener, OptionsChangeListener {
+ public static final String NAME = "Listing View";
private static final String DIFF_NAVIGATE_GROUP = "A2_DiffNavigate";
- private static final String HOVER_GROUP = "A5_Hovers";
- private static final String PROPERTIES_GROUP = "B1_Properties";
//@formatter:off
private static final Icon NEXT_DIFF_ICON = new GIcon("icon.base.util.listingcompare.diff.next");
@@ -95,67 +72,33 @@ public class ListingCodeComparisonPanel
private static final Icon BOTH_VIEWS_ICON = new GIcon("icon.base.util.listingcompare.area.markers.all");
private static final Icon UNMATCHED_ICON = new GIcon("icon.base.util.listingcompare.area.markers.unmatched");
private static final Icon DIFF_ICON = new GIcon("icon.base.util.listingcompare.area.markers.diff");
- private static final Icon CURSOR_LOC_ICON = new GIcon("icon.base.util.listingcompare.cursor");
- //@formatter:on
-
private static final Icon HOVER_ON_ICON = new GIcon("icon.base.util.listingcompare.hover.on");
private static final Icon HOVER_OFF_ICON = new GIcon("icon.base.util.listingcompare.hover.off");
+ //@formatter:on
- private static final String ALL_AREA_MARKERS = "All Area Markers";
- private static final String UNMATCHED_AREA_MARKERS = "Unmatched Area Markers";
- private static final String DIFF_AREA_MARKERS = "Diff Area Markers";
- private String nextPreviousAreaType;
+ private enum NavigateType {
+ ALL, UNMATCHED, DIFF
+ }
- private ListingPanel[] listingPanels = new ListingPanel[2];
- private ListingDiff listingDiff;
- private ListingDiffActionManager diffActionManager;
- private DualListingServiceProvider[] dualListingServiceProviders =
- new DualListingServiceProvider[2];
- private DualListingNavigator[] navigatables = new DualListingNavigator[2];
- private FieldNavigator[] fieldNavigators = new FieldNavigator[2];
- private AddressIndexMap[] indexMaps = new AddressIndexMap[2];
- private AddressSetView[] addressSets =
- new AddressSetView[] { EMPTY_ADDRESS_SET, EMPTY_ADDRESS_SET };
- private MarkerManager[] markerManagers = new MarkerManager[2];
- private MarkerSet[] unmatchedCodeMarkers = new MarkerSet[2];
- private MarkerSet[] diffMarkers = new MarkerSet[2];
- private MarkerSet[] currentCursorMarkers = new MarkerSet[2];
- private static final Color CURSOR_LINE_COLOR = GhidraOptions.DEFAULT_CURSOR_LINE_COLOR;
- private Color cursorHighlightColor;
- private boolean isShowingEntireListing;
- private boolean isSideBySide = true;
- private boolean fieldLocationChanging = false;
- private LeftLocationListener leftLocationListener;
- private RightLocationListener rightLocationListener;
- private ToggleHeaderAction toggleHeaderAction;
- private ToggleOrientationAction toggleOrientationAction;
- private ToggleHoverAction toggleHoverAction;
- private NextPreviousAreaMarkerAction nextPreviousAreaMarkerAction;
- private NextDiffAction nextDiffAction;
- private PreviousDiffAction previousDiffAction;
- private ListingCodeComparisonOptionsAction optionsAction;
- private DockingAction[] diffActions;
- private FunctionNameApplyAction applyNameAction;
- private EmptySignatureApplyAction applySignatureEmptyDatatypesAction;
- private SignatureWithDatatypesApplyAction applySignatureWithDatatypesAction;
- private JSplitPane splitPane;
-
- private ListingDiffHighlightProvider leftDiffHighlightProvider;
- private ListingDiffHighlightProvider rightDiffHighlightProvider;
-
- private FunctionAddressCorrelation correlator;
-
- private boolean adjustingLeftLocation = false;
- private boolean adjustingRightLocation = false;
-
- ReferenceListingHover referenceHoverService;
- DataTypeListingHover dataTypeHoverService;
- TruncatedTextListingHover truncatedTextHoverService;
- FunctionNameListingHover functionNameHoverService;
- private String leftTitle;
- private String rightTitle;
private ListingCodeComparisonOptions comparisonOptions;
- private Address[] coordinatorLockedAddresses;
+
+ private Duo displays;
+
+ private ListingAddressCorrelation addressCorrelator;
+ private ListingDiff listingDiff;
+ private ListingCoordinator coordinator;
+ private boolean listingsLocked;
+
+ private ListingDiffActionManager diffActionManager;
+ private DockingAction nextDiffAction;
+ private DockingAction previousDiffAction;
+ private DockingAction optionsAction;
+ private DockingAction applyFunctionNameAction;
+ private DockingAction applyEmptySignatureAction;
+ private DockingAction applySignatureAction;
+ private ToggleDockingAction toggleHeaderAction;
+ private ToggleDockingAction toggleHoverAction;
+ private MultiStateDockingAction nextPreviousAreaTypeAction;
/**
* Creates a comparison panel with two listings.
@@ -165,32 +108,45 @@ public class ListingCodeComparisonPanel
*/
public ListingCodeComparisonPanel(String owner, PluginTool tool) {
super(owner, tool);
- initialize();
- }
-
- private void initialize() {
- listingDiff = new ListingDiff();
- diffActionManager = new ListingDiffActionManager(listingDiff);
- initializeGoToServiceProviders(); // Must be before other buildPanel() and initialize methods.
- buildPanel();
- initializeListingFieldPanels();
- initializeListingFieldNavigation();
- initializeListingHoverService();
- setupMarkerManagers();
- createActions();
- listingDiff.addListingDiffChangeListener(this);
- setScrollingSyncState(true);
- help.registerHelp(this, new HelpLocation(DUAL_LISTING_HELP_TOPIC, "Dual Listing"));
-
- comparisonOptions = new ListingCodeComparisonOptions();
+ Help.getHelpService().registerHelp(this, new HelpLocation(HELP_TOPIC, "Dual Listing"));
initializeOptions();
+
+ listingDiff = buildListingDiff();
+ displays = buildListingDisplays();
+ buildPanel();
+ createActions();
+
+ setSynchronizedScrolling(true);
}
- private void initializeOptions() {
- ToolOptions options = tool.getOptions(ListingCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
- options.addOptionsChangeListener(this);
- comparisonOptions.initializeOptions(options);
- comparisonOptions.loadOptions(options);
+ private ListingDiff buildListingDiff() {
+ ListingDiff diff = new ListingDiff();
+ diffActionManager = new ListingDiffActionManager(diff);
+ return diff;
+ }
+
+ private Duo buildListingDisplays() {
+ ListingDisplay leftDisplay =
+ new ListingDisplay(tool, owner, listingDiff, comparisonOptions, LEFT);
+ ListingDisplay rightDisplay =
+ new ListingDisplay(tool, owner, listingDiff, comparisonOptions, RIGHT);
+
+ // make the right format manager always be the same as the left format manager
+ leftDisplay.getFormatManager().addFormatModelListener(this);
+
+ leftDisplay.setProgramLocationListener((l, t) -> programLocationChanged(LEFT, l, t));
+ rightDisplay.setProgramLocationListener((l, t) -> programLocationChanged(RIGHT, l, t));
+
+ return new Duo<>(leftDisplay, rightDisplay);
+ }
+
+ @Override
+ public JComponent getComparisonComponent(Side side) {
+ return displays.get(side).getListingPanel();
+ }
+
+ public ListingPanel getListingPanel(Side side) {
+ return displays.get(side).getListingPanel();
}
@Override
@@ -198,113 +154,20 @@ public class ListingCodeComparisonPanel
Object newValue) {
comparisonOptions.loadOptions(options);
-
- repaint(); // Refresh the highlights. (byte, mnemonic, & operand)
-
- if (programs[LEFT] == null) {
- // not data is showing; no widgets to update
- return;
- }
-
- // Refresh the area markers for Diff Code Units and Unmatched Code Units.
- Color unmatchedCodeUnitsBackgroundColor =
- comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
- unmatchedCodeMarkers[LEFT].setMarkerColor(unmatchedCodeUnitsBackgroundColor);
- unmatchedCodeMarkers[RIGHT].setMarkerColor(unmatchedCodeUnitsBackgroundColor);
- Color diffCodeUnitsBackgroundColor = comparisonOptions.getDiffCodeUnitsBackgroundColor();
- diffMarkers[LEFT].setMarkerColor(diffCodeUnitsBackgroundColor);
- diffMarkers[RIGHT].setMarkerColor(diffCodeUnitsBackgroundColor);
+ updateProgramViews();
}
@Override
- public JComponent getComponent() {
- return this;
+ public String getName() {
+ return NAME;
}
@Override
- public String getTitle() {
- return TITLE;
- }
-
- @Override
- public void setVisible(boolean aFlag) {
- super.setVisible(aFlag);
+ public void setVisible(boolean b) {
+ super.setVisible(b);
updateActionEnablement();
}
- private FormatManager createFormatManager(int leftOrRight) {
- ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
- ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
-
- FormatManager formatManager = new FormatManager(displayOptions, fieldOptions);
-
- ServiceProviderDecorator sp = ServiceProviderDecorator.createEmptyDecorator();
- sp.overrideService(GoToService.class,
- dualListingServiceProviders[leftOrRight].getService(GoToService.class));
- formatManager.setServiceProvider(sp);
-
- //
- // Unusual Code Alert!
- // In a normal tool, this option is registered by the Code Browse Plugin. In the VT
- // tool, nobody registers this option. Our system logs a warning if an option is used
- // but not registered. So, when in a real tool, use the registered/managed option.
- // Otherwise, just use the default.
- //
- if (fieldOptions.isRegistered(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)) {
- cursorHighlightColor =
- fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR, CURSOR_LINE_COLOR);
- }
- else {
- cursorHighlightColor = CURSOR_LINE_COLOR;
- }
-
- return formatManager;
- }
-
- private void initializeGoToServiceProviders() {
- dualListingServiceProviders[LEFT] = new DualListingServiceProvider(tool, this, true);
- dualListingServiceProviders[RIGHT] = new DualListingServiceProvider(tool, this, false);
- }
-
- private void initializeListingFieldPanels() {
- FieldPanel[] fieldPanels = new FieldPanel[2];
- for (int i = 0; i < 2; i++) {
- fieldPanels[i] = listingPanels[i].getFieldPanel();
- fieldPanels[i].addFocusListener(this);
- fieldPanels[i].addMouseListener(new DualListingMouseListener(i));
- }
-
- leftLocationListener = new LeftLocationListener();
- rightLocationListener = new RightLocationListener();
- fieldPanels[LEFT].addFieldLocationListener(leftLocationListener);
- fieldPanels[RIGHT].addFieldLocationListener(rightLocationListener);
- }
-
- /**
- * Sets the coordinator for the two listings within this code comparison panel. It coordinates
- * their scrolling and location synchronization.
- *
- * @param listingFieldPanelCoordinator the coordinator for the two listings
- */
- @Override
- public void setFieldPanelCoordinator(
- ListingComparisonFieldPanelCoordinator listingFieldPanelCoordinator) {
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator = getFieldPanelCoordinator();
- if (fieldPanelCoordinator == listingFieldPanelCoordinator) {
- return;
- }
-
- super.setFieldPanelCoordinator(listingFieldPanelCoordinator);
-
- if (listingFieldPanelCoordinator != null) {
- ListingPanel focusedListingPanel = getFocusedListingPanel();
- ProgramLocation programLocation = focusedListingPanel.getProgramLocation();
- if (programLocation != null) {
- focusedListingPanel.goTo(programLocation);
- }
- }
- }
-
/**
* Adds the indicated highlight providers for the left and right listing panels.
*
@@ -313,16 +176,8 @@ public class ListingCodeComparisonPanel
*/
public void addHighlightProviders(ListingHighlightProvider leftHighlightProvider,
ListingHighlightProvider rightHighlightProvider) {
- addLeftHighlightProvider(leftHighlightProvider);
- addRightHighlightProvider(rightHighlightProvider);
- }
-
- private void addLeftHighlightProvider(ListingHighlightProvider leftHighlightProvider) {
- listingPanels[LEFT].getFormatManager().addHighlightProvider(leftHighlightProvider);
- }
-
- private void addRightHighlightProvider(ListingHighlightProvider rightHighlightProvider) {
- listingPanels[RIGHT].getFormatManager().addHighlightProvider(rightHighlightProvider);
+ displays.get(LEFT).addHighlightProvider(leftHighlightProvider);
+ displays.get(RIGHT).addHighlightProvider(rightHighlightProvider);
}
/**
@@ -333,153 +188,26 @@ public class ListingCodeComparisonPanel
*/
public void removeHighlightProviders(ListingHighlightProvider leftHighlightProvider,
ListingHighlightProvider rightHighlightProvider) {
- removeLeftHighlightProvider(leftHighlightProvider);
- removeRightHighlightProvider(rightHighlightProvider);
- }
-
- private void removeLeftHighlightProvider(ListingHighlightProvider leftHighlightProvider) {
- listingPanels[LEFT].getFormatManager().removeHighlightProvider(leftHighlightProvider);
- }
-
- private void removeRightHighlightProvider(ListingHighlightProvider rightHighlightProvider) {
- listingPanels[RIGHT].getFormatManager().removeHighlightProvider(rightHighlightProvider);
+ displays.get(LEFT).removeHighlightProvider(leftHighlightProvider);
+ displays.get(RIGHT).removeHighlightProvider(rightHighlightProvider);
}
@Override
- protected void setPrograms(Program leftProgram, Program rightProgram) {
- boolean programChanged = false;
- if (leftProgram != programs[LEFT]) {
- programs[LEFT] = leftProgram;
- listingPanels[LEFT].setProgram(leftProgram);
- addressSets[LEFT] = EMPTY_ADDRESS_SET;
- indexMaps[LEFT] = new AddressIndexMap(addressSets[LEFT]);
- updateLeftListingTitle();
- programChanged = true;
- }
- if (rightProgram != programs[RIGHT]) {
- programs[RIGHT] = rightProgram;
- listingPanels[RIGHT].setProgram(rightProgram);
- addressSets[RIGHT] = EMPTY_ADDRESS_SET;
- indexMaps[RIGHT] = new AddressIndexMap(addressSets[RIGHT]);
- updateRightListingTitle();
- programChanged = true;
- }
- setupAreaMarkerSets();
- setupCursorMarkerSets();
- if (programChanged) {
- showEntireListing(isShowingEntireListing);
- }
- }
+ public List getActions() {
+ List actions = super.getActions();
- private void updateLeftListingTitle() {
- titlePanels[LEFT].setTitleName(getLeftProgramName());
- }
+ actions.add(nextPreviousAreaTypeAction);
+ actions.add(toggleHeaderAction);
+ actions.add(toggleHoverAction);
+ actions.add(applyFunctionNameAction);
+ actions.add(applyEmptySignatureAction);
+ actions.add(applySignatureAction);
+ actions.add(nextDiffAction);
+ actions.add(previousDiffAction);
+ actions.add(optionsAction);
- private String getLeftProgramName() {
- String leftProgramName =
- (programs[LEFT] != null) ? programs[LEFT].getDomainFile().toString() : "none";
- return leftProgramName;
- }
+ actions.addAll(diffActionManager.getActions());
- private void updateRightListingTitle() {
- titlePanels[RIGHT].setTitleName(getRightProgramName());
- }
-
- private String getRightProgramName() {
- String rightProgramName =
- (programs[RIGHT] != null) ? programs[RIGHT].getDomainFile().toString() : "none";
- return rightProgramName;
- }
-
- private void initializeListingFieldNavigation() {
- initializeListingFieldNavigation(LEFT);
- initializeListingFieldNavigation(RIGHT);
- }
-
- private void initializeListingFieldNavigation(int leftOrRight) {
- boolean isLeftSide = (leftOrRight == LEFT);
- navigatables[leftOrRight] = new DualListingNavigator(this, isLeftSide);
- fieldNavigators[leftOrRight] =
- new FieldNavigator(dualListingServiceProviders[leftOrRight], navigatables[leftOrRight]);
- listingPanels[leftOrRight].addButtonPressedListener(fieldNavigators[leftOrRight]);
- }
-
- private Navigatable getFocusedNavigatable() {
- return navigatables[currProgramIndex];
- }
-
- private void initializeListingHoverService() {
-
- // The CodeFormatService is needed by the ReferenceHover.
- referenceHoverService = new ReferenceListingHover(tool, this);
- dataTypeHoverService = new DataTypeListingHover(tool);
- truncatedTextHoverService = new TruncatedTextListingHover(tool);
- functionNameHoverService = new FunctionNameListingHover(tool);
-
- initializeListingHoverService(LEFT);
- initializeListingHoverService(RIGHT);
- }
-
- private void initializeListingHoverService(int leftOrRight) {
- ListingPanel listingPanel = listingPanels[leftOrRight];
- listingPanel.addHoverService(referenceHoverService);
- listingPanel.addHoverService(dataTypeHoverService);
- listingPanel.addHoverService(truncatedTextHoverService);
- listingPanel.addHoverService(functionNameHoverService);
- listingPanel.setHoverMode(true);
- }
-
- /**
- * Sets a listener for program location changes for the left side's listing panel.
- *
- * @param programLocationListener the listener
- */
- public void setLeftProgramLocationListener(ProgramLocationListener programLocationListener) {
- listingPanels[LEFT].setProgramLocationListener(programLocationListener);
- }
-
- /**
- * Sets a listener for program location changes for the right side's listing panel.
- *
- * @param programLocationListener the listener
- */
- public void setRightProgramLocationListener(ProgramLocationListener programLocationListener) {
- listingPanels[RIGHT].setProgramLocationListener(programLocationListener);
- }
-
- private void createActions() {
- toggleHeaderAction = new ToggleHeaderAction();
- toggleOrientationAction = new ToggleOrientationAction();
- toggleHoverAction = new ToggleHoverAction();
- applyNameAction = new FunctionNameApplyAction(owner);
- applySignatureEmptyDatatypesAction = new EmptySignatureApplyAction(owner);
- applySignatureWithDatatypesAction = new SignatureWithDatatypesApplyAction(owner);
- nextDiffAction = new NextDiffAction();
- previousDiffAction = new PreviousDiffAction();
- optionsAction = new ListingCodeComparisonOptionsAction();
- // NextDiff and PreviousDiff must be created before the area marker action.
- nextPreviousAreaMarkerAction = new NextPreviousAreaMarkerAction(owner);
- diffActions = getListingDiffActions();
- }
-
- private DockingAction[] getListingDiffActions() {
- return diffActionManager.getActions();
- }
-
- @Override
- public DockingAction[] getActions() {
- DockingAction[] codeCompActions = super.getActions();
- DockingAction[] otherActions = new DockingAction[] { toggleHeaderAction,
- toggleOrientationAction, toggleHoverAction, applyNameAction,
- applySignatureEmptyDatatypesAction, applySignatureWithDatatypesAction,
- nextPreviousAreaMarkerAction, nextDiffAction, previousDiffAction, optionsAction };
- int compCount = codeCompActions.length;
- int otherCount = otherActions.length;
- int diffCount = diffActions.length;
- DockingAction[] actions = new DockingAction[compCount + otherCount + diffCount];
- System.arraycopy(codeCompActions, 0, actions, 0, compCount);
- System.arraycopy(otherActions, 0, actions, compCount, otherCount);
- System.arraycopy(diffActions, 0, actions, compCount + otherCount, diffCount);
return actions;
}
@@ -496,1596 +224,38 @@ public class ListingCodeComparisonPanel
diffActionManager.updateActionEnablement(listingDiffActionEnablement);
}
- class ToggleHeaderAction extends ToggleDockingAction {
- ToggleHeaderAction() {
- super("Dual Listing Toggle Header", owner);
- setDescription("Toggle Format Header");
- setEnabled(true);
- MenuData menuData = new MenuData(new String[] { "Show Listing Format Header" },
- DUAL_LISTING_ACTION_GROUP);
- setMenuBarData(menuData);
-
- setHelpLocation(
- new HelpLocation(DUAL_LISTING_HELP_TOPIC, "Dual Listing Toggle Format Header"));
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- ListingPanel listingPanel = getLeftPanel();
- boolean show = !listingPanel.isHeaderShowing();
- listingPanel.showHeader(show);
- listingPanel.validate();
- listingPanel.invalidate();
- }
- }
-
- class ToggleOrientationAction extends ToggleDockingAction {
- ToggleOrientationAction() {
- super("Dual Listing Toggle Orientation", owner);
- setDescription("Toggle the layout of the listings " +
- "
between side-by-side and one above the other.");
- setEnabled(true);
- setSelected(isSideBySide);
- MenuData menuData = new MenuData(new String[] { "Show Listings Side-by-Side" },
- DUAL_LISTING_ACTION_GROUP);
- setMenuBarData(menuData);
-
- setHelpLocation(
- new HelpLocation(DUAL_LISTING_HELP_TOPIC, "Dual Listing Toggle Orientation"));
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- boolean sideBySide = !isSideBySide();
- showSideBySide(sideBySide);
- }
- }
-
- class ToggleHoverAction extends ToggleDockingAction {
- ToggleHoverAction() {
- super("Dual Listing Toggle Mouse Hover Popups", owner);
- setEnabled(true);
- setToolBarData(new ToolBarData(HOVER_ON_ICON, HOVER_GROUP));
- setSelected(true);
-
- setHelpLocation(new HelpLocation(DUAL_LISTING_HELP_TOPIC,
- "Dual Listing Toggle Mouse Hover Popups"));
- setHover(true);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return isShowing();
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- setHover(isSelected());
- }
-
- void setHover(boolean enabled) {
- getToolBarData().setIcon(enabled ? HOVER_ON_ICON : HOVER_OFF_ICON);
- setHoverEnabled(enabled);
- }
- }
-
- private void setHoverEnabled(boolean enabled) {
- listingPanels[LEFT].setHoverMode(enabled);
- listingPanels[RIGHT].setHoverMode(enabled);
- }
-
- private boolean isValidPanelContext(ActionContext context) {
- CodeComparisonPanel extends FieldPanelCoordinator> displayedPanel = null;
- if (context instanceof CodeComparisonPanelActionContext) {
- CodeComparisonPanelActionContext compareContext =
- (CodeComparisonPanelActionContext) context;
- CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel =
- compareContext.getCodeComparisonPanel();
- if (codeComparisonPanel == this) {
- displayedPanel = codeComparisonPanel;
- }
- }
- if (displayedPanel != ListingCodeComparisonPanel.this) {
- return false;
- }
- ListingCodeComparisonPanel dualListingPanel = (ListingCodeComparisonPanel) displayedPanel;
- ListingPanel leftPanel = dualListingPanel.getLeftPanel();
- ListingPanel rightPanel = dualListingPanel.getRightPanel();
-
- Object sourceObject = context.getSourceObject();
- if (sourceObject instanceof ListingPanel) {
- ListingPanel listingPanel = (ListingPanel) sourceObject;
- return listingPanel == leftPanel || listingPanel == rightPanel;
- }
- return true;
- }
-
- private void nextAreaDiff(String currentUserData, boolean forward) {
- boolean leftHasFocus = (currProgramIndex == LEFT);
- ListingPanel focusPanel = getFocusedListingPanel();
- ProgramLocation focusLocation = focusPanel.getProgramLocation();
- if (focusLocation == null) {
- tool.setStatusInfo("The " + (leftHasFocus ? "first" : "second") + " listing is empty.");
- return;
- }
- Address focusAddress = focusLocation.getAddress();
-
- ArrayList iteratorList = new ArrayList<>();
-
- if (currentUserData.equals(ALL_AREA_MARKERS) || currentUserData.equals(DIFF_AREA_MARKERS)) {
- // DIFF Area Markers
- AddressSetView focusDiffs =
- leftHasFocus ? listingDiff.getListing1Diffs() : listingDiff.getListing2Diffs();
- iteratorList.add(focusDiffs.getAddressRanges(focusAddress, forward));
- }
- if (currentUserData.equals(ALL_AREA_MARKERS) ||
- currentUserData.equals(UNMATCHED_AREA_MARKERS)) {
- // UNMATCHED CODE Area Markers
- AddressSetView unmatchedCode = leftHasFocus ? listingDiff.getListing1UnmatchedCode()
- : listingDiff.getListing2UnmatchedCode();
- iteratorList.add(unmatchedCode.getAddressRanges(focusAddress, forward));
- }
-
- MultiAddressRangeIterator multiIterator = new MultiAddressRangeIterator(
- iteratorList.toArray(new AddressRangeIterator[iteratorList.size()]), forward);
-
- if (multiIterator.hasNext()) {
- AddressRange nextRange = multiIterator.next();
- Address minAddress = nextRange.getMinAddress();
- if ((forward ? nextRange.contains(focusAddress) : minAddress.equals(focusAddress)) &&
- multiIterator.hasNext()) {
- nextRange = multiIterator.next();
- minAddress = nextRange.getMinAddress();
- }
- if (minAddress.equals(focusAddress)) {
- outputNoNextPreviousMessage(forward, leftHasFocus);
- return;
- }
- tool.clearStatusInfo();
- focusPanel.goTo(minAddress);
- }
- else {
- outputNoNextPreviousMessage(forward, leftHasFocus);
- }
- }
-
- private void outputNoNextPreviousMessage(boolean forward, boolean isFirstListing) {
- tool.setStatusInfo("There isn't another " + (forward ? "next " : "previous ") +
- getCurrentAreaMarkerType().toLowerCase() + " area in the " +
- (isFirstListing ? "first" : "second") + " listing.");
- }
-
- private String getCurrentAreaMarkerType() {
- String type = "Highlighted";
- if (nextPreviousAreaType.equals(UNMATCHED_AREA_MARKERS)) {
- type = "Unmatched";
- }
- else if (nextPreviousAreaType.equals(DIFF_AREA_MARKERS)) {
- type = "Difference";
- }
- return type;
- }
-
- class NextPreviousAreaMarkerAction extends MultiStateDockingAction {
-
- public NextPreviousAreaMarkerAction(String owner) {
- super("Dual Listing Next/Previous Area Marker", owner);
-
- ToolBarData toolBarData = new ToolBarData(DIFF_ICON, DIFF_NAVIGATE_GROUP);
- setToolBarData(toolBarData);
-
- HelpLocation helpLocation =
- new HelpLocation(DUAL_LISTING_HELP_TOPIC, "Dual Listing Next/Previous Area Marker");
- setHelpLocation(helpLocation);
- setDescription("Set Navigate Next/Previous Area Marker options");
-
- ActionState allAreaMarkers =
- new ActionState<>(ALL_AREA_MARKERS, BOTH_VIEWS_ICON, ALL_AREA_MARKERS);
- allAreaMarkers.setHelpLocation(helpLocation);
- ActionState unmatchedAreaMarkers =
- new ActionState<>(UNMATCHED_AREA_MARKERS, UNMATCHED_ICON, UNMATCHED_AREA_MARKERS);
- unmatchedAreaMarkers.setHelpLocation(helpLocation);
- ActionState diffAreaMarkers =
- new ActionState<>(DIFF_AREA_MARKERS, DIFF_ICON, DIFF_AREA_MARKERS);
- diffAreaMarkers.setHelpLocation(helpLocation);
-
- addActionState(allAreaMarkers);
- addActionState(unmatchedAreaMarkers);
- addActionState(diffAreaMarkers);
-
- setCurrentActionState(allAreaMarkers); // default
- adjustNextPreviousAreaType();
- }
-
- private void adjustNextPreviousAreaType() {
- nextPreviousAreaType = getCurrentUserData();
- nextDiffAction.setMenuString();
- previousDiffAction.setMenuString();
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return isShowing() && listingDiff.hasCorrelation();
- }
-
- @Override
- public void actionStateChanged(ActionState newActionState, EventTrigger trigger) {
- adjustNextPreviousAreaType();
- }
-
- public void refresh() {
- actionStateChanged(getCurrentState(), (EventTrigger) null);
- }
- }
-
- class NextDiffAction extends DockingAction {
-
- NextDiffAction() {
- super("Dual Listing Go To Next Area Marker", owner);
- setEnabled(true);
- setKeyBindingData(new KeyBindingData('N',
- DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.ALT_DOWN_MASK));
- setDescription("Go to the next highlighted area.");
- setPopupMenuData(new MenuData(new String[] { "Go To Next Highlighted Area" },
- NEXT_DIFF_ICON, DIFF_NAVIGATE_GROUP));
- ToolBarData newToolBarData = new ToolBarData(NEXT_DIFF_ICON, DIFF_NAVIGATE_GROUP);
- setToolBarData(newToolBarData);
-
- HelpLocation helpLocation = new HelpLocation(DUAL_LISTING_HELP_TOPIC,
- "Dual Listing Go To Next Highlighted Area");
- setHelpLocation(helpLocation);
- setEnabled(true);
- }
-
- @Override
- public boolean isValidContext(ActionContext context) {
- return isValidPanelContext(context);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return isShowing() && listingDiff.hasCorrelation();
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- if (isValidContext(context)) {
- nextAreaDiff(nextPreviousAreaType, true);
- }
- }
-
- void setMenuString() {
- String type = getCurrentAreaMarkerType();
- setPopupMenuData(new MenuData(new String[] { "Go To Next " + type + " Area" },
- NEXT_DIFF_ICON, DIFF_NAVIGATE_GROUP));
- setDescription("Go to the next " + type.toLowerCase() + " area.");
- }
- }
-
- class PreviousDiffAction extends DockingAction {
-
- PreviousDiffAction() {
- super("Dual Listing Go To Previous Area Marker", owner);
- setEnabled(true);
- setKeyBindingData(new KeyBindingData('P',
- DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.ALT_DOWN_MASK));
- setDescription("Go to the previous highlighted area.");
- setPopupMenuData(new MenuData(new String[] { "Go To Previous Highlighted Area" },
- PREVIOUS_DIFF_ICON, DIFF_NAVIGATE_GROUP));
- ToolBarData newToolBarData = new ToolBarData(PREVIOUS_DIFF_ICON, DIFF_NAVIGATE_GROUP);
- setToolBarData(newToolBarData);
-
- HelpLocation helpLocation = new HelpLocation(DUAL_LISTING_HELP_TOPIC,
- "Dual Listing Go To Previous Highlighted Area");
- setHelpLocation(helpLocation);
- setEnabled(true);
- }
-
- @Override
- public boolean isValidContext(ActionContext context) {
- return isValidPanelContext(context);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return isShowing() && listingDiff.hasCorrelation();
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- if (isValidContext(context)) {
- nextAreaDiff(nextPreviousAreaType, false);
- }
- }
-
- void setMenuString() {
- String type = getCurrentAreaMarkerType();
- setPopupMenuData(new MenuData(new String[] { "Go To Previous " + type + " Area" },
- PREVIOUS_DIFF_ICON, DIFF_NAVIGATE_GROUP));
- setDescription("Go to the previous " + type.toLowerCase() + " area.");
- }
- }
-
- class ListingCodeComparisonOptionsAction extends DockingAction {
-
- ListingCodeComparisonOptionsAction() {
- super("Listing Code Comparison Options", owner);
- setEnabled(true);
- setDescription("Show the tool options for the Listing Code Comparison.");
- setPopupMenuData(new MenuData(new String[] { "Properties" }, null, PROPERTIES_GROUP));
- setHelpLocation(
- new HelpLocation(DUAL_LISTING_HELP_TOPIC, "Listing_Code_Comparison_Options"));
- setEnabled(true);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return isShowing() && listingDiff.hasCorrelation();
- }
-
- @Override
- public boolean isValidContext(ActionContext context) {
- return isValidPanelContext(context);
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- OptionsService service = tool.getService(OptionsService.class);
- service.showOptionsDialog(ListingCodeComparisonOptions.OPTIONS_CATEGORY_NAME,
- "Listing Code Comparison");
- }
- }
-
- public boolean isEntireListingShowing() {
- return isShowingEntireListing;
- }
-
/**
- * Sets whether or not the entire programs are displayed in the listings or only the addresses
- * in the limited set.
- *
- * @param show if true, the entire program will be shown. Otherwise the listings will only show
- * the limited addresses.
- */
- public void showEntireListing(boolean show) {
- try {
- fieldLocationChanging = true;
-
- isShowingEntireListing = show;
-
- ProgramLocation leftLocation = listingPanels[LEFT].getProgramLocation();
- ProgramLocation rightLocation = listingPanels[RIGHT].getProgramLocation();
- if (show) {
- loadEntirePrograms();
- }
- else {
- loadLimitedAddresses();
- }
- if (leftLocation != null) {
- listingPanels[LEFT].goTo(leftLocation);
- }
- if (rightLocation != null) {
- listingPanels[RIGHT].goTo(rightLocation);
- }
- }
- finally {
- fieldLocationChanging = false;
- }
- }
-
- /**
- * Determines if the listing's layout field header is currently showing.
- *
- * @return true if the header is showing.
- */
- public boolean isHeaderShowing() {
- return getLeftPanel().isHeaderShowing();
- }
-
- /**
- * Shows or hides the listing's layout field header.
- *
- * @param show true means show the field header. false means hide the header.
- */
- public void setHeaderShowing(boolean show) {
- ListingPanel listingPanel = getLeftPanel();
- boolean isShowing = listingPanel.isHeaderShowing();
- if (show == isShowing) {
- return;
- }
- listingPanel.showHeader(show);
- toggleHeaderAction.setSelected(show);
- }
-
- public boolean isSideBySide() {
- return isSideBySide;
- }
-
- /**
- * Sets whether or not the listings are displayed side by side.
- *
- * @param sideBySide if true, the listings are side by side, otherwise one is above the other.
- */
- public void showSideBySide(boolean sideBySide) {
- isSideBySide = sideBySide;
- splitPane.setOrientation(
- isSideBySide ? JSplitPane.HORIZONTAL_SPLIT : JSplitPane.VERTICAL_SPLIT);
- splitPane.setDividerLocation(0.5);
- toggleOrientationAction.setSelected(sideBySide);
- }
-
- private void loadEntirePrograms() {
- AddressSetView leftSet =
- (programs[LEFT] != null) ? programs[LEFT].getMemory() : EMPTY_ADDRESS_SET;
- AddressSetView rightSet =
- (programs[RIGHT] != null) ? programs[RIGHT].getMemory() : EMPTY_ADDRESS_SET;
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator = getFieldPanelCoordinator();
- if (fieldPanelCoordinator != null) {
- fieldPanelCoordinator.resetLockedLines();
- }
- listingPanels[LEFT].setView(leftSet);
- listingPanels[RIGHT].setView(rightSet);
- }
-
- private void loadLimitedAddresses() {
- AddressSetView leftSet =
- (addressSets[LEFT] != null) ? addressSets[LEFT] : EMPTY_ADDRESS_SET;
- AddressSetView rightSet =
- (addressSets[RIGHT] != null) ? addressSets[RIGHT] : EMPTY_ADDRESS_SET;
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator = getFieldPanelCoordinator();
- if (fieldPanelCoordinator != null) {
- fieldPanelCoordinator.resetLockedLines();
- }
- listingPanels[LEFT].setView(leftSet);
- listingPanels[RIGHT].setView(rightSet);
- }
-
- @Override
- public void loadFunctions(Function leftFunction, Function rightFunction) {
- setFunctions(leftFunction, rightFunction);
- }
-
- /**
- * Gets the function loaded in the left listing panel.
- *
- * @return the function or null
- */
- @Override
- public Function getLeftFunction() {
- return functions[LEFT];
- }
-
- /**
- * Gets the function loaded in the right listing panel.
- *
- * @return the function or null
- */
- @Override
- public Function getRightFunction() {
- return functions[RIGHT];
- }
-
- private void setFunctions(Function leftFunction, Function rightFunction) {
-
- if (leftFunction != functions[LEFT] || rightFunction != functions[RIGHT]) {
- clearMarkers();
-
- Program leftProgram =
- ((leftFunction != null) ? leftFunction.getProgram() : programs[LEFT]);
- Program rightProgram =
- ((rightFunction != null) ? rightFunction.getProgram() : programs[RIGHT]);
- setPrograms(leftProgram, rightProgram);
- // Adjust the data and functions only after the correct programs are set.
- data[LEFT] = null;
- data[RIGHT] = null;
- functions[LEFT] = leftFunction;
- functions[RIGHT] = rightFunction;
- setFunctionTitles();
- }
- doLoadFunctions(leftFunction, rightFunction, TaskMonitor.DUMMY);
- if (leftFunction == null || rightFunction == null) {
- correlator = null; // Clear the correlation. Need 2 functions for a correlation.
- }
- try {
- listingDiff.setCorrelation(correlator);
- // Setting the correlation will also reset the locked line numbers.
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator =
- getFieldPanelCoordinator();
- if (fieldPanelCoordinator != null) {
- fieldPanelCoordinator.setCorrelation(correlator);
- }
- }
- catch (MemoryAccessException e) {
- String leftName = (leftFunction != null) ? leftFunction.getName() : "No Function";
- String rightName = (rightFunction != null) ? rightFunction.getName() : "No Function";
- Msg.error(this, "Failed to load functions, " + leftName + " and " + rightName +
- " , into dual listing panel. " + e.getMessage(), e);
- }
- loadCursorArrow();
- updateActionEnablement();
- }
-
- /**
- * Establishes the location and display of the arrow cursor. This method should be called after
- * the function comparison window is loaded with functions, data, etc.
- */
- private void loadCursorArrow() {
- int focusedSide = currProgramIndex;
- boolean leftHasFocus = (focusedSide == LEFT);
- int nonFocusedSide = leftHasFocus ? RIGHT : LEFT;
- ProgramLocation focusedProgramLocation = listingPanels[focusedSide].getProgramLocation();
- ProgramLocation nonFocusedProgramLocation = null;
- if (focusedProgramLocation != null) {
- nonFocusedProgramLocation =
- getProgramLocation((leftHasFocus ? RIGHT : LEFT), focusedProgramLocation);
- }
-
- if (focusedProgramLocation != null) {
- setCursorMarkers(focusedSide, focusedProgramLocation);
- }
- else {
- //remove obsolete cursor background highlight for cursor on focused side
- listingPanels[focusedSide].getFieldPanel().repaint();
- }
-
- if (nonFocusedProgramLocation != null) {
- setCursorMarkers(nonFocusedSide, nonFocusedProgramLocation);
- }
- else {
- //remove obsolete cursor background highlight for cursor on non-focused side
- listingPanels[nonFocusedSide].getFieldPanel().repaint();
- }
- }
-
- /**
- * Gets an equivalent left side program location when given a right side program location or
- * vice versa. The intent of this method is to translate a location from one side of the dual
- * listing to an equivalent location for the other side if possible.
- *
- * @param leftOrRight LEFT or RIGHT indicating which side's program location is needed.
- * @param programLocation the program location for the other side.
- * @return a program location for the desired side. Otherwise, null.
- */
- private ProgramLocation getProgramLocation(int leftOrRight, ProgramLocation programLocation) {
- if (programLocation == null) {
- return null;
- }
- if (programLocation instanceof VariableLocation) {
- return getVariableLocation(leftOrRight, (VariableLocation) programLocation);
- }
-
- SaveState saveState = new SaveState();
- programLocation.saveState(saveState);
- Address address = programLocation.getAddress();
-
- // Try to get the indicated side's address using one of the address correlators.
- Address desiredAddress = getAddress(leftOrRight, address);
- if (desiredAddress == null || desiredAddress == Address.NO_ADDRESS) {
- return null; // Couldn't determine the indicated side's address.
- }
-
- saveState.remove("_ADDRESS");
- saveState.putString("_ADDRESS", desiredAddress.toString());
-
- Address byteAddress = programLocation.getByteAddress();
- saveState.remove("_BYTE_ADDR");
- Address desiredByteAddress = null;
- if (byteAddress != null) {
- // Try to get the indicated side's byte address using one of the address
- // correlators or by inferring it.
- desiredByteAddress = inferDesiredByteAddress(address, desiredAddress, byteAddress,
- programLocation.getProgram(), programs[leftOrRight]);
- if (desiredByteAddress != null) {
- saveState.putString("_BYTE_ADDR", desiredByteAddress.toString());
- }
- }
-
- // Adjust symbol path for labels if it is part of the location.
- adjustSymbolPath(saveState, address, desiredAddress, byteAddress, desiredByteAddress,
- programLocation.getProgram(), programs[leftOrRight]);
-
- // ref address can't be used with indicated side so remove it.
- saveState.remove("_REF_ADDRESS");
- // Don't know how to find equivalent referenced address for the indicated side,
- // so don't put any _REF_ADDRESS back.
-
- return ProgramLocation.getLocation(programs[leftOrRight], saveState);
- }
-
- private void adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
- Address byteAddress, Address desiredByteAddress, Program program,
- Program desiredProgram) {
-
- String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
- saveState.remove("_SYMBOL_PATH");
- if (symbolPathArray.length == 0) {
- return; // save state has no labels for program location.
- }
- Address symbolAddress = (byteAddress != null) ? byteAddress : address;
- Address desiredSymbolAddress =
- (desiredByteAddress != null) ? desiredByteAddress : desiredAddress;
- if (symbolAddress == null || desiredSymbolAddress == null) {
- return; // no address match.
- }
- Symbol[] symbols = program.getSymbolTable().getSymbols(symbolAddress);
- if (symbols.length == 0) {
- return; // no symbols in program for matching.
- }
- Symbol[] desiredSymbols = desiredProgram.getSymbolTable().getSymbols(desiredSymbolAddress);
- if (desiredSymbols.length == 0) {
- return; // no symbols in desiredProgram for matching.
- }
-
- int desiredRow = adjustSymbolRow(saveState, symbols, desiredSymbols);
-
- int desiredIndex = getDesiredSymbolIndex(desiredSymbols, desiredRow);
-
- // Now get the desired symbol.
- Symbol desiredSymbol = desiredSymbols[desiredIndex];
- SymbolPath symbolPath = getSymbolPath(desiredSymbol);
- // Set symbol path for desiredProgram in the save state.
- saveState.putStrings("_SYMBOL_PATH", symbolPath.asArray());
- }
-
- private int adjustSymbolRow(SaveState saveState, Symbol[] symbols, Symbol[] desiredSymbols) {
- // For now just try to choose the same label index if more than one.
- int row = saveState.getInt("_ROW", 0);
- int desiredRow = row;
- if ((desiredRow >= desiredSymbols.length) || // desiredRow is beyond last one so set to last row.
- (isFunctionCompare() && (row == (symbols.length - 1)))) { // row is function so set to last row.
-
- desiredRow = desiredSymbols.length - 1;
- }
- saveState.remove("_ROW");
- saveState.putInt("_ROW", desiredRow);
- return desiredRow;
- }
-
- private int getDesiredSymbolIndex(Symbol[] desiredSymbols, int desiredRow) {
-
- boolean hasFunction = desiredSymbols[0].getSymbolType().equals(SymbolType.FUNCTION);
-
- // Get the array index of the desired symbol.
- int desiredIndex = 0; // Default to first entry in array.
- if (desiredRow >= 0 && desiredRow < desiredSymbols.length) {
- desiredIndex = desiredRow;
- }
- if (hasFunction) {
- // Last row in GUI is also first entry in array.
- if (desiredIndex == desiredSymbols.length - 1) {
- desiredIndex = 0; // Set to function element.
- }
- else {
- desiredIndex++; // Adjust for function element at start of array.
- }
- }
- return desiredIndex;
- }
-
- private SymbolPath getSymbolPath(Symbol desiredSymbol) {
- String label = desiredSymbol.getName();
- Namespace namespace = desiredSymbol.getParentNamespace();
- SymbolPath symbolPath;
- if (namespace == null || namespace.isGlobal()) {
- symbolPath = new SymbolPath(label);
- }
- else {
- symbolPath = new SymbolPath(new SymbolPath(namespace.getSymbol()), label);
- }
- return symbolPath;
- }
-
- /**
- * Infers a desired byte address based on the specified byteAddress as well as the
- * address and desiredAddress that were matched.
- *
- * @param address matches up with the desiredAddress from the other function/data.
- * @param desiredAddress matches up with the address from the other function/data.
- * @param byteAddress the byte address that is associated with address
- * @param program the program for the address and byteAddress.
- * @param desiredProgram the program for the desiredAddress and
- * desiredByteAddress.
- * @return the desired byte address that matches up with the indicated byteAddress
- * or null if it can't be determined.
- */
- private Address inferDesiredByteAddress(Address address, Address desiredAddress,
- Address byteAddress, Program program, Program desiredProgram) {
-
- // Functions and Data have their addresses inferred differently.
- // Functions can use the address correlator for code units, while internal addresses
- // may get matched for Data.
- if (isFunctionCompare()) {
- return inferDesiredFunctionAddress(address, desiredAddress, byteAddress, program,
- desiredProgram);
- }
- if (isDataCompare()) {
- return inferDesiredDataAddress(address, desiredAddress, byteAddress, program,
- desiredProgram);
- }
- return null;
- }
-
- /**
- * This infers the desired byte address within Data based on the code units at
- * codeUnitAddress and desiredCodeUnitAddress. The inferred address
- * will be at an offset from the desiredCodeUnitAddress that is the same distance
- * the byteAddress is from the codeUnitAddress.
- *
- * @param codeUnitAddress matches up with the desiredCodeUnitAddress from the other
- * data.
- * @param desiredCodeUnitAddress matches up with the codeUnitAddress from the other
- * data.
- * @param byteAddress the byte address that is associated with codeUnitAddress
- * @param program the program for the codeUnitAddress and byteAddress.
- * @param desiredProgram the program for the desiredCodeUnitAddress and
- * desiredByteAddress.
- * @return the desired byte address within the data that matches up with the indicated
- * byteAddress or null if it can't be determined.
- */
- private Address inferDesiredDataAddress(Address codeUnitAddress, Address desiredCodeUnitAddress,
- Address byteAddress, Program program, Program desiredProgram) {
-
- long offset = byteAddress.subtract(codeUnitAddress);
- if (offset == 0) {
- return desiredCodeUnitAddress;
- }
- if (offset > 0) {
- CodeUnit codeUnit = program.getListing().getCodeUnitContaining(codeUnitAddress);
- CodeUnit desiredCodeUnit =
- desiredProgram.getListing().getCodeUnitContaining(desiredCodeUnitAddress);
- if (codeUnit != null && desiredCodeUnit != null) {
- try {
- return desiredCodeUnitAddress.add(offset);
- }
- catch (AddressOutOfBoundsException e) {
- return null; // No matching address.
- }
- }
- }
- return null; // No matching address.
- }
-
- /**
- * This infers the desired byte address within a function based on the code units at
- * address and desiredAddress. If the inferred address would be beyond
- * the last byte of the code unit then it will get set to the last byte of the code unit at the
- * desiredAddress.
- *
- * @param address matches up with the desiredAddress from the other function.
- * @param desiredAddress matches up with the address from the other function.
- * @param byteAddress the byte address that is associated with address
- * @param program the program for the address and byteAddress.
- * @param desiredProgram the program for the desiredAddress and
- * desiredByteAddress.
- * @return the desired byte address within the data that matches up with the indicated
- * byteAddress or null if it can't be determined.
- */
- private Address inferDesiredFunctionAddress(Address address, Address desiredAddress,
- Address byteAddress, Program program, Program desiredProgram) {
-
- long numBytesIntoCodeUnit = byteAddress.subtract(address);
- if (numBytesIntoCodeUnit == 0) {
- return desiredAddress;
- }
- if (numBytesIntoCodeUnit > 0) {
- CodeUnit codeUnit = program.getListing().getCodeUnitAt(address);
- CodeUnit desiredCodeUnit = desiredProgram.getListing().getCodeUnitAt(desiredAddress);
- if (codeUnit != null && desiredCodeUnit != null) {
- int desiredCodeUnitLength = desiredCodeUnit.getLength();
- if (numBytesIntoCodeUnit < desiredCodeUnitLength) {
- // Position at byte within code unit.
- return desiredAddress.add(numBytesIntoCodeUnit);
- }
- // Otherwise position at last byte of code unit.
- return desiredAddress.add(desiredCodeUnitLength - 1);
- }
- }
- return null;
- }
-
- /**
- * Gets an equivalent left side variable location when given a right side variable location or
- * vice versa. The intent of this method is to translate a variable location from one side of
- * the dual listing to an equivalent variable location for the other side if possible.
- *
- * @param leftOrRight LEFT or RIGHT indicating which side's variable location is needed.
- * @param variableLocation the variable location for the other side.
- * @return a variable location for the desired side. Otherwise, null.
- */
- private ProgramLocation getVariableLocation(int leftOrRight,
- VariableLocation variableLocation) {
- if (variableLocation == null) {
- return null;
- }
- SaveState saveState = new SaveState();
- variableLocation.saveState(saveState);
- Address address = variableLocation.getAddress();
- Address byteAddress = variableLocation.getByteAddress();
- Address functionAddress = variableLocation.getFunctionAddress();
-
- // Try to get the indicated side's address using one of the address correlators.
- Address desiredAddress = getAddress(leftOrRight, address);
- if (desiredAddress == null || desiredAddress == Address.NO_ADDRESS) {
- return null; // Couldn't determine the indicated side's address.
- }
-
- // Try to use a byte address.
- Address desiredByteAddress = null;
- if (byteAddress != null) {
- desiredByteAddress = getAddress(leftOrRight, byteAddress);
- }
-
- Address desiredFunctionAddress = null;
- if (functionAddress != null) {
- desiredFunctionAddress = getAddress(leftOrRight, functionAddress);
- }
- if ((desiredFunctionAddress == null) && (functions[leftOrRight] != null)) {
- // If this is a thunk function get the thunked address.
- Function thunkedFunction = functions[leftOrRight].getThunkedFunction(true);
- if (thunkedFunction != null) {
- desiredFunctionAddress = thunkedFunction.getEntryPoint();
- }
- }
-
- saveState.remove("_ADDRESS");
- saveState.putString("_ADDRESS", desiredAddress.toString());
-
- saveState.remove("_BYTE_ADDR");
- if (desiredByteAddress != null) {
- saveState.putString("_BYTE_ADDR", desiredByteAddress.toString());
- }
-
- saveState.remove("_FUNC_ADDRESS");
- if (desiredFunctionAddress != null) {
- saveState.putString("_FUNC_ADDRESS", desiredFunctionAddress.toString());
- }
-
- // ref address can't be used with indicated side so remove it.
- saveState.remove("_REF_ADDRESS");
- // Don't know how to find equivalent referenced address for the indicated side,
- // so don't put any _REF_ADDRESS back.
-
- return ProgramLocation.getLocation(programs[leftOrRight], saveState);
- }
-
- private void clearMarkers() {
- clearUnmatchedCodeMarkers();
- clearDiffMarkers();
- clearCursorMarkers();
- }
-
- private void clearUnmatchedCodeMarkers() {
- if (unmatchedCodeMarkers[LEFT] != null) {
- unmatchedCodeMarkers[LEFT].clearAll();
- }
- if (unmatchedCodeMarkers[RIGHT] != null) {
- unmatchedCodeMarkers[RIGHT].clearAll();
- }
- }
-
- private void clearDiffMarkers() {
- if (diffMarkers[LEFT] != null) {
- diffMarkers[LEFT].clearAll();
- }
- if (diffMarkers[RIGHT] != null) {
- diffMarkers[RIGHT].clearAll();
- }
- }
-
- private void setCursorMarkers(int leftOrRight, ProgramLocation location) {
- if (currentCursorMarkers[leftOrRight] != null) {
- currentCursorMarkers[leftOrRight].clearAll();
- if (location != null) {
- currentCursorMarkers[leftOrRight].add(location.getAddress());
- }
- }
- }
-
- private void clearCursorMarkers() {
- if (currentCursorMarkers[LEFT] != null) {
- currentCursorMarkers[LEFT].clearAll();
-
- }
- if (currentCursorMarkers[RIGHT] != null) {
- currentCursorMarkers[RIGHT].clearAll();
- }
- }
-
- private void setFunctionTitles() {
- setLeftTitle(getFunctionTitle(functions[LEFT]));
- setRightTitle(getFunctionTitle(functions[RIGHT]));
- }
-
- private String getFunctionTitle(Function function) {
- if (function == null) {
- return "none";
- }
- StringBuffer buf = new StringBuffer();
- String padStr = HTMLUtilities.spaces(4);
- buf.append(padStr);
-
- String functionStr = HTMLUtilities.friendlyEncodeHTML(function.getName(true) + "()");
- String specialFunctionStr = HTMLUtilities.bold(functionStr);
- buf.append(specialFunctionStr);
-
- Program program = function.getProgram();
- if (program != null) {
- buf.append(" in ");
-
- String programStr =
- HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
- String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
- buf.append(specialProgramStr);
- buf.append(padStr);
- }
- return HTMLUtilities.wrapAsHTML(buf.toString());
- }
-
- private void setDataTitles() {
- setLeftTitle(getDataTitle(data[LEFT]));
- setRightTitle(getDataTitle(data[RIGHT]));
- }
-
- private String getDataTitle(Data currentData) {
- if (currentData == null) {
- return "none";
- }
- StringBuffer buf = new StringBuffer();
- String padStr = HTMLUtilities.spaces(4);
- buf.append(padStr);
-
- String dataLabel = currentData.getLabel();
- if (dataLabel == null) { // If we can't get a label for the data then use the address .
- Address address = currentData.getAddress();
- dataLabel = address.toString();
- }
- String dataStr = HTMLUtilities.friendlyEncodeHTML(dataLabel);
- String specialDataStr = HTMLUtilities.bold(dataStr);
- buf.append(specialDataStr);
-
- Program program = currentData.getProgram();
- if (program != null) {
- buf.append(" in ");
-
- String programStr =
- HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
- String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
- buf.append(specialProgramStr);
- buf.append(padStr);
- }
- return HTMLUtilities.wrapAsHTML(buf.toString());
- }
-
- private void setAddressesTitles() {
- setLeftTitle(getAddressesTitle(programs[LEFT], addressSets[LEFT]));
- setRightTitle(getAddressesTitle(programs[RIGHT], addressSets[RIGHT]));
- }
-
- private String getAddressesTitle(Program program, AddressSetView addresses) {
- if (program == null) {
- return "none";
- }
- StringBuffer buf = new StringBuffer();
- String padStr = HTMLUtilities.spaces(4);
- buf.append(padStr);
- String programStr = HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
- String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
- buf.append(specialProgramStr);
- buf.append(padStr);
- return HTMLUtilities.wrapAsHTML(buf.toString());
- }
-
- private void setDiffHighlights() {
- setFunctionComparisonDiffHighlights();
- setUnmatchedCodeUnitAreaMarkers();
- setDiffAreaMarkers();
- }
-
- private void setFunctionComparisonDiffHighlights() {
- // Remove old highlight providers if they exist.
- removeHighlightProviders(leftDiffHighlightProvider, rightDiffHighlightProvider);
-
- leftDiffHighlightProvider =
- new ListingDiffHighlightProvider(listingDiff, true, comparisonOptions);
- rightDiffHighlightProvider =
- new ListingDiffHighlightProvider(listingDiff, false, comparisonOptions);
- addHighlightProviders(leftDiffHighlightProvider, rightDiffHighlightProvider);
- }
-
- private void setDiffAreaMarkers() {
- Color codeUnitDiffsBackgroundColor = comparisonOptions.getDiffCodeUnitsBackgroundColor();
- AddressSetView listing1Diffs = listingDiff.getListing1Diffs();
- AddressSetView listing2Diffs = listingDiff.getListing2Diffs();
- if (diffMarkers[LEFT] != null) {
- diffMarkers[LEFT].setMarkerColor(codeUnitDiffsBackgroundColor);
- diffMarkers[LEFT].clearAll();
- diffMarkers[LEFT].add(listing1Diffs);
- listingPanels[LEFT].getFieldPanel().repaint();
- }
- if (diffMarkers[RIGHT] != null) {
- diffMarkers[RIGHT].setMarkerColor(codeUnitDiffsBackgroundColor);
- diffMarkers[RIGHT].clearAll();
- diffMarkers[RIGHT].add(listing2Diffs);
- listingPanels[RIGHT].getFieldPanel().repaint();
- }
- }
-
- private void setUnmatchedCodeUnitAreaMarkers() {
- Color unmatchedCodeUnitsBackgroundColor =
- comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
- AddressSetView listing1UnmatchedCode = listingDiff.getListing1UnmatchedCode();
- AddressSetView listing2UnmatchedCode = listingDiff.getListing2UnmatchedCode();
- if (unmatchedCodeMarkers[LEFT] != null) {
- unmatchedCodeMarkers[LEFT].setMarkerColor(unmatchedCodeUnitsBackgroundColor);
- unmatchedCodeMarkers[LEFT].clearAll();
- unmatchedCodeMarkers[LEFT].add(listing1UnmatchedCode);
- listingPanels[LEFT].getFieldPanel().repaint();
- }
- if (unmatchedCodeMarkers[RIGHT] != null) {
- unmatchedCodeMarkers[RIGHT].setMarkerColor(unmatchedCodeUnitsBackgroundColor);
- unmatchedCodeMarkers[RIGHT].clearAll();
- unmatchedCodeMarkers[RIGHT].add(listing2UnmatchedCode);
- listingPanels[RIGHT].getFieldPanel().repaint();
- }
- }
-
- private void setupMarkerManagers() {
- setupMarkerManager(LEFT);
- setupMarkerManager(RIGHT);
- }
-
- private void setupMarkerManager(int leftOrRight) {
- if (markerManagers[leftOrRight] == null) {
- markerManagers[leftOrRight] =
- new DualListingMarkerManager(owner, tool, dualListingServiceProviders[leftOrRight]);
-
- // setup a marker change listener that can repaint the listing as markers are enabled/disabled.
- markerManagers[leftOrRight].addChangeListener(new MarkerChangeListener(leftOrRight));
-
- // Set up the marker margin that is on the left side of the listing.
- MarginProvider marginProvider = markerManagers[leftOrRight].getMarginProvider();
- JComponent providerComp = marginProvider.getComponent();
- DualListingMouseListener providerMouseListener =
- new DualListingMouseListener(leftOrRight);
- providerComp.addMouseListener(providerMouseListener);
- listingPanels[leftOrRight].addMarginProvider(marginProvider);
-
- // Set up the overview margin that is on the right side of the listing.
- OverviewProvider overviewProvider = markerManagers[leftOrRight].getOverviewProvider();
- JComponent overviewComp = overviewProvider.getComponent();
- DualListingMouseListener overviewMouseListener =
- new DualListingMouseListener(leftOrRight);
- overviewComp.addMouseListener(overviewMouseListener);
- listingPanels[leftOrRight].addOverviewProvider(overviewProvider);
- }
- }
-
- private void setupAreaMarkerSets() {
- Color diffCodeUnitsBackgroundColor = comparisonOptions.getDiffCodeUnitsBackgroundColor();
- Color unmatchedCodeUnitsBackgroundColor =
- comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
- if (programs[LEFT] != null) {
- AddressIndexMap indexMap = listingPanels[LEFT].getAddressIndexMap();
- listingPanels[LEFT].getFieldPanel()
- .setBackgroundColorModel(new MarkerServiceBackgroundColorModel(
- markerManagers[LEFT], programs[LEFT], indexMap));
- unmatchedCodeMarkers[LEFT] =
- markerManagers[LEFT].createAreaMarker("Listing1 Unmatched Code",
- "Instructions that are not matched to an instruction in the other function.",
- programs[LEFT], MarkerService.DIFF_PRIORITY, true, true, true,
- unmatchedCodeUnitsBackgroundColor);
- diffMarkers[LEFT] = markerManagers[LEFT].createAreaMarker("Listing1 Diffs",
- "Instructions that have a difference.", programs[LEFT], MarkerService.DIFF_PRIORITY,
- true, true, true, diffCodeUnitsBackgroundColor);
- }
- if (programs[RIGHT] != null) {
- AddressIndexMap rightIndexMap = listingPanels[RIGHT].getAddressIndexMap();
- listingPanels[RIGHT].getFieldPanel()
- .setBackgroundColorModel(new MarkerServiceBackgroundColorModel(
- markerManagers[RIGHT], programs[RIGHT], rightIndexMap));
- unmatchedCodeMarkers[RIGHT] =
- markerManagers[RIGHT].createAreaMarker("Listing2 Unmatched Code",
- "Instructions that are not matched to an instruction in the other function.",
- programs[RIGHT], MarkerService.DIFF_PRIORITY, true, true, true,
- unmatchedCodeUnitsBackgroundColor);
- diffMarkers[RIGHT] = markerManagers[RIGHT].createAreaMarker("Listing2 Diffs",
- "Instructions that have a difference.", programs[RIGHT],
- MarkerService.DIFF_PRIORITY, true, true, true, diffCodeUnitsBackgroundColor);
-
- }
- }
-
- private void setupCursorMarkerSets() {
- if (programs[LEFT] != null) {
- currentCursorMarkers[LEFT] = markerManagers[LEFT].createPointMarker("Cursor",
- "Cursor Location", programs[LEFT], MarkerService.FUNCTION_COMPARE_CURSOR_PRIORITY,
- true, true, true, cursorHighlightColor, CURSOR_LOC_ICON, false);
- }
- if (programs[RIGHT] != null) {
- currentCursorMarkers[RIGHT] = markerManagers[RIGHT].createPointMarker("Cursor",
- "Cursor Location", programs[RIGHT], MarkerService.FUNCTION_COMPARE_CURSOR_PRIORITY,
- true, true, true, cursorHighlightColor, CURSOR_LOC_ICON, false);
- }
- }
-
- private void doLoadFunctions(Function leftFunction, Function rightFunction,
- TaskMonitor monitor) {
- try {
- fieldLocationChanging = true;
- updateLeftAddressSet(leftFunction);
- updateRightAddressSet(rightFunction);
- try {
- correlator =
- new HashedFunctionAddressCorrelation(leftFunction, rightFunction, monitor);
- }
- catch (CancelledException e) {
- correlator = null;
- }
- catch (MemoryAccessException e) {
- correlator = null;
- }
- if (isShowingEntireListing) {
- loadEntirePrograms();
- }
- else {
- loadLimitedAddresses();
- }
- goToLeftFunction(leftFunction);
- goToRightFunction(rightFunction);
- validate();
- }
- finally {
- fieldLocationChanging = false;
- }
- }
-
- private void goToLeftFunction(Function leftFunction) {
- if (leftFunction != null && !adjustingLeftLocation) {
- try {
- adjustingLeftLocation = true;
-
- listingPanels[LEFT].goTo(new FunctionSignatureFieldLocation(
- leftFunction.getProgram(), leftFunction.getEntryPoint(), null, 0,
- leftFunction.getPrototypeString(false, false)));
- }
- finally {
- adjustingLeftLocation = false;
- }
- }
- }
-
- private void goToRightFunction(Function rightFunction) {
- if (rightFunction != null && !adjustingRightLocation) {
- try {
- adjustingRightLocation = true;
-
- listingPanels[RIGHT].goTo(new FunctionSignatureFieldLocation(
- rightFunction.getProgram(), rightFunction.getEntryPoint(), null, 0,
- rightFunction.getPrototypeString(false, false)));
- }
- finally {
- adjustingRightLocation = false;
- }
- }
- }
-
- private void updateLeftAddressSet(Function leftFunction) {
- addressSets[LEFT] = (leftFunction != null) ? leftFunction.getBody() : EMPTY_ADDRESS_SET;
-
- // If we have a function and no addresses, then set the entry point as the address set.
- // This allows external functions to be displayed.
- if (leftFunction != null && addressSets[LEFT].isEmpty()) {
- Address entryPoint = leftFunction.getEntryPoint();
- addressSets[LEFT] = new AddressSet(leftFunction.getProgram(), entryPoint, entryPoint);
- }
-
- indexMaps[LEFT] = new AddressIndexMap(addressSets[LEFT]);
- markerManagers[LEFT].getOverviewProvider().setProgram(getLeftProgram(), indexMaps[LEFT]);
- listingPanels[LEFT].getFieldPanel()
- .setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManagers[LEFT],
- programs[LEFT], indexMaps[LEFT]));
- }
-
- private void updateRightAddressSet(Function rightFunction) {
- addressSets[RIGHT] = (rightFunction != null) ? rightFunction.getBody() : EMPTY_ADDRESS_SET;
-
- // If we have a function and no addresses, then set the entry point as the address set.
- // This allows external functions to be displayed.
- if (rightFunction != null && addressSets[RIGHT].isEmpty()) {
- Address entryPoint = rightFunction.getEntryPoint();
- addressSets[RIGHT] = new AddressSet(rightFunction.getProgram(), entryPoint, entryPoint);
- }
-
- indexMaps[RIGHT] = new AddressIndexMap(addressSets[RIGHT]);
- markerManagers[RIGHT].getOverviewProvider().setProgram(getRightProgram(), indexMaps[RIGHT]);
- listingPanels[RIGHT].getFieldPanel()
- .setBackgroundColorModel(new MarkerServiceBackgroundColorModel(
- markerManagers[RIGHT], programs[RIGHT], indexMaps[RIGHT]));
- }
-
- @Override
- public void loadAddresses(Program leftProgram, Program rightProgram,
- AddressSetView leftAddresses, AddressSetView rightAddresses) {
-
- setPrograms(leftProgram, rightProgram);
- try {
- fieldLocationChanging = true;
- addressSets[LEFT] =
- (leftProgram != null && leftAddresses != null) ? leftAddresses : EMPTY_ADDRESS_SET;
- addressSets[RIGHT] = (rightProgram != null && rightAddresses != null) ? rightAddresses
- : EMPTY_ADDRESS_SET;
- clearCorrelation();
- if (isShowingEntireListing) {
- loadEntirePrograms();
- }
- else {
- loadLimitedAddresses();
- }
- setAddressesTitles();
- if (programs[LEFT] != null && !addressSets[LEFT].isEmpty()) {
- listingPanels[LEFT].goTo(
- new ProgramLocation(programs[LEFT], addressSets[LEFT].getMinAddress()));
- }
- if (programs[RIGHT] != null && !addressSets[RIGHT].isEmpty()) {
- listingPanels[RIGHT].goTo(
- new ProgramLocation(programs[RIGHT], addressSets[RIGHT].getMinAddress()));
- }
- }
- finally {
- loadCursorArrow();
- updateActionEnablement();
- fieldLocationChanging = false;
- }
- }
-
- /**
- * Sets the cursor location in the left and right listing at the specified functions.
- *
- * @param leftFunction the function in the left listing panel.
- * @param rightFunction the function in the right listing panel.
- */
- public void setLocation(Function leftFunction, Function rightFunction) {
- goToLeftFunction(leftFunction);
- goToRightFunction(rightFunction);
- }
-
- /**
- * Sets the cursor in the left side's listing to the specified location.
- *
- * @param program the left side's program
+ * Sets the cursor for the side to the given location
+ * @param side The side to goto
+ * @param program the side's program
* @param location the location
*/
- public void setLeftLocation(Program program, ProgramLocation location) {
+ public void setLocation(Side side, Program program, ProgramLocation location) {
if (isShowing()) {
- goToLeftLocation(location);
+ displays.get(side).goTo(location);
}
}
- /**
- * Sets the cursor in the right side's listing to the specified location.
- *
- * @param program the right side's program
- * @param location the location
- */
- public void setRightLocation(Program program, ProgramLocation location) {
- if (isShowing()) {
- goToRightLocation(location);
- }
- }
-
- private void goToLeftLocation(ProgramLocation location) {
- if (adjustingLeftLocation || location == null) {
- return;
- }
- try {
- adjustingLeftLocation = true;
-
- Address address = location.getAddress();
- if (location instanceof CodeUnitLocation) {
- CodeUnit leftCodeUnit = programs[LEFT].getListing().getCodeUnitContaining(address);
- if (leftCodeUnit == null) {
- return;
- }
- }
-
- // Now that we have the correct addresses, position the cursor location.
- listingPanels[LEFT].goTo(location, false);
- }
- finally {
- adjustingLeftLocation = false;
- }
- }
-
- private void goToRightLocation(ProgramLocation location) {
- if (adjustingRightLocation || location == null) {
- return;
- }
- try {
- adjustingRightLocation = true;
-
- Address address = location.getAddress();
- if (location instanceof CodeUnitLocation) {
- CodeUnit rightCodeUnit =
- programs[RIGHT].getListing().getCodeUnitContaining(address);
- if (rightCodeUnit == null) {
- return;
- }
- }
-
- // Now that we have the correct addresses, position the cursor location.
- listingPanels[RIGHT].goTo(location, false);
- }
- finally {
- adjustingRightLocation = false;
- }
- }
-
- private void buildPanel() {
- setName(DUAL_LISTING_ACTION_GROUP);
- setLayout(new BorderLayout());
- // cleanup remnants
- if (splitPane != null) {
- remove(splitPane);
- listingPanels[LEFT].dispose();
- listingPanels[RIGHT].dispose();
- }
-
- FormatManager leftFormatManager = createFormatManager(LEFT);
- leftFormatManager.addFormatModelListener(this);
- listingPanels[LEFT] = new ListingPanel(leftFormatManager, programs[LEFT]);
-
- FormatManager rightFormatManager = createFormatManager(RIGHT);
- listingPanels[RIGHT] = new ListingPanel(rightFormatManager, programs[RIGHT]);
-
- listingPanels[LEFT].setBorder(FOCUS_BORDER);
- listingPanels[RIGHT].setBorder(NON_FOCUS_BORDER);
-
- // Turn off selection in the listings so it can be set up s desired elsewhere.
- listingPanels[LEFT].getFieldPanel().enableSelection(false);
- listingPanels[RIGHT].getFieldPanel().enableSelection(false);
-
- String leftProgramName =
- (programs[LEFT] != null) ? programs[LEFT].getDomainFile().toString() : "none";
- String rightProgramName =
- (programs[RIGHT] != null) ? programs[RIGHT].getDomainFile().toString() : "none";
-
- titlePanels[LEFT] = new TitledPanel(leftProgramName, listingPanels[LEFT], 5);
- titlePanels[RIGHT] = new TitledPanel(rightProgramName, listingPanels[RIGHT], 5);
-
- // Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's
- // divider from becoming locked (can't be moved) due to extra long title names.
- titlePanels[LEFT].setMinimumSize(
- new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[LEFT].getMinimumSize().height));
- titlePanels[RIGHT].setMinimumSize(
- new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[RIGHT].getMinimumSize().height));
-
- splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, titlePanels[LEFT],
- titlePanels[RIGHT]);
- splitPane.setResizeWeight(0.5);
- splitPane.setDividerSize(4);
- splitPane.setBorder(BorderFactory.createEmptyBorder());
- add(splitPane, BorderLayout.CENTER);
- }
-
- private void setTitle(TitledPanel titlePanel, String titlePrefix, String title) {
- if (!titlePrefix.isEmpty()) {
- titlePrefix += " "; // Add a space between prefix and title.
- }
- String htmlPrefix = "";
- if (title.startsWith(htmlPrefix)) {
- titlePanel.setTitleName(htmlPrefix + HTMLUtilities.friendlyEncodeHTML(titlePrefix) +
- title.substring(htmlPrefix.length()));
- }
- else {
- titlePanel.setTitleName(titlePrefix + title);
- }
- }
-
- /**
- * Sets the title for the left side's listing.
- *
- * @param leftTitle the title
- */
- public void setLeftTitle(String leftTitle) {
- this.leftTitle = leftTitle;
- setTitle(titlePanels[LEFT], leftTitlePrefix, leftTitle);
- }
-
- /**
- * Sets the title for the right side's listing.
- *
- * @param rightTitle the title
- */
- public void setRightTitle(String rightTitle) {
- this.rightTitle = rightTitle;
- setTitle(titlePanels[RIGHT], rightTitlePrefix, rightTitle);
- }
-
- /**
- * Sets the component displayed in the top of this panel.
- *
- * @param comp the component.
- */
- public void setTopComponent(JComponent comp) {
- if (topComp == comp) {
- return;
- }
- if (topComp != null) {
- remove(topComp);
- }
- topComp = comp;
- if (topComp != null) {
- add(topComp, BorderLayout.NORTH);
- }
- validate();
- }
-
- /**
- * Sets the component displayed in the bottom of this panel.
- *
- * @param comp the component.
- */
- public void setBottomComponent(JComponent comp) {
- if (bottomComp == comp) {
- return;
- }
- if (bottomComp != null) {
- remove(bottomComp);
- }
- validate(); // Since we are removing this while the panel is on the screen.
- bottomComp = comp;
- if (bottomComp != null) {
- add(bottomComp, BorderLayout.SOUTH);
- }
- validate(); // Since we are adding this while the panel is on the screen.
- }
-
- /**
- * Gets the program from the left or right side that has or last had focus.
- *
- * @return the program from the side of this panel with focus or null
- */
- public Program getFocusedProgram() {
- return programs[currProgramIndex];
- }
-
- /**
- * Gets the program in the left listing panel.
- *
- * @return the left program or null
- */
- @Override
- public Program getLeftProgram() {
- return programs[LEFT];
- }
-
- /**
- * Gets the program in the right listing panel.
- *
- * @return the right program or null
- */
- @Override
- public Program getRightProgram() {
- return programs[RIGHT];
- }
-
- /**
- * Gets the addresses in the left listing panel.
- *
- * @return the addresses
- */
- @Override
- public AddressSetView getLeftAddresses() {
- return addressSets[LEFT];
- }
-
- /**
- * Gets the addresses in the right listing panel.
- *
- * @return the addresses
- */
- @Override
- public AddressSetView getRightAddresses() {
- return addressSets[RIGHT];
- }
-
- /**
- * Get the left or right listing panel that has or last had focus.
- *
- * @return the listing panel with focus.
- */
- public ListingPanel getFocusedListingPanel() {
- return listingPanels[currProgramIndex];
- }
-
- /**
- * Get the left side's listing panel.
- *
- * @return the left panel
- */
- public ListingPanel getLeftPanel() {
- return listingPanels[LEFT];
- }
-
- /**
- * Get the right side's listing panel.
- *
- * @return the right panel
- */
- public ListingPanel getRightPanel() {
- return listingPanels[RIGHT];
- }
-
- /**
- * Go to the indicated address in the listing that last had focus.
- *
- * @param addr the cursor should go to this address
- * @return true if the location changed
- */
- public boolean goTo(Address addr) {
- return listingPanels[currProgramIndex].goTo(addr);
- }
-
- /**
- * Go to the indicated location in the listing that last had focus.
- *
- * @param loc the cursor should go to this location.
- * @param centerOnScreen true indicates that the location should be centered in the listing's
- * viewport.
- * @return true if the location changed
- */
- public boolean goTo(ProgramLocation loc, boolean centerOnScreen) {
- return listingPanels[currProgramIndex].goTo(loc, centerOnScreen);
+ public ListingPanel getActiveListingPanel() {
+ return displays.get(activeSide).getListingPanel();
}
@Override
public void dispose() {
- setFieldPanelCoordinator(null);
- listingDiff.removeListingDiffChangeListener(this);
- markerManagers[LEFT].dispose();
- markerManagers[RIGHT].dispose();
- listingPanels[LEFT].getFieldPanel().removeFieldLocationListener(leftLocationListener);
- listingPanels[RIGHT].getFieldPanel().removeFieldLocationListener(rightLocationListener);
- for (int i = 0; i < 2; i++) {
- listingPanels[i].dispose();
- }
- }
-
- @Override
- public void focusGained(FocusEvent e) {
- Component comp = e.getComponent();
- for (int i = 0; i < listingPanels.length; i++) {
- if (listingPanels[i].getFieldPanel() == comp) {
- setDualPanelFocus(i);
- }
- }
-
- // Kick the tool so action buttons will be updated
- if (tool.getActiveComponentProvider() != null) {
- tool.getActiveComponentProvider().contextChanged();
- }
- }
-
- private void setDualPanelFocus(int leftOrRight) {
- currProgramIndex = leftOrRight;
- listingPanels[leftOrRight].setBorder(FOCUS_BORDER);
- listingPanels[((leftOrRight == LEFT) ? RIGHT : LEFT)].setBorder(NON_FOCUS_BORDER);
+ setSynchronizedScrolling(false);
+ displays.each(ListingDisplay::dispose);
}
@Override
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
- ListingCodeComparisonPanel dualListingPanel = this;
-
if (event == null) {
- DualListingActionContext myActionContext = new DualListingActionContext(provider);
- myActionContext.setContextObject(this);
- myActionContext.setCodeComparisonPanel(this);
- return myActionContext;
+ DualListingActionContext context = new DualListingActionContext(provider, this);
+ return context;
}
- ListingPanel leftPanel = dualListingPanel.getLeftPanel();
- ListingPanel rightPanel = dualListingPanel.getRightPanel();
+ ListingPanel leftPanel = displays.get(LEFT).getListingPanel();
+ ListingPanel rightPanel = displays.get(RIGHT).getListingPanel();
Object leftMarginContext = getContextForMarginPanels(leftPanel, event);
if (leftMarginContext != null) {
@@ -2103,11 +273,290 @@ public class ListingCodeComparisonPanel
return new DefaultActionContext(provider).setContextObject(fieldHeaderLocation);
}
- DualListingActionContext myActionContext = new DualListingActionContext(provider);
- myActionContext.setContextObject(this);
- myActionContext.setCodeComparisonPanel(this);
- myActionContext.setSourceObject(source);
- return myActionContext;
+ return new DualListingActionContext(provider, this);
+ }
+
+ /**
+ * Repaints both the left and right listing panels if they are visible.
+ */
+ public void updateListings() {
+ if (!isVisible()) {
+ return;
+ }
+ displays.each(ListingDisplay::repaint);
+ }
+
+ @Override
+ public void formatModelChanged(FieldFormatModel model) {
+ changeRightToMatchLeftFormat(model);
+ }
+
+ /**
+ * Gets the left or right listing panel that contains the indicated field panel.
+ *
+ * @param fieldPanel the field panel
+ * @return the listing panel or null.
+ */
+ public ListingPanel getListingPanel(FieldPanel fieldPanel) {
+ ListingPanel listingPanel = displays.get(LEFT).getListingPanel();
+ if (listingPanel.getFieldPanel() == fieldPanel) {
+ return listingPanel;
+ }
+
+ listingPanel = displays.get(RIGHT).getListingPanel();
+ if (listingPanel.getFieldPanel() == fieldPanel) {
+ return listingPanel;
+ }
+
+ return null;
+ }
+
+ /**
+ * Displays the indicated text int the tool's status area.
+ *
+ * @param text the message to display
+ */
+ public void setStatusInfo(String text) {
+ tool.setStatusInfo(text);
+ }
+
+ /**
+ * Gets a marker margin or overview margin context object if the mouse event occurred on one of
+ * the GUI components for the indicated listing panel's marker margin (left edge of listing) or
+ * overview margin (right edge of listing).
+ *
+ * @param panel The listing panel to check
+ * @param event the mouse event
+ * @return a marker margin context object if the event was on a margin.
+ */
+ public Object getContextObjectForMarginPanels(ListingPanel panel, MouseEvent event) {
+ Object source = event.getSource();
+ // Is event source a marker margin provider on the left side of the listing?
+ List marginProviders = panel.getMarginProviders();
+ for (MarginProvider marginProvider : marginProviders) {
+ JComponent c = marginProvider.getComponent();
+ if (c == source) {
+ MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
+ if (loc != null) {
+ return loc; // Return the marker margin location that was clicked.
+ }
+ return source; // Return the margin provider that was clicked.
+ }
+ }
+ // Is event source an overview provider on the right side of the listing?
+ List overviewProviders = panel.getOverviewProviders();
+ for (OverviewProvider overviewProvider : overviewProviders) {
+ JComponent c = overviewProvider.getComponent();
+ if (c == source) {
+ return source; // Return the overview provider that was clicked.
+ }
+ }
+ return null; // Not one of the listing panel's margin panels.
+ }
+
+ @Override
+ public void setSynchronizedScrolling(boolean synchronize) {
+ listingsLocked = synchronize;
+ updateCoordinator();
+ }
+
+ /**
+ * Notification that a program location changed on one side.
+ * @param side the side that changed
+ * @param location the new location for the given side
+ * @param trigger the trigger for the change
+ */
+ private void programLocationChanged(Side side, ProgramLocation location, EventTrigger trigger) {
+
+ // only respond to GUI actions to avoid bouncing
+ if (trigger != EventTrigger.GUI_ACTION) {
+ return;
+ }
+ displays.get(side).updateCursorMarkers(location);
+ displays.get(side.otherSide()).updateCursorMarkers(null);
+
+ if (coordinator != null) {
+ coordinator.setLocation(side, location);
+ }
+
+ }
+
+ private void createActions() {
+ nextDiffAction = new ActionBuilder("Dual Listing Go To Next Area Marker", owner)
+ .description("Go to the next highlighted area.")
+ .helpLocation(
+ new HelpLocation(HELP_TOPIC, "Dual Listing Go To Next Highlighted Area"))
+ .popupMenuPath("Go To Next Highlighted Area")
+ .popupMenuIcon(NEXT_DIFF_ICON)
+ .popupMenuGroup(DIFF_NAVIGATE_GROUP)
+ .toolBarIcon(NEXT_DIFF_ICON)
+ .toolBarGroup(DIFF_NAVIGATE_GROUP)
+ .keyBinding("ctrl alt N")
+ .validContextWhen(c -> isValidPanelContext(c))
+ .enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
+ .onAction(c -> nextAreaDiff(true))
+ .build();
+
+ previousDiffAction = new ActionBuilder("Dual Listing Go To Previous Area Marker", owner)
+ .description("Go to the previous highlighted area.")
+ .helpLocation(
+ new HelpLocation(HELP_TOPIC, "Dual Listing Go To Previous Highlighted Area"))
+ .popupMenuPath("Go To Previous Highlighted Area")
+ .popupMenuIcon(PREVIOUS_DIFF_ICON)
+ .popupMenuGroup(DIFF_NAVIGATE_GROUP)
+ .toolBarIcon(PREVIOUS_DIFF_ICON)
+ .toolBarGroup(DIFF_NAVIGATE_GROUP)
+ .keyBinding("ctrl alt P")
+ .validContextWhen(c -> isValidPanelContext(c))
+ .enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
+ .onAction(c -> nextAreaDiff(false))
+ .build();
+
+ toggleHeaderAction = new ToggleActionBuilder("Dual Listing Toggle Header", owner)
+ .description("Toggle Format Header")
+ .menuPath("Show Listing Format Header")
+ .menuGroup("Listing Group")
+ .helpLocation(new HelpLocation(HELP_TOPIC, "Dual Listing Toggle Format Header"))
+ .onAction(c -> displays.get(LEFT).showHeader(toggleHeaderAction.isSelected()))
+ .build();
+
+ toggleHoverAction = new ToggleActionBuilder("Dual Listing Toggle Mouse Hover Popups", owner)
+ .description("Toggles Mouse Hover Popups")
+ .toolBarIcon(HOVER_ON_ICON)
+ .helpLocation(
+ new HelpLocation(HELP_TOPIC, "Dual Listing Toggle Mouse Hover Popups"))
+ .enabledWhen(c -> isShowing())
+ .selected(true)
+ .onAction(c -> setHover(toggleHoverAction.isSelected()))
+ .build();
+ nextPreviousAreaTypeAction =
+ new MultiStateActionBuilder("Dual Listing Next/Previous Area Marker",
+ owner)
+ .description("Set Navigate Next/Previous Area Marker options")
+ .helpLocation(
+ new HelpLocation(HELP_TOPIC, "Dual Listing Next/Previous Area Marker"))
+ .toolBarIcon(DIFF_ICON)
+ .toolBarGroup(DIFF_NAVIGATE_GROUP)
+ .addState("All Area Markers", BOTH_VIEWS_ICON, NavigateType.ALL)
+ .addState("Unmatched Area Markers", UNMATCHED_ICON, NavigateType.UNMATCHED)
+ .addState("Diff AreaMarkers", DIFF_ICON, NavigateType.DIFF)
+ .enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
+ .onActionStateChanged((s, t) -> adjustNextPreviousAreaType(s.getUserData()))
+ .build();
+
+ optionsAction = new ActionBuilder("Listing Code Comparison Options", owner)
+ .description("Show the tool options for the Listing Code Comparison.")
+ .popupMenuPath("Properties")
+ .helpLocation(new HelpLocation(HELP_TOPIC, "Listing_Code_Comparison_Options"))
+ .validContextWhen(c -> isValidPanelContext(c))
+ .enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
+ .onAction(c -> showOptionsDialog())
+ .build();
+
+ applyFunctionNameAction = new FunctionNameApplyAction(owner);
+ applyEmptySignatureAction = new EmptySignatureApplyAction(owner);
+ applySignatureAction = new SignatureWithDatatypesApplyAction(owner);
+
+ }
+
+ private void showOptionsDialog() {
+ OptionsService service = tool.getService(OptionsService.class);
+ service.showOptionsDialog(ListingCodeComparisonOptions.OPTIONS_CATEGORY_NAME,
+ "Listing Code Comparison");
+ }
+
+ private void adjustNextPreviousAreaType(NavigateType type) {
+ String typeString = getTypeName(type);
+ nextDiffAction.getPopupMenuData()
+ .setMenuPath(new String[] { "Go to Next " + typeString + " Area" });
+ nextDiffAction.setDescription("Go to the next " + typeString + " area");
+ previousDiffAction.getPopupMenuData()
+ .setMenuPath(new String[] { "Go to Previous " + typeString + " Area" });
+ previousDiffAction.setDescription("Go to the previous " + typeString + " area");
+
+ }
+
+ private String getTypeName(NavigateType type) {
+ switch (type) {
+ case DIFF:
+ return "Difference";
+ case UNMATCHED:
+ return "Unmatched";
+ case ALL:
+ return "Highlighted";
+ default:
+ throw new AssertException("Unexpected navigate type" + type);
+ }
+ }
+
+ private void setHover(boolean enabled) {
+ toggleHoverAction.getToolBarData().setIcon(enabled ? HOVER_ON_ICON : HOVER_OFF_ICON);
+ displays.each(d -> d.setHoverMode(enabled));
+ }
+
+ private boolean isValidPanelContext(ActionContext context) {
+ if (!(context instanceof CodeComparisonPanelActionContext comparisonContext)) {
+ return false;
+ }
+ CodeComparisonPanel comparisonPanel = comparisonContext.getCodeComparisonPanel();
+ return comparisonPanel == this;
+ }
+
+ @Override
+ protected void comparisonDataChanged() {
+ addressCorrelator = createCorrelator();
+ updateProgramViews();
+ updateCoordinator();
+ updateListingDiff();
+ initializeCursorMarkers();
+ updateActionEnablement();
+ validate();
+ }
+
+ private void updateCoordinator() {
+ if (coordinator != null) {
+ coordinator.dispose();
+ coordinator = null;
+ }
+ if (listingsLocked) {
+ coordinator = new ListingCoordinator(displays, addressCorrelator);
+ coordinator.sync(activeSide);
+ }
+ }
+
+ private ListingAddressCorrelation createCorrelator() {
+ Function f1 = getFunction(LEFT);
+ Function f2 = getFunction(RIGHT);
+ if (f1 != null && f2 != null) {
+ try {
+ return new HashedFunctionAddressCorrelation(f1, f2, TaskMonitor.DUMMY);
+ }
+ catch (CancelledException | MemoryAccessException e) {
+ // fall back to linear address correlation
+ }
+ }
+ if (comparisonData.get(LEFT).isEmpty() || comparisonData.get(RIGHT).isEmpty()) {
+ return null;
+ }
+ return new LinearAddressCorrelation(comparisonData);
+ }
+
+ private void updateListingDiff() {
+ try {
+ listingDiff.setCorrelation(addressCorrelator);
+ }
+ catch (MemoryAccessException e) {
+ Msg.error(this, "Failed to load correlator", e);
+ }
+ }
+
+ /**
+ * Establishes the location and display of the arrow cursor. This method should be called after
+ * the function comparison window is loaded with functions, data, etc.
+ */
+ private void initializeCursorMarkers() {
+ ProgramLocation activeProgramLocation = displays.get(activeSide).getProgramLocation();
+ programLocationChanged(activeSide, activeProgramLocation, null);
}
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
@@ -2133,722 +582,75 @@ public class ListingCodeComparisonPanel
return null;
}
- /**
- * Adds the indicated button press listener to both listing panels in this code comparison
- * panel.
- *
- * @param listener the listener
- */
- public void addButtonPressedListener(ButtonPressedListener listener) {
- for (ListingPanel listingPanel : listingPanels) {
- listingPanel.addButtonPressedListener(listener);
- }
- }
-
- /**
- * Repaints both the left and right listing panels if they are visible.
- */
- public void updateListings() {
- if (!isVisible()) {
- return;
- }
- listingPanels[LEFT].repaint();
- listingPanels[RIGHT].repaint();
- }
-
- /**
- * Gets the indicated (LEFT or RIGHT) side's address that is equivalent to the other side's
- * address.
- *
- * @param leftOrRight LEFT or RIGHT indicating which side's address is needed.
- * @param otherSidesAddress the address for the other side. If leftOrRight = LEFT, then this
- * should be a right side address. If leftOrRight = RIGHT, then this should be a left
- * side address.
- * @return an address for the indicated side (LEFT or RIGHT) that is equivalent to the other
- * side's address that is specified. Otherwise, null.
- */
- private Address getAddress(int leftOrRight, Address otherSidesAddress) {
- if (isFunctionCompare()) {
- return getFunctionAddress(leftOrRight, otherSidesAddress);
- }
- if (isDataCompare()) {
- return getDataAddress(leftOrRight, otherSidesAddress);
- }
- return null;
- }
-
- /**
- * Gets an address in the program indicated by leftOrRight that matches the
- * otherSidesAddress that is an address in a function in the other program.
- *
- * @param leftOrRight indicates whether to get the address from the LEFT or RIGHT program.
- * @param otherSidesAddress address in the other program that is equivalent to the desired
- * address.
- * @return the matching address in the indicated program or null.
- */
- private Address getFunctionAddress(int leftOrRight, Address otherSidesAddress) {
- // Try to get the address using the correlator.
- // If the correlator couldn't determine it, then try to infer it.
- int otherSide = (leftOrRight == RIGHT) ? LEFT : RIGHT;
- // Finding desired side's address.
- Address desiredSidesAddress =
- (leftOrRight == RIGHT) ? getRightCorrelatedAddress(otherSidesAddress)
- : getLeftCorrelatedAddress(otherSidesAddress);
- if (desiredSidesAddress != null) {
- return desiredSidesAddress;
- }
- // Couldn't directly correlate the address.
- CodeUnit otherCodeUnit =
- programs[otherSide].getListing().getCodeUnitContaining(otherSidesAddress);
- if (otherCodeUnit == null) {
- return null; // Can't get the code unit's address.
- }
- Address otherCodeUnitAddress = otherCodeUnit.getMinAddress();
- Address desiredCodeUnitAddress =
- (leftOrRight == RIGHT) ? getRightCorrelatedAddress(otherCodeUnitAddress)
- : getLeftCorrelatedAddress(otherCodeUnitAddress);
- if (desiredCodeUnitAddress == null) {
- return null; // Can't match the code unit address either.
- }
- return inferDesiredFunctionAddress(otherCodeUnitAddress, desiredCodeUnitAddress,
- otherSidesAddress, programs[otherSide], programs[leftOrRight]);
- }
-
- private Address getDataAddress(int leftOrRight, Address otherSidesAddress) {
- // Correlator doesn't handle data compare, so associate beginning of data and
- // infer the others based on relative position.
- Address leftDataAddress = getLeftDataAddress();
- Address rightDataAddress = getRightDataAddress();
- if (leftOrRight == RIGHT) {
- // Finding right side address.
- return inferDesiredDataAddress(leftDataAddress, rightDataAddress, otherSidesAddress,
- programs[LEFT], programs[RIGHT]);
- }
- // Finding left side address.
- return inferDesiredDataAddress(rightDataAddress, leftDataAddress, otherSidesAddress,
- programs[RIGHT], programs[LEFT]);
- }
-
- /**
- * Is this panel currently comparing a function match?
- *
- * @return true if comparing functions.
- */
- private boolean isFunctionCompare() {
- Address leftFunctionAddress = getLeftFunctionAddress();
- Address rightFunctionAddress = getRightFunctionAddress();
- return (leftFunctionAddress != null && rightFunctionAddress != null);
- }
-
- /**
- * Is this panel currently comparing a data match?
- *
- * @return true if comparing data.
- */
- private boolean isDataCompare() {
- Address leftDataAddress = getLeftDataAddress();
- Address rightDataAddress = getRightDataAddress();
- return (leftDataAddress != null && rightDataAddress != null);
- }
-
- /**
- * Gets the left side address that is equivalent to the indicated right side address.
- *
- * @param rightByteAddress the right side address
- * @return the left side address or null.
- */
- private Address getLeftCorrelatedAddress(Address rightByteAddress) {
- if (correlator != null) {
- return correlator.getAddressInFirst(rightByteAddress);
- }
- return null;
- }
-
- /**
- * Gets the right side address that is equivalent to the indicated left side address.
- *
- * @param leftByteAddress the left side address
- * @return the right side address or null.
- */
- private Address getRightCorrelatedAddress(Address leftByteAddress) {
- if (correlator != null) {
- return correlator.getAddressInSecond(leftByteAddress);
- }
- return null;
- }
-
- /**
- * Gets the left side function's entry point address.
- *
- * @return the left side function's entry point address or null.
- */
- private Address getLeftFunctionAddress() {
- if (functions[LEFT] != null) {
- return functions[LEFT].getEntryPoint();
- }
- return null;
- }
-
- /**
- * Gets the right side function's entry point address.
- *
- * @return the right side function's entry point address or null.
- */
- private Address getRightFunctionAddress() {
- if (functions[RIGHT] != null) {
- return functions[RIGHT].getEntryPoint();
- }
- return null;
- }
-
- /**
- * Gets the left side data's minimum address.
- *
- * @return the left side data's minimum address or null.
- */
- private Address getLeftDataAddress() {
- if (data[LEFT] != null) {
- return data[LEFT].getMinAddress();
- }
- return null;
- }
-
- /**
- * Gets the right side data's minimum address.
- *
- * @return the right side data's minimum address or null.
- */
- private Address getRightDataAddress() {
- if (data[RIGHT] != null) {
- return data[RIGHT].getMinAddress();
- }
- return null;
- }
-
- private class LeftLocationListener implements FieldLocationListener {
-
- @Override
- public void fieldLocationChanged(FieldLocation location, Field field,
- EventTrigger trigger) {
- if (fieldLocationChanging) {
- return;
- }
- try {
- fieldLocationChanging = true;
-
- ProgramLocation leftProgramLocation = listingPanels[LEFT].getProgramLocation();
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator =
- getFieldPanelCoordinator();
- // Only set other side's cursor if we are coordinating right now.
- ProgramLocation rightProgramLocation =
- (fieldPanelCoordinator != null) ? getProgramLocation(RIGHT, leftProgramLocation)
- : null;
-
- setCursorMarkers(LEFT, leftProgramLocation);
- setCursorMarkers(RIGHT, rightProgramLocation);
- if (rightProgramLocation == null) {
- //remove obsolete cursor background highlight for right-hand cursor
- listingPanels[RIGHT].getFieldPanel().repaint();
- return;
- }
-
- if (fieldPanelCoordinator != null) {
- fieldPanelCoordinator.leftLocationChanged(leftProgramLocation);
- setRightLocation(getRightProgram(), rightProgramLocation);
- }
- }
- finally {
- fieldLocationChanging = false;
- }
- }
- }
-
- private class RightLocationListener implements FieldLocationListener {
-
- @Override
- public void fieldLocationChanged(FieldLocation location, Field field,
- EventTrigger trigger) {
- if (fieldLocationChanging) {
- return;
- }
- try {
- fieldLocationChanging = true;
-
- ProgramLocation rightProgramLocation = listingPanels[RIGHT].getProgramLocation();
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator =
- getFieldPanelCoordinator();
- // Only set other side's cursor if we are coordinating right now.
- ProgramLocation leftProgramLocation =
- (fieldPanelCoordinator != null) ? getProgramLocation(LEFT, rightProgramLocation)
- : null;
-
- setCursorMarkers(RIGHT, rightProgramLocation);
- setCursorMarkers(LEFT, leftProgramLocation);
- if (leftProgramLocation == null) {
- //remove obsolete cursor background highlight for left-hand cursor
- listingPanels[LEFT].getFieldPanel().repaint();
- return;
- }
-
- if (fieldPanelCoordinator != null) {
- fieldPanelCoordinator.rightLocationChanged(rightProgramLocation);
- setLeftLocation(getLeftProgram(), leftProgramLocation);
- }
- }
- finally {
- fieldLocationChanging = false;
- }
- }
- }
-
- @Override
- public void formatModelAdded(FieldFormatModel model) {
- changeRightToMatchLeftFormat(model);
- }
-
- @Override
- public void formatModelChanged(FieldFormatModel model) {
- changeRightToMatchLeftFormat(model);
- }
-
- @Override
- public void formatModelRemoved(FieldFormatModel model) {
- changeRightToMatchLeftFormat(model);
+ private void initializeOptions() {
+ comparisonOptions = new ListingCodeComparisonOptions();
+ ToolOptions options = tool.getOptions(ListingCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
+ options.addOptionsChangeListener(this);
+ comparisonOptions.initializeOptions(options);
+ comparisonOptions.loadOptions(options);
}
private void changeRightToMatchLeftFormat(FieldFormatModel model) {
SaveState saveState = new SaveState();
- listingPanels[LEFT].getFormatManager().saveState(saveState);
- listingPanels[RIGHT].getFormatManager().readState(saveState);
+ displays.get(LEFT).getFormatManager().saveState(saveState);
+ displays.get(RIGHT).getFormatManager().readState(saveState);
}
- /**
- * Gets the left or right listing panel that contains the indicated field panel.
- *
- * @param fieldPanel the field panel
- * @return the listing panel or null.
- */
- public ListingPanel getListingPanel(FieldPanel fieldPanel) {
- if (listingPanels[LEFT].getFieldPanel() == fieldPanel) {
- return listingPanels[LEFT];
- }
- if (listingPanels[RIGHT].getFieldPanel() == fieldPanel) {
- return listingPanels[RIGHT];
- }
- return null;
+ private void updateProgramViews() {
+ displays.get(LEFT).setProgramView(getProgram(LEFT), getAddresses(LEFT), "listing1");
+ displays.get(RIGHT).setProgramView(getProgram(RIGHT), getAddresses(RIGHT), "listing2");
}
- @Override
- public FormatManager getFormatManager() {
- return listingPanels[LEFT].getFormatManager();
- }
-
- /**
- * Disable mouse navigation from within this dual listing panel.
- *
- * @param enabled false disables navigation
- */
- @Override
- public void setMouseNavigationEnabled(boolean enabled) {
- // always remove before adding to avoid double adding
- listingPanels[LEFT].removeButtonPressedListener(fieldNavigators[LEFT]);
- listingPanels[RIGHT].removeButtonPressedListener(fieldNavigators[RIGHT]);
-
- if (enabled) {
- listingPanels[LEFT].addButtonPressedListener(fieldNavigators[LEFT]);
- listingPanels[RIGHT].addButtonPressedListener(fieldNavigators[RIGHT]);
- }
- }
-
- @Override
- public void loadData(Data leftData, Data rightData) {
- clearCorrelation();
- Program leftProgram = (leftData != null) ? leftData.getProgram() : null;
- Program rightProgram = (rightData != null) ? rightData.getProgram() : null;
- // Try to show matching code units based on the size of the larger data item.
- long maxOffset = getMaxOffset(leftData, rightData);
- AddressSetView leftAddressSet = EMPTY_ADDRESS_SET;
- if (leftData != null) {
- Address leftMinAddress = leftData.getMinAddress();
- Address leftEndAddress = getEndAddress(leftProgram, maxOffset, leftMinAddress);
- leftAddressSet = new AddressSet(leftMinAddress, leftEndAddress);
- }
- AddressSetView rightAddressSet = EMPTY_ADDRESS_SET;
- if (rightData != null) {
- Address rightMinAddress = rightData.getMinAddress();
- Address rightEndAddress = getEndAddress(rightProgram, maxOffset, rightMinAddress);
- rightAddressSet = new AddressSet(rightMinAddress, rightEndAddress);
- }
- setPrograms(leftProgram, rightProgram);
- // Adjust the data and functions only after the correct programs are set.
- functions[LEFT] = null;
- functions[RIGHT] = null;
- data[LEFT] = leftData;
- data[RIGHT] = rightData;
- // Adjust the addresses only after the Data is set.
- loadAddresses(leftProgram, rightProgram, leftAddressSet, rightAddressSet);
- setDataTitles();
- updateActionEnablement();
- }
-
- /**
- * Gets the maximum offset based on the larger data that is passed to this method.
- *
- * @param leftData the left view's data
- * @param rightData the right view's data
- * @return the maximum offset (one less than the larger data item's size).
- */
- private long getMaxOffset(Data leftData, Data rightData) {
- long leftOffset = 0;
- if (leftData != null) {
- leftOffset = leftData.getMaxAddress().subtract(leftData.getMinAddress());
- }
- long rightOffset = 0;
- if (rightData != null) {
- rightOffset = rightData.getMaxAddress().subtract(rightData.getMinAddress());
- }
- long maxOffset = Math.max(leftOffset, rightOffset);
- return maxOffset;
- }
-
- /**
- * Gets the ending address to be displayed. It tries to get an ending address that is maxOffset
- * number of bytes beyond the minAddress without leaving the memory block that contains the
- * minAddress. If the maxOffset is beyond the end of the block then the end of the block is
- * returned. For an externalAddress the minAddress is returned.
- *
- * @param program the program containing the data
- * @param maxOffset the max offset
- * @param minAddress the minimum address of the data
- * @return the end address to display
- */
- private Address getEndAddress(Program program, long maxOffset, Address minAddress) {
- if (minAddress.isExternalAddress()) {
- return minAddress; // Begin and end address are same for external data.
- }
- MemoryBlock block = program.getMemory().getBlock(minAddress);
- Address blockEnd = block.getEnd();
- Address endAddress;
- try {
- endAddress = minAddress.add(maxOffset);
- if (endAddress.compareTo(blockEnd) > 0) {
- endAddress = blockEnd;
- }
- }
- catch (AddressOutOfBoundsException e) {
- endAddress = blockEnd;
- }
- return endAddress;
- }
-
- /**
- * Clears the address correlation being used with the ListingDiff and the dual listing field
- * panel coordinator.
- */
- private void clearCorrelation() {
- correlator = null;
- try {
- listingDiff.setCorrelation(correlator);
- // Setting the correlation will also reset the locked line numbers.
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator =
- getFieldPanelCoordinator();
- if (fieldPanelCoordinator != null) {
- fieldPanelCoordinator.setCorrelation(correlator);
- }
- }
- catch (MemoryAccessException e) {
- Msg.error(this, "Couldn't clear the address correlator for the dual listing.", e);
- }
- }
-
- /**
- * Gets the data loaded in the left listing panel.
- *
- * @return the data or null
- */
- @Override
- public Data getLeftData() {
- return data[LEFT];
- }
-
- /**
- * Gets the data loaded in the right listing panel.
- *
- * @return the data or null
- */
- @Override
- public Data getRightData() {
- return data[RIGHT];
- }
-
- private class DualListingMarkerManager extends MarkerManager {
-
- private DualListingServiceProvider serviceProvider;
-
- private DualListingMarkerManager(String Owner, PluginTool tool,
- DualListingServiceProvider serviceProvider) {
- super(owner, tool);
- this.serviceProvider = serviceProvider;
- }
-
- @Override
- public GoToService getGoToService() {
- return serviceProvider.getService(GoToService.class);
- }
- }
-
- @Override
- public void listingDiffChanged() {
- setDiffHighlights();
- }
-
- /**
- * Displays the indicated text int the tool's status area.
- *
- * @param text the message to display
- */
- void setStatusInfo(String text) {
- tool.setStatusInfo(text);
- }
-
- @Override
- public void refreshLeftPanel() {
- // Listing will update automatically, so just update title.
- setLeftTitle(getFunctionTitle(functions[LEFT]));
- }
-
- @Override
- public void refreshRightPanel() {
- // Listing will update automatically, so just update title.
- setRightTitle(getFunctionTitle(functions[RIGHT]));
- }
-
- @Override
- public void programRestored(Program program) {
- if (getLeftProgram() == program) {
- setLeftTitle(getFunctionTitle(functions[LEFT]));
- }
- if (getRightProgram() == program) {
- setRightTitle(getFunctionTitle(functions[RIGHT]));
- }
- }
-
- @Override
- public boolean leftPanelHasFocus() {
- return currProgramIndex == LEFT;
- }
-
- @Override
- public void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix) {
- this.leftTitlePrefix = leftTitlePrefix;
- this.rightTitlePrefix = rightTitlePrefix;
- setLeftTitle(leftTitle);
- setRightTitle(rightTitle);
- }
-
- private class DualListingMouseListener extends MouseAdapter {
-
- private int leftOrRight;
-
- DualListingMouseListener(int leftOrRight) {
- this.leftOrRight = leftOrRight;
- }
-
- @Override
- public void mousePressed(MouseEvent e) {
- setDualPanelFocus(leftOrRight);
- }
- }
-
- /**
- * Gets the GoToService that is used for either the left listing or the right listing.
- *
- * @param isLeftSide true means get the GoToService for the left side listing. false means get
- * it for the right side listing.
- * @return the goToService
- */
- GoToService getGoToService(boolean isLeftSide) {
- return dualListingServiceProviders[isLeftSide ? LEFT : RIGHT].getService(GoToService.class);
- }
-
- public ActionContext getActionContext(MouseEvent event, ComponentProvider provider) {
- Object source = (event != null) ? event.getSource() : null;
- Component sourceComponent = (source instanceof Component) ? (Component) source : null;
- // Is the action being taken on the dual listing.
- if (this.isAncestorOf(sourceComponent)) {
- ListingPanel sourcePanel = getLeftPanel();
- ListingPanel destinationPanel = getRightPanel();
-
- // Are we on a marker margin of the left listing? Return that margin's context.
- Object sourceMarginContextObject = getContextObjectForMarginPanels(sourcePanel, event);
- if (sourceMarginContextObject != null) {
- return new DefaultActionContext(provider)
- .setContextObject(sourceMarginContextObject);
- }
- // Are we on a marker margin of the right listing? Return that margin's context.
- Object destinationMarginContextObject =
- getContextObjectForMarginPanels(destinationPanel, event);
- if (destinationMarginContextObject != null) {
- return new DefaultActionContext(provider)
- .setContextObject(destinationMarginContextObject);
- }
-
- // If the action is on the Field Header of the left listing panel return an
- // appropriate context for the field actions.
- if (sourceComponent instanceof FieldHeaderComp) {
- FieldHeaderLocation fieldHeaderLocation =
- sourcePanel.getFieldHeader().getFieldHeaderLocation(event.getPoint());
- return new DefaultActionContext(provider).setContextObject(fieldHeaderLocation);
- }
- }
- return null;
- }
-
- /**
- * Gets a marker margin or overview margin context object if the mouse event occurred on one of
- * the GUI components for the indicated listing panel's marker margin (left edge of listing) or
- * overview margin (right edge of listing).
- *
- * @param lp The listing panel to check
- * @param event the mouse event
- * @return a marker margin context object if the event was on a margin.
- */
- public Object getContextObjectForMarginPanels(ListingPanel lp, MouseEvent event) {
- Object source = event.getSource();
- // Is event source a marker margin provider on the left side of the listing?
- List marginProviders = lp.getMarginProviders();
- for (MarginProvider marginProvider : marginProviders) {
- JComponent c = marginProvider.getComponent();
- if (c == source) {
- MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
- if (loc != null) {
- return loc; // Return the marker margin location that was clicked.
- }
- return source; // Return the margin provider that was clicked.
- }
- }
- // Is event source an overview provider on the right side of the listing?
- List overviewProviders = lp.getOverviewProviders();
- for (OverviewProvider overviewProvider : overviewProviders) {
- JComponent c = overviewProvider.getComponent();
- if (c == source) {
- return source; // Return the overview provider that was clicked.
- }
- }
- return null; // Not one of the listing panel's margin panels.
- }
-
- /**
- * Change listener that performs a repaint when a marker changes between enabled and disabled.
- */
- private class MarkerChangeListener implements ChangeListener {
-
- private int leftOrRight;
-
- private MarkerChangeListener(int leftOrRight) {
- this.leftOrRight = leftOrRight;
- }
-
- @Override
- public void stateChanged(ChangeEvent e) {
- listingPanels[leftOrRight].getFieldPanel().repaint();
- }
- }
-
- @Override
- public FieldPanel getLeftFieldPanel() {
- return getLeftPanel().getFieldPanel();
- }
-
- @Override
- public FieldPanel getRightFieldPanel() {
- return getRightPanel().getFieldPanel();
- }
-
- @Override
- protected ListingComparisonFieldPanelCoordinator createFieldPanelCoordinator() {
- ListingComparisonFieldPanelCoordinator coordinator =
- new ListingComparisonFieldPanelCoordinator(this);
- // If we already have an address correlator established then use it.
- if (correlator != null) {
- coordinator.setCorrelation(correlator);
- }
- return coordinator;
- }
-
- /**
- * Restores this panel to the indicated saved configuration state.
- *
- * @param prefix identifier to prepend to any save state names to make them unique.
- * @param saveState the configuration state to restore
- */
- public void readConfigState(String prefix, SaveState saveState) {
- showSideBySide(saveState.getBoolean(prefix + DUAL_LISTING_SIDE_BY_SIDE, true));
- setHeaderShowing(saveState.getBoolean(prefix + DUAL_LISTING_HEADER_SHOWING, false));
- }
-
- /**
- * Saves the current configuration state of this panel.
- *
- * @param prefix identifier to prepend to any save state names to make them unique.
- * @param saveState the new configuration state
- */
- public void writeConfigState(String prefix, SaveState saveState) {
- saveState.putBoolean(prefix + DUAL_LISTING_SIDE_BY_SIDE, isSideBySide());
- saveState.putBoolean(prefix + DUAL_LISTING_HEADER_SHOWING, isHeaderShowing());
- }
-
- @Override
- public void setScrollingSyncState(boolean syncScrolling) {
- // Overrides the base method so it can save and restore the coordinator sync state.
- if (isScrollingSynced() == syncScrolling) {
+ private void nextAreaDiff(boolean forward) {
+ NavigateType type = nextPreviousAreaTypeAction.getCurrentState().getUserData();
+ ListingPanel activeListingPanel = getActiveListingPanel();
+ ProgramLocation activePanelLocation = activeListingPanel.getProgramLocation();
+ if (activePanelLocation == null) {
+ tool.setStatusInfo(
+ "The " + (activeSide == LEFT ? "first" : "second") + " listing is empty.");
return;
}
- FieldPanel currentFieldPanel = listingPanels[currProgramIndex].getFieldPanel();
- ViewerPosition viewerPosition = currentFieldPanel.getViewerPosition(); // Save focused side's viewer position.
- saveCoordinatorState(); // Saves sync state when disabling synchronized scrolling.
- super.setScrollingSyncState(syncScrolling);
- // Need to restore sync point if current location can't determine matching one.
- if (!hasMatchingLocation()) {
- restoreCoordinatorState(); // Restores sync state when enabling synchronized scrolling.
- }
- currentFieldPanel.setViewerPosition(viewerPosition.getIndex(), viewerPosition.getXOffset(),
- viewerPosition.getYOffset()); // Restore the focused side's viewer position.
- }
+ Address activeAddress = activePanelLocation.getAddress();
- private boolean hasMatchingLocation() {
- ProgramLocation cursorLocation = listingPanels[currProgramIndex].getCursorLocation();
- if (cursorLocation != null) {
- Address address = cursorLocation.getAddress();
- Address otherAddress;
- if (currProgramIndex == LEFT) {
- otherAddress = getRightCorrelatedAddress(address);
+ ArrayList iteratorList = new ArrayList<>();
+
+ if (type == NavigateType.ALL || type == NavigateType.DIFF) {
+ AddressSetView activeDiffs = listingDiff.getDiffs(activeSide);
+ iteratorList.add(activeDiffs.getAddressRanges(activeAddress, forward));
+ }
+ if (type == NavigateType.ALL || type == NavigateType.UNMATCHED) {
+ AddressSetView unmatchedCode = listingDiff.getUnmatchedCode(activeSide);
+ iteratorList.add(unmatchedCode.getAddressRanges(activeAddress, forward));
+ }
+
+ MultiAddressRangeIterator multiIterator = new MultiAddressRangeIterator(
+ iteratorList.toArray(new AddressRangeIterator[iteratorList.size()]), forward);
+
+ if (multiIterator.hasNext()) {
+ AddressRange nextRange = multiIterator.next();
+ Address minAddress = nextRange.getMinAddress();
+ if ((forward ? nextRange.contains(activeAddress) : minAddress.equals(activeAddress)) &&
+ multiIterator.hasNext()) {
+ nextRange = multiIterator.next();
+ minAddress = nextRange.getMinAddress();
}
- else {
- otherAddress = getLeftCorrelatedAddress(address);
+ if (minAddress.equals(activeAddress)) {
+ outputNoNextPreviousMessage(forward, activeSide);
+ return;
}
- return (otherAddress != null);
+ tool.clearStatusInfo();
+ activeListingPanel.goTo(minAddress);
}
- return false;
- }
-
- private void saveCoordinatorState() {
- // If we can get the field panel coordinator, save its state.
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator = getFieldPanelCoordinator();
- if (fieldPanelCoordinator != null) {
- // Save the coordinator state.
- coordinatorLockedAddresses = fieldPanelCoordinator.getLockedAddresses();
+ else {
+ outputNoNextPreviousMessage(forward, activeSide);
}
}
- private void restoreCoordinatorState() {
- // If we can get the field panel coordinator, restore its state.
- ListingComparisonFieldPanelCoordinator fieldPanelCoordinator = getFieldPanelCoordinator();
- if (fieldPanelCoordinator != null && coordinatorLockedAddresses != null &&
- coordinatorLockedAddresses.length == 2) {
- // Restore the coordinator state.
- fieldPanelCoordinator.setLockedAddresses(coordinatorLockedAddresses[LEFT],
- coordinatorLockedAddresses[RIGHT]);
- }
+ private void outputNoNextPreviousMessage(boolean forward, Side side) {
+ NavigateType type = nextPreviousAreaTypeAction.getCurrentState().getUserData();
+ String typeName = getTypeName(type).toLowerCase();
+ tool.setStatusInfo("There isn't another " + (forward ? "next " : "previous ") +
+ typeName + " area in the " +
+ (side == LEFT ? "first" : "second") + " listing.");
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingComparisonFieldPanelCoordinator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingComparisonFieldPanelCoordinator.java
deleted file mode 100644
index d8ff25c7fc..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingComparisonFieldPanelCoordinator.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.util.viewer.listingpanel;
-
-import java.math.BigInteger;
-
-import docking.widgets.fieldpanel.FieldPanel;
-import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelCoordinator;
-import docking.widgets.fieldpanel.support.ViewerPosition;
-import ghidra.app.util.viewer.util.AddressIndexMap;
-import ghidra.program.model.address.Address;
-import ghidra.program.util.ListingAddressCorrelation;
-import ghidra.program.util.ProgramLocation;
-
-/**
- * Coordinates cursor location and scrolling between the two sides of a ListingCodeComparisonPanel.
- */
-public class ListingComparisonFieldPanelCoordinator extends LayoutLockedFieldPanelCoordinator
- implements DualListingFieldPanelCoordinator {
-
- private ListingCodeComparisonPanel dualListingPanel;
- private ListingAddressCorrelation addressCorrelation;
- private Address[] lockLineAddresses = new Address[2];
-
- /**
- * Constructor for this dual listing field panel coordinator.
- * @param dualListingPanel the dual listing to be controlled by this coordinator.
- */
- public ListingComparisonFieldPanelCoordinator(ListingCodeComparisonPanel dualListingPanel) {
- super(new FieldPanel[] { dualListingPanel.getLeftPanel().getFieldPanel(),
- dualListingPanel.getRightPanel().getFieldPanel() });
- this.dualListingPanel = dualListingPanel;
- }
-
- /**
- * Sets a new address correlation for associating addresses between the left and right sides.
- * The field panels can then be coordinated by locking the layouts together whenever the
- * current location on one side can be correlated with a location on the other side.
- * @param addressCorrelation the correlation to use for locking the two sides together for
- * scrolling.
- */
- public void setCorrelation(ListingAddressCorrelation addressCorrelation) {
- this.addressCorrelation = addressCorrelation;
- resetLockedLines();
- }
-
- @Override
- public void leftLocationChanged(ProgramLocation leftLocation) {
- if (addressCorrelation == null) {
- return; // Do nothing since no address correlator.
- }
- Address leftAddress = leftLocation.getAddress();
- if (leftAddress == null) {
- return; // Do nothing since can't get a location address.
- }
- // The correlation only gives a right side address for a left side address that is a code unit minimum.
- Address rightAddress = addressCorrelation.getAddressInSecond(leftAddress);
- if (rightAddress == null) {
- return; // Do nothing since can't get a matching address.
- }
- // Got an address so let's try to lock the two panels at the indexes for the matching addresses.
- setLockedAddresses(leftAddress, rightAddress);
-
- FieldPanel fp = dualListingPanel.getLeftPanel().getFieldPanel();
- adjustFieldPanel(fp);
- }
-
- @Override
- public void rightLocationChanged(ProgramLocation rightLocation) {
- if (addressCorrelation == null) {
- return; // Do nothing since no address correlator.
- }
- Address rightAddress = rightLocation.getAddress();
- if (rightAddress == null) {
- return; // Do nothing since can't get a location address.
- }
- // The correlation only gives a left side address for a right side address that is a code unit minimum.
- Address leftAddress = addressCorrelation.getAddressInFirst(rightAddress);
- if (leftAddress == null) {
- return; // Do nothing since can't get a matching address.
- }
- // Got an address so let's try to lock the two panels at the indexes for the matching addresses.
- setLockedAddresses(leftAddress, rightAddress);
-
- FieldPanel fp = dualListingPanel.getRightPanel().getFieldPanel();
- adjustFieldPanel(fp);
- }
-
- /**
- * Kicks the field panels viewChanged() method so that the field panels will realign their
- * layouts using the locked line numbers.
- *
- * @param fp the field panel that has focus.
- */
- void adjustFieldPanel(FieldPanel fp) {
- ViewerPosition viewerPosition = fp.getViewerPosition();
- BigInteger topIndex = viewerPosition.getIndex();
- int topXOffset = viewerPosition.getXOffset();
- int topYOffset = viewerPosition.getYOffset();
- viewChanged(fp, topIndex, topXOffset, topYOffset);
- }
-
- /**
- * Sets the left and right addresses that should currently be locked together for
- * synchronized scrolling.
- *
- * @param leftAddress the address in the left listing.
- * @param rightAddress the address in the right listing.
- */
- void setLockedAddresses(Address leftAddress, Address rightAddress) {
- lockLineAddresses[0] = leftAddress;
- lockLineAddresses[1] = rightAddress;
- ListingPanel leftListingPanel = dualListingPanel.getLeftPanel();
- ListingPanel rightListingPanel = dualListingPanel.getRightPanel();
- AddressIndexMap leftAddressIndexMap = leftListingPanel.getAddressIndexMap();
- AddressIndexMap rightAddressIndexMap = rightListingPanel.getAddressIndexMap();
-
- BigInteger leftIndex =
- (leftAddress != null) ? leftAddressIndexMap.getIndex(leftAddress) : null;
- BigInteger rightIndex =
- (rightAddress != null) ? rightAddressIndexMap.getIndex(rightAddress) : null;
- //lockLines will set null args to BigInteger.ZERO
- lockLines(leftIndex, rightIndex);
- }
-
- /**
- * Gets the left and right addresses that are currently locked together for synchronized
- * scrolling.
- *
- * @return an array containing the left (index 0) and right (index 1) addresses that are
- * locked together.
- */
- Address[] getLockedAddresses() {
- return lockLineAddresses;
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingComparisonProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingComparisonProvider.java
deleted file mode 100644
index 1d37b0f2e4..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingComparisonProvider.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.util.viewer.listingpanel;
-
-import javax.swing.Icon;
-
-import generic.theme.GIcon;
-import ghidra.framework.plugintool.ComponentProviderAdapter;
-import ghidra.framework.plugintool.PluginTool;
-import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.listing.Program;
-
-/**
- * Provider for displaying a ListingCodeComparisonPanel.
- */
-public class ListingComparisonProvider extends ComponentProviderAdapter {
-
- private static final Icon DUAL_LISTING_ICON =
- new GIcon("icon.base.util.listingcompare.provider");
- private ListingCodeComparisonPanel dualListingPanel;
-
- /**
- * Constructor for a provider that can display a ListingCodeComparisonPanel.
- * @param tool the tool that contains this provider.
- * @param name the owner of this provider, which is usually a plugin name.
- * @param p1 program for the listing displayed in the left side of the panel.
- * @param p2 program for the listing displayed in the right side of the panel.
- * @param set1 the address set indicating the portion of the listing displayed in the left side
- * of the panel.
- * @param set2 the address set indicating the portion of the listing displayed in the right side
- * of the panel.
- */
- public ListingComparisonProvider(PluginTool tool, String name, Program p1, Program p2,
- AddressSetView set1, AddressSetView set2) {
- super(tool, "Listing Comparison", name);
- setIcon(DUAL_LISTING_ICON);
- dualListingPanel = new ListingCodeComparisonPanel(name, tool);
- dualListingPanel.loadAddresses(p1, p2, set1, set2);
- setTransient();
- tool.addComponentProvider(this, true);
- }
-
- @Override
- public ListingCodeComparisonPanel getComponent() {
- return dualListingPanel;
- }
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCoordinator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCoordinator.java
new file mode 100644
index 0000000000..91a63421b1
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingCoordinator.java
@@ -0,0 +1,128 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.listingpanel;
+
+import static ghidra.util.datastruct.Duo.Side.*;
+
+import java.math.BigInteger;
+
+import docking.widgets.fieldpanel.FieldPanel;
+import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelCoordinator;
+import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
+import docking.widgets.fieldpanel.support.ViewerPosition;
+import ghidra.app.util.viewer.util.AddressIndexMap;
+import ghidra.program.model.address.Address;
+import ghidra.program.util.ListingAddressCorrelation;
+import ghidra.program.util.ProgramLocation;
+import ghidra.util.datastruct.Duo;
+import ghidra.util.datastruct.Duo.Side;
+
+/**
+ * Keeps two listing panels synchronized, both the view and cursor location
+ */
+public class ListingCoordinator {
+ private Duo displays;
+ private Duo lockLineAddresses = new Duo<>();
+
+ private ProgramLocationTranslator locationTranslator;
+ private LineLockedFieldPanelCoordinator viewCoordinator;
+
+ ListingCoordinator(Duo displays, ListingAddressCorrelation correlator) {
+ this.displays = displays;
+ this.locationTranslator = new ProgramLocationTranslator(correlator);
+ FieldPanel left = displays.get(LEFT).getListingPanel().getFieldPanel();
+ FieldPanel right = displays.get(RIGHT).getListingPanel().getFieldPanel();
+ viewCoordinator = new LayoutLockedFieldPanelCoordinator(left, right);
+ }
+
+ /**
+ * notification that the given side change to the given location
+ * @param side the side that changed
+ * @param location the location from the given side
+ */
+ void setLocation(Side side, ProgramLocation location) {
+
+ // Only set other side's cursor if we are coordinating right now.
+ Side otherSide = side.otherSide();
+ ProgramLocation otherLocation = locationTranslator.getProgramLocation(otherSide, location);
+
+ if (otherLocation != null) {
+ updateViewCoordinator(side, location, otherLocation);
+ displays.get(otherSide).goTo(otherLocation);
+ displays.get(side.otherSide()).updateCursorMarkers(otherLocation);
+ }
+
+ }
+
+ void dispose() {
+ viewCoordinator.dispose();
+ }
+
+ /**
+ * synchronized the two listings using the given side as the source
+ * @param side to synchronize from
+ */
+ void sync(Side side) {
+ adjustFieldPanel(displays.get(side).getListingPanel().getFieldPanel());
+ ProgramLocation programLocation = displays.get(side).getProgramLocation();
+ if (programLocation != null) {
+ setLocation(side, programLocation);
+ }
+ }
+
+ /**
+ * Kicks the field panels viewChanged() method so that the field panels will realign their
+ * layouts using the locked line numbers.
+ *
+ * @param fieldPanel the field panel that has focus.
+ */
+ private void adjustFieldPanel(FieldPanel fieldPanel) {
+ ViewerPosition viewerPosition = fieldPanel.getViewerPosition();
+ BigInteger topIndex = viewerPosition.getIndex();
+ int topXOffset = viewerPosition.getXOffset();
+ int topYOffset = viewerPosition.getYOffset();
+ viewCoordinator.viewChanged(fieldPanel, topIndex, topXOffset, topYOffset);
+ }
+
+ /**
+ * Sets the left and right addresses that should currently be locked together for
+ * synchronized scrolling.
+ *
+ * @param leftAddress the address in the left listing.
+ * @param rightAddress the address in the right listing.
+ */
+ private void setLockedAddresses(Address leftAddress, Address rightAddress) {
+ if (leftAddress == null || rightAddress == null) {
+ return;
+ }
+ lockLineAddresses = new Duo<>(leftAddress, rightAddress);
+ AddressIndexMap leftMap = displays.get(LEFT).getListingPanel().getAddressIndexMap();
+ AddressIndexMap rightMap = displays.get(RIGHT).getListingPanel().getAddressIndexMap();
+
+ BigInteger leftIndex = leftMap.getIndex(leftAddress);
+ BigInteger rightIndex = rightMap.getIndex(rightAddress);
+ viewCoordinator.lockLines(leftIndex, rightIndex);
+ }
+
+ private void updateViewCoordinator(Side side, ProgramLocation location,
+ ProgramLocation otherLocation) {
+ Address leftAddress = side == LEFT ? location.getAddress() : otherLocation.getAddress();
+ Address rightAddress = side == LEFT ? otherLocation.getAddress() : location.getAddress();
+ setLockedAddresses(leftAddress, rightAddress);
+ FieldPanel fp = displays.get(side).getListingPanel().getFieldPanel();
+ adjustFieldPanel(fp);
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffActionManager.java
index 749dd7f864..cb226dc422 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffActionManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffActionManager.java
@@ -15,6 +15,9 @@
*/
package ghidra.app.util.viewer.listingpanel;
+import java.util.Arrays;
+import java.util.List;
+
import javax.swing.Icon;
import docking.ActionContext;
@@ -71,9 +74,9 @@ public class ListingDiffActionManager {
* Gets the actions.
* @return the docking actions.
*/
- public DockingAction[] getActions() {
- return new DockingAction[] { toggleIgnoreByteDiffsAction, toggleIgnoreConstantsAction,
- toggleIgnoreRegisterNamesAction };
+ public List getActions() {
+ return Arrays.asList(toggleIgnoreByteDiffsAction, toggleIgnoreConstantsAction,
+ toggleIgnoreRegisterNamesAction);
}
/**
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffHighlightProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffHighlightProvider.java
index d4e2711648..17315b1497 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffHighlightProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDiffHighlightProvider.java
@@ -16,7 +16,7 @@
package ghidra.app.util.viewer.listingpanel;
import java.awt.Color;
-import java.util.ArrayList;
+import java.util.*;
import docking.widgets.fieldpanel.support.Highlight;
import ghidra.app.util.ListingHighlightProvider;
@@ -26,6 +26,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Instruction;
import ghidra.program.util.ListingDiff;
+import ghidra.util.datastruct.Duo.Side;
public class ListingDiffHighlightProvider implements ListingHighlightProvider {
@@ -33,22 +34,22 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
private static final String DEFAULT_OPERAND_SEPARATOR = ",";
private ListingDiff listingDiff;
- private boolean isListing1;
+ private Side side;
private ListingCodeComparisonOptions comparisonOptions;
/**
* Constructor for this highlight provider.
* @param listingDiff the ListingDiff to use to determine where there are differences that
* need highlighting.
- * @param isListing1 true means that these are the highlights for the first listing.
+ * @param side LEFT or RIGHT
* false means the highlights are for the second listing.
* @param comparisonOptions the tool options that indicate the current
* background colors for the Listing code comparison panel.
*/
- public ListingDiffHighlightProvider(ListingDiff listingDiff, boolean isListing1,
+ public ListingDiffHighlightProvider(ListingDiff listingDiff, Side side,
ListingCodeComparisonOptions comparisonOptions) {
this.listingDiff = listingDiff;
- this.isListing1 = isListing1;
+ this.side = side;
this.comparisonOptions = comparisonOptions;
}
@@ -77,14 +78,12 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
private Highlight[] getByteDiffHighlights(String text, CodeUnit codeUnit,
int cursorTextOffset) {
Address minAddress = codeUnit.getMinAddress();
- AddressSetView unmatchedDiffs = (isListing1) ? listingDiff.getListing1UnmatchedCode()
- : listingDiff.getListing2UnmatchedCode();
+ AddressSetView unmatchedDiffs = listingDiff.getUnmatchedCode(side);
if (unmatchedDiffs.contains(minAddress)) {
return NO_HIGHLIGHTS;
}
Color byteDiffsBackgroundColor = comparisonOptions.getByteDiffsBackgroundColor();
- AddressSetView byteDiffs =
- (isListing1) ? listingDiff.getListing1ByteDiffs() : listingDiff.getListing2ByteDiffs();
+ AddressSetView byteDiffs = listingDiff.getByteDiffs(side);
// Get intersection of Byte Diff addresses and this code unit's addresses
AddressSet diffSet = new AddressSet(codeUnit.getMinAddress(), codeUnit.getMaxAddress());
diffSet = diffSet.intersect(byteDiffs);
@@ -108,19 +107,17 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
private Highlight[] getMnemonicDiffHighlights(String text, CodeUnit codeUnit,
int cursorTextOffset) {
Address minAddress = codeUnit.getMinAddress();
- AddressSetView unmatchedDiffs = (isListing1) ? listingDiff.getListing1UnmatchedCode()
- : listingDiff.getListing2UnmatchedCode();
+ AddressSetView unmatchedDiffs = listingDiff.getUnmatchedCode(side);
if (unmatchedDiffs.contains(minAddress)) {
return NO_HIGHLIGHTS;
}
Color mnemonicDiffsBackgroundColor = comparisonOptions.getMnemonicDiffsBackgroundColor();
- AddressSetView codeUnitDiffs = (isListing1) ? listingDiff.getListing1CodeUnitDiffs()
- : listingDiff.getListing2CodeUnitDiffs();
+ AddressSetView codeUnitDiffs = listingDiff.getCodeUnitDiffs(side);
// Get intersection of Code Unit Diff addresses and this code unit's addresses
AddressSet diffSet = new AddressSet(codeUnit.getMinAddress(), codeUnit.getMaxAddress());
diffSet = diffSet.intersect(codeUnitDiffs);
if (!diffSet.isEmpty()) {
- CodeUnit otherCodeUnit = listingDiff.getMatchingCodeUnit(codeUnit, isListing1);
+ CodeUnit otherCodeUnit = listingDiff.getMatchingCodeUnit(codeUnit, side);
if (otherCodeUnit == null) {
return entireTextHighlight(text, cursorTextOffset, mnemonicDiffsBackgroundColor);
}
@@ -137,33 +134,33 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
private Highlight[] getOperandDiffHighlights(String text, CodeUnit codeUnit,
int cursorTextOffset) {
Address minAddress = codeUnit.getMinAddress();
- AddressSetView unmatchedDiffs = (isListing1) ? listingDiff.getListing1UnmatchedCode()
- : listingDiff.getListing2UnmatchedCode();
+ AddressSetView unmatchedDiffs = listingDiff.getUnmatchedCode(side);
if (unmatchedDiffs.contains(minAddress)) {
return NO_HIGHLIGHTS;
}
Color operandDiffsBackgroundColor = comparisonOptions.getOperandDiffsBackgroundColor();
- AddressSetView codeUnitDiffs = (isListing1) ? listingDiff.getListing1CodeUnitDiffs()
- : listingDiff.getListing2CodeUnitDiffs();
+ AddressSetView codeUnitDiffs = listingDiff.getCodeUnitDiffs(side);
// Get intersection of Code Unit Diff addresses and this code unit's addresses
AddressSet diffSet = new AddressSet(codeUnit.getMinAddress(), codeUnit.getMaxAddress());
diffSet = diffSet.intersect(codeUnitDiffs);
if (!diffSet.isEmpty()) {
- CodeUnit matchingCodeUnit = listingDiff.getMatchingCodeUnit(codeUnit, isListing1);
+ CodeUnit matchingCodeUnit = listingDiff.getMatchingCodeUnit(codeUnit, side);
if (listingDiff.doesEntireOperandSetDiffer(codeUnit, matchingCodeUnit)) {
return entireTextHighlight(text, cursorTextOffset, operandDiffsBackgroundColor);
}
- Pair[] pairs = getOperandPairs(text, codeUnit);
+ List operandRanges = getOperandRanges(text, codeUnit);
int numOperands = codeUnit.getNumOperands();
- if (pairs.length != numOperands) {
+ if (operandRanges.size() != numOperands) {
return entireTextHighlight(text, cursorTextOffset, operandDiffsBackgroundColor);
}
int[] diffOpIndices = listingDiff.getOperandsThatDiffer(codeUnit, matchingCodeUnit);
ArrayList highlights = new ArrayList<>();
for (int diffOpIndex : diffOpIndices) {
// Highlight each operand that differs.
- highlights.add(new Highlight(pairs[diffOpIndex].start, pairs[diffOpIndex].end,
- operandDiffsBackgroundColor));
+ highlights.add(
+ new Highlight(operandRanges.get(diffOpIndex).start,
+ operandRanges.get(diffOpIndex).end,
+ operandDiffsBackgroundColor));
}
return highlights.toArray(new Highlight[highlights.size()]);
}
@@ -174,15 +171,15 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
* Gets an array of start/end positions for each operand within the operand field's full text.
* @param text the full text from the operand field
* @param codeUnit the code unit whose operand text is provided
- * @return the operand pairs indicating the start and end offsets for each individual operand
+ * @return the operand ranges indicating the start and end offsets for each individual operand
* within the text.
*/
- private Pair[] getOperandPairs(String text, CodeUnit codeUnit) {
+ private List getOperandRanges(String text, CodeUnit codeUnit) {
if (text == null || text.isEmpty()) {
- return new Pair[0];
+ return Collections.emptyList();
}
Instruction instruction = (codeUnit instanceof Instruction) ? (Instruction) codeUnit : null;
- ArrayList list = new ArrayList<>();
+ ArrayList list = new ArrayList<>();
int opIndex = 0;
int textLength = text.length();
int start = 0; // Start index in the text for the current operand.
@@ -197,7 +194,7 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
separatorIndex = text.indexOf(separator, start);
start = separatorIndex + separator.length();
}
- // Get a start/end Pair of indexes for each operand.
+ // Get a start/end range of indexes for each operand.
while (start < textLength) {
++opIndex; // Increment the opIndex since we find the separator before an operand.
separator = DEFAULT_OPERAND_SEPARATOR; // default separator
@@ -209,17 +206,15 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
separatorIndex =
(separator != null && !separator.isEmpty()) ? text.indexOf(separator, start) : -1;
if (separatorIndex == -1) {
- // Add the last operand's index Pair to the list.
- list.add(new Pair(start, textLength - 1));
+ list.add(new Range(start, textLength - 1));
start = textLength;
continue;
}
- // Add the current operand's index Pair to the list.
- list.add(new Pair(start, separatorIndex - 1));
+ list.add(new Range(start, separatorIndex - 1));
// Move start to the beginning of the next operand.
start = separatorIndex + separator.length();
}
- return list.toArray(new Pair[list.size()]);
+ return list;
}
private Highlight[] entireTextHighlight(String text, int cursorTextOffset, Color color) {
@@ -229,21 +224,12 @@ public class ListingDiffHighlightProvider implements ListingHighlightProvider {
return new Highlight[] { highlight };
}
- /**
- * Determines if this highlight provider is for the first listing of the ListingDiff.
- * @return true if this provider's highlights are for the first listing. false if the
- * highlights are for the second listing.
- */
- public boolean isListing1() {
- return isListing1;
- }
-
- private class Pair {
+ private class Range {
private int start;
private int end;
- private Pair(int start, int end) {
+ private Range(int start, int end) {
this.start = start;
this.end = end;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDisplay.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDisplay.java
new file mode 100644
index 0000000000..f08a2bb60c
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDisplay.java
@@ -0,0 +1,345 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.listingpanel;
+
+import static ghidra.GhidraOptions.*;
+
+import java.awt.Color;
+
+import javax.swing.Icon;
+
+import docking.widgets.fieldpanel.support.ViewerPosition;
+import generic.theme.GIcon;
+import ghidra.GhidraOptions;
+import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
+import ghidra.app.plugin.core.codebrowser.hover.*;
+import ghidra.app.plugin.core.marker.MarkerManager;
+import ghidra.app.services.*;
+import ghidra.app.util.ListingHighlightProvider;
+import ghidra.app.util.viewer.format.FormatManager;
+import ghidra.app.util.viewer.util.AddressIndexMap;
+import ghidra.app.util.viewer.util.FieldNavigator;
+import ghidra.framework.options.ToolOptions;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.framework.plugintool.ServiceProviderStub;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.program.util.ListingDiff;
+import ghidra.program.util.ProgramLocation;
+import ghidra.util.datastruct.Duo.Side;
+
+/**
+ * Represents one side of a dual listing compare window. It holds the listing panel and
+ * related state information for one side.
+ */
+public class ListingDisplay implements ListingDiffChangeListener {
+ private static final Icon CURSOR_LOC_ICON = new GIcon("icon.base.util.listingcompare.cursor");
+ private ListingPanel listingPanel;
+ private PluginTool tool;
+ private ListingDisplayServiceProvider serviceProvider;
+ private MarkerManager markerManager;
+ private ListingCodeComparisonOptions comparisonOptions;
+ private Color cursorHighlightColor;
+ private MarkerSet unmatchedMarkers;
+ private MarkerSet diffMarkers;
+ private MarkerSet currentCursorMarkers;
+ private ListingDiffHighlightProvider diffHighlights;
+ private FieldNavigator fieldNavigator;
+ private ListingDiff listingDiff;
+ private Side side;
+
+ public ListingDisplay(PluginTool tool, String owner, ListingDiff listingDiff,
+ ListingCodeComparisonOptions comparsionOptions, Side side) {
+ this.tool = tool;
+ this.listingDiff = listingDiff;
+ this.comparisonOptions = comparsionOptions;
+ this.side = side;
+
+ FormatManager formatManager = createFormatManager();
+ loadOptions();
+
+ listingPanel = new ListingPanel(formatManager);
+ // Turn off selection in the listings so it can be set up as desired elsewhere.
+ listingPanel.getFieldPanel().enableSelection(false);
+
+ serviceProvider = new ListingDisplayServiceProvider();
+ formatManager.setServiceProvider(serviceProvider);
+ fieldNavigator = new FieldNavigator(serviceProvider, null);
+ setMouseNavigationEnabled(true);
+ createMarkerManager(owner);
+
+ listingPanel.addHoverService(new ReferenceListingHover(tool, () -> formatManager));
+ listingPanel.addHoverService(new DataTypeListingHover(tool));
+ listingPanel.addHoverService(new TruncatedTextListingHover(tool));
+ listingPanel.addHoverService(new FunctionNameListingHover(tool));
+
+ listingDiff.addListingDiffChangeListener(this);
+ setHoverMode(true);
+ }
+
+ private void createMarkerManager(String owner) {
+ markerManager = new ListingDisplayMarkerManager(tool, owner);
+ markerManager.addChangeListener(e -> listingPanel.repaint());
+ MarginProvider marginProvider = markerManager.getMarginProvider();
+ listingPanel.addMarginProvider(marginProvider);
+ OverviewProvider overviewProvider = markerManager.getOverviewProvider();
+ listingPanel.addOverviewProvider(overviewProvider);
+ }
+
+ void setProgramLocationListener(ProgramLocationListener listener) {
+ listingPanel.setProgramLocationListener(listener);
+ }
+
+ private FormatManager createFormatManager() {
+ ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
+ ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
+
+ FormatManager formatManager = new FormatManager(displayOptions, fieldOptions);
+
+ return formatManager;
+ }
+
+ public void repaint() {
+ listingPanel.getFieldPanel().repaint();
+ }
+
+ public void setDiffHighlightProvider(ListingDiffHighlightProvider newDiffHighlights) {
+ if (diffHighlights != null) {
+ removeHighlightProvider(diffHighlights);
+ }
+
+ diffHighlights = newDiffHighlights;
+
+ if (diffHighlights != null) {
+ addHighlightProvider(diffHighlights);
+ }
+ }
+
+ public void addHighlightProvider(ListingHighlightProvider highlightProvider) {
+ listingPanel.getFormatManager().addHighlightProvider(highlightProvider);
+ }
+
+ public void removeHighlightProvider(ListingHighlightProvider highlightProvider) {
+ if (highlightProvider == null) {
+ return;
+ }
+ listingPanel.getFormatManager().removeHighlightProvider(highlightProvider);
+ }
+
+ public void addHoverService(ListingHoverService service) {
+ listingPanel.addHoverService(service);
+ }
+
+ public void showHeader(boolean show) {
+ listingPanel.showHeader(show);
+ listingPanel.validate();
+ listingPanel.invalidate();
+ }
+
+ public void setHoverMode(boolean enabled) {
+ listingPanel.setHoverMode(enabled);
+ }
+
+ public void setView(AddressSetView view) {
+ ProgramLocation saved = listingPanel.getProgramLocation();
+ listingPanel.setView(view);
+ if (saved != null) {
+ listingPanel.goTo(saved);
+ }
+ }
+
+ public boolean isHeaderShowing() {
+ return listingPanel.isHeaderShowing();
+ }
+
+ public void setProgramView(Program program, AddressSetView view, String name) {
+ listingPanel.setProgram(program);
+ markerManager.clearAll();
+ listingPanel.setView(view);
+ AddressIndexMap indexMap = listingPanel.getAddressIndexMap();
+ markerManager.getOverviewProvider().setProgram(program, indexMap);
+ listingPanel.setBackgroundColorModel(
+ new MarkerServiceBackgroundColorModel(markerManager, program, indexMap));
+ setUpAreaMarkerSets(program, name);
+ if (!view.isEmpty()) {
+ goTo(new ProgramLocation(program, view.getMinAddress()));
+ }
+ repaint();
+ }
+
+ void setUpAreaMarkerSets(Program program, String name) {
+ if (program == null) {
+ return;
+ }
+ Color diffColor = comparisonOptions.getDiffCodeUnitsBackgroundColor();
+ Color unmatchedColor = comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
+
+ AddressIndexMap indexMap = listingPanel.getAddressIndexMap();
+ listingPanel.getFieldPanel().setBackgroundColorModel(new MarkerServiceBackgroundColorModel(
+ markerManager, program, indexMap));
+
+ unmatchedMarkers = markerManager.createAreaMarker(name + " Unmatched Code",
+ "Instructions that are not matched to an instruction in the other function.",
+ program, MarkerService.DIFF_PRIORITY, true, true, true,
+ unmatchedColor);
+ diffMarkers = markerManager.createAreaMarker(name + " Diffs",
+ "Instructions that have a difference.", program, MarkerService.DIFF_PRIORITY,
+ true, true, true, diffColor);
+
+ currentCursorMarkers = markerManager.createPointMarker("Cursor",
+ "Cursor Location", program, MarkerService.FUNCTION_COMPARE_CURSOR_PRIORITY,
+ true, true, true, cursorHighlightColor, CURSOR_LOC_ICON, false);
+
+ }
+
+ public ProgramLocation getProgramLocation() {
+ return listingPanel.getProgramLocation();
+ }
+
+ private void loadOptions() {
+ ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
+ //
+ // Unusual Code Alert!
+ // In a normal tool, this option is registered by the Code Browse Plugin. In the VT
+ // tool, nobody registers this option. Our system logs a warning if an option is used
+ // but not registered. So, when in a real tool, use the registered/managed option.
+ // Otherwise, just use the default.
+ //
+ if (fieldOptions.isRegistered(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)) {
+ cursorHighlightColor = fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR,
+ DEFAULT_CURSOR_LINE_COLOR);
+ }
+ else {
+ cursorHighlightColor = DEFAULT_CURSOR_LINE_COLOR;
+ }
+
+ }
+
+ private class ListingDisplayMarkerManager extends MarkerManager {
+
+ private ListingDisplayMarkerManager(PluginTool tool, String owner) {
+ super(owner, tool);
+ }
+
+ @Override
+ public GoToService getGoToService() {
+ return serviceProvider.getService(GoToService.class);
+ }
+ }
+
+ private class ListingDisplayServiceProvider extends ServiceProviderStub {
+ private GoToService goToService;
+
+ ListingDisplayServiceProvider() {
+ goToService = new ListingDisplayGoToService(listingPanel);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getService(Class serviceClass) {
+ if (serviceClass == GoToService.class) {
+ return (T) goToService;
+ }
+ return null;
+ }
+ }
+
+ public void updateCursorMarkers(ProgramLocation location) {
+ if (currentCursorMarkers != null) {
+ currentCursorMarkers.clearAll();
+ if (location != null) {
+ currentCursorMarkers.add(location.getAddress());
+ }
+ }
+ repaint();
+ }
+
+ private void setAreaMarkers(MarkerSet markers, AddressSetView diffAddresses, Color color) {
+ if (markers == null) {
+ return;
+ }
+ markers.setMarkerColor(color);
+ markers.clearAll();
+ markers.add(diffAddresses);
+ repaint();
+ }
+
+ public void goTo(ProgramLocation location) {
+ if (location != null) {
+ listingPanel.goTo(location);
+ }
+ updateCursorMarkers(location);
+ }
+
+ public ListingPanel getListingPanel() {
+ return listingPanel;
+ }
+
+ void dispose() {
+ listingDiff.removeListingDiffChangeListener(this);
+
+ setDiffHighlightProvider(null);
+ markerManager.dispose();
+ listingPanel.removeButtonPressedListener(fieldNavigator);
+ listingPanel.dispose();
+ }
+
+ public FormatManager getFormatManager() {
+ return listingPanel.getFormatManager();
+ }
+
+ public ViewerPosition getViewerPosition() {
+ return listingPanel.getFieldPanel().getViewerPosition();
+ }
+
+ public void setViewerPosition(ViewerPosition position) {
+ listingPanel.getFieldPanel().setViewerPosition(position.getIndex(), position.getXOffset(),
+ position.getYOffset());
+ }
+
+ public void setMouseNavigationEnabled(boolean enabled) {
+ listingPanel.removeButtonPressedListener(fieldNavigator);
+ if (enabled) {
+ listingPanel.addButtonPressedListener(new FieldNavigator(serviceProvider, null));
+ }
+ }
+
+ @Override
+ public void listingDiffChanged() {
+ updateFunctionComparisonDiffHighlights();
+ setUnmatchedCodeUnitAreaMarkers();
+ setDiffAreaMarkers();
+ }
+
+ private void updateFunctionComparisonDiffHighlights() {
+ setDiffHighlightProvider(
+ new ListingDiffHighlightProvider(listingDiff, side, comparisonOptions));
+ }
+
+ private void setDiffAreaMarkers() {
+ Color color = comparisonOptions.getDiffCodeUnitsBackgroundColor();
+ AddressSetView addresses = listingDiff.getDiffs(side);
+
+ setAreaMarkers(diffMarkers, addresses, color);
+ }
+
+ private void setUnmatchedCodeUnitAreaMarkers() {
+ Color color = comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
+ AddressSetView addresses = listingDiff.getUnmatchedCode(side);
+ setAreaMarkers(unmatchedMarkers, addresses, color);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDisplayGoToService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDisplayGoToService.java
new file mode 100644
index 0000000000..321f4dfc2c
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingDisplayGoToService.java
@@ -0,0 +1,175 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.listingpanel;
+
+import docking.DockingWindowManager;
+import ghidra.app.nav.Navigatable;
+import ghidra.app.services.*;
+import ghidra.app.util.viewer.util.AddressIndexMap;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.ExternalLocation;
+import ghidra.program.util.ProgramLocation;
+import ghidra.util.Msg;
+import ghidra.util.task.TaskMonitor;
+
+/**
+ * This is a GoToService for a dual listing panel. It allows the goTo to occur relative to the
+ * left or right listing panel of a dual listing panel, since the left and right sides can be
+ * displaying totally different addresses.
+ */
+class ListingDisplayGoToService implements GoToService {
+
+ private ListingPanel listingPanel;
+
+ /**
+ * Constructs a goTo service for a dual listing panel.
+ * @param listingPanel the listing panel to be navigated to
+ */
+ ListingDisplayGoToService(ListingPanel listingPanel) {
+ this.listingPanel = listingPanel;
+ }
+
+ @Override
+ public GoToOverrideService getOverrideService() {
+ return null;
+ }
+
+ @Override
+ public boolean goTo(ProgramLocation loc) {
+ return doGoTo(loc);
+ }
+
+ @Override
+ public boolean goTo(Navigatable navigatable, Program program, Address address,
+ Address refAddress) {
+ return doGoTo(new ProgramLocation(program, address));
+ }
+
+ @Override
+ public boolean goTo(ProgramLocation loc, Program program) {
+ return doGoTo(loc);
+ }
+
+ @Override
+ public boolean goTo(Navigatable navigatable, ProgramLocation loc, Program program) {
+ return doGoTo(loc);
+ }
+
+ @Override
+ public boolean goTo(Navigatable navigatable, Address goToAddress) {
+ return doGoTo(goToAddress);
+ }
+
+ @Override
+ public boolean goTo(Address currentAddress, Address goToAddress) {
+ return doGoTo(goToAddress);
+ }
+
+ @Override
+ public boolean goTo(Address goToAddress) {
+ return doGoTo(goToAddress);
+ }
+
+ @Override
+ public boolean goTo(Address goToAddress, Program program) {
+ return doGoTo(goToAddress);
+ }
+
+ @Override
+ public boolean goToExternalLocation(ExternalLocation extLoc, boolean checkNavigationOption) {
+ Msg.showError(this, null, "Go To Failed!",
+ "Can't naviagate to an external function from here");
+ return false;
+ }
+
+ @Override
+ public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation extLoc,
+ boolean checkNavigationOption) {
+ Msg.showError(this, null, "Go To Failed!",
+ "Can't naviagate to an external function from here");
+ return false;
+ }
+
+ @Override
+ public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
+ TaskMonitor monitor) {
+ throw new UnsupportedOperationException(
+ "Go To Address or Label Query is not allowed in a dual listing view.");
+ }
+
+ @Override
+ public boolean goToQuery(Navigatable navigatable, Address fromAddr, QueryData queryData,
+ GoToServiceListener listener, TaskMonitor monitor) {
+ throw new UnsupportedOperationException(
+ "Go To Address or Label Query is not allowed in a dual listing view.");
+ }
+
+ @Override
+ public void setOverrideService(GoToOverrideService override) {
+ // ignored
+ }
+
+ @Override
+ public Navigatable getDefaultNavigatable() {
+ return new DualListingNavigator(listingPanel, this);
+ }
+
+ private boolean doGoTo(Address addr) {
+
+ // Only go if the address is in the listing's current address set.
+ if (!validateAddress(addr)) {
+ return false;
+ }
+
+ return listingPanel.goTo(addr);
+ }
+
+ private boolean doGoTo(ProgramLocation loc) {
+ if (loc == null) {
+ return false;
+ }
+
+ // Only go if the location address is in the listing's current address set.
+ if (!validateAddress(loc.getAddress())) {
+ return false;
+ }
+
+ return listingPanel.goTo(loc);
+ }
+
+ /**
+ * Checks the address to make sure the listing won't navigate outside the addresses
+ * it currently has loaded. If it is not a valid address it will set a status message
+ * on the dual listing.
+ * @param addr the address to check
+ * @return true if the address is valid for navigation.
+ */
+ private boolean validateAddress(Address addr) {
+ if (addr == null) {
+ return false;
+ }
+ AddressIndexMap map = listingPanel.getAddressIndexMap();
+ AddressSetView addresses = map.getOriginalAddressSet();
+ if (!addresses.contains(addr)) {
+ DockingWindowManager.getActiveInstance().setStatusText(
+ "\"" + addr.toString() + "\" is outside the current listing's view.");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java
index df91f2956d..9f96decfc6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java
@@ -19,7 +19,6 @@ import java.awt.*;
import java.awt.event.*;
import java.math.BigInteger;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import javax.swing.*;
@@ -68,7 +67,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
private ListingModel listingModel;
private FieldHeader headerPanel;
- private ButtonPressedListener[] buttonListeners = new ButtonPressedListener[0];
+ private List buttonListeners = new ArrayList<>();
private List indexMapChangeListeners = new ArrayList<>();
private ListingHoverProvider listingHoverHandler;
@@ -448,9 +447,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
* @param listener the ButtonPressedListener to add.
*/
public void addButtonPressedListener(ButtonPressedListener listener) {
- List list = new ArrayList<>(Arrays.asList(buttonListeners));
- list.add(listener);
- buttonListeners = list.toArray(new ButtonPressedListener[list.size()]);
+ buttonListeners.add(listener);
}
/**
@@ -459,9 +456,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
* @param listener the ButtonPressedListener to remove.
*/
public void removeButtonPressedListener(ButtonPressedListener listener) {
- List list = new ArrayList<>(Arrays.asList(buttonListeners));
- list.remove(listener);
- buttonListeners = list.toArray(new ButtonPressedListener[list.size()]);
+ buttonListeners.remove(listener);
}
/**
@@ -569,7 +564,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
layoutModel.dispose();
layoutModel = createLayoutModel(null);
layoutModel.dispose();
- buttonListeners = null;
+ buttonListeners.clear();
fieldPanel.dispose();
}
@@ -1225,4 +1220,16 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
public void removeDisplayListener(AddressSetDisplayListener listener) {
displayListeners.remove(listener);
}
+
+ @Override
+ public synchronized void addFocusListener(FocusListener l) {
+ // we are not focusable, defer to contained field panel
+ fieldPanel.addFocusListener(l);
+ }
+
+ @Override
+ public synchronized void removeFocusListener(FocusListener l) {
+ // we are not focusable, defer to contained field panel
+ fieldPanel.removeFocusListener(l);
+ }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java
index edefd53de0..b533a952b7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -513,21 +513,11 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
}
- @Override
- public void formatModelAdded(FieldFormatModel model) {
- notifyModelSizeChanged();
- }
-
@Override
public void formatModelChanged(FieldFormatModel model) {
notifyModelSizeChanged();
}
- @Override
- public void formatModelRemoved(FieldFormatModel model) {
- notifyModelSizeChanged();
- }
-
@Override
public void addListener(ListingModelListener listener) {
listeners.add(listener);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramLocationTranslator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramLocationTranslator.java
new file mode 100644
index 0000000000..69da99ff3f
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramLocationTranslator.java
@@ -0,0 +1,305 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.listingpanel;
+
+import ghidra.app.util.SymbolPath;
+import ghidra.framework.options.SaveState;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.symbol.*;
+import ghidra.program.util.*;
+import ghidra.util.datastruct.Duo.Side;
+
+/**
+ * Class for converting a program location from one program to another
+ */
+public class ProgramLocationTranslator {
+
+ private ListingAddressCorrelation correlator;
+
+ /**
+ * Constructor given a correlator for translating addresses
+ * @param correlator converts address from one program to another
+ */
+ public ProgramLocationTranslator(ListingAddressCorrelation correlator) {
+ this.correlator = correlator;
+ }
+
+ /**
+ * Converts a program location from the other side to the given side.
+ * @param side the side to get a location for
+ * @param otherSideLocation the location from the other side
+ * @return a program location for the given side that matches the other given location
+ */
+ public ProgramLocation getProgramLocation(Side side, ProgramLocation otherSideLocation) {
+ if (correlator == null) {
+ return null;
+ }
+ if (otherSideLocation == null) {
+ return null;
+ }
+ if (otherSideLocation instanceof VariableLocation) {
+ return getVariableLocation(side, (VariableLocation) otherSideLocation);
+ }
+
+ SaveState saveState = new SaveState();
+ otherSideLocation.saveState(saveState);
+ Address otherSideAddress = otherSideLocation.getAddress();
+
+ // Try to get the indicated side's address using one of the address correlators.
+ Address address = getAddress(side, otherSideAddress);
+ if (address == null || address == Address.NO_ADDRESS) {
+ return null; // Couldn't determine the indicated side's address.
+ }
+
+ saveState.remove("_ADDRESS");
+ saveState.putString("_ADDRESS", address.toString());
+
+ Address byteAddress = otherSideLocation.getByteAddress();
+ saveState.remove("_BYTE_ADDR");
+ Address desiredByteAddress = null;
+ Program program = correlator.getProgram(side);
+ if (byteAddress != null) {
+ // Try to get the indicated side's byte address using one of the address
+ // correlators or by inferring it.
+ desiredByteAddress =
+ inferDesiredByteAddress(otherSideAddress, address, byteAddress,
+ otherSideLocation.getProgram(), program);
+ if (desiredByteAddress != null) {
+ saveState.putString("_BYTE_ADDR", desiredByteAddress.toString());
+ }
+ }
+
+ // Adjust symbol path for labels if it is part of the location.
+ adjustSymbolPath(saveState, otherSideAddress, address, byteAddress, desiredByteAddress,
+ otherSideLocation.getProgram(), program);
+
+ // ref address can't be used with indicated side so remove it.
+ saveState.remove("_REF_ADDRESS");
+ // Don't know how to find equivalent referenced address for the indicated side,
+ // so don't put any _REF_ADDRESS back.
+
+ return ProgramLocation.getLocation(program, saveState);
+
+ }
+
+ /**
+ * Gets the matching address for the given side given an address from the other side. This
+ * method first attempts to translate the address directly. If that fails, it then attempts
+ * to get an address for the start of the code unit containing the given address because the
+ * correlator may only have translations for code unit starts.
+ *
+ * @param side the LEFT or RIGHT side to get an address for
+ * @param otherSidesAddress address the address from the other side
+ * @return the match address for the given side given an address from the other side
+ */
+ private Address getAddress(Side side, Address otherSidesAddress) {
+ Side otherSide = side.otherSide();
+ Address address = correlator.getAddress(side, otherSidesAddress);
+
+ if (address != null) {
+ return address;
+ }
+ // Couldn't directly correlate the address.
+ CodeUnit otherCodeUnit =
+ correlator.getProgram(otherSide).getListing().getCodeUnitContaining(otherSidesAddress);
+ if (otherCodeUnit == null) {
+ return null; // Can't get the code unit's address.
+ }
+ Address otherCodeUnitAddress = otherCodeUnit.getMinAddress();
+ return correlator.getAddress(side, otherCodeUnitAddress);
+ }
+
+ /**
+ * Gets an matching variable location when given a variable location from the other side.
+ *
+ * @param side LEFT or RIGHT indicating which side's variable location is needed.
+ * @param variableLocation the variable location from the other side.
+ * @return a variable location for the desired side. Otherwise, null.
+ */
+ private ProgramLocation getVariableLocation(Side side, VariableLocation variableLocation) {
+ if (variableLocation == null) {
+ return null;
+ }
+ SaveState saveState = new SaveState();
+ variableLocation.saveState(saveState);
+ Address address = variableLocation.getAddress();
+ Address byteAddress = variableLocation.getByteAddress();
+ Address functionAddress = variableLocation.getFunctionAddress();
+
+ // Try to get the indicated side's address using one of the address correlators.
+ Address desiredAddress = getAddress(side, address);
+ if (desiredAddress == null || desiredAddress == Address.NO_ADDRESS) {
+ return null; // Couldn't determine the indicated side's address.
+ }
+
+ // Try to use a byte address.
+ Address desiredByteAddress = null;
+ if (byteAddress != null) {
+ desiredByteAddress = getAddress(side, byteAddress);
+ }
+
+ Address desiredFunctionAddress = null;
+ if (functionAddress != null) {
+ desiredFunctionAddress = getAddress(side, functionAddress);
+ }
+ Function function = correlator.getFunction(side);
+ if ((desiredFunctionAddress == null) && (function != null)) {
+ // If this is a thunk function get the thunked address.
+ Function thunkedFunction = function.getThunkedFunction(true);
+ if (thunkedFunction != null) {
+ desiredFunctionAddress = thunkedFunction.getEntryPoint();
+ }
+ }
+
+ saveState.remove("_ADDRESS");
+ saveState.putString("_ADDRESS", desiredAddress.toString());
+
+ saveState.remove("_BYTE_ADDR");
+ if (desiredByteAddress != null) {
+ saveState.putString("_BYTE_ADDR", desiredByteAddress.toString());
+ }
+
+ saveState.remove("_FUNC_ADDRESS");
+ if (desiredFunctionAddress != null) {
+ saveState.putString("_FUNC_ADDRESS", desiredFunctionAddress.toString());
+ }
+
+ // ref address can't be used with indicated side so remove it.
+ saveState.remove("_REF_ADDRESS");
+ // Don't know how to find equivalent referenced address for the indicated side,
+ // so don't put any _REF_ADDRESS back.
+
+ return ProgramLocation.getLocation(correlator.getProgram(side), saveState);
+ }
+
+ private void adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
+ Address byteAddress, Address desiredByteAddress, Program program,
+ Program desiredProgram) {
+
+ String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
+ saveState.remove("_SYMBOL_PATH");
+ if (symbolPathArray.length == 0) {
+ return; // save state has no labels for program location.
+ }
+ Address symbolAddress = (byteAddress != null) ? byteAddress : address;
+ Address desiredSymbolAddress =
+ (desiredByteAddress != null) ? desiredByteAddress : desiredAddress;
+ if (symbolAddress == null || desiredSymbolAddress == null) {
+ return; // no address match.
+ }
+ Symbol[] symbols = program.getSymbolTable().getSymbols(symbolAddress);
+ if (symbols.length == 0) {
+ return; // no symbols in program for matching.
+ }
+ Symbol[] desiredSymbols = desiredProgram.getSymbolTable().getSymbols(desiredSymbolAddress);
+ if (desiredSymbols.length == 0) {
+ return; // no symbols in desiredProgram for matching.
+ }
+
+ int desiredRow = adjustSymbolRow(saveState, symbols, desiredSymbols);
+
+ int desiredIndex = getDesiredSymbolIndex(desiredSymbols, desiredRow);
+
+ // Now get the desired symbol.
+ Symbol desiredSymbol = desiredSymbols[desiredIndex];
+ SymbolPath symbolPath = getSymbolPath(desiredSymbol);
+ // Set symbol path for desiredProgram in the save state.
+ saveState.putStrings("_SYMBOL_PATH", symbolPath.asArray());
+ }
+
+ private int adjustSymbolRow(SaveState saveState, Symbol[] symbols, Symbol[] desiredSymbols) {
+ // For now just try to choose the same label index if more than one.
+ int row = saveState.getInt("_ROW", 0);
+ int desiredRow = row;
+ if (desiredRow >= desiredSymbols.length) {
+ desiredRow = desiredSymbols.length - 1;
+ }
+ saveState.putInt("_ROW", desiredRow);
+ return desiredRow;
+ }
+
+ private SymbolPath getSymbolPath(Symbol desiredSymbol) {
+ String label = desiredSymbol.getName();
+ Namespace namespace = desiredSymbol.getParentNamespace();
+ SymbolPath symbolPath;
+ if (namespace == null || namespace.isGlobal()) {
+ symbolPath = new SymbolPath(label);
+ }
+ else {
+ symbolPath = new SymbolPath(new SymbolPath(namespace.getSymbol()), label);
+ }
+ return symbolPath;
+ }
+
+ private int getDesiredSymbolIndex(Symbol[] desiredSymbols, int desiredRow) {
+
+ boolean hasFunction = desiredSymbols[0].getSymbolType().equals(SymbolType.FUNCTION);
+
+ // Get the array index of the desired symbol.
+ int desiredIndex = 0; // Default to first entry in array.
+ if (desiredRow >= 0 && desiredRow < desiredSymbols.length) {
+ desiredIndex = desiredRow;
+ }
+ if (hasFunction) {
+ // Last row in GUI is also first entry in array.
+ if (desiredIndex == desiredSymbols.length - 1) {
+ desiredIndex = 0; // Set to function element.
+ }
+ else {
+ desiredIndex++; // Adjust for function element at start of array.
+ }
+ }
+ return desiredIndex;
+ }
+
+ /**
+ * Infers a desired byte address based on the specified byteAddress as well as the
+ * address and desiredAddress that were matched.
+ *
+ * @param address matches up with the desiredAddress from the other function/data.
+ * @param desiredAddress matches up with the address from the other function/data.
+ * @param byteAddress the byte address that is associated with address
+ * @param program the program for the address and byteAddress.
+ * @param desiredProgram the program for the desiredAddress and
+ * desiredByteAddress.
+ * @return the desired byte address that matches up with the indicated byteAddress
+ * or null if it can't be determined.
+ */
+ private Address inferDesiredByteAddress(Address address, Address desiredAddress,
+ Address byteAddress, Program program, Program desiredProgram) {
+
+ long numBytesIntoCodeUnit = byteAddress.subtract(address);
+ if (numBytesIntoCodeUnit == 0) {
+ return desiredAddress;
+ }
+ if (numBytesIntoCodeUnit > 0) {
+ CodeUnit codeUnit = program.getListing().getCodeUnitAt(address);
+ CodeUnit desiredCodeUnit = desiredProgram.getListing().getCodeUnitAt(desiredAddress);
+ if (codeUnit != null && desiredCodeUnit != null) {
+ int desiredCodeUnitLength = desiredCodeUnit.getLength();
+ if (numBytesIntoCodeUnit < desiredCodeUnitLength) {
+ // Position at byte within code unit.
+ return desiredAddress.add(numBytesIntoCodeUnit);
+ }
+ // Otherwise position at last byte of code unit.
+ return desiredAddress.add(desiredCodeUnitLength - 1);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiListingLayoutModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiListingLayoutModel.java
index 22073a03c2..52982ac989 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiListingLayoutModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiListingLayoutModel.java
@@ -321,25 +321,6 @@ public class MultiListingLayoutModel implements ListingModelListener, FormatMode
}
- /**
- * @see ghidra.app.util.viewer.format.FormatModelListener#formatModelAdded(ghidra.app.util.viewer.format.FieldFormatModel)
- */
- @Override
- public void formatModelAdded(FieldFormatModel model) {
- dataChanged(true);
- }
-
- /**
- * @see ghidra.app.util.viewer.format.FormatModelListener#formatModelRemoved(ghidra.app.util.viewer.format.FieldFormatModel)
- */
- @Override
- public void formatModelRemoved(FieldFormatModel model) {
- dataChanged(true);
- }
-
- /**
- * @see ghidra.app.util.viewer.format.FormatModelListener#formatModelChanged(ghidra.app.util.viewer.format.FieldFormatModel)
- */
@Override
public void formatModelChanged(FieldFormatModel model) {
modelSizeChanged();
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/AddressSetComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/AddressSetComparisonData.java
new file mode 100644
index 0000000000..49d5fc13fa
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/AddressSetComparisonData.java
@@ -0,0 +1,80 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.util;
+
+import java.util.Objects;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.util.HTMLUtilities;
+
+/**
+ * ComparisonData for a generic set of addresses.
+ */
+public class AddressSetComparisonData implements ComparisonData {
+
+ private Program program;
+ private AddressSetView addresses;
+
+ public AddressSetComparisonData(Program program, AddressSetView addresses) {
+ this.program = Objects.requireNonNull(program);
+ this.addresses = Objects.requireNonNull(addresses);
+ }
+
+ @Override
+ public Function getFunction() {
+ return null;
+ }
+
+ @Override
+ public AddressSetView getAddressSet() {
+ return addresses;
+ }
+
+ @Override
+ public Program getProgram() {
+ return program;
+ }
+
+ @Override
+ public String getShortDescription() {
+ Address minAddress = addresses.getMinAddress();
+ Address maxAddress = addresses.getMinAddress();
+ if (minAddress == null) {
+ return "Empty";
+ }
+ return minAddress + ":" + maxAddress;
+ }
+
+ @Override
+ public String getDescription() {
+ StringBuffer buf = new StringBuffer();
+ String padStr = HTMLUtilities.spaces(4);
+ buf.append(padStr);
+ String programStr = HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
+ String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
+ buf.append(specialProgramStr);
+ buf.append(padStr);
+ return HTMLUtilities.wrapAsHTML(buf.toString());
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return addresses.isEmpty();
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonActionContext.java
index acf9089ca7..f798bc7aa2 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonActionContext.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonActionContext.java
@@ -20,29 +20,22 @@ import java.awt.Component;
import docking.ComponentProvider;
import docking.DefaultActionContext;
import ghidra.program.model.listing.Function;
+import ghidra.util.datastruct.Duo.Side;
public abstract class CodeComparisonActionContext extends DefaultActionContext
implements CodeComparisonPanelActionContext {
- /**
- * Constructor with no source component and no context object
- * @param provider the ComponentProvider that generated this context.
- */
- public CodeComparisonActionContext(ComponentProvider provider) {
- super(provider);
- }
+ private CodeComparisonPanel comparisonPanel;
- /**
- * Constructor with source component and context object
- *
- * @param provider the ComponentProvider that generated this context.
- * @param contextObject an optional contextObject that the ComponentProvider can provide; this
- * can be anything that actions wish to later retrieve
- * @param sourceComponent an optional source object; this is intended to be the component that
- * is the source of the context, usually the focused component
+ /**
+ * Constructor
+ * @param provider the ComponentProvider containing the code comparison panel
+ * @param panel the CodeComparisonPanel that generated this context
+ * @param component the focusable component for associated with the comparison panel
*/
- public CodeComparisonActionContext(ComponentProvider provider, Object contextObject,
- Component sourceComponent) {
- super(provider, contextObject, sourceComponent);
+ public CodeComparisonActionContext(ComponentProvider provider, CodeComparisonPanel panel,
+ Component component) {
+ super(provider, panel, component);
+ this.comparisonPanel = panel;
}
/**
@@ -50,13 +43,18 @@ public abstract class CodeComparisonActionContext extends DefaultActionContext
* side of the function diff window that isn't active.
* @return the function to get information from
*/
- public abstract Function getSourceFunction();
+ public Function getSourceFunction() {
+ Side activeSide = comparisonPanel.getActiveSide();
+ return comparisonPanel.getFunction(activeSide.otherSide());
+ }
/**
* Returns the function that is the target of the info being applied. This will be whichever
* side of the function diff window that is active.
* @return the function to apply information to
*/
- public abstract Function getTargetFunction();
-
+ public Function getTargetFunction() {
+ Side activeSide = comparisonPanel.getActiveSide();
+ return comparisonPanel.getFunction(activeSide);
+ }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanel.java
index 75da8e4282..546ffb4904 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanel.java
@@ -15,25 +15,31 @@
*/
package ghidra.app.util.viewer.util;
-import java.awt.Color;
+import static ghidra.app.util.viewer.util.ComparisonData.*;
+import static ghidra.util.datastruct.Duo.Side.*;
+
+import java.awt.*;
import java.awt.event.*;
+import java.util.ArrayList;
+import java.util.List;
import javax.swing.*;
import javax.swing.border.Border;
import docking.ActionContext;
import docking.ComponentProvider;
-import docking.action.DockingAction;
-import docking.widgets.fieldpanel.FieldPanel;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
+import docking.action.*;
import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.plugin.core.functioncompare.FunctionComparisonPanel;
import ghidra.framework.plugintool.PluginTool;
-import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.listing.*;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.util.HTMLUtilities;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ExtensionPoint;
+import ghidra.util.datastruct.Duo;
+import ghidra.util.datastruct.Duo.Side;
/**
* The CodeComparisonPanel class should be extended by any class that is to be
@@ -42,42 +48,29 @@ import ghidra.util.classfinder.ExtensionPoint;
*
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN
* CodeComparisonPanel so they are discoverable by the {@link ClassSearcher}
- * @param the type
*/
-public abstract class CodeComparisonPanel extends JPanel
- implements ExtensionPoint, FocusListener {
-
- // MINIMUM_PANEL_WIDTH is used to establish a minimum panel width for the left or right panel.
- // Without it a long title in either panel can cause the split pane's divider to become locked.
- protected static final int MINIMUM_PANEL_WIDTH = 50;
-
- protected static final int LEFT = 0;
- protected static final int RIGHT = 1;
- private static final Color FOCUS_BORDER_COLOR = Palette.getColor("lightpink");
- protected static final Border FOCUS_BORDER =
- BorderFactory.createMatteBorder(3, 3, 3, 3, FOCUS_BORDER_COLOR);
- protected static final Border NON_FOCUS_BORDER = BorderFactory.createEmptyBorder(3, 3, 3, 3);
- protected static final AddressSetView EMPTY_ADDRESS_SET = new AddressSet();
+public abstract class CodeComparisonPanel extends JPanel
+ implements ExtensionPoint {
+ public static final String HELP_TOPIC = "FunctionComparison";
+ private static final Color ACTIVE_BORDER_COLOR = Palette.getColor("lightpink");
+ private static final int MINIMUM_PANEL_WIDTH = 50;
+ private static final Border NON_ACTIVE_BORDER = BorderFactory.createEmptyBorder(3, 3, 3, 3);
+ private static final Border ACTIVE_BORDER =
+ BorderFactory.createMatteBorder(3, 3, 3, 3, ACTIVE_BORDER_COLOR);
protected String owner;
protected PluginTool tool;
- protected JComponent topComp;
- protected JComponent bottomComp;
- protected TitledPanel[] titlePanels = new TitledPanel[2];
- protected String leftTitlePrefix = "";
- protected String rightTitlePrefix = "";
- protected int currProgramIndex = LEFT; // Side with current focus (LEFT or RIGHT)
- protected Program[] programs = new Program[2];
- protected Function[] functions = new Function[2];
- protected Data[] data = new Data[2];
- /** If true, the title of each comparison panel will be shown */
+ protected Duo comparisonData = new Duo<>(EMPTY, EMPTY);
+ private Duo titlePrefixes = new Duo<>("", "");
+ private Duo titlePanels = new Duo<>();
+
+ protected Side activeSide = LEFT;
+ private JSplitPane splitPane;
+ private ToggleOrientationAction toggleOrientationAction;
+ private JComponent northComponent;
private boolean showTitles = true;
- private boolean syncScrolling = false;
-
- private T fieldPanelCoordinator;
-
/**
* Constructor
*
@@ -87,91 +80,83 @@ public abstract class CodeComparisonPanel exten
protected CodeComparisonPanel(String owner, PluginTool tool) {
this.owner = owner;
this.tool = tool;
+ toggleOrientationAction = new ToggleOrientationAction(getName());
+
+ // Important! Subclasses must call the build() method instead of calling it here. This is
+ // to avoid java's constructor ordering problem
}
/**
- * The GUI component for this CodeComparisonPanel
+ * Displays a comparison of two ComparisonData objects
*
- * @return the component
+ * @param left the comparisonData for the left side
+ * @param right the comparisonData for the right side
*/
- public abstract JComponent getComponent();
+ public void loadComparisons(ComparisonData left, ComparisonData right) {
+ if (comparisonData.equals(left, right)) {
+ return;
+ }
+ comparisonData = new Duo<>(left, right);
+ comparisonDataChanged();
+ updateTitles();
+ }
/**
- * The title for this code comparison panel
- *
- * @return the title
+ * Clears out the current comparisonDatas
*/
- public abstract String getTitle();
-
- /**
- * Specifies the two programs to be compared by this panel
- *
- * @param leftProgram the program for the left side
- * @param rightProgram the program for the right side
- */
- protected abstract void setPrograms(Program leftProgram, Program rightProgram);
-
- /**
- * Displays a comparison of two program's functions
- *
- * @param leftFunction the function to show in the left side of the code comparison view
- * @param rightFunction the function to show in the right side of the code comparison view
- */
- public abstract void loadFunctions(Function leftFunction, Function rightFunction);
-
- /**
- * Displays a comparison of two program's data items
- *
- * @param leftData the data item to show in the left side of the code comparison view
- * @param rightData the data item to show in the right side of the code comparison view
- */
- public abstract void loadData(Data leftData, Data rightData);
-
- /**
- * Displays program information for a particular set of addresses in the two programs
- * being compared
- *
- * @param leftProgram the program in the left side of the code comparison view
- * @param rightProgram the program in the right side of the code comparison view
- * @param leftAddresses the addresses of the program info to show in the left side
- * @param rightAddresses the addresses of the program info to show in the right side
- */
- public abstract void loadAddresses(Program leftProgram, Program rightProgram,
- AddressSetView leftAddresses, AddressSetView rightAddresses);
-
- /**
- * Cleans up resources when this panel is no longer needed
- */
- public abstract void dispose();
-
- /**
- * Enable/disable navigation in this panel using the mouse
- *
- * @param enabled false disables mouse navigation
- */
- public abstract void setMouseNavigationEnabled(boolean enabled);
+ public void clearComparisons() {
+ loadComparisons(ComparisonData.EMPTY, ComparisonData.EMPTY);
+ }
/**
* Returns the actions for this panel
*
* @return an array of docking actions
*/
- public DockingAction[] getActions() {
- // No actions currently that appear for each CodeComparisonPanel.
- // Classes that extend this class will override this method to get all actions
- // specific to that CodeComparisonPanel.
- DockingAction[] actions = new DockingAction[] {};
- return actions;
+ public List getActions() {
+ List actionList = new ArrayList<>();
+ actionList.add(toggleOrientationAction);
+ return actionList;
}
- public boolean getShowTitles() {
- return showTitles;
- }
-
- public void setShowTitles(boolean showTitles) {
+ /**
+ * Toggles whether or not to display data titles for each side.
+ * @param showTitles true to show data titles
+ */
+ public void setShowDataTitles(boolean showTitles) {
this.showTitles = showTitles;
}
+ /**
+ * Returns true if dual panels are displayed horizontally, false if displayed vertically.
+ * @return true if dual panels are displayed horizontally, false if displayed vertically
+ */
+ public boolean isSideBySide() {
+ return toggleOrientationAction.isSelected();
+ }
+
+ /**
+ * Sets the orientation for the dual panels.
+ * @param b if true, panels will be display horizontally, otherwise vertically
+ */
+ public void setSideBySide(boolean b) {
+ toggleOrientationAction.setSelected(b);
+ updateOrientation();
+ }
+
+ /**
+ * Force subclasses to supply a descriptive name.
+ *
+ * @return a descriptive name for this panel type
+ */
+ @Override
+ public abstract String getName();
+
+ /**
+ * Cleans up resources when this panel is no longer needed
+ */
+ public abstract void dispose();
+
/**
* Returns the context object which corresponds to the area of focus within this provider's
* component. Null is returned when there is no context.
@@ -189,13 +174,41 @@ public abstract class CodeComparisonPanel exten
* refreshing itself) to respond to the program changing.
* @param program the program that was restored.
*/
- public abstract void programRestored(Program program);
+ public void programRestored(Program program) {
+ updateTitles();
+ }
/**
- * Determines if the left code panel currently has focus.
- * @return true if the left side of the code comparison has focus.
+ * Called when a program is closed.
+ * @param program the closed program
*/
- public abstract boolean leftPanelHasFocus();
+ public void programClosed(Program program) {
+ // do nothing by default
+ }
+
+ /**
+ * Returns the {@link Side} that is currently active
+ * @return the {@link Side} that is currently active
+ */
+ public Side getActiveSide() {
+ return activeSide;
+ }
+
+ /**
+ * Sets the component displayed in the top of this panel.
+ *
+ * @param component the component.
+ */
+ public void setTopComponent(JComponent component) {
+ if (northComponent != null) {
+ remove(northComponent);
+ }
+ northComponent = component;
+ if (northComponent != null) {
+ add(northComponent, BorderLayout.NORTH);
+ }
+ validate();
+ }
/**
* A CodeComparisonPanel should provide a title based on what the code comparison panel
@@ -204,157 +217,185 @@ public abstract class CodeComparisonPanel exten
* @param leftTitlePrefix the prefix string to prepend to the left panel's title.
* @param rightTitlePrefix the prefix string to prepend to the right panel's title.
*/
- public abstract void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix);
-
- /**
- * Gets the program being viewed in the left side of this panel.
- * @return the program or null
- */
- public Program getLeftProgram() {
- return programs[LEFT];
+ public void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix) {
+ titlePrefixes = new Duo<>(leftTitlePrefix, rightTitlePrefix);
+ updateTitles();
}
/**
- * Gets the program being viewed in the right side of this panel.
- * @return the program or null
+ * Returns the program being shown in the given side.
+ * @param side the {@link Side} to get the program for
+ * @return the program for the given side.
*/
- public Program getRightProgram() {
- return programs[RIGHT];
+ public Program getProgram(Side side) {
+ return comparisonData.get(side).getProgram();
}
/**
- * Gets the function loaded in the left side of this panel.
- * @return the function or null
+ * Returns the function being shown in the given side.
+ * @param side the {@link Side} to get the function for
+ * @return the function for the given side.
*/
- public Function getLeftFunction() {
- return functions[LEFT];
+ public Function getFunction(Side side) {
+ return comparisonData.get(side).getFunction();
}
/**
- * Gets the function loaded in the right side of this panel.
- * @return the function or null
+ * Returns the addresses being shown in the given side.
+ * @param side the {@link Side} to get the program for
+ * @return the address set for the given side
*/
- public Function getRightFunction() {
- return functions[RIGHT];
+ public AddressSetView getAddresses(Side side) {
+ return comparisonData.get(side).getAddressSet();
}
/**
- * Gets the data loaded in the left side of this panel.
- * @return the data or null
- */
- public Data getLeftData() {
- return data[LEFT];
- }
-
- /**
- * Gets the data loaded in the right side of this panel.
- * @return the data or null
- */
- public Data getRightData() {
- return data[RIGHT];
- }
-
- /**
- * Gets the addresses loaded in the left side of this panel.
- * @return the addresses or an empty set
- */
- public abstract AddressSetView getLeftAddresses();
-
- /**
- * Gets the addresses loaded in the right side of this panel.
- * @return the addresses or an empty set
- */
- public abstract AddressSetView getRightAddresses();
-
- /**
- * Refreshes the left side of this panel.
- */
- public abstract void refreshLeftPanel();
-
- /**
- * Refreshes the right side of this panel.
- */
- public abstract void refreshRightPanel();
-
- @Override
- public void focusLost(FocusEvent e) {
- // Do nothing.
- }
-
- /**
- * Updates the enablement for any actions created by this code comparison panel.
- */
+ * Updates the enablement for any actions created by this code comparison panel.
+ */
public abstract void updateActionEnablement();
- /**
- * Sets the coordinator for the two views within this code comparison panel. It coordinates
- * their scrolling and location synchronization.
- * @param fieldPanelCoordinator the coordinator for the two views
- */
- public void setFieldPanelCoordinator(T fieldPanelCoordinator) {
- if (this.fieldPanelCoordinator != null) {
- this.fieldPanelCoordinator.dispose();
- }
- this.fieldPanelCoordinator = fieldPanelCoordinator;
- }
-
- /**
- * Gets the current field panel coordinator used to synchronize scrolling between the
- * left and right view for this CodeComparisonPanel.
- * @return the current FieldPanelCoordinator. Otherwise, null if scrolling is not
- * currently synchronized.
- */
- protected T getFieldPanelCoordinator() {
- return fieldPanelCoordinator;
- }
-
- /**
- * Creates a new FieldPanelCoordinator used to synchronize scrolling between the
- * left and right view for this CodeComparisonPanel.
- * @return a new FieldPanelCoordinator
- */
- protected abstract T createFieldPanelCoordinator();
-
- /**
- * Gets the left field panel for this CodeComparisonPanel.
- * @return the left FieldPanel.
- */
- public abstract FieldPanel getLeftFieldPanel();
-
- /**
- * Gets the right field panel for this CodeComparisonPanel.
- * @return the right FieldPanel.
- */
- public abstract FieldPanel getRightFieldPanel();
-
- /**
- * Determines if the layouts of the views are synchronized with respect to scrolling and
- * location.
- * @return true if scrolling is synchronized between the two views.
- */
- public final boolean isScrollingSynced() {
- return syncScrolling;
- }
-
/**
* Sets whether or not scrolling is synchronized.
- * @param syncScrolling true means synchronize scrolling and location between the two views.
+ * @param b true means synchronize scrolling between the two views.
*/
- public void setScrollingSyncState(boolean syncScrolling) {
- if (isScrollingSynced() == syncScrolling) {
- return;
- }
- this.syncScrolling = syncScrolling;
+ public abstract void setSynchronizedScrolling(boolean b);
- // Refresh the left panel.
- FieldPanel leftPanel = getLeftFieldPanel();
- leftPanel.validate();
- leftPanel.invalidate();
- // Refresh the right panel.
- FieldPanel rightPanel = getRightFieldPanel();
- rightPanel.validate();
- rightPanel.invalidate();
+ /**
+ * Returns the Component for the given {@link Side}
+ * @param side the Side to its component
+ * @return the Component for the given {@link Side}
+ */
+ public abstract JComponent getComparisonComponent(Side side);
- setFieldPanelCoordinator(syncScrolling ? createFieldPanelCoordinator() : null);
+ /**
+ * Notification to subclasses that the comparison data has changed
+ */
+ protected abstract void comparisonDataChanged();
+
+ private final String getTitle(Side side) {
+ return comparisonData.get(side).getDescription();
}
+
+ private void updateTitles() {
+ updateTitle(Side.LEFT);
+ updateTitle(Side.RIGHT);
+ }
+
+ private void updateTitle(Side side) {
+ String title = showTitles ? getTitle(side) : "";
+ setTitle(titlePanels.get(side), titlePrefixes.get(side), title);
+ }
+
+ private void updateOrientation() {
+ int orientation = toggleOrientationAction.isSelected() ? JSplitPane.HORIZONTAL_SPLIT
+ : JSplitPane.VERTICAL_SPLIT;
+ splitPane.setOrientation(orientation);
+ splitPane.setDividerLocation(0.5);
+ }
+
+ private void setTitle(TitledPanel titlePanel, String titlePrefix, String title) {
+ if (!titlePrefix.isEmpty()) {
+ titlePrefix += " "; // Add a space between prefix and title.
+ }
+ String htmlPrefix = "";
+ if (title.startsWith(htmlPrefix)) {
+ titlePanel.setTitleName(htmlPrefix + HTMLUtilities.friendlyEncodeHTML(titlePrefix) +
+ title.substring(htmlPrefix.length()));
+ }
+ else {
+ titlePanel.setTitleName(titlePrefix + title);
+ }
+ }
+
+ protected final void buildPanel() {
+ setLayout(new BorderLayout());
+
+ TitledPanel leftPanel = new TitledPanel(getTitle(LEFT), getComparisonComponent(LEFT), 5);
+ TitledPanel rightPanel = new TitledPanel(getTitle(RIGHT), getComparisonComponent(RIGHT), 5);
+ titlePanels = new Duo<>(leftPanel, rightPanel);
+
+ // Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's
+ // divider from becoming locked (can't be moved) due to extra long title names.
+ titlePanels.get(LEFT).setMinimumSize(
+ new Dimension(MINIMUM_PANEL_WIDTH, titlePanels.get(LEFT).getMinimumSize().height));
+ titlePanels.get(RIGHT).setMinimumSize(
+ new Dimension(MINIMUM_PANEL_WIDTH, titlePanels.get(RIGHT).getMinimumSize().height));
+
+ splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, titlePanels.get(LEFT),
+ titlePanels.get(RIGHT));
+ splitPane.setResizeWeight(0.5);
+ splitPane.setDividerSize(4);
+ splitPane.setBorder(BorderFactory.createEmptyBorder());
+ add(splitPane, BorderLayout.CENTER);
+ updateOrientation();
+
+ addMouseAndFocusListeners(LEFT);
+ addMouseAndFocusListeners(RIGHT);
+ setActiveSide(LEFT);
+ }
+
+ private void addMouseAndFocusListeners(Side side) {
+ JComponent comp = getComparisonComponent(side);
+ comp.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ setActiveSide(side);
+ updateContextForFocusGained(e.getComponent());
+ }
+ });
+
+ comp = getComparisonComponent(side);
+
+ MouseAdapter mouseListener = new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ setActiveSide(side);
+ }
+ };
+
+ addMouseListenerRecursively(comp, mouseListener);
+ }
+
+ private void updateContextForFocusGained(Component component) {
+ ComponentProvider provider = tool.getWindowManager().getProvider(component);
+ if (provider != null) {
+ provider.contextChanged();
+ }
+
+ }
+
+ private void addMouseListenerRecursively(Component component, MouseListener listener) {
+ component.addMouseListener(listener);
+ if (component instanceof Container container) {
+ for (int i = 0; i < container.getComponentCount(); i++) {
+ Component child = container.getComponent(i);
+ addMouseListenerRecursively(child, listener);
+ }
+ }
+ }
+
+ protected void setActiveSide(Side side) {
+ activeSide = side;
+ getComparisonComponent(side).setBorder(ACTIVE_BORDER);
+ getComparisonComponent(side.otherSide()).setBorder(NON_ACTIVE_BORDER);
+ }
+
+ private class ToggleOrientationAction extends ToggleDockingAction {
+ ToggleOrientationAction(String name) {
+ super(name + " Toggle Orientation", "FunctionComparison");
+ setDescription(
+ "Toggle the layout to be either side by side or one above the other");
+ setEnabled(true);
+ MenuData menuData =
+ new MenuData(new String[] { "Show " + name + " Side-by-Side" }, "Orientation");
+ setMenuBarData(menuData);
+ setSelected(true);
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+ updateOrientation();
+ }
+ }
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanelActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanelActionContext.java
index bc1bd23282..1082410139 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanelActionContext.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/CodeComparisonPanelActionContext.java
@@ -15,8 +15,6 @@
*/
package ghidra.app.util.viewer.util;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
-
/**
* Action context for a CodeComparisonPanel.
*/
@@ -26,6 +24,6 @@ public interface CodeComparisonPanelActionContext {
* Gets the CodeComparisonPanel associated with this context.
* @return the code comparison panel.
*/
- public abstract CodeComparisonPanel extends FieldPanelCoordinator> getCodeComparisonPanel();
+ public abstract CodeComparisonPanel getCodeComparisonPanel();
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/ComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/ComparisonData.java
new file mode 100644
index 0000000000..f112453437
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/ComparisonData.java
@@ -0,0 +1,71 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.util;
+
+import java.awt.Color;
+
+import generic.theme.GThemeDefaults.Colors.Palette;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+
+/**
+ * ComparisonData is an abstract of items that can be compared in a {@link CodeComparisonPanel}.
+ * Not all comparison panels can handle all types of comparison data. For example, the decompiler
+ * comparison only works when the comparison data is a function.
+ */
+public interface ComparisonData {
+ public static final Color FG_COLOR_TITLE = Palette.DARK_GRAY;
+ public static final ComparisonData EMPTY = new EmptyComparisonData();
+
+ /**
+ * Returns the function being compared or null if this comparison data is not function based.
+ * @return the function being compared or null if this comparison data is not function based
+ */
+ public Function getFunction();
+
+ /**
+ * Returns the set of addresses being compared. Currently, all comparisons are address based,
+ * so this should never be null.
+ * @return the set of addresses being compared
+ */
+ public AddressSetView getAddressSet();
+
+ /**
+ * Returns the program containing the data being compared.
+ * @return the program containing the data being compared.
+ */
+ public Program getProgram();
+
+ /**
+ * Returns a description of the data being compared.
+ * @return a description of the data being compared.
+ */
+ public String getDescription();
+
+ /**
+ * Returns a short description (useful for tab name)
+ * @return a short description
+ */
+ public String getShortDescription();
+
+ /**
+ * Returns true if this comparison has no addresses to compare
+ * @return true if this comparison has no addresses to compare
+ */
+ public boolean isEmpty();
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/DataComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/DataComparisonData.java
new file mode 100644
index 0000000000..c7b80d0925
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/DataComparisonData.java
@@ -0,0 +1,111 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.util;
+
+import java.util.Objects;
+
+import ghidra.program.model.address.*;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.util.HTMLUtilities;
+
+/**
+ * ComparisonData for a Data object
+ */
+public class DataComparisonData implements ComparisonData {
+
+ private final Data data;
+ private final AddressSet addresses;
+
+ public DataComparisonData(Data data, int otherLength) {
+ this.data = Objects.requireNonNull(data);
+ int size = Math.max(data.getLength(), otherLength);
+ this.addresses = new AddressSet(data.getMinAddress(), getEndAddress(size));
+ }
+
+ @Override
+ public AddressSetView getAddressSet() {
+ return addresses;
+ }
+
+ @Override
+ public Program getProgram() {
+ return data.getProgram();
+ }
+
+ @Override
+ public Function getFunction() {
+ return null;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public String getShortDescription() {
+ return data.getDataType().getName();
+ }
+
+ @Override
+ public String getDescription() {
+ StringBuffer buf = new StringBuffer();
+ String padStr = HTMLUtilities.spaces(4);
+ buf.append(padStr);
+
+ String dataLabel = data.getLabel();
+ if (dataLabel == null) { // If we can't get a label for the data then use the address .
+ Address address = data.getAddress();
+ dataLabel = address.toString();
+ }
+ String dataStr = HTMLUtilities.friendlyEncodeHTML(dataLabel);
+ String specialDataStr = HTMLUtilities.bold(dataStr);
+ buf.append(specialDataStr);
+
+ Program program = data.getProgram();
+ if (program != null) {
+ buf.append(" in ");
+
+ String programStr =
+ HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
+ String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
+ buf.append(specialProgramStr);
+ buf.append(padStr);
+ }
+ return HTMLUtilities.wrapAsHTML(buf.toString());
+ }
+
+ private Address getEndAddress(int size) {
+ Address minAddress = data.getMinAddress();
+ if (minAddress.isExternalAddress()) {
+ return minAddress; // Begin and end address are same for external data.
+ }
+ MemoryBlock block = data.getProgram().getMemory().getBlock(minAddress);
+ Address blockEnd = block.getEnd();
+ Address endAddress;
+ try {
+ endAddress = minAddress.add(size);
+ if (endAddress.compareTo(blockEnd) > 0) {
+ endAddress = blockEnd;
+ }
+ }
+ catch (AddressOutOfBoundsException e) {
+ endAddress = blockEnd;
+ }
+ return endAddress;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/EmptyComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/EmptyComparisonData.java
new file mode 100644
index 0000000000..de6cccf4b2
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/EmptyComparisonData.java
@@ -0,0 +1,54 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.util;
+
+import ghidra.program.model.address.AddressSet;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+
+public class EmptyComparisonData implements ComparisonData {
+
+ @Override
+ public Function getFunction() {
+ return null;
+ }
+
+ @Override
+ public AddressSetView getAddressSet() {
+ return new AddressSet();
+ }
+
+ @Override
+ public Program getProgram() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "No Comparison Data";
+ }
+
+ @Override
+ public String getShortDescription() {
+ return "Empty";
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/FunctionComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/FunctionComparisonData.java
new file mode 100644
index 0000000000..9c6a22081b
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/FunctionComparisonData.java
@@ -0,0 +1,87 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.util;
+
+import java.util.Objects;
+
+import ghidra.program.model.address.AddressSet;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.util.HTMLUtilities;
+
+/**
+ * ComparisonData for a function
+ */
+public class FunctionComparisonData implements ComparisonData {
+
+ private final Function function;
+
+ public FunctionComparisonData(Function function) {
+ this.function = Objects.requireNonNull(function);
+ }
+
+ @Override
+ public Function getFunction() {
+ return function;
+ }
+
+ @Override
+ public AddressSetView getAddressSet() {
+ if (function.isExternal()) {
+ return new AddressSet(function.getEntryPoint(), function.getEntryPoint());
+ }
+ return function.getBody();
+ }
+
+ @Override
+ public Program getProgram() {
+ return function.getProgram();
+ }
+
+ @Override
+ public String getDescription() {
+ StringBuffer buf = new StringBuffer();
+ String padStr = HTMLUtilities.spaces(4);
+ buf.append(padStr);
+
+ String functionStr = HTMLUtilities.friendlyEncodeHTML(function.getName(true) + "()");
+ String specialFunctionStr = HTMLUtilities.bold(functionStr);
+ buf.append(specialFunctionStr);
+ Program program = function.getProgram();
+ if (program != null) {
+ buf.append(" in ");
+
+ String programStr =
+ HTMLUtilities.friendlyEncodeHTML(program.getDomainFile().getPathname());
+ String specialProgramStr = HTMLUtilities.colorString(FG_COLOR_TITLE, programStr);
+ buf.append(specialProgramStr);
+ buf.append(padStr);
+ }
+ return HTMLUtilities.wrapAsHTML(buf.toString());
+ }
+
+ @Override
+ public String getShortDescription() {
+ return function.getName();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ListingDiff.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ListingDiff.java
index 86f0fef0e6..520fe84fb5 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ListingDiff.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ListingDiff.java
@@ -15,6 +15,10 @@
*/
package ghidra.program.util;
+import static ghidra.util.datastruct.Duo.Side.*;
+
+import java.util.ArrayList;
+
import ghidra.app.util.viewer.listingpanel.ListingDiffChangeListener;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
@@ -22,10 +26,8 @@ import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
-import ghidra.program.util.ListingAddressCorrelation;
import ghidra.util.Msg;
-
-import java.util.ArrayList;
+import ghidra.util.datastruct.Duo.Side;
/**
* Determines where instructions couldn't be matched and where they differ between sets of
@@ -100,15 +102,15 @@ public class ListingDiff {
private void getDiffs() throws MemoryAccessException {
init();
- AddressSetView addrSet1 = correlation.getAddressesInFirst();
- AddressSetView addrSet2 = correlation.getAddressesInSecond();
- Listing listing1 = correlation.getFirstProgram().getListing();
- Listing listing2 = correlation.getSecondProgram().getListing();
+ AddressSetView addrSet1 = correlation.getAddresses(LEFT);
+ AddressSetView addrSet2 = correlation.getAddresses(RIGHT);
+ Listing listing1 = correlation.getProgram(LEFT).getListing();
+ Listing listing2 = correlation.getProgram(RIGHT).getListing();
CodeUnitIterator cuIter1 = listing1.getCodeUnits(addrSet1, true);
CodeUnitIterator cuIter2 = listing2.getCodeUnits(addrSet2, true);
for (CodeUnit cu1 : cuIter1) {
Address min1 = cu1.getMinAddress();
- Address addr2 = correlation.getAddressInSecond(min1);
+ Address addr2 = correlation.getAddress(RIGHT, min1);
if (addr2 == null) {
// Add codeunit1 to the unmatchedDiffs
unmatchedCode1.addRange(cu1.getMinAddress(), cu1.getMaxAddress());
@@ -122,7 +124,7 @@ public class ListingDiff {
}
for (CodeUnit cu2 : cuIter2) {
Address min2 = cu2.getMinAddress();
- Address addr1 = correlation.getAddressInFirst(min2);
+ Address addr1 = correlation.getAddress(LEFT, min2);
if (addr1 == null) {
// Add codeunit2 to the unmatchedDiffs
unmatchedCode2.addRange(cu2.getMinAddress(), cu2.getMaxAddress());
@@ -138,18 +140,18 @@ public class ListingDiff {
}
private void recomputeCodeUnitDiffs() {
- AddressSetView addressSet1 = correlation.getAddressesInFirst();
- AddressSetView addressSet2 = correlation.getAddressesInSecond();
+ AddressSetView addressSet1 = correlation.getAddresses(LEFT);
+ AddressSetView addressSet2 = correlation.getAddresses(RIGHT);
AddressSetView matchedAddresses1 = addressSet1.subtract(unmatchedCode1);
AddressSetView matchedAddresses2 = addressSet2.subtract(unmatchedCode2);
- Listing listing1 = correlation.getFirstProgram().getListing();
- Listing listing2 = correlation.getSecondProgram().getListing();
+ Listing listing1 = correlation.getProgram(LEFT).getListing();
+ Listing listing2 = correlation.getProgram(RIGHT).getListing();
CodeUnitIterator cuIter1 = listing1.getCodeUnits(matchedAddresses1, true);
CodeUnitIterator cuIter2 = listing2.getCodeUnits(matchedAddresses2, true);
codeUnitDiffs1.clear();
for (CodeUnit cu1 : cuIter1) {
Address min1 = cu1.getMinAddress();
- Address addr2 = correlation.getAddressInSecond(min1);
+ Address addr2 = correlation.getAddress(RIGHT, min1);
if (addr2 == null) {
continue;
}
@@ -160,7 +162,7 @@ public class ListingDiff {
codeUnitDiffs2.clear();
for (CodeUnit cu2 : cuIter2) {
Address min2 = cu2.getMinAddress();
- Address addr1 = correlation.getAddressInFirst(min2);
+ Address addr1 = correlation.getAddress(LEFT, min2);
if (addr1 == null) {
continue;
}
@@ -386,85 +388,49 @@ public class ListingDiff {
}
/**
- * Gets the addresses in the first listing where matching code couldn't be determined in the
+ * Gets the address set for unmatched code for the given side
* second listing.
+ * @param side the LEFT or RIGHT side
* @return the addresses of the unmatched code in the first listing.
*/
- public AddressSetView getListing1UnmatchedCode() {
- return new AddressSet(unmatchedCode1);
- }
-
- /**
- * Gets the addresses in the second listing where matching code couldn't be determined in the
- * first listing.
- * @return the addresses of the unmatched code in the second listing.
- */
- public AddressSetView getListing2UnmatchedCode() {
- return new AddressSet(unmatchedCode2);
+ public AddressSetView getUnmatchedCode(Side side) {
+ return side == LEFT ? unmatchedCode1 : unmatchedCode2;
}
/**
* Gets the addresses in the first listing where differences were found based on the current
* difference settings.
+ * @param side the side (LEFT or RIGHT) to get the listing diffs for
* @return the addresses with differences in the first listing.
*/
- public AddressSetView getListing1Diffs() {
- AddressSet diffs = new AddressSet(getListing1ByteDiffs());
- diffs.add(getListing1CodeUnitDiffs());
- return DiffUtility.getCodeUnitSet(diffs, correlation.getFirstProgram());
- }
+ public AddressSetView getDiffs(Side side) {
- /**
- * Gets the addresses in the second listing where differences were found based on the current
- * difference settings.
- * @return the addresses with differences in the second listing.
- */
- public AddressSetView getListing2Diffs() {
- AddressSet diffs = new AddressSet(getListing2ByteDiffs());
- diffs.add(getListing2CodeUnitDiffs());
- return DiffUtility.getCodeUnitSet(diffs, correlation.getSecondProgram());
+ AddressSet diffs = new AddressSet(getByteDiffs(side));
+ diffs.add(getCodeUnitDiffs(side));
+ return DiffUtility.getCodeUnitSet(diffs, correlation.getProgram(side));
}
/**
* Gets the addresses in the first listing where code unit (mnemonic and/or operand) differences
* were found based on the current difference settings.
+ * @param side the side (LEFT or RIGHT) to get the code unit diffs for
* @return the addresses with code unit differences in the first listing.
*/
- public AddressSetView getListing1CodeUnitDiffs() {
- return new AddressSet(codeUnitDiffs1);
- }
-
- /**
- * Gets the addresses in the second listing where code unit (mnemonic and/or operand) differences
- * were found based on the current difference settings.
- * @return the addresses with code unit differences in the second listing.
- */
- public AddressSetView getListing2CodeUnitDiffs() {
- return new AddressSet(codeUnitDiffs2);
+ public AddressSetView getCodeUnitDiffs(Side side) {
+ return new AddressSet(side == LEFT ? codeUnitDiffs1 : codeUnitDiffs2);
}
/**
* Gets the addresses in the first listing where byte differences were found based on the
* current difference settings.
+ * @param side the side (LEFT or RIGHT) to get the byte diffs for
* @return the addresses with byte differences in the first listing.
*/
- public AddressSetView getListing1ByteDiffs() {
+ public AddressSetView getByteDiffs(Side side) {
if (ignoreByteDiffs) {
return new AddressSet();
}
- return new AddressSet(byteDiffs1);
- }
-
- /**
- * Gets the addresses in the second listing where byte differences were found based on the
- * current difference settings.
- * @return the addresses with byte differences in the second listing.
- */
- public AddressSetView getListing2ByteDiffs() {
- if (ignoreByteDiffs) {
- return new AddressSet();
- }
- return new AddressSet(byteDiffs2);
+ return new AddressSet(side == LEFT ? byteDiffs1 : byteDiffs2);
}
/**
@@ -480,9 +446,9 @@ public class ListingDiff {
return null;
}
if (isListing1) {
- return correlation.getAddressInSecond(address);
+ return correlation.getAddress(RIGHT, address);
}
- return correlation.getAddressInFirst(address);
+ return correlation.getAddress(LEFT, address);
}
/**
@@ -604,32 +570,26 @@ public class ListingDiff {
}
/**
- * Gets the matching code unit from the other listing for the specified code unit from one
+ * Gets the matching code unit from the other side listing given a code unit from a given side
* of the two listings whose differences this class determines.
* @param codeUnit the code unit whose match this determines.
- * @param isListing1 true indicates the code unit is from the first listing. false indicates
- * it is from the second listing.
+ * @param side the side the code unit came from
* @return the matching code unit or null
*/
- public CodeUnit getMatchingCodeUnit(CodeUnit codeUnit, boolean isListing1) {
+ public CodeUnit getMatchingCodeUnit(CodeUnit codeUnit, Side side) {
if (correlation == null) {
return null;
}
+ Side otherSide = side.otherSide();
+
Address minAddress = codeUnit.getMinAddress();
- Program sourceProgram = correlation.getFirstProgram();
- Program destinationProgram = correlation.getSecondProgram();
- if (isListing1) {
- Address destination = correlation.getAddressInSecond(minAddress);
- if (destination != null) {
- return destinationProgram.getListing().getCodeUnitAt(destination);
- }
- }
- else {
- Address source = correlation.getAddressInFirst(minAddress);
- if (source != null) {
- return sourceProgram.getListing().getCodeUnitAt(source);
- }
+ Program otherSideProgram = correlation.getProgram(otherSide);
+
+ Address otherAddress = correlation.getAddress(otherSide, minAddress);
+ if (otherAddress != null) {
+ return otherSideProgram.getListing().getCodeUnitAt(otherAddress);
}
+
// code unit not from our programs.
return null;
}
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsSlowTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsSlowTest.java
index c53dc39dca..a0e62a627a 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsSlowTest.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsSlowTest.java
@@ -15,6 +15,7 @@
*/
package ghidra.app.plugin.core.functioncompare;
+import static ghidra.util.datastruct.Duo.Side.*;
import static org.junit.Assert.*;
import java.awt.Window;
@@ -149,7 +150,7 @@ public class CompareFunctionsSlowTest extends AbstractGhidraHeadedIntegrationTes
assertTrue(prevAction.isEnabledForContext(context));
JPanel rightPanel =
- provider.getComponent().getDualListingPanel().getRightPanel().getFieldPanel();
+ provider.getComponent().getDualListingPanel().getListingPanel(RIGHT).getFieldPanel();
clickMouse(rightPanel, 1, 30, 30, 1, 0);
waitForSwing();
provider.getComponent().updateActionEnablement();
@@ -243,7 +244,7 @@ public class CompareFunctionsSlowTest extends AbstractGhidraHeadedIntegrationTes
* Builds a program with 2 functions
*/
private ProgramBuilder buildTestProgram1() throws Exception {
- ProgramBuilder builder = new ProgramBuilder("TestPgm1", ProgramBuilder._TOY_BE);
+ ProgramBuilder builder = new ProgramBuilder("Program 1", ProgramBuilder._TOY_BE);
builder.createMemory(".text", "0x1001000", 0x6600);
builder.setProperty(Program.DATE_CREATED, new Date(100000000)); // arbitrary, but consistent
@@ -261,7 +262,7 @@ public class CompareFunctionsSlowTest extends AbstractGhidraHeadedIntegrationTes
* Builds a program with 1 function
*/
private ProgramBuilder buildTestProgram2() throws Exception {
- ProgramBuilder builder = new ProgramBuilder("TestPgm1", ProgramBuilder._TOY_BE);
+ ProgramBuilder builder = new ProgramBuilder("Program 2", ProgramBuilder._TOY_BE);
builder.createMemory(".text", "0x1001000", 0x6600);
builder.setProperty(Program.DATE_CREATED, new Date(100000000)); // arbitrary, but consistent
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/viewer/listingpanel/ProgramLocationTranslatorTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/viewer/listingpanel/ProgramLocationTranslatorTest.java
new file mode 100644
index 0000000000..2978a05f40
--- /dev/null
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/viewer/listingpanel/ProgramLocationTranslatorTest.java
@@ -0,0 +1,191 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.viewer.listingpanel;
+
+import static ghidra.util.datastruct.Duo.Side.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import generic.test.AbstractGenericTest;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.data.IntegerDataType;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.symbol.Symbol;
+import ghidra.program.util.*;
+import ghidra.test.ToyProgramBuilder;
+import ghidra.util.datastruct.Duo.Side;
+
+public class ProgramLocationTranslatorTest extends AbstractGenericTest {
+ private static long PROGRAM1_BASE = 0x100000;
+ private static long PROGRAM2_BASE = 0x200000;
+
+ private Program program1;
+ private Program program2;
+ private Function function1;
+ private Function functionA;
+ private TestAddressCorrelation correlator;
+ private ProgramLocationTranslator translator;
+
+ @Before
+ public void setUp() throws Exception {
+ program1 = createProgram1();
+ program2 = createProgram2();
+ function1 = program1.getListing().getFunctionAt(addr(program1, PROGRAM1_BASE));
+ functionA = program1.getListing().getFunctionAt(addr(program2, PROGRAM2_BASE));
+ correlator = new TestAddressCorrelation();
+ translator = new ProgramLocationTranslator(correlator);
+ }
+
+ @After
+ public void tearDown() {
+ program1.release(this);
+ program2.release(this);
+ }
+
+ @Test
+ public void testBasicProgramLocation() {
+ ProgramLocation location = new ProgramLocation(program1, program1.getMinAddress());
+ ProgramLocation otherLocation = translator.getProgramLocation(RIGHT, location);
+ assertEquals(program2, otherLocation.getProgram());
+ assertEquals(program2.getMinAddress(), otherLocation.getAddress());
+
+ ProgramLocation roundTripLocation = translator.getProgramLocation(LEFT, otherLocation);
+ assertEquals(location, roundTripLocation);
+ }
+
+ @Test
+ public void testBytesLocation() {
+ Address minAddress = program1.getMinAddress();
+ Address byteAddress = minAddress.add(1);
+ ProgramLocation location =
+ new BytesFieldLocation(program1, minAddress, byteAddress, null, 2);
+
+ ProgramLocation otherLocation = translator.getProgramLocation(RIGHT, location);
+ assertTrue(otherLocation instanceof BytesFieldLocation);
+ assertEquals(program2, otherLocation.getProgram());
+ assertEquals(program2.getMinAddress(), otherLocation.getAddress());
+ assertEquals(program2.getMinAddress().add(1), otherLocation.getByteAddress());
+ assertEquals(2, otherLocation.getCharOffset());
+
+ ProgramLocation roundTripLocation = translator.getProgramLocation(LEFT, otherLocation);
+ assertEquals(location, roundTripLocation);
+
+ }
+
+ @Test
+ public void testVariableLocation() {
+ Function f = program1.getListing().getFunctionAt(addr(program1, PROGRAM1_BASE));
+ Variable[] variables = f.getAllVariables();
+ VariableLocation location = new VariableLocation(program1, variables[0], 0, 1);
+
+ ProgramLocation otherLocation = translator.getProgramLocation(RIGHT, location);
+ assertTrue(otherLocation instanceof VariableLocation);
+
+ ProgramLocation roundTripLocation = translator.getProgramLocation(LEFT, otherLocation);
+ assertEquals(location, roundTripLocation);
+
+ }
+
+ @Test
+ public void testLableFieldLocation() {
+ Symbol[] symbols = program1.getSymbolTable().getSymbols(addr(program1, PROGRAM1_BASE));
+ assertEquals(1, symbols.length);
+ LabelFieldLocation location = new LabelFieldLocation(symbols[0], 0, 3);
+ ProgramLocation otherLocation = translator.getProgramLocation(RIGHT, location);
+ assertTrue(otherLocation instanceof LabelFieldLocation);
+
+ ProgramLocation roundTripLocation = translator.getProgramLocation(LEFT, otherLocation);
+ assertEquals(location, roundTripLocation);
+ }
+
+ private Address addr(Program program, long offset) {
+ return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
+ }
+
+ private Program createProgram1() throws Exception {
+ ToyProgramBuilder builder = new ToyProgramBuilder("program1", true, this);
+
+ builder.createMemory("text", Long.toHexString(PROGRAM1_BASE), 0x5000);
+ buildFunction(builder, "fun1", PROGRAM1_BASE);
+ buildFunction(builder, "fun2", PROGRAM1_BASE + 0x1000);
+ buildFunction(builder, "fun3", PROGRAM1_BASE + 0x2000);
+
+ Program program = builder.getProgram();
+
+ builder.dispose();
+ return program;
+ }
+
+ private Program createProgram2() throws Exception {
+ ToyProgramBuilder builder = new ToyProgramBuilder("program2", true, this);
+
+ builder.createMemory("text", Long.toHexString(PROGRAM2_BASE), 5000);
+ buildFunction(builder, "funA", PROGRAM2_BASE);
+ buildFunction(builder, "funB", PROGRAM2_BASE + 0x1000);
+
+ Program program = builder.getProgram();
+
+ builder.dispose();
+ return program;
+ }
+
+ private void buildFunction(ToyProgramBuilder builder, String name, long address)
+ throws Exception {
+ builder.addBytesFallthrough(address);
+ builder.addBytesReturn(address + 2);
+
+ String functionStart = Long.toHexString(address);
+ builder.disassemble(functionStart, 3, true);
+ Function function = builder.createFunction(functionStart);
+ Variable var =
+ new LocalVariableImpl("i", new IntegerDataType(), -0x4, builder.getProgram());
+ builder.addFunctionVariable(function, var);
+
+ builder.createLabel(functionStart, name);// function label
+ }
+
+ private class TestAddressCorrelation implements ListingAddressCorrelation {
+
+ @Override
+ public Program getProgram(Side side) {
+ return side == LEFT ? program1 : program2;
+ }
+
+ @Override
+ public Function getFunction(Side side) {
+ return side == LEFT ? function1 : functionA;
+ }
+
+ @Override
+ public AddressSetView getAddresses(Side side) {
+ return side == LEFT ? program1.getMemory() : program2.getMemory();
+ }
+
+ @Override
+ public Address getAddress(Side side, Address otherSideAddress) {
+ if (side == LEFT) {
+ long offset = otherSideAddress.getOffset() - PROGRAM2_BASE;
+ return addr(program1, PROGRAM1_BASE + offset);
+ }
+ long offset = otherSideAddress.getOffset() - PROGRAM1_BASE;
+ return addr(program2, PROGRAM2_BASE + offset);
+
+ }
+
+ }
+}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompilerDiffCodeComparisonPanel.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompilerDiffCodeComparisonPanel.java
deleted file mode 100755
index d574f0a0d4..0000000000
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompilerDiffCodeComparisonPanel.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.codecompare;
-
-import java.util.List;
-
-import javax.swing.Icon;
-
-import docking.ActionContext;
-import docking.action.DockingAction;
-import docking.action.ToggleDockingAction;
-import docking.action.ToolBarData;
-import generic.theme.GIcon;
-import ghidra.app.decompiler.component.DecompileData;
-import ghidra.app.decompiler.component.DecompilerCodeComparisonPanel;
-import ghidra.app.decompiler.component.DualDecompileResultsListener;
-import ghidra.app.decompiler.component.DualDecompilerActionContext;
-import ghidra.codecompare.graphanalysis.TokenBin;
-import ghidra.framework.options.OptionsChangeListener;
-import ghidra.framework.options.ToolOptions;
-import ghidra.framework.plugintool.PluginTool;
-import ghidra.util.HTMLUtilities;
-import ghidra.util.HelpLocation;
-import ghidra.util.exception.CancelledException;
-import ghidra.util.task.Task;
-import ghidra.util.task.TaskBuilder;
-import ghidra.util.task.TaskLauncher;
-import ghidra.util.task.TaskListener;
-import resources.Icons;
-import resources.MultiIcon;
-
-/**
- * This is a CodeComparisonPanel that gets discovered by other providers that display dual
- * comparison views.
- * Note: Therefore there may not be any other classes that refer directly to it.
- */
-public class DecompilerDiffCodeComparisonPanel
- extends DecompilerCodeComparisonPanel
- implements DualDecompileResultsListener, OptionsChangeListener {
-
- public static final String CODE_DIFF_VIEW = "Decompiler Diff View";
- private static final String HELP_TOPIC = "FunctionComparison";
- private DecompileDataDiff decompileDataDiff;
- private DiffClangHighlightController leftHighlightController;
- private DiffClangHighlightController rightHighlightController;
- private CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator;
- private MyToggleExactConstantMatching toggleExactConstantMatchingAction;
- private boolean isMatchingConstantsExactly = true;
- private boolean toggleFlagWhenLastVisible = isMatchingConstantsExactly;
- private CompareFuncsFromMatchedTokensAction compareFuncsAction;
- private DecompilerCodeComparisonOptions comparisonOptions;
-
- /**
- * Constructor
- * @param owner owner
- * @param tool tool
- */
- public DecompilerDiffCodeComparisonPanel(String owner, PluginTool tool) {
- super(owner, tool);
-
- comparisonOptions = new DecompilerCodeComparisonOptions();
- initializeOptions();
- leftHighlightController = new DiffClangHighlightController(comparisonOptions);
- rightHighlightController = new DiffClangHighlightController(comparisonOptions);
- setHighlightControllers(leftHighlightController, rightHighlightController);
-
- // Make the left highlight listen to the right.
- leftHighlightController.addListener(rightHighlightController);
- // Make the right highlight listen to the left.
- rightHighlightController.addListener(leftHighlightController);
-
- addDualDecompileResultsListener(this);
- decompilerFieldPanelCoordinator = new CodeDiffFieldPanelCoordinator(this);
- setFieldPanelCoordinator(decompilerFieldPanelCoordinator);
-
- }
-
- private void initializeOptions() {
- ToolOptions options =
- tool.getOptions(DecompilerCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
- options.addOptionsChangeListener(this);
- comparisonOptions.registerOptions(options);
- comparisonOptions.loadOptions(options);
- }
-
- @Override
- public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
- Object newValue) {
- comparisonOptions.loadOptions(options);
- repaint();
- }
-
- @Override
- public void setVisible(boolean aFlag) {
- if (aFlag == isVisible()) {
- return;
- }
- if (aFlag) {
- // Becoming visible.
- if (toggleFlagWhenLastVisible != isMatchingConstantsExactly) {
- if (decompileDataDiff != null) {
- determineDecompilerDifferences();
- }
- }
- }
- else {
- // No longer visible.
- toggleFlagWhenLastVisible = isMatchingConstantsExactly;
- }
- super.setVisible(aFlag);
- updateActionEnablement();
- }
-
- @Override
- public String getTitle() {
- return CODE_DIFF_VIEW;
- }
-
- @Override
- public void decompileResultsSet(DecompileData leftDecompileResults,
- DecompileData rightDecompileResults) {
-
- if ((leftDecompileResults == null) || (rightDecompileResults == null) ||
- (leftDecompileResults.getFunction() == null) ||
- (rightDecompileResults.getFunction() == null)) {
- return;
- }
-
- decompileDataDiff = new DecompileDataDiff(leftDecompileResults, rightDecompileResults);
- determineDecompilerDifferences();
- }
-
- List getHighBins() {
- return decompilerFieldPanelCoordinator.getHighBins();
- }
-
- private void determineDecompilerDifferences() {
- if (decompileDataDiff == null) {
- return;
- }
- DetermineDecompilerDifferencesTask task =
- new DetermineDecompilerDifferencesTask(decompileDataDiff, isMatchingConstantsExactly,
- leftHighlightController, rightHighlightController, decompilerFieldPanelCoordinator);
-
- task.addTaskListener(new TaskListener() {
-
- @Override
- public void taskCompleted(Task completedTask) {
- // Does this need anything here?
- }
-
- @Override
- public void taskCancelled(Task cancelledTask) {
- // Does this need anything here?
- }
- });
-
- new TaskLauncher(task, getComponent());
- }
-
- @Override
- protected void createActions() {
- super.createActions();
- toggleExactConstantMatchingAction = new MyToggleExactConstantMatching(getClass().getName());
- compareFuncsAction = new CompareFuncsFromMatchedTokensAction(this, tool);
- }
-
- @Override
- public DockingAction[] getActions() {
- DockingAction[] parentActions = super.getActions();
- DockingAction[] myActions =
- new DockingAction[] { toggleExactConstantMatchingAction, compareFuncsAction };
- DockingAction[] allActions = new DockingAction[parentActions.length + myActions.length];
- System.arraycopy(parentActions, 0, allActions, 0, parentActions.length);
- System.arraycopy(myActions, 0, allActions, parentActions.length, myActions.length);
- return allActions;
- }
-
- @Override
- public void updateActionEnablement() {
- // Need to enable/disable toolbar button.
- toggleExactConstantMatchingAction.setEnabled(isVisible());
- }
-
- public class MyToggleExactConstantMatching extends ToggleDockingAction {
-
- private final Icon EXACT_CONSTANT_MATCHING_ICON = new GIcon("icon.base.source.c");
- private final Icon NO_EXACT_CONSTANT_MATCHING_ICON =
- new MultiIcon(EXACT_CONSTANT_MATCHING_ICON, Icons.NOT_ALLOWED_ICON);
-
- /**
- * Creates an action for toggling exact constant matching in the code diff's
- * dual decompiler.
- * @param owner the owner of this action (typically the provider).
- */
- public MyToggleExactConstantMatching(String owner) {
- super("Toggle Exact Constant Matching", owner);
- setHelpLocation(new HelpLocation(HELP_TOPIC, "Toggle Exact Constant Matching"));
-
- this.setToolBarData(new ToolBarData(NO_EXACT_CONSTANT_MATCHING_ICON, "toggles"));
-
- setDescription(HTMLUtilities.toHTML("Toggle whether or not constants must\n" +
- "be exactly the same value to be a match\n" + "in the " + CODE_DIFF_VIEW + "."));
- setSelected(false);
- setEnabled(true);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- if (context instanceof DualDecompilerActionContext) {
- return true;
- }
- return false;
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- isMatchingConstantsExactly = !isSelected();
- if (DecompilerDiffCodeComparisonPanel.this.isVisible()) {
- DecompilerDiffCodeComparisonPanel.this.determineDecompilerDifferences();
- }
- }
-
- @Override
- public void setSelected(boolean selected) {
- getToolBarData().setIcon(
- selected ? NO_EXACT_CONSTANT_MATCHING_ICON : EXACT_CONSTANT_MATCHING_ICON);
- super.setSelected(selected);
- }
- }
-
- @Override
- protected CodeDiffFieldPanelCoordinator createFieldPanelCoordinator() {
- CodeDiffFieldPanelCoordinator coordinator = new CodeDiffFieldPanelCoordinator(this);
- if (decompileDataDiff != null) {
- TaskBuilder.withRunnable(monitor -> {
- try {
- coordinator.replaceDecompileDataDiff(decompileDataDiff,
- isMatchingConstantsExactly, monitor);
- }
- catch (CancelledException e) {
- coordinator.clearLineNumberPairing();
- }
- }).setTitle("Initializing Code Compare").launchNonModal();
- }
- return coordinator;
-
- }
-}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DiffClangHighlightController.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DiffClangHighlightController.java
index 86616803be..9582c4b08c 100755
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DiffClangHighlightController.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DiffClangHighlightController.java
@@ -26,6 +26,7 @@ import generic.theme.GColor;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*;
+import ghidra.codecompare.decompile.DecompilerCodeComparisonOptions;
import ghidra.codecompare.graphanalysis.TokenBin;
import ghidra.util.ColorUtils;
import ghidra.util.SystemUtilities;
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/AbstractMatchedTokensAction.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/AbstractMatchedTokensAction.java
similarity index 81%
rename from Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/AbstractMatchedTokensAction.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/AbstractMatchedTokensAction.java
index a7cd616b77..f195272ef0 100644
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/AbstractMatchedTokensAction.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/AbstractMatchedTokensAction.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.codecompare;
+package ghidra.codecompare.decompile;
+
+import static ghidra.util.datastruct.Duo.Side.*;
import java.util.Iterator;
import java.util.List;
@@ -22,16 +24,17 @@ import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.DecompilerLocation;
-import ghidra.app.decompiler.component.*;
+import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.codecompare.graphanalysis.TokenBin;
import ghidra.program.model.listing.Program;
+import ghidra.util.datastruct.Duo.Side;
/**
* This is a base class for actions in a {@link DecompilerDiffCodeComparisonPanel}
*/
public abstract class AbstractMatchedTokensAction extends DockingAction {
- protected DecompilerDiffCodeComparisonPanel diffPanel;
+ protected DecompilerCodeComparisonPanel diffPanel;
protected boolean disableOnReadOnly;
/**
@@ -43,7 +46,7 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
* @param disableOnReadOnly if true, action will be disabled for read-only programs
*/
public AbstractMatchedTokensAction(String actionName, String owner,
- DecompilerDiffCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
+ DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
super(actionName, owner);
this.diffPanel = diffPanel;
this.disableOnReadOnly = disableOnReadOnly;
@@ -63,26 +66,20 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
if (!(context instanceof DualDecompilerActionContext compareContext)) {
return false;
}
- if (!(compareContext
- .getCodeComparisonPanel() instanceof DecompilerCodeComparisonPanel decompPanel)) {
- return false;
- }
+ DecompilerCodeComparisonPanel decompPanel = compareContext.getCodeComparisonPanel();
if (disableOnReadOnly) {
//get the program corresponding to the panel with focus
- Program program = decompPanel.getLeftProgram();
+ Side focusedSide = decompPanel.getActiveSide();
+ Program program = decompPanel.getProgram(focusedSide);
if (program == null) {
return false; //panel initializing; don't enable action
}
- if (!decompPanel.leftPanelHasFocus()) {
- program = decompPanel.getRightProgram();
- }
if (!program.canSave()) {
return false; //program is read-only, don't enable action
}
}
- @SuppressWarnings("unchecked")
TokenPair currentPair = getCurrentTokenPair(decompPanel);
return enabledForTokens(currentPair);
@@ -96,9 +93,9 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
* @return matching tokens (or null if no match)
*/
protected TokenPair getCurrentTokenPair(
- DecompilerCodeComparisonPanel extends DualDecompilerFieldPanelCoordinator> decompPanel) {
+ DecompilerCodeComparisonPanel decompPanel) {
- DecompilerPanel focusedPanel = decompPanel.getFocusedDecompilerPanel().getDecompilerPanel();
+ DecompilerPanel focusedPanel = decompPanel.getActiveDisplay().getDecompilerPanel();
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
return null;
@@ -126,7 +123,8 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
while (tokenIter.hasNext()) {
ClangToken currentMatch = tokenIter.next();
if (currentMatch.getClass().equals(focusedToken.getClass())) {
- return decompPanel.leftPanelHasFocus() ? new TokenPair(focusedToken, currentMatch)
+ return decompPanel.getActiveSide() == LEFT
+ ? new TokenPair(focusedToken, currentMatch)
: new TokenPair(currentMatch, focusedToken);
}
}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CDisplay.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CDisplay.java
new file mode 100644
index 0000000000..83aad0dc5c
--- /dev/null
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CDisplay.java
@@ -0,0 +1,200 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.codecompare.decompile;
+
+import java.math.BigInteger;
+import java.util.function.Consumer;
+
+import docking.widgets.fieldpanel.FieldPanel;
+import docking.widgets.fieldpanel.support.FieldLocation;
+import docking.widgets.fieldpanel.support.ViewerPosition;
+import ghidra.GhidraOptions;
+import ghidra.app.decompiler.DecompileOptions;
+import ghidra.app.decompiler.component.*;
+import ghidra.codecompare.DiffClangHighlightController;
+import ghidra.framework.options.ToolOptions;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.program.util.ProgramLocation;
+
+/**
+ * Represents one side of a dual decompiler compare window. It holds the decompiler controller and
+ * related state information for one side.
+ */
+public class CDisplay {
+ private final static String OPTIONS_TITLE = "Decompiler";
+
+ private DecompilerController controller;
+ private DecompileOptions decompileOptions;
+ private FieldLocation lastCursorPosition;
+ private DiffClangHighlightController highlightController;
+ private Program program;
+
+ private DecompilerProgramListener programListener;
+
+ public CDisplay(DecompilerCodeComparisonOptions comparisonOptions,
+ DecompileResultsListener decompileListener,
+ Consumer locationConsumer) {
+
+ highlightController = new DiffClangHighlightController(comparisonOptions);
+
+ decompileOptions = new DecompileOptions();
+
+ DecompilerCallbackHandler handler = new DecompilerCallbackHandlerAdapter() {
+ @Override
+ public void locationChanged(ProgramLocation programLocation) {
+ locationConsumer.accept(programLocation);
+ }
+ };
+
+ controller = new DecompilerController(handler, decompileOptions, null) {
+ @Override
+ public void setDecompileData(DecompileData decompileData) {
+ super.setDecompileData(decompileData);
+ decompileListener.setDecompileData(decompileData);
+ controller.getDecompilerPanel().validate();
+ }
+ };
+ controller.getDecompilerPanel().setHighlightController(highlightController);
+
+ programListener = new DecompilerProgramListener(controller, () -> refresh());
+
+ }
+
+ public DecompilerPanel getDecompilerPanel() {
+ return controller.getDecompilerPanel();
+ }
+
+ private void updateProgram(PluginTool tool, Function function) {
+ Program newProgram = function == null ? null : function.getProgram();
+ if (program == newProgram) {
+ return;
+ }
+ if (program != null) {
+ program.removeListener(programListener);
+ }
+
+ program = newProgram;
+
+ if (program != null) {
+ program.addListener(programListener);
+ initializeOptions(tool, function);
+ }
+
+ }
+
+ public void showFunction(PluginTool tool, Function function) {
+ updateProgram(tool, function);
+
+ lastCursorPosition = null;
+ if (function == null) {
+ clearAndShowMessage("No Function");
+ return;
+ }
+ if (function.isExternal()) {
+ clearAndShowMessage("\"" + function.getName(true) + "\" is an external function.");
+ return;
+ }
+
+ Address entry = function.getEntryPoint();
+ ProgramLocation location = new ProgramLocation(program, entry);
+ controller.display(program, location, new ViewerPosition(0, 0, 0));
+ }
+
+ public void clearAndShowMessage(String message) {
+ controller.setDecompileData(new EmptyDecompileData(message));
+ DecompilerPanel decompilerPanel = getDecompilerPanel();
+ decompilerPanel.paintImmediately(decompilerPanel.getBounds());
+ }
+
+ public void setMouseNavigationEnabled(boolean enabled) {
+ controller.setMouseNavigationEnabled(enabled);
+ }
+
+ public void dispose() {
+ if (program != null) {
+ program.removeListener(programListener);
+ program = null;
+ }
+ programListener.dispose();
+ controller.dispose();
+ }
+
+ public DecompilerController getController() {
+ return controller;
+ }
+
+ public void refresh() {
+ saveCursorPosition();
+ DecompileData data = getDecompileData();
+ if (data != null) {
+ controller.refreshDisplay(data.getProgram(), data.getLocation(), null);
+ }
+ }
+
+ public void programClosed(Program closedProgram) {
+ if (closedProgram == this.program) {
+ controller.clear();
+ controller.programClosed(closedProgram);
+ updateProgram(null, null);
+ }
+ }
+
+ public boolean isBusy() {
+ return controller.isDecompiling();
+ }
+
+ public DecompileData getDecompileData() {
+ DecompileData decompileData = controller.getDecompileData();
+ if (decompileData instanceof EmptyDecompileData) {
+ return null;
+ }
+ return decompileData;
+ }
+
+ public void initializeOptions(PluginTool tool, Function function) {
+ if (tool == null) {
+ return;
+ }
+ ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
+ ToolOptions options = tool.getOptions(OPTIONS_TITLE);
+ Program program = function == null ? null : function.getProgram();
+ decompileOptions.grabFromToolAndProgram(fieldOptions, options, program);
+ }
+
+ DiffClangHighlightController getHighlightController() {
+ return highlightController;
+ }
+
+ private void saveCursorPosition() {
+ lastCursorPosition = getDecompilerPanel().getFieldPanel().getCursorLocation();
+ }
+
+ void restoreCursorPosition() {
+ if (lastCursorPosition != null) {
+ BigInteger index = lastCursorPosition.getIndex();
+ int fieldNum = lastCursorPosition.getFieldNum();
+ int row = lastCursorPosition.getRow();
+ int col = lastCursorPosition.getCol();
+
+ FieldPanel fieldPanel = getDecompilerPanel().getFieldPanel();
+ fieldPanel.setCursorPosition(index, fieldNum, row, col);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/CodeDiffFieldPanelCoordinator.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CodeDiffFieldPanelCoordinator.java
similarity index 96%
rename from Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/CodeDiffFieldPanelCoordinator.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CodeDiffFieldPanelCoordinator.java
index 5c3023d913..f3ae591e13 100755
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/CodeDiffFieldPanelCoordinator.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CodeDiffFieldPanelCoordinator.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.codecompare;
+package ghidra.codecompare.decompile;
+
+import static ghidra.util.datastruct.Duo.Side.*;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -25,7 +27,6 @@ import org.apache.commons.collections4.bidimap.DualHashBidiMap;
import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerPanel;
-import ghidra.app.decompiler.component.DualDecompilerFieldPanelCoordinator;
import ghidra.codecompare.graphanalysis.TokenBin;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.util.ProgramLocation;
@@ -52,10 +53,10 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
* Constructor
* @param dualDecompilerPanel decomp comparison panel
*/
- public CodeDiffFieldPanelCoordinator(DecompilerDiffCodeComparisonPanel dualDecompilerPanel) {
+ public CodeDiffFieldPanelCoordinator(DecompilerCodeComparisonPanel dualDecompilerPanel) {
super(dualDecompilerPanel);
- this.leftDecompilerPanel = dualDecompilerPanel.getLeftDecompilerPanel();
- this.rightDecompilerPanel = dualDecompilerPanel.getRightDecompilerPanel();
+ this.leftDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(LEFT);
+ this.rightDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(RIGHT);
leftToRightLineNumberPairing = new DualHashBidiMap<>();
}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/CompareFuncsFromMatchedTokensAction.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CompareFuncsFromMatchedTokensAction.java
similarity index 90%
rename from Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/CompareFuncsFromMatchedTokensAction.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CompareFuncsFromMatchedTokensAction.java
index 9fe42406f0..04acb10b91 100644
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/CompareFuncsFromMatchedTokensAction.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/CompareFuncsFromMatchedTokensAction.java
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.codecompare;
+package ghidra.codecompare.decompile;
+
+import static ghidra.util.datastruct.Duo.Side.*;
import docking.ActionContext;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangFuncNameToken;
-import ghidra.app.decompiler.component.DecompilerCodeComparisonPanel;
-import ghidra.app.decompiler.component.DualDecompilerActionContext;
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider;
import ghidra.app.services.FunctionComparisonService;
import ghidra.framework.plugintool.PluginTool;
@@ -45,7 +45,7 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
* @param diffPanel diff Panel
* @param tool tool
*/
- public CompareFuncsFromMatchedTokensAction(DecompilerDiffCodeComparisonPanel diffPanel,
+ public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
PluginTool tool) {
super(ACTION_NAME, tool.getName(), diffPanel, false);
this.tool = tool;
@@ -84,12 +84,8 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
return;
}
- if (!(compareContext
- .getCodeComparisonPanel() instanceof DecompilerCodeComparisonPanel decompPanel)) {
- return;
- }
+ DecompilerCodeComparisonPanel decompPanel = compareContext.getCodeComparisonPanel();
- @SuppressWarnings("unchecked")
TokenPair currentPair = getCurrentTokenPair(decompPanel);
if (currentPair == null || currentPair.leftToken() == null ||
currentPair.rightToken() == null) {
@@ -99,8 +95,8 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
ClangFuncNameToken leftFuncToken = (ClangFuncNameToken) currentPair.leftToken();
ClangFuncNameToken rightFuncToken = (ClangFuncNameToken) currentPair.rightToken();
- Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getLeftProgram());
- Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getRightProgram());
+ Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getProgram(LEFT));
+ Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getProgram(RIGHT));
if (leftFunction == null || rightFunction == null) {
return;
}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompileDataDiff.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompileDataDiff.java
similarity index 99%
rename from Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompileDataDiff.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompileDataDiff.java
index 56aa8807ed..3af1359ff6 100755
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompileDataDiff.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompileDataDiff.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.codecompare;
+package ghidra.codecompare.decompile;
import java.util.ArrayList;
import java.util.HashSet;
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompilerCodeComparisonOptions.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerCodeComparisonOptions.java
similarity index 83%
rename from Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompilerCodeComparisonOptions.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerCodeComparisonOptions.java
index 07d5a750b1..9f4d6b08ca 100644
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DecompilerCodeComparisonOptions.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerCodeComparisonOptions.java
@@ -13,18 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.codecompare;
+package ghidra.codecompare.decompile;
import java.awt.Color;
import generic.theme.GColor;
+import ghidra.framework.options.OptionsChangeListener;
import ghidra.framework.options.ToolOptions;
+import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation;
+import ghidra.util.bean.opteditor.OptionsVetoException;
+import utility.function.Callback;
+import utility.function.Dummy;
/**
* This class holds the options for the decompiler diff view.
*/
-public class DecompilerCodeComparisonOptions {
+public class DecompilerCodeComparisonOptions implements OptionsChangeListener {
private static final String MATCHING_TOKEN_HIGHLIGHT_KEY = "Focused Token Match Highlight";
private static final String UNMATCHED_TOKEN_HIGHLIGHT_KEY = "Focused Token Unmatched Highlight";
@@ -53,15 +58,18 @@ public class DecompilerCodeComparisonOptions {
private Color unmatchedTokenHighlight;
private Color ineligibleTokenHighlight;
private Color diffHighlight;
+ private Callback optionsChangedCallback;
public static final String OPTIONS_CATEGORY_NAME = "Decompiler Code Comparison";
public static final String HELP_TOPIC = "FunctionComparison";
- /**
- * Constructor
- */
- public DecompilerCodeComparisonOptions() {
-
+ public DecompilerCodeComparisonOptions(PluginTool tool, Callback optionsChangedCallback) {
+ this.optionsChangedCallback = Dummy.ifNull(optionsChangedCallback);
+ ToolOptions options =
+ tool.getOptions(DecompilerCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
+ options.addOptionsChangeListener(this);
+ registerOptions(options);
+ loadOptions(options);
}
/**
@@ -136,4 +144,11 @@ public class DecompilerCodeComparisonOptions {
return diffHighlight;
}
+ @Override
+ public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
+ Object newValue) throws OptionsVetoException {
+ loadOptions(options);
+ optionsChangedCallback.call();
+ }
+
}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerCodeComparisonPanel.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerCodeComparisonPanel.java
new file mode 100644
index 0000000000..b6621b654d
--- /dev/null
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerCodeComparisonPanel.java
@@ -0,0 +1,395 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.codecompare.decompile;
+
+import static ghidra.util.datastruct.Duo.Side.*;
+
+import java.awt.Component;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.Icon;
+import javax.swing.JComponent;
+
+import docking.ActionContext;
+import docking.ComponentProvider;
+import docking.action.*;
+import docking.options.OptionsService;
+import generic.theme.GIcon;
+import ghidra.app.decompiler.component.DecompileData;
+import ghidra.app.decompiler.component.DecompilerPanel;
+import ghidra.app.util.viewer.util.CodeComparisonPanel;
+import ghidra.codecompare.DiffClangHighlightController;
+import ghidra.codecompare.graphanalysis.TokenBin;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.program.util.ProgramLocation;
+import ghidra.util.HTMLUtilities;
+import ghidra.util.HelpLocation;
+import ghidra.util.datastruct.Duo;
+import ghidra.util.datastruct.Duo.Side;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskBuilder;
+import ghidra.util.task.TaskLauncher;
+import resources.Icons;
+import resources.MultiIcon;
+
+/**
+ * Panel that displays two decompilers for comparison
+ */
+public class DecompilerCodeComparisonPanel
+ extends CodeComparisonPanel {
+
+ public static final String NAME = "Decompile Diff View";
+
+ private boolean isStale = true;
+
+ private Duo cDisplays = new Duo<>();
+
+ private DecompilerCodeComparisonOptions comparisonOptions;
+ private CodeDiffFieldPanelCoordinator coordinator;
+ private DecompileDataDiff decompileDataDiff;
+
+ private ToggleExactConstantMatching toggleExactConstantMatchingAction;
+ private List actions = new ArrayList<>();
+ private Duo functions = new Duo<>();
+
+ /**
+ * Creates a comparison panel with two decompilers
+ *
+ * @param owner the owner of this panel
+ * @param tool the tool displaying this panel
+ */
+ public DecompilerCodeComparisonPanel(String owner, PluginTool tool) {
+ super(owner, tool);
+ comparisonOptions = new DecompilerCodeComparisonOptions(tool, () -> repaint());
+
+ cDisplays = new Duo<>(buildCDisplay(LEFT), buildCDisplay(RIGHT));
+ cDisplays.get(LEFT).initializeOptions(tool, getFunction(LEFT));
+ cDisplays.get(RIGHT).initializeOptions(tool, getFunction(RIGHT));
+
+ buildPanel();
+ setSynchronizedScrolling(true);
+ linkHighlightControllers();
+ createActions();
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ protected void comparisonDataChanged() {
+
+ maybeLoadFunction(LEFT, comparisonData.get(LEFT).getFunction());
+ maybeLoadFunction(RIGHT, comparisonData.get(RIGHT).getFunction());
+ if (coordinator != null) {
+ coordinator.leftLocationChanged((ProgramLocation) null);
+ }
+ }
+
+ public void maybeLoadFunction(Side side, Function function) {
+ // we keep a local copy of the function so we know if it is already decompiled
+ if (functions.get(side) == function) {
+ return;
+ }
+
+ // Clear the scroll info and highlight info to prevent unnecessary highlighting, etc.
+ loadFunction(side, null);
+ loadFunction(side, function);
+
+ }
+
+ @Override
+ public void dispose() {
+ setSynchronizedScrolling(false); // disposes any exiting coordinator
+ cDisplays.each(CDisplay::dispose);
+ comparisonOptions = null;
+ }
+
+ /**
+ * Gets the display from the active side.
+ * @return the active display
+ */
+ public CDisplay getActiveDisplay() {
+ return cDisplays.get(activeSide);
+ }
+
+ /**
+ * Gets the left side's C display panel.
+ * @return the left C display panel
+ */
+ public CDisplay getLeftPanel() {
+ return cDisplays.get(LEFT);
+ }
+
+ /**
+ * Gets the right side's C display panel.
+ * @return the right C display panel
+ */
+ public CDisplay getRightPanel() {
+ return cDisplays.get(RIGHT);
+ }
+
+ @Override
+ public List getActions() {
+ List allActions = super.getActions();
+ allActions.addAll(actions);
+ return allActions;
+ }
+
+ @Override
+ public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
+
+ Component component = event != null ? event.getComponent()
+ : getActiveDisplay().getDecompilerPanel().getFieldPanel();
+
+ DualDecompilerActionContext dualDecompContext =
+ new DualDecompilerActionContext(provider, this, component);
+
+ return dualDecompContext;
+ }
+
+ @Override
+ public void programClosed(Program program) {
+ cDisplays.each(c -> c.programClosed(program));
+ }
+
+ @Override
+ public JComponent getComparisonComponent(Side side) {
+ return cDisplays.get(side).getDecompilerPanel();
+ }
+
+ private void createActions() {
+ toggleExactConstantMatchingAction = new ToggleExactConstantMatching(getClass().getName());
+
+ actions.add(new DecompilerDiffViewFindAction(owner, tool));
+ actions.add(new DecompilerCodeComparisonOptionsAction());
+ actions.add(toggleExactConstantMatchingAction);
+ actions.add(new CompareFuncsFromMatchedTokensAction(this, tool));
+ }
+
+ private void decompileDataSet(Side side, DecompileData dcompileData) {
+ cDisplays.get(side).restoreCursorPosition();
+ updateDiffs();
+ }
+
+ private boolean isMatchingConstantsExactly() {
+ return !toggleExactConstantMatchingAction.isSelected();
+ }
+
+ private void loadFunction(Side side, Function function) {
+ if (functions.get(side) != function) {
+ functions = functions.with(side, function);
+ cDisplays.get(side).showFunction(tool, function);
+ }
+ }
+
+ private void locationChanged(Side side, ProgramLocation location) {
+ if (coordinator != null) {
+ if (side == LEFT) {
+ coordinator.leftLocationChanged(location);
+ }
+ else {
+ coordinator.rightLocationChanged(location);
+ }
+ }
+ }
+
+ private void updateDiffs() {
+ DecompileData leftDecompileData = cDisplays.get(LEFT).getDecompileData();
+ DecompileData rightDecompileData = cDisplays.get(RIGHT).getDecompileData();
+
+ if (isValid(leftDecompileData) && isValid(rightDecompileData)) {
+ decompileDataDiff = new DecompileDataDiff(leftDecompileData, rightDecompileData);
+ determineDecompilerDifferences();
+ }
+ }
+
+ private boolean isValid(DecompileData decompileData) {
+ return decompileData != null && decompileData.isValid();
+ }
+
+ private CDisplay buildCDisplay(Side side) {
+ return new CDisplay(comparisonOptions,
+ decompileData -> decompileDataSet(side, decompileData),
+ l -> locationChanged(side, l));
+ }
+
+ private void linkHighlightControllers() {
+ DiffClangHighlightController left = cDisplays.get(LEFT).getHighlightController();
+ DiffClangHighlightController right = cDisplays.get(RIGHT).getHighlightController();
+ left.addListener(right);
+ right.addListener(left);
+
+ }
+
+ @Override
+ public void setSynchronizedScrolling(boolean synchronize) {
+
+ if (coordinator != null) {
+ coordinator.dispose();
+ coordinator = null;
+ }
+
+ if (synchronize) {
+ coordinator = createCoordinator();
+ doSynchronize();
+ }
+ }
+
+ private void doSynchronize() {
+ CDisplay activeDisplay = getActiveDisplay();
+ ProgramLocation programLocation =
+ activeDisplay.getDecompilerPanel().getCurrentLocation();
+ if (activeDisplay == cDisplays.get(LEFT)) {
+ coordinator.leftLocationChanged(programLocation);
+ }
+ else {
+ coordinator.rightLocationChanged(programLocation);
+ }
+ }
+
+ private CodeDiffFieldPanelCoordinator createCoordinator() {
+ CodeDiffFieldPanelCoordinator panelCoordinator = new CodeDiffFieldPanelCoordinator(this);
+ if (decompileDataDiff != null) {
+ TaskBuilder.withRunnable(monitor -> {
+ try {
+ panelCoordinator.replaceDecompileDataDiff(decompileDataDiff,
+ isMatchingConstantsExactly(), monitor);
+ }
+ catch (CancelledException e) {
+ panelCoordinator.clearLineNumberPairing();
+ }
+ }).setTitle("Initializing Code Compare").launchNonModal();
+ }
+ return panelCoordinator;
+
+ }
+
+ private class DecompilerCodeComparisonOptionsAction extends DockingAction {
+
+ DecompilerCodeComparisonOptionsAction() {
+ super("Decompiler Code Comparison Options", owner);
+ setDescription("Show the tool options for the Decompiler Code Comparison.");
+ setPopupMenuData(new MenuData(new String[] { "Properties" }, null, "Z_Properties"));
+ setHelpLocation(
+ new HelpLocation("FunctionComparison", "Decompiler_Code_Comparison_Options"));
+ }
+
+ @Override
+ public boolean isEnabledForContext(ActionContext context) {
+ return (context instanceof DualDecompilerActionContext);
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+ OptionsService service = tool.getService(OptionsService.class);
+ service.showOptionsDialog("FunctionComparison", "Decompiler Code Comparison");
+ }
+ }
+
+ @Override
+ public void setVisible(boolean b) {
+ super.setVisible(b);
+ if (b && isStale) {
+ determineDecompilerDifferences();
+ isStale = false;
+ }
+ updateActionEnablement();
+ }
+
+ List getHighBins() {
+ return coordinator == null ? null : coordinator.getHighBins();
+ }
+
+ private void determineDecompilerDifferences() {
+ if (decompileDataDiff == null) {
+ return;
+ }
+ DiffClangHighlightController leftHighlights = cDisplays.get(LEFT).getHighlightController();
+ DiffClangHighlightController rightHighlights =
+ cDisplays.get(RIGHT).getHighlightController();
+ DetermineDecompilerDifferencesTask task =
+ new DetermineDecompilerDifferencesTask(decompileDataDiff, isMatchingConstantsExactly(),
+ leftHighlights, rightHighlights, coordinator);
+
+ new TaskLauncher(task, this);
+ }
+
+ @Override
+ public void updateActionEnablement() {
+ // Need to enable/disable toolbar button.
+ toggleExactConstantMatchingAction.setEnabled(isVisible());
+ }
+
+ public boolean isBusy() {
+ return cDisplays.get(LEFT).isBusy() || cDisplays.get(RIGHT).isBusy();
+ }
+
+ public DecompilerPanel getDecompilerPanel(Side side) {
+ return cDisplays.get(side).getDecompilerPanel();
+ }
+
+ public class ToggleExactConstantMatching extends ToggleDockingAction {
+
+ private final Icon EXACT_CONSTANT_MATCHING_ICON = new GIcon("icon.base.source.c");
+ private final Icon NO_EXACT_CONSTANT_MATCHING_ICON =
+ new MultiIcon(EXACT_CONSTANT_MATCHING_ICON, Icons.NOT_ALLOWED_ICON);
+
+ /**
+ * Creates an action for toggling exact constant matching in the code diff's
+ * dual decompiler.
+ * @param owner the owner of this action (typically the provider).
+ */
+ public ToggleExactConstantMatching(String owner) {
+ super("Toggle Exact Constant Matching", owner);
+ setHelpLocation(new HelpLocation(HELP_TOPIC, "Toggle Exact Constant Matching"));
+
+ this.setToolBarData(new ToolBarData(NO_EXACT_CONSTANT_MATCHING_ICON, "toggles"));
+
+ setDescription(HTMLUtilities.toHTML("Toggle whether or not constants must\n" +
+ "be exactly the same value to be a match\n" + "in the Decomiler Diff View."));
+ setSelected(false);
+ setEnabled(true);
+ }
+
+ @Override
+ public boolean isEnabledForContext(ActionContext context) {
+ if (context instanceof DualDecompilerActionContext) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+ if (isVisible()) {
+ determineDecompilerDifferences();
+ }
+ }
+
+ @Override
+ public void setSelected(boolean selected) {
+ getToolBarData().setIcon(
+ selected ? NO_EXACT_CONSTANT_MATCHING_ICON : EXACT_CONSTANT_MATCHING_ICON);
+ super.setSelected(selected);
+ }
+ }
+}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerDiffViewFindAction.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerDiffViewFindAction.java
similarity index 66%
rename from Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerDiffViewFindAction.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerDiffViewFindAction.java
index 6e3b89bbd6..5e43233797 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerDiffViewFindAction.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DecompilerDiffViewFindAction.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.app.decompiler.component;
+package ghidra.codecompare.decompile;
+
+import static ghidra.util.datastruct.Duo.Side.*;
import java.awt.event.KeyEvent;
@@ -23,17 +25,17 @@ import docking.ActionContext;
import docking.DockingUtils;
import docking.action.*;
import docking.widgets.FindDialog;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
+import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.actions.DecompilerSearcher;
import ghidra.app.util.HelpTopics;
-import ghidra.app.util.viewer.util.CodeComparisonPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation;
+import ghidra.util.datastruct.Duo;
+import ghidra.util.datastruct.Duo.Side;
public class DecompilerDiffViewFindAction extends DockingAction {
- private FindDialog leftFindDialog;
- private FindDialog rightFindDialog;
+ private Duo findDialogs;
private PluginTool tool;
public DecompilerDiffViewFindAction(String owner, PluginTool tool) {
@@ -53,33 +55,21 @@ public class DecompilerDiffViewFindAction extends DockingAction {
@Override
public boolean isEnabledForContext(ActionContext context) {
- if (!(context instanceof DualDecompilerActionContext dualContext)) {
- return false;
- }
- CodeComparisonPanel extends FieldPanelCoordinator> compPanel =
- dualContext.getCodeComparisonPanel();
- if (!(compPanel instanceof DecompilerCodeComparisonPanel decompilerCompPanel)) {
- return false;
- }
- return true;
+ return (context instanceof DualDecompilerActionContext);
}
@Override
public void actionPerformed(ActionContext context) {
DualDecompilerActionContext dualContext = (DualDecompilerActionContext) context;
- @SuppressWarnings("unchecked")
- DecompilerCodeComparisonPanel extends FieldPanelCoordinator> decompilerCompPanel =
- (DecompilerCodeComparisonPanel extends FieldPanelCoordinator>) dualContext
- .getCodeComparisonPanel();
+ DecompilerCodeComparisonPanel decompilerCompPanel =
+ dualContext.getCodeComparisonPanel();
- DecompilerPanel focusedPanel = decompilerCompPanel.getLeftDecompilerPanel();
- FindDialog dialog = leftFindDialog;
- if (!decompilerCompPanel.leftPanelHasFocus()) {
- focusedPanel = decompilerCompPanel.getRightDecompilerPanel();
- dialog = rightFindDialog;
- }
+ Side focusedSide = decompilerCompPanel.getActiveSide();
+ DecompilerPanel focusedPanel = decompilerCompPanel.getDecompilerPanel(focusedSide);
+ FindDialog dialog = findDialogs.get(focusedSide);
if (dialog == null) {
- dialog = createFindDialog(focusedPanel, decompilerCompPanel.leftPanelHasFocus());
+ dialog = createFindDialog(focusedPanel, focusedSide);
+ findDialogs = findDialogs.with(focusedSide, dialog);
}
String text = focusedPanel.getSelectedText();
@@ -89,8 +79,8 @@ public class DecompilerDiffViewFindAction extends DockingAction {
tool.showDialog(dialog);
}
- private FindDialog createFindDialog(DecompilerPanel decompilerPanel, boolean isLeftFocused) {
- String title = (isLeftFocused ? "Left" : "Right");
+ private FindDialog createFindDialog(DecompilerPanel decompilerPanel, Side side) {
+ String title = (side == LEFT ? "Left" : "Right");
title += " Decompiler Find Text";
FindDialog dialog = new FindDialog(title, new DecompilerSearcher(decompilerPanel)) {
@@ -102,13 +92,6 @@ public class DecompilerDiffViewFindAction extends DockingAction {
};
dialog.setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionFind"));
- if (isLeftFocused) {
- leftFindDialog = dialog;
- }
- else {
- rightFindDialog = dialog;
- }
-
return dialog;
}
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DetermineDecompilerDifferencesTask.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DetermineDecompilerDifferencesTask.java
similarity index 96%
rename from Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DetermineDecompilerDifferencesTask.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DetermineDecompilerDifferencesTask.java
index 6520d029b7..11615891c7 100755
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/DetermineDecompilerDifferencesTask.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DetermineDecompilerDifferencesTask.java
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.codecompare;
+package ghidra.codecompare.decompile;
import java.util.List;
import java.util.Set;
import ghidra.app.decompiler.ClangToken;
+import ghidra.codecompare.DiffClangHighlightController;
import ghidra.codecompare.graphanalysis.TokenBin;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.Task;
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DualDecompilerActionContext.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DualDecompilerActionContext.java
new file mode 100644
index 0000000000..f3939e00ef
--- /dev/null
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DualDecompilerActionContext.java
@@ -0,0 +1,52 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.codecompare.decompile;
+
+import java.awt.Component;
+
+import docking.ComponentProvider;
+import ghidra.app.context.RestrictedAddressSetContext;
+import ghidra.app.util.viewer.util.CodeComparisonActionContext;
+
+/**
+ * Action context for a dual decompiler panel.
+ */
+public class DualDecompilerActionContext extends CodeComparisonActionContext
+ implements RestrictedAddressSetContext {
+
+ private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
+
+ /**
+ * Creates an action context for a dual decompiler panel.
+ * @param provider the provider for this context
+ * @param panel the DecompilerComparisonPanel
+ * @param source the source of the action
+ */
+ public DualDecompilerActionContext(ComponentProvider provider,
+ DecompilerCodeComparisonPanel panel, Component source) {
+ super(provider, panel, source);
+ this.decompilerComparisonPanel = panel;
+ }
+
+ /**
+ * Returns the {@link DecompilerCodeComparisonPanel} that generated this context
+ * @return the decompiler comparison panel that generated this context
+ */
+ @Override
+ public DecompilerCodeComparisonPanel getCodeComparisonPanel() {
+ return decompilerComparisonPanel;
+ }
+}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompilerFieldPanelCoordinator.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DualDecompilerFieldPanelCoordinator.java
similarity index 78%
rename from Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompilerFieldPanelCoordinator.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DualDecompilerFieldPanelCoordinator.java
index 58326d4874..de169cb176 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompilerFieldPanelCoordinator.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/DualDecompilerFieldPanelCoordinator.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.app.decompiler.component;
+package ghidra.codecompare.decompile;
+
+import static ghidra.util.datastruct.Duo.Side.*;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
@@ -22,9 +24,9 @@ import ghidra.program.util.ProgramLocation;
abstract public class DualDecompilerFieldPanelCoordinator extends LineLockedFieldPanelCoordinator {
public DualDecompilerFieldPanelCoordinator(
- DecompilerCodeComparisonPanel extends DualDecompilerFieldPanelCoordinator> dualDecompilerPanel) {
- super(new FieldPanel[] { dualDecompilerPanel.getLeftDecompilerPanel().getFieldPanel(),
- dualDecompilerPanel.getRightDecompilerPanel().getFieldPanel() });
+ DecompilerCodeComparisonPanel dualDecompilerPanel) {
+ super(new FieldPanel[] { dualDecompilerPanel.getDecompilerPanel(LEFT).getFieldPanel(),
+ dualDecompilerPanel.getDecompilerPanel(RIGHT).getFieldPanel() });
}
abstract public void leftLocationChanged(ProgramLocation leftLocation);
diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/TokenPair.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/TokenPair.java
similarity index 94%
rename from Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/TokenPair.java
rename to Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/TokenPair.java
index eab23c7eb0..4b917f038c 100644
--- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/TokenPair.java
+++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/codecompare/decompile/TokenPair.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.codecompare;
+package ghidra.codecompare.decompile;
import ghidra.app.decompiler.ClangToken;
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java
index c5da8d2093..a03315940c 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileResults.java
@@ -73,6 +73,7 @@ public class DecompileResults {
hfunc = null;
hparamid = null;
docroot = null;
+ this.processState = processState;
//dumpResults(raw);
decodeStream(decoder);
}
@@ -144,6 +145,14 @@ public class DecompileResults {
return processState == DecompileProcess.DisposeState.DISPOSED_ON_STARTUP_FAILURE;
}
+ /**
+ * Returns true if the decompile completed normally
+ * @return true if the decompile completed normally
+ */
+ public boolean isValid() {
+ return errMsg == null || errMsg.isBlank();
+ }
+
/**
* Return any error message associated with the
* decompilation producing these results. Generally,
@@ -180,7 +189,7 @@ public class DecompileResults {
/**
* Get the marked up C code associated with these
* decompilation results. If there was an error, or
- * code generation was turned off, retur null
+ * code generation was turned off, return null
* @return the resulting root of C markup
*/
public ClangTokenGroup getCCodeMarkup() {
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/CDisplayPanel.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/CDisplayPanel.java
deleted file mode 100644
index ecb389addb..0000000000
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/CDisplayPanel.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.decompiler.component;
-
-import java.awt.BorderLayout;
-
-import javax.swing.JPanel;
-
-import docking.widgets.fieldpanel.support.ViewerPosition;
-import ghidra.app.decompiler.DecompileOptions;
-import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
-import ghidra.app.util.viewer.listingpanel.ProgramLocationListener;
-import ghidra.program.model.address.Address;
-import ghidra.program.model.listing.Function;
-import ghidra.program.model.listing.Program;
-import ghidra.program.util.ProgramLocation;
-import ghidra.program.util.ProgramSelection;
-import ghidra.util.bean.field.AnnotatedTextFieldElement;
-import utility.function.Callback;
-
-public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
-
- private DecompilerController controller;
- private DecompileResultsListener listener;
-
- private ProgramLocationListener locationListener;
-
- public CDisplayPanel(DecompileResultsListener listener) {
- this(new DecompileOptions(), listener);
- }
-
- public CDisplayPanel(DecompileOptions decompileOptions, DecompileResultsListener listener) {
- super(new BorderLayout());
- this.listener = listener;
- controller = new ExtendedDecompilerController(this, decompileOptions, null);
- DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
- add(decompilerPanel);
- }
-
- public void setProgramLocationListener(ProgramLocationListener locationListener) {
- this.locationListener = locationListener;
- }
-
- class ExtendedDecompilerController extends DecompilerController {
-
- public ExtendedDecompilerController(DecompilerCallbackHandler handler,
- DecompileOptions options, DecompilerClipboardProvider clipboard) {
- super(handler, options, clipboard);
- }
-
- @Override
- public void setDecompileData(DecompileData decompileData) {
- super.setDecompileData(decompileData);
- if (listener != null) {
- listener.setDecompileData(decompileData);
- }
- }
-
- @Override
- public void dispose() {
- listener = null;
- super.dispose();
- }
- }
-
- public DecompilerPanel getDecompilerPanel() {
- return controller.getDecompilerPanel();
- }
-
- public void showFunction(Program program, Address address) {
- controller.display(program, new ProgramLocation(program, address),
- new ViewerPosition(0, 0, 0));
- }
-
- public void showFunction(Function function) {
- if (function == null) {
- clearAndShowMessage("No Function");
- return;
- }
- if (function.isExternal()) {
- clearAndShowMessage("\"" + function.getName(true) + "\" is an external function.");
- return;
- }
- Program program = function.getProgram();
- Address entry = function.getEntryPoint();
- ProgramLocation location = new ProgramLocation(program, entry);
- controller.display(program, location, new ViewerPosition(0, 0, 0));
- }
-
- @Override
- public void contextChanged() {
- // stub
- }
-
- @Override
- public void decompileDataChanged(DecompileData decompileData) {
- // stub
- }
-
- @Override
- public void exportLocation() {
- // stub
- }
-
- @Override
- public void annotationClicked(AnnotatedTextFieldElement annotation, boolean newWindow) {
- // stub
- }
-
- @Override
- public void goToAddress(Address addr, boolean newWindow) {
- // stub
- }
-
- @Override
- public void goToLabel(String labelName, boolean newWindow) {
- // stub
- }
-
- @Override
- public void goToScalar(long value, boolean newWindow) {
- // stub
- }
-
- @Override
- public void goToFunction(Function function, boolean newWindow) {
- // stub
- }
-
- @Override
- public void locationChanged(ProgramLocation programLocation) {
- if (locationListener == null) {
- return;
- }
- this.locationListener.programLocationChanged(programLocation, null);
- }
-
- @Override
- public void selectionChanged(ProgramSelection programSelection) {
- // stub
- }
-
- @Override
- public void setStatusMessage(String message) {
- // stub
- }
-
- @Override
- public void doWhenNotBusy(Callback c) {
- // stub
- }
-
- public void clearAndShowMessage(String message) {
- controller.setDecompileData(new EmptyDecompileData(message));
- paintImmediately(getBounds());
- }
-
- public void setMouseNavigationEnabled(boolean enabled) {
- controller.setMouseNavigationEnabled(enabled);
- }
-
- public void dispose() {
- controller.dispose();
- }
-
- public DecompilerController getController() {
- return controller;
-
- }
-
- public void refresh(DecompileData data) {
- controller.refreshDisplay(data.getProgram(), data.getLocation(), null);
- }
-}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompileData.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompileData.java
index e4d243958a..5dadc16673 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompileData.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompileData.java
@@ -15,6 +15,9 @@
*/
package ghidra.app.decompiler.component;
+import java.io.File;
+
+import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.app.decompiler.ClangTokenGroup;
import ghidra.app.decompiler.DecompileResults;
import ghidra.program.model.address.Address;
@@ -24,10 +27,6 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.util.ProgramLocation;
-import java.io.File;
-
-import docking.widgets.fieldpanel.support.ViewerPosition;
-
public class DecompileData {
private final Program program;
@@ -41,22 +40,26 @@ public class DecompileData {
public DecompileData(Program program, Function function, ProgramLocation location,
DecompileResults decompileResults, String errorMessage, File debugFile,
ViewerPosition viewerPosition) {
- this.program = program;
- this.function = function;
- this.location = location;
- this.decompileResults = decompileResults;
- this.message = errorMessage;
- this.debugFile = debugFile;
- this.viewerPosition = viewerPosition;
+ this.program = program;
+ this.function = function;
+ this.location = location;
+ this.decompileResults = decompileResults;
+ this.message = errorMessage;
+ this.debugFile = debugFile;
+ this.viewerPosition = viewerPosition;
}
-
+
public boolean hasDecompileResults() {
if (decompileResults == null) {
return false;
}
return decompileResults.getCCodeMarkup() != null;
}
-
+
+ public boolean isValid() {
+ return decompileResults != null && decompileResults.isValid();
+ }
+
public DecompileResults getDecompileResults() {
return decompileResults;
}
@@ -75,7 +78,7 @@ public class DecompileData {
}
return null;
}
-
+
public ProgramLocation getLocation() {
return location;
}
@@ -102,7 +105,7 @@ public class DecompileData {
}
return "Unknown Error";
}
-
+
public File getDebugFile() {
return debugFile;
}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCallbackHandlerAdapter.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCallbackHandlerAdapter.java
new file mode 100644
index 0000000000..f48ca487da
--- /dev/null
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCallbackHandlerAdapter.java
@@ -0,0 +1,87 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.decompiler.component;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.listing.Function;
+import ghidra.program.util.ProgramLocation;
+import ghidra.program.util.ProgramSelection;
+import ghidra.util.bean.field.AnnotatedTextFieldElement;
+import utility.function.Callback;
+
+public class DecompilerCallbackHandlerAdapter implements DecompilerCallbackHandler {
+
+ @Override
+ public void decompileDataChanged(DecompileData decompileData) {
+ // stub
+ }
+
+ @Override
+ public void contextChanged() {
+ // stub
+ }
+
+ @Override
+ public void setStatusMessage(String message) {
+ // stub
+ }
+
+ @Override
+ public void locationChanged(ProgramLocation programLocation) {
+ // stub
+ }
+
+ @Override
+ public void selectionChanged(ProgramSelection programSelection) {
+ // stub
+ }
+
+ @Override
+ public void annotationClicked(AnnotatedTextFieldElement annotation, boolean newWindow) {
+ // stub
+ }
+
+ @Override
+ public void goToLabel(String labelName, boolean newWindow) {
+ // stub
+ }
+
+ @Override
+ public void goToAddress(Address addr, boolean newWindow) {
+ // stub
+ }
+
+ @Override
+ public void goToScalar(long value, boolean newWindow) {
+ // stub
+ }
+
+ @Override
+ public void exportLocation() {
+ // stub
+ }
+
+ @Override
+ public void goToFunction(Function function, boolean newWindow) {
+ // stub
+ }
+
+ @Override
+ public void doWhenNotBusy(Callback c) {
+ // stub
+ }
+
+}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java
deleted file mode 100644
index 245bd31d31..0000000000
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java
+++ /dev/null
@@ -1,821 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.decompiler.component;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-
-import javax.swing.*;
-
-import docking.ActionContext;
-import docking.ComponentProvider;
-import docking.action.*;
-import docking.options.OptionsService;
-import docking.widgets.fieldpanel.FieldPanel;
-import docking.widgets.fieldpanel.support.FieldLocation;
-import docking.widgets.label.GDHtmlLabel;
-import ghidra.GhidraOptions;
-import ghidra.app.decompiler.DecompileOptions;
-import ghidra.app.util.viewer.listingpanel.ProgramLocationListener;
-import ghidra.app.util.viewer.util.CodeComparisonPanel;
-import ghidra.app.util.viewer.util.TitledPanel;
-import ghidra.framework.options.ToolOptions;
-import ghidra.framework.plugintool.PluginTool;
-import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.listing.*;
-import ghidra.program.util.FunctionUtility;
-import ghidra.program.util.ProgramLocation;
-import ghidra.util.HTMLUtilities;
-import ghidra.util.HelpLocation;
-
-/**
- * Panel that displays two decompilers for comparison
- */
-public abstract class DecompilerCodeComparisonPanel
- extends CodeComparisonPanel {
-
- private static final String NO_FUNCTION_TITLE = "No Function";
- final static String OPTIONS_TITLE = "Decompiler";
-
- private JSplitPane splitPane;
- private CDisplayPanel[] cPanels = new CDisplayPanel[2];
- private DualDecompilerFieldPanelCoordinator dualDecompilerCoordinator;
- private DecompileData leftDecompileData;
- private DecompileData rightDecompileData;
- private boolean isMatchingConstantsExactly = true;
- private DecompileOptions leftDecompileOptions;
- private DecompileOptions rightDecompileOptions;
-
- private ClangHighlightController[] highlightControllers = new ClangHighlightController[2];
- private ArrayList dualDecompileResultsListenerList =
- new ArrayList<>();
- private String leftTitle = NO_FUNCTION_TITLE;
- private String rightTitle = NO_FUNCTION_TITLE;
- private ProgramLocationListener leftDecompilerLocationListener;
- private ProgramLocationListener rightDecompilerLocationListener;
- private DecompilerDiffViewFindAction diffViewFindAction;
- private boolean isSideBySide = true;
- private ToggleOrientationAction toggleOrientationAction;
- private DecompilerCodeComparisonOptionsAction decompOptionsAction;
-
- private DecompilerProgramListener leftProgramListener;
- private DecompilerProgramListener rightProgramListener;
-
- /**
- * Creates a comparison panel with two decompilers
- *
- * @param owner the owner of this panel
- * @param tool the tool displaying this panel
- */
- public DecompilerCodeComparisonPanel(String owner, PluginTool tool) {
- super(owner, tool);
- functions = new Function[2];
-
- buildPanel();
- loadFunctions(null, null);
-
- highlightControllers[LEFT] = new LocationClangHighlightController();
- highlightControllers[RIGHT] = new LocationClangHighlightController();
- setHighlightControllers(highlightControllers[LEFT], highlightControllers[RIGHT]);
-
- initialize();
- }
-
- private void initialize() {
- ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
- ToolOptions options = tool.getOptions(OPTIONS_TITLE);
- leftDecompileOptions.grabFromToolAndProgram(fieldOptions, options,
- (functions[LEFT] != null) ? functions[LEFT].getProgram() : null);
- rightDecompileOptions.grabFromToolAndProgram(fieldOptions, options,
- (functions[RIGHT] != null) ? functions[RIGHT].getProgram() : null);
- setFieldPanelCoordinator(createFieldPanelCoordinator());
- setScrollingSyncState(true);
- createActions();
- }
-
- @Override
- public JComponent getComponent() {
- return this;
- }
-
- @Override
- public String getTitle() {
- return "Decompile View";
- }
-
- @Override
- public void setVisible(boolean aFlag) {
- super.setVisible(aFlag);
- // If actions are added in the future, you may need to update their enablement here.
- // The applyFunctionSignatureAction enablement is already handled via context.
- }
-
- private void setTitles(String leftTitle, String rightTitle) {
- setLeftTitle(leftTitle);
- setRightTitle(rightTitle);
- }
-
- private void setTitle(TitledPanel titlePanel, String titlePrefix, String title) {
- if (!titlePrefix.isEmpty()) {
- titlePrefix += " "; // Add a space between prefix and title.
- }
- String htmlPrefix = "";
- if (title.startsWith(htmlPrefix)) {
- titlePanel.setTitleName(htmlPrefix + HTMLUtilities.friendlyEncodeHTML(titlePrefix) +
- title.substring(htmlPrefix.length()));
- }
- else {
- titlePanel.setTitleName(titlePrefix + title);
- }
- }
-
- /**
- * Sets the title for the left side's decompiler.
- * @param leftTitle the title
- */
- public void setLeftTitle(String leftTitle) {
- this.leftTitle = leftTitle;
- setTitle(titlePanels[LEFT], leftTitlePrefix, leftTitle);
- }
-
- /**
- * Sets the title for the right side's decompiler.
- * @param rightTitle the title
- */
- public void setRightTitle(String rightTitle) {
- this.rightTitle = rightTitle;
- setTitle(titlePanels[RIGHT], rightTitlePrefix, rightTitle);
- }
-
- private void setTitles(Function leftFunction, Function rightFunction) {
- setTitles(getTitleForFunction(leftFunction), getTitleForFunction(rightFunction));
- }
-
- private String getTitleForFunction(Function function) {
- String title = NO_FUNCTION_TITLE;
- if (function != null) {
- String programName = function.getProgram().getDomainFile().getPathname();
- title = function.getName(true) + " [" + programName + "]";
- }
- return title;
- }
-
- public boolean isMatchingConstantsExactly() {
- return isMatchingConstantsExactly;
- }
-
- @Override
- public void loadFunctions(Function leftFunction, Function rightFunction) {
- if (leftFunction == functions[LEFT] && rightFunction == functions[RIGHT]) {
- return;
- }
-
- // Clear the scroll info and highlight info to prevent unnecessary highlighting, etc.
- if (leftFunction != functions[LEFT]) {
- leftDecompileData = null;
- }
- if (rightFunction != functions[RIGHT]) {
- rightDecompileData = null;
- }
- notifyDecompileResultsListeners();
-
- Program leftProgram = (leftFunction != null) ? leftFunction.getProgram() : null;
- Program rightProgram = (rightFunction != null) ? rightFunction.getProgram() : null;
- setPrograms(leftProgram, rightProgram);
-
- loadLeftFunction(leftFunction);
- loadRightFunction(rightFunction);
-
- if (getShowTitles()) {
- setTitles(leftFunction, rightFunction);
- }
- else {
- setTitles("", "");
- }
- if (dualDecompilerCoordinator != null) {
- dualDecompilerCoordinator.leftLocationChanged((ProgramLocation) null);
- }
- }
-
- private void notifyDecompileResultsListeners() {
- // Notify any decompile results listener we have new left or right decompile results.
- for (DualDecompileResultsListener listener : dualDecompileResultsListenerList) {
- listener.decompileResultsSet(leftDecompileData, rightDecompileData);
- }
- }
-
- private void loadLeftFunction(Function function) {
- if (function == functions[LEFT]) {
- return;
- }
- functions[LEFT] = function;
- cPanels[LEFT].showFunction(function);
- }
-
- private void loadRightFunction(Function function) {
- if (function == functions[RIGHT]) {
- return;
- }
- functions[RIGHT] = function;
- cPanels[RIGHT].showFunction(function);
- }
-
- private void buildPanel() {
- setLayout(new BorderLayout());
- leftDecompileOptions = new DecompileOptions();
- rightDecompileOptions = new DecompileOptions();
-// Options options = tool.getOptions(OPTIONS_TITLE);
-// leftDecompileOptions.grabFromToolAndProgram(null, options,
-// (functions[LEFT] != null) ? functions[LEFT].getProgram() : null);
-// rightDecompileOptions.grabFromToolAndProgram(null, options,
-// (functions[LEFT] != null) ? functions[RIGHT].getProgram() : null);
-
- cPanels[LEFT] = new CDisplayPanel(leftDecompileOptions,
- decompileData -> leftDecompileDataSet(decompileData));
- cPanels[RIGHT] = new CDisplayPanel(rightDecompileOptions,
- decompileData -> rightDecompileDataSet(decompileData));
-
- DecompilerController leftController = cPanels[LEFT].getController();
- leftProgramListener = new DecompilerProgramListener(leftController, () -> refresh(LEFT));
- DecompilerController rightController = cPanels[RIGHT].getController();
- rightProgramListener = new DecompilerProgramListener(rightController, () -> refresh(RIGHT));
-
- leftDecompilerLocationListener = (leftLocation, trigger) -> {
- if (dualDecompilerCoordinator != null) {
- dualDecompilerCoordinator.leftLocationChanged(leftLocation);
- }
- };
- rightDecompilerLocationListener = (rightLocation, trigger) -> {
- if (dualDecompilerCoordinator != null) {
- dualDecompilerCoordinator.rightLocationChanged(rightLocation);
- }
- };
- cPanels[LEFT].setProgramLocationListener(leftDecompilerLocationListener);
- cPanels[RIGHT].setProgramLocationListener(rightDecompilerLocationListener);
-
- // Initialize focus listeners on decompiler panels.
- for (int i = 0; i < cPanels.length; i++) {
- FieldPanel fieldPanel = cPanels[i].getDecompilerPanel().getFieldPanel();
- fieldPanel.addFocusListener(this);
- fieldPanel.addMouseListener(new DualDecompilerMouseListener(i));
- }
- setDualPanelFocus(currProgramIndex);
-
- String leftTitle1 = FunctionUtility.getFunctionTitle(functions[LEFT]);
- String rightTitle1 = FunctionUtility.getFunctionTitle(functions[RIGHT]);
-
- // use mutable labels, as the titles update when functions are selected
- GDHtmlLabel leftTitleLabel = new GDHtmlLabel(leftTitle1);
- GDHtmlLabel rightTitleLabel = new GDHtmlLabel(rightTitle1);
-
- titlePanels[LEFT] = new TitledPanel(leftTitleLabel, cPanels[LEFT], 5);
- titlePanels[RIGHT] = new TitledPanel(rightTitleLabel, cPanels[RIGHT], 5);
-
- // Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's
- // divider from becoming locked (can't be moved) due to extra long title names.
- titlePanels[LEFT].setMinimumSize(
- new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[LEFT].getMinimumSize().height));
- titlePanels[RIGHT].setMinimumSize(
- new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[RIGHT].getMinimumSize().height));
-
- splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, titlePanels[LEFT],
- titlePanels[RIGHT]);
- splitPane.setResizeWeight(0.5);
- splitPane.setDividerSize(4);
- splitPane.setBorder(BorderFactory.createEmptyBorder());
- add(splitPane, BorderLayout.CENTER);
- }
-
- private void refresh(int side) {
- DecompileData decompileData = side == LEFT ? leftDecompileData : rightDecompileData;
- if (decompileData == null) {
- return;
- }
- cPanels[side].refresh(decompileData);
- }
-
- /**
- * Adds the indicated listener to be notified when the decompile results have completed.
- * @param listener the listener
- * @return true if the listener was added.
- */
- public boolean addDualDecompileResultsListener(DualDecompileResultsListener listener) {
- return dualDecompileResultsListenerList.add(listener);
- }
-
- /**
- * Removes the indicated listener from being notified about decompile results.
- * @param listener the listener
- * @return true if the listener was removed.
- */
- public boolean removeDualDecompileResultsListener(DualDecompileResultsListener listener) {
- return dualDecompileResultsListenerList.remove(listener);
- }
-
- /**
- * Sets the highlight controllers for both decompiler panels.
- * @param leftHighlightController the left side's highlight controller
- * @param rightHighlightController the right side's highlight controller
- */
- public void setHighlightControllers(ClangHighlightController leftHighlightController,
- ClangHighlightController rightHighlightController) {
-
- highlightControllers[LEFT] = leftHighlightController;
- highlightControllers[RIGHT] = rightHighlightController;
- cPanels[LEFT].getDecompilerPanel().setHighlightController(leftHighlightController);
- cPanels[RIGHT].getDecompilerPanel().setHighlightController(rightHighlightController);
- }
-
- /**
- * Sets the coordinator for the two decompiler panels within this code comparison panel.
- * It coordinates their scrolling and location synchronization.
- * @param fieldPanelCoordinator the coordinator for the two decompiler panels
- */
- @Override
- public void setFieldPanelCoordinator(
- DualDecompilerFieldPanelCoordinator fieldPanelCoordinator) {
-
- if (this.dualDecompilerCoordinator == fieldPanelCoordinator) {
- return;
- }
- if (this.dualDecompilerCoordinator != null) {
- this.dualDecompilerCoordinator.dispose();
- cPanels[LEFT].setProgramLocationListener(null);
- cPanels[RIGHT].setProgramLocationListener(null);
- }
-
- this.dualDecompilerCoordinator = fieldPanelCoordinator;
-
- if (fieldPanelCoordinator != null) {
- cPanels[LEFT].setProgramLocationListener(leftDecompilerLocationListener);
- cPanels[RIGHT].setProgramLocationListener(rightDecompilerLocationListener);
- CDisplayPanel focusedDecompilerPanel = getFocusedDecompilerPanel();
- ProgramLocation programLocation =
- focusedDecompilerPanel.getDecompilerPanel().getCurrentLocation();
- if (programLocation != null) {
- focusedDecompilerPanel.locationChanged(programLocation);
- }
- }
- }
-
- protected void rightDecompileDataSet(DecompileData decompileData) {
- rightDecompileData = decompileData;
- notifyDecompileResultsListeners();
- }
-
- protected void leftDecompileDataSet(DecompileData decompileData) {
- leftDecompileData = decompileData;
- notifyDecompileResultsListeners();
- }
-
- /**
- * Sets the component displayed in the top of this panel.
- * @param comp the component.
- */
- public void setTopComponent(JComponent comp) {
- if (topComp == comp) {
- return;
- }
- if (topComp != null) {
- remove(topComp);
- }
- topComp = comp;
- if (topComp != null) {
- add(topComp, BorderLayout.NORTH);
- }
- validate();
- }
-
- /**
- * Sets the component displayed in the bottom of this panel.
- * @param comp the component.
- */
- public void setBottomComponent(JComponent comp) {
- if (bottomComp == comp) {
- return;
- }
- if (bottomComp != null) {
- remove(bottomComp);
- }
- validate(); // Since we are removing this while the panel is on the screen.
- bottomComp = comp;
- if (bottomComp != null) {
- add(bottomComp, BorderLayout.SOUTH);
- }
- validate(); // Since we are adding this while the panel is on the screen.
- }
-
- /**
- * Gets the display panel from the left or right side that has or last had focus.
- * @return the last C display panel with focus
- */
- public CDisplayPanel getFocusedDecompilerPanel() {
- return cPanels[currProgramIndex];
- }
-
- /**
- * Gets the left side's C display panel.
- * @return the left C display panel
- */
- public CDisplayPanel getLeftPanel() {
- return cPanels[LEFT];
- }
-
- /**
- * Gets the right side's C display panel.
- * @return the right C display panel
- */
- public CDisplayPanel getRightPanel() {
- return cPanels[RIGHT];
- }
-
- @Override
- public void dispose() {
- removeProgramListeners();
- setFieldPanelCoordinator(null);
- cPanels[LEFT].dispose();
- cPanels[RIGHT].dispose();
- leftProgramListener.dispose();
- rightProgramListener.dispose();
- }
-
- @Override
- public void focusGained(FocusEvent e) {
- Component comp = e.getComponent();
- for (int i = 0; i < cPanels.length; i++) {
- if (cPanels[i].getDecompilerPanel().getFieldPanel() == comp) {
- setDualPanelFocus(i);
- }
- }
-
- // Kick the tool so action buttons will be updated
- ComponentProvider provider = tool.getWindowManager().getProvider(comp);
- if (provider != null) {
- provider.contextChanged();
- }
- }
-
- private void setDualPanelFocus(int leftOrRight) {
- currProgramIndex = leftOrRight;
- cPanels[leftOrRight].setBorder(FOCUS_BORDER);
- cPanels[((leftOrRight == LEFT) ? RIGHT : LEFT)].setBorder(NON_FOCUS_BORDER);
- }
-
- @SuppressWarnings("unused")
- private void clearBothDisplaysAndShowMessage(String message) {
- cPanels[LEFT].clearAndShowMessage(message);
- cPanels[RIGHT].clearAndShowMessage(message);
- }
-
- /**
- * Disable mouse navigation from within this dual decompiler panel.
- * @param enabled false disables navigation
- */
- @Override
- public void setMouseNavigationEnabled(boolean enabled) {
- cPanels[LEFT].setMouseNavigationEnabled(enabled);
- cPanels[RIGHT].setMouseNavigationEnabled(enabled);
- }
-
- @Override
- protected void setPrograms(Program leftProgram, Program rightProgram) {
- removeProgramListeners();
-
- ToolOptions fieldOptions =
- (tool != null) ? tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS) : null;
- ToolOptions options = (tool != null) ? tool.getOptions(OPTIONS_TITLE) : null;
- if (leftProgram != programs[LEFT]) {
- programs[LEFT] = leftProgram;
- if (options != null) {
- leftDecompileOptions.grabFromToolAndProgram(fieldOptions, options, leftProgram);
- }
- }
- if (rightProgram != programs[RIGHT]) {
- programs[RIGHT] = rightProgram;
- if (options != null) {
- rightDecompileOptions.grabFromToolAndProgram(fieldOptions, options, rightProgram);
- }
- }
- addProgramListeners();
- }
-
- private void addProgramListeners() {
- if (programs[LEFT] != null) {
- programs[LEFT].addListener(leftProgramListener);
- }
- if (programs[RIGHT] != null) {
- programs[RIGHT].addListener(rightProgramListener);
- }
- }
-
- private void removeProgramListeners() {
- if (programs[LEFT] != null) {
- programs[LEFT].removeListener(leftProgramListener);
- }
- if (programs[RIGHT] != null) {
- programs[RIGHT].removeListener(rightProgramListener);
- }
- }
-
- @Override
- public void loadData(Data leftData, Data rightData) {
- loadFunctions(null, null);
- }
-
- @Override
- public void loadAddresses(Program leftProgram, Program rightProgram,
- AddressSetView leftAddresses, AddressSetView rightAddresses) {
- loadFunctions(null, null);
- }
-
- /**
- * Gets the left side's decompiler panel.
- * @return the left decompiler panel
- */
- public DecompilerPanel getLeftDecompilerPanel() {
- return cPanels[LEFT].getDecompilerPanel();
- }
-
- /**
- * Gets the right side's decompiler panel.
- * @return the right decompiler panel
- */
- public DecompilerPanel getRightDecompilerPanel() {
- return cPanels[RIGHT].getDecompilerPanel();
- }
-
- @Override
- public void updateActionEnablement() {
- // Nothing to do.
-
- // applyFunctionSignature enablement is handled by context.
- }
-
- /**
- * Creates the actions provided by this panel.
- */
- protected void createActions() {
- diffViewFindAction = new DecompilerDiffViewFindAction(owner, tool);
- toggleOrientationAction = new ToggleOrientationAction();
- decompOptionsAction = new DecompilerCodeComparisonOptionsAction();
- }
-
- @Override
- public DockingAction[] getActions() {
- DockingAction[] codeCompActions = super.getActions();
- DockingAction[] otherActions = new DockingAction[] { diffViewFindAction,
- toggleOrientationAction, decompOptionsAction };
- int compCount = codeCompActions.length;
- int otherCount = otherActions.length;
- DockingAction[] actions = new DockingAction[compCount + otherCount];
- System.arraycopy(codeCompActions, 0, actions, 0, compCount);
- System.arraycopy(otherActions, 0, actions, compCount, otherCount);
- return actions;
- }
-
- /**
- * Sets whether or not the decompilers are displayed side by side.
- *
- * @param sideBySide if true, the decompilers are side by side, otherwise one is above
- * the other.
- */
- private void showSideBySide(boolean sideBySide) {
- isSideBySide = sideBySide;
- splitPane.setOrientation(
- isSideBySide ? JSplitPane.HORIZONTAL_SPLIT : JSplitPane.VERTICAL_SPLIT);
- splitPane.setDividerLocation(0.5);
- toggleOrientationAction.setSelected(sideBySide);
- }
-
- private boolean isSideBySide() {
- return isSideBySide;
- }
-
- @Override
- public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
-
- Component component = event == null ? null : event.getComponent();
- CDisplayPanel focusedDecompilerPanel = getFocusedDecompilerPanel();
- DualDecompilerActionContext dualDecompContext =
- new DualDecompilerActionContext(provider, focusedDecompilerPanel, component);
- dualDecompContext.setCodeComparisonPanel(this);
- return dualDecompContext;
- }
-
- @Override
- public void programRestored(Program program) {
- Function leftFunction = getLeftFunction();
- Function rightFunction = getRightFunction();
- Program leftProgram = (leftFunction != null) ? leftFunction.getProgram() : null;
- Program rightProgram = (rightFunction != null) ? rightFunction.getProgram() : null;
- if (leftProgram == program) {
- titlePanels[LEFT].setTitleName(FunctionUtility.getFunctionTitle(leftFunction));
- refreshLeftPanel();
- }
- if (rightProgram == program) {
- titlePanels[RIGHT].setTitleName(FunctionUtility.getFunctionTitle(rightFunction));
- refreshRightPanel();
- }
- }
-
- @SuppressWarnings("unchecked")
- private void refreshPanel(int leftOrRight) {
- // Hold onto functions for reloading them after the indicated side is cleared,
- // because that will have cleared it in the functions array.
- Function leftFunction = functions[LEFT];
- Function rightFunction = functions[RIGHT];
-
- // Save the location so it can be restored after getting new decompiler results.
- FieldLocation leftCursorLocation =
- getLeftDecompilerPanel().getFieldPanel().getCursorLocation();
- FieldLocation rightCursorLocation =
- getRightDecompilerPanel().getFieldPanel().getCursorLocation();
-
- MyDecompileResultsListener listener =
- new MyDecompileResultsListener(leftCursorLocation, rightCursorLocation);
-
- //TEMP FIX - correct when refactoring
- // Clear any previous MyDecompileResultsListener that is for a decompiler load
- //that hasn't finished.
- Set toRemove = new HashSet<>();
- for (DualDecompileResultsListener l : dualDecompileResultsListenerList) {
- if (MyDecompileResultsListener.class.isInstance(l)) {
- toRemove.add((DecompilerCodeComparisonPanel.MyDecompileResultsListener) l);
- }
- }
- dualDecompileResultsListenerList.removeAll(toRemove);
-
- // Clear the left or right function by passing null to the load method
- // and then reload it below to get it to update.
- loadFunctions(((leftOrRight == LEFT) ? null : leftFunction),
- ((leftOrRight == RIGHT) ? null : rightFunction));
-
- // Setup to restore location to left or right decompiler panel.
- addDualDecompileResultsListener(listener);
-
- // Reload the left or right function to get it to update.
- loadFunctions(leftFunction, rightFunction);
- }
-
- /**
- * Refreshes the left side of this panel.
- */
- @Override
- public void refreshLeftPanel() {
- refreshPanel(LEFT);
- }
-
- /**
- * Refreshes the right side of this panel.
- */
- @Override
- public void refreshRightPanel() {
- refreshPanel(RIGHT);
- }
-
- @Override
- public boolean leftPanelHasFocus() {
- return currProgramIndex == LEFT;
- }
-
- @Override
- public void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix) {
- this.leftTitlePrefix = leftTitlePrefix;
- this.rightTitlePrefix = rightTitlePrefix;
- setTitles(leftTitle, rightTitle);
- }
-
- @Override
- public AddressSetView getLeftAddresses() {
- return (functions[LEFT] != null) ? functions[LEFT].getBody() : EMPTY_ADDRESS_SET;
- }
-
- @Override
- public AddressSetView getRightAddresses() {
- return (functions[RIGHT] != null) ? functions[RIGHT].getBody() : EMPTY_ADDRESS_SET;
- }
-
- @Override
- public FieldPanel getLeftFieldPanel() {
- return getLeftDecompilerPanel().getFieldPanel();
- }
-
- @Override
- public FieldPanel getRightFieldPanel() {
- return getRightDecompilerPanel().getFieldPanel();
- }
-
- @Override
- protected abstract DualDecompilerFieldPanelCoordinator createFieldPanelCoordinator();
-
- private class MyDecompileResultsListener implements DualDecompileResultsListener {
-
- private FieldLocation leftCursorLocation;
- private FieldLocation rightCursorLocation;
-
- private MyDecompileResultsListener(FieldLocation leftCursorLocation,
- FieldLocation rightCursorLocation) {
- this.leftCursorLocation = leftCursorLocation;
- this.rightCursorLocation = rightCursorLocation;
- }
-
- @Override
- public void decompileResultsSet(final DecompileData myLeftDecompileData,
- final DecompileData myRightDecompileData) {
- SwingUtilities.invokeLater(() -> {
- if (myLeftDecompileData != null) {
- // The left side may have reloaded with decompiler results,
- // so restore the cursor location.
- restoreCursor(getLeftDecompilerPanel(), leftCursorLocation);
- }
- if (myRightDecompileData != null) {
- // The right side may have reloaded with decompiler results,
- // so restore the cursor location.
- restoreCursor(getRightDecompilerPanel(), rightCursorLocation);
- }
-
- // The listener did its job so now remove it.
- removeDualDecompileResultsListener(MyDecompileResultsListener.this);
- });
- }
-
- private void restoreCursor(DecompilerPanel decompilerPanel, FieldLocation cursorLocation) {
- FieldPanel fieldPanel = decompilerPanel.getFieldPanel();
- FieldLocation currentLocation = fieldPanel.getCursorLocation();
- if (cursorLocation != null && !cursorLocation.equals(currentLocation)) {
- fieldPanel.setCursorPosition(cursorLocation.getIndex(),
- cursorLocation.getFieldNum(), cursorLocation.getRow(), cursorLocation.getCol());
- }
- }
- }
-
- private class DualDecompilerMouseListener extends MouseAdapter {
-
- private int leftOrRight;
-
- DualDecompilerMouseListener(int leftOrRight) {
- this.leftOrRight = leftOrRight;
- }
-
- @Override
- public void mousePressed(MouseEvent e) {
- setDualPanelFocus(leftOrRight);
- }
- }
-
- private class ToggleOrientationAction extends ToggleDockingAction {
-
- ToggleOrientationAction() {
- super("Dual Decompiler Toggle Orientation", "FunctionComparison");
- setDescription("Toggle the layout of the decompiler " +
- "
between side-by-side and one above the other.");
- setEnabled(true);
- setSelected(isSideBySide);
- MenuData menuData =
- new MenuData(new String[] { "Show Decompilers Side-by-Side" }, "Dual Decompiler");
- setMenuBarData(menuData);
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- boolean sideBySide = !isSideBySide();
- showSideBySide(sideBySide);
- }
- }
-
- private class DecompilerCodeComparisonOptionsAction extends DockingAction {
-
- DecompilerCodeComparisonOptionsAction() {
- super("Decompiler Code Comparison Options", owner);
- setDescription("Show the tool options for the Decompiler Code Comparison.");
- setPopupMenuData(new MenuData(new String[] { "Properties" }, null, "Z_Properties"));
- setHelpLocation(
- new HelpLocation("FunctionComparison", "Decompiler_Code_Comparison_Options"));
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return (context instanceof DualDecompilerActionContext);
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- OptionsService service = tool.getService(OptionsService.class);
- service.showOptionsDialog("FunctionComparison", "Decompiler Code Comparison");
- }
- }
-}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java
index 2ccb99e95a..dc1e215efa 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java
@@ -1214,6 +1214,18 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
buildPanels();
}
+ @Override
+ public synchronized void addFocusListener(FocusListener l) {
+ // we are not focusable, defer to contained field panel
+ fieldPanel.addFocusListener(l);
+ }
+
+ @Override
+ public synchronized void removeFocusListener(FocusListener l) {
+ // we are not focusable, defer to contained field panel
+ fieldPanel.removeFocusListener(l);
+ }
+
private void buildPanels() {
removeAll();
add(buildLeftComponent(), BorderLayout.WEST);
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompileResultsListener.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompileResultsListener.java
deleted file mode 100644
index b5b35defa3..0000000000
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompileResultsListener.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/* ###
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.decompiler.component;
-
-
-public interface DualDecompileResultsListener {
-
-// void setProvider(DecompilerFunctionComparisonProvider provider);
-
- void decompileResultsSet(DecompileData leftDecompileData, DecompileData rightDecompileData);
-
-}
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompilerActionContext.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompilerActionContext.java
deleted file mode 100644
index 5a4ae7abe5..0000000000
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DualDecompilerActionContext.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.app.decompiler.component;
-
-import java.awt.Component;
-
-import docking.ComponentProvider;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
-import ghidra.app.context.RestrictedAddressSetContext;
-import ghidra.app.util.viewer.util.CodeComparisonActionContext;
-import ghidra.app.util.viewer.util.CodeComparisonPanel;
-import ghidra.program.model.listing.Function;
-
-/**
- * Action context for a dual decompiler panel.
- */
-public class DualDecompilerActionContext extends CodeComparisonActionContext
- implements RestrictedAddressSetContext {
-
- private CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel = null;
-
- /**
- * Creates an action context for a dual decompiler panel.
- * @param provider the provider for this context
- * @param cPanel the decompiler panel associated with this context
- * @param source the source of the action
- */
- public DualDecompilerActionContext(ComponentProvider provider, CDisplayPanel cPanel,
- Component source) {
- super(provider, cPanel, source);
- }
-
- /**
- * Sets the CodeComparisonPanel associated with this context.
- * @param codeComparisonPanel the code comparison panel.
- */
- public void setCodeComparisonPanel(
- CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel) {
- this.codeComparisonPanel = codeComparisonPanel;
- }
-
- @Override
- public CodeComparisonPanel extends FieldPanelCoordinator> getCodeComparisonPanel() {
- return codeComparisonPanel;
- }
-
- @Override
- public Function getSourceFunction() {
- boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
-
- return leftHasFocus ? codeComparisonPanel.getRightFunction()
- : codeComparisonPanel.getLeftFunction();
- }
-
- @Override
- public Function getTargetFunction() {
- boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
-
- return leftHasFocus ? codeComparisonPanel.getLeftFunction()
- : codeComparisonPanel.getRightFunction();
- }
-}
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/correlator/address/VTHashedFunctionAddressCorrelation.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/correlator/address/VTHashedFunctionAddressCorrelation.java
index 85d5eaab0d..6902dec99d 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/correlator/address/VTHashedFunctionAddressCorrelation.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/correlator/address/VTHashedFunctionAddressCorrelation.java
@@ -15,11 +15,13 @@
*/
package ghidra.feature.vt.api.correlator.address;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import ghidra.program.model.address.*;
import ghidra.program.model.correlate.HashedFunctionAddressCorrelation;
import ghidra.program.model.listing.Function;
import ghidra.program.model.mem.MemoryAccessException;
-import ghidra.program.util.AddressCorrelation;
+import ghidra.program.util.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -35,14 +37,15 @@ public class VTHashedFunctionAddressCorrelation implements AddressCorrelation {
private final Function sourceFunction;
private final Function destinationFunction;
- private HashedFunctionAddressCorrelation addressCorrelation;
+ private ListingAddressCorrelation addressCorrelation;
/**
* Constructs an address correlation between two functions.
* @param sourceFunction the source function
* @param destinationFunction the destination function
*/
- public VTHashedFunctionAddressCorrelation(Function sourceFunction, Function destinationFunction) {
+ public VTHashedFunctionAddressCorrelation(Function sourceFunction,
+ Function destinationFunction) {
this.sourceFunction = sourceFunction;
this.destinationFunction = destinationFunction;
addressCorrelation = null;
@@ -58,7 +61,7 @@ public class VTHashedFunctionAddressCorrelation implements AddressCorrelation {
throws CancelledException {
try {
initializeCorrelation(monitor);
- Address destinationAddress = addressCorrelation.getAddressInSecond(sourceAddress);
+ Address destinationAddress = addressCorrelation.getAddress(RIGHT, sourceAddress);
if (destinationAddress == null) {
return null; // No matching destination.
}
@@ -83,8 +86,13 @@ public class VTHashedFunctionAddressCorrelation implements AddressCorrelation {
if (addressCorrelation != null) {
return;
}
- addressCorrelation =
+ if (sourceFunction != null && destinationFunction != null) {
+ addressCorrelation =
new HashedFunctionAddressCorrelation(sourceFunction, destinationFunction,
- monitor);
+ monitor);
+ }
+ else {
+ addressCorrelation = new DummyListingAddressCorrelation();
+ }
}
}
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTDualListingDragNDropHandler.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTDualListingDragNDropHandler.java
index c4b2d0b838..be29631133 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTDualListingDragNDropHandler.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTDualListingDragNDropHandler.java
@@ -15,6 +15,8 @@
*/
package ghidra.feature.vt.gui.duallisting;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
@@ -32,12 +34,11 @@ import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
+import ghidra.util.datastruct.Duo;
public class VTDualListingDragNDropHandler implements Draggable, Droppable {
- private final int SOURCE = 0; // left side
- private final int DESTINATION = 1; // right side
- private ListingPanel[] listingPanels = new ListingPanel[2];
+ private Duo listingPanels;
private VTController controller;
ListingCodeComparisonPanel dualListingPanel;
@@ -55,8 +56,9 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
ListingCodeComparisonPanel dualListingPanel) {
this.controller = controller;
this.dualListingPanel = dualListingPanel;
- listingPanels[SOURCE] = dualListingPanel.getLeftPanel();
- listingPanels[DESTINATION] = dualListingPanel.getRightPanel();
+ ListingPanel leftPanel = dualListingPanel.getListingPanel(LEFT);
+ ListingPanel rightPanel = dualListingPanel.getListingPanel(RIGHT);
+ listingPanels = new Duo<>(leftPanel, rightPanel);
setUpDragDrop();
}
@@ -68,7 +70,7 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
dragSource = DragSource.getDefaultDragSource();
dragGestureAdapter = new DragGestureAdapter(this);
dragSourceAdapter = new DragSrcAdapter(this);
- dragSource.createDefaultDragGestureRecognizer(listingPanels[SOURCE].getFieldPanel(),
+ dragSource.createDefaultDragGestureRecognizer(listingPanels.get(LEFT).getFieldPanel(),
dragAction, dragGestureAdapter);
}
@@ -79,7 +81,7 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
// set up the destination fieldPanel as a drop target that accepts mark-up items.
dropTargetAdapter =
new DropTgtAdapter(this, DnDConstants.ACTION_COPY_OR_MOVE, acceptableFlavors);
- dropTarget = new DropTarget(listingPanels[DESTINATION].getFieldPanel(),
+ dropTarget = new DropTarget(listingPanels.get(RIGHT).getFieldPanel(),
DnDConstants.ACTION_COPY_OR_MOVE, dropTargetAdapter, true);
dropTarget.setActive(true);
}
@@ -100,13 +102,14 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
@Override
public boolean isStartDragOk(DragGestureEvent e) {
- if (!listingPanels[SOURCE].isStartDragOk()) {
+ if (!listingPanels.get(LEFT).isStartDragOk()) {
return false;
}
Point p = e.getDragOrigin();
- ProgramLocation programLocation = listingPanels[SOURCE].getProgramLocation(p);
- VTMarkupItem markupItem = controller.getCurrentMarkupForLocation(programLocation,
- dualListingPanel.getLeftProgram());
+ ProgramLocation programLocation = listingPanels.get(LEFT).getProgramLocation(p);
+ VTMarkupItem markupItem =
+ controller.getCurrentMarkupForLocation(programLocation,
+ dualListingPanel.getProgram(LEFT));
if (markupItem == null) {
return false;
}
@@ -122,13 +125,13 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
@Override
public Transferable getTransferable(Point p) {
- if (!listingPanels[SOURCE].contains(p)) {
+ if (!listingPanels.get(LEFT).contains(p)) {
return null;
}
- ProgramLocation programLocation = listingPanels[SOURCE].getProgramLocation(p);
+ ProgramLocation programLocation = listingPanels.get(LEFT).getProgramLocation(p);
VTMarkupItem markupItem = controller.getCurrentMarkupForLocation(programLocation,
- dualListingPanel.getLeftProgram());
+ dualListingPanel.getProgram(LEFT));
if (markupItem == null) {
return null;
}
@@ -145,10 +148,10 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
VTMarkupItem markupItem = (VTMarkupItem) obj;
VTMarkupType markupType = markupItem.getMarkupType();
Point p = event.getLocation();
- ProgramLocation loc = listingPanels[DESTINATION].getProgramLocation(p);
+ ProgramLocation loc = listingPanels.get(RIGHT).getProgramLocation(p);
Address newDestinationAddress =
- markupType.getAddress(loc, dualListingPanel.getRightProgram());
+ markupType.getAddress(loc, dualListingPanel.getProgram(RIGHT));
if (newDestinationAddress == null) {
Msg.showInfo(getClass(), dualListingPanel, "Invalid Drop Location",
markupType.getDisplayName() + " was not dropped at a valid location.");
@@ -166,11 +169,6 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
ArrayList arrayList = new ArrayList();
arrayList.add(markupItem);
- // Use the following if you only want to set the address.
-// SetMarkupItemDestinationAddressTask task =
-// new SetMarkupItemDestinationAddressTask(controller.getSession(), arrayList,
-// newDestinationAddress);
-
// Use the following if you want to set the address and apply the markup item using the default action.
ApplyMarkupAtDestinationAddressTask task = new ApplyMarkupAtDestinationAddressTask(
controller.getSession(), arrayList, newDestinationAddress, controller.getOptions());
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingContext.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingContext.java
index 48eda84392..edaff79d26 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingContext.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingContext.java
@@ -16,7 +16,6 @@
package ghidra.feature.vt.gui.duallisting;
import docking.ComponentProvider;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.context.ListingActionContext;
import ghidra.app.nav.Navigatable;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
@@ -28,7 +27,7 @@ import ghidra.app.util.viewer.util.CodeComparisonPanelActionContext;
public class VTListingContext extends ListingActionContext
implements CodeComparisonPanelActionContext {
- private CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel = null;
+ private CodeComparisonPanel codeComparisonPanel = null;
/**
* Creates an action context for a VT listing.
@@ -44,12 +43,12 @@ public class VTListingContext extends ListingActionContext
* @param codeComparisonPanel the code comparison panel.
*/
public void setCodeComparisonPanel(
- CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel) {
+ CodeComparisonPanel codeComparisonPanel) {
this.codeComparisonPanel = codeComparisonPanel;
}
@Override
- public CodeComparisonPanel extends FieldPanelCoordinator> getCodeComparisonPanel() {
+ public CodeComparisonPanel getCodeComparisonPanel() {
return codeComparisonPanel;
}
}
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingNavigator.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingNavigator.java
index 90080aa64f..f54883be0e 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingNavigator.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/duallisting/VTListingNavigator.java
@@ -92,13 +92,7 @@ public class VTListingNavigator implements Navigatable {
@Override
public boolean goTo(Program program, ProgramLocation location) {
- boolean went = listingPanel.goTo(location);
- // If we tried to go but couldn't, try again after showing entire listing.
- if (!went && !dualListingPanel.isEntireListingShowing()) {
- dualListingPanel.showEntireListing(true);
- return listingPanel.goTo(location);
- }
- return went;
+ return listingPanel.goTo(location);
}
@Override
@@ -147,7 +141,8 @@ public class VTListingNavigator implements Navigatable {
}
@Override
- public void removeHighlightProvider(ListingHighlightProvider highlightProvider, Program program) {
+ public void removeHighlightProvider(ListingHighlightProvider highlightProvider,
+ Program program) {
// currently unsupported
}
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 e9eb6c9a2f..e1f0b9c040 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
@@ -16,6 +16,7 @@
package ghidra.feature.vt.gui.provider.functionassociation;
import static ghidra.feature.vt.gui.provider.functionassociation.FilterSettings.*;
+import static ghidra.util.datastruct.Duo.Side.*;
import java.awt.*;
import java.awt.event.*;
@@ -220,7 +221,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
ListingCodeComparisonPanel dualListingPanel =
functionComparisonPanel.getDualListingPanel();
if (dualListingPanel != null) {
- ListingPanel leftPanel = dualListingPanel.getLeftPanel();
+ ListingPanel leftPanel = dualListingPanel.getListingPanel(LEFT);
return leftPanel.getHeaderActions(getName());
}
}
@@ -261,7 +262,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
}
// Is the action being taken on a toolbar button while the dual listing is visible?
else if (isToolbarButtonAction && isShowingDualListing) {
- listingPanel = dualListingPanel.getFocusedListingPanel();
+ listingPanel = dualListingPanel.getActiveListingPanel();
}
// If the dual listing is showing and this is a toolbar action or the action is
// on one of the listings in the ListingCodeComparisonPanel
@@ -367,10 +368,9 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
statusPanel.add(statusLabel, BorderLayout.CENTER);
dualTablePanel.add(statusPanel, BorderLayout.SOUTH);
- functionComparisonPanel =
- new FunctionComparisonPanel(this, tool, (Function) null, (Function) null);
+ functionComparisonPanel = new FunctionComparisonPanel(this, tool);
addSpecificCodeComparisonActions();
- functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonPanel.TITLE);
+ functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonPanel.NAME);
functionComparisonPanel.setTitlePrefixes("Source:", "Destination:");
comparisonSplitPane =
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemContext.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemContext.java
index 845f37ca99..82851a9a4b 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemContext.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemContext.java
@@ -18,7 +18,6 @@ package ghidra.feature.vt.gui.provider.markuptable;
import java.util.List;
import docking.DefaultActionContext;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
import ghidra.app.util.viewer.util.CodeComparisonPanelActionContext;
import ghidra.feature.vt.api.main.VTMarkupItem;
@@ -30,7 +29,7 @@ public class VTMarkupItemContext extends DefaultActionContext
implements CodeComparisonPanelActionContext {
private final List selectedItems;
- private CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel = null;
+ private CodeComparisonPanel codeComparisonPanel = null;
/**
* Creates an action context for the VT markup item provider.
@@ -55,12 +54,12 @@ public class VTMarkupItemContext extends DefaultActionContext
* @param codeComparisonPanel the code comparison panel.
*/
public void setCodeComparisonPanel(
- CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel) {
+ CodeComparisonPanel codeComparisonPanel) {
this.codeComparisonPanel = codeComparisonPanel;
}
@Override
- public CodeComparisonPanel extends FieldPanelCoordinator> getCodeComparisonPanel() {
+ public CodeComparisonPanel getCodeComparisonPanel() {
return codeComparisonPanel;
}
}
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 9a4c63b09a..3dcd67a23c 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
@@ -18,6 +18,7 @@ package ghidra.feature.vt.gui.provider.markuptable;
import static ghidra.feature.vt.api.impl.VTEvent.*;
import static ghidra.feature.vt.gui.plugin.VTPlugin.*;
import static ghidra.framework.model.DomainObjectEvent.*;
+import static ghidra.util.datastruct.Duo.Side.*;
import java.awt.*;
import java.awt.event.*;
@@ -34,7 +35,6 @@ import docking.action.*;
import docking.actions.PopupActionProvider;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.FieldPanel;
-import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.table.GTable;
import docking.widgets.table.RowObjectTableModel;
import docking.widgets.table.threaded.ThreadedTableModel;
@@ -150,23 +150,24 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
markupItemsTablePanel.add(tablePanel, BorderLayout.CENTER);
markupItemsTablePanel.add(filterAreaPanel, BorderLayout.SOUTH);
- functionComparisonPanel =
- new FunctionComparisonPanel(this, tool, (Function) null, (Function) null);
+ functionComparisonPanel = new FunctionComparisonPanel(this, tool);
addSpecificCodeComparisonActions();
- functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonPanel.TITLE);
+ functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonPanel.NAME);
functionComparisonPanel.setTitlePrefixes("Source:", "Destination:");
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
if (dualListingPanel != null) {
- dualListingPanel.setLeftProgramLocationListener(new SourceProgramLocationListener());
- dualListingPanel
- .setRightProgramLocationListener(new DestinationProgramLocationListener());
+
+ dualListingPanel.getListingPanel(LEFT)
+ .setProgramLocationListener(new SourceProgramLocationListener());
+ dualListingPanel.getListingPanel(RIGHT).setProgramLocationListener(
+ new DestinationProgramLocationListener());
sourceHighlightProvider = new VTDualListingHighlightProvider(controller, true);
destinationHighlightProvider = new VTDualListingHighlightProvider(controller, false);
dualListingPanel.addHighlightProviders(sourceHighlightProvider,
destinationHighlightProvider);
- sourceHighlightProvider.setListingPanel(dualListingPanel.getLeftPanel());
- destinationHighlightProvider.setListingPanel(dualListingPanel.getRightPanel());
+ sourceHighlightProvider.setListingPanel(dualListingPanel.getListingPanel(LEFT));
+ destinationHighlightProvider.setListingPanel(dualListingPanel.getListingPanel(RIGHT));
new VTDualListingDragNDropHandler(controller, dualListingPanel);
}
@@ -267,24 +268,24 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
// Don't set source or destination if the location change was initiated by the dual listing.
if (!processingSourceLocationChange &&
!processingDestinationLocationChange) {
- dualListingPanel.setLeftLocation(dualListingPanel.getLeftProgram(),
+ dualListingPanel.setLocation(LEFT, dualListingPanel.getProgram(LEFT),
markupItem.getSourceLocation());
- dualListingPanel.setRightLocation(dualListingPanel.getRightProgram(),
+ dualListingPanel.setLocation(RIGHT, dualListingPanel.getProgram(RIGHT),
markupItem.getDestinationLocation());
}
else {
// Only adjust the side of the dual listing panel that didn't initiate this.
ProgramLocation sourceLocation = markupItem.getSourceLocation();
if (processingDestinationLocationChange && sourceLocation != null) {
- dualListingPanel.setLeftLocation(dualListingPanel.getLeftProgram(),
- sourceLocation);
+ dualListingPanel.setLocation(LEFT,
+ dualListingPanel.getProgram(LEFT), sourceLocation);
}
ProgramLocation destinationLocation =
markupItem.getDestinationLocation();
if (processingSourceLocationChange && destinationLocation != null) {
- dualListingPanel.setRightLocation(
- dualListingPanel.getRightProgram(), destinationLocation);
+ dualListingPanel.setLocation(RIGHT,
+ dualListingPanel.getProgram(RIGHT), destinationLocation);
}
}
dualListingPanel.updateListings(); // refresh the dual listing's background markup colors.
@@ -361,10 +362,10 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
splitPane.add(functionComparisonPanel);
markupPanel.add(splitPane, BorderLayout.CENTER);
if (dualListingPanel != null) {
- dualListingPanel
- .setLeftProgramLocationListener(new SourceProgramLocationListener());
- dualListingPanel.setRightProgramLocationListener(
- new DestinationProgramLocationListener());
+ dualListingPanel.getListingPanel(LEFT)
+ .setProgramLocationListener(new SourceProgramLocationListener());
+ dualListingPanel.getListingPanel(LEFT)
+ .setProgramLocationListener(new DestinationProgramLocationListener());
}
markupPanel.validate();
@@ -378,8 +379,8 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
if (contains) {
// Remove the split pane.
if (dualListingPanel != null) {
- dualListingPanel.setLeftProgramLocationListener(null);
- dualListingPanel.setRightProgramLocationListener(null);
+ dualListingPanel.getListingPanel(LEFT).setProgramLocationListener(null);
+ dualListingPanel.getListingPanel(RIGHT).setProgramLocationListener(null);
}
markupPanel.remove(splitPane);
splitPane.remove(functionComparisonPanel);
@@ -462,7 +463,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
public List getPopupActions(Tool t, ActionContext context) {
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
if (context.getComponentProvider() == this && dualListingPanel != null) {
- ListingPanel sourcePanel = dualListingPanel.getLeftPanel();
+ ListingPanel sourcePanel = dualListingPanel.getListingPanel(LEFT);
return sourcePanel.getHeaderActions(getName());
}
return new ArrayList<>();
@@ -477,7 +478,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
List selectedItems = getSelectedMarkupItems();
VTMarkupItemContext vtMarkupItemContext = new VTMarkupItemContext(this, selectedItems);
if (functionComparisonPanel.isVisible()) {
- CodeComparisonPanel extends FieldPanelCoordinator> displayedPanel =
+ CodeComparisonPanel displayedPanel =
functionComparisonPanel.getDisplayedPanel();
vtMarkupItemContext.setCodeComparisonPanel(displayedPanel);
}
@@ -626,7 +627,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
}
}
else {
- functionComparisonPanel.loadFunctions(null, null);
+ functionComparisonPanel.clear();
}
if (sourceHighlightProvider != null) {
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/internal/LayoutLockedFieldPanelCoordinator.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/internal/LayoutLockedFieldPanelCoordinator.java
index a26795ea19..df9c150f94 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/internal/LayoutLockedFieldPanelCoordinator.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/internal/LayoutLockedFieldPanelCoordinator.java
@@ -37,24 +37,25 @@ public class LayoutLockedFieldPanelCoordinator extends LineLockedFieldPanelCoord
* Constructor for the coordinator.
* @param panels the field panels that will have their positions coordinated with each other.
*/
- public LayoutLockedFieldPanelCoordinator(FieldPanel[] panels) {
+ public LayoutLockedFieldPanelCoordinator(FieldPanel... panels) {
super(panels);
}
@Override
public void viewChanged(FieldPanel fp, BigInteger index, int xPos, int yPos) {
- if (valuesChanging)
+ if (valuesChanging) {
return;
+ }
try {
valuesChanging = true;
// "lockedLineIndex" is the IndexMap index indicating where this field panel
// is locked to the other when scrolling.
BigInteger lockedLineIndex1 = getLockedLineForPanel(fp);
if (lockedLineIndex1 == null) { // This shouldn't happen.
- throw new AssertException("Couldn't find line number for indicated field panel."
- + " FieldPanel is not one of those being managed by this coordinator.");
+ throw new AssertException("Couldn't find line number for indicated field panel." +
+ " FieldPanel is not one of those being managed by this coordinator.");
}
-
+
// "topIndex" is the IndexMap index of the top of the listing in view.
BigInteger topIndex1 = index;
LayoutModel layoutModel1 = fp.getLayoutModel();
@@ -62,7 +63,7 @@ public class LayoutLockedFieldPanelCoordinator extends LineLockedFieldPanelCoord
if (lockedLineLayout1 == null) {
return; // transitioning from one function to another.
}
-
+
// "lockedLineHeight" is the height of the layout in this field panel
// where it is locked to the other panel when scrolling.
int lockedLineHeight1 = lockedLineLayout1.getHeight();
@@ -70,10 +71,10 @@ public class LayoutLockedFieldPanelCoordinator extends LineLockedFieldPanelCoord
// numIndexes is the total number of indexes in this field panels indexMap.
BigInteger numIndexes1 = layoutModel1.getNumIndexes();
Layout firstLayout1 = layoutModel1.getLayout(topIndex1);
-
+
// "yPos" is a negative number indicating the number of pixels the start of the current
// layout is above the top of the field panel view.
-
+
// "remainingHeight" is the number of pixels vertically from the first visible pixel
// in the layout at the top of the listing view to the end of that layout.
int remainingHeight = firstLayout1.getHeight() + yPos;
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/correlate/HashedFunctionAddressCorrelation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/correlate/HashedFunctionAddressCorrelation.java
index 6ddad466b6..3770eb175a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/correlate/HashedFunctionAddressCorrelation.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/correlate/HashedFunctionAddressCorrelation.java
@@ -15,6 +15,8 @@
*/
package ghidra.program.model.correlate;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import java.util.*;
import java.util.Map.Entry;
@@ -23,7 +25,9 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.block.CodeBlock;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
-import ghidra.program.util.FunctionAddressCorrelation;
+import ghidra.program.util.ListingAddressCorrelation;
+import ghidra.util.datastruct.Duo;
+import ghidra.util.datastruct.Duo.Side;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -41,7 +45,6 @@ import ghidra.util.task.TaskMonitor;
* 5) Sequences with no corresponding match are also removed from consideration.
* 6) Sequences are limited to a single basic-block, and the algorithm is basic-block aware.
* Once a match establishes a correspondence between a pair of basic blocks, the algorithm uses
- * that information to further narrow in on and disambiguate matching sequences.
* 7) If a particular sequence has matches that are not unique, the algorithm tries to disambiguate the potential
* matches by looking at parent/child relationships of the containing basic-blocks. (see DisambiguateStrategy)
* 8) Multiple passes are attempted, each time the set of potential sequences is completely regenerated,
@@ -49,7 +52,7 @@ import ghidra.util.task.TaskMonitor;
* allows matches discovered by earlier passes to disambiguate sequences in later passes.
*
*/
-public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelation {
+public class HashedFunctionAddressCorrelation implements ListingAddressCorrelation {
/**
* A helper class for sorting through, disambiguating, sequences with identical hashes
@@ -59,64 +62,54 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
public Hash hash; // The disambiguating (secondary) hash
public int count; // Number of sequences (n-grams) in the subset matching the secondary hash
public InstructHash instruct; // (Starting Instruction of) the n-gram
- public DisambiguatorEntry(Hash h,InstructHash inst) {
+
+ public DisambiguatorEntry(Hash h, InstructHash inst) {
hash = h;
instruct = inst;
count = 1;
}
}
-
- private Function srcFunction;
- private Function destFunction;
- private TreeMap srcToDest; // Final source -> destination address mapping
- private TreeMap destToSrc; // Final destination -> source address mapping
+
+ private Duo functions;
+ private TreeMap srcToDest; // Final source -> destination address mapping
+ private TreeMap destToSrc; // Final destination -> source address mapping
private HashStore srcStore; // Sorted list of source n-grams from which to draw potential matches
private HashStore destStore; // List of destination n-grams
private HashCalculator hashCalc; // Object that calculates n-gram hashes
private TaskMonitor monitor;
-
+
/**
* Correlates addresses between the two specified functions.
- * @param function1 the first function
- * @param function2 the second function
- * @param mon the task monitor that indicates progress and allows the user to cancel.
+ * @param leftFunction the first function
+ * @param rightFunction the second function
+ * @param monitor the task monitor that indicates progress and allows the user to cancel.
* @throws CancelledException if the user cancels
* @throws MemoryAccessException if either functions memory can't be accessed.
*/
- public HashedFunctionAddressCorrelation(Function function1, Function function2, TaskMonitor mon)
- throws CancelledException, MemoryAccessException {
- srcFunction = function1;
- destFunction = function2;
- monitor = mon;
- srcToDest = new TreeMap();
- destToSrc = new TreeMap();
- if (function1 == null || function2 == null)
- return;
- srcStore = new HashStore(function1, monitor);
- destStore = new HashStore(function2, monitor);
+ public HashedFunctionAddressCorrelation(Function leftFunction, Function rightFunction,
+ TaskMonitor monitor) throws CancelledException, MemoryAccessException {
+ if (leftFunction == null || rightFunction == null) {
+ throw new IllegalArgumentException("Functions can't be null!");
+ }
+ this.functions = new Duo<>(leftFunction, rightFunction);
+ this.monitor = monitor;
+ srcToDest = new TreeMap();
+ destToSrc = new TreeMap();
+ srcStore = new HashStore(leftFunction, monitor);
+ destStore = new HashStore(rightFunction, monitor);
hashCalc = new MnemonicHashCalculator();
calculate();
buildFinalMaps();
}
@Override
- public Program getFirstProgram() {
- return srcFunction.getProgram();
+ public Program getProgram(Side side) {
+ return functions.get(side).getProgram();
}
@Override
- public Program getSecondProgram() {
- return destFunction.getProgram();
- }
-
- @Override
- public AddressSetView getAddressesInFirst() {
- return srcFunction.getBody();
- }
-
- @Override
- public AddressSetView getAddressesInSecond() {
- return destFunction.getBody();
+ public AddressSetView getAddresses(Side side) {
+ return functions.get(side).getBody();
}
/**
@@ -126,7 +119,7 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
public int getTotalInstructionsInFirst() {
return srcStore.getTotalInstructions();
}
-
+
/**
* Gets the total number of instructions that are in the second function.
* @return the second function's instruction count.
@@ -161,7 +154,7 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
public List getUnmatchedInstructionsInFirst() {
return srcStore.getUnmatchedInstructions();
}
-
+
/**
* Determines the number of instructions from the second function that do not match an
* instruction in the first function.
@@ -180,7 +173,8 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
* @param destInstruct is (the starting Instruction of) the destination n-gram
* @throws MemoryAccessException
*/
- private void declareMatch(HashEntry srcEntry,InstructHash srcInstruct,HashEntry destEntry,InstructHash destInstruct) throws MemoryAccessException {
+ private void declareMatch(HashEntry srcEntry, InstructHash srcInstruct, HashEntry destEntry,
+ InstructHash destInstruct) throws MemoryAccessException {
boolean cancelMatch = false;
int matchSize = srcEntry.hash.size;
// Its possible that some instructions of the n-gram have already been matched
@@ -192,7 +186,8 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
destStore.removeHash(destEntry); // Remove this HashEntry
cancelMatch = true; // Cancel the match
}
- if (cancelMatch) return;
+ if (cancelMatch)
+ return;
ArrayList srcInstructVec = new ArrayList();
ArrayList destInstructVec = new ArrayList();
ArrayList srcBlockVec = new ArrayList();
@@ -202,7 +197,7 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
HashStore.extendMatch(matchSize, srcInstruct, srcMatch, destInstruct, destMatch, hashCalc);
srcStore.matchHash(srcMatch, srcInstructVec, srcBlockVec);
destStore.matchHash(destMatch, destInstructVec, destBlockVec);
- for(int i=0;i constructDisambiguatorTree(HashEntry entry,
HashStore store, DisambiguateStrategy strategy)
throws CancelledException, MemoryAccessException {
- TreeMap entryMap = new TreeMap();
+ TreeMap entryMap = new TreeMap();
int matchSize = entry.hash.size;
- for(InstructHash curInstruct : entry.instList) {
+ for (InstructHash curInstruct : entry.instList) {
ArrayList hashList = strategy.calcHashes(curInstruct, matchSize, store);
Iterator iter = hashList.iterator();
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Hash curHash = iter.next();
DisambiguatorEntry curEntry = entryMap.get(curHash);
if (curEntry == null) {
- curEntry = new DisambiguatorEntry(curHash,curInstruct);
+ curEntry = new DisambiguatorEntry(curHash, curInstruct);
entryMap.put(curHash, curEntry);
}
else
@@ -247,24 +242,30 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
* @throws CancelledException
* @throws MemoryAccessException
*/
- private int disambiguateNgramsWithStrategy(DisambiguateStrategy strategy,HashEntry srcEntry,HashEntry destEntry) throws CancelledException, MemoryAccessException {
+ private int disambiguateNgramsWithStrategy(DisambiguateStrategy strategy, HashEntry srcEntry,
+ HashEntry destEntry) throws CancelledException, MemoryAccessException {
TreeMap srcDisambig =
constructDisambiguatorTree(srcEntry, srcStore, strategy);
TreeMap destDisambig =
constructDisambiguatorTree(destEntry, destStore, strategy);
int count = 0;
Iterator iter = srcDisambig.values().iterator();
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
DisambiguatorEntry srcDisEntry = iter.next();
- if (srcDisEntry.count != 1) continue;
+ if (srcDisEntry.count != 1)
+ continue;
// Its possible for this InstructHash to have been matched by an earlier DisambiguatorEntry
- if (srcDisEntry.instruct.isMatched) continue;
+ if (srcDisEntry.instruct.isMatched)
+ continue;
DisambiguatorEntry destDisEntry = destDisambig.get(srcDisEntry.hash);
- if (destDisEntry == null) continue;
- if (destDisEntry.count != 1) continue;
- if (destDisEntry.instruct.isMatched) continue;
+ if (destDisEntry == null)
+ continue;
+ if (destDisEntry.count != 1)
+ continue;
+ if (destDisEntry.instruct.isMatched)
+ continue;
// If both sides have exactly one matching InstructHash, call it a match
- declareMatch(srcEntry,srcDisEntry.instruct,destEntry,destDisEntry.instruct);
+ declareMatch(srcEntry, srcDisEntry.instruct, destEntry, destDisEntry.instruct);
count += 1;
}
return count;
@@ -278,21 +279,27 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
* @throws CancelledException
* @throws MemoryAccessException
*/
- private boolean disambiguateMatchingNgrams(HashEntry srcEntry,HashEntry destEntry) throws CancelledException, MemoryAccessException {
+ private boolean disambiguateMatchingNgrams(HashEntry srcEntry, HashEntry destEntry)
+ throws CancelledException, MemoryAccessException {
if (srcEntry.hasDuplicateBlocks())
return false;
if (destEntry.hasDuplicateBlocks())
return false;
if (srcEntry.hash.size != destEntry.hash.size)
return false; // This likely never happens, because we know the hash values are equal
- int count = disambiguateNgramsWithStrategy(new DisambiguateByParent(),srcEntry,destEntry);
- if (count != 0) return true;
- count = disambiguateNgramsWithStrategy(new DisambiguateByChild(),srcEntry,destEntry);
- if (count != 0) return true;
- count = disambiguateNgramsWithStrategy(new DisambiguateByBytes(),srcEntry,destEntry);
- if (count != 0) return true;
- count= disambiguateNgramsWithStrategy(new DisambiguateByParentWithOrder(),srcEntry,destEntry);
- if (count != 0) return true;
+ int count = disambiguateNgramsWithStrategy(new DisambiguateByParent(), srcEntry, destEntry);
+ if (count != 0)
+ return true;
+ count = disambiguateNgramsWithStrategy(new DisambiguateByChild(), srcEntry, destEntry);
+ if (count != 0)
+ return true;
+ count = disambiguateNgramsWithStrategy(new DisambiguateByBytes(), srcEntry, destEntry);
+ if (count != 0)
+ return true;
+ count = disambiguateNgramsWithStrategy(new DisambiguateByParentWithOrder(), srcEntry,
+ destEntry);
+ if (count != 0)
+ return true;
return false;
}
@@ -303,7 +310,7 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
* @throws CancelledException
*/
private void findMatches() throws MemoryAccessException, CancelledException {
- while(!srcStore.isEmpty() && !destStore.isEmpty()) {
+ while (!srcStore.isEmpty() && !destStore.isEmpty()) {
HashEntry srcEntry = srcStore.getFirstEntry();
HashEntry destEntry = destStore.getEntry(srcEntry.hash);
if (destEntry == null) {
@@ -311,7 +318,8 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
}
else if (srcEntry.instList.size() == 1 && destEntry.instList.size() == 1) {
// Found a unique match
- declareMatch(srcEntry,srcEntry.instList.getFirst(),destEntry,destEntry.instList.getFirst());
+ declareMatch(srcEntry, srcEntry.instList.getFirst(), destEntry,
+ destEntry.instList.getFirst());
}
else {
HashEntry destEntry2 = destStore.getFirstEntry();
@@ -321,14 +329,15 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
}
else if (srcEntry2.instList.size() == 1 && destEntry2.instList.size() == 1) {
// Found a unique match
- declareMatch(srcEntry2,srcEntry2.instList.getFirst(),destEntry2,destEntry2.instList.getFirst());
+ declareMatch(srcEntry2, srcEntry2.instList.getFirst(), destEntry2,
+ destEntry2.instList.getFirst());
}
else {
if (!disambiguateMatchingNgrams(srcEntry, destEntry))
srcStore.removeHash(srcEntry);
}
}
- }
+ }
}
/**
@@ -344,21 +353,24 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
* @throws MemoryAccessException
* @throws CancelledException
*/
- private void runPasses(int minLength,int maxLength,boolean wholeBlock,boolean matchBlock,int maxPasses) throws MemoryAccessException, CancelledException {
+ private void runPasses(int minLength, int maxLength, boolean wholeBlock, boolean matchBlock,
+ int maxPasses) throws MemoryAccessException, CancelledException {
srcStore.calcHashes(minLength, maxLength, wholeBlock, matchBlock, hashCalc);
destStore.calcHashes(minLength, maxLength, wholeBlock, matchBlock, hashCalc);
- for(int pass=0;pass < maxPasses;++pass) {
+ for (int pass = 0; pass < maxPasses; ++pass) {
int curMatch = srcStore.numMatchedInstructions();
- if (curMatch == srcStore.getTotalInstructions()) break; // quit if there are no unmatched instructions
+ if (curMatch == srcStore.getTotalInstructions())
+ break; // quit if there are no unmatched instructions
srcStore.clearSort();
destStore.clearSort();
-
+
srcStore.insertHashes();
destStore.insertHashes();
-
+
findMatches();
- if (curMatch == srcStore.numMatchedInstructions()) break; // quit if no new matched instructions
- }
+ if (curMatch == srcStore.numMatchedInstructions())
+ break; // quit if no new matched instructions
+ }
}
/**
@@ -377,33 +389,40 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
findMatches();
- if (srcStore.numMatchedInstructions() == srcStore.getTotalInstructions()) return;
- if (destStore.numMatchedInstructions() == destStore.getTotalInstructions()) return;
+ if (srcStore.numMatchedInstructions() == srcStore.getTotalInstructions())
+ return;
+ if (destStore.numMatchedInstructions() == destStore.getTotalInstructions())
+ return;
// Now try multiple passes of 3 and 4 long n-grams hopefully filling in a lot of small holes in our match
// given a scaffolding of previously matched basic blocks
- runPasses(3,4,true,true,10);
+ runPasses(3, 4, true, true, 10);
- if (srcStore.numMatchedInstructions() == srcStore.getTotalInstructions()) return;
- if (destStore.numMatchedInstructions() == destStore.getTotalInstructions()) return;
+ if (srcStore.numMatchedInstructions() == srcStore.getTotalInstructions())
+ return;
+ if (destStore.numMatchedInstructions() == destStore.getTotalInstructions())
+ return;
// Repeat with big n-grams
int curMatch = srcStore.numMatchedInstructions();
- runPasses(5,10,false,false,3);
+ runPasses(5, 10, false, false, 3);
+
+ if (srcStore.numMatchedInstructions() == curMatch)
+ return; // No progress
+ if (srcStore.numMatchedInstructions() == srcStore.getTotalInstructions())
+ return;
+ if (destStore.numMatchedInstructions() == destStore.getTotalInstructions())
+ return;
- if (srcStore.numMatchedInstructions() == curMatch) return; // No progress
- if (srcStore.numMatchedInstructions() == srcStore.getTotalInstructions()) return;
- if (destStore.numMatchedInstructions() == destStore.getTotalInstructions()) return;
-
// Repeat with small n-grams
- runPasses(3,4,true,true,10);
+ runPasses(3, 4, true, true, 10);
}
/**
* {@literal Given the src -> dest map, build the dest -> src map}
*/
private void buildFinalMaps() {
- for(Entry entry : srcToDest.entrySet()) {
+ for (Entry entry : srcToDest.entrySet()) {
destToSrc.put(entry.getValue(), entry.getKey()); // Build the reverse map of srcToDest
}
}
@@ -412,27 +431,20 @@ public class HashedFunctionAddressCorrelation implements FunctionAddressCorrelat
* Gets an iterator of the matching addresses from the first function to the second.
* @return the iterator
*/
- public Iterator> getFirstToSecondIterator() {
+ public Iterator> getFirstToSecondIterator() {
return srcToDest.entrySet().iterator();
}
@Override
- public Address getAddressInSecond(Address addressInFirst) {
- return srcToDest.get(addressInFirst);
- }
-
- @Override
- public Address getAddressInFirst(Address addressInSecond) {
- return destToSrc.get(addressInSecond);
+ public Address getAddress(Side side, Address otherSideAddress) {
+ if (side == LEFT) {
+ return destToSrc.get(otherSideAddress);
+ }
+ return srcToDest.get(otherSideAddress);
}
@Override
- public Function getFirstFunction() {
- return srcFunction;
- }
-
- @Override
- public Function getSecondFunction() {
- return destFunction;
+ public Function getFunction(Side side) {
+ return functions.get(side);
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FunctionAddressCorrelation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DummyListingAddressCorrelation.java
similarity index 52%
rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FunctionAddressCorrelation.java
rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DummyListingAddressCorrelation.java
index 12ae85651e..345c615027 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FunctionAddressCorrelation.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DummyListingAddressCorrelation.java
@@ -15,25 +15,32 @@
*/
package ghidra.program.util;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.util.datastruct.Duo.Side;
-/**
- * This is the interface for a correlator that associates instructions from one function to
- * instructions from another function. Given an address from one function it determines the matching
- * address in the other function if possible.
- */
-public interface FunctionAddressCorrelation extends ListingAddressCorrelation {
+public class DummyListingAddressCorrelation implements ListingAddressCorrelation {
- /**
- * Gets the first function for this address correlator.
- * @return the first function.
- */
- public Function getFirstFunction();
+ @Override
+ public Program getProgram(Side side) {
+ return null;
+ }
- /**
- * Gets the second function for this address correlator.
- * @return the second function.
- */
- public Function getSecondFunction();
+ @Override
+ public Function getFunction(Side side) {
+ return null;
+ }
+
+ @Override
+ public AddressSetView getAddresses(Side side) {
+ return null;
+ }
+
+ @Override
+ public Address getAddress(Side side, Address otherSideAddress) {
+ return null;
+ }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ListingAddressCorrelation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ListingAddressCorrelation.java
index 642f4706bd..fe68a353f9 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ListingAddressCorrelation.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ListingAddressCorrelation.java
@@ -17,54 +17,47 @@ package ghidra.program.util;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
+import ghidra.util.datastruct.Duo.Side;
/**
* This is the interface for a correlator that associates addresses from one program with
* addresses from another program or it can associate addresses from one part of a program
- * with addresses from another part of the same program. Given an address from the address set
- * in the first program it determines the matching address from the address set for the second
- * program if possible.
+ * with addresses from another part of the same program. Given an address from one program, it
+ * can provide the corresponding address for the other program. The two programs are referred to
+ * as the LEFT program and the RIGHT program. See {@link ghidra.util.datastruct.Duo.Side}
*/
public interface ListingAddressCorrelation {
/**
- * Gets the program containing the first set of addresses.
- * @return the program for the first set of addresses.
+ * Gets the program for the given side.
+ * @param side LEFT or RIGHT
+ * @return the program for the given side
*/
- public abstract Program getFirstProgram();
+ public abstract Program getProgram(Side side);
/**
- * Gets the program containing the second set of addresses.
- * This program may be different from or the same as the first program.
- * @return the program for the second set of addresses.
+ * Gets the function for the given side. This will be null if the addresses are not function
+ * based.
+ * @param side LEFT or RIGHT
+ * @return the function for the given side or null if not function based
*/
- public abstract Program getSecondProgram();
+ public abstract Function getFunction(Side side);
/**
- * Gets the first set of addresses for this correlator.
- * @return the first set of addresses.
+ * Gets the addresses that are part of the correlator for the given side
+ * @param side LEFT or RIGHT
+ * @return the addresses that are part of the correlator for the given side
*/
- public abstract AddressSetView getAddressesInFirst();
+ public abstract AddressSetView getAddresses(Side side);
/**
- * Gets the second set of addresses for this correlator.
- * @return the second set of addresses.
+ * Gets the address for the given side that matches the given address from the other side.
+ * @param side the side to get an address for
+ * @param otherSideAddress the address from the other side to find a match for
+ * @return the address for the given side that matches the given address from the other side.
*/
- public abstract AddressSetView getAddressesInSecond();
-
- /**
- * Determine the address from the second set that matches the specified address in the first set.
- * @param addressInFirst the address in the first address set.
- * @return the matching address in the second set or null if a match couldn't be determined.
- */
- public abstract Address getAddressInSecond(Address addressInFirst);
-
- /**
- * Determine the address from the first set that matches the specified address in the second set.
- * @param addressInSecond the address in the second address set.
- * @return the matching address in the first set or null if a match couldn't be determined.
- */
- public abstract Address getAddressInFirst(Address addressInSecond);
+ public abstract Address getAddress(Side side, Address otherSideAddress);
}
diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/util/datastruct/Duo.java b/Ghidra/Framework/Utility/src/main/java/ghidra/util/datastruct/Duo.java
new file mode 100644
index 0000000000..754f842c4c
--- /dev/null
+++ b/Ghidra/Framework/Utility/src/main/java/ghidra/util/datastruct/Duo.java
@@ -0,0 +1,126 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.util.datastruct;
+
+import static ghidra.util.datastruct.Duo.Side.*;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Class for holding two objects of the same type. We are using the idiom of LEFT and RIGHT to
+ * refer to each item in this pair of objects.
+ * The enum "Side" is used to represent either the LEFT (or first) or RIGHT (or second) item.
+ *
+ * @param The type of item that is stored in this Duo.
+ */
+public class Duo {
+ public enum Side {
+ LEFT, RIGHT;
+
+ public Side otherSide() {
+ return this == LEFT ? RIGHT : LEFT;
+ }
+ }
+
+ private final T left;
+ private final T right;
+
+ /**
+ * Constructor with no values.
+ */
+ public Duo() {
+ this(null, null);
+ }
+
+ /**
+ * Constructor with a left and right value.
+ * @param left the left value
+ * @param right the right value
+ */
+ public Duo(T left, T right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * Gets the value for the given side.
+ * @param side LEFT or RIGHT
+ * @return the value for the given side
+ */
+ public T get(Side side) {
+ return side == LEFT ? left : right;
+ }
+
+ /**
+ * Creates a new Duo, replacing the value for just one side. The other side uses the value
+ * from this Duo.
+ * @param side the side that gets a new value
+ * @param newValue the new value for the given side
+ * @return the new Duo
+ * value as this
+ */
+ public Duo with(Side side, T newValue) {
+ if (side == LEFT) {
+ return new Duo<>(newValue, right);
+ }
+ return new Duo<>(left, newValue);
+ }
+
+ /**
+ * Invokes the given consumer on both the left and right values.
+ * @param c the consumer to invoke on both values
+ */
+ public void each(Consumer c) {
+ if (left != null) {
+ c.accept(left);
+ }
+ if (right != null) {
+ c.accept(right);
+ }
+ }
+
+ /**
+ * Returns true if both values are equals to this objects values.
+ * @param otherLeft the value to compare to our left side value
+ * @param otherRight the value to compare to our right side value
+ * @return true if both values are equals to this objects values
+ */
+ public boolean equals(T otherLeft, T otherRight) {
+ return Objects.equals(left, otherLeft) && Objects.equals(right, otherRight);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(left, right);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Duo> other = (Duo>) obj;
+ return Objects.equals(left, other.left) && Objects.equals(right, other.right);
+ }
+
+}
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 d1526ecc65..ce219cb949 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionComparisonScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FunctionComparisonScreenShots.java
@@ -15,6 +15,8 @@
*/
package help.screenshot;
+import static ghidra.util.datastruct.Duo.Side.*;
+
import java.io.IOException;
import javax.swing.table.TableColumn;
@@ -102,7 +104,7 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
ListingCodeComparisonPanel dualListing =
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
- ListingPanel leftPanel = dualListing.getLeftPanel();
+ ListingPanel leftPanel = dualListing.getListingPanel(LEFT);
leftPanel.goTo(addr(0x004119aa));
});
waitForSwing();
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
new file mode 100644
index 0000000000..9c01bd5428
--- /dev/null
+++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/compare/CompareFunctionsDecompilerViewTest.java
@@ -0,0 +1,104 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.plugin.compare;
+
+import static org.junit.Assert.*;
+
+import java.util.Set;
+
+import org.junit.*;
+
+import ghidra.app.plugin.core.functioncompare.*;
+import ghidra.codecompare.decompile.CDisplay;
+import ghidra.codecompare.decompile.DecompilerCodeComparisonPanel;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.listing.*;
+import ghidra.test.*;
+
+/**
+ * Tests for the {@link FunctionComparisonPlugin function comparison plugin}
+ * that involve the GUI
+ */
+public class CompareFunctionsDecompilerViewTest extends AbstractGhidraHeadedIntegrationTest {
+
+ private TestEnv env;
+ private Program program1;
+ private Function fun1;
+ private Function fun2;
+ private FunctionComparisonPlugin plugin;
+ private FunctionComparisonProvider provider;
+
+ @Before
+ public void setUp() throws Exception {
+ env = new TestEnv();
+ plugin = env.addPlugin(FunctionComparisonPlugin.class);
+ program1 = buildTestProgram();
+ showTool(plugin.getTool());
+ env.open(program1);
+ FunctionManager functionManager = program1.getFunctionManager();
+ fun1 = functionManager.getFunctionAt(addr(0x01002cf5));
+ fun2 = functionManager.getFunctionAt(addr(0x0100415a));
+
+ }
+
+ private Address addr(long offset) {
+ return program1.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ env.dispose();
+ }
+
+ @Test
+ public void testDecompDifView() throws Exception {
+ Set functions = CompareFunctionsTestUtility.getFunctionsAsSet(fun1, fun2);
+ provider = compareFunctions(functions);
+
+ CompareFunctionsTestUtility.checkSourceFunctions(provider, fun1, fun2);
+ DecompilerCodeComparisonPanel panel =
+ (DecompilerCodeComparisonPanel) provider
+ .getCodeComparisonPanelByName(DecompilerCodeComparisonPanel.NAME);
+
+ waitForDecompiler(panel);
+ assertHasLines(panel.getLeftPanel(), 28);
+ assertHasLines(panel.getRightPanel(), 23);
+ }
+
+ private void assertHasLines(CDisplay panel, int lineCount) {
+ assertEquals(lineCount, panel.getDecompilerPanel().getLines().size());
+ }
+
+ private void waitForDecompiler(DecompilerCodeComparisonPanel panel) {
+ waitForSwing();
+ waitForCondition(() -> !panel.isBusy());
+ waitForSwing();
+ }
+
+ private FunctionComparisonProvider compareFunctions(Set functions) {
+ provider = runSwing(() -> plugin.compareFunctions(functions));
+ provider.setVisible(true);
+ waitForSwing();
+ return provider;
+ }
+
+ private Program buildTestProgram() throws Exception {
+ ClassicSampleX86ProgramBuilder builder =
+ new ClassicSampleX86ProgramBuilder("Test", false);
+ return builder.getProgram();
+ }
+
+}