Merge remote-tracking branch 'origin/GP-2897_ghidra1_DiffWithOpenProgram--SQUASHED'

This commit is contained in:
ghidra1 2022-12-07 23:23:33 -05:00
commit db932d2228
17 changed files with 536 additions and 281 deletions

View file

@ -95,7 +95,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin
private DataTypeManagerHandler dataTypeManagerHandler;
private DataTypesProvider provider;
private OpenVersionedFileDialog openDialog;
private OpenVersionedFileDialog<DataTypeArchive> openDialog;
private Map<String, DockingAction> recentlyOpenedArchiveMap;
private Map<String, DockingAction> installArchiveMap;
@ -587,8 +587,8 @@ public class DataTypeManagerPlugin extends ProgramPlugin
}
};
openDialog =
new OpenVersionedFileDialog(tool, "Open Project Data Type Archive",
df -> DataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass()));
new OpenVersionedFileDialog<>(tool, "Open Project Data Type Archive",
DataTypeArchive.class);
openDialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog"));
openDialog.addOkActionListener(listener);
}

View file

@ -83,7 +83,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
private MultiProgramManager programMgr;
private ProgramSaveManager programSaveMgr;
private int transactionID = -1;
private OpenVersionedFileDialog openDialog;
private OpenVersionedFileDialog<Program> openDialog;
private boolean locked = false;
private UndoAction undoAction;
private RedoAction redoAction;
@ -617,9 +617,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
doOpenProgram(domainFile, version, OPEN_CURRENT);
}
};
openDialog = new OpenVersionedFileDialog(tool, "Open Program", f -> {
return Program.class.isAssignableFrom(f.getDomainObjectClass());
});
openDialog = new OpenVersionedFileDialog<>(tool, "Open Program", Program.class);
openDialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog"));
openDialog.addOkActionListener(listener);
}

View file

