Candidate release of source code.
@@ -0,0 +1 @@
|
||||
EXCLUDE FROM GHIDRA JAR: true
|
||||
@@ -0,0 +1,10 @@
|
||||
apply plugin: 'eclipse'
|
||||
eclipse.project.name = 'Features ProgramDiff'
|
||||
|
||||
apply from: "$rootProject.projectDir/gradleScripts/buildHelp.gradle"
|
||||
|
||||
dependencies {
|
||||
compile project(":Base")
|
||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
##VERSION: 2.0
|
||||
##MODULE IP: Nuvola Icons - LGPL 2.1
|
||||
##MODULE IP: Modified Nuvola Icons - LGPL 2.1
|
||||
##MODULE IP: Tango Icons - Public Domain
|
||||
##MODULE IP: FAMFAMFAM Icons - CC 2.5
|
||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
##MODULE IP: Crystal Clear Icons - LGPL 2.1
|
||||
.classpath||GHIDRA||||END|
|
||||
.project||GHIDRA||||END|
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/shared/arrow.gif||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/close16.gif||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/menu16.gif||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/note-red.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0) Renamed version of flag-red.png|END|
|
||||
src/main/help/help/shared/note.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0) Renamed version of flag-green.png|END|
|
||||
src/main/help/help/shared/note.yellow.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0) Renamed version of flag-yellow.png|END|
|
||||
src/main/help/help/shared/redo.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0) Renamed version of help-hint.png|END|
|
||||
src/main/help/help/shared/undo.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0) Renamed version of security-medium.png|END|
|
||||
src/main/help/help/topics/Diff/Diff.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/DetermineDiffs.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/Diff.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/Diff16.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/DiffApplySettings.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/DiffApplySettingsPopup.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/DiffDetails.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/DiffSelect16.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/SelectOtherProgram.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/SelectOtherVersionedProgram.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/disk.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/help/help/topics/Diff/images/down.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/erase16.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/eraser_arrow16.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Diff/images/pencil16.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/pencil_arrow16.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/settings16.gif||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/table_relationship.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/help/help/topics/Diff/images/up.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Diff/images/xmag.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
||||
src/main/resources/images/Diff16.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/DiffSelect16.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/erase16.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/eraser_arrow16.png||GHIDRA||||END|
|
||||
src/main/resources/images/pencil16.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/pencil_arrow16.png||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/settings16.gif||GHIDRA||reviewed||END|
|
||||
src/main/resources/images/table_relationship.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/xmag.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version='1.0' encoding='ISO-8859-1' ?>
|
||||
<!--
|
||||
|
||||
This is an XML file intended to be parsed by the Ghidra help system. It is loosely based
|
||||
upon the JavaHelp table of contents document format. The Ghidra help system uses a
|
||||
TOC_Source.xml file to allow a module with help to define how its contents appear in the
|
||||
Ghidra help viewer's table of contents. The main document (in the Base module)
|
||||
defines a basic structure for the
|
||||
Ghidra table of contents system. Other TOC_Source.xml files may use this structure to insert
|
||||
their files directly into this structure (and optionally define a substructure).
|
||||
|
||||
|
||||
In this document, a tag can be either a <tocdef> or a <tocref>. The former is a definition
|
||||
of an XML item that may have a link and may contain other <tocdef> and <tocref> children.
|
||||
<tocdef> items may be referred to in other documents by using a <tocref> tag with the
|
||||
appropriate id attribute value. Using these two tags allows any module to define a place
|
||||
in the table of contents system (<tocdef>), which also provides a place for
|
||||
other TOC_Source.xml files to insert content (<tocref>).
|
||||
|
||||
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
|
||||
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
|
||||
<module name>_TOC.xml files, which are table of contents files written in the format
|
||||
desired by the JavaHelp system. Additionally, the genated files will be merged together
|
||||
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
|
||||
help GUI, there will be on table of contents that has been created from the definitions in
|
||||
all of the modules' TOC_Source.xml files.
|
||||
|
||||
|
||||
Tags and Attributes
|
||||
|
||||
<tocdef>
|
||||
-id - the name of the definition (this must be unique across all TOC_Source.xml files)
|
||||
-text - the display text of the node, as seen in the help GUI
|
||||
-target** - the file to display when the node is clicked in the GUI
|
||||
-sortgroup - this is a string that defines where a given node should appear under a given
|
||||
parent. The string values will be sorted by the JavaHelp system using
|
||||
a javax.text.RulesBasedCollator. If this attribute is not specified, then
|
||||
the text of attribute will be used.
|
||||
|
||||
<tocref>
|
||||
-id - The id of the <tocdef> that this reference points to
|
||||
|
||||
**The URL for the target is relative and should start with 'help/topics'. This text is
|
||||
used by the Ghidra help system to provide a universal starting point for all links so that
|
||||
they can be resolved at runtime, across modules.
|
||||
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<tocroot>
|
||||
<tocref id="Code Browser">
|
||||
<tocdef id="Program Differences" sortgroup="e" text="Program Differences" target="help/topics/Diff/Diff.htm" />
|
||||
</tocref>
|
||||
</tocroot>
|
||||
@@ -0,0 +1,58 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
WARNING!
|
||||
This file is copied to all help directories. If you change this file, you must copy it
|
||||
to each src/main/help/help/shared directory.
|
||||
|
||||
|
||||
Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but
|
||||
px (pixel) or with no type marking.
|
||||
|
||||
*/
|
||||
|
||||
body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */
|
||||
li { font-family:times new roman; font-size:14pt; }
|
||||
h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; }
|
||||
h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; }
|
||||
h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; }
|
||||
h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; }
|
||||
|
||||
/*
|
||||
P tag code. Most of the help files nest P tags inside of blockquote tags (the was the
|
||||
way it had been done in the beginning). The net effect is that the text is indented. In
|
||||
modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in
|
||||
blockquote tags, as well as naked P tags. The following two lines accomplish this. Note
|
||||
that the 'blockquote p' definition will inherit from the first 'p' definition.
|
||||
*/
|
||||
p { margin-left: 40px; font-family:times new roman; font-size:14pt; }
|
||||
blockquote p { margin-left: 10px; }
|
||||
|
||||
p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
|
||||
/*
|
||||
We wish for a tables to have space between it and the preceding element, so that text
|
||||
is not too close to the top of the table. Also, nest the table a bit so that it is clear
|
||||
the table relates to the preceding text.
|
||||
*/
|
||||
table { margin-left: 20px; margin-top: 10px; width: 80%;}
|
||||
td { font-family:times new roman; font-size:14pt; vertical-align: top; }
|
||||
th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; }
|
||||
|
||||
code { color: black; font-family: courier new; font-size: 14pt; }
|
||||
|
After Width: | Height: | Size: 69 B |
|
After Width: | Height: | Size: 859 B |
|
After Width: | Height: | Size: 62 B |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 187 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 185 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 217 B |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 253 B |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 620 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 435 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 142 B |
|
After Width: | Height: | Size: 663 B |
|
After Width: | Height: | Size: 193 B |
|
After Width: | Height: | Size: 1000 B |
@@ -0,0 +1,158 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* NOTE: This disables auto analysis while differences are applied and restores auto analysis enablement at end.
|
||||
*
|
||||
* 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.diff;
|
||||
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.plugin.core.analysis.AnalysisWorker;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.widgets.dialogs.ReadTextDialog;
|
||||
|
||||
/**
|
||||
* Command to apply diffs to current program.
|
||||
*
|
||||
*/
|
||||
class ApplyDiffCommand extends BackgroundCommand implements AnalysisWorker {
|
||||
|
||||
private AddressSetView p1AddressSet;
|
||||
private DiffController diffControl;
|
||||
private String title;
|
||||
private String applyMsg;
|
||||
private boolean applied;
|
||||
private ProgramDiffPlugin plugin;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
ApplyDiffCommand(ProgramDiffPlugin plugin, AddressSetView program1AddressSet,
|
||||
DiffController diffControl) {
|
||||
super("Apply Differences", false, true, true);
|
||||
this.plugin = plugin;
|
||||
this.p1AddressSet = program1AddressSet;
|
||||
this.diffControl = diffControl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor)
|
||||
throws Exception, CancelledException {
|
||||
// Diff apply done with analysis disabled
|
||||
return diffControl.apply(p1AddressSet, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorkerName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.cmd.BackgroundCommand#applyTo(ghidra.framework.model.DomainObject, ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
monitor.setMessage("ApplyDiffTask starting...");
|
||||
applied = false;
|
||||
final ProgramLocation origLocation = plugin.getProgramLocation();
|
||||
if (!plugin.isTaskInProgress()) {
|
||||
|
||||
plugin.setTaskInProgress(true);
|
||||
String statusMsg = "One or more differences couldn't be applied.";
|
||||
title = "Program Diff: One or more differences couldn't be applied.";
|
||||
applyMsg = null;
|
||||
setStatusMsg(null);
|
||||
try {
|
||||
AutoAnalysisManager autoAnalysisManager =
|
||||
AutoAnalysisManager.getAnalysisManager(plugin.getFirstProgram());
|
||||
boolean merged = autoAnalysisManager.scheduleWorker(this, null, false, monitor);
|
||||
if (merged) {
|
||||
statusMsg =
|
||||
"Apply differences has finished."
|
||||
+ " If your expected change didn't occur, check your Diff Apply Settings.";
|
||||
title = "Program Diff: Apply differences has finished.";
|
||||
applied = true;
|
||||
}
|
||||
else {
|
||||
applyMsg = diffControl.getApplyMessage();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
applyMsg = "Unexpected InterruptedException\n" + diffControl.getApplyMessage();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Throwable t = e.getCause();
|
||||
String message = "";
|
||||
// Protect against dereferencing the getCause call above, which may return null.
|
||||
if (t != null) {
|
||||
String excMessage = t.getMessage();
|
||||
if (excMessage != null && excMessage.length() > 0) {
|
||||
message = excMessage + "\n";
|
||||
}
|
||||
}
|
||||
Msg.showError(this, plugin.getListingPanel(), "Error Applying Diff",
|
||||
"An error occured while applying differences.\n"
|
||||
+ "Only some of the differences may have been applied.",
|
||||
(t != null) ? t : e);
|
||||
applyMsg = message + diffControl.getApplyMessage();
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
statusMsg =
|
||||
"User cancelled \"Apply Differences\". "
|
||||
+ "Differences were only partially applied.";
|
||||
applyMsg = diffControl.getApplyMessage();
|
||||
}
|
||||
finally {
|
||||
setStatusMsg(statusMsg);
|
||||
plugin.getTool().setStatusInfo(statusMsg);
|
||||
plugin.setTaskInProgress(false);
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
plugin.adjustDiffDisplay();
|
||||
plugin.firePluginEvent(new ProgramSelectionPluginEvent(plugin.getName(),
|
||||
plugin.getCurrentSelection(), plugin.getCurrentProgram()));
|
||||
plugin.programLocationChanged(origLocation, null);
|
||||
if (applyMsg != null && applyMsg.length() > 0) {
|
||||
ReadTextDialog detailsDialog = new ReadTextDialog(title, applyMsg);
|
||||
plugin.getTool().showDialog(detailsDialog, plugin.getListingPanel());
|
||||
}
|
||||
}
|
||||
};
|
||||
// // The events were disabled while doing apply Diff. Now re-enable them by firing object restored event.
|
||||
// ((DomainObjectAdapter)currentProgram).fireEvent(new DomainObjectChangeRecord(
|
||||
// DomainObject.DO_OBJECT_RESTORED));
|
||||
// ((DomainObjectAdapter)currentProgram).flushEvents();
|
||||
if (!monitor.isCancelled()) {
|
||||
SwingUtilities.invokeLater(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import ghidra.framework.model.DomainObjectException;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.ClosedException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Task that generates the address set containing the differences between
|
||||
* two programs.
|
||||
*
|
||||
*/
|
||||
class CreateDiffTask extends Task {
|
||||
private ProgramDiffPlugin plugin;
|
||||
private Program program1;
|
||||
private Program program2;
|
||||
private AddressSetView limitedAddressSet;
|
||||
private ProgramDiffFilter diffFilter;
|
||||
private ProgramMergeFilter applyFilter;
|
||||
private DiffApplySettingsProvider diffApplySettingsProvider;
|
||||
private boolean isLimitedToSelection;
|
||||
|
||||
/**
|
||||
* Construct new LoadDiffTask that loads the dialog with the two
|
||||
* programs and indicates their differences. The differences should be
|
||||
* restricted to the limitedAddressSet.
|
||||
*
|
||||
*/
|
||||
CreateDiffTask(ProgramDiffPlugin plugin, Program program1, Program program2,
|
||||
AddressSetView limitedAddressSet, boolean isLimitedToSelection,
|
||||
ProgramDiffFilter diffFilter, ProgramMergeFilter applyFilter) {
|
||||
super("Checking Program Differences", true, false, false);
|
||||
this.plugin = plugin;
|
||||
this.program1 = program1;
|
||||
this.program2 = program2;
|
||||
this.limitedAddressSet = limitedAddressSet;
|
||||
this.isLimitedToSelection = isLimitedToSelection;
|
||||
this.diffFilter = diffFilter;
|
||||
this.applyFilter = applyFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the method TaskMonitor called to do the work.
|
||||
*
|
||||
* @param monitor The TaskMonitor that will monitor the executing Task. Will be null if
|
||||
* this task declared that it does not use a TaskMonitor
|
||||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
if (plugin.isTaskInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DiffController dc = null;
|
||||
plugin.setTaskInProgress(true);
|
||||
monitor.setIndeterminate(true);
|
||||
monitor.setMessage("Checking Program Differences");
|
||||
try {
|
||||
dc = new DiffController(program1, program2, limitedAddressSet, this.diffFilter,
|
||||
this.applyFilter, monitor);
|
||||
AddressSetView filteredDifferences = dc.getFilteredDifferences(monitor);
|
||||
boolean noFilteredDifferencesFound = filteredDifferences.isEmpty();
|
||||
plugin.setDiffController(dc);
|
||||
dc.differencesChanged(monitor);
|
||||
dc.setLocation(plugin.getCurrentAddress());
|
||||
monitor.setMessage("Done");
|
||||
Runnable r = () -> displayDifferencesMessageIfNecessary(noFilteredDifferencesFound);
|
||||
SwingUtilities.invokeLater(r);
|
||||
}
|
||||
catch (DomainObjectException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof ClosedException) {
|
||||
// this can happen if you close the tool while this task is calculating diffs
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
catch (ProgramConflictException e) {
|
||||
showErrorMessage(e.getMessage());
|
||||
return;
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
plugin.setDiffController(dc);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
completed();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayDifferencesMessageIfNecessary(final boolean noFilteredDifferencesFound) {
|
||||
|
||||
try {
|
||||
ProgramMemoryComparator programMemoryComparator =
|
||||
new ProgramMemoryComparator(program1, program2);
|
||||
boolean hasMemoryDifferences = programMemoryComparator.hasMemoryDifferences();
|
||||
|
||||
String title = null;
|
||||
String message = null;
|
||||
|
||||
if (isLimitedToSelection) {
|
||||
if (noFilteredDifferencesFound) {
|
||||
title = "No Differences In Selection";
|
||||
message = "No differences were found for the addresses in the selection" +
|
||||
"\nand for the types of program information being compared by this Diff.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hasMemoryDifferences) {
|
||||
title = "Memory Differs";
|
||||
message = getMemoryDifferenceMessage(noFilteredDifferencesFound,
|
||||
programMemoryComparator);
|
||||
}
|
||||
else if (noFilteredDifferencesFound) {
|
||||
// Not a Diff on a selection, memory is the same, and no differences found
|
||||
// for current filter and compatible addresses.
|
||||
title = "No Differences";
|
||||
message =
|
||||
"No differences were found for the addresses that are compatible between the two" +
|
||||
"\nprograms for the types of program information being compared by this Diff.";
|
||||
}
|
||||
}
|
||||
if (title != null) {
|
||||
String note =
|
||||
"\n \nNote: Some parts of the program are not handled by Diff (for example:" +
|
||||
"\n Markup where only one program has that memory address," +
|
||||
"\n Registers that are not common to both programs' languages," +
|
||||
"\n Program Trees, Data Types that haven't been applied to memory, etc.)";
|
||||
|
||||
Msg.showInfo(getClass(), plugin.getListingPanel(), title, message + note);
|
||||
}
|
||||
}
|
||||
catch (ProgramConflictException e) {
|
||||
Msg.showError(getClass(), plugin.getListingPanel(), "Can't Compare Memory",
|
||||
"Diff can't compare the two programs memory. " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private String getMemoryDifferenceMessage(final boolean noFilteredDifferencesFound,
|
||||
ProgramMemoryComparator programMemoryComparator) {
|
||||
String message;
|
||||
message = "The memory addresses defined by the two programs are not the same.\n \n" +
|
||||
(noFilteredDifferencesFound ? "However, no differences were found "
|
||||
: "Differences are highlighted ") +
|
||||
"for the addresses that are compatible between" +
|
||||
"\nthe two programs for the types of program information being compared by this Diff.";
|
||||
|
||||
AddressSet addressesOnlyInOne = programMemoryComparator.getAddressesOnlyInOne();
|
||||
if (!addressesOnlyInOne.isEmpty()) {
|
||||
message +=
|
||||
"\n \nSome addresses are only in program 1 : " + addressesOnlyInOne.toString();
|
||||
}
|
||||
|
||||
AddressSet addressesOnlyInTwo = programMemoryComparator.getAddressesOnlyInTwo();
|
||||
if (!addressesOnlyInTwo.isEmpty()) {
|
||||
message +=
|
||||
"\n \nSome addresses are only in program 2 : " + addressesOnlyInTwo.toString();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private void showErrorMessage(String message) {
|
||||
SystemUtilities.runSwingLater(() -> Msg.showError(getClass(),
|
||||
plugin.getTool().getToolFrame(), "Can't Perform Diff", message));
|
||||
}
|
||||
|
||||
private void completed() {
|
||||
if (plugin.isDisposed()) {
|
||||
// the tool was closed while this task was running
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.getCurrentProgram() == null) {
|
||||
// the program was closed while this task was running
|
||||
return;
|
||||
}
|
||||
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
diffApplySettingsProvider = plugin.getDiffApplySettingsProvider();
|
||||
diffApplySettingsProvider.configure(applyFilter);
|
||||
plugin.adjustDiffDisplay();
|
||||
});
|
||||
|
||||
plugin.setTaskInProgress(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.codebrowser.OtherPanelContext;
|
||||
import ghidra.app.services.CodeViewerService;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.util.ToolConstants;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Creates the actions for the program diff plugin.
|
||||
*/
|
||||
class DiffActionManager {
|
||||
|
||||
private static final String SELECT_GROUP = "Select";
|
||||
private static final String GET_DIFF_GROUP = "GetDiff";
|
||||
private static final String DIFF_INFO_GROUP = "DiffInfo";
|
||||
private static final String DIFF_NAVIGATE_GROUP = "DiffNavigate";
|
||||
private static final String TOGGLE_VIEW_ICON_NAME = "images/table_relationship.png";
|
||||
private static final String GROUP = "Diff";
|
||||
private ProgramDiffPlugin plugin;
|
||||
private CodeViewerService codeViewerService;
|
||||
|
||||
static final String APPLY_DIFFS_ACTION = "Apply Differences";
|
||||
static final String APPLY_DIFFS_NEXT_ACTION = "Apply Differences and Goto Next Difference";
|
||||
static final String IGNORE_DIFFS_NEXT_ACTION = "Ignore Selection and Goto Next Difference";
|
||||
static final String NEXT_DIFF_ACTION = "Next Difference";
|
||||
static final String PREVIOUS_DIFF_ACTION = "Previous Difference";
|
||||
static final String DIFF_DETAILS_ACTION = "Diff Location Details";
|
||||
static final String SHOW_DIFF_SETTINGS_ACTION = "Show Diff Apply Settings";
|
||||
static final String GET_DIFFS_ACTION = "Get Differences";
|
||||
static final String SELECT_ALL_DIFFS_ACTION = "Select All Differences";
|
||||
static final String P1_SELECT_TO_P2_ACTION = "Set Program1 Selection On Program2";
|
||||
static final String OPEN_CLOSE_PROGRAM2_ACTION = "Open/Close Program View";
|
||||
static final String VIEW_PROGRAM_DIFF_ACTION = "View Program Differences";
|
||||
|
||||
private DockingAction applyDiffsAction;
|
||||
private DockingAction applyDiffsNextAction;
|
||||
private DockingAction ignoreDiffsAction;
|
||||
private DockingAction nextDiffAction;
|
||||
private DockingAction previousDiffAction;
|
||||
private DockingAction diffDetailsAction;
|
||||
private DockingAction showDiffSettingsAction;
|
||||
private DockingAction getDiffsAction;
|
||||
private DockingAction selectAllDiffsAction;
|
||||
private DockingAction p1SelectToP2Action;
|
||||
private ToggleDockingAction openCloseProgram2Action;
|
||||
private DockingAction viewProgramDiffAction;
|
||||
|
||||
/**
|
||||
* Creates an action manager for the Program Diff plugin.
|
||||
*/
|
||||
public DiffActionManager(ProgramDiffPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
createActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the code viewer service that the Program Diff will use for setting local actions.
|
||||
* @param codeViewerService the code viewer service
|
||||
*/
|
||||
void setCodeViewerService(CodeViewerService codeViewerService) {
|
||||
this.codeViewerService = codeViewerService;
|
||||
codeViewerService.addLocalAction(openCloseProgram2Action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Program Diff's local actions to the code viewer.
|
||||
*/
|
||||
void addActions() {
|
||||
codeViewerService.addLocalAction(applyDiffsAction);
|
||||
codeViewerService.addLocalAction(applyDiffsNextAction);
|
||||
codeViewerService.addLocalAction(ignoreDiffsAction);
|
||||
codeViewerService.addLocalAction(diffDetailsAction);
|
||||
codeViewerService.addLocalAction(nextDiffAction);
|
||||
codeViewerService.addLocalAction(previousDiffAction);
|
||||
codeViewerService.addLocalAction(showDiffSettingsAction);
|
||||
codeViewerService.addLocalAction(getDiffsAction);
|
||||
codeViewerService.addLocalAction(selectAllDiffsAction);
|
||||
codeViewerService.addLocalAction(p1SelectToP2Action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Program Diff's local actions from the code viewer.
|
||||
*/
|
||||
void removeActions() {
|
||||
codeViewerService.removeLocalAction(applyDiffsAction);
|
||||
codeViewerService.removeLocalAction(applyDiffsNextAction);
|
||||
codeViewerService.removeLocalAction(ignoreDiffsAction);
|
||||
codeViewerService.removeLocalAction(diffDetailsAction);
|
||||
codeViewerService.removeLocalAction(nextDiffAction);
|
||||
codeViewerService.removeLocalAction(previousDiffAction);
|
||||
codeViewerService.removeLocalAction(showDiffSettingsAction);
|
||||
codeViewerService.removeLocalAction(getDiffsAction);
|
||||
codeViewerService.removeLocalAction(selectAllDiffsAction);
|
||||
codeViewerService.removeLocalAction(p1SelectToP2Action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to adjust the Program Diff's actions when a program is closed.
|
||||
* @param program the closed program
|
||||
*/
|
||||
void programClosed(Program program) {
|
||||
boolean hasProgram = (plugin.getCurrentProgram() != null);
|
||||
openCloseProgram2Action.setEnabled(hasProgram && !plugin.isTaskInProgress());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to adjust or add/remove the Program Diff's actions when a program
|
||||
* becomes the active program.
|
||||
* @param program the newly active program
|
||||
*/
|
||||
void setActiveProgram(Program program) {
|
||||
viewProgramDiffAction.setEnabled(program != null);
|
||||
|
||||
boolean enabled = program != null && !plugin.isTaskInProgress();
|
||||
openCloseProgram2Action.setEnabled(enabled);
|
||||
|
||||
if (enabled) {
|
||||
if (openCloseProgram2Action.isSelected()) {
|
||||
// we are diffing--is the current program the diff program?
|
||||
Program firstProgram = plugin.getFirstProgram();
|
||||
if (firstProgram == program) {
|
||||
openCloseProgram2Action.setDescription("Close Diff View");
|
||||
}
|
||||
else {
|
||||
openCloseProgram2Action.setDescription("Bring Diff View to Front");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no active diff
|
||||
openCloseProgram2Action.setDescription("Open Diff View");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification to the action manager that a program was opened as the
|
||||
* second program to the program Diff. Actions are adjusted accordingly.
|
||||
*/
|
||||
void secondProgramOpened() {
|
||||
openCloseProgram2Action.setSelected(true);
|
||||
openCloseProgram2Action.setDescription("Close Diff View");
|
||||
|
||||
Program firstProgram = plugin.getFirstProgram();
|
||||
Program secondProgram = plugin.getSecondProgram();
|
||||
String firstName = firstProgram.getName();
|
||||
String secondName = secondProgram.getName();
|
||||
|
||||
//@formatter:off
|
||||
openCloseProgram2Action.setDescription("<html><center>Close Diff View</center><br>" +
|
||||
"Current diff: " +
|
||||
"<b>"+firstName+"</b> to <b>" +secondName+"</b>");
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification to the action manager that the second program to the
|
||||
* program Diff was closed. Actions are adjusted accordingly.
|
||||
*/
|
||||
void secondProgramClosed() {
|
||||
openCloseProgram2Action.setSelected(false);
|
||||
openCloseProgram2Action.setDescription("Open Diff View");
|
||||
showDiffSettingsAction.setEnabled(false);
|
||||
diffDetailsAction.setEnabled(false);
|
||||
removeActions();
|
||||
}
|
||||
|
||||
void setP1SelectToP2ActionEnabled(boolean enabled) {
|
||||
p1SelectToP2Action.setEnabled(enabled);
|
||||
}
|
||||
|
||||
void setOpenCloseActionSelected(boolean selected) {
|
||||
openCloseProgram2Action.setSelected(selected);
|
||||
}
|
||||
|
||||
void updateActions(boolean taskInProgress, boolean inDiff, boolean hasSelectionInView,
|
||||
boolean applyFilterIsSet, boolean hasProgram2, boolean hasHighlights) {
|
||||
DiffController diffControl = plugin.getDiffController();
|
||||
applyDiffsAction.setEnabled(!taskInProgress && inDiff && hasSelectionInView);
|
||||
applyDiffsNextAction.setEnabled(
|
||||
!taskInProgress && inDiff && hasSelectionInView && diffControl.hasNext());
|
||||
ignoreDiffsAction.setEnabled(!taskInProgress && inDiff && hasSelectionInView);
|
||||
nextDiffAction.setEnabled(!taskInProgress && inDiff && diffControl.hasNext());
|
||||
previousDiffAction.setEnabled(!taskInProgress && inDiff && diffControl.hasPrevious());
|
||||
diffDetailsAction.setEnabled(!taskInProgress && hasProgram2);
|
||||
showDiffSettingsAction.setEnabled(!taskInProgress && inDiff);
|
||||
getDiffsAction.setEnabled(!taskInProgress && hasProgram2);
|
||||
selectAllDiffsAction.setEnabled(!taskInProgress && !inDiff || hasHighlights);
|
||||
p1SelectToP2Action.setEnabled(hasProgram2 && !plugin.getCurrentSelection().isEmpty());
|
||||
|
||||
Program currentProgram = plugin.getCurrentProgram();
|
||||
boolean hasProgram = (currentProgram != null);
|
||||
openCloseProgram2Action.setEnabled(hasProgram && !taskInProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the actions.
|
||||
*/
|
||||
void dispose() {
|
||||
codeViewerService.removeLocalAction(openCloseProgram2Action);
|
||||
plugin.getTool().removeAction(viewProgramDiffAction);
|
||||
removeActions();
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
viewProgramDiffAction = new DockingAction(VIEW_PROGRAM_DIFF_ACTION, plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.selectProgram2();
|
||||
}
|
||||
};
|
||||
String[] menuPath = { ToolConstants.MENU_TOOLS, "Program &Differences..." };
|
||||
viewProgramDiffAction.setEnabled(plugin.getCurrentProgram() != null);
|
||||
viewProgramDiffAction.setMenuBarData(new MenuData(menuPath, "View"));
|
||||
viewProgramDiffAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_2, 0));
|
||||
viewProgramDiffAction.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.DIFF, "Program_Differences"));
|
||||
plugin.getTool().addAction(viewProgramDiffAction);
|
||||
|
||||
applyDiffsAction = new DiffAction(APPLY_DIFFS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.applyDiff();
|
||||
}
|
||||
};
|
||||
ImageIcon icon = ResourceManager.loadImage("images/pencil16.png");
|
||||
applyDiffsAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_F3, 0));
|
||||
applyDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Apply Selection" }, icon, GROUP));
|
||||
applyDiffsAction.setDescription(
|
||||
"Applies the differences from the second program's selection using the settings.");
|
||||
applyDiffsAction.setToolBarData(new ToolBarData(icon, GROUP));
|
||||
|
||||
applyDiffsNextAction = new DiffAction(APPLY_DIFFS_NEXT_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.applyDiffAndGoNext();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/pencil_arrow16.png");
|
||||
String[] applyDiffsPath = { "Apply Selection and Goto Next Difference" };
|
||||
applyDiffsNextAction.setPopupMenuData(new MenuData(applyDiffsPath, icon, GROUP));
|
||||
applyDiffsNextAction.setKeyBindingData(
|
||||
new KeyBindingData(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK));
|
||||
applyDiffsNextAction.setToolBarData(new ToolBarData(icon, GROUP));
|
||||
applyDiffsNextAction.setDescription(
|
||||
"Applies the differences from the second program's selection using the settings. " +
|
||||
"Then moves the cursor to the next difference.");
|
||||
|
||||
ignoreDiffsAction = new DiffAction(IGNORE_DIFFS_NEXT_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.ignoreDiff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/eraser_arrow16.png");
|
||||
ignoreDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Ignore Selection and Goto Next Difference" }, icon,
|
||||
GROUP));
|
||||
ignoreDiffsAction.setDescription(
|
||||
"Ignores the selected program differences and moves the cursor to the next difference.");
|
||||
ignoreDiffsAction.setToolBarData(new ToolBarData(icon, GROUP));
|
||||
|
||||
nextDiffAction = new DiffAction(NEXT_DIFF_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.nextDiff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/down.png");
|
||||
nextDiffAction.setKeyBindingData(
|
||||
new KeyBindingData('N', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
nextDiffAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Next Difference" }, icon, DIFF_NAVIGATE_GROUP));
|
||||
nextDiffAction.setToolBarData(new ToolBarData(icon, DIFF_NAVIGATE_GROUP));
|
||||
nextDiffAction.setDescription("Go to the next highlighted difference.");
|
||||
|
||||
previousDiffAction = new DiffAction(PREVIOUS_DIFF_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.previousDiff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/up.png");
|
||||
previousDiffAction.setKeyBindingData(
|
||||
new KeyBindingData('P', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
previousDiffAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Previous Difference" }, icon, DIFF_NAVIGATE_GROUP));
|
||||
previousDiffAction.setToolBarData(new ToolBarData(icon, DIFF_NAVIGATE_GROUP));
|
||||
previousDiffAction.setDescription("Go to previous highlighted difference.");
|
||||
|
||||
diffDetailsAction = new DiffAction(DIFF_DETAILS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.showDiffDetails();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/xmag.png");
|
||||
diffDetailsAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_F5, 0));
|
||||
diffDetailsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Location Details..." }, icon, DIFF_INFO_GROUP));
|
||||
diffDetailsAction.setToolBarData(new ToolBarData(icon, DIFF_INFO_GROUP));
|
||||
diffDetailsAction.setDescription(
|
||||
"Show details of the program differences at the current location.");
|
||||
|
||||
showDiffSettingsAction = new DiffAction(SHOW_DIFF_SETTINGS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.showDiffApplySettings();
|
||||
}
|
||||
};
|
||||
showDiffSettingsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Show Diff Apply Settings..." }, GET_DIFF_GROUP));
|
||||
showDiffSettingsAction.setDescription(
|
||||
"Displays the current program difference apply settings.");
|
||||
showDiffSettingsAction.setToolBarData(
|
||||
new ToolBarData(DiffApplySettingsProvider.ICON, GET_DIFF_GROUP));
|
||||
|
||||
getDiffsAction = new DiffAction(GET_DIFFS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.diff();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/Diff16.png");
|
||||
getDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Get Differences..." }, icon, GET_DIFF_GROUP));
|
||||
getDiffsAction.setToolBarData(new ToolBarData(icon, GET_DIFF_GROUP));
|
||||
getDiffsAction.setDescription("Determines program differences.");
|
||||
|
||||
selectAllDiffsAction = new DiffAction(SELECT_ALL_DIFFS_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.selectAllDiffs();
|
||||
}
|
||||
};
|
||||
selectAllDiffsAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Select All Differences" }, SELECT_GROUP));
|
||||
selectAllDiffsAction.setDescription(
|
||||
"Selects all highlighted differences in the second program.");
|
||||
|
||||
p1SelectToP2Action = new DiffAction(P1_SELECT_TO_P2_ACTION) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
plugin.setP1SelectionOnP2();
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage("images/DiffSelect16.png");
|
||||
p1SelectToP2Action.setDescription(
|
||||
"Select Program 2 highlights using selection in Program 1.");
|
||||
p1SelectToP2Action.setToolBarData(new ToolBarData(icon, SELECT_GROUP));
|
||||
|
||||
openCloseProgram2Action =
|
||||
new ToggleDockingAction(OPEN_CLOSE_PROGRAM2_ACTION, plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
//
|
||||
// No active diff--start one.
|
||||
//
|
||||
if (openCloseProgram2Action.isSelected()) {
|
||||
plugin.selectProgram2();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// We have a diff session--is the current program the one for the diff session?
|
||||
// If not, simply make the diff program the active program (this is useful for
|
||||
// users when they were diffing, but then opened a different program).
|
||||
//
|
||||
if (activateDiffProgram()) {
|
||||
// clicking the action will change the selected state--keep it selected
|
||||
setSelected(true);
|
||||
setDescription("Close Diff View");
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Otherwise, close the diff.
|
||||
//
|
||||
closeDiff();
|
||||
}
|
||||
|
||||
private void closeDiff() {
|
||||
int choice = OptionDialog.showYesNoCancelDialog(null, "Close Diff Session",
|
||||
"Close the current diff session?");
|
||||
if (choice == OptionDialog.YES_OPTION) {
|
||||
plugin.closeProgram2();
|
||||
setDescription("Open Diff View");
|
||||
}
|
||||
else {
|
||||
// clicking the action will change the selected state--keep it selected
|
||||
setSelected(true);
|
||||
setDescription("Close Diff View");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean activateDiffProgram() {
|
||||
|
||||
Program currentProgram = plugin.getCurrentProgram();
|
||||
Program firstProgram = plugin.getFirstProgram();
|
||||
boolean isFirstProgram = (firstProgram == currentProgram);
|
||||
if (isFirstProgram) {
|
||||
return false; // already active--nothing to do!
|
||||
}
|
||||
|
||||
plugin.activeProgram(firstProgram);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
icon = ResourceManager.loadImage(TOGGLE_VIEW_ICON_NAME);
|
||||
openCloseProgram2Action.setEnabled(false);
|
||||
openCloseProgram2Action.setKeyBindingData(
|
||||
new KeyBindingData('C', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
|
||||
openCloseProgram2Action.setToolBarData(new ToolBarData(icon, "zzz"));
|
||||
openCloseProgram2Action.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.DIFF, OPEN_CLOSE_PROGRAM2_ACTION));
|
||||
openCloseProgram2Action.setSelected(false);
|
||||
openCloseProgram2Action.setDescription("Open Diff View");
|
||||
}
|
||||
|
||||
private abstract class DiffAction extends DockingAction {
|
||||
DiffAction(String name) {
|
||||
super(name, plugin.getName());
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DIFF, name));
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
return (context instanceof OtherPanelContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.WindowPosition;
|
||||
import docking.widgets.VariableHeightPanel;
|
||||
import ghidra.app.plugin.core.diff.DiffApplySettingsOptionManager.*;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* The DiffSettingsDialog is used to change the types of differences currently
|
||||
* highlighted. It also allows the user to change the types of differences being
|
||||
* applied and whether labels and/or comments are being merged or replaced.
|
||||
*/
|
||||
public class DiffApplySettingsProvider extends ComponentProviderAdapter {
|
||||
|
||||
public static final String APPLY_FILTER_CHANGED_ACTION = "Apply Filter Changed";
|
||||
public static final ImageIcon ICON = ResourceManager.loadImage("images/settings16.gif");
|
||||
public static final String TITLE = "Diff Apply Settings";
|
||||
|
||||
private ProgramDiffPlugin plugin;
|
||||
|
||||
private ArrayList<Choice> choices;
|
||||
private Choice programContextCB;
|
||||
private Choice bytesCB;
|
||||
private Choice codeUnitsCB;
|
||||
private Choice refsCB;
|
||||
private SymbolsChoice symbolsCB;
|
||||
private Choice plateCommentsCB;
|
||||
private Choice preCommentsCB;
|
||||
private Choice eolCommentsCB;
|
||||
private Choice repeatableCommentsCB;
|
||||
private Choice postCommentsCB;
|
||||
private Choice bookmarksCB;
|
||||
private Choice propertiesCB;
|
||||
private Choice functionsCB;
|
||||
private Choice functionTagsCB;
|
||||
|
||||
private int applyProgramContext;
|
||||
private int applyBytes;
|
||||
private int applyCodeUnits;
|
||||
private int applyReferences;
|
||||
private int applyPlateComments;
|
||||
private int applyPreComments;
|
||||
private int applyEolComments;
|
||||
private int applyRepeatableComments;
|
||||
private int applyPostComments;
|
||||
private int applySymbols;
|
||||
private int applyBookmarks;
|
||||
private int applyProperties;
|
||||
private int applyFunctions;
|
||||
private int applyFunctionTags;
|
||||
private int replacePrimary;
|
||||
|
||||
private ProgramMergeFilter applyFilter;
|
||||
private boolean adjustingApplyFilter = false;
|
||||
private JComponent applyPanel;
|
||||
private ArrayList<ActionListener> listenerList = new ArrayList<>();
|
||||
private boolean pgmContextEnabled = true;
|
||||
|
||||
public DiffApplySettingsProvider(ProgramDiffPlugin plugin) {
|
||||
super(plugin.getTool(), TITLE, plugin.getName());
|
||||
this.plugin = plugin;
|
||||
setIcon(ICON);
|
||||
setWindowMenuGroup("Diff");
|
||||
setDefaultWindowPosition(WindowPosition.BOTTOM);
|
||||
|
||||
// transient so that is only there while the Diff is available
|
||||
setTransient();
|
||||
|
||||
setHelpLocation(new HelpLocation("Diff", "Diff_Apply_Settings"));
|
||||
applyPanel = createApplyFilterPanel();
|
||||
applyPanel.setName("Diff Apply Settings Panel");
|
||||
}
|
||||
|
||||
public void configure(ProgramMergeFilter apply) {
|
||||
setApplyFilter(apply);
|
||||
}
|
||||
|
||||
public void addActions() {
|
||||
plugin.getTool().addLocalAction(this,
|
||||
new SaveApplySettingsAction(this, plugin.applySettingsMgr));
|
||||
plugin.getTool().addLocalAction(this, new DiffIgnoreAllAction(this));
|
||||
plugin.getTool().addLocalAction(this, new DiffReplaceAllAction(this));
|
||||
plugin.getTool().addLocalAction(this, new DiffMergeAllAction(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the user choices to indicate the filter settings.
|
||||
*/
|
||||
private void createChoices() {
|
||||
choices = new ArrayList<>();
|
||||
programContextCB = new Choice("Program Context", false);
|
||||
programContextCB.addActionListener(e -> {
|
||||
applyProgramContext = programContextCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PROGRAM_CONTEXT, applyProgramContext);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(programContextCB);
|
||||
|
||||
bytesCB = new Choice("Bytes", false);
|
||||
bytesCB.addActionListener(e -> {
|
||||
applyBytes = bytesCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.BYTES, applyBytes);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(bytesCB);
|
||||
|
||||
codeUnitsCB = new Choice("Code Units", false);
|
||||
codeUnitsCB.addActionListener(e -> {
|
||||
applyCodeUnits = codeUnitsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.CODE_UNITS | ProgramMergeFilter.EQUATES,
|
||||
applyCodeUnits);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(codeUnitsCB);
|
||||
|
||||
refsCB = new Choice("References", false);
|
||||
refsCB.addActionListener(e -> {
|
||||
applyReferences = refsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.REFERENCES, applyReferences);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(refsCB);
|
||||
|
||||
plateCommentsCB = new Choice("Plate Comments", true);
|
||||
plateCommentsCB.addActionListener(e -> {
|
||||
applyPlateComments = plateCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PLATE_COMMENTS, applyPlateComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(plateCommentsCB);
|
||||
|
||||
preCommentsCB = new Choice("Pre Comments", true);
|
||||
preCommentsCB.addActionListener(e -> {
|
||||
applyPreComments = preCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PRE_COMMENTS, applyPreComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(preCommentsCB);
|
||||
|
||||
eolCommentsCB = new Choice("Eol Comments", true);
|
||||
eolCommentsCB.addActionListener(e -> {
|
||||
applyEolComments = eolCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.EOL_COMMENTS, applyEolComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(eolCommentsCB);
|
||||
|
||||
repeatableCommentsCB = new Choice("Repeatable Comments", true);
|
||||
repeatableCommentsCB.addActionListener(e -> {
|
||||
applyRepeatableComments = repeatableCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.REPEATABLE_COMMENTS, applyRepeatableComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(repeatableCommentsCB);
|
||||
|
||||
postCommentsCB = new Choice("Post Comments", true);
|
||||
postCommentsCB.addActionListener(e -> {
|
||||
applyPostComments = postCommentsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.POST_COMMENTS, applyPostComments);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(postCommentsCB);
|
||||
|
||||
symbolsCB = new SymbolsChoice();
|
||||
symbolsCB.addActionListener(e -> {
|
||||
SYMBOL_MERGE_CHOICE symbols =
|
||||
SYMBOL_MERGE_CHOICE.values()[symbolsCB.getSelectedIndex()];
|
||||
MERGE_CHOICE merge =
|
||||
plugin.applySettingsMgr.convertSymbolMergeChoiceToMergeChoice(symbols);
|
||||
REPLACE_CHOICE primary =
|
||||
plugin.applySettingsMgr.convertSymbolMergeChoiceToReplaceChoiceForPrimay(symbols);
|
||||
applySymbols = merge.ordinal();
|
||||
replacePrimary = primary.ordinal();
|
||||
applyFilter.setFilter(ProgramMergeFilter.SYMBOLS, applySymbols);
|
||||
applyFilter.setFilter(ProgramMergeFilter.PRIMARY_SYMBOL, replacePrimary);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(symbolsCB);
|
||||
|
||||
bookmarksCB = new Choice("Bookmarks", false);
|
||||
bookmarksCB.addActionListener(e -> {
|
||||
applyBookmarks = bookmarksCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.BOOKMARKS, applyBookmarks);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(bookmarksCB);
|
||||
|
||||
propertiesCB = new Choice("Properties", false);
|
||||
propertiesCB.addActionListener(e -> {
|
||||
applyProperties = propertiesCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.PROPERTIES, applyProperties);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(propertiesCB);
|
||||
|
||||
functionsCB = new Choice("Functions", false);
|
||||
functionsCB.addActionListener(e -> {
|
||||
applyFunctions = functionsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.FUNCTIONS, applyFunctions);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(functionsCB);
|
||||
|
||||
functionTagsCB = new Choice("Function Tags", true);
|
||||
functionTagsCB.addActionListener(e -> {
|
||||
applyFunctionTags = functionTagsCB.getSelectedIndex();
|
||||
applyFilter.setFilter(ProgramMergeFilter.FUNCTION_TAGS, applyFunctionTags);
|
||||
applyFilterChanged();
|
||||
});
|
||||
choices.add(functionTagsCB);
|
||||
|
||||
int maxLabelWidth = 0;
|
||||
int maxComboWidth = 0;
|
||||
for (Choice choice : choices) {
|
||||
maxLabelWidth = Math.max(maxLabelWidth, choice.label.getPreferredSize().width);
|
||||
maxComboWidth = Math.max(maxComboWidth, choice.applyCB.getPreferredSize().width);
|
||||
}
|
||||
for (Choice choice : choices) {
|
||||
int height = choice.label.getPreferredSize().height;
|
||||
choice.label.setPreferredSize(new Dimension(maxLabelWidth, height));
|
||||
height = choice.applyCB.getPreferredSize().height;
|
||||
choice.applyCB.setPreferredSize(new Dimension(maxComboWidth, height));
|
||||
}
|
||||
Collections.sort(choices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the checkboxes to indicate the filter settings.
|
||||
*/
|
||||
private JComponent createApplyFilterPanel() {
|
||||
createChoices();
|
||||
VariableHeightPanel panel = new VariableHeightPanel(false, 10, 3);
|
||||
|
||||
panel.setToolTipText(
|
||||
"<HTML>" + "For each difference type, select whether to ignore, replace, or merge." +
|
||||
"<BR>  <B>Ignore</B> - don't apply this type of difference." +
|
||||
"<BR>  <B>Replace</B> - replace the difference type with the one from program 2." +
|
||||
"<BR>  <B>Merge</B> - merge the difference type from program 2 with what's there." +
|
||||
"</HTML>");
|
||||
for (Choice choice : choices) {
|
||||
panel.add(choice);
|
||||
}
|
||||
|
||||
return new JScrollPane(panel);
|
||||
}
|
||||
|
||||
private void adjustApplyFilter() {
|
||||
try {
|
||||
adjustingApplyFilter = true;
|
||||
programContextCB.setSelectedIndex(applyProgramContext);
|
||||
bytesCB.setSelectedIndex(applyBytes);
|
||||
codeUnitsCB.setSelectedIndex(applyCodeUnits);
|
||||
refsCB.setSelectedIndex(applyReferences);
|
||||
plateCommentsCB.setSelectedIndex(applyPlateComments);
|
||||
preCommentsCB.setSelectedIndex(applyPreComments);
|
||||
eolCommentsCB.setSelectedIndex(applyEolComments);
|
||||
repeatableCommentsCB.setSelectedIndex(applyRepeatableComments);
|
||||
postCommentsCB.setSelectedIndex(applyPostComments);
|
||||
int symbolsIndex =
|
||||
plugin.applySettingsMgr.convertFiltersToSymbolIndex(applySymbols, replacePrimary);
|
||||
symbolsCB.setSelectedIndex(symbolsIndex);
|
||||
bookmarksCB.setSelectedIndex(applyBookmarks);
|
||||
propertiesCB.setSelectedIndex(applyProperties);
|
||||
functionsCB.setSelectedIndex(applyFunctions);
|
||||
functionTagsCB.setSelectedIndex(applyFunctionTags);
|
||||
}
|
||||
finally {
|
||||
adjustingApplyFilter = false;
|
||||
applyFilterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void setPgmContextEnabled(boolean enable) {
|
||||
pgmContextEnabled = enable;
|
||||
if (!pgmContextEnabled) {
|
||||
applyProgramContext = 0;
|
||||
programContextCB.setSelectedIndex(applyProgramContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the merge filter for applying differences.
|
||||
* @return the current merge Filter settings.
|
||||
*/
|
||||
ProgramMergeFilter getApplyFilter() {
|
||||
return new ProgramMergeFilter(applyFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the diff tool merge filter settings for applying differences.
|
||||
* @param filter the new apply Filter settings.
|
||||
*/
|
||||
void setApplyFilter(ProgramMergeFilter filter) {
|
||||
applyFilter = new ProgramMergeFilter(filter);
|
||||
applyProgramContext = applyFilter.getFilter(ProgramMergeFilter.PROGRAM_CONTEXT);
|
||||
applyBytes = applyFilter.getFilter(ProgramMergeFilter.BYTES);
|
||||
applyCodeUnits = Math.max(applyFilter.getFilter(ProgramMergeFilter.INSTRUCTIONS),
|
||||
applyFilter.getFilter(ProgramMergeFilter.DATA));
|
||||
applyFilter.setFilter(ProgramMergeFilter.CODE_UNITS, applyCodeUnits);
|
||||
applyReferences = applyFilter.getFilter(ProgramMergeFilter.REFERENCES);
|
||||
applyPlateComments = applyFilter.getFilter(ProgramMergeFilter.PLATE_COMMENTS);
|
||||
applyPreComments = applyFilter.getFilter(ProgramMergeFilter.PRE_COMMENTS);
|
||||
applyEolComments = applyFilter.getFilter(ProgramMergeFilter.EOL_COMMENTS);
|
||||
applyRepeatableComments = applyFilter.getFilter(ProgramMergeFilter.REPEATABLE_COMMENTS);
|
||||
applyPostComments = applyFilter.getFilter(ProgramMergeFilter.POST_COMMENTS);
|
||||
applySymbols = applyFilter.getFilter(ProgramMergeFilter.SYMBOLS);
|
||||
applyBookmarks = applyFilter.getFilter(ProgramMergeFilter.BOOKMARKS);
|
||||
applyProperties = applyFilter.getFilter(ProgramMergeFilter.PROPERTIES);
|
||||
applyFunctions = applyFilter.getFilter(ProgramMergeFilter.FUNCTIONS);
|
||||
applyFunctionTags = applyFilter.getFilter(ProgramMergeFilter.FUNCTION_TAGS);
|
||||
replacePrimary = applyFilter.getFilter(ProgramMergeFilter.PRIMARY_SYMBOL);
|
||||
|
||||
adjustApplyFilter();
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener listener) {
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if at least one of the checkboxes for the filter
|
||||
* has been selected.
|
||||
*/
|
||||
boolean hasApplySelection() {
|
||||
return ((applyProgramContext | applyBytes | applyCodeUnits | applyReferences |
|
||||
applyPlateComments | applyPreComments | applyEolComments | applyRepeatableComments |
|
||||
applyPostComments | applySymbols | applyBookmarks | applyProperties | applyFunctions |
|
||||
applyFunctionTags) != 0);
|
||||
}
|
||||
|
||||
protected void applyFilterChanged() {
|
||||
if (adjustingApplyFilter) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
ActionListener listener = listenerList.get(i);
|
||||
listener.actionPerformed(new ActionEvent(this, 0, APPLY_FILTER_CHANGED_ACTION));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeComponent() {
|
||||
// overridden to not remove this transient provider
|
||||
plugin.getTool().showComponentProvider(this, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return applyPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin associated with this provider.
|
||||
*/
|
||||
Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
class Choice extends JPanel implements Comparable<Choice> {
|
||||
private final static long serialVersionUID = 1L;
|
||||
String type;
|
||||
boolean allowMerge;
|
||||
JLabel label;
|
||||
JComboBox<Enum<?>> applyCB;
|
||||
|
||||
public Choice(String type, boolean allowMerge) {
|
||||
setLayout(new BorderLayout());
|
||||
this.type = type;
|
||||
this.allowMerge = allowMerge;
|
||||
init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
applyCB =
|
||||
new JComboBox<>(allowMerge ? DiffApplySettingsOptionManager.MERGE_CHOICE.values()
|
||||
: DiffApplySettingsOptionManager.REPLACE_CHOICE.values());
|
||||
applyCB.setName(type + " Diff Apply CB");
|
||||
String typeName = type;
|
||||
if (typeName.endsWith(" Comments")) {
|
||||
typeName = "Comments, " + typeName.substring(0, typeName.length() - 9);
|
||||
}
|
||||
label = new JLabel(" " + typeName + " ");
|
||||
label.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
add(applyCB, BorderLayout.EAST);
|
||||
add(label, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
void setSelectedIndex(int index) {
|
||||
applyCB.setSelectedIndex(index);
|
||||
}
|
||||
|
||||
int getSelectedIndex() {
|
||||
return applyCB.getSelectedIndex();
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener listener) {
|
||||
applyCB.addActionListener(listener);
|
||||
}
|
||||
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
applyCB.removeActionListener(listener);
|
||||
}
|
||||
|
||||
void setLabelSize(Dimension preferredSize) {
|
||||
label.setPreferredSize(preferredSize);
|
||||
}
|
||||
|
||||
void setComboSize(Dimension preferredSize) {
|
||||
applyCB.setPreferredSize(preferredSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Choice o) {
|
||||
return label.toString().compareTo(o.label.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SymbolsChoice extends Choice {
|
||||
private final static long serialVersionUID = 1L;
|
||||
|
||||
public SymbolsChoice() {
|
||||
super("Labels", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
applyCB = new JComboBox<>(DiffApplySettingsOptionManager.SYMBOL_MERGE_CHOICE.values());
|
||||
applyCB.setName(type + " Diff Apply CB");
|
||||
label = new JLabel(" " + type + " ");
|
||||
label.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
add(applyCB, BorderLayout.EAST);
|
||||
add(label, BorderLayout.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.diff;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* DiffController controls a program Diff. It maintains address sets indicating
|
||||
* the differences between two programs. It can limit the determined differences
|
||||
* to an address set. It allows differences to be applied or ignored. It has a
|
||||
* diff filter that controls the differences being indicated. It has a merge
|
||||
* filter that controls the types of differences being applied. It allows
|
||||
* differences at particular addresses to be ignored.
|
||||
*
|
||||
* The Diff controller also maintains the current location. It provides a way
|
||||
* to navigate from one difference to the next or previous difference.
|
||||
*/
|
||||
public class DiffController {
|
||||
private ProgramMergeManager mergeEngine;
|
||||
private AddressSetView p1LastDiffs;
|
||||
private AddressSetView p1LimitSet;
|
||||
private Address p1CurrentAddress;
|
||||
private ArrayList<DiffControllerListener> listenerList =
|
||||
new ArrayList<DiffControllerListener>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* <P>Note: This method is potentially time consuming and should normally
|
||||
* be called from within a background task.
|
||||
* @param p1 The first program to apply differences to.
|
||||
* @param p2 The second program to apply differences from.
|
||||
* @param p1LimitSet Address set the determined differences are limited to.
|
||||
* The addresses in this set should be derived from p1.
|
||||
* @param diffFilter filter indicating difference types to mark.
|
||||
* @param mergeFilter filter indicating difference types to apply.
|
||||
* @param monitor the progress monitor
|
||||
* @throws ProgramConflictException
|
||||
*/
|
||||
public DiffController(Program p1, Program p2, AddressSetView p1LimitSet,
|
||||
ProgramDiffFilter diffFilter, ProgramMergeFilter mergeFilter, TaskMonitor monitor)
|
||||
throws ProgramConflictException {
|
||||
mergeEngine = new ProgramMergeManager(p1, p2, p1LimitSet, monitor);
|
||||
mergeEngine.setDiffFilter(diffFilter);
|
||||
mergeEngine.setMergeFilter(mergeFilter);
|
||||
this.p1LimitSet = p1LimitSet;
|
||||
if (p1LimitSet == null) {
|
||||
p1CurrentAddress = p1.getMinAddress();
|
||||
}
|
||||
else {
|
||||
p1CurrentAddress = p1LimitSet.getMinAddress();
|
||||
}
|
||||
p1LastDiffs = new AddressSet();
|
||||
}
|
||||
|
||||
/** Gets the first program being compared by the ProgramDiff.
|
||||
* @return program1. the program to apply differences to.
|
||||
*/
|
||||
Program getProgramOne() {
|
||||
return mergeEngine.getProgramOne();
|
||||
}
|
||||
|
||||
/** Gets the second program being compared by the ProgramDiff.
|
||||
* @return program2. the program to get differences from for applying.
|
||||
*/
|
||||
Program getProgramTwo() {
|
||||
return mergeEngine.getProgramTwo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set the current Program Diff is limited to.
|
||||
* @return the address set the current diff is limited to.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
AddressSetView getLimitedAddressSet() {
|
||||
return p1LimitSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set indicating the addresses currently being ignored.
|
||||
* @return the address set indicating the addresses currently being ignored.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
AddressSetView getIgnoredAddressSet() {
|
||||
return mergeEngine.getIgnoreAddressSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set being used to restrict the resulting difference set
|
||||
* that is reported by getting the differences. This address set is
|
||||
* effectively a view port into the differences address set.
|
||||
* @return the address set used to restrict the differences.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
AddressSetView getRestrictedAddressSet() {
|
||||
return mergeEngine.getRestrictedAddressSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the diff filter that the merge is using.
|
||||
*/
|
||||
public ProgramDiffFilter getDiffFilter() {
|
||||
return mergeEngine.getDiffFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter that indicates which parts of the Program should be
|
||||
* diffed.
|
||||
*/
|
||||
public void setDiffFilter(ProgramDiffFilter filter) {
|
||||
mergeEngine.setDiffFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the filter that indicates which parts of the Program
|
||||
* should be merged.
|
||||
*/
|
||||
public ProgramMergeFilter getMergeFilter() {
|
||||
return mergeEngine.getMergeFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter that indicates which parts of the Program should be
|
||||
* merged.
|
||||
*/
|
||||
public void setMergeFilter(ProgramMergeFilter filter) {
|
||||
mergeEngine.setMergeFilter(filter);
|
||||
}
|
||||
|
||||
/** Gets the filtered program differences for this merge. Only differences are
|
||||
* indicated for merge filter categories that are enabled and for addresses
|
||||
* that have not been marked as ignored.
|
||||
* <P>Note: This method is potentially time consuming and should normally
|
||||
* be called from within a background task.
|
||||
* @param monitor the task monitor for indicating the progress of
|
||||
* determining differences. This monitor also allows the user to cancel if
|
||||
* the diff takes too long. If no monitor is desired, use null.
|
||||
* @return the program differences.
|
||||
* The addresses in this set are derived from the p1 program.
|
||||
*/
|
||||
public AddressSetView getFilteredDifferences(TaskMonitor monitor) throws CancelledException {
|
||||
AddressSetView diffs1 = mergeEngine.getFilteredDifferences(monitor);
|
||||
Program program1 = getProgramOne();
|
||||
Program program2 = getProgramTwo();
|
||||
monitor.setMessage("Adjusting differences to code unit boundaries...");
|
||||
AddressSet diffSet2 = DiffUtility.getCompatibleAddressSet(diffs1, program2);
|
||||
AddressSet diffCuSet2 = DiffUtility.getCodeUnitSet(diffSet2, program2);
|
||||
monitor.setMessage("Converting Diffs to program 1 set...");
|
||||
diffs1 = DiffUtility.getCompatibleAddressSet(diffCuSet2, program1);
|
||||
if (!p1LastDiffs.equals(diffs1)) {
|
||||
p1LastDiffs = diffs1;
|
||||
}
|
||||
return diffs1;
|
||||
}
|
||||
|
||||
/** Restrict the resulting differences to the indicated address set.
|
||||
* @param p1AddressSet the address set to restrict the getFilteredDifferences() to.
|
||||
* The addresses in this set should be derived from the p1 program.
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* differences due to the restriction.
|
||||
*/
|
||||
public void restrictResults(AddressSetView p1AddressSet, TaskMonitor monitor) {
|
||||
mergeEngine.restrictResults(p1AddressSet);
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
/** Remove the restriction for the resulting differences to the indicated address set.
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* differences due to the removal of the restriction.
|
||||
*/
|
||||
public void removeResultRestrictions(TaskMonitor monitor) {
|
||||
mergeEngine.removeResultRestrictions();
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply differences in the address set from program p2
|
||||
* into the current program p1.
|
||||
* @param p1AddressSet address set of differences
|
||||
* The addresses in this set should be derived from the p1 program.
|
||||
* @param filter merge filter
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* differences due to the removal of the restriction.
|
||||
* @throws MemoryAccessException
|
||||
* @throws CancelledException if user cancels via the monitor.
|
||||
*/
|
||||
boolean apply(AddressSetView p1AddressSet, TaskMonitor monitor) throws MemoryAccessException,
|
||||
CancelledException {
|
||||
boolean applied = mergeEngine.merge(p1AddressSet, monitor);
|
||||
return applied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any error and information messages associated with the last apply.
|
||||
*/
|
||||
String getApplyMessage() {
|
||||
return mergeEngine.getErrorMessage() + mergeEngine.getInfoMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore any differences in the specified address set.
|
||||
* @param p1AddressSet address set set of differences
|
||||
* The addresses in this set should be derived from the p1 program.
|
||||
*/
|
||||
void ignore(AddressSetView p1AddressSet, TaskMonitor monitor) {
|
||||
mergeEngine.ignore(p1AddressSet);
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any warning messages associated with the initial Diff of the two programs.
|
||||
*/
|
||||
public String getWarnings() {
|
||||
return mergeEngine.getWarnings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address for the diff controller's current location.
|
||||
* @return the current address.
|
||||
* This address is derived from the p1 program.
|
||||
*/
|
||||
Address getCurrentAddress() {
|
||||
return p1CurrentAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the given address; update the last address so that
|
||||
* iterator will be adjusted for next and previous.
|
||||
* @param p1Address address to go to
|
||||
* This address should be derived from the p1 program.
|
||||
*/
|
||||
private void goTo(Address p1Address) {
|
||||
p1CurrentAddress = p1Address;
|
||||
locationChanged(p1CurrentAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Diff controller's current address to the specified address.
|
||||
* @param p1NewAddress the address
|
||||
* This address should be derived from the p1 program.
|
||||
*/
|
||||
void setLocation(Address p1NewAddress) {
|
||||
if (p1NewAddress.equals(p1CurrentAddress))
|
||||
return;
|
||||
p1CurrentAddress = p1NewAddress;
|
||||
locationChanged(p1CurrentAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the dialog when the cursor should move to the first difference.
|
||||
* Update the buttons in the dialog according to whether there is
|
||||
* a next or previous.
|
||||
*/
|
||||
void first() {
|
||||
if (p1LastDiffs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
goTo(p1LastDiffs.getMinAddress());
|
||||
}
|
||||
|
||||
boolean hasNext() {
|
||||
return getNextAddress() != null;
|
||||
}
|
||||
|
||||
private Address getNextAddress() {
|
||||
AddressRangeIterator it = p1LastDiffs.getAddressRanges(p1CurrentAddress, true);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange range = it.next();
|
||||
if (range.contains(p1CurrentAddress)) {
|
||||
if (it.hasNext()) {
|
||||
return it.next().getMinAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return range.getMinAddress();
|
||||
|
||||
}
|
||||
|
||||
private Address getPreviousAddress() {
|
||||
AddressRangeIterator it = p1LastDiffs.getAddressRanges(p1CurrentAddress, false);
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange range = it.next();
|
||||
if (range.getMinAddress().equals(p1CurrentAddress)) {
|
||||
if (it.hasNext()) {
|
||||
return it.next().getMinAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return range.getMinAddress();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the dialog when the "next diff" button is hit.
|
||||
* Update the buttons in the dialog according to whether there is
|
||||
* a next or previous.
|
||||
*/
|
||||
void next() {
|
||||
Address nextAddress = getNextAddress();
|
||||
if (nextAddress != null) {
|
||||
goTo(nextAddress);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasPrevious() {
|
||||
return getPreviousAddress() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the dialog when the "previous diff" button is hit.
|
||||
* Update the buttons in the dialog according to whether there is
|
||||
* a next or previous.
|
||||
*/
|
||||
void previous() {
|
||||
Address previousAddress = getPreviousAddress();
|
||||
if (previousAddress != null) {
|
||||
goTo(previousAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the differences to show what is still different between the two
|
||||
* programs. After calling this method, any differences that were being
|
||||
* ignored are still being ignored. The differences are restricted to the
|
||||
* same address set as before the refresh.
|
||||
* @param monitor the task monitor for canceling the fix up of the
|
||||
* recompute of the differences.
|
||||
* @throws ProgramConflictException
|
||||
*/
|
||||
void refresh(boolean keepIgnored, TaskMonitor monitor) throws ProgramConflictException {
|
||||
AddressSetView ignoreSet = getIgnoredAddressSet();
|
||||
recomputeDiffs(monitor);
|
||||
if (keepIgnored) {
|
||||
mergeEngine.ignore(ignoreSet);
|
||||
}
|
||||
differencesChanged(monitor);
|
||||
}
|
||||
|
||||
private void recomputeDiffs(TaskMonitor monitor) throws ProgramConflictException {
|
||||
recomputeDiffs(getLimitedAddressSet(), monitor);
|
||||
}
|
||||
|
||||
private void recomputeDiffs(AddressSetView newLimitSet, TaskMonitor monitor)
|
||||
throws ProgramConflictException {
|
||||
Program p1 = mergeEngine.getProgramOne();
|
||||
Program p2 = mergeEngine.getProgramTwo();
|
||||
ProgramDiffFilter diffFilter = mergeEngine.getDiffFilter();
|
||||
ProgramMergeFilter mergeFilter = mergeEngine.getMergeFilter();
|
||||
this.p1LimitSet = newLimitSet;
|
||||
|
||||
mergeEngine = new ProgramMergeManager(p1, p2, newLimitSet, monitor);
|
||||
mergeEngine.setDiffFilter(diffFilter);
|
||||
mergeEngine.setMergeFilter(mergeFilter);
|
||||
}
|
||||
|
||||
public void addDiffControllerListener(DiffControllerListener listener) {
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
public void removeDiffControllerListener(DiffControllerListener listener) {
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
|
||||
public void locationChanged(Address program1Location) {
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
DiffControllerListener listener = listenerList.get(i);
|
||||
listener.diffLocationChanged(this, program1Location);
|
||||
}
|
||||
}
|
||||
|
||||
public void differencesChanged(TaskMonitor monitor) {
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
DiffControllerListener listener = listenerList.get(i);
|
||||
listener.differencesChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.diff;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface DiffControllerListener {
|
||||
|
||||
public abstract void diffLocationChanged(DiffController diffControl,
|
||||
Address location);
|
||||
|
||||
public abstract void differencesChanged(DiffController diffControl);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* The DiffDetailsProvider is used to view the differences for an address or
|
||||
* code unit address range.
|
||||
*/
|
||||
public class DiffDetailsProvider extends ComponentProviderAdapter {
|
||||
public static final String DIFF_DETAILS_HIDDEN_ACTION = "Diff Details Hidden";
|
||||
public static final String AUTO_UPDATE_CHECK_BOX = "Auto Update Check Box";
|
||||
public static final String FILTER_DIFFS_CHECK_BOX = "Filter Diffs Check Box";
|
||||
public static final String DIFF_DETAILS_TEXT_AREA = "Diff Details Text Area";
|
||||
public static final String DIFF_DETAILS_PANEL = "Diff Location Details Panel";
|
||||
public static final ImageIcon ICON = ResourceManager.loadImage("images/xmag.png");
|
||||
public static final ImageIcon REFRESH_ICON = Icons.REFRESH_ICON;
|
||||
public static final String TITLE = "Diff Details";
|
||||
|
||||
private ProgramDiffPlugin plugin;
|
||||
private JTextPane textPane;
|
||||
private StyledDocument doc;
|
||||
private JCheckBox filterDiffsCB;
|
||||
private JCheckBox autoUpdateCB;
|
||||
private boolean filterDiffs = false;
|
||||
private boolean autoUpdate = false;
|
||||
private JComponent detailsPanel;
|
||||
private ArrayList<ActionListener> listenerList = new ArrayList<>();
|
||||
private ProgramLocation p1DetailsLocation;
|
||||
private AddressSetView detailsAddrSet;
|
||||
private boolean isDisplayed = false;
|
||||
|
||||
private SwingUpdateManager updateManager;
|
||||
private ProgramLocation currentLocation;
|
||||
|
||||
/**
|
||||
* @param plugin
|
||||
*/
|
||||
public DiffDetailsProvider(ProgramDiffPlugin plugin) {
|
||||
super(plugin.getTool(), "Diff Location Details", plugin.getName());
|
||||
setTitle(TITLE);
|
||||
this.plugin = plugin;
|
||||
setIcon(ICON);
|
||||
setTransient();
|
||||
setWindowMenuGroup("Diff");
|
||||
setDefaultWindowPosition(WindowPosition.BOTTOM);
|
||||
setHelpLocation(new HelpLocation("Diff", "Diff_Location_Details"));
|
||||
detailsPanel = createDiffDetailsPanel();
|
||||
detailsPanel.setName(DIFF_DETAILS_PANEL);
|
||||
setAutoUpdate(true);
|
||||
setFilterDiffs(false);
|
||||
setUpRefreshDetailsUpdateManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selected
|
||||
*/
|
||||
public void setAutoUpdate(boolean selected) {
|
||||
autoUpdateCB.setSelected(selected);
|
||||
autoUpdate = selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selected
|
||||
*/
|
||||
public void setFilterDiffs(boolean selected) {
|
||||
filterDiffsCB.setSelected(selected);
|
||||
filterDiffs = selected;
|
||||
}
|
||||
|
||||
public void addActions() {
|
||||
DockingAction refreshDetailsAction =
|
||||
new DockingAction("Refresh Diff Details", plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
refreshDetails(plugin.getCurrentLocation());
|
||||
}
|
||||
};
|
||||
|
||||
refreshDetailsAction.setDescription(
|
||||
"Refresh the details to show any differences at the current location.");
|
||||
refreshDetailsAction.setEnabled(true);
|
||||
|
||||
refreshDetailsAction.setToolBarData(new ToolBarData(REFRESH_ICON, "Diff"));
|
||||
refreshDetailsAction.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.DIFF, "Refresh Diff Details"));
|
||||
plugin.getTool().addLocalAction(this, refreshDetailsAction);
|
||||
// plugin.getTool().addLocalAction(this, new DiffIgnoreAllAction(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the automatically update checkbox for diff details.
|
||||
*/
|
||||
private void createAutoUpdateCheckBox() {
|
||||
|
||||
autoUpdateCB = new JCheckBox("Automatically Update Details", false);
|
||||
autoUpdateCB.setName(AUTO_UPDATE_CHECK_BOX);
|
||||
autoUpdateCB.addActionListener(e -> {
|
||||
autoUpdate = autoUpdateCB.isSelected();
|
||||
if (autoUpdate) {
|
||||
refreshDetails(plugin.getCurrentLocation());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the only show the filtered differences checkbox for diff details
|
||||
*/
|
||||
private void createFilterDiffsCheckBox() {
|
||||
|
||||
filterDiffsCB = new JCheckBox("Only Show Expected Difference Types", false);
|
||||
filterDiffsCB.setName(FILTER_DIFFS_CHECK_BOX);
|
||||
filterDiffsCB.addActionListener(e -> {
|
||||
filterDiffs = filterDiffsCB.isSelected();
|
||||
if (autoUpdateCB.isSelected()) {
|
||||
refreshDetails(plugin.getCurrentLocation());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param p1Location
|
||||
*/
|
||||
protected void locationChanged(ProgramLocation p1Location) {
|
||||
if (isDisplayed && autoUpdate) {
|
||||
refreshDetails(p1Location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the displayed Diff Details for the indicated program location address.
|
||||
* @param p1Location This should be a program1 location.
|
||||
*/
|
||||
void refreshDetails(ProgramLocation p1Location) {
|
||||
if (p1Location == null) {
|
||||
return;
|
||||
}
|
||||
currentLocation = p1Location;
|
||||
updateManager.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a swing update manager that is used to refresh the DiffDetails.
|
||||
*/
|
||||
private void setUpRefreshDetailsUpdateManager() {
|
||||
updateManager = new SwingUpdateManager(100, 4000, () -> doRefreshDetails(currentLocation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the displayed Diff Details for the indicated program location address.
|
||||
* @param p1Location This should be a program1 location.
|
||||
*/
|
||||
private void doRefreshDetails(ProgramLocation p1Location) {
|
||||
if (p1Location == null) {
|
||||
return;
|
||||
}
|
||||
// Do nothing if not visible.
|
||||
if (!isDisplayed) {
|
||||
return;
|
||||
}
|
||||
|
||||
Program p1 = p1Location.getProgram();
|
||||
if (p1 != plugin.getFirstProgram() && p1 != plugin.getSecondProgram()) {
|
||||
return; // If this location isn't for our Diff then ignore it. May be switching program tabs.
|
||||
}
|
||||
if (p1DetailsLocation != p1Location) {
|
||||
p1DetailsLocation = p1Location;
|
||||
}
|
||||
AddressSetView newAddrSet = getDetailsAddressSet(p1DetailsLocation);
|
||||
|
||||
if (newAddrSet == null) {
|
||||
setDocumentToErrorMessage("Must have a second program open to determine Diff details.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newAddrSet.equals(detailsAddrSet)) {
|
||||
detailsAddrSet = newAddrSet;
|
||||
}
|
||||
Address p1Address = p1DetailsLocation.getAddress();
|
||||
try {
|
||||
if (filterDiffs) {
|
||||
getFilteredDiffDetails(p1Address);
|
||||
}
|
||||
else {
|
||||
getDiffDetails(p1Address);
|
||||
}
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
setDocumentToErrorMessage(
|
||||
"Failed to determine Diff Details due to concurrent program changes.\n" +
|
||||
"This may be caused by background analysis activity.\n" +
|
||||
" *** Press the Refresh button to update *** ");
|
||||
|
||||
// The program is being modified while this is trying to get the details.
|
||||
// If we want to automatically try again, this would need to re-issue the update
|
||||
// by calling updateManager.updateLater() here.
|
||||
}
|
||||
}
|
||||
|
||||
private void setDocumentToErrorMessage(String message) {
|
||||
try {
|
||||
doc.remove(0, doc.getLength());
|
||||
doc.insertString(0, message, new SimpleAttributeSet());
|
||||
textPane.setCaretPosition(0);
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Diff Details at the indicated address
|
||||
* @param p1Address the program1 address
|
||||
* @throws ConcurrentModificationException if analysis is modifying the program.
|
||||
*/
|
||||
private void getDiffDetails(Address p1Address) {
|
||||
plugin.addDiffDetails(p1Address, doc);
|
||||
textPane.setCaretPosition(0);
|
||||
}
|
||||
|
||||
private void getFilteredDiffDetails(Address p1Address) {
|
||||
plugin.addFilteredDiffDetails(p1Address, plugin.getDiffController().getDiffFilter(), doc);
|
||||
textPane.setCaretPosition(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address set where detailed differences will be determined for details at the
|
||||
* indicated address. An address set is returned since the indicated address may be in different
|
||||
* sized code units in each of the two programs.
|
||||
* @param p1Location the program location in program1 where details are desired.
|
||||
* @return the address set for code units containing that address within the programs being
|
||||
* compared to determine differences.
|
||||
* Otherwise null if a diff of two programs isn't being performed.
|
||||
*/
|
||||
private AddressSetView getDetailsAddressSet(ProgramLocation p1Location) {
|
||||
Address p1Address = p1Location.getAddress();
|
||||
return plugin.getDetailsAddressSet(p1Address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a panel for the Diff details and auto update checkbox.
|
||||
*/
|
||||
private JPanel createDiffDetailsPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setPreferredSize(new Dimension(600, 400));
|
||||
JScrollPane scrolledDetails = createDetailsPane();
|
||||
panel.add(scrolledDetails, BorderLayout.CENTER);
|
||||
createAutoUpdateCheckBox();
|
||||
createFilterDiffsCheckBox();
|
||||
JPanel bottomPanel = new JPanel();
|
||||
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
|
||||
bottomPanel.add(Box.createHorizontalGlue());
|
||||
bottomPanel.add(autoUpdateCB);
|
||||
bottomPanel.add(Box.createHorizontalStrut(10));
|
||||
bottomPanel.add(filterDiffsCB);
|
||||
bottomPanel.add(Box.createHorizontalGlue());
|
||||
panel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JScrollPane createDetailsPane() {
|
||||
Font font = new Font("Monospaced", Font.PLAIN, 12);
|
||||
textPane = new JTextPane();
|
||||
doc = textPane.getStyledDocument();
|
||||
textPane.setName(DIFF_DETAILS_TEXT_AREA);
|
||||
textPane.setEditable(false);
|
||||
textPane.setMargin(new Insets(5, 5, 5, 5));
|
||||
textPane.setFont(font);
|
||||
textPane.setOpaque(true);
|
||||
textPane.setCaretPosition(0);
|
||||
JScrollPane scrolledDetails = new JScrollPane(textPane);
|
||||
JViewport vp = scrolledDetails.getViewport();
|
||||
vp.add(textPane);
|
||||
return scrolledDetails;
|
||||
}
|
||||
|
||||
StyledDocument getStyledDocument() {
|
||||
return doc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
for (int i = 0; i < listenerList.size(); i++) {
|
||||
ActionListener listener = listenerList.get(i);
|
||||
listener.actionPerformed(new ActionEvent(this, 0, DIFF_DETAILS_HIDDEN_ACTION));
|
||||
}
|
||||
isDisplayed = false;
|
||||
p1DetailsLocation = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown() {
|
||||
isDisplayed = true;
|
||||
refreshDetails(plugin.getProgramLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeComponent() {
|
||||
// overridden to not remove this transient provider
|
||||
plugin.getTool().showComponentProvider(this, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return detailsPanel;
|
||||
}
|
||||
|
||||
Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener listener) {
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.gotoquery.GoToHelper;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.ExternalLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A service that provides the ability to go to a particular address or location in the
|
||||
* right-hand listing of the Diff.
|
||||
*/
|
||||
public class DiffGoToService implements GoToService {
|
||||
|
||||
private GoToService goToService;
|
||||
private ProgramDiffPlugin diffPlugin;
|
||||
private GoToHelper helper;
|
||||
|
||||
/**
|
||||
* Creates a service that provides the ability to go to a particular address or location
|
||||
* in the right-hand listing of the Diff.
|
||||
* @param goToService basic GoToService for the left-side listing that this will override
|
||||
* so it can go to addresses and locations in the right-side listing.
|
||||
* @param diffPlugin the plugin which provides the Diff capability.
|
||||
*/
|
||||
public DiffGoToService(GoToService goToService, ProgramDiffPlugin diffPlugin) {
|
||||
this.goToService = goToService;
|
||||
this.diffPlugin = diffPlugin;
|
||||
helper = new GoToHelper(diffPlugin.getTool());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoToOverrideService getOverrideService() {
|
||||
return goToService.getOverrideService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(ProgramLocation loc) {
|
||||
return diffGoTo(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Navigatable navigatable, Program program, Address address,
|
||||
Address refAddress) {
|
||||
ProgramLocation location = helper.getLocation(program, refAddress, address);
|
||||
return goTo(navigatable, location, program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(ProgramLocation loc, Program program) {
|
||||
if (program == null || program == diffPlugin.getSecondProgram()) {
|
||||
return diffGoTo(loc);
|
||||
}
|
||||
showProgramFailureStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Navigatable navigatable, ProgramLocation loc, Program program) {
|
||||
if (loc == null || loc.getAddress() == null) {
|
||||
return false;
|
||||
}
|
||||
if (program == null) {
|
||||
program = navigatable.getProgram();
|
||||
}
|
||||
if (program == null) {
|
||||
return false;
|
||||
}
|
||||
if (program == diffPlugin.getSecondProgram()) {
|
||||
return diffGoTo(loc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Navigatable navigatable, Address goToAddress) {
|
||||
if (goToAddress == null) {
|
||||
return false;
|
||||
}
|
||||
if (navigatable == null) {
|
||||
return diffGoTo(goToAddress);
|
||||
}
|
||||
|
||||
Program program = navigatable.getProgram();
|
||||
if (program != null) {
|
||||
Memory memory = program.getMemory();
|
||||
if (!memory.contains(goToAddress)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return goTo(goToAddress, program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Address currentAddress, Address goToAddress) {
|
||||
if (diffGoTo(goToAddress)) {
|
||||
return true;
|
||||
}
|
||||
return goToService.goTo(currentAddress, goToAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Address goToAddress) {
|
||||
return diffGoTo(goToAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Address goToAddress, Program program) {
|
||||
if (program == null || program == diffPlugin.getSecondProgram()) {
|
||||
return diffGoTo(goToAddress);
|
||||
}
|
||||
showProgramFailureStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToExternalLocation(ExternalLocation extLoc, boolean checkNavigationOption) {
|
||||
showProgramFailureStatus();
|
||||
return false; // Can only go to locations in the Diff's second program.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
|
||||
TaskMonitor monitor) {
|
||||
// Does this need to do something different here? Maybe if Diff becomes searchable?
|
||||
return goToService.goToQuery(fromAddr, queryData, listener, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Navigatable navigatable, Address fromAddr, QueryData queryData,
|
||||
GoToServiceListener listener, TaskMonitor monitor) {
|
||||
// Does this need to do something different here? Maybe if Diff becomes searchable?
|
||||
return goToService.goToQuery(navigatable, fromAddr, queryData, listener, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverrideService(GoToOverrideService override) {
|
||||
// Do nothing. (May need to change this later if there is reason to override Diff.)
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigatable getDefaultNavigatable() {
|
||||
return goToService.getDefaultNavigatable();
|
||||
}
|
||||
|
||||
private void showProgramFailureStatus() {
|
||||
diffPlugin.getTool().setStatusInfo(
|
||||
"Can't navigate from the Diff program to another program.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the specified program location in the right hand Diff listing.
|
||||
* @param loc go to this location
|
||||
* @return true if the listing went to the location.
|
||||
*/
|
||||
private boolean diffGoTo(ProgramLocation loc) {
|
||||
if (loc == null) {
|
||||
return false;
|
||||
}
|
||||
Address addr = loc.getAddress();
|
||||
if (addr == null) {
|
||||
return false;
|
||||
}
|
||||
saveLocation();
|
||||
boolean went = diffPlugin.getListingPanel().goTo(loc);
|
||||
saveLocation();
|
||||
return went;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the specified address in the right hand Diff listing.
|
||||
* @param addr go to this address
|
||||
* @return true if the listing went to the address.
|
||||
*/
|
||||
private boolean diffGoTo(Address addr) {
|
||||
if (addr == null) {
|
||||
return false;
|
||||
}
|
||||
saveLocation();
|
||||
boolean went = diffPlugin.getListingPanel().goTo(addr);
|
||||
saveLocation();
|
||||
return went;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving the first program's location (the left listing) in the navigation history.
|
||||
* The second program's location (the right listing) isn't saved since the navigation is
|
||||
* relative to a program in the tool's main listing. Also, if the second program's
|
||||
* locations were saved in the history, their program wouldn't be found and would cause
|
||||
* errors when restarting the application with the tool and primary program displayed, but
|
||||
* no Diff program.
|
||||
*/
|
||||
private void saveLocation() {
|
||||
Program firstProgram = diffPlugin.getFirstProgram();
|
||||
if (firstProgram == null) {
|
||||
return;
|
||||
}
|
||||
NavigationHistoryService historyService =
|
||||
diffPlugin.getTool().getService(NavigationHistoryService.class);
|
||||
if (historyService != null) {
|
||||
historyService.addNewLocation(goToService.getDefaultNavigatable());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
|
||||
public class DiffIgnoreAllAction extends DockingAction {
|
||||
|
||||
private final static String ACTION_NAME = "Set Ignore for All Apply Settings";
|
||||
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
|
||||
private final static String DESCRIPTION = "Change all the difference type apply settings to Ignore.";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private DiffApplySettingsProvider provider;
|
||||
|
||||
/**
|
||||
* @param provider the provider using this action
|
||||
*/
|
||||
public DiffIgnoreAllAction(DiffApplySettingsProvider provider) {
|
||||
super("Set All To Ignore", provider.getPlugin().getName());
|
||||
this.provider = provider;
|
||||
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
|
||||
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
|
||||
|
||||
setDescription(DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.IGNORE));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
|
||||
public class DiffMergeAllAction extends DockingAction {
|
||||
|
||||
private final static String ACTION_NAME = "Set Merge for All Apply Settings";
|
||||
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
|
||||
private final static String DESCRIPTION = "Change all the difference type apply settings to Merge if possible. Otherwise, change to Replace.";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private DiffApplySettingsProvider provider;
|
||||
|
||||
/**
|
||||
* @param provider the provider using this action
|
||||
*/
|
||||
public DiffMergeAllAction(DiffApplySettingsProvider provider) {
|
||||
super("Set All To Merge", provider.getPlugin().getName());
|
||||
this.provider = provider;
|
||||
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
|
||||
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
|
||||
setDescription(DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.MERGE));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento;
|
||||
import ghidra.app.util.HighlightProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
/**
|
||||
* This is a navigatable for use by the right-hand listing of the Diff.
|
||||
* It should navigate within the Diff's listing, which would then reposition
|
||||
* the CodeViewer's listing.
|
||||
*/
|
||||
class DiffNavigatable implements Navigatable {
|
||||
|
||||
private ProgramDiffPlugin diffPlugin;
|
||||
private Navigatable navigatable;
|
||||
private boolean disposed = false;
|
||||
private WeakSet<NavigatableRemovalListener> navigationListeners =
|
||||
WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||
|
||||
/**
|
||||
* The navigatable for the Diff. The CodeViewerService provides Diff with a listing,
|
||||
* so where appropriate this navigatable will defer to the CodeViewerService navigatable.
|
||||
* @param diffPlugin the plugin for the Diff which can be used to obtain needed info.
|
||||
* @param navigatable navigatable that the CodeViewerService provides.
|
||||
*/
|
||||
DiffNavigatable(ProgramDiffPlugin diffPlugin, Navigatable navigatable) {
|
||||
this.diffPlugin = diffPlugin;
|
||||
this.navigatable = navigatable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Program program, ProgramLocation location) {
|
||||
// Defer to the CodeViewer navigatable.
|
||||
return navigatable.goTo(program, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getLocation() {
|
||||
// CodeViewer is designed to handle this based on focus.
|
||||
return navigatable.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return diffPlugin.getSecondProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationMemento getMemento() {
|
||||
int cursorOffset = diffPlugin.getListingPanel().getFieldPanel().getCursorOffset();
|
||||
return new CodeViewerLocationMemento(diffPlugin.getSecondProgram(),
|
||||
diffPlugin.getCurrentLocation(), cursorOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMemento(LocationMemento memento) {
|
||||
CodeViewerLocationMemento cvMemento = (CodeViewerLocationMemento) memento;
|
||||
int cursorOffset = cvMemento.getCursorOffset();
|
||||
diffPlugin.getListingPanel().getFieldPanel().positionCursor(cursorOffset);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getNavigatableIcon() {
|
||||
// Just use the CodeViewer's navigatable icon.
|
||||
return navigatable.getNavigatableIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMarkers() {
|
||||
return isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
diffPlugin.getListingPanel().getFieldPanel().requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
// Is the CodeViewer visible and the Diff visible?
|
||||
return navigatable.isVisible() && diffPlugin.isShowingDiff();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInstanceID() {
|
||||
// CodeViewer provides the listing for Diff.
|
||||
return navigatable.getInstanceID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
if (selection == null) {
|
||||
selection = new ProgramSelection();
|
||||
}
|
||||
diffPlugin.setProgram2Selection(selection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlight(ProgramSelection highlight) {
|
||||
// The right-hand Diff listing doesn't currently support highlight.
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getSelection() {
|
||||
// CodeViewer is designed to handle this based on focus.
|
||||
return navigatable.getSelection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getHighlight() {
|
||||
// CodeViewer is designed to handle this based on focus.
|
||||
return navigatable.getHighlight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNavigatableListener(NavigatableRemovalListener listener) {
|
||||
navigationListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNavigatableListener(NavigatableRemovalListener listener) {
|
||||
navigationListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
disposed = true;
|
||||
for (NavigatableRemovalListener listener : navigationListeners) {
|
||||
listener.navigatableRemoved(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisposed() {
|
||||
return disposed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHighlight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlightProvider(HighlightProvider highlightProvider, Program program) {
|
||||
// CodeViewerProvider handles the other listing (the Diff listing) highlights.
|
||||
navigatable.setHighlightProvider(highlightProvider, program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHighlightProvider(HighlightProvider highlightProvider, Program program) {
|
||||
// CodeViewerProvider handles the other listing (the Diff listing) highlights.
|
||||
navigatable.removeHighlightProvider(highlightProvider, program);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* A stubbed {@link ProgramManager} that used the 'second program' at the current program. This
|
||||
* is used to secondary views in order to install the right program.
|
||||
*/
|
||||
public class DiffProgramManager implements ProgramManager {
|
||||
ProgramDiffPlugin programDiffPlugin;
|
||||
|
||||
public DiffProgramManager(ProgramDiffPlugin programDiffPlugin) {
|
||||
this.programDiffPlugin = programDiffPlugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getCurrentProgram() {
|
||||
return programDiffPlugin.getSecondProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeOtherPrograms(boolean ignoreChanges) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeAllPrograms(boolean ignoreChanges) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeProgram() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeProgram(Program program, boolean ignoreChanges) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program[] getAllOpenPrograms() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram(Address addr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(Program program) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(URL ghidraURL, int state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile domainFile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile df, int version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile domainFile, Component dialogParent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program openProgram(DomainFile domainFile, int version, int state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openProgram(Program program) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openProgram(Program program, boolean current) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openProgram(Program program, int state) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseProgram(Program program, Object persistentOwner) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentProgram(Program p) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPersistentOwner(Program program, Object owner) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockDown(boolean state) {
|
||||
// Not doing anything
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* ###
|
||||
* 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.diff;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.program.util.ProgramMergeFilter;
|
||||
|
||||
public class DiffReplaceAllAction extends DockingAction {
|
||||
|
||||
private final static String ACTION_NAME = "Set Replace for All Apply Settings";
|
||||
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
|
||||
private final static String DESCRIPTION = "Change all the difference type apply settings to Replace.";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
private static String[] menuPath = new String[] { ACTION_NAME };
|
||||
private DiffApplySettingsProvider provider;
|
||||
|
||||
/**
|
||||
* @param provider the provider using this action
|
||||
*/
|
||||
public DiffReplaceAllAction(DiffApplySettingsProvider provider) {
|
||||
super("Set All To Replace", provider.getPlugin().getName());
|
||||
this.provider = provider;
|
||||
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
|
||||
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
|
||||
setDescription(DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.REPLACE));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.diff;
|
||||
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.framework.plugintool.util.ServiceListener;
|
||||
|
||||
public class DiffServiceProvider implements ServiceProvider {
|
||||
|
||||
private ServiceProvider serviceProvider;
|
||||
private ProgramDiffPlugin programDiffPlugin;
|
||||
private DiffProgramManager diffProgramManager;
|
||||
private DiffGoToService diffGoToService;
|
||||
|
||||
DiffServiceProvider(ServiceProvider serviceProvider, ProgramDiffPlugin programDiffPlugin) {
|
||||
this.serviceProvider = serviceProvider;
|
||||
this.programDiffPlugin = programDiffPlugin;
|
||||
this.diffProgramManager = new DiffProgramManager(this.programDiffPlugin);
|
||||
GoToService goToService = serviceProvider.getService(GoToService.class);
|
||||
this.diffGoToService = new DiffGoToService(goToService, programDiffPlugin);
|
||||
}
|
||||
|
||||
public void addServiceListener(ServiceListener listener) {
|
||||
serviceProvider.addServiceListener(listener);
|
||||
}
|
||||
|
||||
public <T> T getService(Class<T> serviceClass) {
|
||||
if (serviceClass == ProgramManager.class) {
|
||||
return serviceClass.cast( diffProgramManager );
|
||||
}
|
||||
else if (serviceClass == GoToService.class) {
|
||||
return serviceClass.cast( diffGoToService );
|
||||
}
|
||||
return serviceProvider.getService(serviceClass);
|
||||
}
|
||||
|
||||
public void removeServiceListener(ServiceListener listener) {
|
||||
serviceProvider.removeServiceListener(listener);
|
||||
}
|
||||
|
||||
}
|
||||