GP-4251: Added Function Compare action to the Version Tracking main

match table and associated match tables.
This commit is contained in:
isabella3412 2024-01-30 22:48:58 +00:00 committed by Ryan Kurtz
parent 77aa79caf1
commit f982e9bba5
12 changed files with 494 additions and 67 deletions

View file

@ -383,11 +383,19 @@
<BR>
<BR>
<P align="left"><A name="Match_Table_Settings"></A>The <b>Settings</b> <IMG src="images/settings16.gif" border="0">
action will bring up the version tracking accept and apply options.</P>
<P align="left"><A name="Match_Table_Compare_Functions"></A>The <b>Compare Functions</b>
action allows users to visually compare selected matched functions. To initiate this action,
select one or more matches from the Version Tracking Matches Table, then choose
<b>Compare Functions</b> from the pop up menu. This will open a new
<A href="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</A>
table containing a list of source functions and a list of destination functions.
The user can choose one from each list at a time to visually compare to each other.
Note: You cannot compare Data matches or External Functions using this action, so
if you select either of these as matches they will not be populated in the table.</P>
</BLOCKQUOTE>
<H2><A name="Match_Filters"></A>Match Filters</H2>

View file

@ -109,6 +109,24 @@
</BLOCKQUOTE>
<H2><A name="Related_Match_Actions"></A>Actions</H2>
<BLOCKQUOTE>
<H3><A name="Compare_Functions">Compare Functions</A></H3>
<BLOCKQUOTE>
<P>To initiate this action, navigate to the <B>Related Matches</B> table in either the
Destination Tool or the Source Tool. Select one or more matches in the table, then choose
<B>Compare Functions</B> from the right-mouse menu. This action will open a new
<A HREF="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</A>
table. The left side of the window will display the function in the current tool location.
The right side of the window will contain a pull-down list with the selected matched
function(s) from the other program. This allows a user to visually compare the function at
the current tool location with the selected matched function(s) from the other program.</P>
<P>Note: You cannot compare Data matches or External Functions using this action, so
if you select either of these as matches they will not be populated in the table.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<BLOCKQUOTE>
<H3><A name="Select_Same_Match_In_Version_Tracking_Matches_Table">Select Match in VT

View file

