diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest
index 0da242d8d8..5d8a8163a5 100644
--- a/Ghidra/Features/Base/certification.manifest
+++ b/Ghidra/Features/Base/certification.manifest
@@ -377,6 +377,7 @@ src/main/help/help/topics/FunctionComparison/images/ListingCodeComparisonOptions
src/main/help/help/topics/FunctionComparison/images/MultiFunctionComparisonWindow.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/NavNextIcon.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/NavPreviousIcon.png||GHIDRA||||END|
+src/main/help/help/topics/FunctionComparison/images/NavSelectedIcon.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/RemoveFromComparisonIcon.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/binaryData.gif||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/class.png||GHIDRA||||END|
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/FunctionComparison.htm b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/FunctionComparison.htm
index b316e28d21..0d06c447db 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/FunctionComparison.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/FunctionComparison.htm
@@ -428,16 +428,22 @@
sides of the comparison window.
-
Remove Function From Comparison
+
Remove Function From Comparison
Removes the function in the focused panel from the comparison. This
will remove the function from both the source and target selection pulldowns.
-
Go To Next Function
+
Go To Next Function
Navigates to the next available function in the selection pulldown
-
Go To Previous Function
+
Go To Previous Function
Navigates to the previous available function in the selection pulldown
+
Navigate To Selected Function
+ When toggled on, the function comparison panels become navigable,
+ meaning a mouse click on the panel or change in the function being viewed
+ will result in a GoTo event being generated. This allows other panels
+ (eg: the listing) to update their views accordingly.
+
The Remove and Go To actions described
above will operate on the comparison panel that has focus, identified by the
pink border.
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png
index aa6e53c0fa..f568e64ef8 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png and b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png differ
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 75a373a37b..16a66de34d 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
@@ -116,6 +116,24 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
return sourceHasFocus ? sourceFunctionsCB : targetFunctionsCB;
}
+ /**
+ * Returns the source combo box
+ *
+ * @return the source combo box
+ */
+ public JComboBox getSourceComponent() {
+ return sourceFunctionsCB;
+ }
+
+ /**
+ * Returns the target combo box
+ *
+ * @return the target combo box
+ */
+ public JComboBox getTargetComponent() {
+ return targetFunctionsCB;
+ }
+
/**
* Clears out and reloads the source function list. Any selection currently
* made on the list will be reestablished.
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonProvider.java
index 0d57424345..e03d1f5ce2 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonProvider.java
@@ -56,11 +56,13 @@ public class MultiFunctionComparisonProvider extends FunctionComparisonProvider
DockingAction previousFunctionAction = new PreviousFunctionAction(this);
DockingAction removeFunctionsAction = new RemoveFunctionsAction(this);
DockingAction openFunctionTableAction = getOpenFunctionTableAction();
+ DockingAction navigateToAction = new NavigateToFunctionAction(this);
addLocalAction(nextFunctionAction);
addLocalAction(previousFunctionAction);
addLocalAction(removeFunctionsAction);
addLocalAction(openFunctionTableAction);
+ addLocalAction(navigateToAction);
}
/**
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
new file mode 100644
index 0000000000..102213797c
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/NavigateToFunctionAction.java
@@ -0,0 +1,187 @@
+/* ###
+ * 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.actions;
+
+import java.awt.event.*;
+import java.util.List;
+
+import javax.swing.ImageIcon;
+
+import docking.action.ToggleDockingAction;
+import docking.action.ToolBarData;
+import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
+import ghidra.app.plugin.core.functioncompare.*;
+import ghidra.app.services.GoToService;
+import ghidra.app.util.viewer.util.CodeComparisonPanel;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.listing.Function;
+import ghidra.util.HTMLUtilities;
+import ghidra.util.HelpLocation;
+import resources.Icons;
+
+/**
+ * Toggle Action designed to be used with a {@link MultiFunctionComparisonProvider}.
+ * When toggled on, a GoTo event will be issued for the function displayed in
+ * the comparison panel after the following events:
+ *
+ * - focus is gained on either the left or right panels
+ * - the function displayed in a comparison panel changes
+ *
+ * Note that the GoTo will only operate on the comparison panel that
+ * has focus. eg: If the left panel has focus but the user changes the
+ * function being viewed in the right panel, no GoTo will be issued.
+ */
+public class NavigateToFunctionAction extends ToggleDockingAction {
+
+ private GoToService goToService;
+
+ private static final ImageIcon NAV_FUNCTION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
+
+ /**
+ * Constructor
+ *
+ * @param provider the function comparison provider containing this action
+ */
+ public NavigateToFunctionAction(MultiFunctionComparisonProvider provider) {
+ super("Navigate To Selected Function", provider.getName());
+
+ goToService = provider.getTool().getService(GoToService.class);
+
+ setEnabled(true);
+ setSelected(false);
+ ToolBarData newToolBarData = new ToolBarData(NAV_FUNCTION_ICON);
+ setToolBarData(newToolBarData);
+ setDescription(
+ HTMLUtilities.toHTML("Toggle On means to navigate to whatever " +
+ "function is selected in the comparison panel, when focus changes or" +
+ "a new function is selected."));
+ setHelpLocation(
+ new HelpLocation(MultiFunctionComparisonPanel.HELP_TOPIC, "Navigate_To_Function"));
+
+ addFocusListeners(provider);
+ addChangeListeners(provider);
+ }
+
+ /**
+ * Adds a listener to each of the function selection widgets in the
+ * 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();
+
+ 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) {
+
+ FunctionComparisonPanel mainPanel = provider.getComponent();
+ List> panels =
+ mainPanel.getComparisonPanels();
+
+ for (CodeComparisonPanel extends FieldPanelCoordinator> panel : panels) {
+
+ panel.getRightFieldPanel().addFocusListener(new FocusAdapter() {
+
+ @Override
+ public void focusGained(FocusEvent e) {
+ if (NavigateToFunctionAction.this.isSelected()) {
+
+ Address addr = null;
+
+ 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());
+ }
+ }
+ });
+
+ panel.getRightFieldPanel().addFocusListener(new FocusAdapter() {
+
+ @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());
+ }
+ }
+
+ });
+ }
+ }
+}
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 59a82b3c68..532db7c46b 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
@@ -2713,12 +2713,12 @@ public class ListingCodeComparisonPanel
}
@Override
- protected FieldPanel getLeftFieldPanel() {
+ public FieldPanel getLeftFieldPanel() {
return getLeftPanel().getFieldPanel();
}
@Override
- protected FieldPanel getRightFieldPanel() {
+ public FieldPanel getRightFieldPanel() {
return getRightPanel().getFieldPanel();
}
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 a019ca7437..02e9d9a78d 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
@@ -326,13 +326,13 @@ public abstract class CodeComparisonPanel exten
* Gets the left field panel for this CodeComparisonPanel.
* @return the left FieldPanel.
*/
- protected abstract FieldPanel getLeftFieldPanel();
+ public abstract FieldPanel getLeftFieldPanel();
/**
* Gets the right field panel for this CodeComparisonPanel.
* @return the right FieldPanel.
*/
- protected abstract FieldPanel getRightFieldPanel();
+ public abstract FieldPanel getRightFieldPanel();
/**
* Determines if the layouts of the views are synchronized with respect to scrolling and
diff --git a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java
index d1fe2ee4dd..49f7aea503 100644
--- a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java
+++ b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java
@@ -15,7 +15,7 @@
*/
package help.screenshot;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
import java.awt.*;
import java.awt.geom.GeneralPath;
@@ -604,6 +604,12 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
runSwing(() -> {
ImageIcon imageIcon = ResourceManager.getImageIcon(icon);
image = imageIcon.getImage();
+
+ // The image returned here must be a BufferedImage, so create one
+ // if not. It may be a ToolkitImage (eg: if the icon in question
+ // is retrieved from Icons.java), which would fail on a cast to
+ // BufferedImage during the save operation.
+ image = ImageUtils.getBufferedImage(image);
});
}
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
index 24bd516695..47b788f529 100644
--- 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
@@ -688,12 +688,12 @@ public abstract class DecompilerCodeComparisonPanel