diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java
index 9d13288744..f2727b03f8 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java
@@ -95,7 +95,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin
private DataTypeManagerHandler dataTypeManagerHandler;
private DataTypesProvider provider;
- private OpenVersionedFileDialog openDialog;
+ private OpenVersionedFileDialog
The Open/Close Diff View icon
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, Tools
@@ -70,9 +70,9 @@
Program Differences... . This is one of two ways to open the Diff - tool. The other is the Open/Close Program Diff icon + tool. The other is the Open/Close Diff View icon
- in the Code Browser tool bar which is described above in Open/Close Diff View. + in the Code Browser tool bar which is described above in Opening and Closing the Diff View. The following describes using the menu action to open the Diff tool.
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).
- If you wish to Diff against a previous version of a - versioned program, select the History>> - 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.
+ If you wish to Diff against a specific version of a + versioned program, a specific version may be seleected from the + Version History table. The project file history panel is displayed + when the History>> button is clicked. + Select the versioned program from the tree. + Next select the desired version of the program from the Version History + table on the right side of the dialog.
+ +![]()
+ If you wish to Diff against another program which is already open in the same tool + you can select a compatible program from the Open Programs tab. Programs + which are not compatible (e.g., different architecture) are not shown in the table. + The Open Programs tab is not available if compatible open programs are not + found. You may return to the project file tree selection panel by clicking the + Project Files tab. +
+![]()
+Click the OK button. The Determine Program Differences dialog is displayed. @@ -269,7 +282,7 @@ - + diff --git a/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOpenProgram.png b/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOpenProgram.png new file mode 100644 index 0000000000..239796f283 Binary files /dev/null and b/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOpenProgram.png differ diff --git a/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherProgram.png b/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherProgram.png index 201c7847e5..99b79cb0d3 100644 Binary files a/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherProgram.png and b/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherProgram.png differ diff --git a/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherVersionedProgram.png b/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherVersionedProgram.png index dc6412956e..825f2a340f 100644 Binary files a/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherVersionedProgram.png and b/Ghidra/Features/ProgramDiff/src/main/help/help/topics/Diff/images/SelectOtherVersionedProgram.png differ diff --git a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/DiffActionManager.java b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/DiffActionManager.java index 3d96eb7732..871d870f42 100644 --- a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/DiffActionManager.java +++ b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/DiffActionManager.java @@ -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( "![]()
Close Diff View
" + "Current diff: " + ""+HTMLUtilities.escapeHTML(firstName)+" to " +HTMLUtilities.escapeHTML(secondName)+""); @@ -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; + } + } } diff --git a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java index 29dc54331a..3d26cbb5c1 100644 --- a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java +++ b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java @@ -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 OpenVersionedFileDialogopenVersionedFileDialog; /** * 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 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 getOpenProgramList() { + List primaryList = new ArrayList<>(); + List 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 programComparator = (a, b) -> { + return a.getDomainFile().getName().compareTo(b.getDomainFile().getName()); + }; + Collections.sort(primaryList, programComparator); + Collections.sort(secondaryList, programComparator); + + List programList = new ArrayList<>(); + programList.addAll(primaryList); + programList.addAll(secondaryList); + return programList; + } + + private void selectAndOpenProgram2() { + final OpenVersionedFileDialog dialog = getOpenVersionedFileDialog(); + + List 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 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 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) { diff --git a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/services/DiffService.java b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/services/DiffService.java index dbf9a10065..6a67a369fa 100644 --- a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/services/DiffService.java +++ b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/services/DiffService.java @@ -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(); } diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffSaveSettingsTest.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffSaveSettingsTest.java index 2987ff4916..29fe6236f2 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffSaveSettingsTest.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffSaveSettingsTest.java @@ -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 diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java index b4ed548e7e..f0c01a99c8 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java @@ -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(); diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java index ac7f16fff2..405b2b5ede 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java @@ -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 { 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; } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java index f9e65aacd5..110482a6c3 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java @@ -135,7 +135,9 @@ public abstract class LinkHandler 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 diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java index 3ea00eebac..71b72a1790 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java @@ -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()); } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java index bbcbab9d2e..b43abc176d 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DiffScreenShots.java @@ -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 dialog = (OpenVersionedFileDialog ) 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 dialog = (OpenVersionedFileDialog ) 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 dialog = (OpenVersionedFileDialog ) 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 dialog = (OpenVersionedFileDialog ) 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 dialog = (OpenVersionedFileDialog ) 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 dialog = (OpenVersionedFileDialog ) getDialog(); DataTree dataTree = (DataTree) findComponentByName(dialog.getComponent(), "Data Tree"); selectPath(dataTree, env.getProject().getName(), "WinHelloCpp.exe"); waitForSwing();