@ -15,10 +15,10 @@
*/
package ghidra.framework.main;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -26,6 +26,8 @@ import javax.swing.*;
import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.event.mouse.GMouseListenerAdapter;
import docking.widgets.table.*;
import ghidra.framework.main.datatree.VersionHistoryPanel;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
@ -35,10 +37,9 @@ import ghidra.util.Msg;
/**
* Dialog to open a file that is versioned and allow a version to be
* opened.
*
*
* @param <T> domain object class
*/
public class OpenVersionedFileDialog extends DataTreeDialog {
public class OpenVersionedFileDialog<T extends DomainObject> extends DataTreeDialog {
private static final String SHOW_HISTORY_PREFERENCES_KEY = "OPEN_PROGRAM_DIALOG.SHOW_HISTORY";
private static final String HEIGHT_PREFERENCES_KEY = "OPEN_PROGRAM_DIALOG.HEIGHT";
private static final String WIDTH_NO_HISTORY_PREFERENCES_KEY =
@ -50,6 +51,10 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
private final static int DEFAULT_WIDTH_WITH_HISTORY = 800;
private final static int DIVIDER_SIZE = 2;
private final static int PROJECT_FILES_TAB = 0;
private final static int OPEN_OBJECT_LIST_TAB = 1;
private JTabbedPane tabbedPane; // null if openDomainObjects not specified
private JSplitPane splitPane;
private JButton historyButton;
private JPanel mainPanel;
@ -59,30 +64,71 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
private VersionHistoryPanel historyPanel;
private List<DockingActionIf> popupActions = Collections.emptyList();
private Class<T> domainObjectClass;
private List<T> openDomainObjects; // list of allowed domain object which are already open
private GFilterTable<T> openObjectsTable;
/**
* Constructor
* @param tool tool where the file is being opened.
* @param title title to use
* @param filter filter used to control what is displayed in data tree.
* @param domainObjectClass allowed domain object class which corresponds to {@code <T>}
*/
public OpenVersionedFileDialog(PluginTool tool, String title, DomainFileFilter filter) {
super(tool.getToolFrame(), title, DataTreeDialog.OPEN, filter);
public OpenVersionedFileDialog(PluginTool tool, String title, Class<T> domainObjectClass) {
super(tool.getToolFrame(), title, DataTreeDialog.OPEN, f -> {
return domainObjectClass.isAssignableFrom(f.getDomainObjectClass());
});
this.tool = tool;
this.domainObjectClass = domainObjectClass;
init();
}
/**
* Get the domain object for the selected version.
* @param consumer consumer
* @param readOnly true if the domain object should be opened read only,
* versus immutable
* @return null if a versioned file was not selected
* Set an optional list of already open domain objects of type {@code <T>} which may be
* selected instead of a project domain file. The {@link #getDomainObject(Object, boolean)}
* method should be used when this optional list has been set. If this dialog is reused
* the list should be set null if previously set. This method must be invoked prior to
* showing the dialog.
* @param openDomainObjects list of open domain objects from which a selection may be made.
*/
public DomainObject getVersionedDomainObject(Object consumer, boolean readOnly) {
if (historyPanel != null) {
return historyPanel.getSelectedVersion(consumer, readOnly);
public void setOpenObjectChoices(List<T> openDomainObjects) {
this.openDomainObjects = (openDomainObjects != null && !openDomainObjects.isEmpty())
? new ArrayList<>(openDomainObjects)
: null;
}
/**
* Get the selected domain object for read-only or immutable use.
* If an existing open object is selected its original mode applies but consumer will
* be added.
* @param consumer consumer
* @param readOnly true if the domain object should be opened read only, versus immutable
* @return null if a file was not selected
*/
@SuppressWarnings("unchecked") // relies on content class filter
public T getDomainObject(Object consumer, boolean readOnly) {
T dobj = null;
if (usingOpenProgramList()) {
dobj = getSelectedOpenDomainObject();
if (dobj != null) {
dobj.addConsumer(consumer);
}
return dobj;
}
return null;
if (historyPanel != null) {
dobj = (T) historyPanel.getSelectedVersion(consumer, readOnly);
}
if (dobj == null) {
DomainFile domainFile = getDomainFile();
if (domainFile != null) {
GetVersionedObjectTask task =
new GetVersionedObjectTask(consumer, domainFile, DomainFile.DEFAULT_VERSION,
readOnly);
tool.execute(task, 1000);
return (T) task.getVersionedObject();
}
}
return dobj;
}
/**
@ -90,18 +136,36 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
* @return -1 if a version history was not selected
*/
public int getVersion() {
if (historyPanel != null) {
if (historyPanel != null && !usingOpenProgramList()) {
return historyPanel.getSelectedVersionNumber();
}
return -1;
}
/* (non-Javadoc)
* @see ghidra.framework.main.DataTreeDialog#buildMainPanel()
*/
@Override
public DomainFile getDomainFile() {
if (usingOpenProgramList()) {
return null;
}
return super.getDomainFile();
}
@Override
public DomainFolder getDomainFolder() {
if (usingOpenProgramList()) {
return null;
}
return super.getDomainFolder();
}
@Override
protected JPanel buildMainPanel() {
mainPanel = super.buildMainPanel();
mainPanel = new JPanel(new BorderLayout());
mainPanel.add(super.buildMainPanel(), BorderLayout.CENTER);
JPanel historyButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
historyButtonPanel.add(historyButton);
mainPanel.add(historyButtonPanel, BorderLayout.SOUTH);
mainPanel.setMinimumSize(new Dimension(200, HEIGHT));
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
@ -112,8 +176,8 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
splitPane.setDividerLocation(1.0);
splitPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
JPanel outerPanel = new JPanel(new BorderLayout());
outerPanel.add(splitPane);
JPanel projectFilePanel = new JPanel(new BorderLayout());
projectFilePanel.add(splitPane);
String showHistory =
Preferences.getProperty(SHOW_HISTORY_PREFERENCES_KEY, Boolean.FALSE.toString(), true);
@ -122,26 +186,91 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
showHistoryPanel(true);
}
return outerPanel;
openObjectsTable = null;
tabbedPane = null;
updateOkTooltip();
if (openDomainObjects == null) {
return projectFilePanel; // return Project File selection panel only
}
// Create tabbed pane with "Project Files" and "Open Program" tabs
// NOTE: actual tab name reflects domainObjectClass name
tabbedPane = new JTabbedPane();
tabbedPane.setName("Tabs");
tabbedPane.add("Project Files", projectFilePanel);
tabbedPane.add("Open " + domainObjectClass.getSimpleName() + "s",
buildOpenObjectsTable());
tabbedPane.addChangeListener(e -> {
int selectedTabIndex = tabbedPane.getModel().getSelectedIndex();
if (selectedTabIndex == PROJECT_FILES_TAB) {
// Project tree and History use
String nameText = getNameText();
setOkEnabled((nameText != null) && !nameText.isEmpty());
}
else { // OPEN_OBJECT_LIST_TAB
setOkEnabled(getSelectedOpenDomainObject() != null);
}
updateOkTooltip();
});
JPanel tabbedPanel = new JPanel();
tabbedPanel.setLayout(new BorderLayout());
tabbedPanel.add(tabbedPane, BorderLayout.CENTER);
tabbedPane.setSelectedIndex(PROJECT_FILES_TAB);
return tabbedPanel;
}
private void advancedButtonCallback() {
showHistoryPanel(!historyIsShowing);
private boolean usingOpenProgramList() {
return tabbedPane != null &&
tabbedPane.getModel().getSelectedIndex() == OPEN_OBJECT_LIST_TAB;
}
private T getSelectedOpenDomainObject() {
if (!usingOpenProgramList()) {
return null;
}
return openObjectsTable.getSelectedRowObject();
}
private Component buildOpenObjectsTable() {
openObjectsTable = new GFilterTable<>(new OpenObjectsTableModel());
GTable table = openObjectsTable.getTable();
table.getSelectionModel()
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
openObjectsTable.addSelectionListener(e -> {
setOkEnabled(true);
okButton.setToolTipText("Use the selected " + domainObjectClass.getSimpleName());
});
table.addMouseListener(new GMouseListenerAdapter() {
@Override
public void doubleClickTriggered(MouseEvent e) {
if (okButton.isEnabled()) {
okCallback();
}
}
});
return openObjectsTable;
}
private void showHistoryPanel(boolean showHistory) {
historyIsShowing = showHistory;
if (showHistory) {
createHistoryPanel();
historyButton.setText("No History");
historyButton.setText("Hide History");
DomainFile df = treePanel.getSelectedDomainFile();
historyPanel.setDomainFile(df);
splitPane.setDividerSize(DIVIDER_SIZE);
splitPane.setDividerLocation(DEFAULT_WIDTH_NO_HISTORY - 4);
}
else {
historyButton.setText("History>>");
historyButton.setText("Show History>>");
splitPane.setDividerSize(0);
splitPane.setRightComponent(null);
historyPanel = null;
@ -175,7 +304,6 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
Preferences.setProperty(SHOW_HISTORY_PREFERENCES_KEY, Boolean.toString(historyIsShowing));
Preferences.store();
}
@Override
@ -203,6 +331,25 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
}
}
private void updateOkTooltip() {
String tip;
if (usingOpenProgramList()) {
tip = "Use selected " + domainObjectClass.getSimpleName();
}
else {
tip = "Open the selected file";
if (historyPanel != null && historyIsShowing) {
int versionNumber = historyPanel.getSelectedVersionNumber();
if (versionNumber >= 0) {
DomainFile df = OpenVersionedFileDialog.super.getDomainFile();
okButton.setToolTipText(
"Open version " + versionNumber + " for " + df.getName());
}
}
}
okButton.setToolTipText(tip);
}
private boolean createHistoryPanel() {
try {
historyPanel = new VersionHistoryPanel(tool, null);
@ -220,24 +367,16 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
if (e.getValueIsAdjusting()) {
return;
}
okButton.setToolTipText("Open the selected file");
int versionNumber = historyPanel.getSelectedVersionNumber();
if (versionNumber >= 0) {
DomainFile df = OpenVersionedFileDialog.super.getDomainFile();
okButton.setToolTipText("Open version " + versionNumber + " for " + df.getName());
}
updateOkTooltip();
});
return true;
}
private void init() {
historyButton = new JButton("History>>");
historyButton.addActionListener(e -> advancedButtonCallback());
addButton(historyButton);
historyButton.addActionListener(e -> showHistoryPanel(!historyIsShowing));
okButton.setToolTipText("Open the selected file");
rootPanel.setPreferredSize(getPreferredSizeForHistoryState());
}
@Override
@ -249,6 +388,7 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
DomainFile df = treePanel.getSelectedDomainFile();
historyPanel.setDomainFile(df);
}
updateOkTooltip();
});
}
@ -264,4 +404,44 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
return actionContext;
}
private class OpenObjectsTableModel extends AbstractGTableModel<T> {
@Override
public String getName() {
return "Open DomainObject List";
}
@Override
public List<T> getModelData() {
return openDomainObjects;
}
@Override
public Object getColumnValueForRow(T t, int columnIndex) {
// only one column
return t.getDomainFile().toString();
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public String getColumnName(int columnIndex) {
return "Program Path";
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
}
}

View file

@ -26,6 +26,7 @@ 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/SelectOpenProgram.png||GHIDRA||||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|
@ -44,5 +45,4 @@ 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|

View file

@ -21,14 +21,14 @@
program in order to get another user's changes into your program.
</p>
<h2>
<a name="Open_Close_Program_View"></a>
Opening and Closing the Second Program
<a name="Open_Close_Diff_View"></a>
Opening and Closing the Diff View
</h2>
<blockquote>
<p>The <b>Open/Close Diff View</b> icon
<img src="images/table_relationship.png" alt="">
in the Code Browser tool bar is used for both opening and closing
the second program in the Code Browser. This is one of two ways to open the Diff
a second program in the Code Browser Diff panel. This is one of two ways to open the Diff
tool. The other is the menu action, <span style="font-weight: bold;">Tools</span>
<b>
<img src="../../shared/arrow.gif" alt="-&gt;" border="0">
@ -70,9 +70,9 @@
<img src="../../shared/arrow.gif" alt="-&gt;" border="0">
Program Differences...
</b>. This is one of two ways to open the Diff
tool. The other is the <b>Open/Close Program Diff </b> icon
tool. The other is the <b>Open/Close Diff View</b> icon
<img src="images/table_relationship.png" alt="">
in the Code Browser tool bar which is described above in <b>Open/Close Diff View</b>.
in the Code Browser tool bar which is described above in <b>Opening and Closing the Diff View</b>.
The following describes using the menu action to open the Diff tool.
</p>
<p>With a program open in the Code Browser tool, do the following to
@ -100,14 +100,27 @@
defined. In other words, they should be the same type of program
(i.e. based on the same program language).<br>
<img src="../../shared/tip.png" alt="">
If you wish to Diff against a previous version of a
<b>versioned program, </b>select the <b>History&gt;&gt;</b>
button of a program. Select the versioned program from the tree.
Next select the desired version of the program from the history
list on the right side of the dialog.<br>
If you wish to Diff against a specific version of a
<b>versioned program</b>, a specific version may be seleected from the
<b>Version History</b> table. The project file history panel is displayed
when the <b>History&gt;&gt;</b> button is clicked.
Select the versioned program from the tree.
Next select the desired version of the program from the <b>Version History</b>
table on the right side of the dialog.<br>
<div align="center">
<img src="images/SelectOtherVersionedProgram.png" alt=""> <br>
</div>
<img src="../../shared/tip.png" alt="">
If you wish to Diff against another program which is already open in the same tool
you can select a compatible program from the <b>Open Programs</b> tab. Programs
which are not compatible (e.g., different architecture) are not shown in the table.
The <b>Open Programs</b> tab is not available if compatible open programs are not
found. You may return to the project file tree selection panel by clicking the
<b>Project Files</b> tab.
<div align="center">
<img src="images/SelectOpenProgram.png" alt=""> <br>
</div>
</li>
<li>Click the <b>OK</b> button.</li>
<li>The <i>Determine Program Differences</i> dialog is displayed.
@ -269,7 +282,7 @@
</tr>
<tr>
<td align="left" valign="top" width="16">
<a href="#Open_Close_Program_View">
<a href="#Open_Close_Diff_View">
<img src="images/table_relationship.png" alt="" border="0">
</a>
</td>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -56,7 +56,7 @@ class DiffActionManager {
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 OPEN_CLOSE_DIFF_VIEW_ACTION = "Open/Close Diff View";
static final String VIEW_PROGRAM_DIFF_ACTION = "View Program Differences";
private DockingAction applyDiffsAction;
@ -69,11 +69,12 @@ class DiffActionManager {
private DockingAction getDiffsAction;
private DockingAction selectAllDiffsAction;
private DockingAction p1SelectToP2Action;
private ToggleDockingAction openCloseProgram2Action;
private OpenCloseDiffViewAction openCloseDiffViewAction;
private DockingAction viewProgramDiffAction;
/**
* Creates an action manager for the Program Diff plugin.
* @param plugin Diff plugin
*/
public DiffActionManager(ProgramDiffPlugin plugin) {
this.plugin = plugin;
@ -86,7 +87,7 @@ class DiffActionManager {
*/
void setCodeViewerService(CodeViewerService codeViewerService) {
this.codeViewerService = codeViewerService;
codeViewerService.addLocalAction(openCloseProgram2Action);
codeViewerService.addLocalAction(openCloseDiffViewAction);
}
/**
@ -127,7 +128,7 @@ class DiffActionManager {
*/
void programClosed(Program program) {
boolean hasProgram = (plugin.getCurrentProgram() != null);
openCloseProgram2Action.setEnabled(hasProgram && !plugin.isTaskInProgress());
openCloseDiffViewAction.setEnabled(hasProgram && !plugin.isTaskInProgress());
}
/**
@ -136,26 +137,27 @@ class DiffActionManager {
* @param program the newly active program
*/
void setActiveProgram(Program program) {
viewProgramDiffAction.setEnabled(program != null);
boolean enabled = program != null && !plugin.isTaskInProgress();
openCloseProgram2Action.setEnabled(enabled);
openCloseDiffViewAction.setEnabled(enabled);
if (enabled) {
if (openCloseProgram2Action.isSelected()) {
if (openCloseDiffViewAction.isSelected()) {
// we are diffing--is the current program the diff program?
Program firstProgram = plugin.getFirstProgram();
if (firstProgram == program) {
openCloseProgram2Action.setDescription("Close Diff View");
openCloseDiffViewAction.setDescription("Close Diff View");
}
else {
openCloseProgram2Action.setDescription("Bring Diff View to Front");
openCloseDiffViewAction.setDescription("Bring Diff View to Front");
}
}
}
else {
// no active diff
openCloseProgram2Action.setDescription("Open Diff View");
openCloseDiffViewAction.setDescription("Open Diff View");
}
}
@ -164,16 +166,16 @@ class DiffActionManager {
* second program to the program Diff. Actions are adjusted accordingly.
*/
void secondProgramOpened() {
openCloseProgram2Action.setSelected(true);
openCloseProgram2Action.setDescription("Close Diff View");
openCloseDiffViewAction.setSelected(true);
openCloseDiffViewAction.setDescription("Close Diff View");
Program firstProgram = plugin.getFirstProgram();
Program secondProgram = plugin.getSecondProgram();
String firstName = firstProgram.getName();
String secondName = secondProgram.getName();
String firstName = firstProgram.getDomainFile().getName();
String secondName = secondProgram.getDomainFile().getName();
//@formatter:off
openCloseProgram2Action.setDescription(
openCloseDiffViewAction.setDescription(
"<html><center>Close Diff View</center><br>" +
"Current diff: " +
"<b>"+HTMLUtilities.escapeHTML(firstName)+"</b> to <b>" +HTMLUtilities.escapeHTML(secondName)+"</b>");
@ -185,8 +187,8 @@ class DiffActionManager {
* program Diff was closed. Actions are adjusted accordingly.
*/
void secondProgramClosed() {
openCloseProgram2Action.setSelected(false);
openCloseProgram2Action.setDescription("Open Diff View");
openCloseDiffViewAction.setSelected(false);
openCloseDiffViewAction.setDescription("Open Diff View");
showDiffSettingsAction.setEnabled(false);
diffDetailsAction.setEnabled(false);
removeActions();
@ -197,7 +199,7 @@ class DiffActionManager {
}
void setOpenCloseActionSelected(boolean selected) {
openCloseProgram2Action.setSelected(selected);
openCloseDiffViewAction.setSelected(selected);
}
void updateActions(boolean taskInProgress, boolean inDiff, boolean hasSelectionInView,
@ -217,32 +219,34 @@ class DiffActionManager {
Program currentProgram = plugin.getCurrentProgram();
boolean hasProgram = (currentProgram != null);
openCloseProgram2Action.setEnabled(hasProgram && !taskInProgress);
openCloseDiffViewAction.setEnabled(hasProgram && !taskInProgress);
}
/**
* Removes all the actions.
*/
void dispose() {
codeViewerService.removeLocalAction(openCloseProgram2Action);
codeViewerService.removeLocalAction(openCloseDiffViewAction);
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 =
new DockingAction(VIEW_PROGRAM_DIFF_ACTION, plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
plugin.selectProgram2();
}
};
viewProgramDiffAction.setEnabled(plugin.getCurrentProgram() != null);
viewProgramDiffAction.setMenuBarData(new MenuData(menuPath, "View"));
viewProgramDiffAction.setMenuBarData(new MenuData(
new String[] { ToolConstants.MENU_TOOLS, VIEW_PROGRAM_DIFF_ACTION + "..." },
"ProgramDiff"));
viewProgramDiffAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_2, 0));
viewProgramDiffAction.setHelpLocation(
new HelpLocation(HelpTopics.DIFF, "Program_Differences"));
viewProgramDiffAction
.setHelpLocation(new HelpLocation(HelpTopics.DIFF, "Program_Differences"));
plugin.getTool().addAction(viewProgramDiffAction);
applyDiffsAction = new DiffAction(APPLY_DIFFS_ACTION) {
@ -377,72 +381,7 @@ class DiffActionManager {
"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 = new GIcon("icon.plugin.programdiff.open.close.program");
openCloseProgram2Action.setEnabled(false);
openCloseProgram2Action.setKeyBindingData(
new KeyBindingData('C', InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_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");
openCloseDiffViewAction = new OpenCloseDiffViewAction();
}
private abstract class DiffAction extends DockingAction {
@ -457,4 +396,73 @@ class DiffActionManager {
return (context instanceof OtherPanelContext);
}
}
private class OpenCloseDiffViewAction extends ToggleDockingAction {
OpenCloseDiffViewAction() {
super(OPEN_CLOSE_DIFF_VIEW_ACTION, plugin.getName());
GIcon icon = new GIcon("icon.plugin.programdiff.open.close.program");
setEnabled(false);
setToolBarData(new ToolBarData(icon, "zzz"));
setHelpLocation(
new HelpLocation(HelpTopics.DIFF, OPEN_CLOSE_DIFF_VIEW_ACTION));
setSelected(false);
setDescription("Open Diff View");
}
@Override
public void actionPerformed(ActionContext context) {
//
// No active diff--start one.
//
if (openCloseDiffViewAction.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;
}
}
}

View file

@ -20,6 +20,7 @@ import java.awt.event.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.text.*;
@ -59,8 +60,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.util.*;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.exception.*;
import ghidra.util.task.*;
import help.Help;
import help.HelpService;
@ -82,7 +82,8 @@ import help.HelpService;
"the current program. This plugin also computes differences between the two " +
"programs and allows the user to apply differences from the second program onto" +
"the first.",
servicesRequired = { GoToService.class, CodeViewerService.class, MarkerService.class },
servicesRequired = { GoToService.class, CodeViewerService.class, MarkerService.class,
ProgramManager.class },
servicesProvided = { DiffService.class },
eventsProduced = { ProgramSelectionPluginEvent.class, ViewChangedPluginEvent.class },
eventsConsumed = { ProgramClosedPluginEvent.class, ViewChangedPluginEvent.class }
@ -101,6 +102,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
private Color cursorHighlightColor = GhidraOptions.DEFAULT_CURSOR_LINE_COLOR;
protected static final HelpService help = Help.getHelpService();
private ProgramManager programManagerService;
private GoToService goToService;
private CodeViewerService codeViewerService;
private MarkerManager markerManager;
@ -139,7 +141,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
private DiffDetailsProvider diffDetailsProvider;
private boolean settingLocation;
private ActionListener okListener;
private DiffTaskListener diffTaskListener = DiffTaskListener.NULL_LISTENER;
private ProgramLocation previousP1Location;
@ -148,7 +149,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
DiffApplySettingsOptionManager applySettingsMgr;
private boolean isHighlightCursorLine;
private Program activeProgram;
private OpenVersionedFileDialog openProgramDialog;
private OpenVersionedFileDialog<Program> openVersionedFileDialog;
/**
* Creates the plugin for indicating program differences to the user.
@ -289,7 +290,11 @@ public class ProgramDiffPlugin extends ProgramPlugin
ProgramSelection previousP2DiffHighlight = p2DiffHighlight;
ProgramSelection previousP2Selection = p2Selection;
AddressSet p2ViewAddrSet =
DiffUtility.getCompatibleAddressSet(p1ViewAddrSet, secondaryDiffProgram);
diffListingPanel.setView(p2ViewAddrSet);
FieldPanel fp = diffListingPanel.getFieldPanel();
AddressSet p1AddressSetAsP2 =
DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram);
AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2);
@ -327,8 +332,8 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
@Override
public boolean inProgress() {
return taskInProgress;
public boolean isDiffActive() {
return secondaryDiffProgram != null;
}
private boolean launchDiffOnOpenProgram() {
@ -336,7 +341,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (diffControl != null) { // There is currently a Diff already so clear it.
clearDiff();
}
diff(p1ViewAddrSet);
diff();
return true;
}
catch (Exception e) {
@ -347,6 +352,13 @@ public class ProgramDiffPlugin extends ProgramPlugin
@Override
public boolean launchDiff(DomainFile otherProgram) {
if (taskInProgress) {
Msg.error(this, "Diff is busy and can't be launched");
return false;
}
if (isDiffActive()) {
closeProgram2();
}
if (openSecondProgram(otherProgram)) {
return launchDiffOnOpenProgram();
}
@ -355,20 +367,18 @@ public class ProgramDiffPlugin extends ProgramPlugin
@Override
public boolean launchDiff(Program otherProgram) {
try {
if (diffControl != null) { // There is currently a Diff already so clear it.
clearDiff();
}
if (openSecondProgram(otherProgram, null)) {
secondaryDiffProgram.addConsumer(this);
diff(p1ViewAddrSet);
}
return true;
}
catch (Exception e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
if (taskInProgress) {
Msg.error(this, "Diff is busy and can't be launched");
return false;
}
if (isDiffActive()) {
closeProgram2();
}
otherProgram.addConsumer(this);
if (openSecondProgram(otherProgram, null)) {
launchDiffOnOpenProgram();
}
return true;
}
@Override
@ -487,8 +497,12 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
}
void setOpenDiffProgramDialog(OpenVersionedFileDialog dialog) {
this.openProgramDialog = dialog;
/**
* Used for testing to force file selection dialog instance.
* @param dialog project file selection dialog
*/
void setDiffOpenVersionedFileDialog(OpenVersionedFileDialog<Program> dialog) {
this.openVersionedFileDialog = dialog;
}
private void setActiveProgram(Program newActiveProgram) {
@ -575,6 +589,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
protected void init() {
codeViewerService = tool.getService(CodeViewerService.class);
goToService = tool.getService(GoToService.class);
programManagerService = tool.getService(ProgramManager.class);
FormatManager formatManager = codeViewerService.getFormatManager();
ServiceProvider diffServiceProvider =
@ -934,21 +949,11 @@ public class ProgramDiffPlugin extends ProgramPlugin
updatePgm2Enablement();
}
/**
* Computes the differences between program1 and program2 that are displayed in the browser
* using the current Limiting set. It allows the user to specify the Diff settings to use.
*/
void diff() {
diff(createLimitingSet());
}
/**
* Computes the differences between program1 and program2 that are displayed in the browser. It
* allows the user to specify the Diff settings to use.
*
* @param p1LimitSet an address set to use to limit the extent of the Diff.
*/
void diff(AddressSetView p1LimitSet) {
void diff() {
if (taskInProgress) {
Msg.showInfo(getClass(), tool.getToolFrame(), "Can't Start Another Diff",
"A Diff or Apply is already in progress.");
@ -956,10 +961,10 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
boolean reload = diffControl != null;
if (reload) {
reloadDiff(p1LimitSet);
reloadDiff();
}
else {
createDiff(p1LimitSet);
createDiff();
}
}
@ -1087,23 +1092,60 @@ public class ProgramDiffPlugin extends ProgramPlugin
return;
}
final OpenVersionedFileDialog dialog = getOpenProgramDialog();
okListener = e -> {
selectAndOpenProgram2();
actionManager.setOpenCloseActionSelected(secondaryDiffProgram != null);
getDiffDetailsProvider();
}
/**
* Generate a list of programs which are currently open in the tool which are compatible
* with the primaryProgram to be diff'd. The top/primary entries correspond to this
* programs which have the same name (see {@link Program#getName()}) as the primaryProgram,
* while the remaining compatible programs will be considered secondary. Each of these
* two groups will be sorted by its domain file name then concatenated to form a single list.
* @return ordered open program list for use with the {@link OpenVersionedFileDialog}.
*/
private List<Program> getOpenProgramList() {
List<Program> primaryList = new ArrayList<>();
List<Program> secondaryList = new ArrayList<>();
for (Program p : programManagerService.getAllOpenPrograms()) {
if (!programManagerService.isVisible(p) || p == activeProgram ||
!ProgramMemoryComparator.similarPrograms(activeProgram, p)) {
continue;
}
if (p.getName().equals(activeProgram.getName())) {
primaryList.add(p);
}
else {
secondaryList.add(p);
}
}
Comparator<Program> programComparator = (a, b) -> {
return a.getDomainFile().getName().compareTo(b.getDomainFile().getName());
};
Collections.sort(primaryList, programComparator);
Collections.sort(secondaryList, programComparator);
List<Program> programList = new ArrayList<>();
programList.addAll(primaryList);
programList.addAll(secondaryList);
return programList;
}
private void selectAndOpenProgram2() {
final OpenVersionedFileDialog<Program> dialog = getOpenVersionedFileDialog();
List<Program> openProgramList = getOpenProgramList();
dialog.setOpenObjectChoices(openProgramList.isEmpty() ? null : openProgramList);
dialog.addOkActionListener(e -> {
tool.clearStatusInfo();
JComponent component = dialog.getComponent();
DomainObject dobj = dialog.getVersionedDomainObject(ProgramDiffPlugin.this, false);
Program dobj = dialog.getDomainObject(ProgramDiffPlugin.this, false);
if (dobj != null) {
if (openSecondProgram((Program) dobj, component)) {
dialog.close();
launchDiffOnOpenProgram();
}
return;
}
DomainFile df = dialog.getDomainFile();
if (df != null) {
if (openSecondProgram(df)) {
if (openSecondProgram(dobj, component)) {
dialog.close();
launchDiffOnOpenProgram();
}
@ -1112,25 +1154,18 @@ public class ProgramDiffPlugin extends ProgramPlugin
displayStatus(component, "Can't Open Selected Program",
"Please select a file, not a folder.", OptionDialog.INFORMATION_MESSAGE);
};
dialog.addOkActionListener(okListener);
});
dialog.showComponent();
actionManager.setOpenCloseActionSelected(secondaryDiffProgram != null);
getDiffDetailsProvider();
}
private OpenVersionedFileDialog getOpenProgramDialog() {
private OpenVersionedFileDialog<Program> getOpenVersionedFileDialog() {
if (openProgramDialog != null) {
return openProgramDialog;
if (openVersionedFileDialog != null) {
return openVersionedFileDialog;
}
OpenVersionedFileDialog dialog =
new OpenVersionedFileDialog(tool, "Select Other Program", f -> {
Class<?> c = f.getDomainObjectClass();
return Program.class.isAssignableFrom(c);
});
OpenVersionedFileDialog<Program> dialog =
new OpenVersionedFileDialog<>(tool, "Select Other Program", Program.class);
dialog.setTreeSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
dialog.setHelpLocation(new HelpLocation("Diff", "Open_Close_Program_View"));
return dialog;
@ -1144,12 +1179,10 @@ public class ProgramDiffPlugin extends ProgramPlugin
executeDiffDialog = new ExecuteDiffDialog();
executeDiffDialog.addActionListener(new DiffActionListener());
}
if (executeDiffDialog != null) {
executeDiffDialog.configure(primaryProgram, secondaryDiffProgram, currentSelection,
execDiffFilter);
executeDiffDialog.setPgmContextEnabled(sameProgramContext);
tool.showDialog(executeDiffDialog);
}
executeDiffDialog.configure(primaryProgram, secondaryDiffProgram, currentSelection,
execDiffFilter);
executeDiffDialog.setPgmContextEnabled(sameProgramContext);
tool.showDialog(executeDiffDialog);
}
void setP1SelectionOnP2() {
@ -1252,7 +1285,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
if (primaryProgram == null) {
return null;
}
if (executeDiffDialog != null) {
if (executeDiffDialog != null) { // TODO: don't reuse if it could change
return executeDiffDialog.getAddressSet();
}
AddressSet limitSet = new AddressSet(primaryProgram.getMemory());
@ -1266,38 +1299,29 @@ public class ProgramDiffPlugin extends ProgramPlugin
/**
* Reload the marked differences in the diff panel.
*/
private void reloadDiff(AddressSetView p1LimitSet) {
private void reloadDiff() {
if (diffControl == null) {
createDiff(p1LimitSet);
createDiff();
}
else {
tool.clearStatusInfo();
if (p1LimitSet == null) {
p1LimitSet = createLimitingSet();
}
displayExecuteDiff();
}
}
private void createDiff(AddressSetView p1LimitSet) {
private void createDiff() {
Frame frame = tool.getToolFrame();
try {
frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
tool.clearStatusInfo();
if (secondaryDiffProgram == null) {
selectProgram2();
if (secondaryDiffProgram == null) {
return;
}
throw new AssertException("Expected secondaryDiffProgram");
}
if (executeDiffDialog != null) {
executeDiffDialog.close();
executeDiffDialog = null;
}
if (p1LimitSet == null) {
p1LimitSet = createLimitingSet();
}
displayExecuteDiff();
}
finally {
@ -1521,29 +1545,29 @@ public class ProgramDiffPlugin extends ProgramPlugin
return false;
}
private boolean openSecondProgram(Program newProgram, JComponent selectDialog) {
private boolean openSecondProgram(Program newProgram, JComponent popupParent) {
if (newProgram == null) {
displayStatus(selectDialog, "Can't Open Selected Program",
displayStatus(popupParent, "Can't Open Selected Program",
"Couldn't open second program.", OptionDialog.ERROR_MESSAGE);
return false;
}
if (!ProgramMemoryComparator.similarPrograms(currentProgram, newProgram)) {
newProgram.release(this);
String message = "Programs languages don't match.\n" + currentProgram.getName() + " (" +
currentProgram.getLanguageID() + ")\n" + newProgram.getName() + " (" +
newProgram.getLanguageID() + ")";
displayStatus(selectDialog, "Can't Open Selected Program", message,
displayStatus(popupParent, "Can't Open Selected Program", message,
OptionDialog.ERROR_MESSAGE);
newProgram.release(this);
return false;
}
ProgramMemoryComparator programMemoryComparator = null;
try {
programMemoryComparator = new ProgramMemoryComparator(currentProgram, newProgram);
}
catch (ProgramConflictException e) {
Msg.error(this, "Unexpected exception creating memory comparator", e);
newProgram.release(this);
return false;
}
addressesOnlyInP1 = programMemoryComparator.getAddressesOnlyInOne();
@ -1552,7 +1576,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
AddressSet combinedAddresses =
ProgramMemoryComparator.getCombinedAddresses(currentProgram, newProgram);
if (addressesInCommon.isEmpty()) {
int selectedOption = OptionDialog.showYesNoDialog(selectDialog, "No Memory In Common",
int selectedOption = OptionDialog.showYesNoDialog(popupParent, "No Memory In Common",
"The two programs have no memory addresses in common.\n" +
"Do you want to continue?");
if (selectedOption != OptionDialog.YES_OPTION) {

View file

@ -30,23 +30,29 @@ public interface DiffService {
/**
* Launch the Diff dialog and display differences between the current program
* and the otherProgram.
* and the otherProgram. This will force the current Diff, if active, to be terminated.
* @param otherProgram a domain file for the program to Diff the current program against.
* @return true if the second program is opened and successfully Diffed.
* @return true if the second program is opened and successfully Diffed. A false will be
* returned if the Diff fails to launch.
*/
public boolean launchDiff(DomainFile otherProgram);
/**
* Launch the Diff dialog and display differences between the current program
* and the otherProgram.
* and the otherProgram. This will force the current Diff, if active, to be terminated.
* This Diff service will be added as a consumer on the specified otherProgram while the Diff
* remains active.
* @param otherProgram the program to Diff the current program against.
* @return true if the second program is opened and successfully Diffed.
* @return true if the second program is opened and successfully Diffed. A false will be
* returned if the Diff fails to launch.
*/
public boolean launchDiff(Program otherProgram);
/**
* Determine if the Diff service is currently displaying a Diff.
* Determine if the Diff service is currently displaying a Diff within the Tool associated with
* this service.
* @return true if a Diff is currently active
*/
public boolean inProgress();
public boolean isDiffActive();
}

View file

@ -56,7 +56,7 @@ public class DiffSaveSettingsTest extends DiffApplyTestAdapter {
diffListingPanel = diffPlugin.getListingPanel();
fp1 = cb.getFieldPanel();
fp2 = diffListingPanel.getFieldPanel();
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Program View");
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Diff View");
}
private void showNewTool() throws Exception {
@ -73,7 +73,7 @@ public class DiffSaveSettingsTest extends DiffApplyTestAdapter {
diffListingPanel = diffPlugin.getListingPanel();
fp1 = cb.getFieldPanel();
fp2 = diffListingPanel.getFieldPanel();
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Program View");
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Diff View");
}
@Override

View file

@ -659,7 +659,7 @@ public class DiffTest extends DiffTestAdapter {
assertEquals(true, isShowingDiff());
// Check action enablement.
checkDiffAction("View Program Differences", true, true);
checkDiffAction("Open/Close Program View", true, true);
checkDiffAction("Open/Close Diff View", true, true);
checkDiffAction("Apply Differences", true, false);
checkDiffAction("Apply Differences and Goto Next Difference", true, false);
checkDiffAction("Ignore Selection and Goto Next Difference", true, false);
@ -777,13 +777,13 @@ public class DiffTest extends DiffTestAdapter {
assertEquals(true, isDiffing());
assertEquals(true, isShowingDiff());
checkDiffAction("Open/Close Program View", true, true);
checkDiffAction("Open/Close Diff View", true, true);
//
// Different tab--still enabled
//
selectTab(panel, program3);
checkDiffAction("Open/Close Program View", true, true);
checkDiffAction("Open/Close Diff View", true, true);
clickDiffButton();
assertTrue("Not diffing after clicking the diff button when on a non-diff tab",
@ -794,7 +794,7 @@ public class DiffTest extends DiffTestAdapter {
//
// Diff tab--still enabled
//
checkDiffAction("Open/Close Program View", true, true);
checkDiffAction("Open/Close Diff View", true, true);
clickDiffButton();

View file

@ -43,7 +43,8 @@ import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
import ghidra.app.services.ProgramManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.framework.main.*;
import ghidra.framework.model.*;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
@ -479,7 +480,7 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
diffListingPanel = diffPlugin.getListingPanel();
fp1 = cb.getFieldPanel();
fp2 = diffListingPanel.getFieldPanel();
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Program View");
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Diff View");
tool.addPlugin(ProgramTreePlugin.class.getName());
pt = env.getPlugin(ProgramTreePlugin.class);
@ -664,10 +665,8 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
void pickSecondProgram(final Program program2) {
program2.addConsumer(diffPlugin);
OpenVersionedFileDialogTestFake dialog = new OpenVersionedFileDialogTestFake(program2);
diffPlugin.setOpenDiffProgramDialog(dialog);
diffPlugin.setDiffOpenVersionedFileDialog(dialog);
launchDiffByAction();
@ -1154,13 +1153,13 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
// Inner Classes
//==================================================================================================
private class OpenVersionedFileDialogTestFake extends OpenVersionedFileDialog {
private class OpenVersionedFileDialogTestFake extends OpenVersionedFileDialog<Program> {
private ActionListener listener;
private Program chosenProgram;
OpenVersionedFileDialogTestFake(Program program) {
super(tool, "Select Other Program", null);
super(tool, "Select Other Program", Program.class);
this.chosenProgram = program;
}
@ -1183,7 +1182,10 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
}
@Override
public DomainObject getVersionedDomainObject(Object consumer, boolean readOnly) {
public Program getDomainObject(Object consumer, boolean readOnly) {
if (chosenProgram != null) {
chosenProgram.addConsumer(consumer);
}
return chosenProgram;
}

View file

@ -135,7 +135,9 @@ public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBCon
@Override
public T getImmutableObject(FolderItem item, Object consumer, int version, int minChangeVersion,
TaskMonitor monitor) throws IOException, CancelledException, VersionException {
throw new UnsupportedOperationException("link-file does not support getImmutableObject");
//throw new UnsupportedOperationException("link-file does not support getImmutableObject");
// See GP-2903
return getReadOnlyObject(item, version, true, consumer, monitor);
}
@Override

View file

@ -497,7 +497,8 @@ public class DefaultProject implements Project {
addProjectView(url, true);
}
catch (IOException e) {
Msg.error(this, e.getMessage());
Msg.error(this, "Project view not opended (" + GhidraURL.getDisplayString(url) +
"): " + e.getMessage());
}
}
it = root.getChildren(OPEN_REPOSITORY_VIEW_XML_NAME).iterator();
@ -509,7 +510,8 @@ public class DefaultProject implements Project {
addProjectView(url, true);
}
catch (IOException e) {
Msg.error(this, e.getMessage());
Msg.error(this, "Project view not opended (" + GhidraURL.getDisplayString(url) +
"): " + e.getMessage());
}
}

View file

@ -45,8 +45,7 @@ public class DiffScreenShots extends GhidraScreenShotGenerator {
public void testSelectOtherProgram() throws Exception {
addProgramsToProject();
program = env.getProgram("WinHelloCPP.exe");
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Diff View", "ProgramDiffPlugin", false);
waitForSwing();
DialogComponentProvider dialog = getDialog();
captureDialog(dialog.getPreferredSize().width, 500);
@ -59,10 +58,10 @@ public class DiffScreenShots extends GhidraScreenShotGenerator {
addProgramsToProject();
createProgramVersions();
waitForSwing();
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Diff View", "ProgramDiffPlugin", false);
waitForSwing();
OpenVersionedFileDialog dialog = (OpenVersionedFileDialog) getDialog();
@SuppressWarnings("unchecked")
OpenVersionedFileDialog<Program> dialog = (OpenVersionedFileDialog<Program>) getDialog();
DataTree dataTree = (DataTree) findComponentByName(dialog.getComponent(), "Data Tree");
selectPath(dataTree, env.getProject().getName(), "WinHelloCpp.exe");
JButton historyButton = findButtonByText(dialog.getComponent(), "History>>");
@ -70,13 +69,34 @@ public class DiffScreenShots extends GhidraScreenShotGenerator {
captureDialog();
}
@Test
public void testSelectOpenProgram() throws Exception {
addProgramsToProject();
createProgramVersions();
runSwing(() -> {
Project project = env.getProject();
ProjectData projectData = project.getProjectData();
DomainFile file = projectData.getRootFolder().getFile("OldWinHelloCpp.exe");
ProgramManager pm = tool.getService(ProgramManager.class);
pm.openProgram(file);
});
waitForSwing();
performAction("Open/Close Diff View", "ProgramDiffPlugin", false);
waitForSwing();
@SuppressWarnings("unchecked")
OpenVersionedFileDialog<Program> dialog = (OpenVersionedFileDialog<Program>) getDialog();
JTabbedPane tabPane = (JTabbedPane) findComponentByName(dialog.getComponent(), "Tabs");
runSwing(() -> tabPane.setSelectedIndex(1)); // select Open Programs tab
captureDialog(dialog.getPreferredSize().width, 500);
}
@Test
public void testDetermineDiffs() throws Exception {
addProgramsToProject();
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Diff View", "ProgramDiffPlugin", false);
waitForSwing();
OpenVersionedFileDialog dialog = (OpenVersionedFileDialog) getDialog();
@SuppressWarnings("unchecked")
OpenVersionedFileDialog<Program> dialog = (OpenVersionedFileDialog<Program>) getDialog();
DataTree dataTree = (DataTree) findComponentByName(dialog.getComponent(), "Data Tree");
selectPath(dataTree, env.getProject().getName(), "WinHelloCpp.exe");
waitForSwing();
@ -89,10 +109,10 @@ public class DiffScreenShots extends GhidraScreenShotGenerator {
public void testDiff() throws Exception {
addProgramsToProject();
createDifferences();
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Diff View", "ProgramDiffPlugin", false);
waitForSwing();
OpenVersionedFileDialog dialog = (OpenVersionedFileDialog) getDialog();
@SuppressWarnings("unchecked")
OpenVersionedFileDialog<Program> dialog = (OpenVersionedFileDialog<Program>) getDialog();
DataTree dataTree = (DataTree) findComponentByName(dialog.getComponent(), "Data Tree");
selectPath(dataTree, env.getProject().getName(), "WinHelloCpp.exe");
waitForSwing();
@ -111,10 +131,10 @@ public class DiffScreenShots extends GhidraScreenShotGenerator {
public void testDiffApplySettings() throws Exception {
addProgramsToProject();
createDifferences();
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Diff View", "ProgramDiffPlugin", false);
waitForSwing();
OpenVersionedFileDialog dialog = (OpenVersionedFileDialog) getDialog();
@SuppressWarnings("unchecked")
OpenVersionedFileDialog<Program> dialog = (OpenVersionedFileDialog<Program>) getDialog();
DataTree dataTree = (DataTree) findComponentByName(dialog.getComponent(), "Data Tree");
selectPath(dataTree, env.getProject().getName(), "WinHelloCpp.exe");
waitForSwing();
@ -149,10 +169,10 @@ public class DiffScreenShots extends GhidraScreenShotGenerator {
public void testDiffDetails() throws Exception {
addProgramsToProject();
createDifferencesAt401417();
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Program View", "ProgramDiffPlugin", false);
performAction("Open/Close Diff View", "ProgramDiffPlugin", false);
waitForSwing();
OpenVersionedFileDialog dialog = (OpenVersionedFileDialog) getDialog();
@SuppressWarnings("unchecked")
OpenVersionedFileDialog<Program> dialog = (OpenVersionedFileDialog<Program>) getDialog();
DataTree dataTree = (DataTree) findComponentByName(dialog.getComponent(), "Data Tree");
selectPath(dataTree, env.getProject().getName(), "WinHelloCpp.exe");
waitForSwing();