@ -16,8 +16,8 @@
package ghidra.feature.vt.gui.plugin;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import javax.swing.Icon;
import javax.swing.JFrame;
@ -42,6 +42,7 @@ import ghidra.framework.model.*;
import ghidra.framework.options.Options;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginException;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.framework.preferences.Preferences;
import ghidra.program.model.address.AddressSetView;
@ -119,6 +120,7 @@ public class VTPlugin extends Plugin {
new ImpliedMatchAssociationHook(controller);
initializeOptions();
}
private DockingActionIf getToolAction(String actionName) {
@ -141,9 +143,37 @@ public class VTPlugin extends Plugin {
@Override
protected void init() {
addCustomPlugins();
maybeShowHelp();
}
private void addCustomPlugins() {
List<String> names = new ArrayList<>(
List.of("ghidra.app.plugin.core.functioncompare.FunctionComparisonPlugin"));
List<Plugin> plugins = tool.getManagedPlugins();
Set<String> existingNames =
plugins.stream().map(c -> c.getName()).collect(Collectors.toSet());
// Note: we check to see if the plugins we want to add have already been added to the tool.
// We should not needed to do this, but once the tool has been saved with the plugins added,
// they will get added again the next time the tool is loaded. Adding this check here seems
// easier than modifying the default to file to load the plugins, since the amount of xml
// required for that is non-trivial.
try {
for (String className : names) {
if (!existingNames.contains(className)) {
tool.addPlugin(className);
}
}
}
catch (PluginException e) {
Msg.error(this, "Unable to load plugin", e);
}
}
private void maybeShowHelp() {
if (SystemUtilities.isInDevelopmentMode() || SystemUtilities.isInTestingMode()) {
return; // don't show help for dev mode

View file

@ -658,7 +658,7 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
*
* @return The source tool from the VT session.
*/
PluginTool getSourceTool() {
public PluginTool getSourceTool() {
return sourceTool;
}

View file

@ -15,9 +15,12 @@
*/
package ghidra.feature.vt.gui.provider.matchtable;
import java.util.ArrayList;
import java.util.List;
import docking.DefaultActionContext;
import ghidra.feature.vt.api.main.VTAssociation;
import ghidra.feature.vt.api.main.VTAssociationType;
import ghidra.feature.vt.api.main.VTMatch;
import ghidra.feature.vt.api.main.VTSession;
@ -37,7 +40,25 @@ public class VTMatchContext extends DefaultActionContext {
return selectedMatches;
}
public int getSelectedRowCount() {
return selectedMatches.size();
}
public VTSession getSession() {
return session;
}
public List<VTMatch> getFunctionMatches() {
List<VTMatch> functionMatches = new ArrayList<>();
for (VTMatch match : selectedMatches) {
VTAssociation association = match.getAssociation();
if (association.getType() != VTAssociationType.FUNCTION) {
continue;
}
functionMatches.add(match);
}
return functionMatches;
}
}

View file

@ -15,39 +15,170 @@
*/
package ghidra.feature.vt.gui.provider.matchtable;
import static ghidra.feature.vt.api.impl.VTEvent.*;
import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.*;
import static ghidra.feature.vt.gui.plugin.VTPlugin.*;
import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
import static ghidra.framework.model.DomainObjectEvent.*;
import static ghidra.feature.vt.api.impl.VTEvent.ASSOCIATION_STATUS_CHANGED;
import static ghidra.feature.vt.api.impl.VTEvent.MATCH_SET_ADDED;
import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.MAINTAIN_SELECTED_ROW_INDEX;
import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.MAINTAIN_SELECTED_ROW_VALUE;
import static ghidra.feature.vt.gui.actions.TableSelectionTrackingState.NO_SELECTION_TRACKING;
import static ghidra.feature.vt.gui.plugin.VTPlugin.FILTERED_ICON;
import static ghidra.feature.vt.gui.plugin.VTPlugin.UNFILTERED_ICON;
import static ghidra.feature.vt.gui.util.VTOptionDefines.ACCEPT_MATCH_OPTIONS_NAME;
import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_DATA_NAME_ON_ACCEPT;
import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_FUNCTION_NAME_ON_ACCEPT;
import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_IMPLIED_MATCHES_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.APPLY_MARKUP_OPTIONS_NAME;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_CREATE_IMPLIED_MATCH;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_DATA_CORRELATOR;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_DUPLICATE_FUNCTION_CORRELATOR;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_EXACT_FUNCTION_CORRELATORS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_IMPLIED_MATCH_CORRELATOR;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_OPTIONS_NAME;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_REFERENCE_CORRELATORS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.AUTO_VT_SYMBOL_CORRELATOR;
import static ghidra.feature.vt.gui.util.VTOptionDefines.CALLING_CONVENTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.CALL_FIXUP;
import static ghidra.feature.vt.gui.util.VTOptionDefines.CREATE_IMPLIED_MATCHES_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DATA_CORRELATOR_MIN_LEN_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DATA_MATCH_DATA_TYPE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_CALLING_CONVENTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_CALL_FIXUP;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_DATA_MATCH_DATA_TYPE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_EOL_COMMENTS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_FUNCTION_NAME;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_FUNCTION_RETURN_TYPE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_FUNCTION_SIGNATURE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_HIGHEST_NAME_PRIORITY;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_IGNORE_EXCLUDED_MARKUP_ITEMS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_IGNORE_INCOMPLETE_MARKUP_ITEMS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_INLINE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_LABELS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_NO_RETURN;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_COMMENTS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_DATA_TYPES;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_NAMES;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PLATE_COMMENTS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_POST_COMMENTS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_PRE_COMMENTS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_REPEATABLE_COMMENTS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_VAR_ARGS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DISPLAY_APPLY_MARKUP_OPTIONS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.DUPE_FUNCTION_CORRELATOR_MIN_LEN_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.END_OF_LINE_COMMENT;
import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_CORRELATOR_MIN_LEN_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_NAME;
import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_RETURN_TYPE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_SIGNATURE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.HIGHEST_NAME_PRIORITY;
import static ghidra.feature.vt.gui.util.VTOptionDefines.IGNORE_EXCLUDED_MARKUP_ITEMS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.IGNORE_INCOMPLETE_MARKUP_ITEMS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.INLINE;
import static ghidra.feature.vt.gui.util.VTOptionDefines.LABELS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.MAX_CONFLICTS_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.MIN_VOTES_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.NO_RETURN;
import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_COMMENTS;
import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_DATA_TYPES;
import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_NAMES;
import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY;
import static ghidra.feature.vt.gui.util.VTOptionDefines.PLATE_COMMENT;
import static ghidra.feature.vt.gui.util.VTOptionDefines.POST_COMMENT;
import static ghidra.feature.vt.gui.util.VTOptionDefines.PRE_COMMENT;
import static ghidra.feature.vt.gui.util.VTOptionDefines.REF_CORRELATOR_MIN_CONF_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.REF_CORRELATOR_MIN_SCORE_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.REPEATABLE_COMMENT;
import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_DUPE_FUNCTION_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_DATA_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_FUNCTION_BYTES_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_FUNCTION_INST_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_EXACT_SYMBOL_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.RUN_REF_CORRELATORS_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.SYMBOL_CORRELATOR_MIN_LEN_OPTION;
import static ghidra.feature.vt.gui.util.VTOptionDefines.VAR_ARGS;
import static ghidra.framework.model.DomainObjectEvent.RESTORED;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import docking.*;
import docking.widgets.table.*;
import docking.ActionContext;
import docking.DockingWindowManager;
import docking.WindowPosition;
import docking.action.builder.ActionBuilder;
import docking.widgets.table.AbstractSortedTableModel;
import docking.widgets.table.GTable;
import docking.widgets.table.RowObjectSelectionManager;
import docking.widgets.table.RowObjectTableModel;
import docking.widgets.table.SelectionManager;
import docking.widgets.table.threaded.ThreadedTableModel;
import generic.theme.GIcon;
import ghidra.app.services.FunctionComparisonService;
import ghidra.feature.vt.api.impl.VTEvent;
import ghidra.feature.vt.api.impl.VersionTrackingChangeRecord;
import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.gui.actions.*;
import ghidra.feature.vt.api.main.VTMarkupItem;
import ghidra.feature.vt.api.main.VTMatch;
import ghidra.feature.vt.api.main.VTSession;
import ghidra.feature.vt.gui.actions.AcceptMatchAction;
import ghidra.feature.vt.gui.actions.ApplyBlockedMatchAction;
import ghidra.feature.vt.gui.actions.ApplyMatchAction;
import ghidra.feature.vt.gui.actions.ChooseMatchTagAction;
import ghidra.feature.vt.gui.actions.ClearMatchAction;
import ghidra.feature.vt.gui.actions.CreateSelectionAction;
import ghidra.feature.vt.gui.actions.EditAllTagsAction;
import ghidra.feature.vt.gui.actions.MatchTableSelectionAction;
import ghidra.feature.vt.gui.actions.RejectMatchAction;
import ghidra.feature.vt.gui.actions.RemoveMatchAction;
import ghidra.feature.vt.gui.actions.RemoveMatchTagAction;
import ghidra.feature.vt.gui.actions.TableSelectionTrackingState;
import ghidra.feature.vt.gui.editors.MatchTagCellEditor;
import ghidra.feature.vt.gui.filters.*;
import ghidra.feature.vt.gui.filters.AncillaryFilterDialogComponentProvider;
import ghidra.feature.vt.gui.filters.Filter;
import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus;
import ghidra.feature.vt.gui.plugin.*;
import ghidra.feature.vt.gui.util.*;
import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.*;
import ghidra.framework.model.*;
import ghidra.feature.vt.gui.filters.FilterDialogModel;
import ghidra.feature.vt.gui.filters.FilterStatusListener;
import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.feature.vt.gui.plugin.VTControllerListener;
import ghidra.feature.vt.gui.plugin.VTPlugin;
import ghidra.feature.vt.gui.plugin.VersionTrackingPluginPackage;
import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.DestinationLabelTableColumn;
import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.SourceLabelTableColumn;
import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.StatusTableColumn;
import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.TagTableColumn;
import ghidra.feature.vt.gui.util.AllTextFilter;
import ghidra.feature.vt.gui.util.FilterIconFlashTimer;
import ghidra.feature.vt.gui.util.MatchInfo;
import ghidra.feature.vt.gui.util.MatchStatusRenderer;
import ghidra.feature.vt.gui.util.VTSymbolRenderer;
import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.EventType;
import ghidra.framework.options.Options;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.model.listing.Function;
import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
@ -60,6 +191,8 @@ import help.HelpService;
public class VTMatchTableProvider extends ComponentProviderAdapter
implements FilterDialogModel<VTMatch>, VTControllerListener {
private static final Icon COMPARISON_ICON = new GIcon("icon.plugin.functioncompare.new");
private static final String TITLE = "Version Tracking Matches";
private static final String TABLE_SELECTION_STATE = "TABLE_SELECTION_STATE";
@ -128,6 +261,42 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
addLocalAction(new CreateSelectionAction(controller));
tableSelectionStateAction = new MatchTableSelectionAction(this);
addLocalAction(tableSelectionStateAction);
new ActionBuilder("Compare Functions", getName()).popupMenuPath("Compare Functions")
.popupMenuGroup("Selection")
.popupMenuIcon(COMPARISON_ICON)
.keyBinding("shift c")
.sharedKeyBinding()
.description("Compares the Function(s) with its remote match")
.helpLocation(
new HelpLocation("VersionTrackingPlugin", "Match_Table_Compare_Functions"))
.withContext(VTMatchContext.class)
.enabledWhen(this::isValidFunctionComparison)
.onAction(this::compareFunctions)
.buildAndInstallLocal(this);
}
private boolean isValidFunctionComparison(VTMatchContext context) {
List<VTMatch> functionMatches = context.getFunctionMatches();
return !functionMatches.isEmpty();
}
private void compareFunctions(VTMatchContext c) {
Set<Function> sourceFunctions = new HashSet<>();
Set<Function> destinationFunctions = new HashSet<>();
List<VTMatch> matches = c.getFunctionMatches();
for (VTMatch match : matches) {
MatchInfo matchInfo = controller.getMatchInfo(match);
Function sourceFunction = matchInfo.getSourceFunction();
sourceFunctions.add(sourceFunction);
Function destinationFunction = matchInfo.getDestinationFunction();
destinationFunctions.add(destinationFunction);
}
FunctionComparisonService service = tool.getService(FunctionComparisonService.class);
service.compareFunctions(sourceFunctions, destinationFunctions);
}
// callback method from the MatchTableSelectionAction
@ -601,12 +770,14 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
@Override
public void optionsChanged(Options options) {
// implemented as ControllerListener. Don't care about options changed right now.
// implemented as ControllerListener. Don't care about options changed right
// now.
}
@Override
public void markupItemSelected(VTMarkupItem markupItem) {
// Do nothing since the matches table doesn't need to respond to the mark-up that is selected.
// Do nothing since the matches table doesn't need to respond to the mark-up
// that is selected.
}
private void initializeOptions() {
@ -828,8 +999,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
}
/**
* Forces a refilter, even though filtering operations may be disabled. The reload
* is necessary since the model contents may have changed
* Forces a refilter, even though filtering operations may be disabled. The
* reload is necessary since the model contents may have changed
*/
@Override
public void forceRefilter() {
@ -891,20 +1062,22 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
}
/**
* A class meant to override the default table selection behavior <b>in special situations</b>.
* A class meant to override the default table selection behavior <b>in special
* situations</b>.
* <p>
* <u>Issue 1:</u> Accepting or applying a match can trigger the match to be filtered out
* of the table. The default SelectionManager does not restore the selection for that item,
* as it knows that the item is gone.
* <u>Issue 1:</u> Accepting or applying a match can trigger the match to be
* filtered out of the table. The default SelectionManager does not restore the
* selection for that item, as it knows that the item is gone.
* <p>
* <u>Issue 2:</u> Accepting or applying a match can trigger the match to be moved due to a
* sort operation after the edit.
* <u>Issue 2:</u> Accepting or applying a match can trigger the match to be
* moved due to a sort operation after the edit.
* <p>
* <u>Desired Behavior:</u> Have the selection restored to the previous location, even if the
* item is moved or removed.
* <u>Desired Behavior:</u> Have the selection restored to the previous
* location, even if the item is moved or removed.
* <p>
* Creating this object will cancel the default behavior. Calling <tt>restoreSelection</tt>
* will set the new selection, depending upon the conditions described above.
* Creating this object will cancel the default behavior. Calling
* <tt>restoreSelection</tt> will set the new selection, depending upon the
* conditions described above.
*/
private class SelectionOverrideMemento {
private final int row;
@ -946,13 +1119,14 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
ListSelectionModel selectionModel = matchesTable.getSelectionModel();
int rowToSelect = row;
if (row > matchesTableModel.getRowCount()) {
// The model has shrunk. Not sure what the best action is?
// The model has shrunk. Not sure what the best action is?
tryToSelectMatch(selectionModel);// this only works if we are tracking by match and not index
return;
}
// At this point the selection model may still believe that its selection is the
// value we are setting. Calling clearSelection() will kick the model. Without the
// value we are setting. Calling clearSelection() will kick the model. Without
// the
// kick, the setSelectionInterval() call we make may ultimately have no effect.
selectionModel.clearSelection();
@ -989,8 +1163,8 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
}
/**
* Override the built-in SelectionManager so that we can respond to the current table
* selection mode.
* Override the built-in SelectionManager so that we can respond to the current
* table selection mode.
*/
private class VTMatchTableSelectionManager extends RowObjectSelectionManager<VTMatch> {
VTMatchTableSelectionManager(JTable table, AbstractSortedTableModel<VTMatch> tableModel) {

View file

@ -15,9 +15,12 @@
*/
package ghidra.feature.vt.gui.provider.onetomany;
import java.util.ArrayList;
import java.util.List;
import docking.DefaultActionContext;
import ghidra.feature.vt.api.main.VTAssociation;
import ghidra.feature.vt.api.main.VTAssociationType;
import ghidra.feature.vt.api.main.VTMatch;
public class VTMatchOneToManyContext extends DefaultActionContext {
@ -32,4 +35,23 @@ public class VTMatchOneToManyContext extends DefaultActionContext {
public List<VTMatch> getSelectedMatches() {
return selectedItems;
}
public int getSelectedRowCount() {
return selectedItems.size();
}
public List<VTMatch> getFunctionMatches() {
List<VTMatch> functionMatches = new ArrayList<>();
for (VTMatch match : selectedItems) {
VTAssociation association = match.getAssociation();
if (association.getType() != VTAssociationType.FUNCTION) {
continue;
}
functionMatches.add(match);
}
return functionMatches;
}
}

View file

@ -15,39 +15,70 @@
*/
package ghidra.feature.vt.gui.provider.onetomany;
import java.awt.*;
import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import docking.ActionContext;
import docking.action.builder.ActionBuilder;
import docking.widgets.label.GDLabel;
import docking.widgets.table.GTable;
import docking.widgets.table.RowObjectTableModel;
import docking.widgets.table.threaded.ThreadedTableModel;
import generic.theme.GColor;
import generic.theme.GIcon;
import ghidra.app.services.FunctionComparisonService;
import ghidra.feature.vt.api.impl.VTEvent;
import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.gui.actions.*;
import ghidra.feature.vt.gui.filters.*;
import ghidra.feature.vt.api.main.VTMarkupItem;
import ghidra.feature.vt.api.main.VTMatch;
import ghidra.feature.vt.api.main.VTSession;
import ghidra.feature.vt.gui.actions.AcceptMatchAction;
import ghidra.feature.vt.gui.actions.ClearMatchAction;
import ghidra.feature.vt.gui.actions.SetVTMatchFromOneToManyAction;
import ghidra.feature.vt.gui.filters.Filter;
import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus;
import ghidra.feature.vt.gui.plugin.*;
import ghidra.feature.vt.gui.filters.FilterDialogModel;
import ghidra.feature.vt.gui.filters.FilterStatusListener;
import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.feature.vt.gui.plugin.VTControllerListener;
import ghidra.feature.vt.gui.plugin.VTPlugin;
import ghidra.feature.vt.gui.plugin.VTSubToolManager;
import ghidra.feature.vt.gui.plugin.VTSubToolManagerListener;
import ghidra.feature.vt.gui.provider.markuptable.DisplayableListingAddress;
import ghidra.feature.vt.gui.provider.matchtable.MatchTableRenderer;
import ghidra.feature.vt.gui.util.AbstractVTMatchTableModel.StatusTableColumn;
import ghidra.feature.vt.gui.util.MatchInfo;
import ghidra.feature.vt.gui.util.MatchStatusRenderer;
import ghidra.framework.model.*;
import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.model.EventType;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.ComponentProviderAdapter;
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.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
@ -58,24 +89,25 @@ import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraThreadedTablePanel;
/**
* The docking window that provides a table of the other tool's function matches for the function
* containing the current cursor location in this tool's listing.
* The docking window that provides a table of the other tool's function matches
* for the function containing the current cursor location in this tool's
* listing.
*/
public abstract class VTMatchOneToManyTableProvider extends ComponentProviderAdapter
implements FilterDialogModel<VTMatch>, VTControllerListener, VTSubToolManagerListener {
private static final String TITLE_PREFIX = "Version Tracking Matches for ";
private static final Icon ICON = new GIcon("icon.version.tracking.provider.one.to.many");
private static final Icon COMPARISON_ICON = new GIcon("icon.plugin.functioncompare.new");
protected static final Color LOCAL_INFO_FOREGROUND_COLOR =
new GColor("color.fg.version.tracking.function.match.local.info");
protected static final Color LOCAL_INFO_FOREGROUND_COLOR = new GColor(
"color.fg.version.tracking.function.match.local.info");
private JComponent component;
private MatchThreadedTablePanel tablePanel;
protected GhidraTable matchesTable;
private ListSelectionListener matchSelectionListener;
protected VTMatchOneToManyTableModel oneToManyTableModel;
private JToggleButton ancillaryFilterButton;
private Set<Filter<VTMatch>> filters = new HashSet<>();
private FilterStatusListener refilterListener = new RefilterListener();
@ -100,7 +132,8 @@ public abstract class VTMatchOneToManyTableProvider extends ComponentProviderAda
private VTMatch pendingMatchSelection;
public VTMatchOneToManyTableProvider(PluginTool tool, VTController controller,
VTSubToolManager subToolManager, boolean isSource) {
VTSubToolManager subToolManager,
boolean isSource) {
super(tool, TITLE_PREFIX + (isSource ? "Source" : "Destination"), VTPlugin.OWNER);
this.controller = controller;
this.subToolManager = subToolManager;
@ -130,6 +163,43 @@ public abstract class VTMatchOneToManyTableProvider extends ComponentProviderAda
addLocalAction(new SetVTMatchFromOneToManyAction(controller, true));
addLocalAction(new ClearMatchAction(controller));
addLocalAction(new AcceptMatchAction(controller));
new ActionBuilder("Compare Functions", getName())
.popupMenuPath("Compare Functions")
.popupMenuIcon(COMPARISON_ICON)
.popupMenuGroup("AAA_VT_Main")
.keyBinding("shift c")
.sharedKeyBinding()
.description("Compares the Function(s) with its remote match")
.helpLocation(new HelpLocation("VersionTrackingPlugin", "Compare_Functions"))
.withContext(VTMatchOneToManyContext.class)
.enabledWhen(this::isValidFunctionComparison)
.onAction(this::compareFunctions)
.buildAndInstallLocal(this);
}
private boolean isValidFunctionComparison(VTMatchOneToManyContext context) {
List<VTMatch> functionMatches = context.getFunctionMatches();
return !functionMatches.isEmpty();
}
private void compareFunctions(VTMatchOneToManyContext c) {
List<VTMatch> selectedMatches = c.getSelectedMatches();
for (VTMatch match : selectedMatches) {
MatchInfo matchInfo = controller.getMatchInfo(match);
// Whichever codebrowser we are currently in, is what will be on the left
// side of the compare functions window.
Function leftFunction = matchInfo.getSourceFunction(),
rightFunction = matchInfo.getDestinationFunction();
if (!isSource) {
leftFunction = matchInfo.getDestinationFunction();
rightFunction = matchInfo.getSourceFunction();
}
FunctionComparisonService service = tool.getService(FunctionComparisonService.class);
service.compareFunctions(leftFunction, rightFunction);
}
}
@Override
@ -292,9 +362,9 @@ public abstract class VTMatchOneToManyTableProvider extends ComponentProviderAda
private List<VTMatch> getSelectedMatches() {
List<VTMatch> list = new ArrayList<>();
int selectedRowCount = matchesTable.getSelectedRowCount();
if (selectedRowCount == 1) {
int row = matchesTable.getSelectedRow();
int[] selectedRows = matchesTable.getSelectedRows();
for (int row : selectedRows) {
VTMatch mySelectedMatch = oneToManyTableModel.getRowObject(row);
list.add(mySelectedMatch);
}
@ -467,7 +537,8 @@ public abstract class VTMatchOneToManyTableProvider extends ComponentProviderAda
@Override
public void markupItemSelected(VTMarkupItem markupItem) {
// Do nothing since the one to many match table doesn't need to respond to the mark-up that is selected.
// Do nothing since the one to many match table doesn't need to respond to the
// mark-up that is selected.
}
//==================================================================================================