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: + * + * 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 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