Candidate release of source code.

This commit is contained in:
Dan
2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions
@@ -0,0 +1 @@
EXCLUDE FROM GHIDRA JAR: true
+10
View File
@@ -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; }
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

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>&nbsp&nbsp<B>Ignore</B> - don't apply this type of difference." +
"<BR>&nbsp&nbsp<B>Replace</B> - replace the difference type with the one from program 2." +
"<BR>&nbsp&nbsp<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);
}
}

Some files were not shown because too many files have changed in this diff Show More