diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index 09122d462b..0da242d8d8 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -369,10 +369,15 @@ src/main/help/help/topics/FrontEndPlugin/images/program_obj.png||GHIDRA|||Custom src/main/help/help/topics/FrontEndPlugin/images/up.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/user.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/FunctionComparison/FunctionComparison.htm||GHIDRA||||END| +src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png||GHIDRA||||END| +src/main/help/help/topics/FunctionComparison/images/AddToComparisonIcon.png||GHIDRA||||END| src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png||GHIDRA||||END| src/main/help/help/topics/FunctionComparison/images/FunctionScope.gif||GHIDRA||||END| src/main/help/help/topics/FunctionComparison/images/ListingCodeComparisonOptions.png||GHIDRA||||END| 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/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| src/main/help/help/topics/FunctionComparison/images/cursor_arrow_flipped.gif||GHIDRA||||END| @@ -946,6 +951,7 @@ src/main/resources/images/arrow_up.png||FAMFAMFAM Icons - CC 2.5|||famfamfam sil src/main/resources/images/binaryData.gif||GHIDRA||||END| src/main/resources/images/browser.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END| src/main/resources/images/bullet_green.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| +src/main/resources/images/bullet_star.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/cUnion.png||GHIDRA||||END| src/main/resources/images/camera-photo.png||Tango Icons - Public Domain|||tango|END| src/main/resources/images/carry.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 5ade8ca421..b316e28d21 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 @@ -11,38 +11,21 @@
The Function Comparison window can provide different types of panels for visually comparing - two or more functions. The Listing View of the Function Comparison window and its capabilities - is explained below.
+The Function Comparison window provides a way to compare two or more + functions in a simple side-by-side panel.
-To begin, select a function (or multiple functions) from the listing or + the function table. Then right-click and select the Compare Selected + Functions option.
--- -A Function Comparison window can be displayed from a selection containing two or more - functions in the CodeBrowser listing.
-
-- --
-- In the CodeBrowser Listing make a selection containing two or more functions that you - want to compare.
- -- Right mouse click and select the
-Compare - Selected Functions... option on the right mouse popup menu.
A Function Comparison window will appear.
+A new function comparison window will appear (subsequent + invocations of this option will create a new tab in the existing window).
This window can have multiple tabs, one for each type of function comparison view that is - available.
-When comparing more than two functions, a Function Comparison window will have additional - components at the top of it. This window displays two functions side by side again, but has - two choice components at the top of the panel, which indicates the current function being - displayed in each side (left and right) of the panel. Each choice component can display a - list indicating the functions that can be displayed in the listing below it. Only one - function at a a time from the list can be displayed below it based on the type of view - currently set. You can simply change the current function for either side of the function - comparison using the choice component in order to get a different function comparison.
+There is no limit to the number of functions that can be compared. Users may + add and remove functions from each comparison panel as desired. Simply use the pulldowns + above the listing panels to change what is being compared.
-There are two toolbar actions with quick keys that allow you to change to the - next/previous function for the side that has focus as indicated by the pink border.
+The following toolbar options are available:
-
-- -
-
++- -+
Add To Existing Comparison
Allows the user to add functions to the current comparison window. When + selected, the following table containing all functions in the current program is + displayed:
++
++
Select the functions to be added and hit the OK + button; the selected functions will be available in both the left and right + sides of the comparison window. +
+ ++
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
Navigates to the next available function in the selection pulldown
+ ++
Go To Previous Function
Navigates to the previous available function in the selection pulldown
+ ++
The Remove and Go To actions described + above will operate on the comparison panel that has focus, identified by the + pink border.
Multi-Function Comparison Actions
- ---There are two additional actions that are available when you are comparing more than - just two functions. The following describes them.
- -Compare The Next Function
- --- -The Function Comparison window has an icon (
- -) on the tool bar to - change the function being compared to the next one in the list for the side that - currently has focus.
-
- -- Make sure the side (left or right) has focus for the function you want to change to - the next one. The side with focus will have the pink border around it. It if doesn't, - you can mouse click in the other side to get the focused side to change or you can use - the Tab key to move the focus until it moves the pink border around the correct - side.
- -- Right mouse click and select the
-Compare The Next Function option, OR select - the
- button on the tool bar.
The function should change to the next one available in the list from the choice - component. The differences will be recomputed for the new function comparison that is now - displayed. If the currently displayed function is at the end of the list, a message will - display in the tool's status area indicating there isn't a next function.
-Compare The Previous Function
- ---The Function Comparison window has an icon (
- -) on the tool bar to - change the function being compared to the previous one in the list for the side that - currently has focus.
-
- -- Make sure the side (left or right) has focus for the function you want to change - to the previous one. The side with focus will have the pink border around it. It if - doesn't, you can mouse click in the other side to get the focused side to change or - you can use the Tab key to move the focus until it moves the pink border around the - correct side.
- -- Right mouse click and select the
-Compare The Previous Function option, OR - select the
button on the tool bar.
The function should change to the previous one available in the list from the choice - component. The differences will be recomputed for the new function comparison that is - now displayed. If the currently displayed function is at the beginning of the list, a - message will display in the tool's status area indicating there isn't a previous - function.
-Other Function Comparison Actions
The following are additional actions that are available in the Function Comparison @@ -582,9 +518,7 @@
Provided By: FunctionComparisonPlugin
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png new file mode 100644 index 0000000000..cf0ba976e9 Binary files /dev/null and b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/AddToComparisonIcon.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/AddToComparisonIcon.png new file mode 100644 index 0000000000..c56b0061c7 Binary files /dev/null and b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/AddToComparisonIcon.png differ 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 572c49532a..aa6e53c0fa 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/help/help/topics/FunctionComparison/images/NavNextIcon.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/NavNextIcon.png new file mode 100644 index 0000000000..b35164f0e0 Binary files /dev/null and b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/NavNextIcon.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/NavPreviousIcon.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/NavPreviousIcon.png new file mode 100644 index 0000000000..228e41894e Binary files /dev/null and b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/NavPreviousIcon.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/RemoveFromComparisonIcon.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/RemoveFromComparisonIcon.png new file mode 100644 index 0000000000..e6d8d2c96f Binary files /dev/null and b/Ghidra/Features/Base/src/main/help/help/topics/FunctionComparison/images/RemoveFromComparisonIcon.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm b/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm index b5f09d3c3f..1876141aa5 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm @@ -42,20 +42,24 @@ on the tool bar to make a selection in the Code Browser. To make a selection,Compare Selected Functions-The Functions window has an icon (
+) -on the tool bar to compare the functions whose rows are currently selected in the table. -
The Functions window has an icon (
+which contains a star +
) on the tool bar that allows users to compare +the functions currently selected in the table.
+
Note that selecting this +will always create a new comparison. If you have an existing comparison +and wish to add functions to it, you must initiate that directly from the +existing comparison window itself.
To create a new comparison:
-
-- Select the functions you want to compare from the table in the Functions window. - You must select two or more rows in the table.
-- Right mouse click and select the
+- Compare Selected Functions option, OR select the
- button on the tool bar.
- Select the functions you want to compare from the table in the Functions window. + You must select one or more rows in the table.
+- Right mouse click and select the Compare Selected Functions option, + OR click the create-comparison icon on the tool bar.
This will display a -Function Comparison -window which allows the functions to be viewed -side by side.
+ +The resulting Function Comparison +window will allow the functions to be viewed side by side.
+
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/images/FunctionWindow.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/images/FunctionWindow.png index e15e3593c7..b66a38580e 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/images/FunctionWindow.png and b/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/images/FunctionWindow.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsAction.java deleted file mode 100644 index 2cd2242edb..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsAction.java +++ /dev/null @@ -1,86 +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 java.util.ArrayList; - -import docking.ActionContext; -import docking.action.MenuData; -import ghidra.app.context.*; -import ghidra.program.model.listing.*; -import ghidra.program.util.ProgramSelection; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; - -/** - * An action that displays a function comparison panel for the two functions that are selected - * in the program. - */ -public class CompareFunctionsAction extends ProgramContextAction { - - FunctionComparisonPlugin functionComparisonPlugin; - - /** - * Constructs an action for displaying a panel that allows the user to compare functions. - * @param plugin the plugin that owns this action. - */ - CompareFunctionsAction(FunctionComparisonPlugin plugin) { - super("Compare Two Functions", plugin.getName()); - functionComparisonPlugin = plugin; - - // TODO no icon for now, while this action is at the top-level menu. When we put it in - // its final resting place, we can put the icon back. - // ImageIcon icon = ResourceManager.loadImage("images/page_white_c.png"); - setPopupMenuData(new MenuData(new String[] { "Compare Selected Functions..." }, null, - FunctionComparisonPlugin.FUNCTION_MENU_SUBGROUP, MenuData.NO_MNEMONIC, - "Z_End" /* See the FunctionPlugin for this value */)); - - setHelpLocation(new HelpLocation("FunctionComparison", "Compare_Selected_Functions")); - } - - @Override - public boolean isAddToPopup(ActionContext context) { - return (context instanceof ListingActionContext); - } - - @Override - protected boolean isEnabledForContext(ProgramActionContext context) { - return (context instanceof ListingActionContext); - } - - @Override - public void actionPerformed(ProgramActionContext context) { - - if (context instanceof ListingActionContext) { - ListingActionContext listingContext = (ListingActionContext) context; - ProgramSelection selection = listingContext.getSelection(); - Program program = listingContext.getProgram(); - FunctionManager functionManager = program.getFunctionManager(); - ArrayList
source
function may be associated with one
+ * or more target
functions.
+ *
+ * This is the basic unit for the
+ * {@link FunctionComparisonModel function comparison data model}
+ */
+public class FunctionComparison implements Comparable
+ * Note that the target list is a {@link Set}, so there will only ever
+ * be at most one entry that matches the given function
+ *
+ * @param function the function to remove
+ */
+ public void removeTarget(Function function) {
+ targets.remove(function);
+ }
+
+ /**
+ * Removes all targets from the comparison
+ */
+ public void clearTargets() {
+ targets.clear();
+ }
+
+ /**
+ * Ensures that FunctionComparison objects are always ordered according
+ * to the source function name
+ */
+ @Override
+ public int compareTo(FunctionComparison o) {
+ if (o == null) {
+ return 1;
+ }
+ return getSource().getName().compareTo(o.getSource().getName());
+ }
+
+ /**
+ * Forces an ordering on {@link Function} objects by name. This is
+ * to ensure that the list of targets is kept in sorted order at all times.
+ */
+ class FunctionComparator implements Comparator
+ * Note: This is strictly for a one-to-one comparison; if multiple items are to
+ * be compared, use a {@link MultiFunctionComparisonPanel}
*/
public class FunctionComparisonPanel extends JPanel implements ChangeListener {
+ private FunctionComparisonData leftComparisonData;
+ private FunctionComparisonData rightComparisonData;
+
private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonPanel.TITLE;
private static final String COMPARISON_VIEW_DISPLAYED = "COMPARISON_VIEW_DISPLAYED";
private static final String CODE_COMPARISON_LOCK_SCROLLING_TOGETHER =
@@ -71,55 +74,606 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
private static final String DUAL_SCROLLING_HELP_TOPIC = "FunctionComparison";
private JTabbedPane tabbedPane;
- private HashMap
+ * The underlying data backing the comparison provider is managed by the
+ * {@link FunctionComparisonService}.
*/
//@formatter:off
@PluginInfo(
@@ -36,36 +48,37 @@ import ghidra.program.model.listing.Program;
packageName = CorePluginPackage.NAME,
category = PluginCategoryNames.DIFF,
shortDescription = "Compare Functions",
- description = "This plugin provides actions that allow you to compare two or more functions with each other.",
- eventsConsumed = { ProgramClosedPluginEvent.class }
+ description = "Allows users to compare two or more functions",
+ servicesProvided = { FunctionComparisonService.class },
+ eventsConsumed = { ProgramSelectionPluginEvent.class, ProgramActivatedPluginEvent.class,
+ ProgramClosedPluginEvent.class }
)
//@formatter:on
-public class FunctionComparisonPlugin extends ProgramPlugin implements DomainObjectListener {
+public class FunctionComparisonPlugin extends ProgramPlugin
+ implements DomainObjectListener, FunctionComparisonService {
public final static String FUNCTION_MENU_SUBGROUP = "Function";
static final String MENU_PULLRIGHT = "CompareFunctions";
static final String POPUP_MENU_GROUP = "CompareFunction";
+
private FunctionComparisonProviderManager functionComparisonManager;
/**
- * Creates a plugin that provides actions for comparing functions.
- * @param tool the tool that owns this plugin.
+ * Constructor
+ *
+ * @param tool the tool that owns this plugin
*/
public FunctionComparisonPlugin(PluginTool tool) {
super(tool, true, true);
-
functionComparisonManager = new FunctionComparisonProviderManager(this);
-
tool.setMenuGroup(new String[] { MENU_PULLRIGHT }, POPUP_MENU_GROUP);
}
@Override
protected void init() {
- createActions();
- }
-
- private void createActions() {
- tool.addAction(new CompareFunctionsAction(this));
+ CompareFunctionsAction compareFunctionsAction =
+ new CompareFunctionsFromListingAction(tool, getName());
+ tool.addAction(compareFunctionsAction);
}
@Override
@@ -85,18 +98,75 @@ public class FunctionComparisonPlugin extends ProgramPlugin implements DomainObj
}
/**
- * Displays a panel for comparing the specified functions.
- * @param functions the functions that are used to populate both the left and right side
- * of the function comparison panel.
+ * Overridden to listen for two event types:
+ *
+ * Throughout this class the terms
+ * Note: The custom renderer is used so the name of the program associated
+ * with each function can be displayed in the combo box; this is necessary
+ * since a combo box may show functions from any number of programs, and
+ * the default is to simply show the function name
+ * Note: The custom renderer is used so the name of the program associated
+ * with each function can be displayed in the combo box; this is necessary
+ * since a combo box may show functions from any number of programs, and
+ * the default is to simply show the function name
+ * Each CodeComparisonPanel can extend this class in order to provide this action
+ * using its context
*/
public abstract class AbstractApplyFunctionSignatureAction extends DockingAction {
@@ -42,9 +43,9 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
private static final String ACTION_NAME = "Apply Function Signature To Other Side";
/**
- * Constructor for the action that applies a function signature from one side of a code
- * comparison panel to the other.
- * @param owner the owner of this action.
+ * Constructor
+ *
+ * @param owner the owner of this action
*/
public AbstractApplyFunctionSignatureAction(String owner) {
super(ACTION_NAME, owner);
@@ -97,13 +98,25 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
}
}
+ /**
+ * Returns true if the comparison panel opposite the one with focus,
+ * is read-only
+ *
+ * eg: if the right-side panel has focus, and the left-side panel is
+ * read-only, this will return true
+ *
+ * @param codeComparisonPanel the comparison panel
+ * @return true if the non-focused panel is read-only
+ */
protected boolean hasReadOnlyNonFocusedSide(
CodeComparisonPanel extends FieldPanelCoordinator> codeComparisonPanel) {
Function leftFunction = codeComparisonPanel.getLeftFunction();
Function rightFunction = codeComparisonPanel.getRightFunction();
+
if (leftFunction == null || rightFunction == null) {
return false; // Doesn't have a function on both sides.
}
+
boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
Program leftProgram = leftFunction.getProgram();
Program rightProgram = rightFunction.getProgram();
@@ -111,11 +124,22 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
(leftHasFocus && rightProgram.getDomainFile().isReadOnly());
}
+ /**
+ * Attempts to change the signature of a function to that of another
+ * function
+ *
+ * @param provider the parent component provider
+ * @param destinationFunction the function to change
+ * @param sourceFunction the function to copy
+ * @return true if the operation was successful
+ */
protected boolean updateFunction(ComponentProvider provider, Function destinationFunction,
Function sourceFunction) {
+
Program program = destinationFunction.getProgram();
int txID = program.startTransaction(ACTION_NAME);
boolean commit = false;
+
try {
FunctionUtility.updateFunction(destinationFunction, sourceFunction);
commit = true;
@@ -129,6 +153,7 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
finally {
program.endTransaction(txID, commit);
}
+
return commit;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsAction.java
new file mode 100644
index 0000000000..f1fbd8a4ab
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsAction.java
@@ -0,0 +1,110 @@
+/* ###
+ * 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.InputEvent;
+import java.util.Set;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+import docking.ActionContext;
+import docking.action.*;
+import ghidra.app.services.FunctionComparisonService;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.model.listing.Function;
+import ghidra.util.HelpLocation;
+import resources.MultiIcon;
+import resources.ResourceManager;
+import resources.icons.ScaledImageIconWrapper;
+import resources.icons.TranslateIcon;
+
+/**
+ * Creates a new comparison between a set of functions, launching a new
+ * comparison provider in the process
+ *
+ * This class is abstract to force implementors to supply the source of the
+ * functions (may be the listing, a table, etc...)
+ *
+ * @see {@link #getSelectedFunctions(ActionContext) getSelectedFunctions}
+ */
+public abstract class CompareFunctionsAction extends DockingAction {
+
+ protected FunctionComparisonService comparisonService;
+
+ private static final ImageIcon COMPARISON_ICON =
+ ResourceManager.loadImage("images/page_white_c.png");
+ private static final Icon NEW_ICON = ResourceManager.loadImage("images/bullet_star.png");
+ private static final Icon SCALED_NEW_ICON = new ScaledImageIconWrapper(NEW_ICON, 16, 16);
+ private static final Icon TRANSLATED_NEW_ICON = new TranslateIcon(SCALED_NEW_ICON, 4, -4);
+ private static final Icon CREATE_NEW_COMPARISON_ICON =
+ new MultiIcon(COMPARISON_ICON, TRANSLATED_NEW_ICON);
+ private static final String CREATE_COMPARISON_GROUP = "A9_CreateComparison";
+
+ /**
+ * Constructor
+ *
+ * @param tool the plugin tool
+ * @param owner the action owner (usually the plugin name)
+ */
+ public CompareFunctionsAction(PluginTool tool, String owner) {
+ super("Compare Functions", owner, KeyBindingType.SHARED);
+ this.comparisonService = tool.getService(FunctionComparisonService.class);
+ setActionAttributes();
+ }
+
+ @Override
+ public void actionPerformed(ActionContext context) {
+ Set
+ * If this action is to be used with a different type of table, simply
+ * extend this class and override {@link #getSelectedFunctions(ActionContext) getSelectedFunctions}
+ * and {@link #isModelSupported(ActionContext) isModelSupported} as-needed.
+ */
+public class CompareFunctionsFromFunctionTableAction extends CompareFunctionsAction {
+
+ /**
+ * Constructor
+ *
+ * @param tool the plugin tool
+ * @param owner the action owner
+ */
+ public CompareFunctionsFromFunctionTableAction(PluginTool tool, String owner) {
+ super(tool, owner);
+ }
+
+ @Override
+ public boolean isAddToPopup(ActionContext context) {
+ return isModelSupported(context);
+ }
+
+ @Override
+ public boolean isValidContext(ActionContext context) {
+ return isModelSupported(context);
+ }
+
+ @Override
+ protected Set
+ * By default this method verifies that the table in question is a
+ * {@link FunctionTableModel}. If another table is being used, override this
+ * method.
+ *
+ * @param context the action context
+ * @return true if the context is a function table model
+ */
+ protected boolean isModelSupported(ActionContext context) {
+ if (!(context.getContextObject() instanceof GhidraTable)) {
+ return false;
+ }
+ GhidraTable table = (GhidraTable) context.getContextObject();
+ return table.getModel() instanceof FunctionTableModel;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsFromListingAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsFromListingAction.java
new file mode 100644
index 0000000000..2f29db3bb4
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsFromListingAction.java
@@ -0,0 +1,66 @@
+/* ###
+ * 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.util.HashSet;
+import java.util.Set;
+
+import docking.ActionContext;
+import ghidra.app.context.ListingActionContext;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.model.listing.*;
+import ghidra.program.util.ProgramSelection;
+
+/**
+ * Creates a comparison between a set of functions extracted from selections
+ * in the listing
+ */
+public class CompareFunctionsFromListingAction extends CompareFunctionsAction {
+
+ /**
+ * Constructor
+ *
+ * @param tool the plugin tool
+ * @param owner the action owner
+ */
+ public CompareFunctionsFromListingAction(PluginTool tool, String owner) {
+ super(tool, owner);
+ }
+
+ @Override
+ public boolean isAddToPopup(ActionContext actionContext) {
+ return actionContext instanceof ListingActionContext;
+ }
+
+ @Override
+ public boolean isValidContext(ActionContext context) {
+ return context instanceof ListingActionContext;
+ }
+
+ @Override
+ protected Set
+ * This model is intended to be used by the {@link FunctionComparisonProvider}
+ * as the basis for its display. It should never be created manually, and should
+ * only be accessed via the {@link FunctionComparisonService}.
+ *
+ * Note: Subscribers may register to be informed of changes to this model via the
+ * {@link FunctionComparisonModelListener comparison model listener} interface.
+ */
+public class FunctionComparisonModel {
+
+ private List
+ * Note: It is assumed that when using this method, all functions can be
+ * compared with all other functions; meaning each function will be added as
+ * both a source AND a target. To specify a specific source/target
+ * relationship, use {@link #compareFunctions(Function, Function)}.
+ *
+ * @param functions the set of functions to compare
+ */
+ public void compareFunctions(Set
+ * Note that this could be a long-running process if many (thousands)
+ * functions are chosen to compare, hence the monitored task. In practice
+ * this should never be the case, as users will likely not be
+ * comparing more than a handful of functions at any given time.
+ *
+ * @param functions the set of functions to create comparisons for
+ */
+ private void createNewComparisons(Set
+ * eg: Given a set of 3 functions (f1, f2, f3), the comparison dialog will
+ * allow the user to display either f1, f2 or f3 on EITHER side of the
+ * comparison.
+ *
+ * Note that this method will always create a new provider; if you want to
+ * add functions to an existing comparison, use
+ * {@link #compareFunctions(Set, FunctionComparisonProvider) this}
+ * variant that takes a provider.
+ *
+ * @param functions the functions to compare
+ * @return the new comparison provider
+ */
+ public FunctionComparisonProvider compareFunctions(Set
+ * Note that this will always create a new provider; if you want to add
+ * functions to an existing comparison, use
+ * {@link #compareFunctions(Function, Function, FunctionComparisonProvider) this}
+ * variant that takes a provider.
+ *
+ * @param source a function in the comparison
+ * @param target a function in the comparison
+ * @return the comparison provider
+ */
+ public FunctionComparisonProvider compareFunctions(Function source,
+ Function target);
+
+ /**
+ * Creates a comparison between a set of functions, adding them to the
+ * given comparison provider. Each function in the given set will be added
+ * to both sides of the comparison, allowing users to compare any functions
+ * in the existing provider with the new set.
+ *
+ * @see #compareFunctions(Set)
+ * @param functions the functions to compare
+ * @param provider the provider to add the comparisons to
+ */
+ public void compareFunctions(Set
+ * NOTE: ALL CodeComparisonPanel CLASSES MUST END IN
+ *
- * This panel is intended to discover and provide multiple different types of
- * CodeComparisonPanels. Each type of {@link CodeComparisonPanel} will be in its own tab.
- * The user can only view one type of CodeComparisonPanel at a time, but can select the
- * currently displayed one.
- *
+ * A panel for displaying {@link Function functions}, {@link Data data}, or
+ * {@link AddressSet address sets} side-by-side for comparison purposes
+ * source
and target
+ * are used when referencing functions. This is because the model that backs
+ * this panel maintains a relationship between the functions being compared
+ * such that each source function can only be compared to a specific set
+ * of target functions. For all practical purposes, the source functions
+ * appear in the left-side panel and targets appear on the right.
+ *
*/
-public class MultiFunctionComparisonPanel extends FunctionChoiceComparisonPanel {
+public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
+
+ /** Functions that will show up on the left side of the panel */
+ private JComboBoxfunctionsL
and functionsR
match the functions
- * that can be displayed for comparison in the left and right side of this panel.
+ * Constructor
*
- * @param functionsL the functions to check against those used to populate the left side
- * @param functionsR the functions to check against those used to populate the right side
- * @return true if functionsL and functionsR match the functions that can be displayed by
- * this panel.
+ * @param provider the comparison provider associated with this panel
+ * @param tool the active plugin tool
*/
- boolean matchesTheseFunctions(Function[] functionsL, Function[] functionsR) {
- return Arrays.equals(getLeftFunctions(), getSortedFunctions(functionsL)) &&
- Arrays.equals(getRightFunctions(), getSortedFunctions(functionsR));
+ public MultiFunctionComparisonPanel(MultiFunctionComparisonProvider provider,
+ PluginTool tool) {
+ super(provider, tool, null, null);
+
+ JPanel choicePanel = new JPanel(new GridLayout(1, 2));
+ choicePanel.add(createSourcePanel());
+ choicePanel.add(createTargetPanel());
+ add(choicePanel, BorderLayout.NORTH);
+
+ // 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));
+ }
+
+ /**
+ * Clears out the source and targets lists and reloads them to
+ * ensure that they reflect the current state of the data model. Any
+ * currently-selected list items will be restored after the lists
+ * are reloaded.
+ */
+ @Override
+ public void reload() {
+ SwingUtilities.invokeLater(() -> {
+ reloadSourceList();
+ Function selectedSource = (Function) sourceFunctionsCBModel.getSelectedItem();
+ reloadTargetList(selectedSource);
+ loadFunctions(selectedSource, (Function) targetFunctionsCBModel.getSelectedItem());
+
+ updateTabText();
+
+ // 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");
+ });
+ }
+
+ /**
+ * Returns the combo box (source or target) which has focus
+ *
+ * @return the focused component
+ */
+ public JComboBox
+ * eg: "init (notepad)"
+ *
+ * @return the source panel
+ */
+ private JPanel createSourcePanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ sourceFunctionsCB = new JComboBox<>();
+ sourceFunctionsCBModel = new DefaultComboBoxModel<>();
+ sourceFunctionsCB.setModel(sourceFunctionsCBModel);
+ sourceFunctionsCB.setRenderer(new FunctionListCellRenderer());
+ sourceFunctionsCB.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() != ItemEvent.SELECTED) {
+ 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());
+
+ updateTabText();
+
+ // Fire a notification to update the UI state; without this the
+ // actions would not be properly enabled/disabled
+ tool.contextChanged(provider);
+ }
+ });
+
+ panel.add(sourceFunctionsCB, BorderLayout.CENTER);
+ return panel;
+ }
+
+ /**
+ * Creates the panel for the target functions selection components
+ *
+ * eg: "init (notepad)"
+ *
+ * @return the target panel
+ */
+ private JPanel createTargetPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ targetFunctionsCB = new JComboBox<>();
+ targetFunctionsCBModel = new DefaultComboBoxModel<>();
+ targetFunctionsCB.setModel(targetFunctionsCBModel);
+ targetFunctionsCB.setRenderer(new FunctionListCellRenderer());
+ targetFunctionsCB.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() != ItemEvent.SELECTED) {
+ return;
+ }
+
+ Function selected = (Function) targetFunctionsCBModel.getSelectedItem();
+ loadFunctions((Function) sourceFunctionsCBModel.getSelectedItem(), selected);
+
+ updateTabText();
+
+ // Fire a notification to update the UI state; without this the
+ // actions would not be properly enabled/disabled
+ tool.contextChanged(provider);
+ }
+ });
+
+ panel.add(targetFunctionsCB, BorderLayout.CENTER);
+ return panel;
+ }
+
+ /**
+ * Cell renderer for combo boxes that changes the default display to show
+ * both the function name and the program it comes from
+ */
+ private class FunctionListCellRenderer extends DefaultListCellRenderer {
+
+ @Override
+ public Component getListCellRendererComponent(JList> list, Object value, int index,
+ boolean isSelected, boolean cellHasFocus) {
+
+ if (value == null) {
+ // It's possible during a close program operation to have this
+ // renderer called with a null value. If so, we can't get the
+ // function so just use the default renderer.
+ return super.getListCellRendererComponent(list, value, index, isSelected,
+ cellHasFocus);
+ }
+
+ Function f = (Function) value;
+ String text = f.getName() + " (" + f.getProgram().getName() + ")";
+ return super.getListCellRendererComponent(list, text, index, isSelected,
+ cellHasFocus);
+ }
}
}
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
new file mode 100644
index 0000000000..0d57424345
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/MultiFunctionComparisonProvider.java
@@ -0,0 +1,76 @@
+/* ###
+ * 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 docking.action.DockingAction;
+import ghidra.app.plugin.core.functioncompare.actions.*;
+import ghidra.framework.plugintool.Plugin;
+
+/**
+ * Provider for a {@link MultiFunctionComparisonPanel}. This differs from the
+ * base comparison provider in that it has additional actions that are
+ * appropriate for managing multiple comparisons (add, remove, etc...).
+ */
+public class MultiFunctionComparisonProvider extends FunctionComparisonProvider {
+
+ /**
+ * Constructor
+ *
+ * @param plugin the parent plugin
+ */
+ public MultiFunctionComparisonProvider(Plugin plugin) {
+ super(plugin, "functioncomparisonprovider", plugin.getName());
+ }
+
+ @Override
+ public FunctionComparisonPanel getComponent() {
+ if (functionComparisonPanel == null) {
+ functionComparisonPanel = new MultiFunctionComparisonPanel(this, tool);
+ }
+ return functionComparisonPanel;
+ }
+
+ @Override
+ boolean isEmpty() {
+ return model.getSourceFunctions().isEmpty();
+ }
+
+ @Override
+ protected void initFunctionComparisonPanel() {
+ super.initFunctionComparisonPanel();
+
+ DockingAction nextFunctionAction = new NextFunctionAction(this);
+ DockingAction previousFunctionAction = new PreviousFunctionAction(this);
+ DockingAction removeFunctionsAction = new RemoveFunctionsAction(this);
+ DockingAction openFunctionTableAction = getOpenFunctionTableAction();
+
+ addLocalAction(nextFunctionAction);
+ addLocalAction(previousFunctionAction);
+ addLocalAction(removeFunctionsAction);
+ addLocalAction(openFunctionTableAction);
+ }
+
+ /**
+ * Returns an action that opens a table from which users may select
+ * functions for comparison. By default this returns an action that will
+ * open a standard function table, but may be overridden as-needed.
+ *
+ * @return the docking action
+ */
+ protected DockingAction getOpenFunctionTableAction() {
+ return new OpenFunctionTableAction(tool, this);
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/AbstractApplyFunctionSignatureAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/AbstractApplyFunctionSignatureAction.java
similarity index 83%
rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/AbstractApplyFunctionSignatureAction.java
rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/AbstractApplyFunctionSignatureAction.java
index 0617ac54d1..9464f12602 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/AbstractApplyFunctionSignatureAction.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/AbstractApplyFunctionSignatureAction.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.app.plugin.core.functioncompare;
+package ghidra.app.plugin.core.functioncompare.actions;
import docking.ActionContext;
import docking.ComponentProvider;
@@ -30,10 +30,11 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
/**
- * Action that applies the signature of the function in the currently active side of a
- * code comparison panel to the function in the other side of the panel.
- *
Each CodeComparisonPanel can extend this class in order to provide this action
- * using its context.
+ * Applies the signature of the function in the currently active side of a
+ * code comparison panel to the function in the other side of the panel
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * If this model already contains a comparison for a given function
+ * (meaning the model contains a comparison with the function as the
+ * source) then that function is skipped.
+ *
- * NOTE: ALL CodeComparisonPanel CLASSES MUST END IN "CodeComparisonPanel".
- * If not, the ClassSearcher will not find them.
+ * The CodeComparisonPanel class should be extended by any class that is to be
+ * discovered by the {@link FunctionComparisonPanel} class and included as a
+ * form of comparing two sections of code within the same or different programs
+ * CodeComparisonPanel
so they are discoverable by the
+ * {@link ClassSearcher}
*/
public abstract class CodeComparisonPanel