mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch
'origin/GP-3648-dragonmacher-fg-comparisons' (Closes #1154)
This commit is contained in:
commit
ed1a1b81f0
116 changed files with 4228 additions and 1350 deletions
|
@ -30,8 +30,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import docking.action.*;
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.widgets.*;
|
||||
|
@ -345,7 +344,8 @@ public class SampleGraphProvider extends ComponentProviderAdapter {
|
|||
private void addLayoutAction() {
|
||||
|
||||
MultiStateDockingAction<LayoutProvider<SampleVertex, SampleEdge, SampleGraph>> layoutAction =
|
||||
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
|
||||
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName(),
|
||||
KeyBindingType.SHARED) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
|
|
@ -25,10 +25,10 @@ import ghidra.util.task.*;
|
|||
/**
|
||||
* Provides the ability to synchronize concurrent connection task
|
||||
* instances within the same thread. This can occur within the swing thread due to the presence
|
||||
* of a modal task dialog event queue. It also allows password cancelation to be propogated to the
|
||||
* of a modal task dialog event queue. It also allows password cancellation to be propagated to the
|
||||
* other tasks(s).
|
||||
*/
|
||||
public class BSimDBConnectTaskCoordinator {
|
||||
public class BSimDBConnectTaskManager {
|
||||
|
||||
private final BSimServerInfo serverInfo;
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
private boolean isCancelled = false;
|
||||
private int count = 0;
|
||||
|
||||
public BSimDBConnectTaskCoordinator(BSimServerInfo serverInfo) {
|
||||
public BSimDBConnectTaskManager(BSimServerInfo serverInfo) {
|
||||
this.serverInfo = serverInfo;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
* Initiate a DB connection.
|
||||
* @param connectionSupplier DB connection supplier
|
||||
* @return DB connection
|
||||
* @throws SQLException if a database connection error occured
|
||||
* @throws SQLException if a database connection error occurred
|
||||
* @throws CancelledSQLException if task was cancelled (password entry cancelled)
|
||||
*/
|
||||
public Connection getConnection(DBConnectionSupplier connectionSupplier) throws SQLException {
|
||||
|
@ -66,7 +66,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
.launchModal();
|
||||
//@formatter:on
|
||||
|
||||
synchronized (BSimDBConnectTaskCoordinator.this) {
|
||||
synchronized (BSimDBConnectTaskManager.this) {
|
||||
Connection c = connectTask.getConnection();
|
||||
if (c != null) {
|
||||
return c;
|
||||
|
@ -85,7 +85,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
}
|
||||
}
|
||||
finally {
|
||||
synchronized (BSimDBConnectTaskCoordinator.this) {
|
||||
synchronized (BSimDBConnectTaskManager.this) {
|
||||
if (--count == 0) {
|
||||
clear();
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
synchronized (BSimDBConnectTaskCoordinator.this) {
|
||||
synchronized (BSimDBConnectTaskManager.this) {
|
||||
monitor.setMessage("Connecting...");
|
||||
++count;
|
||||
if (isCancelled) {
|
|
@ -109,11 +109,11 @@ public class BSimPostgresDBConnectionManager {
|
|||
private boolean successfulConnection = false;
|
||||
|
||||
private BasicDataSource bds = new BasicDataSource();
|
||||
private BSimDBConnectTaskCoordinator taskCoordinator;
|
||||
private BSimDBConnectTaskManager taskManager;
|
||||
|
||||
private BSimPostgresDataSource(BSimServerInfo serverInfo) {
|
||||
this.serverInfo = serverInfo;
|
||||
this.taskCoordinator = new BSimDBConnectTaskCoordinator(serverInfo);
|
||||
this.taskManager = new BSimDBConnectTaskManager(serverInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -254,7 +254,7 @@ public class BSimPostgresDBConnectionManager {
|
|||
|
||||
setDefaultProperties();
|
||||
|
||||
return taskCoordinator.getConnection(() -> connect());
|
||||
return taskManager.getConnection(() -> connect());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -306,16 +306,18 @@ public class BSimPostgresDBConnectionManager {
|
|||
loginError = "Access denied: " + serverInfo;
|
||||
Msg.error(this, loginError);
|
||||
}
|
||||
|
||||
// Use Ghidra's authentication infrastructure
|
||||
connectionType = ConnectionType.SSL_Password_Authentication; // Try again with a password
|
||||
// fallthru to second attempt at getConnection
|
||||
// Try again with a password; fallthrough to second attempt at getConnection
|
||||
connectionType = ConnectionType.SSL_Password_Authentication;
|
||||
}
|
||||
else if (e.getMessage().contains("SSL on") &&
|
||||
e.getMessage().contains("no pg_hba.conf entry")) {
|
||||
connectionType = ConnectionType.Unencrypted_No_Authentication; // Try again without any SSL
|
||||
|
||||
// Try again without any SSL; fallthrough to second attempt at getConnection
|
||||
connectionType = ConnectionType.Unencrypted_No_Authentication;
|
||||
bds.removeConnectionProperty("sslmode");
|
||||
bds.removeConnectionProperty("sslfactory");
|
||||
// fallthru to second attempt at getConnection
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
|
@ -364,6 +366,7 @@ public class BSimPostgresDBConnectionManager {
|
|||
catch (SQLException e) {
|
||||
if ((clientAuthenticator instanceof DefaultClientAuthenticator) &&
|
||||
e.getMessage().contains("password authentication failed")) {
|
||||
|
||||
// wrong password provided via popup dialog - try again
|
||||
loginError = "Access denied: " + serverInfo;
|
||||
continue;
|
||||
|
|
|
@ -43,8 +43,8 @@ public class BSimH2FileDBConnectionManager {
|
|||
private static boolean shutdownHookInstalled = false;
|
||||
|
||||
/**
|
||||
* Get all H2 File DB data sorces which exist in the JVM.
|
||||
* @return all H2 File DB data sorces
|
||||
* Get all H2 File DB data sources which exist in the JVM.
|
||||
* @return all H2 File DB data sources
|
||||
*/
|
||||
public static synchronized Collection<BSimH2FileDataSource> getAllDataSources() {
|
||||
// Create copy to avoid potential concurrent modification
|
||||
|
@ -138,11 +138,11 @@ public class BSimH2FileDBConnectionManager {
|
|||
private boolean successfulConnection = false;
|
||||
|
||||
private BasicDataSource bds = new BasicDataSource();
|
||||
private BSimDBConnectTaskCoordinator taskCoordinator;
|
||||
private BSimDBConnectTaskManager taskManager;
|
||||
|
||||
private BSimH2FileDataSource(BSimServerInfo serverInfo) {
|
||||
this.serverInfo = serverInfo;
|
||||
this.taskCoordinator = new BSimDBConnectTaskCoordinator(serverInfo);
|
||||
this.taskManager = new BSimDBConnectTaskManager(serverInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,7 +159,7 @@ public class BSimH2FileDBConnectionManager {
|
|||
* Delete the database files associated with this H2 File DB. This will fail immediately
|
||||
* if active connections exist. Otherwise removal will be attempted and this data source
|
||||
* will no longer be valid.
|
||||
* @return true if DB sucessfully removed
|
||||
* @return true if DB successfully removed
|
||||
*/
|
||||
public synchronized boolean delete() {
|
||||
|
||||
|
@ -278,7 +278,7 @@ public class BSimH2FileDBConnectionManager {
|
|||
* Get a connection to the H2 file database.
|
||||
* It is important to note that if the database does not exist and empty one will
|
||||
* be created. The {@link #exists()} method should be used to check for the database
|
||||
* existance prior to connecting the first time.
|
||||
* existence prior to connecting the first time.
|
||||
* @return database connection
|
||||
* @throws SQLException if a database error occurs
|
||||
*/
|
||||
|
@ -294,7 +294,7 @@ public class BSimH2FileDBConnectionManager {
|
|||
|
||||
setDefaultProperties();
|
||||
|
||||
return taskCoordinator.getConnection(() -> connect());
|
||||
return taskManager.getConnection(() -> connect());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,7 +14,7 @@ MachoRelocationHandler
|
|||
ElfRelocationHandler
|
||||
ElfExtension
|
||||
RelocationFixupHandler
|
||||
CodeComparisonPanel
|
||||
CodeComparisonView
|
||||
InstructionSkipper
|
||||
DataTypeReferenceFinder
|
||||
ChecksumAlgorithm
|
||||
|
|
|
@ -494,7 +494,9 @@
|
|||
</P>
|
||||
|
||||
|
||||
|
||||
<H3><A name="Decompiler_Code_Comparison_Actions"></A>Decompiler Code Comparison Actions</H3>
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<H4><A name="Compare_Matching_Callees"></A>Compare Matching Callees</H4>
|
||||
<BLOCKQUOTE>
|
||||
|
@ -514,6 +516,29 @@
|
|||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
<H2><A name="FunctionGraph_Diff_View"></A>Function Graph Diff View</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The <B><I> Function Graph Diff View</I></B> shows a pair of Function Graphs side by
|
||||
side.
|
||||
</P>
|
||||
<BR><BR>
|
||||
|
||||
<H3><A name="FunctionGraph_Code_Comparison_Actions"></A>Function Graph Code Comparison Actions</H3>
|
||||
|
||||
<H4><A name="Dual_Function_Graph_View_Toggle_Orientation"></A>Show Function Graphs Side-by-Side</H4>
|
||||
<BLOCKQUOTE>
|
||||
<P> This toggles the Function Graph panels between a vertical split and a horizontal split. </P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
<H2><A name="Compare Multiple Functions"></A>Comparing Multiple Functions</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -25,7 +25,7 @@ import docking.widgets.EmptyBorderButton;
|
|||
import docking.widgets.TitledPanel;
|
||||
import docking.widgets.button.GRadioButton;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.GhidraOptions;
|
||||
|
@ -214,7 +214,7 @@ class ExternalAddConflictPanel extends JPanel implements CodeFormatService {
|
|||
latestPanel.setProgram(latestProgram);
|
||||
myPanel.setProgram(myProgram);
|
||||
|
||||
new FieldPanelCoordinator(
|
||||
new FieldPanelScrollCoordinator(
|
||||
new FieldPanel[] { latestPanel.getFieldPanel(), myPanel.getFieldPanel() });
|
||||
|
||||
buttonGroup = new ButtonGroup();
|
||||
|
|
|
@ -27,7 +27,7 @@ import docking.widgets.EmptyBorderButton;
|
|||
import docking.widgets.TitledPanel;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.BackgroundColorModel;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.GhidraOptions;
|
||||
|
@ -66,7 +66,7 @@ public class ListingMergePanel extends JPanel
|
|||
private JComponent bottomComp;
|
||||
protected TitledPanel[] titlePanels;
|
||||
private ListingPanel[] listingPanels;
|
||||
private FieldPanelCoordinator coordinator;
|
||||
private FieldPanelScrollCoordinator coordinator;
|
||||
private FormatManager formatMgr;
|
||||
private MultiListingLayoutModel multiModel;
|
||||
private Program[] programs = new Program[4];
|
||||
|
@ -120,7 +120,7 @@ public class ListingMergePanel extends JPanel
|
|||
}
|
||||
|
||||
backgroundColorModel.addChangeListener(backgroundChangeListener);
|
||||
coordinator = new FieldPanelCoordinator(fieldPanels);
|
||||
coordinator = new FieldPanelScrollCoordinator(fieldPanels);
|
||||
|
||||
titlePanels[RESULT].addTitleComponent(new ShowHeaderButton());
|
||||
|
||||
|
|
|
@ -130,9 +130,9 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
private String stringContent;
|
||||
private boolean includeQuotesForStringData;
|
||||
|
||||
public CodeBrowserClipboardProvider(PluginTool tool, ComponentProvider codeViewerProvider) {
|
||||
public CodeBrowserClipboardProvider(PluginTool tool, ComponentProvider componentProvider) {
|
||||
this.tool = tool;
|
||||
this.componentProvider = codeViewerProvider;
|
||||
this.componentProvider = componentProvider;
|
||||
|
||||
PAINT_CONTEXT.setTextCopying(true);
|
||||
|
||||
|
@ -140,7 +140,6 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
includeQuotesForStringData =
|
||||
!options.getBoolean(ClipboardPlugin.REMOVE_QUOTES_OPTION, false);
|
||||
options.addOptionsChangeListener(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -36,7 +36,7 @@ import docking.dnd.*;
|
|||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.HoverHandler;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import docking.widgets.tab.GTabPanel;
|
||||
import generic.theme.GIcon;
|
||||
|
@ -105,7 +105,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
private ListingPanel otherPanel;
|
||||
private CoordinatedListingPanelListener coordinatedListingPanelListener;
|
||||
private FormatManager formatMgr;
|
||||
private FieldPanelCoordinator coordinator;
|
||||
private FieldPanelScrollCoordinator coordinator;
|
||||
private ProgramSelectionListener liveProgramSelectionListener = (selection, trigger) -> {
|
||||
liveSelection = selection;
|
||||
updateSubTitle();
|
||||
|
@ -753,7 +753,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
ListingModel otherAlignedModel = multiModel.getAlignedModel(1);
|
||||
listingPanel.setListingModel(myAlignedModel);
|
||||
lp.setListingModel(otherAlignedModel);
|
||||
coordinator = new FieldPanelCoordinator(
|
||||
coordinator = new FieldPanelScrollCoordinator(
|
||||
new FieldPanel[] { listingPanel.getFieldPanel(), lp.getFieldPanel() });
|
||||
addHoverServices(otherPanel);
|
||||
HoverHandler hoverHandler = listingPanel.getFieldPanel().getHoverHandler();
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Collection;
|
|||
|
||||
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.model.MatchedFunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import utility.function.Callback;
|
||||
|
||||
|
@ -84,6 +85,17 @@ public interface FunctionComparisonService {
|
|||
* @param closeListener an optional callback if the client wants to be notified when the
|
||||
* associated function comparison windows is closed.
|
||||
*/
|
||||
public void createCustomComparison(FunctionComparisonModel model,
|
||||
Callback closeListener);
|
||||
public void createCustomComparison(FunctionComparisonModel model, Callback closeListener);
|
||||
|
||||
/**
|
||||
* Creates a new comparison view that the caller can install into their UI. This is in contrast
|
||||
* with {@link #createCustomComparison(FunctionComparisonModel, Callback)}, which will install
|
||||
* the new comparison into an existing UI.
|
||||
* <p>
|
||||
* Note: clients are responsible for calling {@link FunctionComparisonPanel#dispose()} when done
|
||||
* using the panel.
|
||||
*
|
||||
* @return the new panel
|
||||
*/
|
||||
public FunctionComparisonPanel createComparisonViewer();
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.nav.NavigationUtils;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
|
@ -54,10 +53,11 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
Program program = programManager.getCurrentProgram();
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit codeUnit = listing.getCodeUnitAt(location.getAddress());
|
||||
return checkMemReferences(codeUnit, serviceProvider);
|
||||
return checkMemReferences(codeUnit, sourceNavigatable, serviceProvider);
|
||||
}
|
||||
|
||||
private boolean checkMemReferences(CodeUnit codeUnit, ServiceProvider serviceProvider) {
|
||||
private boolean checkMemReferences(CodeUnit codeUnit, Navigatable navigatable,
|
||||
ServiceProvider serviceProvider) {
|
||||
|
||||
if (codeUnit == null) {
|
||||
return false;
|
||||
|
@ -77,8 +77,8 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
|
||||
TableService service = serviceProvider.getService(TableService.class);
|
||||
if (service != null) {
|
||||
Navigatable nav = NavigationUtils.getActiveNavigatable();
|
||||
service.showTable("Mnemonic References", "Mnemonic", model, "References", nav);
|
||||
service.showTable("Mnemonic References", "Mnemonic", model, "References",
|
||||
navigatable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
|
||||
GoToService goToService = serviceProvider.getService(GoToService.class);
|
||||
if (goToService != null) {
|
||||
return goToService.goTo(loc);
|
||||
return goToService.goTo(navigatable, loc, navigatable.getProgram());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,9 +83,15 @@ public class ProgramLocationTranslator {
|
|||
}
|
||||
}
|
||||
|
||||
String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
|
||||
if (symbolPathArray.length != 0) {
|
||||
// Adjust symbol path for labels if it is part of the location.
|
||||
adjustSymbolPath(saveState, otherSideAddress, address, byteAddress, desiredByteAddress,
|
||||
otherSideLocation.getProgram(), program);
|
||||
boolean hasSymbol = adjustSymbolPath(saveState, otherSideAddress, address, byteAddress,
|
||||
desiredByteAddress, otherSideLocation.getProgram(), program);
|
||||
if (!hasSymbol) {
|
||||
return new ProgramLocation(program, desiredByteAddress);
|
||||
}
|
||||
}
|
||||
|
||||
// ref address can't be used with indicated side so remove it.
|
||||
saveState.remove("_REF_ADDRESS");
|
||||
|
@ -186,32 +192,28 @@ public class ProgramLocationTranslator {
|
|||
return ProgramLocation.getLocation(correlator.getProgram(side), saveState);
|
||||
}
|
||||
|
||||
private void adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
|
||||
private boolean adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
|
||||
Address byteAddress, Address desiredByteAddress, Program program,
|
||||
Program desiredProgram) {
|
||||
|
||||
String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
|
||||
saveState.remove("_SYMBOL_PATH");
|
||||
if (symbolPathArray.length == 0) {
|
||||
return; // save state has no labels for program location.
|
||||
}
|
||||
|
||||
Address symbolAddress = (byteAddress != null) ? byteAddress : address;
|
||||
Address desiredSymbolAddress =
|
||||
(desiredByteAddress != null) ? desiredByteAddress : desiredAddress;
|
||||
if (symbolAddress == null || desiredSymbolAddress == null) {
|
||||
return; // no address match.
|
||||
return false; // no address match.
|
||||
}
|
||||
Symbol[] symbols = program.getSymbolTable().getSymbols(symbolAddress);
|
||||
if (symbols.length == 0) {
|
||||
return; // no symbols in program for matching.
|
||||
return false; // no symbols in program for matching.
|
||||
}
|
||||
Symbol[] desiredSymbols = desiredProgram.getSymbolTable().getSymbols(desiredSymbolAddress);
|
||||
if (desiredSymbols.length == 0) {
|
||||
return; // no symbols in desiredProgram for matching.
|
||||
return false; // no symbols in desiredProgram for matching.
|
||||
}
|
||||
|
||||
int desiredRow = adjustSymbolRow(saveState, symbols, desiredSymbols);
|
||||
|
||||
int desiredIndex = getDesiredSymbolIndex(desiredSymbols, desiredRow);
|
||||
|
||||
// Now get the desired symbol.
|
||||
|
@ -219,6 +221,7 @@ public class ProgramLocationTranslator {
|
|||
SymbolPath symbolPath = getSymbolPath(desiredSymbol);
|
||||
// Set symbol path for desiredProgram in the save state.
|
||||
saveState.putStrings("_SYMBOL_PATH", symbolPath.asArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
private int adjustSymbolRow(SaveState saveState, Symbol[] symbols, Symbol[] desiredSymbols) {
|
||||
|
|
|
@ -37,8 +37,8 @@ import ghidra.app.plugin.core.functioncompare.actions.*;
|
|||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.format.*;
|
||||
import ghidra.app.util.viewer.listingpanel.*;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanelActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonViewActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
|
@ -57,12 +57,10 @@ import ghidra.util.task.TaskMonitor;
|
|||
import help.Help;
|
||||
|
||||
/**
|
||||
* Panel that displays two listings for comparison.
|
||||
* UI that displays two listings for comparison.
|
||||
*/
|
||||
|
||||
public class ListingCodeComparisonPanel
|
||||
extends CodeComparisonPanel implements
|
||||
FormatModelListener, OptionsChangeListener {
|
||||
public class ListingCodeComparisonView
|
||||
extends CodeComparisonView implements FormatModelListener, OptionsChangeListener {
|
||||
|
||||
public static final String NAME = "Listing View";
|
||||
private static final String DIFF_NAVIGATE_GROUP = "A2_DiffNavigate";
|
||||
|
@ -87,7 +85,7 @@ public class ListingCodeComparisonPanel
|
|||
|
||||
private ListingAddressCorrelation addressCorrelator;
|
||||
private ListingDiff listingDiff;
|
||||
private ListingCoordinator coordinator;
|
||||
private ListingDisplaySynchronizer coordinator;
|
||||
private boolean listingsLocked;
|
||||
|
||||
private ListingDiffActionManager diffActionManager;
|
||||
|
@ -107,7 +105,7 @@ public class ListingCodeComparisonPanel
|
|||
* @param owner the owner of this panel
|
||||
* @param tool the tool displaying this panel
|
||||
*/
|
||||
public ListingCodeComparisonPanel(String owner, PluginTool tool) {
|
||||
public ListingCodeComparisonView(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
Help.getHelpService().registerHelp(this, new HelpLocation(HELP_TOPIC, "Listing_View"));
|
||||
initializeOptions();
|
||||
|
@ -450,7 +448,7 @@ public class ListingCodeComparisonPanel
|
|||
.description("Show the tool options for the Listing Code Comparison.")
|
||||
.popupMenuPath("Properties")
|
||||
.helpLocation(new HelpLocation(HELP_TOPIC, "Listing_Code_Comparison_Options"))
|
||||
.validContextWhen(c -> isValidPanelContext(c))
|
||||
.validWhen(c -> isValidPanelContext(c))
|
||||
.enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
|
||||
.onAction(c -> showOptionsDialog())
|
||||
.build();
|
||||
|
@ -497,10 +495,10 @@ public class ListingCodeComparisonPanel
|
|||
}
|
||||
|
||||
private boolean isValidPanelContext(ActionContext context) {
|
||||
if (!(context instanceof CodeComparisonPanelActionContext comparisonContext)) {
|
||||
if (!(context instanceof CodeComparisonViewActionContext comparisonContext)) {
|
||||
return false;
|
||||
}
|
||||
CodeComparisonPanel comparisonPanel = comparisonContext.getCodeComparisonPanel();
|
||||
CodeComparisonView comparisonPanel = comparisonContext.getCodeComparisonView();
|
||||
return comparisonPanel == this;
|
||||
}
|
||||
|
||||
|
@ -521,7 +519,7 @@ public class ListingCodeComparisonPanel
|
|||
coordinator = null;
|
||||
}
|
||||
if (listingsLocked) {
|
||||
coordinator = new ListingCoordinator(displays, addressCorrelator);
|
||||
coordinator = new ListingDisplaySynchronizer(displays, addressCorrelator);
|
||||
coordinator.sync(activeSide);
|
||||
}
|
||||
}
|
|
@ -23,24 +23,24 @@ import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
|
|||
*/
|
||||
public class ListingComparisonActionContext extends CodeComparisonActionContext {
|
||||
|
||||
private ListingCodeComparisonPanel codeComparisonPanel = null;
|
||||
private ListingCodeComparisonView codeComparisonPanel = null;
|
||||
|
||||
/**
|
||||
* Constructor for a dual listing's action context.
|
||||
* @param provider the provider that uses this action context.
|
||||
* @param panel the ListingCodeComparisonPanel that generated this context
|
||||
*/
|
||||
public ListingComparisonActionContext(ComponentProvider provider, ListingCodeComparisonPanel panel) {
|
||||
public ListingComparisonActionContext(ComponentProvider provider, ListingCodeComparisonView panel) {
|
||||
super(provider, panel, panel.getActiveListingPanel().getFieldPanel());
|
||||
this.codeComparisonPanel = panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ListingCodeComparisonPanel} that generated this context
|
||||
* Returns the {@link ListingCodeComparisonView} that generated this context
|
||||
* @return the listing comparison panel that generated this context
|
||||
*/
|
||||
@Override
|
||||
public ListingCodeComparisonPanel getCodeComparisonPanel() {
|
||||
public ListingCodeComparisonView getCodeComparisonView() {
|
||||
return codeComparisonPanel;
|
||||
}
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ public class ListingDiffActionManager {
|
|||
ToggleIgnoreRegisterNamesAction() {
|
||||
super("Toggle Ignore Register Names", "DualListing");
|
||||
setDescription(HTMLUtilities.toHTML(
|
||||
"If selected, difference highlights should\n" + "ignore operand Registers."));
|
||||
"If selected, difference highlights should\nignore operand Registers."));
|
||||
setEnabled(true);
|
||||
setPopupMenuData(new MenuData(
|
||||
new String[] { "Ignore Operand Registers As Differences" },
|
||||
|
|
|
@ -20,8 +20,8 @@ import static ghidra.util.datastruct.Duo.Side.*;
|
|||
import java.math.BigInteger;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import ghidra.app.util.viewer.listingpanel.ProgramLocationTranslator;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
|
@ -34,23 +34,23 @@ import ghidra.util.datastruct.Duo.Side;
|
|||
/**
|
||||
* Keeps two listing panels synchronized, both the view and cursor location
|
||||
*/
|
||||
public class ListingCoordinator {
|
||||
class ListingDisplaySynchronizer {
|
||||
private Duo<ListingDisplay> displays;
|
||||
private Duo<Address> lockLineAddresses = new Duo<>();
|
||||
|
||||
private ProgramLocationTranslator locationTranslator;
|
||||
private LineLockedFieldPanelCoordinator viewCoordinator;
|
||||
private LineLockedFieldPanelScrollCoordinator viewCoordinator;
|
||||
|
||||
ListingCoordinator(Duo<ListingDisplay> displays, ListingAddressCorrelation correlator) {
|
||||
ListingDisplaySynchronizer(Duo<ListingDisplay> displays,
|
||||
ListingAddressCorrelation correlation) {
|
||||
this.displays = displays;
|
||||
this.locationTranslator = new ProgramLocationTranslator(correlator);
|
||||
this.locationTranslator = new ProgramLocationTranslator(correlation);
|
||||
FieldPanel left = displays.get(LEFT).getListingPanel().getFieldPanel();
|
||||
FieldPanel right = displays.get(RIGHT).getListingPanel().getFieldPanel();
|
||||
viewCoordinator = new LayoutLockedFieldPanelCoordinator(left, right);
|
||||
viewCoordinator = new LayoutLockedFieldPanelScrollCoordinator(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* notification that the given side change to the given location
|
||||
* Notification that the given side change to the given location
|
||||
* @param side the side that changed
|
||||
* @param location the location from the given side
|
||||
*/
|
||||
|
@ -63,9 +63,7 @@ public class ListingCoordinator {
|
|||
if (otherLocation != null) {
|
||||
updateViewCoordinator(side, location, otherLocation);
|
||||
displays.get(otherSide).goTo(otherLocation);
|
||||
displays.get(side.otherSide()).updateCursorMarkers(otherLocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
@ -73,7 +71,7 @@ public class ListingCoordinator {
|
|||
}
|
||||
|
||||
/**
|
||||
* synchronized the two listings using the given side as the source
|
||||
* Synchronize the two listings using the given side as the source
|
||||
* @param side to synchronize from
|
||||
*/
|
||||
void sync(Side side) {
|
||||
|
@ -109,7 +107,7 @@ public class ListingCoordinator {
|
|||
if (leftAddress == null || rightAddress == null) {
|
||||
return;
|
||||
}
|
||||
lockLineAddresses = new Duo<>(leftAddress, rightAddress);
|
||||
|
||||
AddressIndexMap leftMap = displays.get(LEFT).getListingPanel().getAddressIndexMap();
|
||||
AddressIndexMap rightMap = displays.get(RIGHT).getListingPanel().getAddressIndexMap();
|
||||
|
|
@ -46,7 +46,7 @@ abstract class ListingDisplayToggleAction extends ToggleDockingAction {
|
|||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
if (contextObject instanceof ListingCodeComparisonPanel) {
|
||||
if (contextObject instanceof ListingCodeComparisonView) {
|
||||
Object sourceObject = context.getSourceObject();
|
||||
return sourceObject instanceof FieldPanel;
|
||||
}
|
||||
|
|
|
@ -23,19 +23,20 @@ import ghidra.program.model.listing.Function;
|
|||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
public abstract class CodeComparisonActionContext extends DefaultActionContext
|
||||
implements CodeComparisonPanelActionContext {
|
||||
private CodeComparisonPanel comparisonPanel;
|
||||
implements CodeComparisonViewActionContext {
|
||||
private CodeComparisonView comparisonProvider;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param provider the ComponentProvider containing the code comparison panel
|
||||
* @param panel the CodeComparisonPanel that generated this context
|
||||
* @param comparisonProvider the provider that generated this context
|
||||
* @param component the focusable component for associated with the comparison panel
|
||||
*/
|
||||
public CodeComparisonActionContext(ComponentProvider provider, CodeComparisonPanel panel,
|
||||
public CodeComparisonActionContext(ComponentProvider provider,
|
||||
CodeComparisonView comparisonProvider,
|
||||
Component component) {
|
||||
super(provider, panel, component);
|
||||
this.comparisonPanel = panel;
|
||||
super(provider, comparisonProvider, component);
|
||||
this.comparisonProvider = comparisonProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,8 +45,8 @@ public abstract class CodeComparisonActionContext extends DefaultActionContext
|
|||
* @return the function to get information from
|
||||
*/
|
||||
public Function getSourceFunction() {
|
||||
Side activeSide = comparisonPanel.getActiveSide();
|
||||
return comparisonPanel.getFunction(activeSide.otherSide());
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
return comparisonProvider.getFunction(activeSide.otherSide());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +55,7 @@ public abstract class CodeComparisonActionContext extends DefaultActionContext
|
|||
* @return the function to apply information to
|
||||
*/
|
||||
public Function getTargetFunction() {
|
||||
Side activeSide = comparisonPanel.getActiveSide();
|
||||
return comparisonPanel.getFunction(activeSide);
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
return comparisonProvider.getFunction(activeSide);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import docking.ComponentProvider;
|
|||
import docking.action.*;
|
||||
import docking.widgets.TitledPanel;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -41,17 +42,19 @@ import ghidra.util.classfinder.ClassSearcher;
|
|||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* The CodeComparisonPanel class should be extended by any class that is to be
|
||||
* The {@link CodeComparisonView} 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
|
||||
* <p>
|
||||
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN
|
||||
* <code>CodeComparisonPanel</code> so they are discoverable by the {@link ClassSearcher}
|
||||
* NOTE: ALL CodeComparisonView CLASSES MUST END IN
|
||||
* <code>CodeComparisonView</code> so they are discoverable by the {@link ClassSearcher}
|
||||
*/
|
||||
public abstract class CodeComparisonPanel extends JPanel
|
||||
public abstract class CodeComparisonView extends JPanel
|
||||
implements ExtensionPoint {
|
||||
|
||||
public static final String HELP_TOPIC = "FunctionComparison";
|
||||
private static final Color ACTIVE_BORDER_COLOR = Palette.getColor("lightpink");
|
||||
private static final int MINIMUM_PANEL_WIDTH = 50;
|
||||
|
@ -71,6 +74,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
private ToggleOrientationAction toggleOrientationAction;
|
||||
private JComponent northComponent;
|
||||
private boolean showTitles = true;
|
||||
private Callback orientationChangedCallback = Callback.dummy();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -78,7 +82,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
* @param owner the name of the owner of this component
|
||||
* @param tool the tool that contains the component
|
||||
*/
|
||||
protected CodeComparisonPanel(String owner, PluginTool tool) {
|
||||
protected CodeComparisonView(String owner, PluginTool tool) {
|
||||
this.owner = owner;
|
||||
this.tool = tool;
|
||||
toggleOrientationAction = new ToggleOrientationAction(getName());
|
||||
|
@ -91,6 +95,10 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
return tool;
|
||||
}
|
||||
|
||||
public void setSaveState(SaveState saveState) {
|
||||
// for subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a comparison of two ComparisonData objects
|
||||
*
|
||||
|
@ -163,7 +171,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
public abstract void dispose();
|
||||
|
||||
/**
|
||||
* Returns the context object which corresponds to the area of focus within this provider's
|
||||
* Returns the context object which corresponds to the area of focus within this view's
|
||||
* component. Null is returned when there is no context.
|
||||
* @param componentProvider the provider that includes this code comparison component.
|
||||
* @param event mouse event which corresponds to this request.
|
||||
|
@ -296,6 +304,12 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
: JSplitPane.VERTICAL_SPLIT;
|
||||
splitPane.setOrientation(orientation);
|
||||
splitPane.setDividerLocation(0.5);
|
||||
|
||||
orientationChangedCallback.call();
|
||||
}
|
||||
|
||||
public void setOrientationChangedCallback(Callback callback) {
|
||||
this.orientationChangedCallback = Callback.dummyIfNull(callback);
|
||||
}
|
||||
|
||||
private void setTitle(TitledPanel titlePanel, String titlePrefix, String title) {
|
||||
|
@ -343,7 +357,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
setActiveSide(LEFT);
|
||||
}
|
||||
|
||||
private void addMouseAndFocusListeners(Side side) {
|
||||
protected void addMouseAndFocusListeners(Side side) {
|
||||
JComponent comp = getComparisonComponent(side);
|
||||
comp.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
|
@ -374,6 +388,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
}
|
||||
|
||||
private void addMouseListenerRecursively(Component component, MouseListener listener) {
|
||||
component.removeMouseListener(listener);
|
||||
component.addMouseListener(listener);
|
||||
if (component instanceof Container container) {
|
||||
for (int i = 0; i < container.getComponentCount(); i++) {
|
|
@ -16,14 +16,14 @@
|
|||
package ghidra.features.base.codecompare.panel;
|
||||
|
||||
/**
|
||||
* Action context for a CodeComparisonPanel.
|
||||
* Action context for a {@link CodeComparisonView}.
|
||||
*/
|
||||
public interface CodeComparisonPanelActionContext {
|
||||
public interface CodeComparisonViewActionContext {
|
||||
|
||||
/**
|
||||
* Gets the CodeComparisonPanel associated with this context.
|
||||
* @return the code comparison panel.
|
||||
* Gets the view associated with this context.
|
||||
* @return the code comparison provider.
|
||||
*/
|
||||
public abstract CodeComparisonPanel getCodeComparisonPanel();
|
||||
public abstract CodeComparisonView getCodeComparisonView();
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.base.codecompare.panel;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
/**
|
||||
* A state object to save settings each type of comparison view known by the system. This class
|
||||
* is meant to be used to allow user settings to be applied to each new comparison widget that is
|
||||
* created. Also, the class allows the tool to save those settings when the tool is saved.
|
||||
* <p>
|
||||
* When a comparison provider updates its save state object, it should call
|
||||
* {@link PluginTool#setConfigChanged(boolean)} so that tool knows there are settings to be saved.
|
||||
*/
|
||||
public class CodeComparisonViewState {
|
||||
|
||||
private static final String FUNCTION_COMPARISON_STATES = "CodeComparisonStates";
|
||||
|
||||
private Map<Class<? extends CodeComparisonView>, SaveState> states = new HashMap<>();
|
||||
|
||||
public SaveState getSaveState(Class<? extends CodeComparisonView> clazz) {
|
||||
return states.computeIfAbsent(clazz, this::createSaveState);
|
||||
}
|
||||
|
||||
private SaveState createSaveState(Class<? extends CodeComparisonView> clazz) {
|
||||
return new SaveState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the tool to write the panels' saved states into the tools save state
|
||||
* @param saveState the tool's save state
|
||||
*/
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
Set<Entry<Class<? extends CodeComparisonView>, SaveState>> entries = states.entrySet();
|
||||
SaveState classStates = new SaveState();
|
||||
for (Entry<Class<? extends CodeComparisonView>, SaveState> entry : entries) {
|
||||
Class<? extends CodeComparisonView> clazz = entry.getKey();
|
||||
SaveState subState = entry.getValue();
|
||||
classStates.putSaveState(clazz.getName(), subState);
|
||||
}
|
||||
|
||||
saveState.putSaveState(FUNCTION_COMPARISON_STATES, classStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the tool to load saved state for the comparison providers
|
||||
* @param saveState the tool's state
|
||||
*/
|
||||
public void readConfigState(SaveState saveState) {
|
||||
|
||||
SaveState classStates = saveState.getSaveState(FUNCTION_COMPARISON_STATES);
|
||||
if (classStates == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] names = classStates.getNames();
|
||||
for (String className : names) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends CodeComparisonView> clazz =
|
||||
(Class<? extends CodeComparisonView>) Class.forName(className);
|
||||
SaveState classState = classStates.getSaveState(className);
|
||||
states.put(clazz, classState);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* ComparisonData is an abstract of items that can be compared in a {@link CodeComparisonPanel}.
|
||||
* ComparisonData is an abstraction of items that can be compared in a {@link CodeComparisonView}.
|
||||
* Not all comparison panels can handle all types of comparison data. For example, the decompiler
|
||||
* comparison only works when the comparison data is a function.
|
||||
*/
|
||||
|
@ -71,6 +71,7 @@ public interface ComparisonData {
|
|||
|
||||
/**
|
||||
* Returns the initial program location to put the cursor when the panel is first displayed
|
||||
* @return the location
|
||||
*/
|
||||
public ProgramLocation getInitialLocation();
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import static ghidra.util.datastruct.Duo.Side.*;
|
|||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -34,10 +33,9 @@ import docking.ComponentProvider;
|
|||
import docking.action.*;
|
||||
import docking.widgets.tabbedpane.DockingTabRenderer;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
@ -48,18 +46,16 @@ import help.Help;
|
|||
import help.HelpService;
|
||||
|
||||
/**
|
||||
* A panel for displaying {@link Function functions}, {@link Data data}, or
|
||||
* {@link AddressSet address sets} side-by-side for comparison purposes
|
||||
* A panel for displaying {@link Function functions} side-by-side for comparison purposes
|
||||
*/
|
||||
public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
||||
private static final String ORIENTATION_PROPERTY_NAME = "ORIENTATION";
|
||||
|
||||
private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonPanel.NAME;
|
||||
private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonView.NAME;
|
||||
private static final String COMPARISON_VIEW_DISPLAYED = "COMPARISON_VIEW_DISPLAYED";
|
||||
private static final String CODE_COMPARISON_LOCK_SCROLLING_TOGETHER =
|
||||
"CODE_COMPARISON_LOCK_SCROLLING_TOGETHER";
|
||||
|
||||
private static final HelpService help = Help.getHelpService();
|
||||
private static final String HELP_TOPIC = "FunctionComparison";
|
||||
|
||||
private static final Icon SYNC_SCROLLING_ICON =
|
||||
|
@ -72,23 +68,40 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
|
||||
private JTabbedPane tabbedPane;
|
||||
private Map<String, JComponent> tabNameToComponentMap;
|
||||
private List<CodeComparisonPanel> codeComparisonPanels;
|
||||
private List<CodeComparisonView> codeComparisonViews;
|
||||
private ToggleScrollLockAction toggleScrollLockAction;
|
||||
private boolean syncScrolling = false;
|
||||
|
||||
private Duo<ComparisonData> comparisonData = new Duo<ComparisonData>();
|
||||
|
||||
public FunctionComparisonPanel(PluginTool tool, String owner) {
|
||||
this.comparisonData = new Duo<>(EMPTY, EMPTY);
|
||||
private FunctionComparisonState state;
|
||||
|
||||
codeComparisonPanels = getCodeComparisonPanels(tool, owner);
|
||||
/**
|
||||
* Constructor
|
||||
* @param tool the tool
|
||||
* @param owner the owner's name
|
||||
* @param state the comparison save state
|
||||
*/
|
||||
public FunctionComparisonPanel(PluginTool tool, String owner, FunctionComparisonState state) {
|
||||
this.comparisonData = new Duo<>(EMPTY, EMPTY);
|
||||
this.state = state;
|
||||
|
||||
state.addUpdateCallback(this::comparisonStateUpdated);
|
||||
|
||||
codeComparisonViews = getCodeComparisonViews(tool, owner);
|
||||
tabNameToComponentMap = new HashMap<>();
|
||||
createMainPanel();
|
||||
createActions(owner);
|
||||
setScrollingSyncState(true);
|
||||
HelpService help = Help.getHelpService();
|
||||
help.registerHelp(this, new HelpLocation(HELP_TOPIC, "Function Comparison"));
|
||||
}
|
||||
|
||||
private void comparisonStateUpdated() {
|
||||
readPanelState();
|
||||
readViewState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the given functions into the views of this panel
|
||||
*
|
||||
|
@ -118,9 +131,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
public void loadComparisons(ComparisonData left, ComparisonData right) {
|
||||
comparisonData = new Duo<>(left, right);
|
||||
|
||||
CodeComparisonPanel activePanel = getActiveComparisonPanel();
|
||||
if (activePanel != null) {
|
||||
activePanel.loadComparisons(left, right);
|
||||
CodeComparisonView activeView = getActiveComparisonView();
|
||||
if (activeView != null) {
|
||||
activeView.loadComparisons(left, right);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -173,9 +186,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
|
||||
// Setting the addresses to be displayed to null effectively clears
|
||||
// the display
|
||||
CodeComparisonPanel activePanel = getActiveComparisonPanel();
|
||||
if (activePanel != null) {
|
||||
activePanel.clearComparisons();
|
||||
CodeComparisonView activeView = getActiveComparisonView();
|
||||
if (activeView != null) {
|
||||
activeView.clearComparisons();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,15 +203,15 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the ListingCodeComparisonPanel being displayed by this panel
|
||||
* Gets the ListingCodeComparisonView being displayed by this panel
|
||||
* if one exists
|
||||
*
|
||||
* @return the comparison panel or null
|
||||
*/
|
||||
public ListingCodeComparisonPanel getDualListingPanel() {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
if (codeComparisonPanel instanceof ListingCodeComparisonPanel listingPanel) {
|
||||
return listingPanel;
|
||||
public ListingCodeComparisonView getDualListingView() {
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
if (view instanceof ListingCodeComparisonView listingView) {
|
||||
return listingView;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -207,13 +220,14 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
tabChanged();
|
||||
writeTabState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current tabbed panel to be the component with the given name
|
||||
*
|
||||
* @param name name of view to set as the current tab
|
||||
* @return true if the named view was found in the provider map
|
||||
* @return true if the named view was found in the view map
|
||||
*/
|
||||
public boolean setCurrentTabbedComponent(String name) {
|
||||
|
||||
|
@ -255,26 +269,34 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
tabbedPane.removeAll();
|
||||
|
||||
setVisible(false);
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.dispose();
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void programClosed(Program program) {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.programClosed(program);
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.programClosed(program);
|
||||
}
|
||||
}
|
||||
|
||||
public CodeComparisonPanel getCodeComparisonPanelByName(String name) {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
if (name.equals(codeComparisonPanel.getName())) {
|
||||
return codeComparisonPanel;
|
||||
public CodeComparisonView getCodeComparisonView(String name) {
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
if (name.equals(view.getName())) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void selectComparisonView(String name) {
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
if (name.equals(view.getName())) {
|
||||
tabbedPane.setSelectedComponent(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the main tabbed panel
|
||||
*/
|
||||
|
@ -287,22 +309,21 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
add(tabbedPane, BorderLayout.CENTER);
|
||||
setPreferredSize(new Dimension(200, 300));
|
||||
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
tabbedPane.add(codeComparisonPanel.getName(), codeComparisonPanel);
|
||||
tabNameToComponentMap.put(codeComparisonPanel.getName(), codeComparisonPanel);
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
tabbedPane.add(view.getName(), view);
|
||||
tabNameToComponentMap.put(view.getName(), view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when there is a tab change. This loads the active tab with
|
||||
* the appropriate data to be compared.
|
||||
* Invoked when there is a tab change. This loads the active tab with the data to be compared.
|
||||
*/
|
||||
private void tabChanged() {
|
||||
CodeComparisonPanel activePanel = getActiveComparisonPanel();
|
||||
if (activePanel == null) {
|
||||
CodeComparisonView activeView = getActiveComparisonView();
|
||||
if (activeView == null) {
|
||||
return; // initializing
|
||||
}
|
||||
activePanel.loadComparisons(comparisonData.get(LEFT), comparisonData.get(RIGHT));
|
||||
activeView.loadComparisons(comparisonData.get(LEFT), comparisonData.get(RIGHT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,70 +332,73 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
* @return the currently selected comparison panel, or null if nothing
|
||||
* selected
|
||||
*/
|
||||
private CodeComparisonPanel getActiveComparisonPanel() {
|
||||
return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
|
||||
private CodeComparisonView getActiveComparisonView() {
|
||||
return (CodeComparisonView) tabbedPane.getSelectedComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the FunctionComparisonPanel and which CodeComparisonPanel is currently
|
||||
* displayed based on the specified saveState
|
||||
*
|
||||
* @param prefix identifier to prepend to any save state names to make them unique
|
||||
* @param saveState the save state for retrieving information
|
||||
*/
|
||||
public void readConfigState(String prefix, SaveState saveState) {
|
||||
private void readViewState() {
|
||||
CodeComparisonViewState viewState = state.getViewState();
|
||||
codeComparisonViews.forEach(v -> {
|
||||
Class<? extends CodeComparisonView> viewClass = v.getClass();
|
||||
SaveState saveState = viewState.getSaveState(viewClass);
|
||||
v.setSaveState(saveState);
|
||||
});
|
||||
}
|
||||
|
||||
private void readPanelState() {
|
||||
|
||||
SaveState panelState = state.getPanelState();
|
||||
String currentTabView =
|
||||
saveState.getString(prefix + COMPARISON_VIEW_DISPLAYED, DEFAULT_CODE_COMPARISON_VIEW);
|
||||
panelState.getString(COMPARISON_VIEW_DISPLAYED, DEFAULT_CODE_COMPARISON_VIEW);
|
||||
setCurrentTabbedComponent(currentTabView);
|
||||
setScrollingSyncState(
|
||||
saveState.getBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true));
|
||||
panelState.getBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true));
|
||||
|
||||
for (CodeComparisonPanel panel : codeComparisonPanels) {
|
||||
String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
panel.setSideBySide(saveState.getBoolean(key, true));
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
String key = view.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
view.setSideBySide(panelState.getBoolean(key, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the information to the save state about the FunctionComparisonPanel and
|
||||
* which CodeComparisonPanel is currently displayed
|
||||
*
|
||||
* @param prefix identifier to prepend to any save state names to make them unique
|
||||
* @param saveState the save state where the information gets written
|
||||
*/
|
||||
public void writeConfigState(String prefix, SaveState saveState) {
|
||||
private void writeTabState() {
|
||||
String currentComponentName = getCurrentComponentName();
|
||||
if (currentComponentName != null) {
|
||||
saveState.putString(prefix + COMPARISON_VIEW_DISPLAYED, getCurrentComponentName());
|
||||
}
|
||||
saveState.putBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced());
|
||||
|
||||
for (CodeComparisonPanel panel : codeComparisonPanels) {
|
||||
String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
boolean sideBySide = panel.isSideBySide();
|
||||
saveState.putBoolean(key, sideBySide);
|
||||
if (currentComponentName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SaveState panelState = state.getPanelState();
|
||||
panelState.putString(COMPARISON_VIEW_DISPLAYED, getCurrentComponentName());
|
||||
state.setChanged();
|
||||
}
|
||||
|
||||
private void writeScrollState() {
|
||||
SaveState panelState = state.getPanelState();
|
||||
panelState.putBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced());
|
||||
state.setChanged();
|
||||
}
|
||||
|
||||
private void writeOrientationState() {
|
||||
|
||||
SaveState panelState = state.getPanelState();
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
String key = view.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
boolean sideBySide = view.isSideBySide();
|
||||
panelState.putBoolean(key, sideBySide);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all actions for the FunctionComparisonPanel and all CodeComparisonPanels in this
|
||||
* FunctionComparisonPanel
|
||||
*
|
||||
* @return the code comparison actions
|
||||
*/
|
||||
public DockingAction[] getCodeComparisonActions() {
|
||||
ArrayList<DockingAction> dockingActionList = new ArrayList<>();
|
||||
|
||||
// Get actions for this functionComparisonPanel
|
||||
DockingAction[] functionComparisonActions = getActions();
|
||||
for (DockingAction dockingAction : functionComparisonActions) {
|
||||
dockingActionList.add(dockingAction);
|
||||
// Get actions for this panel
|
||||
DockingAction[] actions = getActions();
|
||||
for (DockingAction action : actions) {
|
||||
dockingActionList.add(action);
|
||||
}
|
||||
|
||||
// Get actions for each CodeComparisonPanel
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
dockingActionList.addAll(codeComparisonPanel.getActions());
|
||||
// Get actions for each view
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
dockingActionList.addAll(view.getActions());
|
||||
}
|
||||
|
||||
return dockingActionList.toArray(new DockingAction[dockingActionList.size()]);
|
||||
|
@ -382,7 +406,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
|
||||
/**
|
||||
* Sets the prefixes that are to be prepended to the title displayed for each side of
|
||||
* each CodeComparisonPanel
|
||||
* each {@link CodeComparisonView}
|
||||
*
|
||||
* @param leftTitlePrefix the prefix to prepend to the left titles
|
||||
* @param rightTitlePrefix the prefix to prepend to the right titles
|
||||
|
@ -390,8 +414,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
public void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix) {
|
||||
Component[] components = tabbedPane.getComponents();
|
||||
for (Component component : components) {
|
||||
if (component instanceof CodeComparisonPanel) {
|
||||
((CodeComparisonPanel) component).setTitlePrefixes(leftTitlePrefix,
|
||||
if (component instanceof CodeComparisonView) {
|
||||
((CodeComparisonView) component).setTitlePrefixes(leftTitlePrefix,
|
||||
rightTitlePrefix);
|
||||
}
|
||||
}
|
||||
|
@ -405,9 +429,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
* @return the action context
|
||||
*/
|
||||
public ActionContext getActionContext(MouseEvent event, ComponentProvider componentProvider) {
|
||||
CodeComparisonPanel activePanel = getDisplayedPanel();
|
||||
if (activePanel != null) {
|
||||
return activePanel.getActionContext(componentProvider, event);
|
||||
CodeComparisonView activeProvider = getDisplayedView();
|
||||
if (activeProvider != null) {
|
||||
return activeProvider.getActionContext(componentProvider, event);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -432,43 +456,46 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
if (isScrollingSynced() == syncScrolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleScrollLockAction.setSelected(syncScrolling);
|
||||
toggleScrollLockAction.setToolBarData(new ToolBarData(
|
||||
syncScrolling ? SYNC_SCROLLING_ICON : UNSYNC_SCROLLING_ICON, SCROLLING_GROUP));
|
||||
// Notify each comparison panel of the scrolling sync state.
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.setSynchronizedScrolling(syncScrolling);
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.setSynchronizedScrolling(syncScrolling);
|
||||
}
|
||||
this.syncScrolling = syncScrolling;
|
||||
|
||||
writeScrollState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently displayed CodeComparisonPanel
|
||||
* Gets the currently displayed {@link CodeComparisonView}
|
||||
*
|
||||
* @return the current panel or null.
|
||||
*/
|
||||
public CodeComparisonPanel getDisplayedPanel() {
|
||||
public CodeComparisonView getDisplayedView() {
|
||||
int selectedIndex = tabbedPane.getSelectedIndex();
|
||||
Component component = tabbedPane.getComponentAt(selectedIndex);
|
||||
return (CodeComparisonPanel) component;
|
||||
return (CodeComparisonView) component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the enablement for all actions provided by each panel
|
||||
* Updates the enablement for all actions provided by each view
|
||||
*/
|
||||
public void updateActionEnablement() {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.updateActionEnablement();
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.updateActionEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current code comparison panel being viewed
|
||||
* Get the current code comparison view being viewed
|
||||
*
|
||||
* @return null if there is no code comparison panel
|
||||
* @return null if there is no code comparison view
|
||||
*/
|
||||
public CodeComparisonPanel getCurrentComponent() {
|
||||
return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
|
||||
public CodeComparisonView getCurrentView() {
|
||||
return (CodeComparisonView) tabbedPane.getSelectedComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -519,45 +546,48 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
public List<CodeComparisonPanel> getComparisonPanels() {
|
||||
return codeComparisonPanels;
|
||||
public List<CodeComparisonView> getComparisonView() {
|
||||
return codeComparisonViews;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovers the CodeComparisonPanels which are extension points
|
||||
* Discovers the {@link CodeComparisonView}s which are extension points
|
||||
*
|
||||
* @return the CodeComparisonPanels which are extension points
|
||||
* @return the views which are extension points
|
||||
*/
|
||||
private List<CodeComparisonPanel> getCodeComparisonPanels(PluginTool tool, String owner) {
|
||||
if (codeComparisonPanels == null) {
|
||||
codeComparisonPanels = createAllPossibleCodeComparisonPanels(tool, owner);
|
||||
codeComparisonPanels.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
|
||||
private List<CodeComparisonView> getCodeComparisonViews(PluginTool tool, String owner) {
|
||||
if (codeComparisonViews == null) {
|
||||
codeComparisonViews = createAllCodeComparisonViews(tool, owner);
|
||||
codeComparisonViews.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
|
||||
}
|
||||
return codeComparisonPanels;
|
||||
return codeComparisonViews;
|
||||
}
|
||||
|
||||
private List<CodeComparisonPanel> createAllPossibleCodeComparisonPanels(PluginTool tool,
|
||||
private List<CodeComparisonView> createAllCodeComparisonViews(PluginTool tool,
|
||||
String owner) {
|
||||
|
||||
List<CodeComparisonPanel> instances = new ArrayList<>();
|
||||
|
||||
List<Class<? extends CodeComparisonPanel>> classes =
|
||||
ClassSearcher.getClasses(CodeComparisonPanel.class);
|
||||
|
||||
for (Class<? extends CodeComparisonPanel> panelClass : classes) {
|
||||
CodeComparisonViewState viewState = state.getViewState();
|
||||
List<CodeComparisonView> instances = new ArrayList<>();
|
||||
List<Class<? extends CodeComparisonView>> classes =
|
||||
ClassSearcher.getClasses(CodeComparisonView.class);
|
||||
for (Class<? extends CodeComparisonView> viewClass : classes) {
|
||||
try {
|
||||
Constructor<? extends CodeComparisonPanel> constructor =
|
||||
panelClass.getConstructor(String.class, PluginTool.class);
|
||||
CodeComparisonPanel panel = constructor.newInstance(owner, tool);
|
||||
instances.add(panel);
|
||||
Constructor<? extends CodeComparisonView> constructor =
|
||||
viewClass.getConstructor(String.class, PluginTool.class);
|
||||
CodeComparisonView view = constructor.newInstance(owner, tool);
|
||||
|
||||
SaveState saveState = viewState.getSaveState(viewClass);
|
||||
view.setSaveState(saveState);
|
||||
|
||||
view.setOrientationChangedCallback(() -> writeOrientationState());
|
||||
|
||||
instances.add(view);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException | InstantiationException
|
||||
| IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException e) {
|
||||
catch (Exception e) {
|
||||
Msg.showError(this, null, "Error Creating Extension Point",
|
||||
"Error creating class " + panelClass.getName() +
|
||||
"Error creating class " + viewClass.getName() +
|
||||
" when creating extension points for " +
|
||||
CodeComparisonPanel.class.getName(),
|
||||
CodeComparisonView.class.getName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/* ###
|
||||
* 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.features.base.codecompare.panel;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* An object to share config state between providers and all views within those providers.
|
||||
* <p>
|
||||
* When a comparison provider updates its save state object, it should call
|
||||
* {@link PluginTool#setConfigChanged(boolean)} so that tool knows there are settings to be saved.
|
||||
*/
|
||||
public class FunctionComparisonState {
|
||||
|
||||
private static final String PROVIDER_SAVE_STATE_NAME = "FunctionComparison";
|
||||
|
||||
private SaveState panelState = new SaveState();
|
||||
private CodeComparisonViewState comparisonState = new CodeComparisonViewState();
|
||||
|
||||
private PluginTool tool;
|
||||
|
||||
private List<Callback> updateCallbacks = new ArrayList<>();
|
||||
|
||||
public FunctionComparisonState(PluginTool tool) {
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state object for the provider
|
||||
* @return the state object for the provider
|
||||
*/
|
||||
public SaveState getPanelState() {
|
||||
return panelState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the save state object for the views that live inside a provider
|
||||
* @return the state
|
||||
*/
|
||||
public CodeComparisonViewState getViewState() {
|
||||
return comparisonState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals to the tool that there are changes to the config state that can be saved.
|
||||
*/
|
||||
public void setChanged() {
|
||||
tool.setConfigChanged(true);
|
||||
}
|
||||
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
saveState.putSaveState(PROVIDER_SAVE_STATE_NAME, panelState);
|
||||
comparisonState.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
public void readConfigState(SaveState saveState) {
|
||||
SaveState restoredPanelState = saveState.getSaveState(PROVIDER_SAVE_STATE_NAME);
|
||||
if (restoredPanelState != null) {
|
||||
panelState = restoredPanelState;
|
||||
}
|
||||
|
||||
comparisonState.readConfigState(saveState);
|
||||
updateCallbacks.forEach(Callback::call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback to this state that is notified when this state changes.
|
||||
* @param callback the callback
|
||||
*/
|
||||
public void addUpdateCallback(Callback callback) {
|
||||
updateCallbacks.add(Objects.requireNonNull(callback));
|
||||
}
|
||||
}
|
|
@ -82,6 +82,7 @@ public class ProgramSelection implements AddressSetView {
|
|||
* @param addressFactory NOT USED
|
||||
* @param from the start of the selection
|
||||
* @param to the end of the selection
|
||||
* @deprecated use {@link #ProgramSelection(Address, Address)}
|
||||
*/
|
||||
@Deprecated(since = "11.2", forRemoval = true)
|
||||
public ProgramSelection(AddressFactory addressFactory, Address from, Address to) {
|
||||
|
|
|
@ -55,7 +55,8 @@ import ghidra.program.model.lang.*;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.program.util.ProgramUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.TaskUtilities;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.*;
|
||||
|
@ -75,7 +76,7 @@ public class TestEnv {
|
|||
private static Set<TestEnv> instances = new HashSet<>();
|
||||
|
||||
private FrontEndTool frontEndTool;
|
||||
private PluginTool tool;
|
||||
protected PluginTool tool;
|
||||
|
||||
private static TestProgramManager programManager = new TestProgramManager();
|
||||
|
||||
|
@ -962,7 +963,7 @@ public class TestEnv {
|
|||
|
||||
public Program loadResourceProgramAsBinary(String programName, Language language,
|
||||
CompilerSpec compilerSpec) throws LanguageNotFoundException, IOException,
|
||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
||||
CancelledException, VersionException {
|
||||
File file = AbstractGenericTest.getTestDataFile(programName);
|
||||
if (file == null || !file.exists()) {
|
||||
throw new FileNotFoundException("Can not find test program: " + programName);
|
||||
|
@ -971,8 +972,7 @@ public class TestEnv {
|
|||
}
|
||||
|
||||
public Program loadResourceProgramAsBinary(String programName, Processor processor)
|
||||
throws CancelledException, DuplicateNameException, InvalidNameException,
|
||||
VersionException, IOException {
|
||||
throws CancelledException, VersionException, IOException {
|
||||
Language language =
|
||||
DefaultLanguageService.getLanguageService().getDefaultLanguage(processor);
|
||||
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
|
||||
|
|
|
@ -21,6 +21,9 @@ import org.jdom.Element;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
|
||||
public class SaveStateTest {
|
||||
private SaveState saveState;
|
||||
|
||||
|
@ -46,6 +49,16 @@ public class SaveStateTest {
|
|||
assertEquals(2, restoreSubState.getNames().length);
|
||||
assertEquals(5, restoreSubState.getInt("a", 0));
|
||||
assertEquals("bar", restoreSubState.getString("foo", ""));
|
||||
|
||||
SaveState s1 = new SaveState("Parent");
|
||||
SaveState c1 = new SaveState("Child1");
|
||||
c1.putBoolean("Bool1", false);
|
||||
c1.putString("String1", "Hey bob");
|
||||
s1.putSaveState("MapChildName1", c1);
|
||||
Element e = s1.saveToXml();
|
||||
String s = XmlUtilities.toString(e);
|
||||
Msg.debug(this, s);
|
||||
|
||||
}
|
||||
|
||||
private SaveState saveAndRestoreToXml() throws Exception {
|
||||
|
|
|
@ -23,4 +23,5 @@ eclipse.project.name = 'Features CodeCompare'
|
|||
|
||||
dependencies {
|
||||
api project(":Decompiler")
|
||||
api project(":FunctionGraph")
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.Msg;
|
|||
|
||||
/**
|
||||
* Subclass of {@link AbstractMatchedTokensAction} for actions in a
|
||||
* {@link DecompilerCodeComparisonPanel} that are available only when the matched tokens are
|
||||
* {@link DecompilerCodeComparisonView} that are available only when the matched tokens are
|
||||
* function calls
|
||||
*/
|
||||
public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedTokensAction {
|
||||
|
@ -37,12 +37,12 @@ public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedT
|
|||
*
|
||||
* @param actionName name of action
|
||||
* @param owner owner of action
|
||||
* @param diffPanel diff panel containing action
|
||||
* @param comparisonProvider diff comparison provider containing action
|
||||
* @param disableOnReadOnly if true, action will be disabled for read-only programs
|
||||
*/
|
||||
public AbstractMatchedCalleeTokensAction(String actionName, String owner,
|
||||
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
|
||||
super(actionName, owner, diffPanel, disableOnReadOnly);
|
||||
DecompilerCodeComparisonView comparisonProvider, boolean disableOnReadOnly) {
|
||||
super(actionName, owner, comparisonProvider, disableOnReadOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,15 +69,15 @@ public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedT
|
|||
|
||||
@Override
|
||||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
DecompilerCodeComparisonPanel decompPanel = context.getCodeComparisonPanel();
|
||||
DecompilerCodeComparisonView provider = context.getCodeComparisonView();
|
||||
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
ClangFuncNameToken leftFuncToken = (ClangFuncNameToken) currentPair.leftToken();
|
||||
ClangFuncNameToken rightFuncToken = (ClangFuncNameToken) currentPair.rightToken();
|
||||
|
||||
Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getProgram(LEFT));
|
||||
Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getProgram(RIGHT));
|
||||
Function leftFunction = getFuncFromToken(leftFuncToken, provider.getProgram(LEFT));
|
||||
Function rightFunction = getFuncFromToken(rightFuncToken, provider.getProgram(RIGHT));
|
||||
if (leftFunction == null || rightFunction == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -19,13 +19,13 @@ import docking.ActionContext;
|
|||
import docking.action.DockingAction;
|
||||
|
||||
/**
|
||||
* This is a base class for actions in a {@link DecompilerCodeComparisonPanel}
|
||||
* This is a base class for actions in a {@link DecompilerCodeComparisonView}
|
||||
*/
|
||||
public abstract class AbstractMatchedTokensAction extends DockingAction {
|
||||
protected static final String MENU_PARENT = "Apply From Other Function";
|
||||
protected static final String HELP_TOPIC = "FunctionComparison";
|
||||
|
||||
protected DecompilerCodeComparisonPanel diffPanel;
|
||||
protected DecompilerCodeComparisonView comparisonProvider;
|
||||
protected boolean disableOnReadOnly;
|
||||
|
||||
/**
|
||||
|
@ -33,13 +33,13 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
|||
*
|
||||
* @param actionName name of action
|
||||
* @param owner owner of action
|
||||
* @param diffPanel diff panel containing action
|
||||
* @param comparisonProvider diff panel containing action
|
||||
* @param disableOnReadOnly if true, action will be disabled for read-only programs
|
||||
*/
|
||||
public AbstractMatchedTokensAction(String actionName, String owner,
|
||||
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
|
||||
DecompilerCodeComparisonView comparisonProvider, boolean disableOnReadOnly) {
|
||||
super(actionName, owner);
|
||||
this.diffPanel = diffPanel;
|
||||
this.comparisonProvider = comparisonProvider;
|
||||
this.disableOnReadOnly = disableOnReadOnly;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,9 @@ public class ApplyCalleeEmptySignatureFromMatchedTokensAction
|
|||
private PluginTool tool;
|
||||
public static final String ACTION_NAME = "Function Comparison Apply Callee Signature";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyCalleeEmptySignatureFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -53,7 +48,7 @@ public class ApplyCalleeEmptySignatureFromMatchedTokensAction
|
|||
@Override
|
||||
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||
|
|
|
@ -33,14 +33,9 @@ public class ApplyCalleeFunctionNameFromMatchedTokensAction
|
|||
private PluginTool tool;
|
||||
public static final String ACTION_NAME = "Function Comparison Apply Callee Name";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyCalleeFunctionNameFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -52,7 +47,7 @@ public class ApplyCalleeFunctionNameFromMatchedTokensAction
|
|||
@Override
|
||||
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||
|
|
|
@ -33,14 +33,9 @@ public class ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction
|
|||
public static final String ACTION_NAME =
|
||||
"Function Comparison Apply Callee Signature And Datatypes";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -53,7 +48,7 @@ public class ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction
|
|||
@Override
|
||||
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||
|
|
|
@ -35,9 +35,9 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Variable Skeleton Type";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
public ApplyEmptyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyEmptyVariableTypeFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -73,7 +73,7 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
|
|||
protected void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
|
@ -87,7 +87,7 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
|
||||
Program activeProgram = activeFunction.getProgram();
|
||||
|
||||
DataType dt = otherHighSymbol.getDataType();
|
||||
|
|
|
@ -36,14 +36,9 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Global Variable Name";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyGlobalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyGlobalNameFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -83,7 +78,7 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
|
|||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
: (ClangVariableToken) currentPair.rightToken();
|
||||
|
@ -96,7 +91,7 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Program activeProgram = context.getCodeComparisonPanel().getProgram(activeSide);
|
||||
Program activeProgram = context.getCodeComparisonView().getProgram(activeSide);
|
||||
|
||||
Symbol activeSymbol = null;
|
||||
if (activeHighSymbol instanceof HighCodeSymbol activeCodeSymbol) {
|
||||
|
|
|
@ -36,14 +36,9 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Local Variable Name";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyLocalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyLocalNameFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -82,7 +77,7 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
|
|||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
|
@ -96,7 +91,7 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
|
||||
Program activeProgram = activeFunction.getProgram();
|
||||
|
||||
try {
|
||||
|
|
|
@ -37,14 +37,9 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Variable Type";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyVariableTypeFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -80,7 +75,7 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
|
|||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
|
@ -94,7 +89,7 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
|
||||
Program activeProgram = activeFunction.getProgram();
|
||||
|
||||
DataType dt = otherHighSymbol.getDataType();
|
||||
|
|
|
@ -39,7 +39,6 @@ import ghidra.program.util.ProgramLocation;
|
|||
public class CDisplay {
|
||||
private final static String OPTIONS_TITLE = "Decompiler";
|
||||
|
||||
private ServiceProvider serviceProvider;
|
||||
private DecompilerController controller;
|
||||
private DecompileOptions decompileOptions;
|
||||
private FieldLocation lastCursorPosition;
|
||||
|
@ -53,7 +52,6 @@ public class CDisplay {
|
|||
DecompileResultsListener decompileListener,
|
||||
Consumer<ProgramLocation> locationConsumer) {
|
||||
|
||||
this.serviceProvider = serviceProvider;
|
||||
highlightController = new DiffClangHighlightController(comparisonOptions);
|
||||
|
||||
decompileOptions = new DecompileOptions();
|
||||
|
@ -138,10 +136,6 @@ public class CDisplay {
|
|||
controller.dispose();
|
||||
}
|
||||
|
||||
public DecompilerController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
saveCursorPosition();
|
||||
DecompileData data = getDecompileData();
|
||||
|
@ -176,8 +170,8 @@ public class CDisplay {
|
|||
}
|
||||
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
ToolOptions options = tool.getOptions(OPTIONS_TITLE);
|
||||
Program program = function == null ? null : function.getProgram();
|
||||
decompileOptions.grabFromToolAndProgram(fieldOptions, options, program);
|
||||
Program p = function == null ? null : function.getProgram();
|
||||
decompileOptions.grabFromToolAndProgram(fieldOptions, options, p);
|
||||
}
|
||||
|
||||
DiffClangHighlightController getHighlightController() {
|
||||
|
|
|
@ -35,7 +35,7 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedCalleeTo
|
|||
* @param diffPanel diff Panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonView diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, false);
|
||||
this.tool = tool;
|
||||
|
|
|
@ -151,4 +151,9 @@ public class DecompilerCodeComparisonOptions implements OptionsChangeListener {
|
|||
optionsChangedCallback.call();
|
||||
}
|
||||
|
||||
public void dispose(PluginTool tool) {
|
||||
ToolOptions options =
|
||||
tool.getOptions(DecompilerCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
|
||||
options.removeOptionsChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import docking.options.OptionsService;
|
|||
import generic.theme.GIcon;
|
||||
import ghidra.app.decompiler.component.DecompileData;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -49,10 +49,9 @@ import resources.Icons;
|
|||
import resources.MultiIcon;
|
||||
|
||||
/**
|
||||
* Panel that displays two decompilers for comparison
|
||||
* UI that displays two decompilers for comparison
|
||||
*/
|
||||
public class DecompilerCodeComparisonPanel
|
||||
extends CodeComparisonPanel {
|
||||
public class DecompilerCodeComparisonView extends CodeComparisonView {
|
||||
|
||||
public static final String NAME = "Decompiler View";
|
||||
|
||||
|
@ -61,7 +60,7 @@ public class DecompilerCodeComparisonPanel
|
|||
private Duo<CDisplay> cDisplays = new Duo<>();
|
||||
|
||||
private DecompilerCodeComparisonOptions comparisonOptions;
|
||||
private CodeDiffFieldPanelCoordinator coordinator;
|
||||
private DualDecompilerScrollCoordinator coordinator;
|
||||
private DecompileDataDiff decompileDataDiff;
|
||||
|
||||
private ToggleExactConstantMatching toggleExactConstantMatchingAction;
|
||||
|
@ -74,7 +73,7 @@ public class DecompilerCodeComparisonPanel
|
|||
* @param owner the owner of this panel
|
||||
* @param tool the tool displaying this panel
|
||||
*/
|
||||
public DecompilerCodeComparisonPanel(String owner, PluginTool tool) {
|
||||
public DecompilerCodeComparisonView(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
comparisonOptions = new DecompilerCodeComparisonOptions(tool, () -> repaint());
|
||||
|
||||
|
@ -119,7 +118,7 @@ public class DecompilerCodeComparisonPanel
|
|||
public void dispose() {
|
||||
setSynchronizedScrolling(false); // disposes any exiting coordinator
|
||||
cDisplays.each(CDisplay::dispose);
|
||||
comparisonOptions = null;
|
||||
comparisonOptions.dispose(tool);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,6 +188,7 @@ public class DecompilerCodeComparisonPanel
|
|||
actions.add(new ApplyCalleeFunctionNameFromMatchedTokensAction(this, tool));
|
||||
actions.add(new ApplyCalleeEmptySignatureFromMatchedTokensAction(this, tool));
|
||||
actions.add(new ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(this, tool));
|
||||
|
||||
}
|
||||
|
||||
private void decompileDataSet(Side side, DecompileData dcompileData) {
|
||||
|
@ -272,8 +272,9 @@ public class DecompilerCodeComparisonPanel
|
|||
}
|
||||
}
|
||||
|
||||
private CodeDiffFieldPanelCoordinator createCoordinator() {
|
||||
CodeDiffFieldPanelCoordinator panelCoordinator = new CodeDiffFieldPanelCoordinator(this);
|
||||
private DualDecompilerScrollCoordinator createCoordinator() {
|
||||
DualDecompilerScrollCoordinator panelCoordinator =
|
||||
new DualDecompilerScrollCoordinator(this);
|
||||
if (decompileDataDiff != null) {
|
||||
TaskBuilder.withRunnable(monitor -> {
|
||||
try {
|
||||
|
@ -371,7 +372,7 @@ public class DecompilerCodeComparisonPanel
|
|||
this.setToolBarData(new ToolBarData(NO_EXACT_CONSTANT_MATCHING_ICON, "toggles"));
|
||||
|
||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not constants must\n" +
|
||||
"be exactly the same value to be a match\n" + "in the Decomiler Diff View."));
|
||||
"be exactly the same value to be a match\nin the Decomiler Diff View."));
|
||||
setSelected(false);
|
||||
setEnabled(true);
|
||||
}
|
|
@ -67,11 +67,11 @@ public class DecompilerDiffViewFindAction extends DockingAction {
|
|||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DualDecompilerActionContext dualContext = (DualDecompilerActionContext) context;
|
||||
DecompilerCodeComparisonPanel decompilerCompPanel =
|
||||
dualContext.getCodeComparisonPanel();
|
||||
DecompilerCodeComparisonView provider =
|
||||
dualContext.getCodeComparisonView();
|
||||
|
||||
Side focusedSide = decompilerCompPanel.getActiveSide();
|
||||
DecompilerPanel focusedPanel = decompilerCompPanel.getDecompilerPanel(focusedSide);
|
||||
Side focusedSide = provider.getActiveSide();
|
||||
DecompilerPanel focusedPanel = provider.getDecompilerPanel(focusedSide);
|
||||
FindDialog dialog = findDialogs.get(focusedSide);
|
||||
if (dialog == null) {
|
||||
dialog = createFindDialog(focusedPanel, focusedSide);
|
||||
|
|
|
@ -33,12 +33,12 @@ public class DetermineDecompilerDifferencesTask extends Task {
|
|||
|
||||
private DecompileDataDiff decompileDataDiff;
|
||||
|
||||
private CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator;
|
||||
private DualDecompilerScrollCoordinator decompilerFieldPanelCoordinator;
|
||||
|
||||
public DetermineDecompilerDifferencesTask(DecompileDataDiff decompileDataDiff,
|
||||
boolean matchConstantsExactly, DiffClangHighlightController leftHighlightController,
|
||||
DiffClangHighlightController rightHighlightController,
|
||||
CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator) {
|
||||
DualDecompilerScrollCoordinator decompilerFieldPanelCoordinator) {
|
||||
|
||||
super("Mapping C Tokens Between Functions", true, true, true);
|
||||
this.decompileDataDiff = decompileDataDiff;
|
||||
|
|
|
@ -18,7 +18,6 @@ package ghidra.features.codecompare.decompile;
|
|||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
|
@ -38,26 +37,26 @@ import ghidra.util.datastruct.Duo.Side;
|
|||
public class DualDecompilerActionContext extends CodeComparisonActionContext
|
||||
implements RestrictedAddressSetContext {
|
||||
|
||||
private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
|
||||
private DecompilerCodeComparisonView comparisonProvider = null;
|
||||
private TokenPair tokenPair;
|
||||
private boolean overrideReadOnly = false;
|
||||
|
||||
/**
|
||||
* Creates an action context for a dual decompiler panel.
|
||||
* @param provider the provider for this context
|
||||
* @param panel the DecompilerComparisonPanel
|
||||
* @param comparisonProvider the DecompilerComparisonPanel
|
||||
* @param source the source of the action
|
||||
*/
|
||||
public DualDecompilerActionContext(ComponentProvider provider,
|
||||
DecompilerCodeComparisonPanel panel, Component source) {
|
||||
super(provider, panel, source);
|
||||
decompilerComparisonPanel = panel;
|
||||
DecompilerCodeComparisonView comparisonProvider, Component source) {
|
||||
super(provider, comparisonProvider, source);
|
||||
this.comparisonProvider = comparisonProvider;
|
||||
tokenPair = computeTokenPair();
|
||||
}
|
||||
|
||||
private TokenPair computeTokenPair() {
|
||||
DecompilerPanel focusedPanel =
|
||||
decompilerComparisonPanel.getActiveDisplay().getDecompilerPanel();
|
||||
comparisonProvider.getActiveDisplay().getDecompilerPanel();
|
||||
|
||||
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
|
||||
return null;
|
||||
|
@ -67,7 +66,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
if (focusedToken == null) {
|
||||
return null;
|
||||
}
|
||||
List<TokenBin> tokenBin = decompilerComparisonPanel.getHighBins();
|
||||
List<TokenBin> tokenBin = comparisonProvider.getHighBins();
|
||||
if (tokenBin == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -80,13 +79,9 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
return null;
|
||||
}
|
||||
|
||||
//loop over the tokens in the matching bin and return the first one in the same
|
||||
//class as focusedToken
|
||||
Iterator<ClangToken> tokenIter = matchedBin.iterator();
|
||||
while (tokenIter.hasNext()) {
|
||||
ClangToken currentMatch = tokenIter.next();
|
||||
for (ClangToken currentMatch : matchedBin) {
|
||||
if (currentMatch.getClass().equals(focusedToken.getClass())) {
|
||||
return decompilerComparisonPanel.getActiveSide() == LEFT
|
||||
return comparisonProvider.getActiveSide() == LEFT
|
||||
? new TokenPair(focusedToken, currentMatch)
|
||||
: new TokenPair(currentMatch, focusedToken);
|
||||
}
|
||||
|
@ -95,12 +90,12 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DecompilerCodeComparisonPanel} that generated this context
|
||||
* Returns the {@link DecompilerCodeComparisonView} that generated this context
|
||||
* @return the decompiler comparison panel that generated this context
|
||||
*/
|
||||
@Override
|
||||
public DecompilerCodeComparisonPanel getCodeComparisonPanel() {
|
||||
return decompilerComparisonPanel;
|
||||
public DecompilerCodeComparisonView getCodeComparisonView() {
|
||||
return comparisonProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +106,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
* context
|
||||
*/
|
||||
public HighFunction getHighFunction(Side side) {
|
||||
return decompilerComparisonPanel.getDecompilerPanel(side).getController().getHighFunction();
|
||||
return comparisonProvider.getDecompilerPanel(side).getController().getHighFunction();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +139,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
}
|
||||
|
||||
Program activeProgram =
|
||||
decompilerComparisonPanel.getProgram(decompilerComparisonPanel.getActiveSide());
|
||||
comparisonProvider.getProgram(comparisonProvider.getActiveSide());
|
||||
|
||||
if (activeProgram == null) {
|
||||
return true;
|
||||
|
|
|
@ -1,35 +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.features.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
abstract public class DualDecompilerFieldPanelCoordinator extends LineLockedFieldPanelCoordinator {
|
||||
|
||||
public DualDecompilerFieldPanelCoordinator(
|
||||
DecompilerCodeComparisonPanel dualDecompilerPanel) {
|
||||
super(new FieldPanel[] { dualDecompilerPanel.getDecompilerPanel(LEFT).getFieldPanel(),
|
||||
dualDecompilerPanel.getDecompilerPanel(RIGHT).getFieldPanel() });
|
||||
}
|
||||
|
||||
abstract public void leftLocationChanged(ProgramLocation leftLocation);
|
||||
|
||||
abstract public void rightLocationChanged(ProgramLocation rightLocation);
|
||||
}
|
|
@ -24,6 +24,8 @@ import java.util.List;
|
|||
import org.apache.commons.collections4.BidiMap;
|
||||
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
|
@ -33,11 +35,7 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Class to coordinate the scrolling of two decompiler panels as well as cursor location
|
||||
* highlighting due to cursor location changes.
|
||||
*/
|
||||
public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoordinator {
|
||||
public class DualDecompilerScrollCoordinator extends LineLockedFieldPanelScrollCoordinator {
|
||||
|
||||
private BidiMap<Integer, Integer> leftToRightLineNumberPairing;
|
||||
private List<ClangLine> leftLines = new ArrayList<ClangLine>();
|
||||
|
@ -51,12 +49,13 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
* @param dualDecompilerPanel decomp comparison panel
|
||||
* @param comparisonProvider decomp comparison provider
|
||||
*/
|
||||
public CodeDiffFieldPanelCoordinator(DecompilerCodeComparisonPanel dualDecompilerPanel) {
|
||||
super(dualDecompilerPanel);
|
||||
this.leftDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(LEFT);
|
||||
this.rightDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(RIGHT);
|
||||
public DualDecompilerScrollCoordinator(DecompilerCodeComparisonView comparisonProvider) {
|
||||
super(new FieldPanel[] { comparisonProvider.getDecompilerPanel(LEFT).getFieldPanel(),
|
||||
comparisonProvider.getDecompilerPanel(RIGHT).getFieldPanel() });
|
||||
this.leftDecompilerPanel = comparisonProvider.getDecompilerPanel(LEFT);
|
||||
this.rightDecompilerPanel = comparisonProvider.getDecompilerPanel(RIGHT);
|
||||
leftToRightLineNumberPairing = new DualHashBidiMap<>();
|
||||
}
|
||||
|
||||
|
@ -89,7 +88,6 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leftLocationChanged(ProgramLocation leftLocation) {
|
||||
DecompilerLocation leftDecompilerLocation = (DecompilerLocation) leftLocation;
|
||||
|
||||
|
@ -107,7 +105,6 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
panelViewChanged(leftDecompilerPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rightLocationChanged(ProgramLocation rightLocation) {
|
||||
DecompilerLocation rightDecompilerLocation = (DecompilerLocation) rightLocation;
|
||||
|
||||
|
@ -301,5 +298,4 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
|
||||
/**
|
||||
* Action context for a dual Function Graph panel.
|
||||
*/
|
||||
public class FgComparisonContext extends CodeComparisonActionContext {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
private FgDisplay display;
|
||||
private boolean isLeft;
|
||||
|
||||
public FgComparisonContext(ComponentProvider provider,
|
||||
FunctionGraphCodeComparisonView fgPanel,
|
||||
FgDisplay display, Component component, boolean isLeft) {
|
||||
super(provider, fgPanel, component);
|
||||
this.fgProvider = fgPanel;
|
||||
this.display = display;
|
||||
this.isLeft = isLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeComparisonView getCodeComparisonView() {
|
||||
return fgProvider;
|
||||
}
|
||||
|
||||
public FgDisplay getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
public boolean isLeft() {
|
||||
return isLeft;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,535 @@
|
|||
/* ###
|
||||
* 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.features.codecompare.functiongraph;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.tool.ToolConstants;
|
||||
import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.app.plugin.core.functiongraph.*;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.flowchart.FlowChartLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
||||
import ghidra.app.services.ClipboardService;
|
||||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.ServiceListener;
|
||||
import ghidra.graph.viewer.options.VisualGraphOptions;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.UniversalIdGenerator;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
|
||||
/**
|
||||
* This class displays a Function Graph in the left or right side of the Function Comparison view.
|
||||
*/
|
||||
public class FgDisplay implements OptionsChangeListener {
|
||||
|
||||
private static final String FUNCTION_GRAPH_NAME = "Function Graph";
|
||||
|
||||
private PluginTool tool;
|
||||
private String owner;
|
||||
private Program program;
|
||||
private FGController controller;
|
||||
private FgOptions fgOptions;
|
||||
private FormatManager userDefinedFormatManager;
|
||||
private ProgramLocation currentLocation;
|
||||
|
||||
private FgDisplayProgramListener programListener = new FgDisplayProgramListener();
|
||||
private FgServiceListener serviceListener = new FgServiceListener();
|
||||
|
||||
private FGColorProvider colorProvider;
|
||||
private List<FGLayoutProvider> layoutProviders;
|
||||
|
||||
private Consumer<ProgramLocation> locationConsumer;
|
||||
private Consumer<FgDisplay> graphChangedConsumer;
|
||||
|
||||
// Note: this class should probably be using code block highlights and not the code-level
|
||||
// highlights already provided by the Listing.
|
||||
// FgHighlighter highlighter;
|
||||
|
||||
FgDisplay(FunctionGraphCodeComparisonView fgView,
|
||||
Consumer<ProgramLocation> locationConsumer, Consumer<FgDisplay> graphChangedConsumer) {
|
||||
|
||||
this.tool = fgView.getTool();
|
||||
this.owner = fgView.getOwner();
|
||||
this.locationConsumer = locationConsumer;
|
||||
this.graphChangedConsumer = graphChangedConsumer;
|
||||
fgOptions = new FgOptions();
|
||||
|
||||
layoutProviders = loadLayoutProviders();
|
||||
colorProvider = new IndependentColorProvider(tool);
|
||||
|
||||
init();
|
||||
|
||||
FgComparisonEnv env = new FgComparisonEnv();
|
||||
FGControllerListener listener = new FgCoparisonControllerListener();
|
||||
controller = new FGController(env, listener);
|
||||
|
||||
setDefaultLayout();
|
||||
}
|
||||
|
||||
private void setDefaultLayout() {
|
||||
FGLayoutProvider initialLayout = layoutProviders.get(0);
|
||||
for (FGLayoutProvider layout : layoutProviders) {
|
||||
if (layout.getClass().equals(FlowChartLayoutProvider.class)) {
|
||||
initialLayout = layout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
controller.changeLayout(initialLayout);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
tool.addServiceListener(serviceListener);
|
||||
|
||||
ColorizingService colorizingService = tool.getService(ColorizingService.class);
|
||||
if (colorizingService != null) {
|
||||
colorProvider = new ToolBasedColorProvider(() -> program, colorizingService);
|
||||
}
|
||||
}
|
||||
|
||||
private List<FGLayoutProvider> loadLayoutProviders() {
|
||||
|
||||
// Shared Code Note: This code is mirrored in the FgDisplay for the Code Comparison API
|
||||
|
||||
FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
|
||||
List<FGLayoutProvider> instances = layoutFinder.findLayouts();
|
||||
if (instances.isEmpty()) {
|
||||
throw new AssertException("Could not find any layout providers. You project may not " +
|
||||
"be configured properly.");
|
||||
}
|
||||
|
||||
List<FGLayoutProvider> layouts = new ArrayList<>(instances);
|
||||
Collections.sort(layouts, (o1, o2) -> -o1.getPriorityLevel() + o2.getPriorityLevel());
|
||||
return layouts;
|
||||
}
|
||||
|
||||
private void initializeOptions() {
|
||||
ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
|
||||
options.removeOptionsChangeListener(this);
|
||||
options.addOptionsChangeListener(this);
|
||||
|
||||
// Graph -> Function Graph
|
||||
Options subOptions = options.getOptions(FUNCTION_GRAPH_NAME);
|
||||
|
||||
fgOptions.registerOptions(subOptions);
|
||||
fgOptions.loadOptions(subOptions);
|
||||
|
||||
for (FGLayoutProvider layoutProvider : layoutProviders) {
|
||||
|
||||
// Graph -> Function Graph -> Layout Name
|
||||
String layoutName = layoutProvider.getLayoutName();
|
||||
Options layoutToolOptions = subOptions.getOptions(layoutName);
|
||||
FGLayoutOptions layoutOptions = layoutProvider.createLayoutOptions(layoutToolOptions);
|
||||
if (layoutOptions == null) {
|
||||
continue; // many layouts do not have options
|
||||
}
|
||||
|
||||
layoutOptions.registerOptions(layoutToolOptions);
|
||||
layoutOptions.loadOptions(layoutToolOptions);
|
||||
fgOptions.setLayoutOptions(layoutName, layoutOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public FGController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public JComponent getComponent() {
|
||||
return controller.getViewComponent();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (program != null) {
|
||||
program.removeListener(programListener);
|
||||
program = null;
|
||||
}
|
||||
programListener.dispose();
|
||||
controller.cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
// Graph -> Function Graph
|
||||
Options subOptions = options.getOptions(FUNCTION_GRAPH_NAME);
|
||||
fgOptions.loadOptions(subOptions);
|
||||
|
||||
controller.optionsChanged();
|
||||
|
||||
if (fgOptions.optionChangeRequiresRelayout(optionName)) {
|
||||
controller.refresh(true);
|
||||
}
|
||||
else if (VisualGraphOptions.VIEW_RESTORE_OPTIONS_KEY.equals(optionName)) {
|
||||
controller.clearViewSettings();
|
||||
}
|
||||
else {
|
||||
controller.refreshDisplayWithoutRebuilding();
|
||||
}
|
||||
}
|
||||
|
||||
public void showFunction(Function function) {
|
||||
updateProgram(function);
|
||||
|
||||
if (function == null) {
|
||||
controller.setStatusMessage("No Function");
|
||||
return;
|
||||
}
|
||||
if (function.isExternal()) {
|
||||
String name = function.getName(true);
|
||||
controller.setStatusMessage("\"" + name + "\" is an external function.");
|
||||
return;
|
||||
}
|
||||
|
||||
Address entry = function.getEntryPoint();
|
||||
currentLocation = new ProgramLocation(program, entry);
|
||||
controller.display(program, currentLocation);
|
||||
}
|
||||
|
||||
public void setLocation(ProgramLocation location) {
|
||||
controller.setLocation(location);
|
||||
}
|
||||
|
||||
public ProgramLocation getLocation() {
|
||||
return controller.getLocation();
|
||||
}
|
||||
|
||||
private void updateProgram(Function function) {
|
||||
Program newProgram = function == null ? null : function.getProgram();
|
||||
if (program == newProgram) {
|
||||
return;
|
||||
}
|
||||
if (program != null) {
|
||||
program.removeListener(programListener);
|
||||
}
|
||||
|
||||
program = newProgram;
|
||||
|
||||
if (program != null) {
|
||||
program.addListener(programListener);
|
||||
initializeOptions();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return controller.isBusy();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
controller.refresh(true);
|
||||
}
|
||||
|
||||
private class FgComparisonEnv implements FgEnv {
|
||||
|
||||
private Navigatable navigatable = new DummyNavigatable();
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionGraphOptions getOptions() {
|
||||
return fgOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FGLayoutProvider> getLayoutProviders() {
|
||||
return layoutProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAction(DockingAction action) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public FGColorProvider getColorProvider() {
|
||||
return colorProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormatManager getUserDefinedFormat() {
|
||||
return userDefinedFormatManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserDefinedFormat(FormatManager format) {
|
||||
userDefinedFormatManager = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigatable getNavigatable() {
|
||||
return navigatable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getToolLocation() {
|
||||
// this isn't really the tool's location, but maybe this is fine for this display
|
||||
return currentLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getGraphLocation() {
|
||||
return currentLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
controller.setSelection(selection);
|
||||
}
|
||||
}
|
||||
|
||||
private class FgCoparisonControllerListener implements FGControllerListener {
|
||||
|
||||
@Override
|
||||
public void dataChanged() {
|
||||
graphChangedConsumer.accept(FgDisplay.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userChangedLocation(ProgramLocation location, boolean vertexChanged) {
|
||||
currentLocation = location;
|
||||
locationConsumer.accept(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userChangedSelection(ProgramSelection selection) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userSelectedText(String s) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userNavigated(ProgramLocation location) {
|
||||
// stub
|
||||
}
|
||||
}
|
||||
|
||||
private class DummyNavigatable implements Navigatable {
|
||||
|
||||
private long id;
|
||||
|
||||
DummyNavigatable() {
|
||||
id = UniversalIdGenerator.nextID().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInstanceID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Program p, ProgramLocation pl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationMemento getMemento() {
|
||||
return new FgMemento(); // dummy
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMemento(LocationMemento memento) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getNavigatableIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMarkers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlight(ProgramSelection highlight) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getSelection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getHighlight() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextSelection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNavigatableListener(NavigatableRemovalListener listener) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNavigatableListener(NavigatableRemovalListener listener) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisposed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHighlight() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlightProvider(ListingHighlightProvider highlightProvider,
|
||||
Program program) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHighlightProvider(ListingHighlightProvider highlightProvider,
|
||||
Program p) {
|
||||
// stub
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FgMemento extends LocationMemento {
|
||||
FgMemento() {
|
||||
super((Program) null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private class FgDisplayProgramListener implements DomainObjectListener {
|
||||
|
||||
private SwingUpdateManager updater = new SwingUpdateManager(500, 5000, () -> refresh());
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
updater.update();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
updater.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private class FgServiceListener implements ServiceListener {
|
||||
|
||||
@Override
|
||||
public void serviceAdded(Class<?> interfaceClass, Object service) {
|
||||
if (interfaceClass == ClipboardService.class) {
|
||||
// if we decide to support copy/paste in this viewer, then the FGClipboardProvider
|
||||
// needs to be taken out of the FGProvider and made independent. We would also need
|
||||
// to refactor the ClipboardPlugin to not need a provider, instead adding a way to
|
||||
// get a component, context and to add/remove actions.
|
||||
}
|
||||
else if (interfaceClass == ColorizingService.class) {
|
||||
colorProvider =
|
||||
new ToolBasedColorProvider(() -> program, (ColorizingService) service);
|
||||
controller.refresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serviceRemoved(Class<?> interfaceClass, Object service) {
|
||||
if (interfaceClass == ColorizingService.class) {
|
||||
colorProvider = new IndependentColorProvider(tool);
|
||||
controller.refresh(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FgOptions extends FunctionGraphOptions {
|
||||
|
||||
@Override
|
||||
public void setUseAnimation(boolean useAnimation) {
|
||||
// don't allow this to be changed; animations seem like overkill for comparisons
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadOptions(Options options) {
|
||||
super.loadOptions(options);
|
||||
|
||||
useAnimation = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* 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.features.codecompare.functiongraph;
|
||||
|
||||
import ghidra.app.util.viewer.listingpanel.ProgramLocationTranslator;
|
||||
import ghidra.program.util.ListingAddressCorrelation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
/**
|
||||
* A class to synchronize locations between the left and right Function Graph comparison panels.
|
||||
*/
|
||||
class FgDisplaySynchronizer {
|
||||
|
||||
private Duo<FgDisplay> displays;
|
||||
private ProgramLocationTranslator locationTranslator;
|
||||
|
||||
FgDisplaySynchronizer(Duo<FgDisplay> displays, ListingAddressCorrelation correlation) {
|
||||
this.displays = displays;
|
||||
this.locationTranslator = new ProgramLocationTranslator(correlation);
|
||||
}
|
||||
|
||||
void setLocation(Side side, ProgramLocation location) {
|
||||
// Only set other side's cursor if we are coordinating right now.
|
||||
Side otherSide = side.otherSide();
|
||||
ProgramLocation otherLocation = locationTranslator.getProgramLocation(otherSide, location);
|
||||
if (otherLocation != null) {
|
||||
displays.get(otherSide).setLocation(otherLocation);
|
||||
}
|
||||
}
|
||||
|
||||
void sync(Side side) {
|
||||
ProgramLocation programLocation = displays.get(side).getLocation();
|
||||
if (programLocation != null) {
|
||||
setLocation(side, programLocation);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
// this object should probably have a dispose() method
|
||||
// locationTranslator.dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,508 @@
|
|||
/* ###
|
||||
* 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.features.codecompare.functiongraph;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.features.base.codecompare.listing.LinearAddressCorrelation;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.codecompare.functiongraph.actions.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.graph.viewer.GraphSatelliteListener;
|
||||
import ghidra.program.model.correlate.HashedFunctionAddressCorrelation;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.ListingAddressCorrelation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import help.Help;
|
||||
|
||||
/**
|
||||
* Provides a {@link CodeComparisonView} for displaying function graphs.
|
||||
* <P>
|
||||
* Known Issues:
|
||||
* <UL>
|
||||
* <LI>The options used by this API are the same as those for the Function Graph plugin. We
|
||||
* may find that some of the options should not apply to this API. If true, then we would
|
||||
* have to create a new options entry in the tool and a different options object for this
|
||||
* API to use.
|
||||
* </LI>
|
||||
* <LI>Each open panel will potentially update the state that is saved in the tool. This can
|
||||
* lead to confusion when multiple open windows have different settings, as it is not
|
||||
* clear which window's settings will get saved.
|
||||
* </LI>
|
||||
* <LI>The views do not support copying, which is consistent with the other function comparison
|
||||
* views.
|
||||
* </LI>
|
||||
* </UL>
|
||||
*/
|
||||
public class FunctionGraphCodeComparisonView extends CodeComparisonView {
|
||||
|
||||
public static final String NAME = "Function Graph View";
|
||||
|
||||
private static final String FORMAT_KEY = "FIELD_FORMAT";
|
||||
private static final String SHOW_POPUPS_KEY = "SHOW_POPUPS";
|
||||
private static final String SHOW_SATELLITE_KEY = "SHOW_SATELLITE";
|
||||
|
||||
private static final String LAYOUT_NAME = "LAYOUT_NAME";
|
||||
private static final String COMPLEX_LAYOUT_NAME = "COMPLEX_LAYOUT_NAME";
|
||||
private static final String LAYOUT_CLASS_NAME = "LAYOUT_CLASS_NAME";
|
||||
|
||||
private FgDisplaySynchronizer coordinator;
|
||||
|
||||
private Duo<FgDisplay> displays = new Duo<>();
|
||||
private Duo<Function> functions = new Duo<>();
|
||||
|
||||
private ListingAddressCorrelation addressCorrelator;
|
||||
private boolean displaysLocked;
|
||||
|
||||
private SaveState defaultSaveState;
|
||||
private SaveState saveState;
|
||||
private List<DockingAction> actions = new ArrayList<>();
|
||||
private FgTogglePopupsAction showPopupsAction;
|
||||
private FgToggleSatelliteAction showSatelliteAction;
|
||||
|
||||
public FunctionGraphCodeComparisonView(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
|
||||
Help.getHelpService()
|
||||
.registerHelp(this, new HelpLocation(HELP_TOPIC, "FunctionGraph_Diff_View"));
|
||||
|
||||
displays = buildDisplays();
|
||||
createActions();
|
||||
installSatelliteListeners();
|
||||
buildDefaultSaveState();
|
||||
|
||||
buildPanel();
|
||||
setSynchronizedScrolling(true);
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public Duo<FgDisplay> getDisplays() {
|
||||
return displays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by actions to signal that the user changed something worth saving.
|
||||
*/
|
||||
public void stateChanged() {
|
||||
saveShowPopups(saveState);
|
||||
saveShowSatellite(saveState);
|
||||
saveLayout(saveState);
|
||||
saveCustomFormat(saveState);
|
||||
|
||||
if (!hasStateChanges()) {
|
||||
// This implies the user has made changes, but those changes match the default settings.
|
||||
// Clear the save state so no changes get written to the tool.
|
||||
saveState.clear();
|
||||
}
|
||||
|
||||
tool.setConfigChanged(true);
|
||||
}
|
||||
|
||||
private void buildDefaultSaveState() {
|
||||
SaveState ss = new SaveState();
|
||||
saveShowPopups(ss);
|
||||
saveLayout(ss);
|
||||
saveCustomFormat(ss);
|
||||
defaultSaveState = ss;
|
||||
}
|
||||
|
||||
private void saveShowPopups(SaveState ss) {
|
||||
ss.putBoolean(SHOW_POPUPS_KEY, showPopupsAction.isSelected());
|
||||
}
|
||||
|
||||
private void saveShowSatellite(SaveState ss) {
|
||||
ss.putBoolean(SHOW_SATELLITE_KEY, showSatelliteAction.isSelected());
|
||||
}
|
||||
|
||||
private void loadShowPopups(SaveState ss) {
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
boolean currentShowPopups = leftController.arePopupsVisible();
|
||||
boolean savedShowPopups = ss.getBoolean(SHOW_POPUPS_KEY, currentShowPopups);
|
||||
if (currentShowPopups == savedShowPopups) {
|
||||
return;
|
||||
}
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
|
||||
leftController.setPopupsVisible(savedShowPopups);
|
||||
rightController.setPopupsVisible(savedShowPopups);
|
||||
showPopupsAction.setSelected(savedShowPopups);
|
||||
}
|
||||
|
||||
private void loadShowSatellite(SaveState ss) {
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
boolean currentShowSatellite = leftController.isSatelliteVisible();
|
||||
boolean savedShowSatellite = ss.getBoolean(SHOW_SATELLITE_KEY, currentShowSatellite);
|
||||
if (currentShowSatellite == savedShowSatellite) {
|
||||
return;
|
||||
}
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
|
||||
leftController.setSatelliteVisible(savedShowSatellite);
|
||||
rightController.setSatelliteVisible(savedShowSatellite);
|
||||
showSatelliteAction.setSelected(savedShowSatellite);
|
||||
}
|
||||
|
||||
private void saveLayout(SaveState ss) {
|
||||
FgDisplay display = displays.get(LEFT);
|
||||
FGController controller = display.getController();
|
||||
FGLayoutProvider layout = controller.getLayoutProvider();
|
||||
|
||||
SaveState layoutState = new SaveState(COMPLEX_LAYOUT_NAME);
|
||||
String layoutName = layout.getLayoutName();
|
||||
layoutState.putString(LAYOUT_NAME, layoutName);
|
||||
layoutState.putString(LAYOUT_CLASS_NAME, layout.getClass().getName());
|
||||
ss.putSaveState(COMPLEX_LAYOUT_NAME, layoutState);
|
||||
}
|
||||
|
||||
private void loadLayout(SaveState ss) {
|
||||
|
||||
SaveState layoutState = saveState.getSaveState(COMPLEX_LAYOUT_NAME);
|
||||
if (layoutState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
FGLayoutProvider layout = leftController.getLayoutProvider();
|
||||
|
||||
String savedLayoutName = layoutState.getString(LAYOUT_NAME, layout.getLayoutName());
|
||||
if (layout.getLayoutName().equals(savedLayoutName)) {
|
||||
return; // already set
|
||||
}
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
|
||||
FgEnv env = leftController.getEnv();
|
||||
List<FGLayoutProvider> layoutProviders = new ArrayList<>(env.getLayoutProviders());
|
||||
for (FGLayoutProvider layoutProvider : layoutProviders) {
|
||||
String providerName = layoutProvider.getLayoutName();
|
||||
if (providerName.equals(savedLayoutName)) {
|
||||
leftController.changeLayout(layoutProvider);
|
||||
rightController.changeLayout(layoutProvider);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCustomFormat(SaveState ss) {
|
||||
FgDisplay display = displays.get(LEFT);
|
||||
FGController controller = display.getController();
|
||||
FormatManager format = controller.getMinimalFormatManager();
|
||||
SaveState formatState = new SaveState();
|
||||
format.saveState(formatState);
|
||||
ss.putSaveState(FORMAT_KEY, formatState);
|
||||
}
|
||||
|
||||
private void loadCustomFormat(SaveState ss) {
|
||||
|
||||
SaveState formatState = ss.getSaveState(FORMAT_KEY);
|
||||
if (formatState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
|
||||
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
FormatManager format = leftController.getMinimalFormatManager();
|
||||
SaveState testState = new SaveState();
|
||||
format.saveState(testState);
|
||||
|
||||
if (equals(testState, formatState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FormatManager formatManager = new FormatManager(displayOptions, fieldOptions);
|
||||
formatManager.readState(formatState);
|
||||
leftController.updateMinimalFormatManager(formatManager);
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.updateMinimalFormatManager(formatManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSaveState(SaveState ss) {
|
||||
this.saveState = ss;
|
||||
|
||||
if (!hasStateChanges()) {
|
||||
return; // the given state matches the default state; nothing to do
|
||||
}
|
||||
|
||||
loadShowPopups(ss);
|
||||
loadShowSatellite(ss);
|
||||
loadLayout(ss);
|
||||
loadCustomFormat(ss);
|
||||
}
|
||||
|
||||
private boolean hasStateChanges() {
|
||||
if (!saveState.isEmpty()) {
|
||||
return !equals(saveState, defaultSaveState);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean equals(SaveState state1, SaveState state2) {
|
||||
String s1 = state1.toString();
|
||||
String s2 = state2.toString();
|
||||
return Objects.equals(s1, s2);
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
// Note: many of these actions are similar to what is in the main Function Graph. The way
|
||||
// this class is coded, the actions do not share keybindings. This is something we may wish
|
||||
// to change in the future by making the key binding type sharable.
|
||||
|
||||
// Both displays have the same actions they get from the Function Graph API. We will add
|
||||
// them to the comparison provider. We only need to add one set of actions, since they are
|
||||
// the same for both providers.
|
||||
FgDisplay left = displays.get(LEFT);
|
||||
actions.add(new FgResetGraphAction(left));
|
||||
|
||||
FgDisplay right = displays.get(RIGHT);
|
||||
actions.add(new FgResetGraphAction(right));
|
||||
|
||||
showPopupsAction = new FgTogglePopupsAction(this);
|
||||
FGController controller = left.getController();
|
||||
boolean showPopups = controller.arePopupsVisible();
|
||||
showPopupsAction.setSelected(showPopups);
|
||||
|
||||
showSatelliteAction = new FgToggleSatelliteAction(this);
|
||||
boolean showSatellite = controller.isSatelliteVisible();
|
||||
showSatelliteAction.setSelected(showSatellite);
|
||||
|
||||
actions.add(showSatelliteAction);
|
||||
actions.add(showPopupsAction);
|
||||
actions.add(new FgRelayoutAction(this));
|
||||
actions.add(new FgChooseFormatAction(this));
|
||||
}
|
||||
|
||||
private void installSatelliteListeners() {
|
||||
|
||||
FgDisplay left = displays.get(LEFT);
|
||||
FgDisplay right = displays.get(RIGHT);
|
||||
FGController leftController = left.getController();
|
||||
FGController rightController = right.getController();
|
||||
|
||||
GraphSatelliteListener listener = new GraphSatelliteListener() {
|
||||
|
||||
@Override
|
||||
public void satelliteVisibilityChanged(boolean docked, boolean visible) {
|
||||
if (visible) {
|
||||
leftController.setSatelliteVisible(true);
|
||||
rightController.setSatelliteVisible(true);
|
||||
}
|
||||
showSatelliteAction.setSelected(visible);
|
||||
stateChanged();
|
||||
}
|
||||
};
|
||||
|
||||
FGView lv = leftController.getView();
|
||||
FGView rv = rightController.getView();
|
||||
lv.addSatelliteListener(listener);
|
||||
rv.addSatelliteListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingAction> getActions() {
|
||||
List<DockingAction> superActions = super.getActions();
|
||||
superActions.addAll(0, actions);
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void comparisonDataChanged() {
|
||||
|
||||
maybeLoadFunction(LEFT, comparisonData.get(LEFT).getFunction());
|
||||
maybeLoadFunction(RIGHT, comparisonData.get(RIGHT).getFunction());
|
||||
|
||||
addressCorrelator = createCorrelator();
|
||||
// updateProgramViews();
|
||||
updateCoordinator();
|
||||
//initializeCursorMarkers();
|
||||
updateActionEnablement();
|
||||
validate();
|
||||
}
|
||||
|
||||
private ListingAddressCorrelation createCorrelator() {
|
||||
Function f1 = getFunction(LEFT);
|
||||
Function f2 = getFunction(RIGHT);
|
||||
if (f1 != null && f2 != null) {
|
||||
try {
|
||||
return new HashedFunctionAddressCorrelation(f1, f2, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException | MemoryAccessException e) {
|
||||
// fall back to linear address correlation
|
||||
}
|
||||
}
|
||||
if (comparisonData.get(LEFT).isEmpty() || comparisonData.get(RIGHT).isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new LinearAddressCorrelation(comparisonData);
|
||||
}
|
||||
|
||||
private void updateCoordinator() {
|
||||
if (coordinator != null) {
|
||||
coordinator.dispose();
|
||||
coordinator = null;
|
||||
}
|
||||
if (displaysLocked) {
|
||||
coordinator = new FgDisplaySynchronizer(displays, addressCorrelator);
|
||||
coordinator.sync(activeSide);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeLoadFunction(Side side, Function function) {
|
||||
// we keep a local copy of the function so we know if it is already decompiled
|
||||
if (functions.get(side) == function) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the scroll info and highlight info to prevent unnecessary highlighting, etc.
|
||||
loadFunction(side, null);
|
||||
loadFunction(side, function);
|
||||
|
||||
}
|
||||
|
||||
private void loadFunction(Side side, Function function) {
|
||||
if (functions.get(side) != function) {
|
||||
functions = functions.with(side, function);
|
||||
displays.get(side).showFunction(function);
|
||||
}
|
||||
}
|
||||
|
||||
private Duo<FgDisplay> buildDisplays() {
|
||||
|
||||
// The function graph's display is not ready until a graph is loaded. It also gets rebuilt
|
||||
// each time the graph is reloaded. To correctly install listeners, we need to update them
|
||||
// as the graph is rebuilt.
|
||||
Consumer<FgDisplay> graphChangedCallback = display -> {
|
||||
Side side = getSide(display);
|
||||
addMouseAndFocusListeners(side);
|
||||
};
|
||||
|
||||
FgDisplay leftDisplay =
|
||||
new FgDisplay(this, l -> locationChanged(LEFT, l), graphChangedCallback);
|
||||
FgDisplay rightDisplay =
|
||||
new FgDisplay(this, l -> locationChanged(RIGHT, l), graphChangedCallback);
|
||||
|
||||
return new Duo<>(leftDisplay, rightDisplay);
|
||||
}
|
||||
|
||||
private Side getSide(FgDisplay display) {
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
if (display == leftDisplay) {
|
||||
return LEFT;
|
||||
}
|
||||
return RIGHT;
|
||||
}
|
||||
|
||||
private void locationChanged(Side side, ProgramLocation location) {
|
||||
if (coordinator != null) {
|
||||
coordinator.setLocation(side, location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
setSynchronizedScrolling(false); // disposes any exiting coordinator
|
||||
displays.each(FgDisplay::dispose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display from the active side.
|
||||
* @return the active display
|
||||
*/
|
||||
public FgDisplay getActiveDisplay() {
|
||||
return displays.get(activeSide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
|
||||
FgDisplay display = getActiveDisplay();
|
||||
Component component = event != null ? event.getComponent()
|
||||
: display.getComponent();
|
||||
boolean isLeft = activeSide == LEFT;
|
||||
return new FgComparisonContext(provider, this, display, component, isLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionEnablement() {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSynchronizedScrolling(boolean synchronize) {
|
||||
if (coordinator != null) {
|
||||
coordinator.dispose();
|
||||
coordinator = null;
|
||||
}
|
||||
|
||||
displaysLocked = synchronize;
|
||||
if (displaysLocked) {
|
||||
coordinator = new FgDisplaySynchronizer(displays, addressCorrelator);
|
||||
coordinator.sync(activeSide);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComparisonComponent(Side side) {
|
||||
return displays.get(side).getComponent();
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return displays.get(LEFT).isBusy() || displays.get(RIGHT).isBusy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* ###
|
||||
* 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.features.codecompare.functiongraph.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.features.codecompare.functiongraph.FgComparisonContext;
|
||||
import ghidra.features.codecompare.functiongraph.FgDisplay;
|
||||
|
||||
public abstract class AbstractFgAction extends DockingAction {
|
||||
|
||||
private FgDisplay display;
|
||||
|
||||
protected AbstractFgAction(FgDisplay display, String name) {
|
||||
super(name, display.getOwner());
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
protected boolean isMyDisplay(ActionContext context) {
|
||||
if (!(context instanceof FgComparisonContext fgContext)) {
|
||||
return false;
|
||||
}
|
||||
return fgContext.getDisplay() == display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return isMyDisplay(context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* ###
|
||||
* 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.features.codecompare.functiongraph.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.features.codecompare.functiongraph.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
public class FgChooseFormatAction extends DockingAction {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
|
||||
public FgChooseFormatAction(FunctionGraphCodeComparisonView fgProvider) {
|
||||
super("Edit Code Block Fields", fgProvider.getOwner());
|
||||
this.fgProvider = fgProvider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Edit Fields" }));
|
||||
|
||||
setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Format"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
leftController.showFormatChooser();
|
||||
|
||||
FormatManager leftFormatManager = leftController.getMinimalFormatManager();
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.updateMinimalFormatManager(leftFormatManager);
|
||||
|
||||
fgProvider.stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof FgComparisonContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController controller = leftDisplay.getController();
|
||||
return controller.hasResults();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* ###
|
||||
* 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.features.codecompare.functiongraph.actions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.widgets.dialogs.ObjectChooserDialog;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FgEnv;
|
||||
import ghidra.features.codecompare.functiongraph.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
public class FgRelayoutAction extends DockingAction {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
|
||||
public FgRelayoutAction(FunctionGraphCodeComparisonView fgProvider) {
|
||||
super("Relayout Graph", fgProvider.getOwner(), KeyBindingType.SHARED);
|
||||
this.fgProvider = fgProvider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Relayout Graph" }));
|
||||
|
||||
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
FgEnv env = leftController.getEnv();
|
||||
List<FGLayoutProvider> layoutProviders = new ArrayList<>(env.getLayoutProviders());
|
||||
ObjectChooserDialog<FGLayoutProvider> dialog =
|
||||
new ObjectChooserDialog<>("Choose Layout", FGLayoutProvider.class, layoutProviders,
|
||||
"getLayoutName");
|
||||
FGLayoutProvider currentLayout = leftController.getLayoutProvider();
|
||||
dialog.setSelectedObject(currentLayout);
|
||||
PluginTool tool = env.getTool();
|
||||
tool.showDialog(dialog);
|
||||
|
||||
FGLayoutProvider layoutProvider = dialog.getSelectedObject();
|
||||
if (layoutProvider == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
leftController.changeLayout(layoutProvider);
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.changeLayout(layoutProvider);
|
||||
|
||||
fgProvider.stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return context instanceof FgComparisonContext;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* ###
|
||||
* 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.features.codecompare.functiongraph.actions;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.features.codecompare.functiongraph.FgDisplay;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
public class FgResetGraphAction extends AbstractFgAction {
|
||||
|
||||
private FgDisplay display;
|
||||
|
||||
public FgResetGraphAction(FgDisplay display) {
|
||||
super(display, "Reset Graph");
|
||||
this.display = display;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Reset Graph" }));
|
||||
|
||||
setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Reload_Graph"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
FGController controller = display.getController();
|
||||
JComponent component = controller.getViewComponent();
|
||||
int choice = OptionDialog.showYesNoDialog(component, "Reset Graph?",
|
||||
"<html>Erase all vertex position and grouping information?");
|
||||
if (choice != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
controller.resetGraph();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!super.isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
FGController controller = display.getController();
|
||||
return controller.hasResults();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.features.codecompare.functiongraph.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
/**
|
||||
* An action to toggle popup enablement for the Function Graph comparison views.
|
||||
*/
|
||||
public class FgTogglePopupsAction extends ToggleDockingAction {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
|
||||
public FgTogglePopupsAction(FunctionGraphCodeComparisonView fgProvider) {
|
||||
super("Display Popup Windows", fgProvider.getOwner());
|
||||
this.fgProvider = fgProvider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Display Popup Windows" }));
|
||||
|
||||
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Popups"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
|
||||
FGController controller = leftDisplay.getController();
|
||||
boolean visible = isSelected();
|
||||
controller.setPopupsVisible(visible);
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.setPopupsVisible(visible);
|
||||
|
||||
fgProvider.stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof FgComparisonContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController controller = leftDisplay.getController();
|
||||
return controller.hasResults();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.features.codecompare.functiongraph.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
/**
|
||||
* An action to toggle satellite enablement for the Function Graph comparison views.
|
||||
*/
|
||||
public class FgToggleSatelliteAction extends ToggleDockingAction {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
|
||||
public FgToggleSatelliteAction(FunctionGraphCodeComparisonView fgProvider) {
|
||||
super("Display Satellite View", fgProvider.getOwner());
|
||||
this.fgProvider = fgProvider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Display Satellite" }));
|
||||
|
||||
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Satellite_View"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
|
||||
FGController leftController = leftDisplay.getController();
|
||||
boolean visible = isSelected();
|
||||
leftController.setSatelliteVisible(visible);
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.setSatelliteVisible(visible);
|
||||
|
||||
fgProvider.stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof FgComparisonContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController controller = leftDisplay.getController();
|
||||
return controller.hasResults();
|
||||
}
|
||||
}
|
|
@ -28,7 +28,10 @@ import ghidra.app.plugin.ProgramPlugin;
|
|||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.features.base.codecompare.model.AnyToAnyFunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonState;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
|
@ -66,8 +69,12 @@ public class FunctionComparisonPlugin extends ProgramPlugin
|
|||
private Set<FunctionComparisonProvider> providers = new HashSet<>();
|
||||
private FunctionComparisonProvider lastActiveProvider;
|
||||
|
||||
// There is one state shared between all providers and CodeComparison views
|
||||
private FunctionComparisonState comparisonState;
|
||||
|
||||
public FunctionComparisonPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
comparisonState = new FunctionComparisonState(tool);
|
||||
createActions();
|
||||
}
|
||||
|
||||
|
@ -87,14 +94,16 @@ public class FunctionComparisonPlugin extends ProgramPlugin
|
|||
foreEachProvider(p -> p.programClosed(program));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to listen for two event types:
|
||||
* <li>Object Restored: In the event of a redo/undo that affects a function
|
||||
* being shown in the comparison provider, this will allow tell the provider
|
||||
* to reload</li>
|
||||
* <li>Object Removed: If a function is deleted, this will tell the provider
|
||||
* to purge it from the view</li>
|
||||
*/
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
comparisonState.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
comparisonState.readConfigState(saveState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
for (int i = 0; i < ev.numRecords(); ++i) {
|
||||
|
@ -210,7 +219,7 @@ public class FunctionComparisonPlugin extends ProgramPlugin
|
|||
private FunctionComparisonProvider createProvider(FunctionComparisonModel model,
|
||||
Callback closeListener) {
|
||||
FunctionComparisonProvider provider =
|
||||
new FunctionComparisonProvider(this, model, closeListener);
|
||||
new FunctionComparisonProvider(this, model, closeListener, comparisonState);
|
||||
|
||||
providers.add(provider);
|
||||
return provider;
|
||||
|
@ -219,6 +228,7 @@ public class FunctionComparisonPlugin extends ProgramPlugin
|
|||
//==================================================================================================
|
||||
// Service Methods
|
||||
//==================================================================================================
|
||||
|
||||
@Override
|
||||
public void createComparison(Collection<Function> functions) {
|
||||
if (functions.isEmpty()) {
|
||||
|
@ -254,4 +264,8 @@ public class FunctionComparisonPlugin extends ProgramPlugin
|
|||
Swing.runLater(() -> createProvider(model, closeListener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionComparisonPanel createComparisonViewer() {
|
||||
return new FunctionComparisonPanel(tool, name, comparisonState);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,11 +35,9 @@ import ghidra.app.plugin.core.functionwindow.FunctionRowObject;
|
|||
import ghidra.app.plugin.core.functionwindow.FunctionTableModel;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
|
||||
import ghidra.features.base.codecompare.model.*;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.features.base.codecompare.panel.*;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -50,9 +48,9 @@ import util.CollectionUtils;
|
|||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* Dockable provider that displays function comparisons Clients create/modify
|
||||
* these comparisons using the {@link FunctionComparisonService}, which in turn
|
||||
* creates instances of this provider as-needed.
|
||||
* Dockable provider that displays function comparisons. Clients create/modify these comparisons
|
||||
* using the {@link FunctionComparisonService}, which in turn creates instances of this provider
|
||||
* as-needed.
|
||||
*/
|
||||
public class FunctionComparisonProvider extends ComponentProviderAdapter
|
||||
implements PopupActionProvider, FunctionComparisonModelListener {
|
||||
|
@ -80,13 +78,13 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
|
|||
private ToggleDockingAction navigateToAction;
|
||||
|
||||
public FunctionComparisonProvider(FunctionComparisonPlugin plugin,
|
||||
FunctionComparisonModel model, Callback closeListener) {
|
||||
FunctionComparisonModel model, Callback closeListener, FunctionComparisonState state) {
|
||||
super(plugin.getTool(), "Function Comparison Provider", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
this.model = model;
|
||||
this.closeListener = Callback.dummyIfNull(closeListener);
|
||||
|
||||
functionComparisonPanel = new MultiFunctionComparisonPanel(this, tool, model);
|
||||
functionComparisonPanel = new MultiFunctionComparisonPanel(this, tool, model, state);
|
||||
model.addFunctionComparisonModelListener(this);
|
||||
|
||||
setTabText(functionComparisonPanel.getDescription());
|
||||
|
@ -120,8 +118,8 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
CodeComparisonPanel currentComponent =
|
||||
functionComparisonPanel.getCurrentComponent();
|
||||
CodeComparisonView currentComponent =
|
||||
functionComparisonPanel.getCurrentView();
|
||||
return currentComponent.getActionContext(this, event);
|
||||
}
|
||||
|
||||
|
@ -153,10 +151,10 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
|
|||
@Override
|
||||
public List<DockingActionIf> getPopupActions(Tool t, ActionContext context) {
|
||||
if (context.getComponentProvider() == this) {
|
||||
ListingCodeComparisonPanel dualListingPanel =
|
||||
functionComparisonPanel.getDualListingPanel();
|
||||
if (dualListingPanel != null) {
|
||||
ListingPanel leftPanel = dualListingPanel.getListingPanel(LEFT);
|
||||
ListingCodeComparisonView dualListingView =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
if (dualListingView != null) {
|
||||
ListingPanel leftPanel = dualListingView.getListingPanel(LEFT);
|
||||
return leftPanel.getHeaderActions(getOwner());
|
||||
}
|
||||
}
|
||||
|
@ -213,29 +211,8 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
|
|||
* @param program the program that was restored (undo/redo)
|
||||
*/
|
||||
public void programRestored(Program program) {
|
||||
CodeComparisonPanel comparePanel =
|
||||
functionComparisonPanel.getCurrentComponent();
|
||||
comparePanel.programRestored(program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the function comparison providers components to the indicated
|
||||
* saved configuration state
|
||||
*
|
||||
* @param saveState the configuration state to restore
|
||||
*/
|
||||
public void readConfigState(SaveState saveState) {
|
||||
functionComparisonPanel.readConfigState(getName(), saveState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current configuration state of the components that compose
|
||||
* the function comparison provider
|
||||
*
|
||||
* @param saveState the new configuration state
|
||||
*/
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
functionComparisonPanel.writeConfigState(getName(), saveState);
|
||||
CodeComparisonView view = functionComparisonPanel.getCurrentView();
|
||||
view.programRestored(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -291,8 +268,8 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
|
|||
|
||||
navigateToAction = new ToggleActionBuilder("Navigate to Selected Function",
|
||||
plugin.getName())
|
||||
.description(HTMLUtilities.toHTML("Toggle <b>On</b> means to navigate to " +
|
||||
"whatever function is selected in the comparison panel, when focus changes" +
|
||||
.description(HTMLUtilities.toWrappedHTML("Toggle <b>On</b> to navigate the " +
|
||||
"tool to the selected function in the comparison panel when focus changes" +
|
||||
" or a new function is selected."))
|
||||
.helpLocation(new HelpLocation(HELP_TOPIC, "Navigate_To_Function"))
|
||||
.toolBarIcon(NAV_FUNCTION_ICON)
|
||||
|
@ -368,8 +345,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets actions specific to the code comparison panel and adds them to this
|
||||
* provider
|
||||
* Gets actions specific to the code comparison panel and adds them to this provider
|
||||
*/
|
||||
private void addSpecificCodeComparisonActions() {
|
||||
DockingAction[] actions = functionComparisonPanel.getCodeComparisonActions();
|
||||
|
@ -378,8 +354,12 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter
|
|||
}
|
||||
}
|
||||
|
||||
public CodeComparisonPanel getCodeComparisonPanelByName(String name) {
|
||||
return functionComparisonPanel.getCodeComparisonPanelByName(name);
|
||||
public CodeComparisonView getCodeComparisonView(String name) {
|
||||
return functionComparisonPanel.getCodeComparisonView(name);
|
||||
}
|
||||
|
||||
public void selectComparisonView(String name) {
|
||||
functionComparisonPanel.selectComparisonView(name);
|
||||
}
|
||||
|
||||
private void dispose() {
|
||||
|
|
|
@ -26,8 +26,7 @@ import javax.swing.*;
|
|||
import docking.widgets.list.GComboBoxCellRenderer;
|
||||
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.model.FunctionComparisonModelListener;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -60,16 +59,17 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel
|
|||
* @param provider the comparison provider associated with this panel
|
||||
* @param tool the active plugin tool
|
||||
* @param model the comparison data model
|
||||
* @param state the comparison save state
|
||||
*/
|
||||
public MultiFunctionComparisonPanel(FunctionComparisonProvider provider, PluginTool tool,
|
||||
FunctionComparisonModel model) {
|
||||
super(tool, provider.getName());
|
||||
FunctionComparisonModel model, FunctionComparisonState state) {
|
||||
super(tool, provider.getName(), state);
|
||||
this.model = model;
|
||||
model.addFunctionComparisonModelListener(this);
|
||||
|
||||
buildComboPanels();
|
||||
|
||||
getComparisonPanels().forEach(p -> p.setShowDataTitles(false));
|
||||
getComparisonView().forEach(p -> p.setShowDataTitles(false));
|
||||
setPreferredSize(new Dimension(1200, 600));
|
||||
modelDataChanged();
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ public class MultiFunctionComparisonPanel extends FunctionComparisonPanel
|
|||
}
|
||||
|
||||
Side getActiveSide() {
|
||||
CodeComparisonPanel currentComponent = getCurrentComponent();
|
||||
CodeComparisonView currentComponent = getCurrentView();
|
||||
return currentComponent.getActiveSide();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import ghidra.app.decompiler.component.ClangTextField;
|
|||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.codecompare.plugin.FunctionComparisonPlugin;
|
||||
import ghidra.features.codecompare.plugin.FunctionComparisonProvider;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -61,10 +61,10 @@ public abstract class AbstractDualDecompilerTest extends AbstractGhidraHeadedInt
|
|||
return waitForComponentProvider(FunctionComparisonProvider.class);
|
||||
}
|
||||
|
||||
protected DecompilerCodeComparisonPanel findDecompilerPanel(
|
||||
protected DecompilerCodeComparisonView findDecompilerPanel(
|
||||
FunctionComparisonProvider provider) {
|
||||
for (CodeComparisonPanel panel : provider.getComponent().getComparisonPanels()) {
|
||||
if (panel instanceof DecompilerCodeComparisonPanel decompPanel) {
|
||||
for (CodeComparisonView p : provider.getComponent().getComparisonView()) {
|
||||
if (p instanceof DecompilerCodeComparisonView decompPanel) {
|
||||
return decompPanel;
|
||||
}
|
||||
}
|
||||
|
@ -72,24 +72,26 @@ public abstract class AbstractDualDecompilerTest extends AbstractGhidraHeadedInt
|
|||
return null;
|
||||
}
|
||||
|
||||
protected void setActivePanel(FunctionComparisonProvider provider, CodeComparisonPanel panel) {
|
||||
runSwing(() -> provider.getComponent().setCurrentTabbedComponent(panel.getName()));
|
||||
protected void setActivePanel(FunctionComparisonProvider provider,
|
||||
CodeComparisonView comparisonProvider) {
|
||||
runSwing(
|
||||
() -> provider.getComponent().setCurrentTabbedComponent(comparisonProvider.getName()));
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
protected void waitForDecompile(DecompilerCodeComparisonPanel panel) {
|
||||
protected void waitForDecompile(DecompilerCodeComparisonView panel) {
|
||||
waitForSwing();
|
||||
waitForCondition(() -> !panel.isBusy());
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
protected DecompilerPanel getDecompSide(DecompilerCodeComparisonPanel panel, Side side) {
|
||||
protected DecompilerPanel getDecompSide(DecompilerCodeComparisonView panel, Side side) {
|
||||
CDisplay sideDisplay = side == Side.LEFT ? panel.getLeftPanel() : panel.getRightPanel();
|
||||
return sideDisplay.getDecompilerPanel();
|
||||
}
|
||||
|
||||
// 1-indexed lines
|
||||
protected ClangToken setDecompLocation(DecompilerCodeComparisonPanel comparePanel, Side side,
|
||||
protected ClangToken setDecompLocation(DecompilerCodeComparisonView comparePanel, Side side,
|
||||
int line, int charPos) {
|
||||
DecompilerPanel panel = getDecompSide(comparePanel, side);
|
||||
FieldPanel fp = panel.getFieldPanel();
|
||||
|
@ -107,7 +109,7 @@ public abstract class AbstractDualDecompilerTest extends AbstractGhidraHeadedInt
|
|||
}
|
||||
|
||||
// Get the token under the cursor at the given side
|
||||
protected ClangToken getCurrentToken(DecompilerCodeComparisonPanel comparePanel, Side side) {
|
||||
protected ClangToken getCurrentToken(DecompilerCodeComparisonView comparePanel, Side side) {
|
||||
DecompilerPanel panel = getDecompSide(comparePanel, side);
|
||||
FieldLocation loc = panel.getCursorPosition();
|
||||
int lineNumber = loc.getIndex().intValue();
|
||||
|
|
|
@ -169,7 +169,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||
DecompilerCodeComparisonView uncorrelatedPanel =
|
||||
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedCplusDemangle);
|
||||
DockingActionIf localNameTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(localNameTransferAction);
|
||||
|
@ -182,7 +182,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
assertEquals("demangler", currentToken.getText());
|
||||
assertNotEnabled(localNameTransferAction, getProviderContext());
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedMain);
|
||||
// Recreated provider, need to get new handle on action
|
||||
localNameTransferAction = getLocalAction(provider, actionName);
|
||||
|
@ -225,7 +225,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||
DecompilerCodeComparisonView uncorrelatedPanel =
|
||||
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedCplusDemangle);
|
||||
DockingActionIf globalNameTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(globalNameTransferAction);
|
||||
|
@ -238,7 +238,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
assertEquals("program_name", currentToken.getText());
|
||||
assertNotEnabled(globalNameTransferAction, getProviderContext());
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedMain);
|
||||
// Recreated provider, need to get new handle on action
|
||||
globalNameTransferAction = getLocalAction(provider, actionName);
|
||||
|
@ -280,7 +280,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||
DecompilerCodeComparisonView uncorrelatedPanel =
|
||||
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedCplusDemangle);
|
||||
DockingActionIf typeTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(typeTransferAction);
|
||||
|
@ -301,7 +301,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
assertEquals("program_name", currentToken.getText());
|
||||
assertNotEnabled(typeTransferAction, getProviderContext());
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedMain);
|
||||
// Recreated provider, need to get new handle on action
|
||||
typeTransferAction = getLocalAction(provider, actionName);
|
||||
|
@ -436,13 +436,13 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
*/
|
||||
|
||||
@Test
|
||||
public void testFullStructTypeTransferAction() throws RuntimeException {
|
||||
public void testFullStructTypeTransferAction() {
|
||||
final String actionName = ApplyVariableTypeFromMatchedTokensAction.ACTION_NAME;
|
||||
int line;
|
||||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||
DockingActionIf typeTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(typeTransferAction);
|
||||
|
@ -484,7 +484,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||
DockingActionIf typeTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(typeTransferAction);
|
||||
|
@ -524,7 +524,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||
DecompilerCodeComparisonView uncorrelatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedMain);
|
||||
DockingActionIf calleeNameTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(calleeNameTransferAction);
|
||||
|
@ -537,7 +537,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
assertEquals("xstrdup", currentToken.getText());
|
||||
assertNotEnabled(calleeNameTransferAction, getProviderContext());
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||
// Recreated provider, need to get new handle on action
|
||||
calleeNameTransferAction = getLocalAction(provider, actionName);
|
||||
|
@ -582,7 +582,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||
DecompilerCodeComparisonView uncorrelatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedMain);
|
||||
DockingActionIf calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(calleeFullSignatureTransferAction);
|
||||
|
@ -595,7 +595,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
assertEquals("xstrdup", currentToken.getText());
|
||||
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||
// Recreated provider, need to get new handle on action
|
||||
calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||
|
@ -669,7 +669,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
int col;
|
||||
ClangToken currentToken;
|
||||
|
||||
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||
DecompilerCodeComparisonView uncorrelatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedMain);
|
||||
DockingActionIf calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||
assertNotNull(calleeFullSignatureTransferAction);
|
||||
|
@ -682,7 +682,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
assertEquals("xstrdup", currentToken.getText());
|
||||
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||
|
||||
DecompilerCodeComparisonPanel correlatedPanel =
|
||||
DecompilerCodeComparisonView correlatedPanel =
|
||||
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||
// Recreated provider, need to get new handle on action
|
||||
calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||
|
@ -747,7 +747,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
|
||||
// Setup and focus to a decompiler comparison between the two selected functions. Wait for
|
||||
// the decompilation to complete so that subsequent calls to navigation, etc. work correctly
|
||||
private DecompilerCodeComparisonPanel preparePanel(Function leftFunc, Function rightFunc) {
|
||||
private DecompilerCodeComparisonView preparePanel(Function leftFunc, Function rightFunc) {
|
||||
if (provider != null) {
|
||||
// Always want to clear out existing comparison
|
||||
closeProvider(provider);
|
||||
|
@ -755,7 +755,7 @@ public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
|||
|
||||
provider = compareFunctions(Set.of(leftFunc, rightFunc));
|
||||
|
||||
DecompilerCodeComparisonPanel decompPanel = findDecompilerPanel(provider);
|
||||
DecompilerCodeComparisonView decompPanel = findDecompilerPanel(provider);
|
||||
waitForDecompile(decompPanel);
|
||||
decompPanel.setSynchronizedScrolling(true);
|
||||
setActivePanel(provider, decompPanel);
|
||||
|
|
|
@ -35,7 +35,7 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
|||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.model.MatchedFunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -137,7 +137,7 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio
|
|||
DockingActionIf previousAction = getAction(plugin, "Compare Previous Function");
|
||||
|
||||
// since we are clicking the listing panel, bring that to the front first
|
||||
setActivePanel(provider, provider.getComponent().getDualListingPanel());
|
||||
setActivePanel(provider, provider.getComponent().getDualListingView());
|
||||
|
||||
// left panel has focus, so nextAction should be enabled and previous should be disabled
|
||||
ActionContext context = provider.getActionContext(null);
|
||||
|
@ -145,7 +145,7 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio
|
|||
assertNotEnabled(previousAction, context);
|
||||
|
||||
JPanel rightPanel =
|
||||
provider.getComponent().getDualListingPanel().getListingPanel(RIGHT).getFieldPanel();
|
||||
provider.getComponent().getDualListingView().getListingPanel(RIGHT).getFieldPanel();
|
||||
clickMouse(rightPanel, 1, 30, 30, 1, 0);
|
||||
waitForSwing();
|
||||
provider.getComponent().updateActionEnablement();
|
||||
|
@ -291,8 +291,10 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio
|
|||
assertFalse(runSwing(() -> action.isEnabledForContext(context)));
|
||||
}
|
||||
|
||||
private void setActivePanel(FunctionComparisonProvider provider, CodeComparisonPanel panel) {
|
||||
runSwing(() -> provider.getComponent().setCurrentTabbedComponent(panel.getName()));
|
||||
private void setActivePanel(FunctionComparisonProvider provider,
|
||||
CodeComparisonView comparisonProvider) {
|
||||
runSwing(
|
||||
() -> provider.getComponent().setCurrentTabbedComponent(comparisonProvider.getName()));
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/* ###
|
||||
* 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.functiongraph;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DefaultFgEnv implements FgEnv {
|
||||
|
||||
private FGProvider provider;
|
||||
private FunctionGraphPlugin plugin;
|
||||
|
||||
public DefaultFgEnv(FGProvider provider, FunctionGraphPlugin plugin) {
|
||||
this.provider = Objects.requireNonNull(provider);
|
||||
this.plugin = Objects.requireNonNull(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return plugin.getTool();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return provider.getProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionGraphOptions getOptions() {
|
||||
return plugin.getFunctionGraphOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FGColorProvider getColorProvider() {
|
||||
return plugin.getColorProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FGLayoutProvider> getLayoutProviders() {
|
||||
return plugin.getLayoutProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAction(DockingAction action) {
|
||||
provider.addLocalAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormatManager getUserDefinedFormat() {
|
||||
return plugin.getUserDefinedFormat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserDefinedFormat(FormatManager format) {
|
||||
plugin.setUserDefinedFormat(format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigatable getNavigatable() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getToolLocation() {
|
||||
return plugin.getProgramLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
//
|
||||
// The connected provider will synchronize the tool selection with its selection.
|
||||
// Non-connected providers will ignore selection updates, since users have made a snapshot
|
||||
// that should no longer respond to selection changes from the tool. We still want actions
|
||||
// that manipulate selection the graph to work for snapshots. To do this, we can call the
|
||||
// controller directly (which is what the connected provider does).
|
||||
//
|
||||
if (provider.isConnected()) {
|
||||
provider.setSelection(selection);
|
||||
}
|
||||
else {
|
||||
FGController controller = provider.getController();
|
||||
controller.setSelection(selection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getGraphLocation() {
|
||||
return provider.getLocation();
|
||||
}
|
||||
}
|
|
@ -19,15 +19,13 @@ import java.awt.event.InputEvent;
|
|||
import java.awt.event.KeyEvent;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.*;
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.options.OptionsService;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.OptionDialog;
|
||||
import edu.uci.ics.jung.graph.Graph;
|
||||
|
@ -39,19 +37,18 @@ import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
|||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGData;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
class FGActionManager {
|
||||
public class FGActionManager {
|
||||
private static final String EDGE_HOVER_HIGHLIGHT = "EDGE_HOVER_HIGHLIGHT";
|
||||
private static final String EDGE_SELECTION_HIGHLIGHT = "EDGE_SELECTION_HIGHLIGHT";
|
||||
|
||||
|
@ -71,9 +68,8 @@ class FGActionManager {
|
|||
//@formatter:off
|
||||
|
||||
private PluginTool tool;
|
||||
private FunctionGraphPlugin plugin;
|
||||
private String owner;
|
||||
private FGController controller;
|
||||
private FGProvider provider;
|
||||
|
||||
private ToggleDockingAction togglePopups;
|
||||
|
||||
|
@ -82,20 +78,29 @@ class FGActionManager {
|
|||
|
||||
private MultiStateDockingAction<FGLayoutProvider> layoutAction;
|
||||
|
||||
FGActionManager(FunctionGraphPlugin plugin, FGController controller, FGProvider provider) {
|
||||
this.plugin = plugin;
|
||||
this.tool = plugin.getTool();
|
||||
public FGActionManager(FGController controller, String owner) {
|
||||
this.controller = controller;
|
||||
this.provider = provider;
|
||||
this.owner = owner;
|
||||
FgEnv env = controller.getEnv();
|
||||
this.tool = env.getTool();
|
||||
|
||||
createActions();
|
||||
}
|
||||
|
||||
private JComponent getCenterOverComponent() {
|
||||
return controller.getViewComponent();
|
||||
}
|
||||
|
||||
private void addLocalAction(DockingAction action) {
|
||||
FgEnv env = controller.getEnv();
|
||||
env.addLocalAction(action);
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
String toolBarGroup1 = "groupA";
|
||||
String layoutGroup = "groupB";
|
||||
String toolbarEdgeGroup = "groupC";
|
||||
String toolbarEndGroup = "zzzend";
|
||||
|
||||
// this is a dependent, hard-coded value pulled from the plugin that creates highlight actions
|
||||
String popupSelectionGroup = "Highlight";
|
||||
|
@ -107,13 +112,12 @@ class FGActionManager {
|
|||
String popupMutateGroup2 = "zamutate.2";
|
||||
String popupDisplayGroup = "zdisplay";
|
||||
String popuEndPopupGroup = "zzzoom";
|
||||
String popupVeryLastGroup = "zzzzzz";
|
||||
|
||||
int vertexGroupingSubgroupOffset = 1;
|
||||
int groupingSubgroupOffset = 1; // sub-sort of the grouping menu
|
||||
|
||||
DockingAction chooseFormatsAction =
|
||||
new DockingAction("Edit Code Block Fields", plugin.getName()) {
|
||||
new DockingAction("Edit Code Block Fields", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
showFormatChooser();
|
||||
|
@ -132,7 +136,7 @@ class FGActionManager {
|
|||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Format"));
|
||||
|
||||
DockingAction homeAction =
|
||||
new DockingAction("Go To Function Entry Point", plugin.getName()) {
|
||||
new DockingAction("Go To Function Entry Point", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
goHome();
|
||||
|
@ -148,10 +152,10 @@ class FGActionManager {
|
|||
homeAction.setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Home"));
|
||||
|
||||
DockingAction resetGraphAction = new DockingAction("Reset Graph", plugin.getName()) {
|
||||
DockingAction resetGraphAction = new DockingAction("Reset Graph", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
int choice = OptionDialog.showYesNoDialog(provider.getComponent(), "Reset Graph?",
|
||||
int choice = OptionDialog.showYesNoDialog(getCenterOverComponent(), "Reset Graph?",
|
||||
"<html>Erase all vertex position and grouping information?");
|
||||
if (choice != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
|
@ -172,7 +176,7 @@ class FGActionManager {
|
|||
resetGraphAction.setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Reload_Graph"));
|
||||
|
||||
provider.addLocalAction(resetGraphAction);
|
||||
addLocalAction(resetGraphAction);
|
||||
|
||||
addLayoutAction(layoutGroup);
|
||||
addVertexHoverModeAction(toolbarEdgeGroup);
|
||||
|
@ -181,7 +185,7 @@ class FGActionManager {
|
|||
//
|
||||
// Display transforming actions
|
||||
//
|
||||
DockingAction zoomOutAction = new DockingAction("Zoom Out", plugin.getName()) {
|
||||
DockingAction zoomOutAction = new DockingAction("Zoom Out", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
controller.zoomOutGraph();
|
||||
|
@ -201,7 +205,7 @@ class FGActionManager {
|
|||
KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, DockingUtils.CONTROL_KEY_MODIFIER_MASK)));
|
||||
zoomOutAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Zoom"));
|
||||
|
||||
DockingAction zoomInAction = new DockingAction("Zoom In", plugin.getName()) {
|
||||
DockingAction zoomInAction = new DockingAction("Zoom In", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
controller.zoomInGraph();
|
||||
|
@ -220,7 +224,7 @@ class FGActionManager {
|
|||
KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, DockingUtils.CONTROL_KEY_MODIFIER_MASK)));
|
||||
zoomInAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Zoom"));
|
||||
|
||||
DockingAction zoomToWindowAction = new DockingAction("Zoom to Window", plugin.getName()) {
|
||||
DockingAction zoomToWindowAction = new DockingAction("Zoom to Window", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
controller.zoomToWindow();
|
||||
|
@ -238,7 +242,7 @@ class FGActionManager {
|
|||
new MenuData(new String[] { "Zoom to Window" }, popuEndPopupGroup));
|
||||
zoomToWindowAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Zoom"));
|
||||
|
||||
DockingAction zoomToVertexAction = new DockingAction("Zoom to Vertex", plugin.getName()) {
|
||||
DockingAction zoomToVertexAction = new DockingAction("Zoom to Vertex", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
FunctionGraphVertexLocationContextIf vertexContext =
|
||||
|
@ -264,7 +268,7 @@ class FGActionManager {
|
|||
new MenuData(new String[] { "Zoom to Vertex" }, popuEndPopupGroup));
|
||||
zoomToVertexAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Zoom"));
|
||||
|
||||
togglePopups = new ToggleDockingAction("Display Popup Windows", plugin.getName()) {
|
||||
togglePopups = new ToggleDockingAction("Display Popup Windows", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
controller.setPopupsVisible(isSelected());
|
||||
|
@ -290,7 +294,7 @@ class FGActionManager {
|
|||
//
|
||||
// Vertex Actions
|
||||
//
|
||||
DockingAction editLabelAction = new DockingAction("Edit Vertex Label", plugin.getName()) {
|
||||
DockingAction editLabelAction = new DockingAction("Edit Vertex Label", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
FunctionGraphValidGraphActionContextIf graphContext =
|
||||
|
@ -298,7 +302,7 @@ class FGActionManager {
|
|||
|
||||
// size guaranteed to be 1
|
||||
FGVertex vertex = graphContext.getSelectedVertices().iterator().next();
|
||||
vertex.editLabel(provider.getComponent());
|
||||
vertex.editLabel(getCenterOverComponent());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -334,7 +338,7 @@ class FGActionManager {
|
|||
editLabelAction
|
||||
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Label"));
|
||||
|
||||
DockingAction fullViewAction = new DockingAction("Vertex View Mode", plugin.getName()) {
|
||||
DockingAction fullViewAction = new DockingAction("Vertex View Mode", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
FunctionGraphValidGraphActionContextIf graphContext =
|
||||
|
@ -384,7 +388,7 @@ class FGActionManager {
|
|||
fullViewAction.setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Vertex_Action_Full_View"));
|
||||
|
||||
DockingAction xrefsAction = new DockingAction("Jump to a XRef", plugin.getName()) {
|
||||
DockingAction xrefsAction = new DockingAction("Jump to a XRef", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
controller.showXRefsDialog();
|
||||
|
@ -416,7 +420,7 @@ class FGActionManager {
|
|||
// Group Actions
|
||||
//
|
||||
DockingAction groupSelectedVertices =
|
||||
new DockingAction("Group Selected Vertices", plugin.getName()) {
|
||||
new DockingAction("Group Selected Vertices", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
controller.groupSelectedVertices();
|
||||
|
@ -455,7 +459,7 @@ class FGActionManager {
|
|||
new HelpLocation("FunctionGraphPlugin", "Vertex_Grouping_Group_Selected_Popup"));
|
||||
|
||||
DockingAction addSelectedVerticesToGroup =
|
||||
new DockingAction("Group Selected Vertices", plugin.getName()) {
|
||||
new DockingAction("Group Selected Vertices", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
addToGroup(context);
|
||||
|
@ -514,7 +518,7 @@ class FGActionManager {
|
|||
"Vertex_Grouping_Add_Selected_Vertices_To_Group"));
|
||||
|
||||
DockingAction ungroupSelectedVertices =
|
||||
new DockingAction("Ungroup Selected Vertices", plugin.getName()) {
|
||||
new DockingAction("Ungroup Selected Vertices", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
FunctionGraphValidGraphActionContextIf graphContext =
|
||||
|
@ -556,7 +560,7 @@ class FGActionManager {
|
|||
ungroupSelectedVertices.setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Vertex_Grouping_Ungroup_Selected_Popup"));
|
||||
|
||||
DockingAction removeFromGroup = new DockingAction("Remove From Group", plugin.getName()) {
|
||||
DockingAction removeFromGroup = new DockingAction("Remove From Group", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
FunctionGraphValidGraphActionContextIf graphContext =
|
||||
|
@ -603,11 +607,11 @@ class FGActionManager {
|
|||
new HelpLocation("FunctionGraphPlugin", "Vertex_Grouping_Remove_From_Group"));
|
||||
|
||||
DockingAction ungroupAllVertices =
|
||||
new DockingAction("Ungroup All Vertices", plugin.getName()) {
|
||||
new DockingAction("Ungroup All Vertices", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
int choice = OptionDialog.showYesNoDialog(provider.getComponent(),
|
||||
int choice = OptionDialog.showYesNoDialog(getCenterOverComponent(),
|
||||
"Ungroup All Vertices?", "Ungroup all grouped vertices?");
|
||||
if (choice != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
|
@ -645,55 +649,13 @@ class FGActionManager {
|
|||
ungroupAllVertices.setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Vertex_Grouping_Ungroup_All_Popup"));
|
||||
|
||||
//
|
||||
// Miscellaneous Actions
|
||||
//
|
||||
DockingAction cloneAction = new DockingAction("Function Graph Clone", plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.cloneWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return controller.getGraphedFunction() != null;
|
||||
}
|
||||
};
|
||||
Icon image = new GIcon("icon.plugin.functiongraph.action.viewer.clone");
|
||||
cloneAction.setToolBarData(new ToolBarData(image, toolbarEndGroup));
|
||||
cloneAction.setDescription(
|
||||
"Create a snapshot (disconnected) copy of this Function Graph window");
|
||||
cloneAction.setHelpLocation(new HelpLocation("Snapshots", "Snapshots_Start"));
|
||||
cloneAction.setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Snapshot"));
|
||||
cloneAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_T,
|
||||
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
|
||||
|
||||
DockingAction optionsAction =
|
||||
new DockingAction("Function Graph Options", plugin.getName()) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
OptionsService service = tool.getService(OptionsService.class);
|
||||
service.showOptionsDialog(FunctionGraphPlugin.OPTIONS_NAME_PATH,
|
||||
"Function Graph");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
optionsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Properties" }, null, popupVeryLastGroup));
|
||||
optionsAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Options"));
|
||||
|
||||
//
|
||||
// Selection Actions
|
||||
//
|
||||
String selectionMenuName = "Program Selection";
|
||||
DockingAction selectHoveredEdgesAction =
|
||||
new DockingAction("Make Selection From Hovered Edges", plugin.getName()) {
|
||||
new DockingAction("Make Selection From Hovered Edges", owner) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
@ -727,7 +689,7 @@ class FGActionManager {
|
|||
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
||||
|
||||
DockingAction selectFocusedEdgesAction =
|
||||
new DockingAction("Make Selection From Focused Edges", plugin.getName()) {
|
||||
new DockingAction("Make Selection From Focused Edges", owner) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
@ -761,7 +723,7 @@ class FGActionManager {
|
|||
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
||||
|
||||
DockingAction clearCurrentSelectionAction =
|
||||
new DockingAction("Clear Current Selection", plugin.getName()) {
|
||||
new DockingAction("Clear Current Selection", owner) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
@ -775,7 +737,7 @@ class FGActionManager {
|
|||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
ProgramSelection selection = provider.getCurrentProgramSelection();
|
||||
ProgramSelection selection = controller.getSelection();
|
||||
return selection != null && !selection.isEmpty();
|
||||
}
|
||||
};
|
||||
|
@ -785,7 +747,7 @@ class FGActionManager {
|
|||
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Path_Selection"));
|
||||
|
||||
DockingAction selectAllAction =
|
||||
new DockingAction("Select All Code Units", plugin.getName()) {
|
||||
new DockingAction("Select All Code Units", owner) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
@ -828,31 +790,28 @@ class FGActionManager {
|
|||
selectAllAction
|
||||
.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Code_Unit_Selection"));
|
||||
|
||||
provider.addLocalAction(chooseFormatsAction);
|
||||
provider.addLocalAction(homeAction);
|
||||
provider.addLocalAction(zoomInAction);
|
||||
provider.addLocalAction(zoomOutAction);
|
||||
provider.addLocalAction(zoomToVertexAction);
|
||||
provider.addLocalAction(zoomToWindowAction);
|
||||
addLocalAction(chooseFormatsAction);
|
||||
addLocalAction(homeAction);
|
||||
addLocalAction(zoomInAction);
|
||||
addLocalAction(zoomOutAction);
|
||||
addLocalAction(zoomToVertexAction);
|
||||
addLocalAction(zoomToWindowAction);
|
||||
|
||||
provider.addLocalAction(editLabelAction);
|
||||
provider.addLocalAction(fullViewAction);
|
||||
provider.addLocalAction(xrefsAction);
|
||||
addLocalAction(editLabelAction);
|
||||
addLocalAction(fullViewAction);
|
||||
addLocalAction(xrefsAction);
|
||||
|
||||
provider.addLocalAction(groupSelectedVertices);
|
||||
provider.addLocalAction(addSelectedVerticesToGroup);
|
||||
provider.addLocalAction(removeFromGroup);
|
||||
provider.addLocalAction(ungroupSelectedVertices);
|
||||
provider.addLocalAction(ungroupAllVertices);
|
||||
provider.addLocalAction(togglePopups);
|
||||
addLocalAction(groupSelectedVertices);
|
||||
addLocalAction(addSelectedVerticesToGroup);
|
||||
addLocalAction(removeFromGroup);
|
||||
addLocalAction(ungroupSelectedVertices);
|
||||
addLocalAction(ungroupAllVertices);
|
||||
addLocalAction(togglePopups);
|
||||
|
||||
provider.addLocalAction(cloneAction);
|
||||
provider.addLocalAction(optionsAction);
|
||||
|
||||
provider.addLocalAction(selectAllAction);
|
||||
provider.addLocalAction(selectHoveredEdgesAction);
|
||||
provider.addLocalAction(selectFocusedEdgesAction);
|
||||
provider.addLocalAction(clearCurrentSelectionAction);
|
||||
addLocalAction(selectAllAction);
|
||||
addLocalAction(selectHoveredEdgesAction);
|
||||
addLocalAction(selectFocusedEdgesAction);
|
||||
addLocalAction(clearCurrentSelectionAction);
|
||||
|
||||
// this does two things: 1) allows us to subgroup the pull-right menu and 2) it matches
|
||||
// the organization of the highlight and selection actions from the main listing
|
||||
|
@ -865,7 +824,8 @@ class FGActionManager {
|
|||
HelpLocation layoutHelpLocation =
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
|
||||
|
||||
layoutAction = new MultiStateDockingAction<>("Relayout Graph", plugin.getName()) {
|
||||
layoutAction = new MultiStateDockingAction<>("Relayout Graph", owner,
|
||||
KeyBindingType.SHARED) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
@ -896,7 +856,7 @@ class FGActionManager {
|
|||
layoutAction.addActionState(actionState);
|
||||
}
|
||||
|
||||
provider.addLocalAction(layoutAction);
|
||||
addLocalAction(layoutAction);
|
||||
}
|
||||
|
||||
private void changeLayout(FGLayoutProvider layout) {
|
||||
|
@ -904,7 +864,8 @@ class FGActionManager {
|
|||
}
|
||||
|
||||
private List<ActionState<FGLayoutProvider>> loadActionStatesForLayoutProviders() {
|
||||
List<FGLayoutProvider> layoutInstances = plugin.getLayoutProviders();
|
||||
FgEnv env = controller.getEnv();
|
||||
List<FGLayoutProvider> layoutInstances = env.getLayoutProviders();
|
||||
return createActionStates(layoutInstances);
|
||||
}
|
||||
|
||||
|
@ -988,7 +949,7 @@ class FGActionManager {
|
|||
offState.setHelpLocation(pathHelpLocation);
|
||||
|
||||
vertexHoverModeAction =
|
||||
new MultiStateDockingAction<>("Block Hover Mode", plugin.getName()) {
|
||||
new MultiStateDockingAction<>("Block Hover Mode", owner) {
|
||||
|
||||
@Override
|
||||
public void actionStateChanged(ActionState<EdgeDisplayType> newActionState,
|
||||
|
@ -1013,7 +974,7 @@ class FGActionManager {
|
|||
|
||||
vertexHoverModeAction.setCurrentActionState(pathsForwardScopedFlow);
|
||||
|
||||
provider.addLocalAction(vertexHoverModeAction);
|
||||
addLocalAction(vertexHoverModeAction);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1060,7 +1021,7 @@ class FGActionManager {
|
|||
offState.setHelpLocation(pathHelpLocation);
|
||||
|
||||
vertexFocusModeAction =
|
||||
new MultiStateDockingAction<>("Block Focus Mode", plugin.getName()) {
|
||||
new MultiStateDockingAction<>("Block Focus Mode", owner) {
|
||||
|
||||
@Override
|
||||
public void actionStateChanged(ActionState<EdgeDisplayType> newActionState,
|
||||
|
@ -1085,7 +1046,7 @@ class FGActionManager {
|
|||
|
||||
vertexFocusModeAction.setCurrentActionState(allCyclesState);
|
||||
|
||||
provider.addLocalAction(vertexFocusModeAction);
|
||||
addLocalAction(vertexFocusModeAction);
|
||||
}
|
||||
|
||||
private void clearGraphSelection() {
|
||||
|
@ -1093,12 +1054,14 @@ class FGActionManager {
|
|||
FGData functionGraphData = controller.getFunctionGraphData();
|
||||
Function function = functionGraphData.getFunction();
|
||||
AddressSetView functionBody = function.getBody();
|
||||
AddressSet subtraction = provider.getCurrentProgramSelection().subtract(functionBody);
|
||||
ProgramSelection selection = controller.getSelection();
|
||||
|
||||
AddressSet subtraction = selection.subtract(functionBody);
|
||||
ProgramSelection programSelectionWithoutGraphBody = new ProgramSelection(subtraction);
|
||||
plugin.getTool()
|
||||
.firePluginEvent(new ProgramSelectionPluginEvent("Spoof!",
|
||||
programSelectionWithoutGraphBody, provider.getCurrentProgram()));
|
||||
FgEnv env = controller.getEnv();
|
||||
Program program = env.getProgram();
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("Spoof!",
|
||||
programSelectionWithoutGraphBody, program));
|
||||
}
|
||||
|
||||
private Set<FGVertex> getAllVertices() {
|
||||
|
@ -1146,15 +1109,10 @@ class FGActionManager {
|
|||
|
||||
private void goHome() {
|
||||
Function function = controller.getGraphedFunction();
|
||||
ProgramLocation homeLocation =
|
||||
new ProgramLocation(provider.getCurrentProgram(), function.getEntryPoint());
|
||||
if (SystemUtilities.isEqual(provider.getCurrentLocation(), homeLocation)) {
|
||||
// already at the right location, just make sure we are on the screen and selected
|
||||
provider.displayLocation(homeLocation);
|
||||
}
|
||||
else {
|
||||
provider.internalGoTo(homeLocation, provider.getCurrentProgram());
|
||||
}
|
||||
FgEnv env = controller.getEnv();
|
||||
Program program = env.getProgram();
|
||||
ProgramLocation homeLocation = new ProgramLocation(program, function.getEntryPoint());
|
||||
controller.display(program, homeLocation);
|
||||
}
|
||||
|
||||
private AddressSet getAddressesForVertices(Collection<FGVertex> vertices) {
|
||||
|
@ -1167,9 +1125,9 @@ class FGActionManager {
|
|||
|
||||
private void makeSelectionFromAddresses(AddressSet addresses) {
|
||||
ProgramSelection selection = new ProgramSelection(addresses);
|
||||
plugin.getTool()
|
||||
.firePluginEvent(new ProgramSelectionPluginEvent("Spoof!", selection,
|
||||
provider.getCurrentProgram()));
|
||||
FgEnv env = controller.getEnv();
|
||||
Program program = env.getProgram();
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("Spoof!", selection, program));
|
||||
}
|
||||
|
||||
private void ungroupVertices(Set<GroupedFunctionGraphVertex> groupVertices) {
|
||||
|
@ -1181,7 +1139,7 @@ class FGActionManager {
|
|||
|
||||
String vertexString = size == 1 ? "1 group vertex" : size + " group vertices";
|
||||
|
||||
int choice = OptionDialog.showYesNoDialog(provider.getComponent(), "Ungroup Vertices?",
|
||||
int choice = OptionDialog.showYesNoDialog(getCenterOverComponent(), "Ungroup Vertices?",
|
||||
"Ungroup " + vertexString + "?");
|
||||
if (choice != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
|
|
|
@ -41,8 +41,8 @@ public class FGClipboardProvider extends CodeBrowserClipboardProvider {
|
|||
|
||||
private FGController controller;
|
||||
|
||||
FGClipboardProvider(PluginTool tool, FGController controller) {
|
||||
super(tool, controller.getProvider());
|
||||
FGClipboardProvider(PluginTool tool, FGController controller, FGProvider provider) {
|
||||
super(tool, provider);
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,19 +15,22 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.functiongraph;
|
||||
|
||||
import static ghidra.framework.model.DomainObjectEvent.RESTORED;
|
||||
import static ghidra.framework.model.DomainObjectEvent.*;
|
||||
import static ghidra.program.util.ProgramEvent.*;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.options.OptionsService;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import edu.uci.ics.jung.graph.Graph;
|
||||
import generic.stl.Pair;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.functiongraph.action.*;
|
||||
|
@ -103,7 +106,9 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
|
||||
this.tool = plugin.getTool();
|
||||
this.plugin = plugin;
|
||||
controller = new FGController(this, plugin);
|
||||
DefaultFgEnv env = new DefaultFgEnv(this, plugin);
|
||||
DefaultFGControllerListener listener = new DefaultFGControllerListener(this);
|
||||
this.controller = new FGController(env, listener);
|
||||
|
||||
setConnected(isConnected);
|
||||
setIcon(FunctionGraphPlugin.ICON);
|
||||
|
@ -124,7 +129,7 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
addToTool();
|
||||
addSatelliteFeature(); // must be after addToTool();
|
||||
|
||||
actionManager = new FGActionManager(plugin, controller, this);
|
||||
createActions();
|
||||
|
||||
rebuildGraphUpdateManager =
|
||||
new SwingUpdateManager(1000, 10000, () -> refreshAndKeepPerspective());
|
||||
|
@ -132,11 +137,64 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
updateLocationUpdateManager =
|
||||
new SwingUpdateManager(250, 750, () -> setPendingLocationFromUpdateManager());
|
||||
|
||||
clipboardProvider = new FGClipboardProvider(tool, controller);
|
||||
clipboardProvider = new FGClipboardProvider(tool, controller, this);
|
||||
setDefaultFocusComponent(controller.getViewComponent());
|
||||
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
actionManager = new FGActionManager(controller, plugin.getName());
|
||||
|
||||
// Note: these values are coordinated with the FGActionManager
|
||||
String toolbarEndGroup = "zzzend";
|
||||
String popupVeryLastGroup = "zzzzzz";
|
||||
|
||||
String owner = plugin.getName();
|
||||
DockingAction cloneAction = new DockingAction("Function Graph Clone", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
cloneWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return controller.getGraphedFunction() != null;
|
||||
}
|
||||
};
|
||||
Icon image = new GIcon("icon.plugin.functiongraph.action.viewer.clone");
|
||||
cloneAction.setToolBarData(new ToolBarData(image, toolbarEndGroup));
|
||||
cloneAction.setDescription(
|
||||
"Create a snapshot (disconnected) copy of this Function Graph window");
|
||||
cloneAction.setHelpLocation(new HelpLocation("Snapshots", "Snapshots_Start"));
|
||||
cloneAction.setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Snapshot"));
|
||||
cloneAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_T,
|
||||
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
|
||||
|
||||
DockingAction optionsAction =
|
||||
new DockingAction("Function Graph Options", owner) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
OptionsService service = tool.getService(OptionsService.class);
|
||||
service.showOptionsDialog(FunctionGraphPlugin.OPTIONS_NAME_PATH,
|
||||
"Function Graph");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
optionsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Properties" }, null, popupVeryLastGroup));
|
||||
optionsAction.setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Options"));
|
||||
|
||||
addLocalAction(cloneAction);
|
||||
addLocalAction(optionsAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSnapshot() {
|
||||
// we are a snapshot when we are 'disconnected'
|
||||
|
@ -150,6 +208,17 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives to the clipboard of this provider the given string.
|
||||
* <p>
|
||||
* This will prime the clipboard such that a copy action will copy the given string.
|
||||
*
|
||||
* @param string the string to set
|
||||
*/
|
||||
public void setClipboardStringContent(String string) {
|
||||
clipboardProvider.setStringContent(string);
|
||||
}
|
||||
|
||||
FGController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
@ -235,24 +304,13 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
}
|
||||
|
||||
private boolean arePopupsVisible() {
|
||||
return controller.arePopupsEnabled();
|
||||
return controller.arePopupsVisible();
|
||||
}
|
||||
|
||||
public void setPopupsVisible(boolean visible) {
|
||||
actionManager.popupVisibilityChanged(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives to the clipboard of this provider the given string.
|
||||
* <p>
|
||||
* This will prime the clipboard such that a copy action will copy the given string.
|
||||
*
|
||||
* @param string the string to set
|
||||
*/
|
||||
public void setClipboardStringContent(String string) {
|
||||
clipboardProvider.setStringContent(string);
|
||||
}
|
||||
|
||||
public void saveLocationToHistory() {
|
||||
NavigationHistoryService historyService = tool.getService(NavigationHistoryService.class);
|
||||
historyService.addNewLocation(this);
|
||||
|
@ -523,6 +581,16 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO - snapshots are not correctly enabling the back button when the user double-clicks
|
||||
// inside of a node to graph a new function.
|
||||
/*
|
||||
if (isSnapshot()) {
|
||||
if (!isInCurrentFunction(newLocation)) {
|
||||
saveLocationToHistory();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
storeLocation(newLocation);
|
||||
displayLocation(newLocation);
|
||||
notifyContextChanged();
|
||||
|
@ -536,40 +604,11 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
}
|
||||
|
||||
/**
|
||||
* Tells this provider to refresh, which means to rebuild the graph and relayout the vertices.
|
||||
*/
|
||||
private void refresh(boolean keepPerspective) {
|
||||
FGData functionGraphData = controller.getFunctionGraphData();
|
||||
if (functionGraphData.hasResults()) {
|
||||
//
|
||||
// We use the graph's data over the 'currentXXX' data, as there is a chance that the
|
||||
// latter values have been set to new values, while the graph has differing data. In
|
||||
// that case we have made the decision to prefer the graph's data.
|
||||
//
|
||||
Function function = functionGraphData.getFunction();
|
||||
Address address = function.getEntryPoint();
|
||||
Address currentAddress = currentLocation.getAddress();
|
||||
if (function.getBody().contains(currentAddress)) {
|
||||
// prefer the current address if it is within the current function (i.e., the
|
||||
// location hasn't changed out from under the graph due to threading issues)
|
||||
address = currentAddress;
|
||||
}
|
||||
|
||||
Program program = function.getProgram();
|
||||
ProgramLocation programLocation = new ProgramLocation(program, address);
|
||||
controller.rebuildDisplay(program, programLocation, keepPerspective);
|
||||
return;
|
||||
}
|
||||
|
||||
controller.rebuildDisplay(currentProgram, currentLocation, keepPerspective);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the graph and restores the zoom and location of the graph to the values prior to
|
||||
* rebuilding.
|
||||
* Rebuilds the graph and restores the zoom and location of the graph to the values prior
|
||||
* to rebuilding.
|
||||
*/
|
||||
public void refreshAndKeepPerspective() {
|
||||
refresh(true);
|
||||
controller.refresh(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -577,7 +616,7 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
* centered.
|
||||
*/
|
||||
public void refreshAndResetPerspective() {
|
||||
refresh(false);
|
||||
controller.refresh(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -585,11 +624,8 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
* performing a full rebuild
|
||||
*/
|
||||
public void refreshDisplayWithoutRebuilding() {
|
||||
FGData functionGraphData = controller.getFunctionGraphData();
|
||||
if (functionGraphData.hasResults()) {
|
||||
controller.refreshDisplayWithoutRebuilding();
|
||||
}
|
||||
}
|
||||
|
||||
public void optionsChanged() {
|
||||
controller.optionsChanged();
|
||||
|
@ -674,9 +710,7 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
|
||||
AddressSet addresses = new AddressSet();
|
||||
|
||||
Iterator<DomainObjectChangeRecord> iterator = ev.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
DomainObjectChangeRecord record = iterator.next();
|
||||
for (DomainObjectChangeRecord record : ev) {
|
||||
if (record instanceof ProgramChangeRecord) {
|
||||
ProgramChangeRecord programRecord = (ProgramChangeRecord) record;
|
||||
Address start = programRecord.getStart();
|
||||
|
@ -1133,9 +1167,7 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
}
|
||||
|
||||
public void clearViewSettings() {
|
||||
GraphPerspectiveInfo<FGVertex, FGEdge> info =
|
||||
GraphPerspectiveInfo.createInvalidGraphPerspectiveInfo();
|
||||
controller.setGraphPerspective(info);
|
||||
controller.clearViewSettings();
|
||||
}
|
||||
|
||||
void addMarkerProviderSupplier(MarginProviderSupplier supplier) {
|
||||
|
@ -1148,6 +1180,10 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
refreshAndKeepPerspective();
|
||||
}
|
||||
|
||||
public FunctionGraphPlugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Navigatable interface methods
|
||||
//==================================================================================================
|
||||
|
@ -1267,9 +1303,9 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
|
|||
tool.setStatusInfo(message);
|
||||
}
|
||||
|
||||
public void internalGoTo(ProgramLocation location, Program program) {
|
||||
public void internalGoTo(ProgramLocation location) {
|
||||
GoToService goToService = tool.getService(GoToService.class);
|
||||
goToService.goTo(this, location, program);
|
||||
goToService.goTo(this, location, location.getProgram());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -58,7 +58,7 @@ import ghidra.util.exception.AssertException;
|
|||
category = PluginCategoryNames.GRAPH,
|
||||
shortDescription = FunctionGraphPlugin.FUNCTION_GRAPH_NAME,
|
||||
description = "Plugin for show a graphical representation of the code blocks of a function",
|
||||
servicesRequired = { GoToService.class, BlockModelService.class, CodeViewerService.class, ProgramManager.class },
|
||||
servicesRequired = { GoToService.class, CodeViewerService.class, ProgramManager.class },
|
||||
servicesProvided = { FunctionGraphMarginService.class }
|
||||
)
|
||||
//@formatter:on
|
||||
|
@ -126,7 +126,8 @@ public class FunctionGraphPlugin extends ProgramPlugin
|
|||
|
||||
ColorizingService colorizingService = tool.getService(ColorizingService.class);
|
||||
if (colorizingService != null) {
|
||||
colorProvider = new ToolBasedColorProvider(this, colorizingService);
|
||||
colorProvider =
|
||||
new ToolBasedColorProvider(() -> getCurrentProgram(), colorizingService);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +140,8 @@ public class FunctionGraphPlugin extends ProgramPlugin
|
|||
}
|
||||
}
|
||||
else if (interfaceClass == ColorizingService.class) {
|
||||
colorProvider = new ToolBasedColorProvider(this, (ColorizingService) service);
|
||||
colorProvider =
|
||||
new ToolBasedColorProvider(() -> getCurrentProgram(), (ColorizingService) service);
|
||||
connectedProvider.refreshAndKeepPerspective();
|
||||
}
|
||||
else if (interfaceClass == MarkerService.class) {
|
||||
|
@ -172,6 +174,8 @@ public class FunctionGraphPlugin extends ProgramPlugin
|
|||
|
||||
private List<FGLayoutProvider> loadLayoutProviders() {
|
||||
|
||||
// Shared Code Note: This code is mirrored in the FgDisplay for the Code Comparison API
|
||||
|
||||
FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
|
||||
List<FGLayoutProvider> instances = layoutFinder.findLayouts();
|
||||
if (instances.isEmpty()) {
|
||||
|
@ -214,6 +218,8 @@ public class FunctionGraphPlugin extends ProgramPlugin
|
|||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
// Shared Code Note: This code is mirrored in the FgDisplay for the Code Comparison API
|
||||
|
||||
// Graph -> Function Graph
|
||||
Options fgOptions = options.getOptions(FUNCTION_GRAPH_NAME);
|
||||
functionGraphOptions.loadOptions(fgOptions);
|
||||
|
|
|
@ -30,7 +30,7 @@ import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphVertexAttributes;
|
|||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
class IndependentColorProvider implements FGColorProvider {
|
||||
public class IndependentColorProvider implements FGColorProvider {
|
||||
|
||||
private static final String VERTEX_COLORS = "VERTEX_COLORS";
|
||||
|
||||
|
@ -38,7 +38,7 @@ class IndependentColorProvider implements FGColorProvider {
|
|||
|
||||
private final PluginTool tool;
|
||||
|
||||
IndependentColorProvider(PluginTool tool) {
|
||||
public IndependentColorProvider(PluginTool tool) {
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ public class SetFormatDialogComponentProvider extends DialogComponentProvider {
|
|||
return null;
|
||||
}
|
||||
|
||||
/*testing*/ FieldHeader getFieldHeader() {
|
||||
public FieldHeader getFieldHeader() {
|
||||
return listingPanel.getFieldHeader();
|
||||
}
|
||||
//==================================================================================================
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.functiongraph;
|
|||
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||
|
@ -25,13 +26,18 @@ import ghidra.framework.options.SaveState;
|
|||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
class ToolBasedColorProvider implements FGColorProvider {
|
||||
/**
|
||||
* An implementation of the {@link FGColorProvider} that works using the color services of the tool.
|
||||
* A different implementation will be used if the tool's color services are not installed.
|
||||
*/
|
||||
public class ToolBasedColorProvider implements FGColorProvider {
|
||||
|
||||
private final ColorizingService service;
|
||||
private final FunctionGraphPlugin plugin;
|
||||
private final Supplier<Program> programSupplier;
|
||||
|
||||
ToolBasedColorProvider(FunctionGraphPlugin plugin, ColorizingService colorizingService) {
|
||||
this.plugin = plugin;
|
||||
public ToolBasedColorProvider(Supplier<Program> programSupplier,
|
||||
ColorizingService colorizingService) {
|
||||
this.programSupplier = programSupplier;
|
||||
this.service = colorizingService;
|
||||
}
|
||||
|
||||
|
@ -42,7 +48,7 @@ class ToolBasedColorProvider implements FGColorProvider {
|
|||
|
||||
@Override
|
||||
public void setVertexColor(FGVertex vertex, Color color) {
|
||||
Program program = plugin.getCurrentProgram();
|
||||
Program program = programSupplier.get();
|
||||
int id = program.startTransaction("Set Background Color");
|
||||
try {
|
||||
service.setBackgroundColor(vertex.getAddresses(), color);
|
||||
|
@ -56,7 +62,7 @@ class ToolBasedColorProvider implements FGColorProvider {
|
|||
|
||||
@Override
|
||||
public void clearVertexColor(FGVertex vertex) {
|
||||
Program program = plugin.getCurrentProgram();
|
||||
Program program = programSupplier.get();
|
||||
int id = program.startTransaction("Set Background Color");
|
||||
try {
|
||||
service.clearBackgroundColor(vertex.getAddresses());
|
||||
|
|
|
@ -222,7 +222,11 @@ public class FunctionGraphFactory {
|
|||
for (FGVertex v : vertices) {
|
||||
monitor.increment();
|
||||
try {
|
||||
Swing.runNow(v::getComponent);
|
||||
Swing.runNow(() -> {
|
||||
if (!monitor.isCancelled()) {
|
||||
v.getComponent();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
|
|
|
@ -744,7 +744,7 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
|
|||
@Override
|
||||
protected void showPopup(JComponent comp, Field field, MouseEvent event,
|
||||
Rectangle fieldBounds) {
|
||||
if (!controller.arePopupsEnabled()) {
|
||||
if (!controller.arePopupsVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -160,7 +160,6 @@ public class SetVertexMostRecentColorAction extends MultiActionDockingAction {
|
|||
actionList.add(createSeparator());
|
||||
actionList.add(chooseColorAction);
|
||||
actionList.add(clearColorAction);
|
||||
|
||||
return actionList;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* ###
|
||||
* 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.functiongraph.mvc;
|
||||
|
||||
import ghidra.app.plugin.core.functiongraph.FGProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DefaultFGControllerListener implements FGControllerListener {
|
||||
|
||||
private FGProvider provider;
|
||||
|
||||
public DefaultFGControllerListener(FGProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataChanged() {
|
||||
provider.functionGraphDataChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userChangedLocation(ProgramLocation location, boolean vertexChanged) {
|
||||
|
||||
boolean updateHistory = false;
|
||||
if (vertexChanged) {
|
||||
if (shouldSaveVertexChanges()) {
|
||||
// put the navigation on the history stack if we've changed nodes (this is the
|
||||
// location we are leaving)
|
||||
provider.saveLocationToHistory();
|
||||
updateHistory = true;
|
||||
}
|
||||
}
|
||||
|
||||
provider.graphLocationChanged(location);
|
||||
|
||||
if (updateHistory) {
|
||||
// put the new location on the history stack now that we've updated the provider
|
||||
provider.saveLocationToHistory();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSaveVertexChanges() {
|
||||
FunctionGraphPlugin plugin = provider.getPlugin();
|
||||
FunctionGraphOptions options = plugin.getFunctionGraphOptions();
|
||||
return options.getNavigationHistoryChoice() == NavigationHistoryChoices.VERTEX_CHANGES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userChangedSelection(ProgramSelection selection) {
|
||||
provider.graphSelectionChanged(selection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userSelectedText(String s) {
|
||||
provider.setClipboardStringContent(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userNavigated(ProgramLocation location) {
|
||||
// Tell the provider to navigate to this location. This will work for connected and
|
||||
// disconnected providers.
|
||||
provider.internalGoTo(location);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ import java.util.List;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import com.google.common.cache.*;
|
||||
|
||||
|
@ -34,7 +33,8 @@ import docking.widgets.fieldpanel.support.Highlight;
|
|||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.codebrowser.ListingMiddleMouseHighlightProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.*;
|
||||
import ghidra.app.plugin.core.functiongraph.FGColorProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.SetFormatDialogComponentProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
|
@ -63,10 +63,9 @@ import ghidra.util.datastruct.WeakSet;
|
|||
|
||||
public class FGController implements ProgramLocationListener, ProgramSelectionListener {
|
||||
|
||||
private final FunctionGraphPlugin plugin;
|
||||
private FGProvider provider;
|
||||
private final FGModel model;
|
||||
private final FGView view;
|
||||
private FgEnv env;
|
||||
private FGModel model;
|
||||
private FGView view;
|
||||
|
||||
private FGData functionGraphData = new EmptyFunctionGraphData("Uninitialized Function Graph");
|
||||
private FunctionGraphViewSettings viewSettings = new NoFunctionGraphViewSettings();
|
||||
|
@ -82,10 +81,11 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
private FormatManager defaultFormatManager; // lazy!
|
||||
|
||||
private FunctionGraphOptions functionGraphOptions;
|
||||
private FGControllerListener listener;
|
||||
|
||||
private FgHighlightProvider sharedHighlightProvider;
|
||||
private StringSelectionListener sharedStringSelectionListener =
|
||||
string -> provider.setClipboardStringContent(string);
|
||||
string -> listener.userSelectedText(string);
|
||||
|
||||
private Cache<Function, FGData> cache;
|
||||
private BiConsumer<FGData, Boolean> fgDataDisposeListener = (data, evicted) -> {
|
||||
|
@ -95,14 +95,18 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
private WeakSet<MarginProviderSupplier> marginProviders =
|
||||
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
|
||||
|
||||
public FGController(FGProvider provider, FunctionGraphPlugin plugin) {
|
||||
this.provider = provider;
|
||||
this.plugin = plugin;
|
||||
public FGController(FgEnv env, FGControllerListener controllerListener) {
|
||||
this.env = env;
|
||||
this.listener = Objects.requireNonNull(controllerListener);
|
||||
this.cache = buildCache(this::cacheValueRemoved);
|
||||
this.model = new FGModel(this);
|
||||
this.view = new FGView(this, model.getTaskMonitorComponent());
|
||||
|
||||
functionGraphOptions = plugin.getFunctionGraphOptions();
|
||||
this.functionGraphOptions = env.getOptions();
|
||||
}
|
||||
|
||||
public FgEnv getEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
private boolean disposeGraphDataIfNotInUse(FGData data) {
|
||||
|
@ -124,7 +128,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
private FormatManager createMinimalFormatManager() {
|
||||
FormatManager userDefinedFormat = plugin.getUserDefinedFormat();
|
||||
FormatManager userDefinedFormat = env.getUserDefinedFormat();
|
||||
if (userDefinedFormat != null) {
|
||||
return userDefinedFormat;
|
||||
}
|
||||
|
@ -132,10 +136,22 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
private FormatManager createFullFormatManager() {
|
||||
CodeViewerService codeViewer = plugin.getTool().getService(CodeViewerService.class);
|
||||
CodeViewerService codeViewer =
|
||||
env.getTool().getService(CodeViewerService.class);
|
||||
|
||||
if (codeViewer != null) {
|
||||
// Prefer the manager from the service, as this is the current state of the Listing.
|
||||
return codeViewer.getFormatManager();
|
||||
}
|
||||
|
||||
// No code viewer service implies we are not in the default tool
|
||||
PluginTool tool = env.getTool();
|
||||
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
|
||||
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
return new FormatManager(displayOptions, fieldOptions);
|
||||
|
||||
}
|
||||
|
||||
public FormatManager getMinimalFormatManager() {
|
||||
if (minimalFormatManager == null) {
|
||||
setMinimalFormatManager(createMinimalFormatManager());
|
||||
|
@ -149,6 +165,18 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
minimalFormatManager.addHighlightProvider(highlightProvider);
|
||||
}
|
||||
|
||||
public void updateMinimalFormatManager(FormatManager newFormatManager) {
|
||||
|
||||
// ensure the format manager has been created
|
||||
getMinimalFormatManager();
|
||||
|
||||
SaveState saveState = new SaveState();
|
||||
newFormatManager.saveState(saveState);
|
||||
minimalFormatManager.readState(saveState);
|
||||
env.setUserDefinedFormat(minimalFormatManager);
|
||||
view.repaint();
|
||||
}
|
||||
|
||||
public FormatManager getFullFormatManager() {
|
||||
if (fullFormatManager == null) {
|
||||
fullFormatManager = createFullFormatManager();
|
||||
|
@ -156,7 +184,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
return fullFormatManager;
|
||||
}
|
||||
|
||||
private FormatManager getDefaultFormatManager() {
|
||||
public FormatManager getDefaultFormatManager() {
|
||||
if (defaultFormatManager == null) {
|
||||
defaultFormatManager = createDefaultFormat();
|
||||
}
|
||||
|
@ -167,22 +195,24 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
if (sharedHighlightProvider != null) {
|
||||
return sharedHighlightProvider;
|
||||
}
|
||||
|
||||
JComponent centerOverComponent = view.getPrimaryGraphViewer();
|
||||
sharedHighlightProvider =
|
||||
new FgHighlightProvider(plugin.getTool(), provider.getComponent());
|
||||
new FgHighlightProvider(env.getTool(), centerOverComponent);
|
||||
return sharedHighlightProvider;
|
||||
}
|
||||
|
||||
public void formatChanged() {
|
||||
setMinimalFormatManager(plugin.getUserDefinedFormat());
|
||||
setMinimalFormatManager(env.getUserDefinedFormat());
|
||||
view.repaint();
|
||||
}
|
||||
|
||||
public Navigatable getNavigatable() {
|
||||
return provider;
|
||||
return env.getNavigatable();
|
||||
}
|
||||
|
||||
private FormatManager createDefaultFormat() {
|
||||
OptionsService options = plugin.getTool().getService(OptionsService.class);
|
||||
OptionsService options = env.getTool().getService(OptionsService.class);
|
||||
ToolOptions displayOptions = options.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
|
||||
ToolOptions fieldOptions = options.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
|
||||
|
@ -321,31 +351,13 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
FunctionGraph graph = functionGraphData.getFunctionGraph();
|
||||
FGVertex newFocusedVertex = graph.getFocusedVertex();
|
||||
boolean vertexChanged = lastUserNavigatedVertex != newFocusedVertex;
|
||||
boolean updateHistory = false;
|
||||
if (vertexChanged) {
|
||||
if (shouldSaveVertexChanges()) {
|
||||
// put the navigation on the history stack if we've changed nodes (this is the
|
||||
// location we are leaving)
|
||||
provider.saveLocationToHistory();
|
||||
updateHistory = true;
|
||||
}
|
||||
lastUserNavigatedVertex = newFocusedVertex;
|
||||
}
|
||||
|
||||
viewSettings.setLocation(loc);
|
||||
provider.graphLocationChanged(loc);
|
||||
|
||||
if (updateHistory) {
|
||||
// put the new location on the history stack now that we've updated the provider
|
||||
provider.saveLocationToHistory();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSaveVertexChanges() {
|
||||
return functionGraphOptions
|
||||
.getNavigationHistoryChoice() == NavigationHistoryChoices.VERTEX_CHANGES;
|
||||
listener.userChangedLocation(loc, vertexChanged);
|
||||
}
|
||||
|
||||
// this is a callback from the vertex's listing panel
|
||||
@Override
|
||||
public void programSelectionChanged(ProgramSelection selection, EventTrigger trigger) {
|
||||
if (trigger != EventTrigger.GUI_ACTION) {
|
||||
|
@ -360,7 +372,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
|
||||
// push the user changes up to the provider
|
||||
viewSettings.setSelection(fullSelection);
|
||||
provider.graphSelectionChanged(fullSelection);
|
||||
listener.userChangedSelection(fullSelection);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -450,32 +462,28 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
return view.isSatelliteVisible();
|
||||
}
|
||||
|
||||
public void setSatelliteVisible(boolean visible) {
|
||||
view.setSatelliteVisible(visible);
|
||||
}
|
||||
|
||||
public boolean isSatelliteDocked() {
|
||||
return view.isSatelliteDocked();
|
||||
}
|
||||
|
||||
public void satelliteProviderShown() {
|
||||
// note: always show the primary provider when the satellite is shown
|
||||
if (provider.isVisible()) {
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
// We do this later because it is possible during initialization that the provider is
|
||||
// not 'inTool' because of how XML gets restored. So, just do it later--it's less code.
|
||||
SwingUtilities.invokeLater(() -> provider.setVisible(true));
|
||||
}
|
||||
|
||||
public void primaryProviderHidden() {
|
||||
clear();
|
||||
}
|
||||
|
||||
public void setPopupsVisible(boolean visible) {
|
||||
view.setPopupsVisible(visible);
|
||||
provider.setPopupsVisible(visible);
|
||||
}
|
||||
|
||||
public boolean arePopupsVisible() {
|
||||
return view.arePopupsVisible();
|
||||
}
|
||||
|
||||
public boolean arePopupsEnabled() {
|
||||
return view.arePopupsEnabled();
|
||||
return arePopupsVisible();
|
||||
}
|
||||
|
||||
public FGVertex getFocusedVertex() {
|
||||
|
@ -486,6 +494,13 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
return view.getFocusedVertex();
|
||||
}
|
||||
|
||||
public FGVertex getEntryPointVertex() {
|
||||
if (!hasResults()) {
|
||||
return null;
|
||||
}
|
||||
return view.getEntryPointVertex();
|
||||
}
|
||||
|
||||
public Set<FGVertex> getSelectedVertices() {
|
||||
if (!hasResults()) {
|
||||
return null;
|
||||
|
@ -504,6 +519,10 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
view.cleanup();
|
||||
}
|
||||
|
||||
public ProgramSelection getSelection() {
|
||||
return viewSettings.getSelection();
|
||||
}
|
||||
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
viewSettings.setSelection(selection);
|
||||
}
|
||||
|
@ -533,6 +552,12 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
}
|
||||
|
||||
public void clearViewSettings() {
|
||||
GraphPerspectiveInfo<FGVertex, FGEdge> info =
|
||||
GraphPerspectiveInfo.createInvalidGraphPerspectiveInfo();
|
||||
setGraphPerspective(info);
|
||||
}
|
||||
|
||||
public void display(Program program, ProgramLocation location) {
|
||||
if (viewContainsLocation(location)) {
|
||||
// no need to rebuild the graph; just set the location
|
||||
|
@ -587,6 +612,40 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells this provider to refresh, which means to rebuild the graph and relayout the
|
||||
* vertices.
|
||||
* @param keepPerspective true to keep the perspective (e.g., zoom level and view position)
|
||||
*/
|
||||
public void refresh(boolean keepPerspective) {
|
||||
|
||||
if (functionGraphData.hasResults()) {
|
||||
//
|
||||
// We use the graph's data over the 'currentXXX' data, as there is a chance that the
|
||||
// latter values have been set to new values, while the graph has differing data. In
|
||||
// that case we have made the decision to prefer the graph's data.
|
||||
//
|
||||
Function function = functionGraphData.getFunction();
|
||||
Address address = function.getEntryPoint();
|
||||
ProgramLocation currentLocation = env.getGraphLocation();
|
||||
Address currentAddress = currentLocation.getAddress();
|
||||
if (function.getBody().contains(currentAddress)) {
|
||||
// prefer the current address if it is within the current function (i.e., the
|
||||
// location hasn't changed out from under the graph due to threading issues)
|
||||
address = currentAddress;
|
||||
}
|
||||
|
||||
Program program = function.getProgram();
|
||||
ProgramLocation programLocation = new ProgramLocation(program, address);
|
||||
rebuildDisplay(program, programLocation, keepPerspective);
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = env.getProgram();
|
||||
ProgramLocation currentLocation = env.getGraphLocation();
|
||||
rebuildDisplay(program, currentLocation, keepPerspective);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method differs from the <tt>refresh</tt>...() methods in that it will trigger a
|
||||
* graph rebuild, clearing any cached graph data in the process. If <tt>maintainPerspective</tt>
|
||||
|
@ -611,7 +670,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
public void rebuildCurrentDisplay() {
|
||||
provider.refreshAndKeepPerspective();
|
||||
refresh(true);
|
||||
}
|
||||
|
||||
public void resetGraph() {
|
||||
|
@ -629,9 +688,9 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
rebuildDisplay(function.getProgram(), location, false);
|
||||
|
||||
// we are changing the location above--make sure the external tool knows of it
|
||||
ProgramLocation externalLocation = plugin.getProgramLocation();
|
||||
ProgramLocation externalLocation = env.getToolLocation();
|
||||
if (!externalLocation.getAddress().equals(location.getAddress())) {
|
||||
provider.graphLocationChanged(location);
|
||||
listener.userChangedLocation(location, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -649,15 +708,17 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
public void refreshDisplayWithoutRebuilding() {
|
||||
if (functionGraphData.hasResults()) {
|
||||
view.refreshDisplayWithoutRebuilding();
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshDisplayForAddress(Address address) {
|
||||
view.refreshDisplayForAddress(address);
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
return provider.getProgram();
|
||||
return env.getProgram();
|
||||
}
|
||||
|
||||
public FGData getFunctionGraphData() {
|
||||
|
@ -671,6 +732,10 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
return null;
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return model.isBusy();
|
||||
}
|
||||
|
||||
public JComponent getViewComponent() {
|
||||
return view.getViewComponent();
|
||||
}
|
||||
|
@ -680,7 +745,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
view.setLayoutProvider(newLayout);
|
||||
|
||||
if (previousLayout == null) {
|
||||
provider.refreshAndResetPerspective();
|
||||
refresh(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -690,7 +755,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
view.relayout();
|
||||
}
|
||||
else {
|
||||
provider.refreshAndResetPerspective();
|
||||
refresh(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -704,40 +769,37 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
vertex = view.getEntryPointVertex();
|
||||
}
|
||||
|
||||
PluginTool tool = plugin.getTool();
|
||||
PluginTool tool = env.getTool();
|
||||
SetFormatDialogComponentProvider setFormatDialog =
|
||||
new SetFormatDialogComponentProvider(getDefaultFormatManager(), minimalFormatManager,
|
||||
tool, provider.getProgram(), vertex.getAddresses());
|
||||
tool, env.getProgram(), vertex.getAddresses());
|
||||
tool.showDialog(setFormatDialog);
|
||||
FormatManager newFormatManager = setFormatDialog.getNewFormatManager();
|
||||
if (newFormatManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SaveState saveState = new SaveState();
|
||||
newFormatManager.saveState(saveState);
|
||||
minimalFormatManager.readState(saveState);
|
||||
plugin.setUserDefinedFormat(minimalFormatManager);
|
||||
view.repaint();
|
||||
updateMinimalFormatManager(newFormatManager);
|
||||
}
|
||||
|
||||
public void showXRefsDialog() {
|
||||
PluginTool tool = plugin.getTool();
|
||||
Program program = plugin.getCurrentProgram();
|
||||
PluginTool tool = env.getTool();
|
||||
Program program = env.getProgram();
|
||||
List<Reference> references = getXReferencesToGraph();
|
||||
XRefChooserDialog chooserDialog = new XRefChooserDialog(references, program, tool);
|
||||
|
||||
tool.showDialog(chooserDialog, provider);
|
||||
JComponent centerOverComponent = view.getPrimaryGraphViewer();
|
||||
tool.showDialog(chooserDialog, centerOverComponent);
|
||||
Reference reference = chooserDialog.getSelectedReference();
|
||||
if (reference == null) {
|
||||
return; // the user cancelled
|
||||
}
|
||||
|
||||
internalGoTo(new ProgramLocation(program, reference.getFromAddress()), program);
|
||||
listener.userNavigated(new ProgramLocation(program, reference.getFromAddress()));
|
||||
}
|
||||
|
||||
private List<Reference> getXReferencesToGraph() {
|
||||
Program program = plugin.getCurrentProgram();
|
||||
Program program = env.getProgram();
|
||||
Function function = getGraphedFunction();
|
||||
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
|
@ -886,7 +948,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
restoreGraphSettingsForNewFunction();
|
||||
|
||||
viewSettings = new CurrentFunctionGraphViewSettings(view, viewSettings);
|
||||
provider.functionGraphDataChanged();
|
||||
listener.dataChanged();
|
||||
}
|
||||
|
||||
private boolean disposeIfNotInCache(FGData data) {
|
||||
|
@ -921,7 +983,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
private boolean isSnapshot() {
|
||||
return !provider.isConnected();
|
||||
return !getNavigatable().isConnected();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -956,11 +1018,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
public PluginTool getTool() {
|
||||
return provider.getTool();
|
||||
}
|
||||
|
||||
public FGProvider getProvider() {
|
||||
return provider;
|
||||
return env.getTool();
|
||||
}
|
||||
|
||||
public Point getViewerPointFromVertexPoint(FGVertex vertex, Point point) {
|
||||
|
@ -988,22 +1046,22 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
public Color getMostRecentColor() {
|
||||
FGColorProvider colorProvider = plugin.getColorProvider();
|
||||
FGColorProvider colorProvider = env.getColorProvider();
|
||||
return colorProvider.getMostRecentColor();
|
||||
}
|
||||
|
||||
public List<Color> getRecentColors() {
|
||||
FGColorProvider colorProvider = plugin.getColorProvider();
|
||||
FGColorProvider colorProvider = env.getColorProvider();
|
||||
return colorProvider.getRecentColors();
|
||||
}
|
||||
|
||||
public void saveVertexColors(FGVertex vertex, FunctionGraphVertexAttributes settings) {
|
||||
FGColorProvider colorProvider = plugin.getColorProvider();
|
||||
FGColorProvider colorProvider = env.getColorProvider();
|
||||
colorProvider.saveVertexColors(vertex, settings);
|
||||
}
|
||||
|
||||
public void restoreVertexColors(FGVertex vertex, FunctionGraphVertexAttributes settings) {
|
||||
FGColorProvider colorProvider = plugin.getColorProvider();
|
||||
FGColorProvider colorProvider = env.getColorProvider();
|
||||
colorProvider.loadVertexColors(vertex, settings);
|
||||
}
|
||||
|
||||
|
@ -1013,11 +1071,12 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
}
|
||||
|
||||
public FGColorProvider getColorProvider() {
|
||||
return plugin.getColorProvider();
|
||||
return env.getColorProvider();
|
||||
}
|
||||
|
||||
public <T> T getService(Class<T> serviceClass) {
|
||||
return plugin.getService(serviceClass);
|
||||
PluginTool tool = env.getTool();
|
||||
return tool.getService(serviceClass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1026,7 +1085,23 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
*/
|
||||
public void synchronizeProgramLocationAfterEdit() {
|
||||
// It is assumed that the provider's location is the correct location.
|
||||
viewSettings.setLocation(provider.getLocation());
|
||||
viewSettings.setLocation(env.getGraphLocation());
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple method to move the cursor to the given location in the currently graphed function.
|
||||
* If the location is not in the current function, nothing will happen.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
public void setLocation(ProgramLocation location) {
|
||||
if (viewContainsLocation(location)) {
|
||||
viewSettings.setLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
public ProgramLocation getLocation() {
|
||||
return viewSettings.getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1042,16 +1117,12 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
|||
handleLocationChangedFromVertex(location);
|
||||
}
|
||||
|
||||
public void internalGoTo(ProgramLocation programLocation, Program program) {
|
||||
provider.internalGoTo(programLocation, program);
|
||||
}
|
||||
|
||||
private Cache<Function, FGData> buildCache(RemovalListener<Function, FGData> listener) {
|
||||
private Cache<Function, FGData> buildCache(RemovalListener<Function, FGData> removalListener) {
|
||||
//@formatter:off
|
||||
return CacheBuilder
|
||||
.newBuilder()
|
||||
.maximumSize(5)
|
||||
.removalListener(listener)
|
||||
.removalListener(removalListener)
|
||||
// Note: using soft values means that sometimes our data is reclaimed by the
|
||||
// Garbage Collector. We don't want that, we wish to call dispose() on the data
|
||||
//.softValues()
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* ###
|
||||
* 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.functiongraph.mvc;
|
||||
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public interface FGControllerListener {
|
||||
|
||||
/**
|
||||
* Called when the {@link FGData} for the current viewer has been set on the controller.
|
||||
*/
|
||||
public void dataChanged();
|
||||
|
||||
/**
|
||||
* A notification for when the user has changed the location by interacting with the Function
|
||||
* Graph UI.
|
||||
* @param location the new location
|
||||
* @param vertexChanged true if a new vertex has been selected
|
||||
*/
|
||||
public void userChangedLocation(ProgramLocation location, boolean vertexChanged);
|
||||
|
||||
/**
|
||||
* A notification for when the user has changed the selection by interacting with the Function
|
||||
* Graph UI.
|
||||
* @param selection the new selection
|
||||
*/
|
||||
public void userChangedSelection(ProgramSelection selection);
|
||||
|
||||
/**
|
||||
* A notification for when the user has selected text in a vertex by interacting with the
|
||||
* Function Graph UI.
|
||||
* @param s the selected text
|
||||
*/
|
||||
public void userSelectedText(String s);
|
||||
|
||||
/**
|
||||
* Called when the users requests the tool to navigate to a new location, such as when
|
||||
* double-clicking an xref.
|
||||
* @param location the location
|
||||
*/
|
||||
public void userNavigated(ProgramLocation location);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/* ###
|
||||
* 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.functiongraph.mvc;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.functiongraph.FGColorProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
/**
|
||||
* A simple class that allows us to re-use parts of the {@link FunctionGraph} API by abstracting
|
||||
* away the {@link FunctionGraphPlugin}. The env allows the controller to get the state of the
|
||||
* graph, the state of the tool and to share resources among graphs.
|
||||
*/
|
||||
public interface FgEnv {
|
||||
|
||||
public PluginTool getTool();
|
||||
|
||||
public Program getProgram();
|
||||
|
||||
public FunctionGraphOptions getOptions();
|
||||
|
||||
public FGColorProvider getColorProvider();
|
||||
|
||||
public List<FGLayoutProvider> getLayoutProviders();
|
||||
|
||||
/**
|
||||
* Adds the given action to the provider used by this environment.
|
||||
* @param action the action
|
||||
*/
|
||||
public void addLocalAction(DockingAction action);
|
||||
|
||||
/**
|
||||
* Returns the graph format manager that can be shared amongst all graphs.
|
||||
* @return the graph format manager that can be shared amongst all graphs.
|
||||
* @see #setUserDefinedFormat(FormatManager)
|
||||
*/
|
||||
public FormatManager getUserDefinedFormat();
|
||||
|
||||
/**
|
||||
* Sets the graph format manager that can be shared amongst all graphs.
|
||||
* @param format the format manager
|
||||
* @see #getUserDefinedFormat()
|
||||
*/
|
||||
public void setUserDefinedFormat(FormatManager format);
|
||||
|
||||
public Navigatable getNavigatable();
|
||||
|
||||
/**
|
||||
* The tool location is the program location shared by all plugins. Disconnected graphs my not
|
||||
* be using this location.
|
||||
* @return the location
|
||||
* @see #getGraphLocation()
|
||||
*/
|
||||
public ProgramLocation getToolLocation();
|
||||
|
||||
/**
|
||||
* Sets the selection for this function graph environment. If the graph is connected to the
|
||||
* tool, then the selection will be sent to the tool as well as to the graph.
|
||||
* @param selection the selection
|
||||
*/
|
||||
public void setSelection(ProgramSelection selection);
|
||||
|
||||
/**
|
||||
* Graph location is the program location inside of the graph, which may differ from that of the
|
||||
* tool, such as for disconnected graphs.
|
||||
* @return the location
|
||||
* @see #getToolLocation()
|
||||
*/
|
||||
public ProgramLocation getGraphLocation();
|
||||
}
|
|
@ -507,6 +507,10 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
return (FGController) TestUtils.getInstanceField("controller", graphProvider);
|
||||
}
|
||||
|
||||
protected FGProvider getProvider() {
|
||||
return graphProvider;
|
||||
}
|
||||
|
||||
protected FGComponent getGraphComponent() {
|
||||
FGController controller =
|
||||
(FGController) TestUtils.getInstanceField("controller", graphProvider);
|
||||
|
@ -799,8 +803,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected DockingAction getCopyAction() {
|
||||
FGController controller = getFunctionGraphController();
|
||||
FGProvider provider = controller.getProvider();
|
||||
FGProvider provider = getProvider();
|
||||
|
||||
FGClipboardProvider clipboarProvider =
|
||||
(FGClipboardProvider) getInstanceField("clipboardProvider", provider);
|
||||
|
@ -885,11 +888,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
}
|
||||
|
||||
protected boolean isSatelliteVisible() {
|
||||
return isSatelliteVisible(getFunctionGraphController());
|
||||
return isSatelliteVisible(graphProvider);
|
||||
}
|
||||
|
||||
protected boolean isSatelliteVisible(FGController controller) {
|
||||
protected boolean isSatelliteVisible(FGProvider fgProvider) {
|
||||
|
||||
FGController controller = fgProvider.getController();
|
||||
FGView view = controller.getView();
|
||||
GraphComponent<FGVertex, FGEdge, FunctionGraph> gc = view.getGraphComponent();
|
||||
if (gc == null) {
|
||||
|
@ -898,7 +902,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
|
||||
// Note: we cannot rely on 'gc.isSatelliteShowing()', as when the application does not
|
||||
// have focus, isShowing() will return false :(
|
||||
ComponentProvider satellite = controller.getProvider().getSatelliteProvider();
|
||||
ComponentProvider satellite = fgProvider.getSatelliteProvider();
|
||||
boolean satelliteProviderVisible =
|
||||
runSwing(() -> satellite != null && satellite.isVisible());
|
||||
|
||||
|
@ -1206,7 +1210,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
runSwing(() -> controller.invalidateAllCacheForProgram(program));
|
||||
}
|
||||
|
||||
protected FGController cloneGraph() {
|
||||
protected FGProvider cloneGraph() {
|
||||
|
||||
DockingActionIf snapshotAction =
|
||||
AbstractDockingTest.getAction(tool, graphPlugin.getName(), "Function Graph Clone");
|
||||
|
@ -1222,7 +1226,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
waitForBusyRunManager(controllerClone);
|
||||
waitForAnimation(controllerClone);
|
||||
|
||||
return controllerClone;
|
||||
return providerClone;
|
||||
}
|
||||
|
||||
protected void color(final FGVertex v1, final Color color) {
|
||||
|
@ -2139,9 +2143,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
|
|||
assertUndockedProviderShowing(provider);
|
||||
}
|
||||
|
||||
protected void assertUndockedProviderShowing(ComponentProvider satellite) {
|
||||
assertNotNull("Undocked provider is not installed when it should be", satellite);
|
||||
assertTrue("Undocked provider is not showing after being undocked", satellite.isVisible());
|
||||
protected void assertUndockedProviderShowing(ComponentProvider provider) {
|
||||
assertNotNull("Undocked provider is not installed when it should be", provider);
|
||||
assertTrue("Undocked provider is not showing after being undocked", provider.isVisible());
|
||||
}
|
||||
|
||||
protected void assertZoomedIn() {
|
||||
|
|
|
@ -148,7 +148,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest {
|
|||
// First thing we need to do is close the function graph window. It's opened on
|
||||
// startup by default in this test suite but we want it closed until we clear the
|
||||
// function code bytes.
|
||||
this.getFunctionGraphController().getProvider().closeComponent();
|
||||
this.getProvider().closeComponent();
|
||||
|
||||
// Set up some additional plugins we need.
|
||||
try {
|
||||
|
|
|
@ -97,7 +97,8 @@ public class FunctionGraphGroupVertices2Test extends AbstractFunctionGraphTest {
|
|||
//
|
||||
// Clone the graph
|
||||
//
|
||||
FGController clonedController = cloneGraph();
|
||||
FGProvider clonedProvider = cloneGraph();
|
||||
FGController clonedController = clonedProvider.getController();
|
||||
FGData clonedData = clonedController.getFunctionGraphData();
|
||||
FunctionGraph clonedFunctionGraph = clonedData.getFunctionGraph();
|
||||
|
||||
|
|
|
@ -418,15 +418,14 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
|
|||
AddressSetView addresses = focusedVertex.getAddresses();
|
||||
Address address = addresses.getMinAddress();
|
||||
ProgramSelection selection =
|
||||
new ProgramSelection(program.getAddressFactory(), address, address.add(8));
|
||||
new ProgramSelection(address, address.add(8));
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent("Test", selection, program));
|
||||
|
||||
//
|
||||
// Validate and execute the action
|
||||
//
|
||||
DockingAction copyAction = getCopyAction();
|
||||
FGController controller = getFunctionGraphController();
|
||||
ComponentProvider provider = controller.getProvider();
|
||||
ComponentProvider provider = getProvider();
|
||||
assertTrue(copyAction.isEnabledForContext(provider.getActionContext(null)));
|
||||
|
||||
performAction(copyAction, provider, false);
|
||||
|
@ -478,8 +477,7 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
|
|||
//
|
||||
// Validate and execute the action
|
||||
//
|
||||
FGController controller = getFunctionGraphController();
|
||||
ComponentProvider provider = controller.getProvider();
|
||||
ComponentProvider provider = getProvider();
|
||||
ActionContext actionContext = provider.getActionContext(null);
|
||||
boolean isEnabled = copyAction.isEnabledForContext(actionContext);
|
||||
debugAction(copyAction, actionContext);
|
||||
|
@ -585,14 +583,14 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
|
|||
|
||||
@Test
|
||||
public void testGraphNodesCreated() throws Exception {
|
||||
|
||||
FGData graphData = getFunctionGraphData();
|
||||
assertNotNull(graphData);
|
||||
assertTrue("Unexpectedly received an empty FunctionGraphData", graphData.hasResults());
|
||||
FunctionGraph functionGraph = graphData.getFunctionGraph();
|
||||
Collection<FGVertex> vertices = functionGraph.getVertices();
|
||||
|
||||
BlockModelService blockService = tool.getService(BlockModelService.class);
|
||||
CodeBlockModel blockModel = blockService.getActiveBlockModel(program);
|
||||
CodeBlockModel blockModel = new BasicBlockModel(program);
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function function = functionManager.getFunctionContaining(getAddress(startAddressString));
|
||||
CodeBlockIterator iterator =
|
||||
|
|
|
@ -158,9 +158,9 @@ public class FunctionGraphPlugin2Test extends AbstractFunctionGraphTest {
|
|||
|
||||
undockSatellite();
|
||||
|
||||
FGController newController = cloneGraph();
|
||||
assertUndockedProviderShowing(newController.getProvider());
|
||||
isSatelliteVisible(newController);
|
||||
FGProvider newProvider = cloneGraph();
|
||||
assertUndockedProviderShowing(newProvider);
|
||||
isSatelliteVisible(newProvider);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -533,7 +533,8 @@ public class FcgProvider
|
|||
addLocalAction(resetGraphAction);
|
||||
|
||||
MultiStateDockingAction<LayoutProvider<FcgVertex, FcgEdge, FunctionCallGraph>> layoutAction =
|
||||
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
|
||||
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName(),
|
||||
KeyBindingType.SHARED) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
|
|
@ -29,7 +29,7 @@ import ghidra.feature.vt.api.main.*;
|
|||
import ghidra.feature.vt.api.markuptype.VTMarkupType;
|
||||
import ghidra.feature.vt.gui.plugin.VTController;
|
||||
import ghidra.feature.vt.gui.task.ApplyMarkupAtDestinationAddressTask;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Msg;
|
||||
|
@ -41,7 +41,7 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
|
|||
private Duo<ListingPanel> listingPanels;
|
||||
|
||||
private VTController controller;
|
||||
ListingCodeComparisonPanel dualListingPanel;
|
||||
ListingCodeComparisonView dualListingProvider;
|
||||
|
||||
// Drag-N-Drop
|
||||
private DragSource dragSource;
|
||||
|
@ -53,11 +53,11 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
|
|||
private DataFlavor[] acceptableFlavors; // data flavors that are valid.
|
||||
|
||||
public VTDualListingDragNDropHandler(VTController controller,
|
||||
ListingCodeComparisonPanel dualListingPanel) {
|
||||
ListingCodeComparisonView dualListingProvider) {
|
||||
this.controller = controller;
|
||||
this.dualListingPanel = dualListingPanel;
|
||||
ListingPanel leftPanel = dualListingPanel.getListingPanel(LEFT);
|
||||
ListingPanel rightPanel = dualListingPanel.getListingPanel(RIGHT);
|
||||
this.dualListingProvider = dualListingProvider;
|
||||
ListingPanel leftPanel = dualListingProvider.getListingPanel(LEFT);
|
||||
ListingPanel rightPanel = dualListingProvider.getListingPanel(RIGHT);
|
||||
listingPanels = new Duo<>(leftPanel, rightPanel);
|
||||
setUpDragDrop();
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
|
|||
ProgramLocation programLocation = listingPanels.get(LEFT).getProgramLocation(p);
|
||||
VTMarkupItem markupItem =
|
||||
controller.getCurrentMarkupForLocation(programLocation,
|
||||
dualListingPanel.getProgram(LEFT));
|
||||
dualListingProvider.getProgram(LEFT));
|
||||
if (markupItem == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
|
|||
|
||||
ProgramLocation programLocation = listingPanels.get(LEFT).getProgramLocation(p);
|
||||
VTMarkupItem markupItem = controller.getCurrentMarkupForLocation(programLocation,
|
||||
dualListingPanel.getProgram(LEFT));
|
||||
dualListingProvider.getProgram(LEFT));
|
||||
if (markupItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -151,16 +151,16 @@ public class VTDualListingDragNDropHandler implements Draggable, Droppable {
|
|||
ProgramLocation loc = listingPanels.get(RIGHT).getProgramLocation(p);
|
||||
|
||||
Address newDestinationAddress =
|
||||
markupType.getAddress(loc, dualListingPanel.getProgram(RIGHT));
|
||||
markupType.getAddress(loc, dualListingProvider.getProgram(RIGHT));
|
||||
if (newDestinationAddress == null) {
|
||||
Msg.showInfo(getClass(), dualListingPanel, "Invalid Drop Location",
|
||||
Msg.showInfo(getClass(), dualListingProvider, "Invalid Drop Location",
|
||||
markupType.getDisplayName() + " was not dropped at a valid location.");
|
||||
return;
|
||||
}
|
||||
if ((markupItem.getStatus() == VTMarkupItemStatus.SAME) &&
|
||||
(SystemUtilities.isEqual(markupItem.getDestinationAddress(), newDestinationAddress))) {
|
||||
// Dropped at expected address and already the same there.
|
||||
Msg.showInfo(getClass(), dualListingPanel, "Already The Same", markupType
|
||||
Msg.showInfo(getClass(), dualListingProvider, "Already The Same", markupType
|
||||
.getDisplayName() +
|
||||
" was dropped at its expected\ndestination where the value is already the same.");
|
||||
return;
|
||||
|
|
|
@ -18,16 +18,16 @@ package ghidra.feature.vt.gui.duallisting;
|
|||
import docking.ComponentProvider;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanelActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonViewActionContext;
|
||||
|
||||
/**
|
||||
* Action context for a version tracking listing.
|
||||
*/
|
||||
public class VTListingContext extends ListingActionContext
|
||||
implements CodeComparisonPanelActionContext {
|
||||
implements CodeComparisonViewActionContext {
|
||||
|
||||
private CodeComparisonPanel codeComparisonPanel = null;
|
||||
private CodeComparisonView codeComparisonView = null;
|
||||
|
||||
/**
|
||||
* Creates an action context for a VT listing.
|
||||
|
@ -40,15 +40,14 @@ public class VTListingContext extends ListingActionContext
|
|||
|
||||
/**
|
||||
* Sets the CodeComparisonPanel associated with this context.
|
||||
* @param codeComparisonPanel the code comparison panel.
|
||||
* @param codeComparisonView the code comparison panel.
|
||||
*/
|
||||
public void setCodeComparisonPanel(
|
||||
CodeComparisonPanel codeComparisonPanel) {
|
||||
this.codeComparisonPanel = codeComparisonPanel;
|
||||
public void setCodeComparisonPanel(CodeComparisonView codeComparisonView) {
|
||||
this.codeComparisonView = codeComparisonView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeComparisonPanel getCodeComparisonPanel() {
|
||||
return codeComparisonPanel;
|
||||
public CodeComparisonView getCodeComparisonView() {
|
||||
return codeComparisonView;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import javax.swing.Icon;
|
|||
import ghidra.app.nav.*;
|
||||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
@ -28,14 +27,11 @@ import ghidra.util.UniversalIdGenerator;
|
|||
|
||||
public class VTListingNavigator implements Navigatable {
|
||||
|
||||
private final ListingCodeComparisonPanel dualListingPanel;
|
||||
private final ListingPanel listingPanel;
|
||||
private long id;
|
||||
|
||||
public VTListingNavigator(ListingCodeComparisonPanel dualListingPanel,
|
||||
ListingPanel listingPanel) {
|
||||
public VTListingNavigator(ListingPanel listingPanel) {
|
||||
|
||||
this.dualListingPanel = dualListingPanel;
|
||||
this.listingPanel = listingPanel;
|
||||
id = UniversalIdGenerator.nextID().getValue();
|
||||
}
|
||||
|
|
|
@ -87,9 +87,9 @@ public class VTPlugin extends Plugin {
|
|||
|
||||
private VTController controller;
|
||||
|
||||
// common resources
|
||||
|
||||
// destination-side resources
|
||||
// plugins we have to add to our tool manually
|
||||
private Set<String> additionalPluginNames = new HashSet<>(Set.of(
|
||||
"ghidra.features.codecompare.plugin.FunctionComparisonPlugin"));
|
||||
|
||||
private VTMatchTableProvider matchesProvider;
|
||||
private VTMarkupItemsTableProvider markupProvider;
|
||||
|
@ -99,16 +99,15 @@ public class VTPlugin extends Plugin {
|
|||
|
||||
public VTPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
tool.setUnconfigurable();
|
||||
|
||||
OWNER = getName();
|
||||
controller = new VTControllerImpl(this);
|
||||
matchesProvider = new VTMatchTableProvider(controller);
|
||||
markupProvider = new VTMarkupItemsTableProvider(controller);
|
||||
impliedMatchesTable = new VTImpliedMatchesTableProvider(controller);
|
||||
functionAssociationProvider = new VTFunctionAssociationProvider(controller);
|
||||
registerServiceProvided(VTController.class, controller);
|
||||
|
||||
toolManager = new VTSubToolManager(this);
|
||||
createActions();
|
||||
registerServiceProvided(VTController.class, controller);
|
||||
tool.setUnconfigurable();
|
||||
|
||||
DockingActionIf saveAs = getToolAction("Save Tool As");
|
||||
tool.removeAction(saveAs);
|
||||
|
@ -116,11 +115,7 @@ public class VTPlugin extends Plugin {
|
|||
DockingActionIf export = getToolAction("Export Tool");
|
||||
tool.removeAction(export);
|
||||
|
||||
new MatchStatusUpdaterAssociationHook(controller);
|
||||
new ImpliedMatchAssociationHook(controller);
|
||||
|
||||
initializeOptions();
|
||||
|
||||
}
|
||||
|
||||
private DockingActionIf getToolAction(String actionName) {
|
||||
|
@ -145,9 +140,16 @@ public class VTPlugin extends Plugin {
|
|||
protected void init() {
|
||||
|
||||
removeUnwantedPlugins();
|
||||
|
||||
addCustomPlugins();
|
||||
|
||||
matchesProvider = new VTMatchTableProvider(controller);
|
||||
markupProvider = new VTMarkupItemsTableProvider(controller);
|
||||
impliedMatchesTable = new VTImpliedMatchesTableProvider(controller);
|
||||
functionAssociationProvider = new VTFunctionAssociationProvider(controller);
|
||||
|
||||
new MatchStatusUpdaterAssociationHook(controller);
|
||||
new ImpliedMatchAssociationHook(controller);
|
||||
|
||||
maybeShowHelp();
|
||||
}
|
||||
|
||||
|
@ -161,11 +163,11 @@ public class VTPlugin extends Plugin {
|
|||
|
||||
private void addCustomPlugins() {
|
||||
|
||||
List<String> names =
|
||||
new ArrayList<>(List.of("ghidra.features.codecompare.plugin.FunctionComparisonPlugin"));
|
||||
List<Plugin> plugins = tool.getManagedPlugins();
|
||||
Set<String> existingNames =
|
||||
plugins.stream().map(c -> c.getName()).collect(Collectors.toSet());
|
||||
Set<String> existingNames = new HashSet<>(
|
||||
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 need to do this, but once the tool has been saved with the plugins added,
|
||||
|
@ -173,7 +175,7 @@ public class VTPlugin extends Plugin {
|
|||
// 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) {
|
||||
for (String className : additionalPluginNames) {
|
||||
if (!existingNames.contains(className)) {
|
||||
tool.addPlugin(className);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import docking.widgets.label.GDLabel;
|
|||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.feature.vt.api.db.DeletedMatch;
|
||||
import ghidra.feature.vt.api.impl.VTEvent;
|
||||
|
@ -48,7 +49,7 @@ import ghidra.feature.vt.gui.actions.*;
|
|||
import ghidra.feature.vt.gui.duallisting.VTListingNavigator;
|
||||
import ghidra.feature.vt.gui.plugin.*;
|
||||
import ghidra.feature.vt.gui.util.MatchInfo;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -218,10 +219,10 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
@Override
|
||||
public List<DockingActionIf> getPopupActions(Tool t, ActionContext context) {
|
||||
if (context.getComponentProvider() == this) {
|
||||
ListingCodeComparisonPanel dualListingPanel =
|
||||
functionComparisonPanel.getDualListingPanel();
|
||||
if (dualListingPanel != null) {
|
||||
ListingPanel leftPanel = dualListingPanel.getListingPanel(LEFT);
|
||||
ListingCodeComparisonView dualListingProvider =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
if (dualListingProvider != null) {
|
||||
ListingPanel leftPanel = dualListingProvider.getListingPanel(LEFT);
|
||||
return leftPanel.getHeaderActions(getOwner());
|
||||
}
|
||||
}
|
||||
|
@ -247,22 +248,22 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
// Tool bar or function compare panel.
|
||||
if (isToolbarButtonAction || functionComparisonPanel.isAncestorOf(sourceComponent)) {
|
||||
|
||||
ListingCodeComparisonPanel dualListingPanel =
|
||||
functionComparisonPanel.getDualListingPanel();
|
||||
ListingCodeComparisonView dualListingProvider =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
boolean isShowingDualListing =
|
||||
(dualListingPanel != null) && dualListingPanel.isVisible();
|
||||
(dualListingProvider != null) && dualListingProvider.isVisible();
|
||||
boolean sourceIsADualFieldPanel =
|
||||
isShowingDualListing && dualListingPanel.isAncestorOf(sourceComponent) &&
|
||||
isShowingDualListing && dualListingProvider.isAncestorOf(sourceComponent) &&
|
||||
(sourceComponent instanceof FieldPanel);
|
||||
|
||||
ListingPanel listingPanel = null; // Default is don't create a function association listing context.
|
||||
// Is the action being taken on the dual listing?
|
||||
if (sourceIsADualFieldPanel) {
|
||||
listingPanel = dualListingPanel.getListingPanel((FieldPanel) sourceComponent);
|
||||
listingPanel = dualListingProvider.getListingPanel((FieldPanel) sourceComponent);
|
||||
}
|
||||
// Is the action being taken on a toolbar button while the dual listing is visible?
|
||||
else if (isToolbarButtonAction && isShowingDualListing) {
|
||||
listingPanel = dualListingPanel.getActiveListingPanel();
|
||||
listingPanel = dualListingProvider.getActiveListingPanel();
|
||||
}
|
||||
// If the dual listing is showing and this is a toolbar action or the action is
|
||||
// on one of the listings in the ListingCodeComparisonPanel
|
||||
|
@ -270,14 +271,13 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
// popup actions for the ListingDiff and also the function association actions
|
||||
// for the functions selected in the tables.
|
||||
if (listingPanel != null) {
|
||||
VTListingNavigator vtListingNavigator =
|
||||
new VTListingNavigator(dualListingPanel, listingPanel);
|
||||
VTListingNavigator vtListingNavigator = new VTListingNavigator(listingPanel);
|
||||
VTFunctionAssociationCompareContext vtListingContext =
|
||||
new VTFunctionAssociationCompareContext(this, vtListingNavigator, tool,
|
||||
sourceFunction, destinationFunction,
|
||||
getExistingMatch(sourceFunction, destinationFunction));
|
||||
vtListingContext.setCodeComparisonPanel(dualListingPanel);
|
||||
vtListingContext.setContextObject(dualListingPanel);
|
||||
vtListingContext.setCodeComparisonPanel(dualListingProvider);
|
||||
vtListingContext.setContextObject(dualListingProvider);
|
||||
vtListingContext.setSourceObject(source);
|
||||
return vtListingContext;
|
||||
}
|
||||
|
@ -334,6 +334,8 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
destinationFunctionsTable.dispose();
|
||||
destinationTableFilterPanel.dispose();
|
||||
|
||||
functionComparisonPanel.dispose();
|
||||
|
||||
tool.removePopupActionProvider(this);
|
||||
}
|
||||
|
||||
|
@ -368,9 +370,12 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
statusPanel.add(statusLabel, BorderLayout.CENTER);
|
||||
dualTablePanel.add(statusPanel, BorderLayout.SOUTH);
|
||||
|
||||
functionComparisonPanel = new FunctionComparisonPanel(tool, getOwner());
|
||||
// Note: this service should never be null, since it is added by the VTPlugin
|
||||
FunctionComparisonService fcService = tool.getService(FunctionComparisonService.class);
|
||||
functionComparisonPanel = fcService.createComparisonViewer();
|
||||
|
||||
addSpecificCodeComparisonActions();
|
||||
functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonPanel.NAME);
|
||||
functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonView.NAME);
|
||||
functionComparisonPanel.setTitlePrefixes("Source:", "Destination:");
|
||||
|
||||
comparisonSplitPane =
|
||||
|
@ -760,12 +765,9 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
|
|||
sourceFunctionsModel.setFilterSettings(filterSettings);
|
||||
destinationFunctionsModel.setFilterSettings(filterSettings);
|
||||
reload();
|
||||
functionComparisonPanel.readConfigState(getName(), saveState);
|
||||
}
|
||||
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
// save config state here
|
||||
functionComparisonPanel.writeConfigState(getName(), saveState);
|
||||
saveState.putEnum(FILTER_SETTINGS_KEY, filterSettings);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,17 +19,17 @@ import java.util.List;
|
|||
|
||||
import docking.DefaultActionContext;
|
||||
import ghidra.feature.vt.api.main.VTMarkupItem;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanelActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonViewActionContext;
|
||||
|
||||
/**
|
||||
* Action context for the version tracking markup item provider.
|
||||
*/
|
||||
public class VTMarkupItemContext extends DefaultActionContext
|
||||
implements CodeComparisonPanelActionContext {
|
||||
implements CodeComparisonViewActionContext {
|
||||
|
||||
private final List<VTMarkupItem> selectedItems;
|
||||
private CodeComparisonPanel codeComparisonPanel = null;
|
||||
private CodeComparisonView codeComparisonView;
|
||||
|
||||
/**
|
||||
* Creates an action context for the VT markup item provider.
|
||||
|
@ -50,16 +50,15 @@ public class VTMarkupItemContext extends DefaultActionContext
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the CodeComparisonPanel associated with this context.
|
||||
* @param codeComparisonPanel the code comparison panel.
|
||||
* Sets the comparison provider associated with this context.
|
||||
* @param codeComparisonView the code comparison view.
|
||||
*/
|
||||
public void setCodeComparisonPanel(
|
||||
CodeComparisonPanel codeComparisonPanel) {
|
||||
this.codeComparisonPanel = codeComparisonPanel;
|
||||
public void setCodeComparisonView(CodeComparisonView codeComparisonView) {
|
||||
this.codeComparisonView = codeComparisonView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeComparisonPanel getCodeComparisonPanel() {
|
||||
return codeComparisonPanel;
|
||||
public CodeComparisonView getCodeComparisonView() {
|
||||
return codeComparisonView;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import docking.widgets.table.GTable;
|
|||
import docking.widgets.table.RowObjectTableModel;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.listingpanel.ProgramLocationListener;
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
|
@ -51,8 +52,8 @@ import ghidra.feature.vt.gui.filters.Filter.FilterEditingStatus;
|
|||
import ghidra.feature.vt.gui.plugin.*;
|
||||
import ghidra.feature.vt.gui.provider.markuptable.VTMarkupItemsTableModel.AppliedDestinationAddressTableColumn;
|
||||
import ghidra.feature.vt.gui.util.*;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -154,28 +155,33 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
markupItemsTablePanel.add(tablePanel, BorderLayout.CENTER);
|
||||
markupItemsTablePanel.add(filterAreaPanel, BorderLayout.SOUTH);
|
||||
|
||||
functionComparisonPanel = new FunctionComparisonPanel(tool, getOwner());
|
||||
// Note: this service should never be null, since it is added by the VTPlugin
|
||||
FunctionComparisonService fcService = tool.getService(FunctionComparisonService.class);
|
||||
functionComparisonPanel = fcService.createComparisonViewer();
|
||||
|
||||
addSpecificCodeComparisonActions();
|
||||
functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonPanel.NAME);
|
||||
functionComparisonPanel.setCurrentTabbedComponent(ListingCodeComparisonView.NAME);
|
||||
functionComparisonPanel.getAccessibleContext().setAccessibleName("Function Comparison");
|
||||
functionComparisonPanel.setTitlePrefixes("Source:", "Destination:");
|
||||
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
|
||||
if (dualListingPanel != null) {
|
||||
ListingCodeComparisonView dualListingProvider =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
if (dualListingProvider != null) {
|
||||
|
||||
dualListingPanel.getListingPanel(LEFT)
|
||||
dualListingProvider.getListingPanel(LEFT)
|
||||
.setProgramLocationListener(new SourceProgramLocationListener());
|
||||
dualListingPanel.getListingPanel(RIGHT)
|
||||
dualListingProvider.getListingPanel(RIGHT)
|
||||
.setProgramLocationListener(
|
||||
new DestinationProgramLocationListener());
|
||||
|
||||
sourceHighlightProvider = new VTDualListingHighlightProvider(controller, true);
|
||||
destinationHighlightProvider = new VTDualListingHighlightProvider(controller, false);
|
||||
dualListingPanel.addHighlightProviders(sourceHighlightProvider,
|
||||
dualListingProvider.addHighlightProviders(sourceHighlightProvider,
|
||||
destinationHighlightProvider);
|
||||
sourceHighlightProvider.setListingPanel(dualListingPanel.getListingPanel(LEFT));
|
||||
destinationHighlightProvider.setListingPanel(dualListingPanel.getListingPanel(RIGHT));
|
||||
sourceHighlightProvider.setListingPanel(dualListingProvider.getListingPanel(LEFT));
|
||||
destinationHighlightProvider
|
||||
.setListingPanel(dualListingProvider.getListingPanel(RIGHT));
|
||||
|
||||
new VTDualListingDragNDropHandler(controller, dualListingPanel);
|
||||
new VTDualListingDragNDropHandler(controller, dualListingProvider);
|
||||
}
|
||||
|
||||
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, markupItemsTablePanel,
|
||||
|
@ -250,8 +256,8 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
// the same destination address.
|
||||
processingMarkupItemSelected = true;
|
||||
|
||||
ListingCodeComparisonPanel dualListingPanel =
|
||||
functionComparisonPanel.getDualListingPanel();
|
||||
ListingCodeComparisonView dualListingPanel =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
VTMarkupItem markupItem = null;
|
||||
if (table.getSelectedRowCount() == 1) {
|
||||
// we get out the model here in case it has been wrapped by one of the filters
|
||||
|
@ -358,7 +364,8 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
* Otherwise, hide it.
|
||||
*/
|
||||
private void showComparisonPanelWithinProvider(boolean show) {
|
||||
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
|
||||
ListingCodeComparisonView dualListingProvider =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
boolean contains = markupPanel.isAncestorOf(splitPane);
|
||||
if (show) {
|
||||
if (!contains) {
|
||||
|
@ -369,10 +376,10 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
splitPane.add(markupItemsTablePanel);
|
||||
splitPane.add(functionComparisonPanel);
|
||||
markupPanel.add(splitPane, BorderLayout.CENTER);
|
||||
if (dualListingPanel != null) {
|
||||
dualListingPanel.getListingPanel(LEFT)
|
||||
if (dualListingProvider != null) {
|
||||
dualListingProvider.getListingPanel(LEFT)
|
||||
.setProgramLocationListener(new SourceProgramLocationListener());
|
||||
dualListingPanel.getListingPanel(LEFT)
|
||||
dualListingProvider.getListingPanel(LEFT)
|
||||
.setProgramLocationListener(new DestinationProgramLocationListener());
|
||||
}
|
||||
|
||||
|
@ -386,9 +393,9 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
else {
|
||||
if (contains) {
|
||||
// Remove the split pane.
|
||||
if (dualListingPanel != null) {
|
||||
dualListingPanel.getListingPanel(LEFT).setProgramLocationListener(null);
|
||||
dualListingPanel.getListingPanel(RIGHT).setProgramLocationListener(null);
|
||||
if (dualListingProvider != null) {
|
||||
dualListingProvider.getListingPanel(LEFT).setProgramLocationListener(null);
|
||||
dualListingProvider.getListingPanel(RIGHT).setProgramLocationListener(null);
|
||||
}
|
||||
markupPanel.remove(splitPane);
|
||||
splitPane.remove(functionComparisonPanel);
|
||||
|
@ -471,9 +478,10 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public List<DockingActionIf> getPopupActions(Tool t, ActionContext context) {
|
||||
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
|
||||
if (context.getComponentProvider() == this && dualListingPanel != null) {
|
||||
ListingPanel sourcePanel = dualListingPanel.getListingPanel(LEFT);
|
||||
ListingCodeComparisonView dualListingProvider =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
if (context.getComponentProvider() == this && dualListingProvider != null) {
|
||||
ListingPanel sourcePanel = dualListingProvider.getListingPanel(LEFT);
|
||||
return sourcePanel.getHeaderActions(getOwner());
|
||||
}
|
||||
return new ArrayList<>();
|
||||
|
@ -483,34 +491,35 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
public ActionContext getActionContext(MouseEvent event) {
|
||||
Object source = (event != null) ? event.getSource() : null;
|
||||
Component sourceComponent = (source instanceof Component) ? (Component) source : null;
|
||||
|
||||
// If action is on the markup table, return a markup item context for markup popup actions.
|
||||
if (event == null || tablePanel.isAncestorOf(sourceComponent)) {
|
||||
List<VTMarkupItem> selectedItems = getSelectedMarkupItems();
|
||||
VTMarkupItemContext vtMarkupItemContext = new VTMarkupItemContext(this, selectedItems);
|
||||
if (functionComparisonPanel.isVisible()) {
|
||||
CodeComparisonPanel displayedPanel =
|
||||
functionComparisonPanel.getDisplayedPanel();
|
||||
vtMarkupItemContext.setCodeComparisonPanel(displayedPanel);
|
||||
CodeComparisonView displayedProvider =
|
||||
functionComparisonPanel.getDisplayedView();
|
||||
vtMarkupItemContext.setCodeComparisonView(displayedProvider);
|
||||
}
|
||||
return vtMarkupItemContext;
|
||||
}
|
||||
|
||||
// Is the action being taken on the dual listing.
|
||||
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
|
||||
if (dualListingPanel != null && dualListingPanel.isAncestorOf(sourceComponent)) {
|
||||
// If the action is on one of the listings in the ListingCodeComparisonPanel
|
||||
ListingCodeComparisonView listingView = functionComparisonPanel.getDualListingView();
|
||||
if (listingView != null && listingView.isAncestorOf(sourceComponent)) {
|
||||
// If the action is on one of the listings in the Listing view
|
||||
// then return a special version tracking listing context. This will allow
|
||||
// popup actions for the ListingDiff and also the markup item actions for the
|
||||
// current markup item.
|
||||
if (sourceComponent instanceof FieldPanel) {
|
||||
ListingPanel listingPanel =
|
||||
dualListingPanel.getListingPanel((FieldPanel) sourceComponent);
|
||||
listingView.getListingPanel((FieldPanel) sourceComponent);
|
||||
if (listingPanel != null) {
|
||||
VTListingNavigator vtListingNavigator =
|
||||
new VTListingNavigator(dualListingPanel, listingPanel);
|
||||
VTListingNavigator vtListingNavigator = new VTListingNavigator(listingPanel);
|
||||
VTListingContext vtListingContext =
|
||||
new VTListingContext(this, vtListingNavigator);
|
||||
vtListingContext.setCodeComparisonPanel(dualListingPanel);
|
||||
vtListingContext.setContextObject(dualListingPanel);
|
||||
vtListingContext.setCodeComparisonPanel(listingView);
|
||||
vtListingContext.setContextObject(listingView);
|
||||
vtListingContext.setSourceObject(source);
|
||||
return vtListingContext;
|
||||
}
|
||||
|
@ -542,6 +551,8 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
return;
|
||||
}
|
||||
|
||||
functionComparisonPanel.dispose();
|
||||
|
||||
// must remove the listener first to avoid callback whilst we are disposing
|
||||
ListSelectionModel selectionModel = markupItemsTable.getSelectionModel();
|
||||
selectionModel.removeListSelectionListener(markupItemSelectionListener);
|
||||
|
@ -564,9 +575,10 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
private void refresh() {
|
||||
markupItemsTableModel.reload(false);
|
||||
markupItemsTable.repaint();
|
||||
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
|
||||
if (dualListingPanel != null) {
|
||||
dualListingPanel.updateListings();
|
||||
ListingCodeComparisonView dualListingProvider =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
if (dualListingProvider != null) {
|
||||
dualListingProvider.updateListings();
|
||||
}
|
||||
sourceHighlightProvider.updateMarkup();
|
||||
destinationHighlightProvider.updateMarkup();
|
||||
|
@ -773,11 +785,12 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
* @return true if the dual listing is showing
|
||||
*/
|
||||
public boolean isDualListingShowing() {
|
||||
ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel();
|
||||
if (dualListingPanel == null) {
|
||||
ListingCodeComparisonView dualListingProvider =
|
||||
functionComparisonPanel.getDualListingView();
|
||||
if (dualListingProvider == null) {
|
||||
return false;
|
||||
}
|
||||
return dualListingPanel.isShowing();
|
||||
return dualListingProvider.isShowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -836,7 +849,6 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
* @param saveState the configuration state to restore
|
||||
*/
|
||||
public void readConfigState(SaveState saveState) {
|
||||
functionComparisonPanel.readConfigState(getName(), saveState);
|
||||
showComparisonPanelWithinProvider(saveState.getBoolean(SHOW_COMPARISON_PANEL, true));
|
||||
|
||||
for (Filter<VTMarkupItem> filter : filters) {
|
||||
|
@ -881,8 +893,6 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
* @param saveState the new configuration state
|
||||
*/
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
// save config state here
|
||||
functionComparisonPanel.writeConfigState(getName(), saveState);
|
||||
saveState.putBoolean(SHOW_COMPARISON_PANEL, functionComparisonPanel.isShowing());
|
||||
|
||||
for (Filter<VTMarkupItem> filter : filters) {
|
||||
|
@ -947,6 +957,15 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
refilter(); // this will do nothing if we are frozen
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function comparison panel component that possibly contains multiple different views
|
||||
* for comparing code such as a dual listing.
|
||||
* @return the function comparison panel
|
||||
*/
|
||||
public FunctionComparisonPanel getFunctionComparisonPanel() {
|
||||
return functionComparisonPanel;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
@ -1010,13 +1029,4 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function comparison panel component that possibly contains multiple different views
|
||||
* for comparing code such as a dual listing.
|
||||
* @return the function comparison panel
|
||||
*/
|
||||
public FunctionComparisonPanel getFunctionComparisonPanel() {
|
||||
return functionComparisonPanel;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ import ghidra.feature.vt.gui.provider.onetomany.VTMatchSourceTableProvider;
|
|||
import ghidra.feature.vt.gui.task.*;
|
||||
import ghidra.feature.vt.gui.util.MatchInfo;
|
||||
import ghidra.feature.vt.gui.wizard.add.*;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.datatree.DataTree;
|
||||
import ghidra.framework.main.datatree.ProjectDataTreePanel;
|
||||
|
@ -765,7 +765,7 @@ public class VersionTrackingPluginScreenShots extends GhidraScreenShotGenerator
|
|||
|
||||
JComponent component = provider.getComponent();
|
||||
Component listingComponent =
|
||||
findComponentByName(component, ListingCodeComparisonPanel.NAME);
|
||||
findComponentByName(component, ListingCodeComparisonView.NAME);
|
||||
if (listingComponent == null) {
|
||||
return false; // not in the parent's hierarchy
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ public class VTImpliedMatchCorrelatorTest extends AbstractVTCorrelatorTest {
|
|||
// get the resulting implied matches and verify that none of the matches that were already
|
||||
// created
|
||||
VTMatchSet impliedMatchSet = getVTMatchSet("Implied Match");
|
||||
Assert.assertNotEquals("vtMatchSet does not exist", null, impliedMatchSet);
|
||||
assertNotNull(impliedMatchSet);
|
||||
|
||||
// Now test that only the expected items are in this set for the given function we just
|
||||
// applied
|
||||
|
@ -276,9 +276,7 @@ public class VTImpliedMatchCorrelatorTest extends AbstractVTCorrelatorTest {
|
|||
protected VTMatch getMatch(VTMatchSet matches, Address sourceAddress,
|
||||
Address destinationAddress) {
|
||||
|
||||
Iterator<VTMatch> it = matches.getMatches().iterator();
|
||||
while (it.hasNext()) {
|
||||
VTMatch match = it.next();
|
||||
for (VTMatch match : matches.getMatches()) {
|
||||
if (match.getSourceAddress().equals(sourceAddress) &&
|
||||
match.getDestinationAddress().equals(destinationAddress)) {
|
||||
return match;
|
||||
|
|
|
@ -33,9 +33,9 @@ import ghidra.feature.vt.api.db.VTSessionDB;
|
|||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.feature.vt.api.util.VTOptions;
|
||||
import ghidra.feature.vt.gui.plugin.*;
|
||||
import ghidra.feature.vt.gui.provider.markuptable.VTMarkupItemsTableProvider;
|
||||
import ghidra.feature.vt.gui.provider.matchtable.VTMatchTableModel;
|
||||
import ghidra.feature.vt.gui.provider.matchtable.VTMatchTableProvider;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
|
@ -56,9 +56,10 @@ public class VTTestEnv extends TestEnv {
|
|||
|
||||
public VTTestEnv() throws Exception {
|
||||
|
||||
PluginTool tool = getTool();
|
||||
tool.removePlugins(new Plugin[] { getPlugin(ProgramManagerPlugin.class) });
|
||||
tool.addPlugin(VTPlugin.class.getName());
|
||||
PluginTool pluignTool = getTool();
|
||||
pluignTool.removePlugins(List.of(getPlugin(ProgramManagerPlugin.class)));
|
||||
pluignTool.addPlugin(VTPlugin.class.getName());
|
||||
|
||||
plugin = getPlugin(VTPlugin.class);
|
||||
controller = (VTController) getInstanceField("controller", plugin);
|
||||
matchTableProvider = (VTMatchTableProvider) getInstanceField("matchesProvider", plugin);
|
||||
|
@ -238,7 +239,19 @@ public class VTTestEnv extends TestEnv {
|
|||
}
|
||||
|
||||
public void focusMatchTable() {
|
||||
runSwing(() -> matchTableProvider.getComponent().requestFocus());
|
||||
runSwing(() -> matchTableProvider.requestFocus());
|
||||
}
|
||||
|
||||
public VTMarkupItemsTableProvider getMarkupItemsProvider() {
|
||||
return (VTMarkupItemsTableProvider) getInstanceField("markupProvider", plugin);
|
||||
}
|
||||
|
||||
public void focusMarkupItemsTable() {
|
||||
VTMarkupItemsTableProvider markupProvider = getMarkupItemsProvider();
|
||||
runSwing(() -> {
|
||||
markupProvider.toFront();
|
||||
markupProvider.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
public void triggerMatchTableDataChanged() {
|
||||
|
@ -250,4 +263,5 @@ public class VTTestEnv extends TestEnv {
|
|||
public VTMatchTableProvider getMatchTableProvider() {
|
||||
return matchTableProvider;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,7 +67,18 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
|||
* @param owner the owner
|
||||
*/
|
||||
public MultiStateDockingAction(String name, String owner) {
|
||||
super(name, owner);
|
||||
this(name, owner, KeyBindingType.INDIVIDUAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param name the action name
|
||||
* @param owner the owner
|
||||
* @param type the key binding type
|
||||
*/
|
||||
public MultiStateDockingAction(String name, String owner, KeyBindingType type) {
|
||||
super(name, owner, type);
|
||||
multiActionGenerator = context -> getStateActions();
|
||||
|
||||
// set this here so we don't have to check for null elsewhere
|
||||
|
|
|
@ -88,6 +88,10 @@ public class ObjectChooserDialog<T> extends DialogComponentProvider {
|
|||
return selectedObject;
|
||||
}
|
||||
|
||||
public void setSelectedObject(T t) {
|
||||
table.selectRowObject(t);
|
||||
}
|
||||
|
||||
public void setFilterText(String text) {
|
||||
table.setFilterText(text);
|
||||
}
|
||||
|
|
|
@ -21,11 +21,10 @@ import docking.widgets.fieldpanel.FieldPanel;
|
|||
import docking.widgets.fieldpanel.listener.ViewListener;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
|
||||
|
||||
/**
|
||||
* Coordinates the scrolling of a set of field panels by sharing bound scroll models.
|
||||
*/
|
||||
public class FieldPanelCoordinator implements ViewListener {
|
||||
public class FieldPanelScrollCoordinator implements ViewListener {
|
||||
FieldPanel[] panels;
|
||||
boolean valuesChanging;
|
||||
|
||||
|
@ -33,19 +32,28 @@ public class FieldPanelCoordinator implements ViewListener {
|
|||
* Constructs a new FieldPanelCoordinatro to synchronize the scrolling of the given field panels.
|
||||
* @param panels the array of panels to synchronize.
|
||||
*/
|
||||
public FieldPanelCoordinator(FieldPanel[] panels) {
|
||||
public FieldPanelScrollCoordinator(FieldPanel[] panels) {
|
||||
this.panels = new FieldPanel[panels.length];
|
||||
System.arraycopy(panels, 0, this.panels, 0, panels.length);
|
||||
for(int i=0;i<panels.length;i++) {
|
||||
addListeners(panels[i]);
|
||||
for (FieldPanel panel : panels) {
|
||||
addListeners(panel);
|
||||
}
|
||||
}
|
||||
|
||||
protected FieldPanel getOtherPanel(FieldPanel fp) {
|
||||
|
||||
if (panels[0] == fp) {
|
||||
return panels[1];
|
||||
}
|
||||
return panels[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up resources.
|
||||
*/
|
||||
public void dispose() {
|
||||
for(int i=0;i<panels.length;i++) {
|
||||
removeListeners(panels[i]);
|
||||
for (FieldPanel panel : panels) {
|
||||
removeListeners(panel);
|
||||
}
|
||||
panels = null;
|
||||
}
|
||||
|
@ -71,9 +79,9 @@ public class FieldPanelCoordinator implements ViewListener {
|
|||
removeListeners(fp);
|
||||
FieldPanel[] newPanels = new FieldPanel[panels.length - 1];
|
||||
int j = 0;
|
||||
for(int i=0;i<panels.length;i++) {
|
||||
if (panels[i] != fp) {
|
||||
newPanels[j++] = panels[i];
|
||||
for (FieldPanel panel : panels) {
|
||||
if (panel != fp) {
|
||||
newPanels[j++] = panel;
|
||||
}
|
||||
}
|
||||
panels = newPanels;
|
||||
|
@ -81,15 +89,18 @@ public class FieldPanelCoordinator implements ViewListener {
|
|||
|
||||
@Override
|
||||
public void viewChanged(FieldPanel fp, BigInteger index, int xPos, int yPos) {
|
||||
if (valuesChanging) return;
|
||||
if (valuesChanging) {
|
||||
return;
|
||||
}
|
||||
valuesChanging = true;
|
||||
try {
|
||||
for(int i=0;i<panels.length;i++) {
|
||||
if (panels[i] != fp) {
|
||||
panels[i].setViewerPosition(index, xPos, yPos);
|
||||
for (FieldPanel panel : panels) {
|
||||
if (panel != fp) {
|
||||
panel.setViewerPosition(index, xPos, yPos);
|
||||
}
|
||||
}
|
||||
}finally {
|
||||
}
|
||||
finally {
|
||||
valuesChanging = false;
|
||||
}
|
||||
}
|
||||
|
@ -97,12 +108,11 @@ public class FieldPanelCoordinator implements ViewListener {
|
|||
private void addListeners(FieldPanel fp) {
|
||||
fp.addViewListener(this);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void removeListeners(FieldPanel fp) {
|
||||
fp.removeViewListener(this);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,197 +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 docking.widgets.fieldpanel.internal;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import docking.widgets.fieldpanel.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* A LayoutLockedFieldPanelCoordinator is an extension of a LineLockedFieldPanelCoordinator that
|
||||
* handles the fact that field panel layouts vary in size. It coordinates the scrolling of a set
|
||||
* of field panels by sharing bound scroll models that are locked together by a set of index
|
||||
* numbers for the FieldPanel Layouts. All the field panels are locked together at the index
|
||||
* numbers specified in the locked line array.
|
||||
* In other words this coordinator tries to keep the layout indicated by the line (or index)
|
||||
* for each field panel side by side with the indicated layout for each other field panel.
|
||||
* <br>Note: The layouts that are locked together will be positioned so that the bottom of those
|
||||
* layouts line up within the field panels.
|
||||
*/
|
||||
public class LayoutLockedFieldPanelCoordinator extends LineLockedFieldPanelCoordinator {
|
||||
|
||||
/**
|
||||
* Constructor for the coordinator.
|
||||
* @param panels the field panels that will have their positions coordinated with each other.
|
||||
*/
|
||||
public LayoutLockedFieldPanelCoordinator(FieldPanel... panels) {
|
||||
super(panels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void viewChanged(FieldPanel fp, BigInteger index, int xPos, int yPos) {
|
||||
if (valuesChanging) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
valuesChanging = true;
|
||||
// "lockedLineIndex" is the IndexMap index indicating where this field panel
|
||||
// is locked to the other when scrolling.
|
||||
BigInteger lockedLineIndex1 = getLockedLineForPanel(fp);
|
||||
if (lockedLineIndex1 == null) { // This shouldn't happen.
|
||||
throw new AssertException("Couldn't find line number for indicated field panel." +
|
||||
" FieldPanel is not one of those being managed by this coordinator.");
|
||||
}
|
||||
|
||||
// "topIndex" is the IndexMap index of the top of the listing in view.
|
||||
BigInteger topIndex1 = index;
|
||||
LayoutModel layoutModel1 = fp.getLayoutModel();
|
||||
Layout lockedLineLayout1 = layoutModel1.getLayout(lockedLineIndex1);
|
||||
if (lockedLineLayout1 == null) {
|
||||
return; // transitioning from one function to another.
|
||||
}
|
||||
|
||||
// "lockedLineHeight" is the height of the layout in this field panel
|
||||
// where it is locked to the other panel when scrolling.
|
||||
int lockedLineHeight1 = lockedLineLayout1.getHeight();
|
||||
|
||||
// numIndexes is the total number of indexes in this field panels indexMap.
|
||||
BigInteger numIndexes1 = layoutModel1.getNumIndexes();
|
||||
Layout firstLayout1 = layoutModel1.getLayout(topIndex1);
|
||||
|
||||
// "yPos" is a negative number indicating the number of pixels the start of the current
|
||||
// layout is above the top of the field panel view.
|
||||
|
||||
// "remainingHeight" is the number of pixels vertically from the first visible pixel
|
||||
// in the layout at the top of the listing view to the end of that layout.
|
||||
int remainingHeight = firstLayout1.getHeight() + yPos;
|
||||
|
||||
// "offsetInLayout" is the number of pixels that the top of the listing view is below
|
||||
// the start of the current layout.
|
||||
int offsetInLayout1 = 0;
|
||||
int offsetFromLockedIndex1 = 0;
|
||||
if (lockedLineIndex1.compareTo(topIndex1) == 0) {
|
||||
offsetInLayout1 -= yPos;
|
||||
offsetFromLockedIndex1 += offsetInLayout1;
|
||||
}
|
||||
else if (lockedLineIndex1.compareTo(topIndex1) < 0) {
|
||||
BigInteger currentIndex1 = lockedLineIndex1;
|
||||
while (currentIndex1 != null && (currentIndex1.compareTo(numIndexes1) < 0) &&
|
||||
currentIndex1.compareTo(topIndex1) < 0) {
|
||||
Layout currentLayout = layoutModel1.getLayout(currentIndex1);
|
||||
if (currentLayout != null) {
|
||||
offsetFromLockedIndex1 += currentLayout.getHeight();
|
||||
}
|
||||
currentIndex1 = layoutModel1.getIndexAfter(currentIndex1);
|
||||
}
|
||||
offsetFromLockedIndex1 -= yPos;
|
||||
}
|
||||
else { // lockedlineIndex1 > topIndex1
|
||||
BigInteger currentIndex1 = layoutModel1.getIndexAfter(topIndex1);
|
||||
while (currentIndex1 != null && (currentIndex1.compareTo(numIndexes1) < 0) &&
|
||||
currentIndex1.compareTo(lockedLineIndex1) < 0) {
|
||||
Layout currentLayout = layoutModel1.getLayout(currentIndex1);
|
||||
if (currentLayout != null) {
|
||||
offsetFromLockedIndex1 -= currentLayout.getHeight();
|
||||
}
|
||||
currentIndex1 = layoutModel1.getIndexAfter(currentIndex1);
|
||||
}
|
||||
offsetFromLockedIndex1 -= remainingHeight;
|
||||
}
|
||||
|
||||
// Position the views for the other panels to match the changed one the best they can.
|
||||
for (int i = 0; i < panels.length; i++) {
|
||||
if (panels[i] != fp) {
|
||||
// Get the difference in height between our two line locked layouts.
|
||||
LayoutModel layoutModel2 = panels[i].getLayoutModel();
|
||||
BigInteger numIndexes2 = layoutModel2.getNumIndexes();
|
||||
BigInteger lockedLineIndex2 = lockedLineNumbers[i];
|
||||
Layout lockedLineLayout2 = layoutModel2.getLayout(lockedLineIndex2);
|
||||
if (lockedLineLayout2 == null) {
|
||||
return; // Initializing panels.
|
||||
}
|
||||
int lockedLineHeight2 = lockedLineLayout2.getHeight();
|
||||
|
||||
// Handle when the locked line's layout is at the top of the viewer.
|
||||
if (lockedLineIndex1.equals(topIndex1)) {
|
||||
int difference = lockedLineHeight1 - lockedLineHeight2; // positive means layout1 is larger.
|
||||
int yPos2 = yPos + difference;
|
||||
// A negative yPos indicates the number of pixels to move the layout
|
||||
// above the top of the view.
|
||||
panels[i].setViewerPosition(lockedLineIndex2, xPos, yPos2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start with the layout of the line locked index and position the top of the
|
||||
// view at the same distance from it as the other view is from the layout for
|
||||
// its locked line.
|
||||
int offsetFromLockedIndex2 =
|
||||
offsetFromLockedIndex1 + (lockedLineHeight2 - lockedLineHeight1);
|
||||
int remainingOffset2 = offsetFromLockedIndex2;
|
||||
BigInteger currentIndex2 = lockedLineIndex2;
|
||||
if (remainingOffset2 < 0) {
|
||||
currentIndex2 = layoutModel2.getIndexBefore(currentIndex2);
|
||||
}
|
||||
while (currentIndex2 != null && currentIndex2.compareTo(BigInteger.ZERO) >= 0 &&
|
||||
currentIndex2.compareTo(numIndexes2) < 0) {
|
||||
Layout currentLayout2 = layoutModel2.getLayout(currentIndex2);
|
||||
// Gaps in the code will cause the currentIndex to be the last byte's
|
||||
// index before the gap. This results in a null layout for that index,
|
||||
// so we need to go again to get past it.
|
||||
if (currentLayout2 == null) {
|
||||
if (remainingOffset2 < 0) {
|
||||
// Go again when processing layout heights in reverse direction.
|
||||
currentIndex2 = layoutModel2.getIndexBefore(currentIndex2);
|
||||
continue;
|
||||
}
|
||||
else if (remainingOffset2 > 0) {
|
||||
// Go again when processing layout heights in forward direction.
|
||||
currentIndex2 = layoutModel2.getIndexAfter(currentIndex2);
|
||||
continue;
|
||||
}
|
||||
return; // currentLayout2 is null.
|
||||
}
|
||||
int height = currentLayout2.getHeight();
|
||||
if (remainingOffset2 == 0) {
|
||||
panels[i].setViewerPosition(currentIndex2, xPos, 0);
|
||||
return;
|
||||
}
|
||||
else if (remainingOffset2 < 0) {
|
||||
int offset = height + remainingOffset2;
|
||||
if (offset >= 0) {
|
||||
panels[i].setViewerPosition(currentIndex2, xPos, -offset);
|
||||
return;
|
||||
}
|
||||
currentIndex2 = layoutModel2.getIndexBefore(currentIndex2);
|
||||
remainingOffset2 = offset;
|
||||
}
|
||||
else { // remainingOffset2 > 0
|
||||
if (remainingOffset2 < height) {
|
||||
panels[i].setViewerPosition(currentIndex2, xPos, -remainingOffset2);
|
||||
return;
|
||||
}
|
||||
currentIndex2 = layoutModel2.getIndexAfter(currentIndex2);
|
||||
remainingOffset2 -= height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
valuesChanging = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
/* ###
|
||||
* 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 docking.widgets.fieldpanel.internal;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import docking.widgets.fieldpanel.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* A LayoutLockedFieldPanelCoordinator is an extension of a LineLockedFieldPanelCoordinator that
|
||||
* handles the fact that field panel layouts vary in size. It coordinates the scrolling of a set
|
||||
* of field panels by sharing bound scroll models that are locked together by a set of index
|
||||
* numbers for the FieldPanel Layouts. All the field panels are locked together at the index
|
||||
* numbers specified in the locked line array.
|
||||
* In other words this coordinator tries to keep the layout indicated by the line (or index)
|
||||
* for each field panel side by side with the indicated layout for each other field panel.
|
||||
* <br>Note: The layouts that are locked together will be positioned so that the bottom of those
|
||||
* layouts line up within the field panels.
|
||||
*/
|
||||
public class LayoutLockedFieldPanelScrollCoordinator extends LineLockedFieldPanelScrollCoordinator {
|
||||
|
||||
/**
|
||||
* Constructor for the coordinator.
|
||||
* @param panels the field panels that will have their positions coordinated with each other.
|
||||
*/
|
||||
public LayoutLockedFieldPanelScrollCoordinator(FieldPanel... panels) {
|
||||
super(panels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void viewChanged(FieldPanel fp, BigInteger index, int xPos, int yPos) {
|
||||
if (valuesChanging) {
|
||||
return;
|
||||
}
|
||||
|
||||
valuesChanging = true;
|
||||
try {
|
||||
update(fp, index, xPos, yPos);
|
||||
}
|
||||
finally {
|
||||
valuesChanging = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void update(FieldPanel fp, BigInteger index, int xPos, int yPos) {
|
||||
|
||||
// "lockedLineIndex" is the IndexMap index indicating where this field panel
|
||||
// is locked to the other when scrolling.
|
||||
BigInteger lockedLineIndex1 = getLockedLineForPanel(fp);
|
||||
if (lockedLineIndex1 == null) { // This shouldn't happen.
|
||||
throw new AssertException("Couldn't find line number for indicated field panel." +
|
||||
" FieldPanel is not one of those being managed by this coordinator.");
|
||||
}
|
||||
|
||||
// "topIndex" is the IndexMap index of the top of the listing in view.
|
||||
BigInteger topIndex1 = index;
|
||||
LayoutModel layoutModel1 = fp.getLayoutModel();
|
||||
Layout lockedLineLayout1 = layoutModel1.getLayout(lockedLineIndex1);
|
||||
if (lockedLineLayout1 == null) {
|
||||
return; // transitioning from one function to another.
|
||||
}
|
||||
|
||||
// "lockedLineHeight" is the height of the layout in this field panel
|
||||
// where it is locked to the other panel when scrolling.
|
||||
int lockedLineHeight1 = lockedLineLayout1.getHeight();
|
||||
|
||||
int offsetFromLockedIndex1 =
|
||||
getOffsetFromLockedIndex(layoutModel1, topIndex1, lockedLineIndex1, yPos);
|
||||
|
||||
// Position the views for the other panels to match the changed one the best they can.
|
||||
FieldPanel otherPanel = getOtherPanel(fp);
|
||||
|
||||
// Get the difference in height between our two line locked layouts.
|
||||
LayoutModel layoutModel2 = otherPanel.getLayoutModel();
|
||||
BigInteger numIndexes2 = layoutModel2.getNumIndexes();
|
||||
BigInteger lockedLineIndex2 = getLockedLineNumber(otherPanel);
|
||||
Layout lockedLineLayout2 = layoutModel2.getLayout(lockedLineIndex2);
|
||||
if (lockedLineLayout2 == null) {
|
||||
return; // Initializing panels.
|
||||
}
|
||||
int lockedLineHeight2 = lockedLineLayout2.getHeight();
|
||||
|
||||
// Handle when the locked line's layout is at the top of the viewer.
|
||||
if (lockedLineIndex1.equals(topIndex1)) {
|
||||
int difference = lockedLineHeight1 - lockedLineHeight2; // positive means layout1 is larger.
|
||||
int yPos2 = yPos + difference;
|
||||
// A negative yPos indicates the number of pixels to move the layout
|
||||
// above the top of the view.
|
||||
otherPanel.setViewerPosition(lockedLineIndex2, xPos, yPos2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start with the layout of the line locked index and position the top of the
|
||||
// view at the same distance from it as the other view is from the layout for
|
||||
// its locked line.
|
||||
int offsetFromLockedIndex2 =
|
||||
offsetFromLockedIndex1 + (lockedLineHeight2 - lockedLineHeight1);
|
||||
int remainingOffset2 = offsetFromLockedIndex2;
|
||||
BigInteger currentIndex2 = lockedLineIndex2;
|
||||
if (remainingOffset2 < 0) {
|
||||
currentIndex2 = layoutModel2.getIndexBefore(currentIndex2);
|
||||
}
|
||||
while (currentIndex2 != null && currentIndex2.compareTo(BigInteger.ZERO) >= 0 &&
|
||||
currentIndex2.compareTo(numIndexes2) < 0) {
|
||||
Layout currentLayout2 = layoutModel2.getLayout(currentIndex2);
|
||||
// Gaps in the code will cause the currentIndex to be the last byte's
|
||||
// index before the gap. This results in a null layout for that index,
|
||||
// so we need to go again to get past it.
|
||||
if (currentLayout2 == null) {
|
||||
if (remainingOffset2 < 0) {
|
||||
// Go again when processing layout heights in reverse direction.
|
||||
currentIndex2 = layoutModel2.getIndexBefore(currentIndex2);
|
||||
continue;
|
||||
}
|
||||
else if (remainingOffset2 > 0) {
|
||||
// Go again when processing layout heights in forward direction.
|
||||
currentIndex2 = layoutModel2.getIndexAfter(currentIndex2);
|
||||
continue;
|
||||
}
|
||||
return; // currentLayout2 is null.
|
||||
}
|
||||
int height = currentLayout2.getHeight();
|
||||
if (remainingOffset2 == 0) {
|
||||
otherPanel.setViewerPosition(currentIndex2, xPos, 0);
|
||||
return;
|
||||
}
|
||||
else if (remainingOffset2 < 0) {
|
||||
int offset = height + remainingOffset2;
|
||||
if (offset >= 0) {
|
||||
otherPanel.setViewerPosition(currentIndex2, xPos, -offset);
|
||||
return;
|
||||
}
|
||||
currentIndex2 = layoutModel2.getIndexBefore(currentIndex2);
|
||||
remainingOffset2 = offset;
|
||||
}
|
||||
else { // remainingOffset2 > 0
|
||||
if (remainingOffset2 < height) {
|
||||
otherPanel.setViewerPosition(currentIndex2, xPos, -remainingOffset2);
|
||||
return;
|
||||
}
|
||||
currentIndex2 = layoutModel2.getIndexAfter(currentIndex2);
|
||||
remainingOffset2 -= height;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int getOffsetFromLockedIndex(LayoutModel layoutModel1, BigInteger topIndex1,
|
||||
BigInteger lockedLineIndex1, int yPos) {
|
||||
|
||||
// numIndexes is the total number of indexes in this field panels indexMap.
|
||||
BigInteger numIndexes1 = layoutModel1.getNumIndexes();
|
||||
Layout firstLayout1 = layoutModel1.getLayout(topIndex1);
|
||||
|
||||
// "yPos" is a negative number indicating the number of pixels the start of the current
|
||||
// layout is above the top of the field panel view.
|
||||
|
||||
// "remainingHeight" is the number of pixels vertically from the first visible pixel
|
||||
// in the layout at the top of the listing view to the end of that layout.
|
||||
int remainingHeight = firstLayout1.getHeight() + yPos;
|
||||
|
||||
// "offsetInLayout" is the number of pixels that the top of the listing view is below
|
||||
// the start of the current layout.
|
||||
int offsetInLayout1 = 0;
|
||||
int offsetFromLockedIndex1 = 0;
|
||||
if (lockedLineIndex1.compareTo(topIndex1) == 0) {
|
||||
offsetInLayout1 -= yPos;
|
||||
offsetFromLockedIndex1 += offsetInLayout1;
|
||||
}
|
||||
else if (lockedLineIndex1.compareTo(topIndex1) < 0) {
|
||||
return addIndexesBelow(layoutModel1, topIndex1, lockedLineIndex1,
|
||||
offsetFromLockedIndex1, yPos);
|
||||
}
|
||||
else { // lockedlineIndex1 > topIndex1
|
||||
BigInteger currentIndex1 = layoutModel1.getIndexAfter(topIndex1);
|
||||
while (currentIndex1 != null && (currentIndex1.compareTo(numIndexes1) < 0) &&
|
||||
currentIndex1.compareTo(lockedLineIndex1) < 0) {
|
||||
Layout currentLayout = layoutModel1.getLayout(currentIndex1);
|
||||
if (currentLayout != null) {
|
||||
offsetFromLockedIndex1 -= currentLayout.getHeight();
|
||||
}
|
||||
currentIndex1 = layoutModel1.getIndexAfter(currentIndex1);
|
||||
}
|
||||
offsetFromLockedIndex1 -= remainingHeight;
|
||||
}
|
||||
|
||||
return offsetFromLockedIndex1;
|
||||
}
|
||||
|
||||
private int addIndexesBelow(LayoutModel layoutModel, BigInteger topIndex1,
|
||||
BigInteger lockedLineIndex, int offsetFromLockedIndex1, int yPos) {
|
||||
|
||||
BigInteger numIndexes = layoutModel.getNumIndexes();
|
||||
BigInteger currentIndex = lockedLineIndex;
|
||||
while (currentIndex != null && (currentIndex.compareTo(numIndexes) < 0) &&
|
||||
currentIndex.compareTo(topIndex1) < 0) {
|
||||
Layout currentLayout = layoutModel.getLayout(currentIndex);
|
||||
if (currentLayout != null) {
|
||||
offsetFromLockedIndex1 += currentLayout.getHeight();
|
||||
}
|
||||
currentIndex = layoutModel.getIndexAfter(currentIndex);
|
||||
}
|
||||
offsetFromLockedIndex1 -= yPos;
|
||||
return offsetFromLockedIndex1;
|
||||
}
|
||||
}
|
|
@ -29,17 +29,30 @@ import ghidra.util.exception.AssertException;
|
|||
* In other words this coordinator tries to keep the indicated line for each field panel
|
||||
* side by side with the indicated line for each other field panel.
|
||||
*/
|
||||
public class LineLockedFieldPanelCoordinator extends FieldPanelCoordinator {
|
||||
public class LineLockedFieldPanelScrollCoordinator extends FieldPanelScrollCoordinator {
|
||||
|
||||
// Keep an array of the line number for each field panel where we are locking
|
||||
// these field panels together when scrolling or moving the cursor location.
|
||||
protected BigInteger[] lockedLineNumbers;
|
||||
|
||||
public LineLockedFieldPanelCoordinator(FieldPanel[] panels) {
|
||||
public LineLockedFieldPanelScrollCoordinator(FieldPanel[] panels) {
|
||||
super(panels);
|
||||
resetLockedLines();
|
||||
}
|
||||
|
||||
protected BigInteger getLockedLineNumber(FieldPanel fp) {
|
||||
int index = getIndex(fp);
|
||||
return lockedLineNumbers[index];
|
||||
}
|
||||
|
||||
private int getIndex(FieldPanel fp) {
|
||||
|
||||
if (panels[0] == fp) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the locked line numbers for this field panel coordinator to their default
|
||||
* of each being zero.
|
||||
|
@ -117,8 +130,9 @@ public class LineLockedFieldPanelCoordinator extends FieldPanelCoordinator {
|
|||
|
||||
@Override
|
||||
public void viewChanged(FieldPanel fp, BigInteger index, int xPos, int yPos) {
|
||||
if (valuesChanging)
|
||||
if (valuesChanging) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
valuesChanging = true;
|
||||
BigInteger fpLineNumber = getLockedLineForPanel(fp);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue