diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index a0a710f186..5c4b89797b 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -312,11 +312,11 @@ src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectType.png||GHI src/main/help/help/topics/FrontEndPlugin/images/ServerInfo.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/SharedProjectInfo.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/UsersPanel.png||GHIDRA||||END| -src/main/help/help/topics/FrontEndPlugin/images/UsersPanelMini.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/VersionedFileCOnoServer.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/VersionedFileCOwithServer.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/VersionedFileIcon.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png||GHIDRA||||END| +src/main/help/help/topics/FrontEndPlugin/images/ViewProjectAccessPanel.png||GHIDRA||||END| src/main/help/help/topics/FrontEndPlugin/images/hijack_file.png||GHIDRA||||END| src/main/help/help/topics/FunctionComparison/FunctionComparison.htm||GHIDRA||||END| src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png||GHIDRA||||END| diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm index 56dce2da44..c5766675c9 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Ghidra_Front_end.htm @@ -917,7 +917,7 @@ "help/shared/arrow.gif" border="0">View Project Access List will be enabled, which will display the following dialog and allow the user to view the project users and their current access privileges only.

-

+

diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveFileExists.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveFileExists.png index 003714b9cc..3813aa8ee3 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveFileExists.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveFileExists.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveProject.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveProject.png index 6bfb8baa57..05831c64b5 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveProject.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ArchiveProject.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeAccessList.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeAccessList.png index 365070089e..d1c1f8f983 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeAccessList.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeAccessList.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangePassword.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangePassword.png index 1d94f040cc..19ec42776b 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangePassword.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangePassword.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeRepositoryPanel.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeRepositoryPanel.png index 2ccf0c59ae..8ab0adedbd 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeRepositoryPanel.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeRepositoryPanel.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeServerInfoPanel.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeServerInfoPanel.png index 26ba2831d0..267a1d9801 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeServerInfoPanel.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ChangeServerInfoPanel.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureExtensions.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureExtensions.png index f2785eeb03..7670d97f4f 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureExtensions.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureExtensions.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureTool.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureTool.png index 003d5f4a47..b11e2cef60 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureTool.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfigureTool.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmChangePassword.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmChangePassword.png index e20e032db8..5366e322d0 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmChangePassword.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmChangePassword.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmDeleteProject.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmDeleteProject.png index 9d05b0addd..05de979d1e 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmDeleteProject.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConfirmDeleteProject.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConnectTools.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConnectTools.png index bf90dba5dd..4aae14590c 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConnectTools.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ConnectTools.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png index 9285a463fc..2394f74f91 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png index fe4537e1d0..b6589f752c 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png index df7dc1cd95..1a0d730f31 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/LinkOtherProject.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/LinkOtherProject.png index 15d4c8bcaa..7ac46d4370 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/LinkOtherProject.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/LinkOtherProject.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png index 24a00b7bc4..d05da5a65c 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png index 09abf9b48c..09450d416b 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTable.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTable.png index 6e4906ad68..a8bf774690 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTable.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTable.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTree.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTree.png index 47b62fcb71..aa28c01b76 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTree.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectDataTree.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectWindow.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectWindow.png index 59ade2cbf3..257fef7dc9 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectWindow.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ProjectWindow.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ReopenProject.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ReopenProject.png index 5801e475fc..7fa0ae16b4 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ReopenProject.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ReopenProject.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RepositoryNamePanel.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RepositoryNamePanel.png index 9aabcc88d0..93d1471734 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RepositoryNamePanel.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RepositoryNamePanel.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RestoreProjectFilledIn.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RestoreProjectFilledIn.png index d86a95363c..b54f605787 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RestoreProjectFilledIn.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/RestoreProjectFilledIn.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveFiles.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveFiles.png index ab9fa6a0e0..2273e63061 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveFiles.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveFiles.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveReadOnly.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveReadOnly.png index 5ecff9b18a..84da54e497 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveReadOnly.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SaveReadOnly.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectLocation.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectLocation.png index bd5629b072..8194ecd27b 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectLocation.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectLocation.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectType.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectType.png index eb1fd6fa44..eacadbef90 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectType.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectProjectType.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectLocation.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectLocation.png index 07177139e9..8c306a9287 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectLocation.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectLocation.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectType.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectType.png index f80165f9a8..ab4874b9e3 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectType.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SelectSharedProjectType.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ServerInfo.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ServerInfo.png index d778ddd0dc..cc41854bc6 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ServerInfo.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ServerInfo.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SharedProjectInfo.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SharedProjectInfo.png index 662dfdcb0e..83a6ac090f 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SharedProjectInfo.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/SharedProjectInfo.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/UsersPanelMini.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/UsersPanelMini.png deleted file mode 100644 index daaeeecd38..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/UsersPanelMini.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png index 95518a3f13..d2f7c74b4a 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ViewProjectAccessPanel.png b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ViewProjectAccessPanel.png new file mode 100644 index 0000000000..3be025c21a Binary files /dev/null and b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/images/ViewProjectAccessPanel.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonPanel.java index f2c445003b..987e6eb2f0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingCodeComparisonPanel.java @@ -601,6 +601,8 @@ public class ListingCodeComparisonPanel private void updateProgramViews() { displays.get(LEFT).setProgramView(getProgram(LEFT), getAddresses(LEFT), "listing1"); displays.get(RIGHT).setProgramView(getProgram(RIGHT), getAddresses(RIGHT), "listing2"); + displays.get(LEFT).goTo(comparisonData.get(LEFT).getInitialLocation()); + displays.get(RIGHT).goTo(comparisonData.get(LEFT).getInitialLocation()); } private void nextAreaDiff(boolean forward) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingDisplay.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingDisplay.java index 1862805462..943d36fe78 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingDisplay.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/listing/ListingDisplay.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -174,10 +174,6 @@ public class ListingDisplay implements ListingDiffChangeListener { markerManager.getOverviewProvider().setProgram(program, indexMap); listingPanel.setBackgroundColorModel( new MarkerServiceBackgroundColorModel(markerManager, program, indexMap)); - setUpAreaMarkerSets(program, name); - if (!view.isEmpty()) { - goTo(new ProgramLocation(program, view.getMinAddress())); - } repaint(); } @@ -189,8 +185,9 @@ public class ListingDisplay implements ListingDiffChangeListener { Color unmatchedColor = comparisonOptions.getUnmatchedCodeUnitsBackgroundColor(); AddressIndexMap indexMap = listingPanel.getAddressIndexMap(); - listingPanel.getFieldPanel().setBackgroundColorModel(new MarkerServiceBackgroundColorModel( - markerManager, program, indexMap)); + listingPanel.getFieldPanel() + .setBackgroundColorModel(new MarkerServiceBackgroundColorModel( + markerManager, program, indexMap)); unmatchedMarkers = markerManager.createAreaMarker(name + " Unmatched Code", "Instructions that are not matched to an instruction in the other function.", @@ -307,8 +304,9 @@ public class ListingDisplay implements ListingDiffChangeListener { } public void setViewerPosition(ViewerPosition position) { - listingPanel.getFieldPanel().setViewerPosition(position.getIndex(), position.getXOffset(), - position.getYOffset()); + listingPanel.getFieldPanel() + .setViewerPosition(position.getIndex(), position.getXOffset(), + position.getYOffset()); } public void setMouseNavigationEnabled(boolean enabled) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/AddressSetComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/AddressSetComparisonData.java index 785d2706d6..0866a9240e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/AddressSetComparisonData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/AddressSetComparisonData.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,6 +21,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; import ghidra.util.HTMLUtilities; /** @@ -77,4 +78,9 @@ public class AddressSetComparisonData implements ComparisonData { public boolean isEmpty() { return addresses.isEmpty(); } + + @Override + public ProgramLocation getInitialLocation() { + return new ProgramLocation(program, addresses.getMinAddress()); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/ComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/ComparisonData.java index a4a6db0366..e5c705e35a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/ComparisonData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/ComparisonData.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,6 +21,7 @@ import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; /** * ComparisonData is an abstract of items that can be compared in a {@link CodeComparisonPanel}. @@ -68,4 +69,9 @@ public interface ComparisonData { */ public boolean isEmpty(); + /** + * Returns the initial program location to put the cursor when the panel is first displayed + */ + public ProgramLocation getInitialLocation(); + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/DataComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/DataComparisonData.java index 859d6ab907..b683968293 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/DataComparisonData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/DataComparisonData.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,6 +20,7 @@ import java.util.Objects; import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.util.ProgramLocation; import ghidra.util.HTMLUtilities; /** @@ -108,4 +109,9 @@ public class DataComparisonData implements ComparisonData { } return endAddress; } + + @Override + public ProgramLocation getInitialLocation() { + return new ProgramLocation(data.getProgram(), data.getMinAddress()); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/EmptyComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/EmptyComparisonData.java index 36313401c3..f1153afe5c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/EmptyComparisonData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/EmptyComparisonData.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,6 +19,7 @@ import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; public class EmptyComparisonData implements ComparisonData { @@ -51,4 +52,9 @@ public class EmptyComparisonData implements ComparisonData { public boolean isEmpty() { return true; } + + @Override + public ProgramLocation getInitialLocation() { + return null; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonData.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonData.java index 4c8080f4f1..bf3f3b3e05 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/FunctionComparisonData.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,6 +21,8 @@ import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; +import ghidra.program.util.FunctionSignatureFieldLocation; +import ghidra.program.util.ProgramLocation; import ghidra.util.HTMLUtilities; /** @@ -84,4 +86,9 @@ public class FunctionComparisonData implements ComparisonData { return false; } + @Override + public ProgramLocation getInitialLocation() { + return new FunctionSignatureFieldLocation(function.getProgram(), function.getEntryPoint()); + } + } diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/CorrelatorPanel.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/CorrelatorPanel.png index 1e2d0de7e1..1183bc38c0 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/CorrelatorPanel.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/CorrelatorPanel.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/FunctionsTable.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/FunctionsTable.png index aa3346a298..ea3bb74b72 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/FunctionsTable.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/FunctionsTable.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchExample.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchExample.png index e9c49688d0..d92474d5fa 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchExample.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchExample.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchesTable.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchesTable.png index 972afa995f..9e3f723585 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchesTable.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/ImpliedMatchesTable.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MarkupItemsFilters.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MarkupItemsFilters.png index 60c0b12dc0..ea57266b48 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MarkupItemsFilters.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MarkupItemsFilters.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MatchesTable.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MatchesTable.png index e4b4e776e9..b8a79f0a05 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MatchesTable.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/MatchesTable.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManyDestination.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManyDestination.png index 10852acd80..85101ce11c 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManyDestination.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManyDestination.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManySource.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManySource.png index eab5c44609..804ac2aded 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManySource.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OneToManySource.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OptionsPanel.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OptionsPanel.png index 0f4142b5c8..c576bd0497 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OptionsPanel.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/OptionsPanel.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/PreconditionsPanel.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/PreconditionsPanel.png index e2fdbf5b63..c4a5d00996 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/PreconditionsPanel.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/PreconditionsPanel.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SessionPanel.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SessionPanel.png index 07a1da6616..0d52705142 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SessionPanel.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SessionPanel.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SourceTool.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SourceTool.png index 84456dc66a..a961b6128f 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SourceTool.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SourceTool.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SummaryPanel.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SummaryPanel.png index da412c9526..11acd81c13 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SummaryPanel.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/SummaryPanel.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_AcceptMatchDialog.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_AcceptMatchDialog.png index b82a44aeec..c25f9ffd6c 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_AcceptMatchDialog.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_AcceptMatchDialog.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png index e231e936dd..dd63936868 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddToSession_Summary.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddToSession_Summary.png index 0dd9503b8f..06a07141a3 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddToSession_Summary.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddToSession_Summary.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddressSetOptions.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddressSetOptions.png index 770942d6da..a6691d38a2 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddressSetOptions.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_AddressSetOptions.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_SelectAddressRanges.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_SelectAddressRanges.png index d0f63e94d6..f374add593 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_SelectAddressRanges.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VT_Wizard_SelectAddressRanges.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItems.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItems.png index 225dc9e931..797433fe06 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItems.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItems.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsHeader.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsHeader.png index 6faae2059d..43839123db 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsHeader.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsHeader.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsSideBySide.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsSideBySide.png index 5b3b091a30..bbded2b11a 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsSideBySide.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsSideBySide.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsTableOnly.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsTableOnly.png index 5a1d139a99..6fd68cbee3 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsTableOnly.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingMarkupItemsTableOnly.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingTool.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingTool.png index afc82e88d6..4642b4d335 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingTool.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingTool.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AddToVersionTrackingSessionAction.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AddToVersionTrackingSessionAction.java index 64ee1c57dd..f855950287 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AddToVersionTrackingSessionAction.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/AddToVersionTrackingSessionAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,11 +20,11 @@ import javax.swing.Icon; import docking.ActionContext; import docking.action.*; import docking.tool.ToolConstants; -import docking.wizard.WizardManager; +import docking.wizard.WizardDialog; import generic.theme.GIcon; import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.plugin.VTPlugin; -import ghidra.feature.vt.gui.wizard.VTAddToSessionWizardManager; +import ghidra.feature.vt.gui.wizard.add.VTAddToSessionWizardModel; import ghidra.util.HelpLocation; public class AddToVersionTrackingSessionAction extends DockingAction { @@ -46,10 +46,9 @@ public class AddToVersionTrackingSessionAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { - VTAddToSessionWizardManager vtWizardManager = new VTAddToSessionWizardManager(controller); - WizardManager wizardManager = - new WizardManager("Version Tracking Wizard", true, vtWizardManager); - wizardManager.showWizard(controller.getParentComponent()); + VTAddToSessionWizardModel model = new VTAddToSessionWizardModel(controller); + WizardDialog wizardDialog = new WizardDialog(model); + wizardDialog.show(controller.getParentComponent()); } @Override diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/CreateVersionTrackingSessionAction.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/CreateVersionTrackingSessionAction.java index 4f749a718a..5b446f09b9 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/CreateVersionTrackingSessionAction.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/actions/CreateVersionTrackingSessionAction.java @@ -21,11 +21,11 @@ import docking.ActionContext; import docking.action.*; import docking.tool.ToolConstants; import docking.widgets.OptionDialog; -import docking.wizard.WizardManager; +import docking.wizard.WizardDialog; import generic.theme.GIcon; import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.plugin.VTPlugin; -import ghidra.feature.vt.gui.wizard.VTNewSessionWizardManager; +import ghidra.feature.vt.gui.wizard.session.VTNewSessionWizardModel; import ghidra.util.HelpLocation; public class CreateVersionTrackingSessionAction extends DockingAction { @@ -58,10 +58,9 @@ public class CreateVersionTrackingSessionAction extends DockingAction { if (!controller.closeVersionTrackingSession()) { return; // user cancelled during save dialog } - VTNewSessionWizardManager vtWizardManager = new VTNewSessionWizardManager(controller); - WizardManager wizardManager = - new WizardManager("Version Tracking Wizard", true, vtWizardManager); - wizardManager.showWizard(controller.getParentComponent()); + VTNewSessionWizardModel model = new VTNewSessionWizardModel(controller); + WizardDialog wizardDialog = new WizardDialog(model); + wizardDialog.show(controller.getParentComponent()); } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java index eff11789d7..6c2e876af6 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,7 +24,7 @@ import javax.swing.JFrame; import docking.action.DockingActionIf; import docking.tool.ToolConstants; -import docking.wizard.WizardManager; +import docking.wizard.WizardDialog; import generic.theme.GIcon; import ghidra.GhidraOptions; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; @@ -37,7 +37,7 @@ import ghidra.feature.vt.gui.provider.functionassociation.VTFunctionAssociationP import ghidra.feature.vt.gui.provider.impliedmatches.*; import ghidra.feature.vt.gui.provider.markuptable.VTMarkupItemsTableProvider; import ghidra.feature.vt.gui.provider.matchtable.VTMatchTableProvider; -import ghidra.feature.vt.gui.wizard.VTNewSessionWizardManager; +import ghidra.feature.vt.gui.wizard.session.VTNewSessionWizardModel; import ghidra.framework.model.*; import ghidra.framework.options.Options; import ghidra.framework.options.SaveState; @@ -268,11 +268,10 @@ public class VTPlugin extends Plugin { if (!controller.closeVersionTrackingSession()) { return false; // user cancelled during save dialog } - VTNewSessionWizardManager vtWizardManager = - new VTNewSessionWizardManager(controller, programFile1, programFile2); - WizardManager wizardManager = - new WizardManager("Version Tracking Wizard", true, vtWizardManager); - wizardManager.showWizard(tool.getToolFrame()); + VTNewSessionWizardModel model = + new VTNewSessionWizardModel(controller, programFile1, programFile2); + WizardDialog wizardDialog = new WizardDialog(model); + wizardDialog.show(tool.getToolFrame()); return true; } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/SaveTask.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/SaveTask.java index bb0efe1df2..6c3dddb168 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/SaveTask.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/task/SaveTask.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +15,7 @@ */ package ghidra.feature.vt.gui.task; -import ghidra.feature.vt.gui.wizard.VTWizardUtils; +import ghidra.feature.vt.gui.wizard.session.VTWizardUtils; import ghidra.framework.model.DomainFile; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressRangeListener.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressRangeListener.java deleted file mode 100644 index f3cf720223..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressRangeListener.java +++ /dev/null @@ -1,25 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import ghidra.program.model.address.Address; - -public interface AddressRangeListener { - - public void processAddressRange(Address minAddress, Address maxAddress); - -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressSetOptionsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressSetOptionsPanel.java deleted file mode 100644 index 4c919164ec..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressSetOptionsPanel.java +++ /dev/null @@ -1,152 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import java.util.List; - -import javax.swing.BorderFactory; -import javax.swing.JCheckBox; - -import docking.widgets.checkbox.GCheckBox; -import docking.wizard.*; -import ghidra.feature.vt.api.main.VTProgramCorrelatorAddressRestrictionPreference; -import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; -import ghidra.program.model.address.AddressSetView; -import ghidra.util.HTMLUtilities; -import ghidra.util.HelpLocation; -import ghidra.util.layout.VerticalLayout; - -public class AddressSetOptionsPanel extends AbstractMageJPanel { - - private JCheckBox excludeCheckbox; - private JCheckBox showAddressSetPanelsCheckbox; - - public AddressSetOptionsPanel() { // - setBorder(BorderFactory.createEmptyBorder(40, 40, 0, 0)); - - excludeCheckbox = new GCheckBox("Exclude accepted matches", false); - String excludeAcceptedTooltip = "This option will cause the correlator algorithm " + - "to not consider any functions or data that have already been " + - "accepted. Using this option can greatly speed up the processing time " + - "of the correlator algorithm; however, this options should only be " + - "used when you trust that your accepted matches are correct."; - excludeCheckbox.setToolTipText(HTMLUtilities.toWrappedHTML(excludeAcceptedTooltip)); - - showAddressSetPanelsCheckbox = new GCheckBox("Limit source and destination address sets"); - String manuallyLimitTooltip = "Selecting this checkbox will trigger additional wizard " + - " panels allowing you to customize the address sets used " + - " by the selected algorithm. When not selected, the entire address space is used."; - - showAddressSetPanelsCheckbox.setToolTipText( - HTMLUtilities.toWrappedHTML(manuallyLimitTooltip)); - - add(excludeCheckbox); - add(showAddressSetPanelsCheckbox); - setLayout(new VerticalLayout(20)); - } - - @Override - public void addDependencies(WizardState state) { - // none - } - - @Override - public void dispose() { - // nothing to do - } - - @Override - public void enterPanel(WizardState state) { - @SuppressWarnings("unchecked") - List list = (List) state.get( - VTWizardStateKey.PROGRAM_CORRELATOR_FACTORY_LIST); - - Boolean value = (Boolean) state.get(VTWizardStateKey.EXCLUDE_ACCEPTED_MATCHES); - if (value != null) { - excludeCheckbox.setSelected(value.booleanValue()); - } - value = (Boolean) state.get(VTWizardStateKey.SHOW_ADDRESS_SET_PANELS); - if (value != null) { - showAddressSetPanelsCheckbox.setSelected(value.booleanValue()); - } - else { - AddressSetView sourceSelection = - (AddressSetView) state.get(VTWizardStateKey.SOURCE_SELECTION); - AddressSetView destinationSelection = - (AddressSetView) state.get(VTWizardStateKey.DESTINATION_SELECTION); - boolean somethingSelected = (sourceSelection != null && !sourceSelection.isEmpty()) || - (destinationSelection != null && !destinationSelection.isEmpty()); - showAddressSetPanelsCheckbox.setSelected(somethingSelected); - } - - if (allowRestrictions(list)) { - excludeCheckbox.setEnabled(true); - } - else { - excludeCheckbox.setSelected(false); - excludeCheckbox.setEnabled(false); - } - - } - - private boolean allowRestrictions(List list) { - for (VTProgramCorrelatorFactory factory : list) { - if (factory.getAddressRestrictionPreference() != VTProgramCorrelatorAddressRestrictionPreference.RESTRICTION_NOT_ALLOWED) { - return true; - } - } - return false; - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - return WizardPanelDisplayability.CAN_BE_DISPLAYED; - } - - @Override - public void leavePanel(WizardState state) { - updateStateObjectWithPanelInfo(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - state.put(VTWizardStateKey.EXCLUDE_ACCEPTED_MATCHES, excludeCheckbox.isSelected()); - state.put(VTWizardStateKey.SHOW_ADDRESS_SET_PANELS, - showAddressSetPanelsCheckbox.isSelected()); - } - - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation("VersionTrackingPlugin", "Address_Set_Panel"); - } - - @Override - public String getTitle() { - return "Address Set Options"; - } - - @Override - public void initialize() { - // nothing to do - } - - @Override - public boolean isValidInformation() { - return true; - } - -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressSetPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressSetPanel.java deleted file mode 100644 index 8224cd2533..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddressSetPanel.java +++ /dev/null @@ -1,156 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import ghidra.feature.vt.gui.wizard.ChooseAddressSetEditorPanel.AddressSetChoice; -import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.Program; -import ghidra.util.HelpLocation; - -import java.awt.BorderLayout; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import docking.wizard.*; - -public class AddressSetPanel extends AbstractMageJPanel { - - private final PluginTool tool; - private final String name; - private final VTWizardStateKey programDependencyKey; - private final VTWizardStateKey addressSetViewKey; - private final VTWizardStateKey selectionKey; - private final VTWizardStateKey addressSetChoiceKey; - private ChooseAddressSetEditorPanel panel; - private Program program; - - public AddressSetPanel(PluginTool tool, String name, VTWizardStateKey programFileDependencyKey, - VTWizardStateKey programDependencyKey, VTWizardStateKey addressSetViewKey, - VTWizardStateKey selectionKey, VTWizardStateKey addressSetChoiceKey) { - this.tool = tool; - this.name = name; - this.programDependencyKey = programDependencyKey; - this.addressSetViewKey = addressSetViewKey; - this.selectionKey = selectionKey; - this.addressSetChoiceKey = addressSetChoiceKey; - setLayout(new BorderLayout()); - } - - @Override - public void addDependencies(WizardState state) { - // no dependencies - } - - @Override - public void dispose() { - // nothing to do - } - - // Keep this method for now in case we want it as the default for the entire program address set - // instead of the program's memory address set. - @SuppressWarnings("unused") - private AddressSet getAddressFactoryAddressSet(WizardState state) { - Program programFromState = (Program) state.get(programDependencyKey); - AddressFactory factory = programFromState.getAddressFactory(); - AddressSet everything = new AddressSet(); - AddressSpace[] addressSpaces = factory.getAddressSpaces(); - for (AddressSpace addressSpace : addressSpaces) { - Address minAddress = addressSpace.getMinAddress(); - Address maxAddress = addressSpace.getMaxAddress(); - AddressRangeImpl range = new AddressRangeImpl(minAddress, maxAddress); - everything.add(range); - } - return everything; - } - - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation("VersionTrackingPlugin", "Address_Set_Panel"); - } - - @Override - public void enterPanel(WizardState state) { - if (panel != null) { - remove(panel); - } - program = (Program) state.get(programDependencyKey); - AddressSetView addressSetView = (AddressSetView) state.get(addressSetViewKey); - AddressSetView selection = (AddressSetView) state.get(selectionKey); - AddressSetChoice addressSetChoice = (AddressSetChoice) state.get(addressSetChoiceKey); - if (addressSetChoice == null) { - if (selection != null && !selection.isEmpty()) { - addressSetChoice = AddressSetChoice.SELECTION; - } - else { - addressSetChoice = AddressSetChoice.ENTIRE_PROGRAM; - } - } - panel = - new ChooseAddressSetEditorPanel(tool, name, program, selection, addressSetView, - addressSetChoice); - panel.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - notifyListenersOfValidityChanged(); - } - }); - add(panel, BorderLayout.CENTER); - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - Boolean value = (Boolean) state.get(VTWizardStateKey.SHOW_ADDRESS_SET_PANELS); - boolean showPanel = value == null ? false : value.booleanValue(); - if (!showPanel) { - return WizardPanelDisplayability.DO_NOT_DISPLAY; - } - return WizardPanelDisplayability.CAN_BE_DISPLAYED; - } - - @Override - public void leavePanel(WizardState state) { - updateStateObjectWithPanelInfo(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - AddressSetView addressSetView = panel.getAddressSetView(); - state.put(addressSetViewKey, addressSetView); - state.put(addressSetChoiceKey, panel.getAddressSetChoice()); - } - - @Override - public String getTitle() { - return "Select " + name + " Address Range(s)"; - } - - @Override - public void initialize() { - // not sure if we need this - } - - @Override - public boolean isValidInformation() { - boolean empty = panel.getAddressSetView().isEmpty(); - String msg = empty ? "At least one address range is required" : ""; - notifyListenersOfStatusMessage(msg); - return !empty; - } -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/LimitAddressSetsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/LimitAddressSetsPanel.java deleted file mode 100644 index 1853b6ebdd..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/LimitAddressSetsPanel.java +++ /dev/null @@ -1,109 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import ghidra.framework.plugintool.PluginTool; -import ghidra.util.HelpLocation; - -import java.awt.GridLayout; - -import docking.wizard.*; - -public class LimitAddressSetsPanel extends AbstractMageJPanel { - - private AddressSetPanel sourcePanel; - private AddressSetPanel destinationPanel; - - public LimitAddressSetsPanel(PluginTool tool) { - - setLayout(new GridLayout()); - sourcePanel = - new AddressSetPanel(tool, "Source", VTWizardStateKey.SOURCE_PROGRAM_FILE, - VTWizardStateKey.SOURCE_PROGRAM, VTWizardStateKey.SOURCE_ADDRESS_SET_VIEW, - VTWizardStateKey.SOURCE_SELECTION, VTWizardStateKey.SOURCE_ADDRESS_SET_CHOICE); - destinationPanel = - new AddressSetPanel(tool, "Destination", VTWizardStateKey.DESTINATION_PROGRAM_FILE, - VTWizardStateKey.DESTINATION_PROGRAM, - VTWizardStateKey.DESTINATION_ADDRESS_SET_VIEW, - VTWizardStateKey.DESTINATION_SELECTION, - VTWizardStateKey.DESTINATION_ADDRESS_SET_CHOICE); - add(sourcePanel); - add(destinationPanel); - } - - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation("VersionTrackingPlugin", "Select_Address_Ranges_Panel"); - } - - @Override - public void addDependencies(WizardState state) { - sourcePanel.addDependencies(state); - destinationPanel.addDependencies(state); - } - - @Override - public void dispose() { - sourcePanel.dispose(); - destinationPanel.dispose(); - } - - @Override - public void enterPanel(WizardState state) { - sourcePanel.enterPanel(state); - destinationPanel.enterPanel(state); - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - WizardPanelDisplayability sourceDisplayability = - sourcePanel.getPanelDisplayabilityAndUpdateState(state); - destinationPanel.getPanelDisplayabilityAndUpdateState(state); - // Use the displayability of the source panel as that of the source/destination combined. - return sourceDisplayability; - } - - @Override - public void leavePanel(WizardState state) { - sourcePanel.leavePanel(state); - destinationPanel.leavePanel(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - sourcePanel.updateStateObjectWithPanelInfo(state); - destinationPanel.updateStateObjectWithPanelInfo(state); - } - - @Override - public String getTitle() { - return "Select Address Range(s)"; - } - - @Override - public void initialize() { - sourcePanel.initialize(); - destinationPanel.initialize(); - } - - @Override - public boolean isValidInformation() { - return sourcePanel.isValidInformation() && destinationPanel.isValidInformation(); - - } -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/NewSessionPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/NewSessionPanel.java deleted file mode 100644 index e4b0894a35..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/NewSessionPanel.java +++ /dev/null @@ -1,611 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import static ghidra.framework.main.DataTreeDialogType.*; - -import java.awt.*; -import java.util.*; - -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; - -import org.apache.commons.lang3.StringUtils; - -import docking.widgets.button.BrowseButton; -import docking.widgets.label.GDLabel; -import docking.wizard.*; -import generic.theme.GIcon; -import generic.theme.GThemeDefaults.Ids.Fonts; -import generic.theme.Gui; -import ghidra.app.util.task.OpenProgramRequest; -import ghidra.app.util.task.OpenProgramTask; -import ghidra.feature.vt.api.util.VTSessionFileUtil; -import ghidra.framework.main.DataTreeDialog; -import ghidra.framework.model.DomainFile; -import ghidra.framework.model.DomainFolder; -import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.listing.Program; -import ghidra.util.*; -import ghidra.util.task.TaskLauncher; - -/** - * Version tracking wizard panel to create a new session. - */ -public class NewSessionPanel extends AbstractMageJPanel { - - // The maximum length to allow for each program's name portion of the session name. - // In the filesystem API, when saved, the session name is restricted to 60 characters. - // The default VTSession name combines the two program names so split the length between them, - // minus text we add below. - private static final int VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH = 28; - private static final int TEXT_FIELD_LENGTH = 40; - private static final Icon SWAP_ICON = new GIcon("icon.version.tracking.new.session.swap"); - private static final Icon INFO_ICON = new GIcon("icon.version.tracking.new.session.info"); - - private JTextField sourceField; - private JTextField destinationField; - private JButton sourceBrowseButton; - private JButton destinationBrowseButton; - private JButton swapProgramsButton; - private JTextField sessionNameField; - private JTextField folderNameField; - private DomainFolder folder; - private PluginTool tool; - - // All program info objects that the user may have opened while using the wizard. We keep - // these around to avoid reopening them and any accompanying upgrading that may be required. - // These will be released when the wizard is finished. - private Map allProgramInfos = new HashMap<>(); - private ProgramInfo sourceProgramInfo; - private ProgramInfo destinationProgramInfo; - - NewSessionPanel(PluginTool tool) { - - this.tool = tool; - setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); - - JLabel folderLabel = new GDLabel("Project folder "); - folderLabel.setHorizontalAlignment(SwingConstants.RIGHT); - folderLabel.setToolTipText("The folder to store the new Version Tracking Session"); - folderNameField = new JTextField(); - Gui.registerFont(folderNameField, Fonts.MONOSPACED); - folderNameField.setEditable(false); // force user to browse to choose - - JButton browseFolderButton = new BrowseButton(); - browseFolderButton.addActionListener(e -> browseDataTreeFolders()); - - JLabel newSessionLabel = new GDLabel("New Session Name: "); - newSessionLabel.setToolTipText("The name for the new Version Tracking Session"); - newSessionLabel.setHorizontalAlignment(SwingConstants.RIGHT); - - sessionNameField = new JTextField(TEXT_FIELD_LENGTH); - sessionNameField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent e) { - // do nothing - } - - @Override - public void insertUpdate(DocumentEvent e) { - notifyListenersOfValidityChanged(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - notifyListenersOfValidityChanged(); - } - }); - - JLabel sourceLabel = new GDLabel("Source Program: "); - sourceLabel.setIcon(INFO_ICON); - sourceLabel.setToolTipText("Analyzed program with markup to transfer"); - sourceLabel.setHorizontalAlignment(SwingConstants.RIGHT); - - JLabel destinationLabel = new GDLabel("Destination Program: "); - destinationLabel.setIcon(INFO_ICON); - destinationLabel.setToolTipText("New program that receives the transferred markup"); - destinationLabel.setHorizontalAlignment(SwingConstants.RIGHT); - - sourceField = new JTextField(TEXT_FIELD_LENGTH); - sourceField.setEditable(false); - - destinationField = new JTextField(TEXT_FIELD_LENGTH); - destinationField.setEditable(false); - - sourceBrowseButton = createSourceBrowseButton(); - destinationBrowseButton = createDestinationBrowseButton(); - - swapProgramsButton = new JButton(SWAP_ICON); - swapProgramsButton.setText("swap"); - swapProgramsButton.setName("SWAP_BUTTON"); - swapProgramsButton.addActionListener(arg0 -> swapPrograms()); - - JPanel mainPanel = new JPanel(new GridBagLayout()); - GridBagConstraints gbc = new GridBagConstraints(); - - gbc.gridx = 0; - gbc.gridy = 0; - mainPanel.add(Box.createVerticalStrut(15), gbc); - - gbc.gridy++; - gbc.fill = GridBagConstraints.HORIZONTAL; - mainPanel.add(folderLabel, gbc); - - gbc.gridx++; - mainPanel.add(folderNameField, gbc); - - gbc.gridx++; - mainPanel.add(Box.createHorizontalStrut(5), gbc); - - gbc.gridx++; - mainPanel.add(browseFolderButton, gbc); - - gbc.gridx = 0; - gbc.gridy++; - mainPanel.add(Box.createVerticalStrut(10), gbc); - - gbc.gridy++; - gbc.fill = GridBagConstraints.HORIZONTAL; - mainPanel.add(newSessionLabel, gbc); - - gbc.gridx++; - mainPanel.add(sessionNameField, gbc); - - gbc.gridx = 0; - gbc.gridy++; - mainPanel.add(Box.createVerticalStrut(15), gbc); - - gbc.gridy++; - gbc.gridwidth = 4; - mainPanel.add(new JSeparator(), gbc); - - gbc.gridy++; - gbc.gridwidth = 1; - mainPanel.add(Box.createVerticalStrut(25), gbc); - - gbc.gridy++; - mainPanel.add(sourceLabel, gbc); - - gbc.gridx++; - mainPanel.add(sourceField, gbc); - - gbc.gridx += 2; - mainPanel.add(sourceBrowseButton, gbc); - - gbc.gridx = 0; - gbc.gridy++; - gbc.fill = GridBagConstraints.NONE; - gbc.gridwidth = 4; - mainPanel.add(swapProgramsButton, gbc); - - gbc.gridwidth = 1; - gbc.gridy++; - gbc.fill = GridBagConstraints.HORIZONTAL; - mainPanel.add(destinationLabel, gbc); - - gbc.gridx++; - mainPanel.add(destinationField, gbc); - - gbc.gridx += 2; - mainPanel.add(destinationBrowseButton, gbc); - - gbc.gridx = 0; - gbc.gridy++; - mainPanel.add(Box.createVerticalStrut(25), gbc); - - gbc.gridy++; - gbc.gridwidth = 4; - mainPanel.add(new JSeparator(), gbc); - - gbc.gridy++; - gbc.gridwidth = 1; - mainPanel.add(Box.createVerticalStrut(60), gbc); - - setLayout(new BorderLayout()); - add(mainPanel, BorderLayout.NORTH); - } - - private void initializePrograms(WizardState state) { - DomainFile source = (DomainFile) state.get(VTWizardStateKey.SOURCE_PROGRAM_FILE); - DomainFile destintation = (DomainFile) state.get(VTWizardStateKey.DESTINATION_PROGRAM_FILE); - - if (source != null) { - setSourceProgram(source); - } - if (destintation != null) { - setDestinationProgram(destintation); - } - } - - /** - * Presents the user with a tree of the existing project folders and allows - * them to pick one - */ - private void browseDataTreeFolders() { - final DataTreeDialog dataTreeDialog = - new DataTreeDialog(this, "Choose a project folder", CHOOSE_FOLDER); - - dataTreeDialog.addOkActionListener(e -> { - dataTreeDialog.close(); - setFolder(dataTreeDialog.getDomainFolder()); - }); - dataTreeDialog.showComponent(); - } - - void setFolder(DomainFolder folder) { - this.folder = folder; - - if (folder != null) { - folderNameField.setText(folder.toString()); - } - else { - folderNameField.setText("< Choose a folder >"); - } - - notifyListenersOfValidityChanged(); - } - - private void setSourceProgram(DomainFile programFile) { - notifyListenersOfStatusMessage(" "); - - String path; - if (programFile == null) { - sourceProgramInfo = null; - path = ""; - } - else { - sourceProgramInfo = - allProgramInfos.computeIfAbsent(programFile, file -> new ProgramInfo(file)); - path = programFile.getPathname(); - } - - sourceField.setText(path); - - updateSessionNameIfBlank(); - notifyListenersOfValidityChanged(); - } - - private void updateSessionNameIfBlank() { - if (!StringUtils.isBlank(sessionNameField.getText())) { - return; - } - if (sourceProgramInfo == null || destinationProgramInfo == null) { - return; - } - - String defaultSessionName = - createVTSessionName(sourceProgramInfo.getName(), destinationProgramInfo.getName()); - sessionNameField.setText(defaultSessionName); - } - - private String createVTSessionName(String sourceName, String destinationName) { - - // if together they are within the bounds just return session name with both full names - if (sourceName.length() + destinationName.length() <= 2 * - VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) { - return "VT_" + sourceName + "_" + destinationName; - } - - // give destination name all space not used by source name - if (sourceName.length() < VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) { - int leftover = VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH - sourceName.length(); - destinationName = StringUtilities.trimMiddle(destinationName, - VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover); - return "VT_" + sourceName + "_" + destinationName; - } - - // give source name all space not used by destination name - if (destinationName.length() < VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) { - int leftover = VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH - destinationName.length(); - sourceName = StringUtilities.trimMiddle(sourceName, - VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover); - return "VT_" + sourceName + "_" + destinationName; - } - - // if both too long, shorten both of them - sourceName = StringUtilities.trimMiddle(sourceName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH); - destinationName = - StringUtilities.trimMiddle(destinationName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH); - - return "VT_" + sourceName + "_" + destinationName; - } - - private void setDestinationProgram(DomainFile programFile) { - notifyListenersOfStatusMessage(" "); - - String path; - if (programFile == null) { - destinationProgramInfo = null; - path = ""; - } - else { - destinationProgramInfo = - allProgramInfos.computeIfAbsent(programFile, file -> new ProgramInfo(file)); - path = programFile.getPathname(); - } - - destinationField.setText(path); - updateSessionNameIfBlank(); - notifyListenersOfValidityChanged(); - } - - private void swapPrograms() { - notifyListenersOfStatusMessage(" "); - - ProgramInfo temp = destinationProgramInfo; - destinationProgramInfo = sourceProgramInfo; - sourceProgramInfo = temp; - - if (sourceProgramInfo != null) { - sourceField.setText(sourceProgramInfo.getPathname()); - } - else { - sourceField.setText(""); - } - - if (destinationProgramInfo != null) { - destinationField.setText(destinationProgramInfo.getPathname()); - } - else { - destinationField.setText(""); - } - - notifyListenersOfValidityChanged(); - } - - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation("VersionTrackingPlugin", "New_Session_Panel"); - } - - private void releaseConsumers() { - - for (ProgramInfo info : allProgramInfos.values()) { - info.release(tool); - } - - allProgramInfos.clear(); - } - - @Override - public void enterPanel(WizardState state) { - initializePrograms(state); - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - return WizardPanelDisplayability.MUST_BE_DISPLAYED; - } - - @Override - public void leavePanel(WizardState state) { - updateStateObjectWithPanelInfo(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - state.put(VTWizardStateKey.SOURCE_PROGRAM_FILE, sourceProgramInfo.getFile()); - state.put(VTWizardStateKey.DESTINATION_PROGRAM_FILE, destinationProgramInfo.getFile()); - state.put(VTWizardStateKey.SOURCE_PROGRAM, sourceProgramInfo.getProgram()); - state.put(VTWizardStateKey.DESTINATION_PROGRAM, destinationProgramInfo.getProgram()); - state.put(VTWizardStateKey.SESSION_NAME, sessionNameField.getText()); - state.put(VTWizardStateKey.NEW_SESSION_FOLDER, folder); - } - - private boolean openProgram(ProgramInfo programInfo) { - - if (programInfo.hasProgram()) { - return true; // already open - } - - OpenProgramTask openProgramTask = new OpenProgramTask(programInfo.getFile(), tool); - new TaskLauncher(openProgramTask, tool.getActiveWindow()); - OpenProgramRequest openProgram = openProgramTask.getOpenProgram(); - programInfo.setProgram(openProgram != null ? openProgram.getProgram() : null); - return programInfo.hasProgram(); - } - - @Override - public String getTitle() { - return "New Version Tracking Session"; - } - - @Override - public void initialize() { - sourceProgramInfo = null; - destinationProgramInfo = null; - sessionNameField.setText(""); - sourceField.setText(""); - destinationField.setText(""); - setFolder(tool.getProject().getProjectData().getRootFolder()); - } - - @Override - public boolean isValidInformation() { - - if (folder == null) { - notifyListenersOfStatusMessage("Choose a project folder to continue!"); - return false; - } - - if (sourceProgramInfo == null || destinationProgramInfo == null) { - return false; - } - - if (sourceProgramInfo.hasSameFile(destinationProgramInfo)) { - notifyListenersOfStatusMessage("Source and Destination Programs must be different"); - releaseConsumers(); - return false; - } - - String name = sessionNameField.getText().trim(); - if (StringUtils.isBlank(name)) { - notifyListenersOfStatusMessage("Please enter a name for this session"); - return false; - } - - try { - tool.getProject().getProjectData().testValidName(name, false); - } - catch (InvalidNameException e) { - notifyListenersOfStatusMessage("'" + name + "' contains invalid characters"); - return false; - } - - DomainFile file = folder.getFile(name); - if (file != null) { - notifyListenersOfStatusMessage( - "'" + file.getPathname() + "' is the name of an existing project file"); - return false; - } - - // Known Issue: Opening programs before comitted to using them (i.e., Next is clicked) seems - // premature and will subject user to prompts about possible checkout and/or upgrades - // with possible slow re-disassembly (see GP-4151) - - if (!isValidDestinationProgramFile() || !isValidSourceProgramFile()) { - return false; - } - - if (!openProgram(sourceProgramInfo)) { - notifyListenersOfStatusMessage( - "Can't open source program " + sourceProgramInfo.getName()); - return false; - } - - if (!openProgram(destinationProgramInfo)) { - notifyListenersOfStatusMessage( - "Can't open destination program " + destinationProgramInfo.getName()); - return false; - } - - notifyListenersOfStatusMessage(" "); - return true; - } - - private boolean isValidSourceProgramFile() { - try { - VTSessionFileUtil.validateSourceProgramFile(sourceProgramInfo.file, false); - } - catch (Exception e) { - notifyListenersOfStatusMessage(e.getMessage()); - return false; - } - return true; - } - - private boolean isValidDestinationProgramFile() { - try { - VTSessionFileUtil.validateDestinationProgramFile(destinationProgramInfo.file, false, - false); - } - catch (Exception e) { - notifyListenersOfStatusMessage(e.getMessage()); - return false; - } - return true; - } - - @Override - public void addDependencies(WizardState state) { - // none - } - - private JButton createSourceBrowseButton() { - JButton button = new BrowseButton(); - button.setName("SOURCE_BUTTON"); - button.addActionListener(e -> { - DomainFile programFile = VTWizardUtils.chooseDomainFile(NewSessionPanel.this, - "a source program", VTWizardUtils.PROGRAM_FILTER, null); - if (programFile != null) { - setSourceProgram(programFile); - } - }); - return button; - } - - private JButton createDestinationBrowseButton() { - JButton button = new BrowseButton(); - button.setName("DESTINATION_BUTTON"); - button.addActionListener(e -> { - DomainFile programFile = VTWizardUtils.chooseDomainFile(NewSessionPanel.this, - "a destination program", VTWizardUtils.PROGRAM_FILTER, null); - if (programFile != null) { - setDestinationProgram(programFile); - } - }); - return button; - } - - @Override - public void dispose() { - releaseConsumers(); - } - - // simple object to track a domain file and its program - private class ProgramInfo { - - private Program program; - private DomainFile file; - - public ProgramInfo(DomainFile file) { - this.file = Objects.requireNonNull(file); - } - - void setProgram(Program program) { - this.program = program; - } - - Program getProgram() { - return program; - } - - DomainFile getFile() { - return file; - } - - String getPathname() { - return file.getPathname(); - } - - String getName() { - return file.getName(); - } - - void release(Object consumer) { - if (program == null) { - return; - } - - if (program.getConsumerList().contains(consumer)) { - program.release(consumer); - } - - program = null; - } - - boolean hasSameFile(ProgramInfo other) { - return file.getPathname().equals(other.getPathname()); - } - - boolean hasProgram() { - return program != null; - } - } -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/OptionsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/OptionsPanel.java deleted file mode 100644 index eecde4e144..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/OptionsPanel.java +++ /dev/null @@ -1,233 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.*; - -import javax.swing.JPanel; -import javax.swing.JScrollPane; - -import docking.options.editor.OptionsEditorPanel; -import docking.wizard.*; -import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; -import ghidra.feature.vt.api.util.VTOptions; -import ghidra.framework.options.EditorStateFactory; -import ghidra.framework.options.Options; -import ghidra.util.HelpLocation; -import ghidra.util.layout.MiddleLayout; -import ghidra.util.layout.VerticalLayout; -import util.CollectionUtils; - -public class OptionsPanel extends AbstractMageJPanel { - - private static final Dimension DEFAULT_PREFERRED_SIZE = new Dimension(650, 350); - - private List optionsEditorPanelList; - private List optionsList = null; - private PropertyChangeListener propertyChangeListener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - notifyListenersOfValidityChanged(); - } - }; - - OptionsPanel() { - super(new BorderLayout()); - // restricting use? - } - - @Override - public HelpLocation getHelpLocation() { - if (optionsList != null) { - for (VTOptions options : optionsList) { - if (options != null) { - HelpLocation helpLocation = options.getOptionsHelpLocation(); - if (helpLocation != null) { - return helpLocation; - } - } - } - } - - // default - return new HelpLocation("VersionTrackingPlugin", "Options_Panel"); - } - - @Override - public Dimension getPreferredSize() { - Dimension preferredSize = super.getPreferredSize(); - if (preferredSize.width < DEFAULT_PREFERRED_SIZE.width) { - return DEFAULT_PREFERRED_SIZE; - } - return preferredSize; - } - - @Override - public void dispose() { - if (optionsEditorPanelList != null) { - removeAll(); - optionsEditorPanelList = null; - optionsList = null; - } - } - - @Override - public void enterPanel(WizardState state) { - dispose(); - List correlatorFactoryList = getCorrelators(state); - - optionsList = getCorrelatorOptions(state); - if (optionsList == null) { - optionsList = generateDefaultOptions(state); - } - - JPanel panel = new JPanel(new VerticalLayout(30)); - - optionsEditorPanelList = new ArrayList(); - for (int i = 0; i < correlatorFactoryList.size(); i++) { - String correlatorName = correlatorFactoryList.get(i).getName(); - String title = correlatorName + " Options"; - if (optionsList.get(i) == null) { - continue; - } - - EditorStateFactory editorStateFactory = new EditorStateFactory(); - Options options = optionsList.get(i); - List optionNames = options.getLeafOptionNames(); - if (optionNames.isEmpty()) { - continue; - } - Collections.sort(optionNames); - OptionsEditorPanel optionsPanel = - new OptionsEditorPanel(title, options, optionNames, editorStateFactory); - optionsPanel.setOptionsPropertyChangeListener(propertyChangeListener); - optionsEditorPanelList.add(optionsPanel); - panel.add(optionsPanel); - } - JPanel outerPanel = new JPanel(new MiddleLayout()); - outerPanel.add(panel); - JScrollPane scrollPane = new JScrollPane(outerPanel); - scrollPane.getVerticalScrollBar().setUnitIncrement(5); - add(scrollPane); - } - - private List generateDefaultOptions(WizardState state) { - List list = new ArrayList(); - List correlatorFactoryList = getCorrelators(state); - for (VTProgramCorrelatorFactory vtProgramCorrelatorFactory : correlatorFactoryList) { - list.add(vtProgramCorrelatorFactory.createDefaultOptions()); - } - return list; - } - - private List getCorrelatorOptions(WizardState state) { - List list = (List) state.get(VTWizardStateKey.PROGRAM_CORRELATOR_OPTIONS_LIST); - if (list == null) { - return null; - } - return CollectionUtils.asList(list, VTOptions.class); - } - - private List getCorrelators(WizardState state) { - List list = (List) state.get(VTWizardStateKey.PROGRAM_CORRELATOR_FACTORY_LIST); - if (list == null) { - return null; - } - return CollectionUtils.asList(list, VTProgramCorrelatorFactory.class); - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - - List tmpOptions = getCorrelatorOptions(state); - if (tmpOptions == null) { - tmpOptions = generateDefaultOptions(state); - } - for (VTOptions vtOptions : tmpOptions) { - if (vtOptions == null) { - continue; - } - - List names = vtOptions.getOptionNames(); - if (names.isEmpty()) { - continue; - } - - return WizardPanelDisplayability.MUST_BE_DISPLAYED; - } - return WizardPanelDisplayability.DO_NOT_DISPLAY; - } - - @Override - public void leavePanel(WizardState state) { - updateStateObjectWithPanelInfo(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - if (optionsList != null) { - applyOptions(); - } - - List newOptions = - optionsList != null ? optionsList : generateDefaultOptions(state); - - state.put(VTWizardStateKey.PROGRAM_CORRELATOR_OPTIONS_LIST, newOptions); - } - - @Override - public String getTitle() { - return "Correlator Options"; - } - - @Override - public void initialize() { - // nothing to do - } - - @Override - public boolean isValidInformation() { - applyOptions(); - for (VTOptions options : optionsList) { - if (options != null) { - if (!options.validate()) { - return false; - } - } - - } - return true; - } - - private void applyOptions() { - for (OptionsEditorPanel panel : optionsEditorPanelList) { - panel.apply(); - } - } - - @Override - public void addDependencies(WizardState state) { - state.addDependency(VTWizardStateKey.PROGRAM_CORRELATOR_OPTIONS_LIST, - VTWizardStateKey.PROGRAM_CORRELATOR_FACTORY_LIST); - - } - -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/PreconditionsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/PreconditionsPanel.java deleted file mode 100644 index 4865f6bee7..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/PreconditionsPanel.java +++ /dev/null @@ -1,252 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.lang.reflect.Constructor; -import java.util.*; -import java.util.List; - -import javax.swing.*; - -import docking.widgets.conditiontestpanel.*; -import docking.wizard.*; -import ghidra.feature.vt.api.main.VTSession; -import ghidra.feature.vt.gui.validator.VTPreconditionValidator; -import ghidra.program.model.listing.Program; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; -import ghidra.util.classfinder.ClassSearcher; - -public class PreconditionsPanel extends AbstractMageJPanel implements Scrollable { - private static final Dimension DEFAULT_SIZE = new Dimension(650, 480); - private ConditionTestPanel conditionsTestPanel; - private boolean testsDone = false; - private VTNewSessionWizardManager wizardManager; - - public PreconditionsPanel(VTNewSessionWizardManager panelManager) { - - this.wizardManager = panelManager; - setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); - setLayout(new BorderLayout()); - - JPanel runButtonPanel = new JPanel(); - runButtonPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); - runButtonPanel.setLayout(new FlowLayout()); - JButton runTestsButton = new JButton("Run Precondition Checks"); - runTestsButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - conditionsTestPanel.runTests(); - } - }); - runButtonPanel.add(runTestsButton); - - JButton skipTestsButton = new JButton("Skip"); - skipTestsButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - conditionsTestPanel.skipTests(); - wizardManager.getWizardManager().next(); - } - }); - runButtonPanel.add(skipTestsButton); - - add(runButtonPanel, BorderLayout.SOUTH); - } - - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation("VersionTrackingPlugin", "Preconditions_Panel"); - } - - @Override - public void addDependencies(WizardState state) { - state.addDependency(VTWizardStateKey.PRECONDITION_CHECKS_RUN, - VTWizardStateKey.SOURCE_PROGRAM_FILE); - state.addDependency(VTWizardStateKey.PRECONDITION_CHECKS_RUN, - VTWizardStateKey.DESTINATION_PROGRAM_FILE); - - state.addDependency(VTWizardStateKey.HIGHEST_PRECONDITION_STATUS, - VTWizardStateKey.PRECONDITION_CHECKS_RUN); - } - - @Override - public void dispose() { - if (conditionsTestPanel != null) { - conditionsTestPanel.cancel(); - } - } - - @Override - public void enterPanel(WizardState state) { - initializeRunState(state); - - if (!testsDone) { - if (conditionsTestPanel != null) { - remove(conditionsTestPanel); - } - conditionsTestPanel = buildConditionPanel(state); - add(conditionsTestPanel, BorderLayout.CENTER); - } - } - - private void initializeRunState(WizardState state) { - Boolean b = (Boolean) state.get(VTWizardStateKey.PRECONDITION_CHECKS_RUN); - testsDone = b == null ? false : b.booleanValue(); - } - - private ConditionTestPanel buildConditionPanel(final WizardState state) { - - Program sourceProgram = (Program) state.get(VTWizardStateKey.SOURCE_PROGRAM); - Program destinationProgram = (Program) state.get(VTWizardStateKey.DESTINATION_PROGRAM); - - VTSession existingResults = (VTSession) state.get(VTWizardStateKey.EXISTING_SESSION); - - List list = - getConditionTests(sourceProgram, destinationProgram, existingResults); - Collections.sort(list, new ConditionsComparator()); - ConditionTestPanel panel = new ConditionTestPanel(list); - panel.addListener(new ConditionTestListener() { - @Override - public void testsCompleted() { - state.put(VTWizardStateKey.PRECONDITION_CHECKS_RUN, Boolean.valueOf(testsDone)); - testsDone(); - } - }); - return panel; - } - - private void testsDone() { - testsDone = true; - notifyListenersOfValidityChanged(); - if (hasAnyErrorStatus()) { - Msg.showError(getClass(), this, "Warning - Serious Precondition failures", - "The precondition checks discovered one or more serious problems. \n\n" - + "If you continue, your version tracking results may be invalid.\n" - + "You should review the errors, cancel this wizard, and correct the problems."); - } - } - - private List getConditionTests(Program sourceProgram, - Program destinationProgram, VTSession existingResults) throws SecurityException { - List list = new ArrayList(); - - List> vtValidatorClasses = - ClassSearcher.getClasses(VTPreconditionValidator.class); - for (Class validatorClass : vtValidatorClasses) { - try { - Constructor ctor = - validatorClass.getConstructor(Program.class, Program.class, VTSession.class); - VTPreconditionValidator validator = - ctor.newInstance(sourceProgram, destinationProgram, existingResults); - list.add(validator); - } - catch (Exception e) { - Msg.error(this, "error including VTPreconditionValidator " + validatorClass, e); - } - } - return list; - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - - initializeRunState(state); - return testsDone ? WizardPanelDisplayability.CAN_BE_DISPLAYED - : WizardPanelDisplayability.MUST_BE_DISPLAYED; - } - - @Override - public void leavePanel(WizardState state) { - updateStateObjectWithPanelInfo(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - state.put(VTWizardStateKey.PRECONDITION_CHECKS_RUN, Boolean.valueOf(testsDone)); - state.put(VTWizardStateKey.HIGHEST_PRECONDITION_STATUS, hasAnyErrorStatus()); - } - - private Boolean hasAnyErrorStatus() { - return conditionsTestPanel.getErrorCount() > 0; - } - - @Override - public String getTitle() { - return "Precondition Checklist"; - } - - @Override - public void initialize() { - // do nothing - } - - @Override - public boolean isValidInformation() { - return testsDone; - } - - @Override - // Overridden to account for the fact that we don't know our preferred size until later in - // the wizard flow. At that point, the initial size of the wizard is already too small. - public Dimension getPreferredSize() { - Dimension superSize = super.getPreferredSize(); - if (superSize.width > DEFAULT_SIZE.width && superSize.height > DEFAULT_SIZE.height) { - return superSize; - } - return DEFAULT_SIZE; - } - - @Override - public Dimension getPreferredScrollableViewportSize() { - return getPreferredSize(); - } - - @Override - public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - return 25; - } - - @Override - public boolean getScrollableTracksViewportHeight() { - return true; - } - - @Override - public boolean getScrollableTracksViewportWidth() { - return true; - } - - @Override - public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - return 10; - } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class ConditionsComparator implements Comparator { - @Override - public int compare(ConditionTester o1, ConditionTester o2) { - return o1.getName().compareTo(o2.getName()); - } - } -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/SummaryPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/SummaryPanel.java deleted file mode 100644 index 33256dec72..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/SummaryPanel.java +++ /dev/null @@ -1,221 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import java.awt.BorderLayout; -import java.util.List; - -import javax.swing.*; - -import docking.widgets.label.GDHtmlLabel; -import docking.wizard.*; -import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; -import ghidra.feature.vt.gui.wizard.ChooseAddressSetEditorPanel.AddressSetChoice; -import ghidra.framework.model.DomainFile; -import ghidra.util.HTMLUtilities; -import ghidra.util.HelpLocation; -import ghidra.util.layout.PairLayout; -import util.CollectionUtils; - -public class SummaryPanel extends AbstractMageJPanel { - private JLabel labelLabel; - private JLabel summaryLabel; - private static String NEW_SUMMARY_PANEL = "New_Session_Summary_Panel"; - private static String ADD_SUMMARY_PANEL = "Add_To_Session_Summary_Panel"; - private String helpName = ADD_SUMMARY_PANEL; - - SummaryPanel() { - - labelLabel = new GDHtmlLabel(); - summaryLabel = new GDHtmlLabel(); - - JPanel mainPanel = new JPanel(new PairLayout(5, 10)); - mainPanel.add(labelLabel); - mainPanel.add(summaryLabel); - - setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); - setLayout(new BorderLayout()); - add(mainPanel, BorderLayout.CENTER); - } - - @Override - public void dispose() { - // nothing to do - } - - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation("VersionTrackingPlugin", helpName); - } - - @Override - public void enterPanel(WizardState state) { - StringBuilder label = new StringBuilder(); - StringBuilder summary = new StringBuilder(); - - label.append(""); - summary.append(""); - - // session mode - - label.append("Operation:"); - String opDescription = (String) state.get(VTWizardStateKey.WIZARD_OP_DESCRIPTION); - helpName = ((opDescription != null) && opDescription.startsWith("New")) ? NEW_SUMMARY_PANEL - : ADD_SUMMARY_PANEL; - summary.append(opDescription); - label.append("
"); - summary.append("
"); - - String sessionName = (String) state.get(VTWizardStateKey.SESSION_NAME); - - label.append("Session Name:"); - summary.append(sessionName); - label.append("
"); - summary.append("
"); - - String sourceProgramName = null; - String destinationProgramName = null; - - DomainFile sourceProgram = (DomainFile) state.get(VTWizardStateKey.SOURCE_PROGRAM_FILE); - sourceProgramName = sourceProgram.getName(); - - DomainFile destinationProgram = - (DomainFile) state.get(VTWizardStateKey.DESTINATION_PROGRAM_FILE); - destinationProgramName = destinationProgram.getName(); - - // source program - - label.append("Source Program:"); - summary.append( - sourceProgramName == null ? "(null)" : HTMLUtilities.escapeHTML(sourceProgramName)); - label.append("
"); - summary.append("
"); - - // destination program - - label.append("Destination Program:"); - summary.append(destinationProgramName == null ? "(null)" - : HTMLUtilities.escapeHTML(destinationProgramName)); - label.append("
"); - summary.append("
"); - - String correlatorLabel = ""; - String correlatorName = null; - - List correlators = getCorrelators(state); - if (correlators != null) { - for (VTProgramCorrelatorFactory correlatorFactory : correlators) { - correlatorName = correlatorFactory.getName(); - - label.append(correlatorLabel + "Program Correlator:"); - summary.append(correlatorName == null ? "(null)" : correlatorName); - label.append("
"); - summary.append("
"); - - } - } - - Boolean excludeAcceptedMatches = - (Boolean) state.get(VTWizardStateKey.EXCLUDE_ACCEPTED_MATCHES); - if (excludeAcceptedMatches != null) { - label.append("Exclude Accepted Matches:"); - summary.append(excludeAcceptedMatches.booleanValue() ? "Yes" : "No"); - label.append("
"); - summary.append("
"); - } - - Boolean showAddressSetPanels = - (Boolean) state.get(VTWizardStateKey.SHOW_ADDRESS_SET_PANELS); - if (showAddressSetPanels != null) { - boolean manuallySpecifiedAddresses = showAddressSetPanels.booleanValue(); - AddressSetChoice sourceAddressSetChoice = - (AddressSetChoice) state.get(VTWizardStateKey.SOURCE_ADDRESS_SET_CHOICE); - AddressSetChoice destinationAddressSetChoice = - (AddressSetChoice) state.get(VTWizardStateKey.DESTINATION_ADDRESS_SET_CHOICE); - String sourceAddressesInfo = - (sourceAddressSetChoice == AddressSetChoice.MANUALLY_DEFINED) ? "Manually Defined" - : ((sourceAddressSetChoice == AddressSetChoice.SELECTION)) - ? "Source Tool Selection" - : "Entire Source Program"; - String destinationAddressesInfo = - (destinationAddressSetChoice == AddressSetChoice.MANUALLY_DEFINED) - ? "Manually Defined" - : ((destinationAddressSetChoice == AddressSetChoice.SELECTION)) - ? "Destination Tool Selection" - : "Entire Destination Program"; - - label.append("Source Address Set:"); - summary.append(sourceAddressesInfo); - label.append("
"); - summary.append("
"); - - label.append("Destination Address Set:"); - summary.append(destinationAddressesInfo); - label.append("
"); - summary.append("
"); - } - - label.append(""); - summary.append(""); - - labelLabel.setText(label.toString()); - summaryLabel.setText(summary.toString()); - } - - private List getCorrelators(WizardState state) { - List list = (List) state.get(VTWizardStateKey.PROGRAM_CORRELATOR_FACTORY_LIST); - if (list == null) { - return null; - } - return CollectionUtils.asList(list, VTProgramCorrelatorFactory.class); - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - return WizardPanelDisplayability.CAN_BE_DISPLAYED; - } - - @Override - public void leavePanel(WizardState state) { - updateStateObjectWithPanelInfo(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - // Do nothing. The summary panel only displays information. - } - - @Override - public String getTitle() { - return "Summary"; - } - - @Override - public void initialize() { - // nothing to do - } - - @Override - public boolean isValidInformation() { - return true; - } - - @Override - public void addDependencies(WizardState state) { - // no dependencies; we just confirm what's going to happen - } -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTAddToSessionWizardManager.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTAddToSessionWizardManager.java deleted file mode 100644 index cdb922b1b3..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTAddToSessionWizardManager.java +++ /dev/null @@ -1,76 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import ghidra.feature.vt.api.main.VTSession; -import ghidra.feature.vt.gui.plugin.VTController; -import ghidra.program.model.listing.Program; -import ghidra.util.task.Task; -import ghidra.util.task.TaskLauncher; - -import java.util.ArrayList; - -import docking.wizard.*; - -public class VTAddToSessionWizardManager extends AbstractMagePanelManager { - - private final VTController controller; - - public VTAddToSessionWizardManager(VTController controller) { - super(new WizardState()); - this.controller = controller; - Program sourceProgram = controller.getSourceProgram(); - Program destinationProgram = controller.getDestinationProgram(); - VTSession session = controller.getSession(); - - WizardState state = getState(); - state.put(VTWizardStateKey.SOURCE_PROGRAM, sourceProgram); - state.put(VTWizardStateKey.DESTINATION_PROGRAM, destinationProgram); - state.put(VTWizardStateKey.SOURCE_PROGRAM_FILE, sourceProgram.getDomainFile()); - state.put(VTWizardStateKey.DESTINATION_PROGRAM_FILE, destinationProgram.getDomainFile()); - state.put(VTWizardStateKey.EXISTING_SESSION, session); - state.put(VTWizardStateKey.SESSION_NAME, session.getName()); - state.put(VTWizardStateKey.WIZARD_OP_DESCRIPTION, "Add to Version Tracking Session"); - state.put(VTWizardStateKey.SOURCE_SELECTION, controller.getSelectionInSourceTool()); - state.put(VTWizardStateKey.DESTINATION_SELECTION, - controller.getSelectionInDestinationTool()); - - } - - protected ArrayList> createPanels() { - ArrayList> panels = - new ArrayList>(); - panels.add(new CorrelatorPanel(controller.getSession())); - panels.add(new OptionsPanel()); - panels.add(new AddressSetOptionsPanel()); - panels.add(new LimitAddressSetsPanel(controller.getTool())); - panels.add(new SummaryPanel()); - return panels; - } - - @Override - protected void doFinish() { - try { - Task task = new AddToSessionTask(controller, getState()); - new TaskLauncher(task, getWizardManager().getComponent()); - } - finally { - getWizardManager().completed(true); - } - } - -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTNewSessionWizardManager.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTNewSessionWizardManager.java deleted file mode 100644 index f56f1212f2..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTNewSessionWizardManager.java +++ /dev/null @@ -1,82 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -import java.util.ArrayList; -import java.util.List; - -import docking.wizard.*; -import ghidra.feature.vt.gui.plugin.VTController; -import ghidra.framework.model.DomainFile; -import ghidra.framework.plugintool.PluginTool; -import ghidra.util.task.Task; -import ghidra.util.task.TaskLauncher; - -public class VTNewSessionWizardManager extends AbstractMagePanelManager { - - private final VTController controller; - - public VTNewSessionWizardManager(VTController controller) { - super(new WizardState()); - this.controller = controller; - getState().put(VTWizardStateKey.WIZARD_OP_DESCRIPTION, "New Version Tracking Session"); - } - - public VTNewSessionWizardManager(VTController controller, DomainFile sourceFile, - DomainFile destinationFile) { - this(controller); - getState().put(VTWizardStateKey.SOURCE_PROGRAM_FILE, sourceFile); - getState().put(VTWizardStateKey.DESTINATION_PROGRAM_FILE, destinationFile); - } - - @Override - protected List> createPanels() { - List> panels = new ArrayList<>(); - panels.add(new NewSessionPanel(controller.getTool())); - panels.add(new PreconditionsPanel(this)); - panels.add(new SummaryPanel()); - return panels; - } - - @Override - protected void doFinish() { - try { - Task task = new CreateNewSessionTask(controller, getState()); - new TaskLauncher(task, getWizardManager().getComponent()); - } - finally { - getWizardManager().completed(true); - cleanup(); - } - } - - @Override - public void cancel() { - cleanup(); - } - - private void cleanup() { - List> panels = getPanels(); - for (MagePanel magePanel : panels) { - magePanel.dispose(); - } - } - - public PluginTool getTool() { - return controller.getTool(); - } - -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTWizardStateKey.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTWizardStateKey.java deleted file mode 100644 index 4e3903426e..0000000000 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTWizardStateKey.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.vt.gui.wizard; - -enum VTWizardStateKey { - WIZARD_OP_DESCRIPTION, EXISTING_SESSION, SESSION_NAME, - - SOURCE_PROGRAM_FILE, DESTINATION_PROGRAM_FILE, - - SOURCE_PROGRAM, DESTINATION_PROGRAM, - - SOURCE_ADDRESS_SET_VIEW, DESTINATION_ADDRESS_SET_VIEW, - - PRECONDITION_CHECKS_RUN, - - NEW_SESSION_FOLDER, - - ADDRESS_RANGES_MODE, - - PROGRAM_CORRELATOR_FACTORY_LIST, - - PROGRAM_CORRELATOR_OPTIONS_LIST, HIGHEST_PRECONDITION_STATUS, - - SHOW_ADDRESS_SET_PANELS, - - EXCLUDE_ACCEPTED_MATCHES, - - SOURCE_SELECTION, DESTINATION_SELECTION, - - SOURCE_ADDRESS_SET_CHOICE, DESTINATION_ADDRESS_SET_CHOICE; -} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddRemoveAddressRangeDialog.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddRemoveAddressRangeDialog.java similarity index 90% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddRemoveAddressRangeDialog.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddRemoveAddressRangeDialog.java index 850b07c517..96973bb128 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddRemoveAddressRangeDialog.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddRemoveAddressRangeDialog.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.feature.vt.gui.wizard; +package ghidra.feature.vt.gui.wizard.add; import java.awt.Dimension; +import java.util.Objects; +import java.util.function.Consumer; import javax.swing.*; @@ -23,7 +25,7 @@ import docking.DialogComponentProvider; import docking.widgets.label.GDLabel; import ghidra.app.util.AddressInput; import ghidra.app.util.HelpTopics; -import ghidra.program.model.address.Address; +import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; import ghidra.util.layout.PairLayout; @@ -31,7 +33,7 @@ import ghidra.util.layout.PairLayout; public class AddRemoveAddressRangeDialog extends DialogComponentProvider { private Program program; - private AddressRangeListener listener; + private Consumer addressRangeConsumer; private JPanel addressRangePanel; private JLabel minLabel; @@ -40,10 +42,10 @@ public class AddRemoveAddressRangeDialog extends DialogComponentProvider { private AddressInput maxAddressField; protected AddRemoveAddressRangeDialog(String type, String programIndicator, Program program, - AddressRangeListener listener) { + Consumer addressRangeConsumer) { super(programIndicator + " Address Range", true, true, true, false); this.program = program; - this.listener = listener; + this.addressRangeConsumer = Objects.requireNonNull(addressRangeConsumer); setHelpLocation(new HelpLocation(HelpTopics.LABEL, "AddEditDialog")); addWorkPanel(createAddressRangePanel()); @@ -99,9 +101,7 @@ public class AddRemoveAddressRangeDialog extends DialogComponentProvider { @Override protected void okCallback() { if (isValidRange()) { - if (listener != null) { - listener.processAddressRange(getMinAddress(), getMaxAddress()); - } + addressRangeConsumer.accept(new AddressRangeImpl(getMinAddress(), getMaxAddress())); close(); } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddToSessionData.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddToSessionData.java new file mode 100644 index 0000000000..90938de6a8 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddToSessionData.java @@ -0,0 +1,177 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import java.util.*; + +import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; +import ghidra.feature.vt.api.main.VTSession; +import ghidra.feature.vt.api.util.VTOptions; +import ghidra.framework.model.DomainFile; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.listing.Program; + +/** + * Wizard data used by the {@link VTAddToSessionWizardModel} and its steps for the "add to version + * tracking session" wizard. + */ +public class AddToSessionData { + public static enum AddressSetChoice { + ENTIRE_PROGRAM, SELECTION, MANUALLY_DEFINED + } + + private Program sourceProgram; + private Program destinationProgram; + private VTSession session; + private AddressSetView customSourceAddressSet; + private AddressSetView customDestinationAddressSet; + private AddressSetView sourceSelection; + private AddressSetView destinationSelection; + private List correlators; + private Map optionsMap = new HashMap<>(); + private boolean shouldExcludeAcceptedMatches; + private boolean shouldLimitAddressSets; + private AddressSetChoice sourceAddressSetChoice = AddressSetChoice.ENTIRE_PROGRAM; + private AddressSetChoice destinationAddressSetChoice = AddressSetChoice.ENTIRE_PROGRAM; + + public void setSourceProgram(Program sourceProgram) { + this.sourceProgram = sourceProgram; + } + + public Program getSourceProgram() { + return sourceProgram; + } + + public void setDestinationProgram(Program destinationProgram) { + this.destinationProgram = destinationProgram; + } + + public Program getDestinationProgram() { + return destinationProgram; + } + + public void setSession(VTSession session) { + this.session = session; + } + + public VTSession getSession() { + return session; + } + + public DomainFile getSourceFile() { + return sourceProgram.getDomainFile(); + } + + public DomainFile getDestinationFile() { + return destinationProgram.getDomainFile(); + } + + public AddressSetChoice getSourceAddressSetChoice() { + return sourceAddressSetChoice; + } + + public AddressSetChoice getDestinationAddressSetChoice() { + return destinationAddressSetChoice; + } + + public void setSourceAddressSetChoice(AddressSetChoice choice) { + this.sourceAddressSetChoice = choice; + } + + public void setDestinationAddressSetChoice(AddressSetChoice choice) { + this.destinationAddressSetChoice = choice; + } + + public void setSourceSelection(AddressSetView addressSet) { + this.sourceSelection = addressSet; + // if a source selection is set, the limit option defaults to true; + if (addressSet != null && !addressSet.isEmpty()) { + shouldLimitAddressSets = true; + sourceAddressSetChoice = AddressSetChoice.SELECTION; + } + } + + public void setDestinationSelection(AddressSetView addresseSet) { + this.destinationSelection = addresseSet; + // if a destination selection is set, the limit option defaults to true; + if (addresseSet != null && !addresseSet.isEmpty()) { + shouldLimitAddressSets = true; + destinationAddressSetChoice = AddressSetChoice.SELECTION; + } + } + + public AddressSetView getCustomSourceAddressSet() { + return customSourceAddressSet; + } + + public void setCustomSourceAddressSet(AddressSetView addressSet) { + customSourceAddressSet = addressSet; + } + + public void setCustomDestinationAddressSet(AddressSetView addressSet) { + customDestinationAddressSet = addressSet; + } + + public AddressSetView getCustomDestinationAddressSet() { + return customDestinationAddressSet; + } + + public AddressSetView getSourceSelection() { + return sourceSelection; + } + + public AddressSetView getDestinationSelection() { + return destinationSelection; + } + + public List getCorrelators() { + return correlators; + } + + public void setCorrelators(List correlators) { + if (!correlators.equals(this.correlators)) { + this.correlators = correlators; + // whenever the set of correlators changes, reset the options to force options panel + // to show + optionsMap = null; + } + } + + public void setOptions(Map optionsMap) { + this.optionsMap = optionsMap; + } + + public Map getOptions() { + return optionsMap; + } + + public boolean shouldExcludeAcceptedMatches() { + return shouldExcludeAcceptedMatches; + } + + public void setShouldExcludeAcceptedMatches(boolean b) { + shouldExcludeAcceptedMatches = b; + } + + public boolean shouldLimitAddressSets() { + return shouldLimitAddressSets; + } + + public void setShouldLimitAddressSets(boolean b) { + shouldLimitAddressSets = b; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddToSessionTask.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddToSessionTask.java similarity index 64% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddToSessionTask.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddToSessionTask.java index b32adf5aba..aa72881c8d 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/AddToSessionTask.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddToSessionTask.java @@ -4,25 +4,23 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.feature.vt.gui.wizard; +package ghidra.feature.vt.gui.wizard.add; import java.util.*; -import docking.wizard.WizardState; import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.util.VTMatchUtil; import ghidra.feature.vt.api.util.VTOptions; import ghidra.feature.vt.gui.plugin.VTController; -import ghidra.feature.vt.gui.wizard.ChooseAddressSetEditorPanel.AddressSetChoice; import ghidra.framework.data.DomainObjectAdapterDB; import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSetView; @@ -31,16 +29,15 @@ import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.Task; import ghidra.util.task.TaskMonitor; -import util.CollectionUtils; public class AddToSessionTask extends Task { - private final WizardState state; + private final AddToSessionData data; private final VTController controller; - public AddToSessionTask(VTController controller, WizardState state) { + public AddToSessionTask(VTController controller, AddToSessionData data) { super("Merge Version Tracking Session", true, true, true); this.controller = controller; - this.state = state; + this.data = data; } @Override @@ -51,49 +48,10 @@ public class AddToSessionTask extends Task { Program sourceProgram = session.getSourceProgram(); Program destinationProgram = session.getDestinationProgram(); - Boolean value = (Boolean) state.get(VTWizardStateKey.EXCLUDE_ACCEPTED_MATCHES); - boolean excludeAcceptedMatches = (value == null) ? false : value.booleanValue(); + boolean excludeAcceptedMatches = data.shouldExcludeAcceptedMatches(); - AddressSetChoice sourceAddressSetChoice = - (AddressSetChoice) state.get(VTWizardStateKey.SOURCE_ADDRESS_SET_CHOICE); - AddressSetChoice destinationAddressSetChoice = - (AddressSetChoice) state.get(VTWizardStateKey.DESTINATION_ADDRESS_SET_CHOICE); - if (sourceAddressSetChoice == null) { - sourceAddressSetChoice = AddressSetChoice.ENTIRE_PROGRAM; - } - if (destinationAddressSetChoice == null) { - destinationAddressSetChoice = AddressSetChoice.ENTIRE_PROGRAM; - } - - AddressSetView sourceAddressSet; - switch (sourceAddressSetChoice) { - case SELECTION: - sourceAddressSet = (AddressSetView) state.get(VTWizardStateKey.SOURCE_SELECTION); - break; - case MANUALLY_DEFINED: - sourceAddressSet = - (AddressSetView) state.get(VTWizardStateKey.SOURCE_ADDRESS_SET_VIEW); - break; - case ENTIRE_PROGRAM: - default: - sourceAddressSet = sourceProgram.getMemory(); - break; - } - AddressSetView destinationAddressSet; - switch (destinationAddressSetChoice) { - case SELECTION: - destinationAddressSet = - (AddressSetView) state.get(VTWizardStateKey.DESTINATION_SELECTION); - break; - case MANUALLY_DEFINED: - destinationAddressSet = - (AddressSetView) state.get(VTWizardStateKey.DESTINATION_ADDRESS_SET_VIEW); - break; - case ENTIRE_PROGRAM: - default: - destinationAddressSet = destinationProgram.getMemory(); - break; - } + AddressSetView sourceAddressSet = getSourceAddressSet(); + AddressSetView destinationAddressSet = getDestinationAddressSet(); if (excludeAcceptedMatches) { sourceAddressSet = excludeAcceptedMatches(sourceAddressSet, true); @@ -104,14 +62,14 @@ public class AddToSessionTask extends Task { boolean completedSucessfully = false; try { session.setEventsEnabled(false); // prevent table updates while busy - List correlatorFactories = getCorrelators(state); - List correlatorOptions = getCorrelatorOptions(state); + List correlatorFactories = data.getCorrelators(); + Map optionsMap = data.getOptions(); List noMatchList = new ArrayList<>(); - for (int i = 0; i < correlatorFactories.size(); i++) { - VTProgramCorrelatorFactory factory = correlatorFactories.get(i); + for (VTProgramCorrelatorFactory factory : correlatorFactories) { + VTOptions options = optionsMap.get(factory); VTProgramCorrelator correlator = factory.createCorrelator(sourceProgram, sourceAddressSet, destinationProgram, - destinationAddressSet, correlatorOptions.get(i)); + destinationAddressSet, options); VTMatchSet resultSet = correlator.correlate(session, monitor); if (resultSet.getMatchCount() == 0) { @@ -150,6 +108,36 @@ public class AddToSessionTask extends Task { } } + private AddressSetView getSourceAddressSet() { + if (!data.shouldLimitAddressSets()) { + return data.getSourceProgram().getMemory(); + } + switch (data.getSourceAddressSetChoice()) { + case MANUALLY_DEFINED: + return data.getCustomSourceAddressSet(); + case SELECTION: + return data.getSourceSelection(); + case ENTIRE_PROGRAM: + default: + return data.getSourceProgram().getMemory(); + } + } + + private AddressSetView getDestinationAddressSet() { + if (!data.shouldLimitAddressSets()) { + return data.getDestinationProgram().getMemory(); + } + switch (data.getDestinationAddressSetChoice()) { + case MANUALLY_DEFINED: + return data.getCustomDestinationAddressSet(); + case SELECTION: + return data.getDestinationSelection(); + case ENTIRE_PROGRAM: + default: + return data.getDestinationProgram().getMemory(); + } + } + private void endTransaction(VTSession session, int transactionID, boolean completedSucessfully) { if (transactionID == -1) { @@ -165,21 +153,8 @@ public class AddToSessionTask extends Task { return -1; } - private List getCorrelatorOptions(WizardState stateKey) { - return CollectionUtils.asList( - (List) stateKey.get(VTWizardStateKey.PROGRAM_CORRELATOR_OPTIONS_LIST), - VTOptions.class); - } - - private List getCorrelators( - WizardState stateKey) { - return CollectionUtils.asList( - (List) stateKey.get(VTWizardStateKey.PROGRAM_CORRELATOR_FACTORY_LIST), - VTProgramCorrelatorFactory.class); - } - private AddressSet excludeAcceptedMatches(AddressSetView addrSetView, boolean source) { - VTSession session = (VTSession) state.get(VTWizardStateKey.EXISTING_SESSION); + VTSession session = data.getSession(); AddressSet addrSet = new AddressSet(addrSetView); if (session != null) { List matchSets = session.getMatchSets(); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddressSetOptionsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddressSetOptionsPanel.java new file mode 100644 index 0000000000..a7743ac335 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddressSetOptionsPanel.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import static ghidra.feature.vt.api.main.VTProgramCorrelatorAddressRestrictionPreference.*; + +import java.util.List; + +import javax.swing.*; + +import docking.widgets.checkbox.GCheckBox; +import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; +import ghidra.util.HTMLUtilities; +import ghidra.util.layout.VerticalLayout; + +/** + * Panel for choosing the address set to used for performing new correlations when adding to + * a version tracking session. Used by the {@link AddressSetOptionsStep}. + */ +public class AddressSetOptionsPanel extends JPanel { + + private JCheckBox excludeCheckbox; + private JCheckBox limitAddressSetsCheckbox; + + public AddressSetOptionsPanel() { // + setBorder(BorderFactory.createEmptyBorder(40, 40, 0, 0)); + + excludeCheckbox = new GCheckBox("Exclude accepted matches", false); + String excludeAcceptedTooltip = "This option will cause the correlator algorithm " + + "to not consider any functions or data that have already been " + + "accepted. Using this option can greatly speed up the processing time " + + "of the correlator algorithm; however, this options should only be " + + "used when you trust that your accepted matches are correct."; + excludeCheckbox.setToolTipText(HTMLUtilities.toWrappedHTML(excludeAcceptedTooltip)); + + limitAddressSetsCheckbox = new GCheckBox("Limit source and destination address sets"); + String manuallyLimitTooltip = "Selecting this checkbox will trigger additional wizard " + + " panels allowing you to customize the address sets used " + + " by the selected algorithm. When not selected, the entire address space is used."; + + limitAddressSetsCheckbox.setToolTipText( + HTMLUtilities.toWrappedHTML(manuallyLimitTooltip)); + + add(excludeCheckbox); + add(limitAddressSetsCheckbox); + setLayout(new VerticalLayout(20)); + } + + public void initialize(AddToSessionData data) { + + excludeCheckbox.setSelected(data.shouldExcludeAcceptedMatches()); + limitAddressSetsCheckbox.setSelected(data.shouldLimitAddressSets()); + + if (allowRestrictions(data.getCorrelators())) { + excludeCheckbox.setEnabled(true); + } + else { + excludeCheckbox.setSelected(false); + excludeCheckbox.setEnabled(false); + } + } + + private boolean allowRestrictions(List list) { + for (VTProgramCorrelatorFactory factory : list) { + if (factory.getAddressRestrictionPreference() != RESTRICTION_NOT_ALLOWED) { + return true; + } + } + return false; + } + + void updateChoices(AddToSessionData data) { + data.setShouldExcludeAcceptedMatches(excludeCheckbox.isSelected()); + data.setShouldLimitAddressSets(limitAddressSetsCheckbox.isSelected()); + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddressSetOptionsStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddressSetOptionsStep.java new file mode 100644 index 0000000000..6df2375c86 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/AddressSetOptionsStep.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.util.HelpLocation; + +/** + * Wizard step for configuring address sets when adding correlations to an existing version + * tracking session. + */ +public class AddressSetOptionsStep extends WizardStep { + private AddressSetOptionsPanel panel; + + protected AddressSetOptionsStep(WizardModel model) { + super(model, "Address Set Options", + new HelpLocation("VersionTrackingPlugin", "Address_Set_Panel")); + panel = new AddressSetOptionsPanel(); + } + + @Override + public void initialize(AddToSessionData data) { + panel.initialize(data); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public void populateData(AddToSessionData data) { + panel.updateChoices(data); + } + + @Override + public boolean canFinish(AddToSessionData data) { + return true; + } + + @Override + public boolean apply(AddToSessionData data) { + return true; + } + + @Override + public JComponent getComponent() { + return panel; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/ChooseAddressSetEditorPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/ChooseAddressSetEditorPanel.java similarity index 92% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/ChooseAddressSetEditorPanel.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/ChooseAddressSetEditorPanel.java index 7123591adf..7a34b59d77 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/ChooseAddressSetEditorPanel.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/ChooseAddressSetEditorPanel.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.feature.vt.gui.wizard; +package ghidra.feature.vt.gui.wizard.add; import java.awt.BorderLayout; import java.awt.Component; @@ -28,18 +28,18 @@ import docking.widgets.button.GRadioButton; import docking.widgets.label.GLabel; import docking.widgets.list.GList; import generic.theme.GIcon; +import ghidra.feature.vt.gui.wizard.add.AddToSessionData.AddressSetChoice; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.util.layout.MiddleLayout; import ghidra.util.layout.VerticalLayout; +/** + * Panel for manually adjusting address sets. + */ public class ChooseAddressSetEditorPanel extends JPanel { - public static enum AddressSetChoice { - ENTIRE_PROGRAM, SELECTION, MANUALLY_DEFINED - } - private static Icon ADD_ICON = new GIcon("icon.version.tracking.add"); private static Icon SUBTRACT_ICON = new GIcon("icon.version.tracking.subtract"); @@ -210,18 +210,14 @@ public class ChooseAddressSetEditorPanel extends JPanel { } protected void showAddRangeDialog() { - AddressRangeListener addListener = - (minAddress, maxAddress) -> addRange(minAddress, maxAddress); AddRemoveAddressRangeDialog addRangeDialog = - new AddRemoveAddressRangeDialog("Add", name, program, addListener); + new AddRemoveAddressRangeDialog("Add", name, program, r -> addRange(r)); tool.showDialog(addRangeDialog, this.getRootPane()); } protected void showSubtractRangeDialog() { - AddressRangeListener subtractListener = - (minAddress, maxAddress) -> subtractRange(minAddress, maxAddress); AddRemoveAddressRangeDialog removeRangeDialog = - new AddRemoveAddressRangeDialog("Remove", name, program, subtractListener); + new AddRemoveAddressRangeDialog("Remove", name, program, r -> subtractRange(r)); tool.showDialog(removeRangeDialog, this.getRootPane()); } @@ -246,14 +242,14 @@ public class ChooseAddressSetEditorPanel extends JPanel { notifyListeners(); } - private synchronized void addRange(Address minAddress, Address maxAddress) { - myCurrentAddressSet.addRange(minAddress, maxAddress); + private synchronized void addRange(AddressRange range) { + myCurrentAddressSet.add(range); listModel.setData(myCurrentAddressSet.toList()); notifyListeners(); } - private synchronized void subtractRange(Address minAddress, Address maxAddress) { - myCurrentAddressSet.deleteRange(minAddress, maxAddress); + private synchronized void subtractRange(AddressRange range) { + myCurrentAddressSet.delete(range); listModel.setData(myCurrentAddressSet.toList()); notifyListeners(); } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/CorrelatorPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/CorrelatorChooserPanel.java similarity index 62% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/CorrelatorPanel.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/CorrelatorChooserPanel.java index 4bb774ddcd..41ef131d7d 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/CorrelatorPanel.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/CorrelatorChooserPanel.java @@ -4,19 +4,20 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.feature.vt.gui.wizard; +package ghidra.feature.vt.gui.wizard.add; import java.awt.*; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.swing.*; @@ -25,21 +26,26 @@ import javax.swing.table.TableColumnModel; import docking.widgets.table.GTableCellRenderer; import docking.widgets.table.GTableCellRenderingData; -import docking.wizard.*; import ghidra.feature.vt.api.impl.VTProgramCorrelatorInfo; import ghidra.feature.vt.api.main.*; -import ghidra.util.HelpLocation; import ghidra.util.table.GhidraTable; +import utility.function.Callback; -public class CorrelatorPanel extends AbstractMageJPanel implements Scrollable { +/** + * Panel for displaying and choosing from a list of version tracking correlators. Used by the + * {@link CorrelatorChooserStep} of the "add to version tracking session" wizard. + */ +public class CorrelatorChooserPanel extends JPanel { private VTProgramTableCorrelatorModel model; private GhidraTable table; + private Callback statusChangedCallback; - CorrelatorPanel(VTSession session) { + CorrelatorChooserPanel(VTSession session, Callback statusChangedCallback) { setLayout(new BorderLayout()); table = createBasicTable(getPreviouslyRunAlgorithms(session)); add(new JScrollPane(table), BorderLayout.CENTER); + this.statusChangedCallback = statusChangedCallback; } private GhidraTable createBasicTable(Set previouslyRunCorrelators) { @@ -56,16 +62,16 @@ public class CorrelatorPanel extends AbstractMageJPanel implem return table; } - @Override - protected void notifyListenersOfValidityChanged() { - super.notifyListenersOfValidityChanged(); - } - @Override public Dimension getPreferredSize() { return new Dimension(1000, 400); } + void notifyStatusChanged() { + statusChangedCallback.call(); + + } + private void setColumnSizes() { TableColumnModel columnModel = table.getColumnModel(); int width = 0; @@ -102,87 +108,8 @@ public class CorrelatorPanel extends AbstractMageJPanel implem return set; } - @Override - public void dispose() { - // nothing to do - } - - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation("VersionTrackingPlugin", "Correlator_Panel"); - } - - @Override - public void enterPanel(WizardState state) { - // nothing to do - } - - static WizardPanelDisplayability correlatorDisplayability(WizardState state) { - return WizardPanelDisplayability.MUST_BE_DISPLAYED; - } - - @Override - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState( - WizardState state) { - return correlatorDisplayability(state); - } - - @Override - public void leavePanel(WizardState state) { - updateStateObjectWithPanelInfo(state); - } - - @Override - public void updateStateObjectWithPanelInfo(WizardState state) { - java.util.List selectedFactories = model.getSelectedFactories(); - if (!selectedFactories.isEmpty()) { - state.put(VTWizardStateKey.PROGRAM_CORRELATOR_FACTORY_LIST, selectedFactories); - } - } - - @Override - public String getTitle() { - return "Select Correlation Algorithm(s)"; - } - - @Override - public void initialize() { - // nothing to do - } - - @Override - public boolean isValidInformation() { - return !model.getSelectedFactories().isEmpty(); - } - - @Override - public void addDependencies(WizardState state) { - // no dependencies - } - - @Override - public Dimension getPreferredScrollableViewportSize() { - return null; - } - - @Override - public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - return 25; - } - - @Override - public boolean getScrollableTracksViewportHeight() { - return true; - } - - @Override - public boolean getScrollableTracksViewportWidth() { - return true; - } - - @Override - public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - return 10; + public List getSelectedCorrelators() { + return model.getSelectedFactories(); } //================================================================================================== @@ -209,4 +136,5 @@ public class CorrelatorPanel extends AbstractMageJPanel implem return renderer; } } + } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/CorrelatorChooserStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/CorrelatorChooserStep.java new file mode 100644 index 0000000000..7617a92588 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/CorrelatorChooserStep.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import java.util.List; + +import javax.swing.JComponent; + +import docking.wizard.WizardStep; +import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; +import ghidra.feature.vt.api.main.VTSession; +import ghidra.util.HelpLocation; + +/** + * Wizard step for choosing which correlators to run when adding to an existing version + * tracking session. + */ +public class CorrelatorChooserStep extends WizardStep { + private CorrelatorChooserPanel panel; + + public CorrelatorChooserStep(VTAddToSessionWizardModel model, VTSession session) { + super(model, "Select Correlation Algorithm(s)", + new HelpLocation("VersionTrackingPlugin", "Correlator_Panel")); + + panel = new CorrelatorChooserPanel(session, this::notifyStatusChanged); + } + + @Override + public void initialize(AddToSessionData data) { + // nothing to do + } + + @Override + public boolean isValid() { + List correlators = panel.getSelectedCorrelators(); + if (!correlators.isEmpty()) { + return true; + } + return false; + } + + @Override + public void populateData(AddToSessionData data) { + data.setCorrelators(panel.getSelectedCorrelators()); + } + + @Override + public boolean apply(AddToSessionData data) { + return true; + } + + @Override + public JComponent getComponent() { + return panel; + } + + @Override + public boolean canFinish(AddToSessionData data) { + return data.getCorrelators() != null; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/LimitAddressSetsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/LimitAddressSetsPanel.java new file mode 100644 index 0000000000..df24ba3631 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/LimitAddressSetsPanel.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import java.awt.GridLayout; + +import javax.swing.JPanel; + +import ghidra.feature.vt.gui.wizard.add.AddToSessionData.AddressSetChoice; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.listing.Program; + +/** + * Panel for adjusting address sets for both the source and destination programs at the same time. + * Used by the {@link LimitAddressSetsStep} of the "add to version tracking session wizard. + */ +public class LimitAddressSetsPanel extends JPanel { + + private ChooseAddressSetEditorPanel sourcePanel; + private ChooseAddressSetEditorPanel destinationPanel; + private PluginTool tool; + + public LimitAddressSetsPanel(PluginTool tool) { + + this.tool = tool; + setLayout(new GridLayout()); + } + + public void initialize(AddToSessionData data) { + removeAll(); + + sourcePanel = buildSourcePanel(data); + destinationPanel = buildDestinationPanel(data); + + add(sourcePanel); + add(destinationPanel); + } + + private ChooseAddressSetEditorPanel buildSourcePanel(AddToSessionData data) { + Program program = data.getSourceProgram(); + AddressSetView selection = data.getSourceSelection(); + AddressSetView set = data.getCustomSourceAddressSet(); + AddressSetChoice choice = data.getSourceAddressSetChoice(); + return new ChooseAddressSetEditorPanel(tool, "Source", program, selection, set, choice); + } + + private ChooseAddressSetEditorPanel buildDestinationPanel(AddToSessionData data) { + Program program = data.getDestinationProgram(); + AddressSetView selection = data.getDestinationSelection(); + AddressSetView set = data.getCustomDestinationAddressSet(); + AddressSetChoice choice = data.getDestinationAddressSetChoice(); + return new ChooseAddressSetEditorPanel(tool, "Destination", program, selection, set, + choice); + } + + public void apply(AddToSessionData data) { + AddressSetChoice sourceChoice = sourcePanel.getAddressSetChoice(); + AddressSetChoice destinationChoice = destinationPanel.getAddressSetChoice(); + data.setSourceAddressSetChoice(sourceChoice); + data.setDestinationAddressSetChoice(destinationChoice); + data.setCustomSourceAddressSet(null); + data.setCustomDestinationAddressSet(null); + if (sourceChoice == AddressSetChoice.MANUALLY_DEFINED) { + data.setCustomSourceAddressSet(sourcePanel.getAddressSetView()); + } + if (destinationChoice == AddressSetChoice.MANUALLY_DEFINED) { + data.setCustomDestinationAddressSet(destinationPanel.getAddressSetView()); + } + + } +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/LimitAddressSetsStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/LimitAddressSetsStep.java new file mode 100644 index 0000000000..8063fa6da1 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/LimitAddressSetsStep.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import javax.swing.JComponent; + +import docking.wizard.WizardStep; +import ghidra.framework.plugintool.PluginTool; +import ghidra.util.HelpLocation; + +/** + * Wizard step for choosing the addresses to apply for adding correlation runs to an existing + * version tracking session. + */ +public class LimitAddressSetsStep extends WizardStep { + private LimitAddressSetsPanel panel; + + public LimitAddressSetsStep(VTAddToSessionWizardModel model, PluginTool tool) { + super(model, "Select Address Range(s)", + new HelpLocation("VersionTrackingPlugin", "Select_Address_Ranges_Panel")); + + panel = new LimitAddressSetsPanel(tool); + } + + @Override + public void initialize(AddToSessionData data) { + panel.initialize(data); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean apply(AddToSessionData data) { + return true; + } + + @Override + public void populateData(AddToSessionData data) { + panel.apply(data); + } + + @Override + public boolean isApplicable(AddToSessionData data) { + return data.shouldLimitAddressSets(); + } + + @Override + public boolean canFinish(AddToSessionData data) { + return true; + } + + @Override + public JComponent getComponent() { + return panel; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/OptionsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/OptionsPanel.java new file mode 100644 index 0000000000..658816962a --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/OptionsPanel.java @@ -0,0 +1,141 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.util.*; + +import javax.swing.*; + +import docking.options.editor.OptionsEditorPanel; +import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; +import ghidra.feature.vt.api.util.VTOptions; +import ghidra.framework.options.EditorStateFactory; +import ghidra.util.layout.VerticalLayout; +import utility.function.Callback; + +/** + * Panel for displaying version tracking correlator options for selected correlators. Used by + * the {@link OptionsStep} of the "add to version tracking session" wizard to configure the + * selected correlators from a previous step. + */ +public class OptionsPanel extends JPanel { + + private static final Dimension DEFAULT_PREFERRED_SIZE = new Dimension(650, 350); + + private List optionsEditorPanelList = new ArrayList<>(); + private Callback statusChangedCallback; + private Map optionsMap = new HashMap<>(); + + private JPanel stagingPanel; + + OptionsPanel(Callback statusChangedCallback) { + super(new BorderLayout()); + this.statusChangedCallback = statusChangedCallback; + + stagingPanel = new JPanel(new BorderLayout()); + stagingPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + JScrollPane scrollPane = new JScrollPane(stagingPanel); + scrollPane.getVerticalScrollBar().setUnitIncrement(5); + add(scrollPane); + } + + @Override + public Dimension getPreferredSize() { + Dimension preferredSize = super.getPreferredSize(); + if (preferredSize.width < DEFAULT_PREFERRED_SIZE.width) { + return DEFAULT_PREFERRED_SIZE; + } + return preferredSize; + } + + public boolean isApplicable(List correlators) { + updateOptionsMap(correlators); + return !optionsMap.isEmpty(); + } + + private void updateOptionsMap(List correlators) { + optionsMap.keySet().retainAll(correlators); + for (VTProgramCorrelatorFactory correlator : correlators) { + if (!optionsMap.containsKey(correlator)) { + VTOptions defaultOptions = correlator.createDefaultOptions(); + if (defaultOptions != null) { + optionsMap.put(correlator, defaultOptions); + } + } + } + } + + void initialize(List correlators) { + updateOptionsMap(correlators); + JPanel panel = new JPanel(new VerticalLayout(30)); + optionsEditorPanelList.clear(); + for (VTProgramCorrelatorFactory correlator : correlators) { + OptionsEditorPanel optionsPanel = buildOptionsPanel(correlator); + if (optionsPanel != null) { + optionsEditorPanelList.add(optionsPanel); + optionsPanel.setOptionsPropertyChangeListener(e -> statusChangedCallback.call()); + panel.add(optionsPanel); + } + } + + stagingPanel.removeAll(); + stagingPanel.add(panel, BorderLayout.CENTER); + stagingPanel.revalidate(); + } + + private OptionsEditorPanel buildOptionsPanel(VTProgramCorrelatorFactory factory) { + VTOptions options = optionsMap.get(factory); + if (options == null) { + return null; + } + + String title = factory.getName() + " Options"; + + EditorStateFactory editorStateFactory = new EditorStateFactory(); + List optionNames = options.getLeafOptionNames(); + if (optionNames.isEmpty()) { + return null; + } + Collections.sort(optionNames); + return new OptionsEditorPanel(title, options, optionNames, editorStateFactory); + } + + public boolean hasValidOptions() { + applyOptions(); + for (VTOptions options : optionsMap.values()) { + if (options != null) { + if (!options.validate()) { + return false; + } + } + + } + return true; + } + + private void applyOptions() { + for (OptionsEditorPanel panel : optionsEditorPanelList) { + panel.apply(); + } + } + + Map getOptionsMap() { + return optionsMap; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/OptionsStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/OptionsStep.java new file mode 100644 index 0000000000..8cbd1fef55 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/OptionsStep.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.util.HelpLocation; + +/** + * Wizard step for configuring options for the selected correlators when adding to an existing + * version tracking session. + */ +public class OptionsStep extends WizardStep { + private OptionsPanel panel; + + protected OptionsStep(WizardModel model) { + super(model, "Correlator Options", + new HelpLocation("VersionTrackingPlugin", "Options_Panel")); + + panel = new OptionsPanel(this::notifyStatusChanged); + } + + @Override + public void initialize(AddToSessionData data) { + panel.initialize(data.getCorrelators()); + // set the options here so that we know this step was visited + data.setOptions(panel.getOptionsMap()); + } + + @Override + public boolean isApplicable(AddToSessionData data) { + return panel.isApplicable(data.getCorrelators()); + } + + @Override + public boolean isValid() { + return panel.hasValidOptions(); + } + + @Override + public boolean canFinish(AddToSessionData data) { + return true; + } + + @Override + public void populateData(AddToSessionData data) { + data.setOptions(panel.getOptionsMap()); + } + + @Override + public boolean apply(AddToSessionData data) { + return true; + } + + @Override + public JComponent getComponent() { + return panel; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/SummaryStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/SummaryStep.java new file mode 100644 index 0000000000..9d32b0cc45 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/SummaryStep.java @@ -0,0 +1,163 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import java.util.List; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.feature.vt.api.main.VTProgramCorrelatorFactory; +import ghidra.feature.vt.gui.wizard.add.AddToSessionData.AddressSetChoice; +import ghidra.feature.vt.gui.wizard.session.SummaryPanel; +import ghidra.framework.model.DomainFile; +import ghidra.util.HTMLUtilities; +import ghidra.util.HelpLocation; + +/** + * Wizard step in the "add to tracking session" wizard for summarizing the information that + * will be used to add correlations to a version tracking session. + */ +public class SummaryStep extends WizardStep { + private SummaryPanel summaryPanel; + + protected SummaryStep(WizardModel model) { + super(model, "Summary", + new HelpLocation("VersionTrackingPlugin", "New_Session_Summary_Panel")); + + summaryPanel = new SummaryPanel(); + } + + @Override + public void initialize(AddToSessionData data) { + StringBuilder label = new StringBuilder(); + StringBuilder summary = new StringBuilder(); + + label.append(""); + summary.append(""); + + // session mode + + label.append("Operation:"); + summary.append("Add to Version Tracking Session"); + label.append("
"); + summary.append("
"); + + String sessionName = data.getSession().getName(); + + label.append("Session Name:"); + summary.append(sessionName); + label.append("
"); + summary.append("
"); + + String sourceProgramName = null; + String destinationProgramName = null; + + DomainFile sourceProgram = data.getSourceFile(); + sourceProgramName = sourceProgram.getName(); + + DomainFile destinationProgram = data.getDestinationFile(); + destinationProgramName = destinationProgram.getName(); + + // source program + + label.append("Source Program:"); + summary.append(HTMLUtilities.escapeHTML(sourceProgramName)); + label.append("
"); + summary.append("
"); + + // destination program + + label.append("Destination Program:"); + summary.append(HTMLUtilities.escapeHTML(destinationProgramName)); + label.append("
"); + summary.append("
"); + + String correlatorLabel = ""; + String correlatorName = null; + + List correlators = data.getCorrelators(); + for (VTProgramCorrelatorFactory correlatorFactory : correlators) { + correlatorName = correlatorFactory.getName(); + + label.append(correlatorLabel + "Program Correlator:"); + summary.append(correlatorName == null ? "(null)" : correlatorName); + label.append("
"); + summary.append("
"); + } + boolean excludeAcceptedMatches = data.shouldExcludeAcceptedMatches(); + label.append("Exclude Accepted Matches:"); + summary.append(excludeAcceptedMatches ? "Yes" : "No"); + label.append("
"); + summary.append("
"); + + AddressSetChoice sourceAddressSetChoice = data.getSourceAddressSetChoice(); + AddressSetChoice destinationAddressSetChoice = data.getDestinationAddressSetChoice(); + String sourceAddressesInfo = + (sourceAddressSetChoice == AddressSetChoice.MANUALLY_DEFINED) ? "Manually Defined" + : ((sourceAddressSetChoice == AddressSetChoice.SELECTION)) + ? "Source Tool Selection" + : "Entire Source Program"; + String destinationAddressesInfo = + (destinationAddressSetChoice == AddressSetChoice.MANUALLY_DEFINED) + ? "Manually Defined" + : ((destinationAddressSetChoice == AddressSetChoice.SELECTION)) + ? "Destination Tool Selection" + : "Entire Destination Program"; + + label.append("Source Address Set:"); + summary.append(sourceAddressesInfo); + label.append("
"); + summary.append("
"); + + label.append("Destination Address Set:"); + summary.append(destinationAddressesInfo); + label.append("
"); + summary.append("
"); + + label.append(""); + summary.append(""); + + summaryPanel.initialize(label.toString(), summary.toString()); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean canFinish(AddToSessionData data) { + return true; + } + + @Override + public boolean apply(AddToSessionData data) { + return true; + } + + @Override + public void populateData(AddToSessionData data) { + // nothing to do + } + + @Override + public JComponent getComponent() { + return summaryPanel; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/VTAddToSessionWizardModel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/VTAddToSessionWizardModel.java new file mode 100644 index 0000000000..7ba7e08544 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/VTAddToSessionWizardModel.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.add; + +import java.util.List; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.feature.vt.api.main.VTSession; +import ghidra.feature.vt.gui.plugin.VTController; +import ghidra.program.model.listing.Program; +import ghidra.util.task.Task; +import ghidra.util.task.TaskLauncher; + +/** + * Wizard model for adding correlation runs to an existing version tracking session. + */ +public class VTAddToSessionWizardModel extends WizardModel { + + private final VTController controller; + + public VTAddToSessionWizardModel(VTController controller) { + super("Add to Version Tracking Session", new AddToSessionData()); + this.controller = controller; + Program sourceProgram = controller.getSourceProgram(); + Program destinationProgram = controller.getDestinationProgram(); + VTSession session = controller.getSession(); + + data.setSourceProgram(sourceProgram); + data.setDestinationProgram(destinationProgram); + data.setSession(session); + data.setSourceSelection(controller.getSelectionInSourceTool()); + data.setDestinationSelection(controller.getSelectionInDestinationTool()); + + } + + @Override + protected void addWizardSteps(List> list) { + list.add(new CorrelatorChooserStep(this, controller.getSession())); + list.add(new OptionsStep(this)); + list.add(new AddressSetOptionsStep(this)); + list.add(new LimitAddressSetsStep(this, controller.getTool())); + list.add(new SummaryStep(this)); + + } + + @Override + protected boolean doFinish() { + Task task = new AddToSessionTask(controller, data); + new TaskLauncher(task, wizardDialog.getComponent()); + return true; + } +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTProgramTableCorrelatorModel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/VTProgramTableCorrelatorModel.java similarity index 91% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTProgramTableCorrelatorModel.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/VTProgramTableCorrelatorModel.java index a964855841..a8f79fda4f 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTProgramTableCorrelatorModel.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/add/VTProgramTableCorrelatorModel.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.feature.vt.gui.wizard; +package ghidra.feature.vt.gui.wizard.add; import java.util.*; @@ -26,6 +26,9 @@ import ghidra.feature.vt.api.util.VTAbstractProgramCorrelatorFactory; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.exception.AssertException; +/** + * Table model for showing version tracking correlators. + */ public class VTProgramTableCorrelatorModel extends AbstractGTableModel { static final String NAME_COLUMN_NAME = "Name"; @@ -47,9 +50,9 @@ public class VTProgramTableCorrelatorModel extends AbstractGTableModel previouslyRunCorrelators; private Set selectedFactories = new HashSet<>(); - private CorrelatorPanel panel; + private CorrelatorChooserPanel panel; - public VTProgramTableCorrelatorModel(CorrelatorPanel panel, + public VTProgramTableCorrelatorModel(CorrelatorChooserPanel panel, Set previouslyRunCorrelators) { this.panel = panel; this.previouslyRunCorrelators = previouslyRunCorrelators; @@ -57,7 +60,9 @@ public class VTProgramTableCorrelatorModel extends AbstractGTableModel getSelectedFactories() { - return new ArrayList<>(selectedFactories); + List factories = new ArrayList<>(selectedFactories); + factories.sort(comparator); + return factories; } private static List generateList() { @@ -136,7 +141,7 @@ public class VTProgramTableCorrelatorModel extends AbstractGTableModel state; + private final NewSessionData data; private final VTController controller; - public CreateNewSessionTask(VTController controller, WizardState state) { + public CreateNewSessionTask(VTController controller, NewSessionData data) { super("Create New Version Tracking Session", true, true, true); this.controller = controller; - this.state = state; + this.data = data; } @Override @@ -43,16 +42,16 @@ public class CreateNewSessionTask extends Task { VTSessionDB session = null; String name = null; try { - Program sourceProgram = (Program) state.get(VTWizardStateKey.SOURCE_PROGRAM); - Program destinationProgram = (Program) state.get(VTWizardStateKey.DESTINATION_PROGRAM); + Program sourceProgram = data.getSourceProgram(); + Program destinationProgram = data.getDestinationProgram(); session = new VTSessionDB("New Session", sourceProgram, destinationProgram, this); sourceProgram.release(controller.getTool()); destinationProgram.release(controller.getTool()); - name = (String) state.get(VTWizardStateKey.SESSION_NAME); - DomainFolder folder = (DomainFolder) state.get(VTWizardStateKey.NEW_SESSION_FOLDER); + name = data.getSessionName(); + DomainFolder folder = data.getSessionFolder(); try { folder.createFile(name, session, monitor); } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/NewSessionData.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/NewSessionData.java new file mode 100644 index 0000000000..b6182b17f7 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/NewSessionData.java @@ -0,0 +1,112 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainFolder; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.listing.Program; + +/** + * Wizard data used by the {@link VTNewSessionWizardModel} and its steps for the "create new version + * tracking session" wizard. + */ +public class NewSessionData { + private Program sourceProgram; + private Program destinationProgram; + private DomainFile sourceFile; + private DomainFile destinationFile; + private DomainFolder sessionFolder; + private String sessionName; + private boolean preconditionChecksCompleted; + + public DomainFile getSourceFile() { + return sourceFile; + } + + public DomainFile getDestinationFile() { + return destinationFile; + } + + public Program getSourceProgram() { + return sourceProgram; + } + + public Program getDestinationProgram() { + return destinationProgram; + } + + public String getSessionName() { + return sessionName; + } + + public DomainFolder getSessionFolder() { + return sessionFolder; + } + + public void setSourceFile(DomainFile file, PluginTool tool) { + this.sourceFile = file; + if (isProgramInvalidForFile(file, sourceProgram)) { + sourceProgram.release(tool); + sourceProgram = null; + } + } + + private boolean isProgramInvalidForFile(DomainFile file, Program program) { + + if (program == null) { + return false; + } + if (program.getDomainFile().equals(file)) { + return false; + } + return true; + } + + public void setDestinationFile(DomainFile file, PluginTool tool) { + this.destinationFile = file; + if (isProgramInvalidForFile(file, destinationProgram)) { + sourceProgram.release(tool); + sourceProgram = null; + } + } + + public void setSourceProgram(Program program) { + this.sourceProgram = program; + preconditionChecksCompleted = false; + } + + public void setDestinationProgram(Program program) { + this.destinationProgram = program; + preconditionChecksCompleted = false; + } + + public void setSessionName(String name) { + this.sessionName = name; + } + + public void setSessionFolder(DomainFolder folder) { + this.sessionFolder = folder; + } + + public boolean hasPerformedPreconditionChecks() { + return preconditionChecksCompleted; + } + + public void setPerformedPreconditionChecks(boolean b) { + preconditionChecksCompleted = b; + } +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/PreconditionsPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/PreconditionsPanel.java new file mode 100644 index 0000000000..23cfd8fb67 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/PreconditionsPanel.java @@ -0,0 +1,152 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.swing.*; + +import docking.widgets.conditiontestpanel.*; +import docking.wizard.WizardModel; +import ghidra.feature.vt.api.main.VTSession; +import ghidra.feature.vt.gui.validator.VTPreconditionValidator; +import ghidra.program.model.listing.Program; +import ghidra.util.Msg; +import ghidra.util.classfinder.ClassSearcher; +import utility.function.Callback; + +public class PreconditionsPanel extends JPanel { + private static final Dimension DEFAULT_SIZE = new Dimension(650, 480); + private ConditionTestPanel conditionsTestPanel; + private boolean testsDone = false; + private Callback statusChangedCallback; + + public PreconditionsPanel(WizardModel model, + Callback statusChangedCallback) { + this.statusChangedCallback = statusChangedCallback; + setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); + setLayout(new BorderLayout()); + + JPanel runButtonPanel = new JPanel(); + runButtonPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); + runButtonPanel.setLayout(new FlowLayout()); + JButton runTestsButton = new JButton("Run Precondition Checks"); + runTestsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + conditionsTestPanel.runTests(); + } + }); + runButtonPanel.add(runTestsButton); + + JButton skipTestsButton = new JButton("Skip"); + skipTestsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + conditionsTestPanel.skipTests(); + model.goNext(); + } + }); + runButtonPanel.add(skipTestsButton); + + add(runButtonPanel, BorderLayout.SOUTH); + } + + void dispose() { + if (conditionsTestPanel != null) { + conditionsTestPanel.cancel(); + } + } + + private ConditionTestPanel buildConditionPanel(Program source, Program destination) { + List list = getConditionTests(source, destination); + Collections.sort(list, (t1, t2) -> t1.getName().compareTo(t2.getName())); + ConditionTestPanel panel = new ConditionTestPanel(list); + panel.addListener(new ConditionTestListener() { + @Override + public void testsCompleted() { + testsDone(); + } + }); + return panel; + } + + private void testsDone() { + testsDone = true; + statusChangedCallback.call(); + if (hasAnyErrorStatus()) { + Msg.showError(getClass(), this, "Warning - Serious Precondition failures", + "The precondition checks discovered one or more serious problems. \n\n" + + "If you continue, your version tracking results may be invalid.\n" + + "You should review the errors, cancel this wizard, and correct the problems."); + } + } + + private List getConditionTests(Program sourceProgram, + Program destinationProgram) throws SecurityException { + List list = new ArrayList(); + + List> vtValidatorClasses = + ClassSearcher.getClasses(VTPreconditionValidator.class); + for (Class validatorClass : vtValidatorClasses) { + try { + Constructor ctor = + validatorClass.getConstructor(Program.class, Program.class, VTSession.class); + VTPreconditionValidator validator = + ctor.newInstance(sourceProgram, destinationProgram, null); + list.add(validator); + } + catch (Exception e) { + Msg.error(this, "error including VTPreconditionValidator " + validatorClass, e); + } + } + return list; + } + + private Boolean hasAnyErrorStatus() { + return conditionsTestPanel.getErrorCount() > 0; + } + + @Override + // Overridden to account for the fact that we don't know our preferred size until later in + // the wizard flow. At that point, the initial size of the wizard is already too small. + public Dimension getPreferredSize() { + Dimension superSize = super.getPreferredSize(); + if (superSize.width > DEFAULT_SIZE.width && superSize.height > DEFAULT_SIZE.height) { + return superSize; + } + return DEFAULT_SIZE; + } + + public void initializeTests(Program sourceProgram, Program destinationProgram) { + testsDone = false; + if (conditionsTestPanel != null) { + remove(conditionsTestPanel); + } + conditionsTestPanel = buildConditionPanel(sourceProgram, destinationProgram); + add(conditionsTestPanel, BorderLayout.CENTER); + } + + public boolean hasRunTests() { + return testsDone; + } +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/PreconditionsStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/PreconditionsStep.java new file mode 100644 index 0000000000..dae644530c --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/PreconditionsStep.java @@ -0,0 +1,82 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.program.model.listing.Program; +import ghidra.util.HelpLocation; + +/** + * Wizard step for running version tracking precondition tests when creating a new version tracking + * session. + */ +public class PreconditionsStep extends WizardStep { + private PreconditionsPanel preconditionsPanel; + + protected PreconditionsStep(WizardModel model) { + super(model, "Precondition Checklist", + new HelpLocation("VersionTrackingPlugin", "Preconditions_Panel")); + + preconditionsPanel = new PreconditionsPanel(model, this::notifyStatusChanged); + } + + @Override + public void initialize(NewSessionData data) { + if (!data.hasPerformedPreconditionChecks()) { + Program sourceProgram = data.getSourceProgram(); + Program destinationProgram = data.getDestinationProgram(); + preconditionsPanel.initializeTests(sourceProgram, destinationProgram); + } + } + + @Override + public boolean isValid() { + boolean hasRunTests = preconditionsPanel.hasRunTests(); + if (hasRunTests) { + return true; + } + return false; + } + + @Override + public void populateData(NewSessionData data) { + data.setPerformedPreconditionChecks(true); + } + + @Override + public boolean apply(NewSessionData data) { + return true; + } + + @Override + public boolean canFinish(NewSessionData data) { + return data.hasPerformedPreconditionChecks(); + } + + @Override + public JComponent getComponent() { + return preconditionsPanel; + } + + @Override + protected void dispose(NewSessionData data) { + preconditionsPanel.dispose(); + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SessionConfigurationPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SessionConfigurationPanel.java new file mode 100644 index 0000000000..a03f71b445 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SessionConfigurationPanel.java @@ -0,0 +1,348 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import static ghidra.framework.main.DataTreeDialogType.*; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.apache.commons.lang3.StringUtils; + +import docking.widgets.button.BrowseButton; +import docking.widgets.label.GDLabel; +import generic.theme.GIcon; +import generic.theme.GThemeDefaults.Ids.Fonts; +import generic.theme.Gui; +import ghidra.framework.main.DataTreeDialog; +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainFolder; +import ghidra.util.StringUtilities; +import utility.function.Callback; + +public class SessionConfigurationPanel extends JPanel { + // The maximum length to allow for each program's name portion of the session name. + // In the filesystem API, when saved, the session name is restricted to 60 characters. + // The default VTSession name combines the two program names so split the length between them, + // minus text we add below. + private static final int VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH = 28; + private static final int TEXT_FIELD_LENGTH = 40; + private static final Icon SWAP_ICON = new GIcon("icon.version.tracking.new.session.swap"); + private static final Icon INFO_ICON = new GIcon("icon.version.tracking.new.session.info"); + private JTextField sourceField; + private JTextField destinationField; + private JButton sourceBrowseButton; + private JButton destinationBrowseButton; + private JButton swapProgramsButton; + private JTextField sessionNameField; + private JTextField folderNameField; + + private DomainFile sourceFile; + private DomainFile destinationFile; + private DomainFolder sessionFolder; + private Callback statusChangedCallback; + + SessionConfigurationPanel(Callback statusChangedCallback) { + this.statusChangedCallback = statusChangedCallback; + setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); + + JLabel folderLabel = new GDLabel("Project folder "); + folderLabel.setHorizontalAlignment(SwingConstants.RIGHT); + folderLabel.setToolTipText("The folder to store the new Version Tracking Session"); + folderNameField = new JTextField(); + Gui.registerFont(folderNameField, Fonts.MONOSPACED); + folderNameField.setEditable(false); // force user to browse to choose + + JButton browseFolderButton = new BrowseButton(); + browseFolderButton.addActionListener(e -> browseDataTreeFolders()); + + JLabel newSessionLabel = new GDLabel("New Session Name: "); + newSessionLabel.setToolTipText("The name for the new Version Tracking Session"); + newSessionLabel.setHorizontalAlignment(SwingConstants.RIGHT); + + sessionNameField = new JTextField(TEXT_FIELD_LENGTH); + sessionNameField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + statusChangedCallback.call(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + statusChangedCallback.call(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + statusChangedCallback.call(); + } + }); + + JLabel sourceLabel = new GDLabel("Source Program: "); + sourceLabel.setIcon(INFO_ICON); + sourceLabel.setToolTipText("Analyzed program with markup to transfer"); + sourceLabel.setHorizontalAlignment(SwingConstants.RIGHT); + + JLabel destinationLabel = new GDLabel("Destination Program: "); + destinationLabel.setIcon(INFO_ICON); + destinationLabel.setToolTipText("New program that receives the transferred markup"); + destinationLabel.setHorizontalAlignment(SwingConstants.RIGHT); + + sourceField = new JTextField(TEXT_FIELD_LENGTH); + sourceField.setEditable(false); + + destinationField = new JTextField(TEXT_FIELD_LENGTH); + destinationField.setEditable(false); + + sourceBrowseButton = createSourceBrowseButton(); + destinationBrowseButton = createDestinationBrowseButton(); + + swapProgramsButton = new JButton(SWAP_ICON); + swapProgramsButton.setText("swap"); + swapProgramsButton.setName("SWAP_BUTTON"); + swapProgramsButton.addActionListener(arg0 -> swapPrograms()); + + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 0; + gbc.gridy = 0; + mainPanel.add(Box.createVerticalStrut(15), gbc); + + gbc.gridy++; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainPanel.add(folderLabel, gbc); + + gbc.gridx++; + mainPanel.add(folderNameField, gbc); + + gbc.gridx++; + mainPanel.add(Box.createHorizontalStrut(5), gbc); + + gbc.gridx++; + mainPanel.add(browseFolderButton, gbc); + + gbc.gridx = 0; + gbc.gridy++; + mainPanel.add(Box.createVerticalStrut(10), gbc); + + gbc.gridy++; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainPanel.add(newSessionLabel, gbc); + + gbc.gridx++; + mainPanel.add(sessionNameField, gbc); + + gbc.gridx = 0; + gbc.gridy++; + mainPanel.add(Box.createVerticalStrut(15), gbc); + + gbc.gridy++; + gbc.gridwidth = 4; + mainPanel.add(new JSeparator(), gbc); + + gbc.gridy++; + gbc.gridwidth = 1; + mainPanel.add(Box.createVerticalStrut(25), gbc); + + gbc.gridy++; + mainPanel.add(sourceLabel, gbc); + + gbc.gridx++; + mainPanel.add(sourceField, gbc); + + gbc.gridx += 2; + mainPanel.add(sourceBrowseButton, gbc); + + gbc.gridx = 0; + gbc.gridy++; + gbc.fill = GridBagConstraints.NONE; + gbc.gridwidth = 4; + mainPanel.add(swapProgramsButton, gbc); + + gbc.gridwidth = 1; + gbc.gridy++; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainPanel.add(destinationLabel, gbc); + + gbc.gridx++; + mainPanel.add(destinationField, gbc); + + gbc.gridx += 2; + mainPanel.add(destinationBrowseButton, gbc); + + gbc.gridx = 0; + gbc.gridy++; + mainPanel.add(Box.createVerticalStrut(25), gbc); + + gbc.gridy++; + gbc.gridwidth = 4; + mainPanel.add(new JSeparator(), gbc); + + gbc.gridy++; + gbc.gridwidth = 1; + mainPanel.add(Box.createVerticalStrut(60), gbc); + + setLayout(new BorderLayout()); + add(mainPanel, BorderLayout.NORTH); + + } + + public void setDestinationFile(DomainFile file) { + destinationFile = file; + if (destinationFile != null) { + destinationField.setText(destinationFile.getPathname()); + } + else { + destinationField.setText(""); + } + updateSessionNameIfBlank(); + } + + public void setSourceFile(DomainFile file) { + sourceFile = file; + if (sourceFile != null) { + sourceField.setText(sourceFile.getPathname()); + } + else { + sourceField.setText(""); + } + updateSessionNameIfBlank(); + } + + private JButton createSourceBrowseButton() { + JButton button = new BrowseButton(); + button.setName("SOURCE_BUTTON"); + button.addActionListener(e -> { + DomainFile programFile = VTWizardUtils.chooseDomainFile(SessionConfigurationPanel.this, + "a source program", VTWizardUtils.PROGRAM_FILTER, null); + if (programFile != null) { + setSourceFile(programFile); + statusChangedCallback.call(); + } + }); + return button; + } + + private JButton createDestinationBrowseButton() { + JButton button = new BrowseButton(); + button.setName("DESTINATION_BUTTON"); + button.addActionListener(e -> { + DomainFile programFile = VTWizardUtils.chooseDomainFile(SessionConfigurationPanel.this, + "a destination program", VTWizardUtils.PROGRAM_FILTER, null); + if (programFile != null) { + setDestinationFile(programFile); + statusChangedCallback.call(); + } + }); + return button; + } + + /** + * Presents the user with a tree of the existing project folders and allows + * them to pick one + */ + private void browseDataTreeFolders() { + final DataTreeDialog dataTreeDialog = + new DataTreeDialog(this, "Choose a project folder", CHOOSE_FOLDER); + + dataTreeDialog.addOkActionListener(e -> { + dataTreeDialog.close(); + sessionFolder = dataTreeDialog.getDomainFolder(); + folderNameField.setText(sessionFolder.toString()); + statusChangedCallback.call(); + }); + dataTreeDialog.showComponent(); + } + + private void swapPrograms() { + DomainFile newSourceFile = destinationFile; + DomainFile newDestionationFile = sourceFile; + setSourceFile(newSourceFile); + setDestinationFile(newDestionationFile); + statusChangedCallback.call(); + } + + public DomainFolder getSessionFolder() { + return sessionFolder; + } + + public String getSessionName() { + return sessionNameField.getText().trim(); + } + + public DomainFile getSourceFile() { + return sourceFile; + } + + public DomainFile getDestinationFile() { + return destinationFile; + } + + private void updateSessionNameIfBlank() { + if (!StringUtils.isBlank(sessionNameField.getText())) { + return; + } + if (sourceFile == null || destinationFile == null || sourceFile == destinationFile) { + return; + } + + String defaultSessionName = + createVTSessionName(sourceFile.getName(), destinationFile.getName()); + sessionNameField.setText(defaultSessionName); + } + + private String createVTSessionName(String sourceName, String destinationName) { + + // if together they are within the bounds just return session name with both full names + if (sourceName.length() + destinationName.length() <= 2 * + VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) { + return "VT_" + sourceName + "_" + destinationName; + } + + // give destination name all space not used by source name + if (sourceName.length() < VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) { + int leftover = VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH - sourceName.length(); + destinationName = StringUtilities.trimMiddle(destinationName, + VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover); + return "VT_" + sourceName + "_" + destinationName; + } + + // give source name all space not used by destination name + if (destinationName.length() < VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) { + int leftover = VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH - destinationName.length(); + sourceName = StringUtilities.trimMiddle(sourceName, + VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover); + return "VT_" + sourceName + "_" + destinationName; + } + + // if both too long, shorten both of them + sourceName = StringUtilities.trimMiddle(sourceName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH); + destinationName = + StringUtilities.trimMiddle(destinationName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH); + + return "VT_" + sourceName + "_" + destinationName; + } + + public void setSessionFolder(DomainFolder folder) { + sessionFolder = folder; + folderNameField.setText(sessionFolder.toString()); + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SessionConfigurationStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SessionConfigurationStep.java new file mode 100644 index 0000000000..6d6b9e62c1 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SessionConfigurationStep.java @@ -0,0 +1,205 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import javax.swing.JComponent; + +import org.apache.commons.lang3.StringUtils; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.app.util.task.OpenProgramRequest; +import ghidra.app.util.task.OpenProgramTask; +import ghidra.feature.vt.api.util.VTSessionFileUtil; +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainFolder; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.listing.Program; +import ghidra.util.HelpLocation; +import ghidra.util.InvalidNameException; +import ghidra.util.task.TaskLauncher; + +/** + * Wizard step in the new version tracking session wizard for choosing which programs to + * track and naming the session. + */ +public class SessionConfigurationStep extends WizardStep { + + private PluginTool tool; + private SessionConfigurationPanel sessionPanel; + + protected SessionConfigurationStep(WizardModel model, PluginTool tool) { + super(model, "New Version Tracking Session", + new HelpLocation("VersionTrackingPlugin", "New_Session_Panel")); + this.tool = tool; + sessionPanel = new SessionConfigurationPanel(this::notifyStatusChanged); + } + + @Override + public void initialize(NewSessionData data) { + sessionPanel.setSessionFolder(data.getSessionFolder()); + sessionPanel.setSourceFile(data.getSourceFile()); + sessionPanel.setDestinationFile(data.getDestinationFile()); + } + + @Override + public boolean isValid() { + setStatusMessage(""); + DomainFolder sessionFolder = sessionPanel.getSessionFolder(); + String sessionName = sessionPanel.getSessionName(); + DomainFile sourceFile = sessionPanel.getSourceFile(); + DomainFile destinationFile = sessionPanel.getDestinationFile(); + + if (!isValid(sessionFolder, sessionName, sourceFile, destinationFile)) { + return false; + } + + return true; + } + + @Override + public void populateData(NewSessionData data) { + data.setSessionName(sessionPanel.getSessionName()); + data.setSessionFolder(sessionPanel.getSessionFolder()); + data.setSourceFile(sessionPanel.getSourceFile(), tool); + data.setDestinationFile(sessionPanel.getDestinationFile(), tool); + } + + @Override + public boolean canFinish(NewSessionData data) { + return true; + } + + @Override + protected void dispose(NewSessionData data) { + releaseProgram(data.getSourceProgram()); + releaseProgram(data.getDestinationProgram()); + } + + private void releaseProgram(Program program) { + if (program != null) { + if (program.getConsumerList().contains(tool)) { + program.release(tool); + } + } + } + + private boolean isValid(DomainFolder sessionFolder, String sessionName, DomainFile sourceFile, + DomainFile destinationFile) { + if (sessionFolder == null) { + setStatusMessage("Choose a project folder to continue!"); + return false; + } + if (sourceFile == null) { + setStatusMessage("Please choose a source program."); + return false; + } + if (destinationFile == null) { + setStatusMessage("Please choose a destination program."); + return false; + } + if (sourceFile.equals(destinationFile)) { + setStatusMessage("Source and destination files must be different."); + return false; + } + if (StringUtils.isBlank(sessionName)) { + setStatusMessage("Please enter a name for this session"); + return false; + } + try { + tool.getProject().getProjectData().testValidName(sessionName, false); + } + catch (InvalidNameException e) { + setStatusMessage("'" + sessionName + "' contains invalid characters"); + return false; + } + + DomainFile file = sessionFolder.getFile(sessionName); + if (file != null) { + setStatusMessage( + "'" + file.getPathname() + "' is the name of an existing project file"); + return false; + } + return true; + } + + @Override + public boolean apply(NewSessionData data) { + + if (data.getSourceProgram() == null) { + Program program = openSourceProgram(data.getSourceFile()); + if (program == null) { + return false; + } + data.setSourceProgram(program); + } + + if (data.getDestinationProgram() == null) { + Program program = openDestinationProgram(data.getDestinationFile()); + if (program == null) { + return false; + } + data.setDestinationProgram(program); + } + return true; + } + + private Program openSourceProgram(DomainFile file) { + try { + VTSessionFileUtil.validateSourceProgramFile(file, false); + } + catch (Exception e) { + setStatusMessage(e.getMessage()); + return null; + } + + Program program = openProgram(file); + if (program == null) { + setStatusMessage("Open source program failed for " + file.getPathname()); + } + return program; + } + + private Program openDestinationProgram(DomainFile file) { + try { + VTSessionFileUtil.validateDestinationProgramFile(file, false, false); + } + catch (Exception e) { + setStatusMessage(e.getMessage()); + return null; + } + + Program program = openProgram(file); + if (program == null) { + setStatusMessage("Open destination program failed for " + file.getPathname()); + } + return program; + } + + private Program openProgram(DomainFile domainFile) { + + OpenProgramTask openProgramTask = new OpenProgramTask(domainFile, tool); + new TaskLauncher(openProgramTask, tool.getActiveWindow()); + OpenProgramRequest openProgramRequest = openProgramTask.getOpenProgram(); + return openProgramRequest != null ? openProgramRequest.getProgram() : null; + } + + @Override + public JComponent getComponent() { + return sessionPanel; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SummaryPanel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SummaryPanel.java new file mode 100644 index 0000000000..c82df9b970 --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SummaryPanel.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import java.awt.BorderLayout; + +import javax.swing.*; + +import docking.widgets.label.GDHtmlLabel; +import ghidra.util.layout.PairLayout; + +public class SummaryPanel extends JPanel { + private JLabel labelLabel; + private JLabel summaryLabel; + + public SummaryPanel() { + + labelLabel = new GDHtmlLabel(); + summaryLabel = new GDHtmlLabel(); + + JPanel mainPanel = new JPanel(new PairLayout(5, 10)); + mainPanel.add(labelLabel); + mainPanel.add(summaryLabel); + + setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); + setLayout(new BorderLayout()); + add(mainPanel, BorderLayout.CENTER); + } + + public void initialize(String labelText, String summaryText) { + labelLabel.setText(labelText); + summaryLabel.setText(summaryText); + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SummaryStep.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SummaryStep.java new file mode 100644 index 0000000000..e814ada64c --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/SummaryStep.java @@ -0,0 +1,117 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.framework.model.DomainFile; +import ghidra.util.HTMLUtilities; +import ghidra.util.HelpLocation; + +/** + * Wizard step in the new version tracking session wizard for summarizing the information that + * will be used to create a new session. + */ +public class SummaryStep extends WizardStep { + private SummaryPanel summaryPanel; + + protected SummaryStep(WizardModel model) { + super(model, "Summary", + new HelpLocation("VersionTrackingPlugin", "New_Session_Summary_Panel")); + + summaryPanel = new SummaryPanel(); + } + + @Override + public void initialize(NewSessionData data) { + StringBuilder label = new StringBuilder(); + StringBuilder summary = new StringBuilder(); + + label.append(""); + summary.append(""); + + // session mode + + label.append("Operation:"); + String opDescription = "New Version Tracking Session"; + summary.append(opDescription); + label.append("
"); + summary.append("
"); + + String sessionName = data.getSessionName(); + + label.append("Session Name:"); + summary.append(sessionName); + label.append("
"); + summary.append("
"); + + String sourceProgramName = null; + String destinationProgramName = null; + + DomainFile sourceProgram = data.getSourceFile(); + sourceProgramName = sourceProgram.getName(); + + DomainFile destinationProgram = data.getDestinationFile(); + destinationProgramName = destinationProgram.getName(); + + // source program + + label.append("Source Program:"); + summary.append(HTMLUtilities.escapeHTML(sourceProgramName)); + label.append("
"); + summary.append("
"); + + // destination program + + label.append("Destination Program:"); + summary.append(HTMLUtilities.escapeHTML(destinationProgramName)); + label.append("
"); + summary.append("
"); + + label.append(""); + summary.append(""); + + summaryPanel.initialize(label.toString(), summary.toString()); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean apply(NewSessionData data) { + return true; + } + + @Override + public JComponent getComponent() { + return summaryPanel; + } + + @Override + public boolean canFinish(NewSessionData data) { + return true; + } + + @Override + public void populateData(NewSessionData data) { + // this step is display only + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/VTNewSessionWizardModel.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/VTNewSessionWizardModel.java new file mode 100644 index 0000000000..8d914cc1ca --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/VTNewSessionWizardModel.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.feature.vt.gui.wizard.session; + +import java.util.List; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.feature.vt.gui.plugin.VTController; +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainFolder; +import ghidra.framework.plugintool.PluginTool; +import ghidra.util.task.Task; +import ghidra.util.task.TaskLauncher; + +/** + * Wizard model for creating a new version tracking session. + */ +public class VTNewSessionWizardModel extends WizardModel { + + private final VTController controller; + + public VTNewSessionWizardModel(VTController controller) { + this(controller, null, null); + } + + public VTNewSessionWizardModel(VTController controller, DomainFile sourceFile, + DomainFile destinationFile) { + super("New Version Tracking Session", new NewSessionData()); + this.controller = controller; + PluginTool tool = controller.getTool(); + data.setSourceFile(sourceFile, tool); + data.setDestinationFile(destinationFile, tool); + DomainFolder folder = tool.getProject().getProjectData().getRootFolder(); + data.setSessionFolder(folder); + } + + @Override + protected void addWizardSteps(List> list) { + list.add(new SessionConfigurationStep(this, controller.getTool())); + list.add(new PreconditionsStep(this)); + list.add(new SummaryStep(this)); + + } + + @Override + protected boolean doFinish() { + Task task = new CreateNewSessionTask(controller, data); + new TaskLauncher(task, wizardDialog.getComponent()); + return true; + } + +} diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTWizardUtils.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/VTWizardUtils.java similarity index 96% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTWizardUtils.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/VTWizardUtils.java index b87ddeefef..bd12ffbda2 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/VTWizardUtils.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/wizard/session/VTWizardUtils.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.feature.vt.gui.wizard; +package ghidra.feature.vt.gui.wizard.session; import static ghidra.framework.main.DataTreeDialogType.*; @@ -54,7 +54,7 @@ public class VTWizardUtils { return Program.class.isAssignableFrom(f.getDomainObjectClass()); }; - static DomainFile chooseDomainFile(Component parent, String domainIdentifier, + public static DomainFile chooseDomainFile(Component parent, String domainIdentifier, DomainFileFilter filter, DomainFile fileToSelect) { final DataTreeDialog dataTreeDialog = filter == null ? new DataTreeDialog(parent, "Choose " + domainIdentifier, OPEN) diff --git a/Ghidra/Features/VersionTracking/src/screen/java/help/screenshot/VersionTrackingPluginScreenShots.java b/Ghidra/Features/VersionTracking/src/screen/java/help/screenshot/VersionTrackingPluginScreenShots.java index af86e5ca10..c2181bf555 100644 --- a/Ghidra/Features/VersionTracking/src/screen/java/help/screenshot/VersionTrackingPluginScreenShots.java +++ b/Ghidra/Features/VersionTracking/src/screen/java/help/screenshot/VersionTrackingPluginScreenShots.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -51,7 +51,7 @@ import ghidra.feature.vt.gui.provider.onetomany.VTMatchDestinationTableProvider; import ghidra.feature.vt.gui.provider.onetomany.VTMatchSourceTableProvider; import ghidra.feature.vt.gui.task.*; import ghidra.feature.vt.gui.util.MatchInfo; -import ghidra.feature.vt.gui.wizard.*; +import ghidra.feature.vt.gui.wizard.add.*; import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel; import ghidra.framework.main.DataTreeDialog; import ghidra.framework.main.datatree.DataTree; @@ -228,11 +228,13 @@ public class VersionTrackingPluginScreenShots extends GhidraScreenShotGenerator selectMatch("Call_strncpy_s"); - dualListingIsVisible(getProvider(VTMarkupItemsTableProvider.class)); + VTMarkupItemsTableProvider provider = getProvider(VTMarkupItemsTableProvider.class); + tool.showComponentProvider(provider, true); + assertTrue(dualListingIsVisible(provider)); hideDualListing(VTMarkupItemsTableProvider.class); - dualListingIsVisible(getProvider(VTMarkupItemsTableProvider.class)); + assertFalse(dualListingIsVisible(provider)); setToolSize(1300, 750); @@ -441,6 +443,8 @@ public class VersionTrackingPluginScreenShots extends GhidraScreenShotGenerator assertNotNull(destinationTool); tool = sourceTool; + ComponentProvider provider = tool.getComponentProvider("Decompiler"); + tool.showComponentProvider(provider, BATCH_MODE); setToolSize(1200, 550); sourceTool.toFront(); captureToolWindow(1200, 550); @@ -785,7 +789,7 @@ public class VersionTrackingPluginScreenShots extends GhidraScreenShotGenerator assertNotNull(provider); ToggleDockingAction action = - (ToggleDockingAction) getLocalAction(provider, "Dual Listing Toggle Orientation"); + (ToggleDockingAction) getLocalAction(provider, "Listing View Toggle Orientation"); assertNotNull(action); setToggleActionSelected(action, new DefaultActionContext(), vertical); waitForSwing(); @@ -817,12 +821,10 @@ public class VersionTrackingPluginScreenShots extends GhidraScreenShotGenerator DialogComponentProvider dialog = getDialog(); LimitAddressSetsPanel panel = findComponent(dialog, LimitAddressSetsPanel.class); - AddressSetPanel destinationPanel = - (AddressSetPanel) getInstanceField("destinationPanel", panel); - ChooseAddressSetEditorPanel choosePanel = - findComponent(destinationPanel, ChooseAddressSetEditorPanel.class); + ChooseAddressSetEditorPanel destinationPanel = + (ChooseAddressSetEditorPanel) getInstanceField("destinationPanel", panel); JRadioButton myRangesButton = - (JRadioButton) getInstanceField("myRangesButton", choosePanel); + (JRadioButton) getInstanceField("myRangesButton", destinationPanel); pressButton(myRangesButton); } @@ -833,17 +835,18 @@ public class VersionTrackingPluginScreenShots extends GhidraScreenShotGenerator DialogComponentProvider dialog = getDialog(); AddressSetOptionsPanel panel = findComponent(dialog, AddressSetOptionsPanel.class); JCheckBox showAddressSetPanelsCheckbox = - (JCheckBox) getInstanceField("showAddressSetPanelsCheckbox", panel); + (JCheckBox) getInstanceField("limitAddressSetsCheckbox", panel); showAddressSetPanelsCheckbox.setSelected(true); } private void setupVTWizardOptionsPanel() throws Exception { setupVTWizardCorrelatorPanel(); DialogComponentProvider dialog = getDialog(); - CorrelatorPanel correlatorPanel = findComponent(dialog, CorrelatorPanel.class); + CorrelatorChooserPanel correlatorPanel = + findComponent(dialog, CorrelatorChooserPanel.class); VTProgramTableCorrelatorModel model = (VTProgramTableCorrelatorModel) getInstanceField("model", correlatorPanel); - model.setValueAt(true, 1, 0);// Set "Exact Function Bytes Match" to selected. + runSwing(() -> model.setValueAt(true, 6, 0));// Set "Exact Function Bytes Match" to selected model.fireTableDataChanged(); waitForSwing(); pressNextButtonWhenEnabled(); diff --git a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAddToSessionTest.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAddToSessionTest.java index 0f8cab350a..548632db6c 100644 --- a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAddToSessionTest.java +++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTAddToSessionTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,16 +24,17 @@ import javax.swing.table.TableModel; import org.junit.*; -import docking.wizard.WizardManager; -import docking.wizard.WizardPanel; +import docking.wizard.WizardDialog; +import docking.wizard.WizardStep; import generic.test.TestUtils; import ghidra.app.services.CodeViewerService; import ghidra.app.util.AddressInput; import ghidra.feature.vt.api.db.VTSessionDB; import ghidra.feature.vt.api.main.VTMatchSet; import ghidra.feature.vt.gui.plugin.*; -import ghidra.feature.vt.gui.wizard.*; -import ghidra.feature.vt.gui.wizard.ChooseAddressSetEditorPanel.AddressSetChoice; +import ghidra.feature.vt.gui.wizard.add.*; +import ghidra.feature.vt.gui.wizard.add.AddToSessionData.AddressSetChoice; +import ghidra.feature.vt.gui.wizard.session.SummaryPanel; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramDB; import ghidra.program.model.address.*; @@ -59,8 +60,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { private ProgramDB sourceProgram; private ProgramDB destinationProgram; private VTSessionDB session; - private VTAddToSessionWizardManager vtWizardManager; - private WizardManager wizardManager; + private VTAddToSessionWizardModel wizardModel; + private WizardDialog wizardDialog; private AddressSet sourceSelection; private AddressSet destinationSelection; @@ -125,11 +126,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { assertNotNull(controller.getSourceProgram()); assertNotNull(controller.getDestinationProgram()); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -179,11 +180,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { assertNotNull(controller.getSourceProgram()); assertNotNull(controller.getDestinationProgram()); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -239,11 +240,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { assertNotNull(controller.getSourceProgram()); assertNotNull(controller.getDestinationProgram()); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -299,11 +300,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { assertNotNull(controller.getSourceProgram()); assertNotNull(controller.getDestinationProgram()); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -374,11 +375,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { createSelectionInSourceAndDestinationTools(); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -436,11 +437,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { createSelectionInSourceAndDestinationTools(); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -498,11 +499,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { createSelectionInSourceAndDestinationTools(); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -573,11 +574,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { createSelectionInSourceAndDestinationTools(); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Exact Function Instructions Match", VTWizardPanelAction.NEXT); @@ -673,11 +674,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { assertNotNull(controller.getSourceProgram()); assertNotNull(controller.getDestinationProgram()); - createWizardManager(); + createWizardDialog(); - runSwingLater(() -> wizardManager.showWizard(controller.getParentComponent())); + runSwingLater(() -> wizardDialog.show(controller.getParentComponent())); - waitForDialogComponent(WizardManager.class); + waitForDialogComponent(WizardDialog.class); checkWizardButtonEnablement(false, false, false, true); chooseFromCorrelationPanel("Data Reference Match", VTWizardPanelAction.NEXT); @@ -730,10 +731,10 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { private void chooseFromCorrelationPanel(String correlatorName, VTWizardPanelAction wizardAction) { - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof CorrelatorPanel); - CorrelatorPanel correlatorPanel = (CorrelatorPanel) currentWizardPanel; + WizardStep currentStep = wizardDialog.getCurrentStep(); + assertNotNull(currentStep); + CorrelatorChooserPanel correlatorPanel = + (CorrelatorChooserPanel) currentStep.getComponent(); SystemUtilities.runSwingNow(() -> { GhidraTable table = (GhidraTable) TestUtils.getInstanceField("table", correlatorPanel); TableModel model = table.getModel(); @@ -750,59 +751,28 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { VTWizardPanelAction wizardAction) { // Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof OptionsPanel); - // Nothing else to check in this panel for now. - - // TODO Use an OptionsObject or something to pass in correlator options and set them in the panel. -// // get out the correlator options -// AddressCorrelatorManager correlator = controller.getCorrelator(); -// assertNotNull("The controller did not find any correlators", correlator); -// -// // set some options settings -// Options options = correlator.getOptions(LCSAddressCorrelator.class); -// String testDefaultValue = "Test Default Value"; -// String testOptionKey = "Test Option Name"; -// String value = options.getString(testOptionKey, testDefaultValue); -// assertEquals(value, testDefaultValue); -// -// String firstNewOptionValue = "New Option Value"; -// options.putString(testOptionKey, firstNewOptionValue); -// assertEquals(firstNewOptionValue, options.getString(testOptionKey, null)); -// correlator.setOptions(LCSAddressCorrelator.class, options); -// // save the options -// SaveState saveState = new SaveState(); -// controller.writeConfigState(saveState); -// -// // change the options -// String secondNewValue = "Second New Value"; -// options.putString(testOptionKey, secondNewValue); -// correlator.setOptions(LCSAddressCorrelator.class, options); -// -// // pull the values again and make sure they are still correct (that writing the config -// // state did not change the cached controller and options) -// correlator = controller.getCorrelator(); -// options = correlator.getOptions(LCSAddressCorrelator.class); -// assertEquals(secondNewValue, options.getString(testOptionKey, null)); - + WizardStep currentStep = wizardDialog.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof OptionsPanel); + OptionsPanel optionsPanel = (OptionsPanel) component; + assertNotNull(optionsPanel); SystemUtilities.runSwingNow(() -> invoke(wizardAction)); } private void checkAddressSetOptionsPanel(boolean excludeAccepted, boolean limitAddressSets) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof AddressSetOptionsPanel); - AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) currentWizardPanel; + WizardStep currentStep = wizardDialog.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof AddressSetOptionsPanel); + AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) component; JCheckBox excludeCheckbox = (JCheckBox) TestUtils.getInstanceField("excludeCheckbox", addressSetOptionsPanel); assertNotNull(excludeCheckbox); JCheckBox showAddressSetPanelsCheckbox = (JCheckBox) TestUtils - .getInstanceField("showAddressSetPanelsCheckbox", addressSetOptionsPanel); + .getInstanceField("limitAddressSetsCheckbox", addressSetOptionsPanel); assertNotNull(showAddressSetPanelsCheckbox); assertEquals("Exclude Accepted Matches checkbox", excludeAccepted, @@ -815,17 +785,17 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { VTWizardPanelAction wizardAction) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof AddressSetOptionsPanel); - AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) currentWizardPanel; + WizardStep currentStep = wizardDialog.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof AddressSetOptionsPanel); + AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) component; JCheckBox excludeCheckbox = (JCheckBox) TestUtils.getInstanceField("excludeCheckbox", addressSetOptionsPanel); assertNotNull(excludeCheckbox); JCheckBox showAddressSetPanelsCheckbox = (JCheckBox) TestUtils - .getInstanceField("showAddressSetPanelsCheckbox", addressSetOptionsPanel); + .getInstanceField("limitAddressSetsCheckbox", addressSetOptionsPanel); assertNotNull(showAddressSetPanelsCheckbox); if (excludeCheckbox.isSelected() != excludeAccepted) { @@ -843,17 +813,18 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { AddressSetChoice destinationChoice) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof LimitAddressSetsPanel); - LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) currentWizardPanel; + WizardStep currentStep = wizardDialog.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof LimitAddressSetsPanel); + LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) component; + ChooseAddressSetEditorPanel sourcePanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("sourcePanel", + limitAddressSetsPanel); + ChooseAddressSetEditorPanel destinationPanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("destinationPanel", + limitAddressSetsPanel); - AddressSetPanel sourcePanel = - (AddressSetPanel) TestUtils.getInstanceField("sourcePanel", limitAddressSetsPanel); assertNotNull(sourcePanel); - - AddressSetPanel destinationPanel = - (AddressSetPanel) TestUtils.getInstanceField("destinationPanel", limitAddressSetsPanel); assertNotNull(destinationPanel); changeAddressChoice(sourcePanel, sourceChoice); @@ -864,31 +835,25 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { AddressSetView desiredDestinationSet) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof LimitAddressSetsPanel); - LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) currentWizardPanel; + WizardStep currentStep = wizardDialog.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof LimitAddressSetsPanel); + LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) component; + + ChooseAddressSetEditorPanel sourcePanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("sourcePanel", + limitAddressSetsPanel); + ChooseAddressSetEditorPanel destinationPanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("destinationPanel", + limitAddressSetsPanel); - AddressSetPanel sourcePanel = - (AddressSetPanel) TestUtils.getInstanceField("sourcePanel", limitAddressSetsPanel); assertNotNull(sourcePanel); - - AddressSetPanel destinationPanel = - (AddressSetPanel) TestUtils.getInstanceField("destinationPanel", limitAddressSetsPanel); assertNotNull(destinationPanel); - ChooseAddressSetEditorPanel sourceSetPanel = - (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("panel", sourcePanel); - assertNotNull(sourceSetPanel); - - ChooseAddressSetEditorPanel destinationSetPanel = - (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("panel", destinationPanel); - assertNotNull(destinationSetPanel); - AddressSetView panelSourceSet = - (AddressSetView) TestUtils.invokeInstanceMethod("getAddressSetView", sourceSetPanel); + (AddressSetView) TestUtils.invokeInstanceMethod("getAddressSetView", sourcePanel); AddressSetView panelDestinationSet = (AddressSetView) TestUtils - .invokeInstanceMethod("getAddressSetView", destinationSetPanel); + .invokeInstanceMethod("getAddressSetView", destinationPanel); assertEquals("Source Address Set", desiredSourceSet, panelSourceSet); assertEquals("Destination Address Set", desiredDestinationSet, panelDestinationSet); } @@ -897,17 +862,19 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { AddressSetView desiredDestinationSet) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof LimitAddressSetsPanel); - LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) currentWizardPanel; + WizardStep currentStep = wizardDialog.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof LimitAddressSetsPanel); + LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) component; + + ChooseAddressSetEditorPanel sourcePanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("sourcePanel", + limitAddressSetsPanel); + ChooseAddressSetEditorPanel destinationPanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("destinationPanel", + limitAddressSetsPanel); - AddressSetPanel sourcePanel = - (AddressSetPanel) TestUtils.getInstanceField("sourcePanel", limitAddressSetsPanel); assertNotNull(sourcePanel); - - AddressSetPanel destinationPanel = - (AddressSetPanel) TestUtils.getInstanceField("destinationPanel", limitAddressSetsPanel); assertNotNull(destinationPanel); changeAddressSetViaListRemoveRange(true, sourcePanel, desiredSourceSet); @@ -918,37 +885,35 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { AddressSetChoice destinationChoice) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof LimitAddressSetsPanel); - LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) currentWizardPanel; + WizardStep currentStep = wizardDialog.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof LimitAddressSetsPanel); + LimitAddressSetsPanel limitAddressSetsPanel = (LimitAddressSetsPanel) component; - AddressSetPanel sourcePanel = - (AddressSetPanel) TestUtils.getInstanceField("sourcePanel", limitAddressSetsPanel); + ChooseAddressSetEditorPanel sourcePanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("sourcePanel", + limitAddressSetsPanel); assertNotNull(sourcePanel); - AddressSetPanel destinationPanel = - (AddressSetPanel) TestUtils.getInstanceField("destinationPanel", limitAddressSetsPanel); + ChooseAddressSetEditorPanel destinationPanel = + (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("destinationPanel", + limitAddressSetsPanel); assertNotNull(destinationPanel); checkAddressChoice(sourcePanel, sourceChoice); checkAddressChoice(destinationPanel, destinationChoice); } - private void checkAddressChoice(AddressSetPanel addressSetPanel, + private void checkAddressChoice(ChooseAddressSetEditorPanel panel, AddressSetChoice expectedChoice) { - ChooseAddressSetEditorPanel panel = - (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("panel", addressSetPanel); assertNotNull(panel); AddressSetChoice addressSetChoice = panel.getAddressSetChoice(); - assertEquals(addressSetPanel.getName() + " Panel address set choice", expectedChoice, + assertEquals(panel.getName() + " Panel address set choice", expectedChoice, addressSetChoice); } - private void changeAddressChoice(AddressSetPanel addressSetPanel, + private void changeAddressChoice(ChooseAddressSetEditorPanel panel, AddressSetChoice expectedChoice) { - ChooseAddressSetEditorPanel panel = - (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("panel", addressSetPanel); assertNotNull(panel); AddressSetChoice addressSetChoice = panel.getAddressSetChoice(); if (expectedChoice != addressSetChoice) { @@ -976,9 +941,7 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { } private void changeAddressSetViaListRemoveRange(boolean isSource, - AddressSetPanel addressSetPanel, AddressSetView desiredAddressSet) { - ChooseAddressSetEditorPanel panel = - (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("panel", addressSetPanel); + ChooseAddressSetEditorPanel panel, AddressSetView desiredAddressSet) { assertNotNull(panel); JButton addRangeButton = (JButton) TestUtils.getInstanceField("addRangeButton", panel); @@ -1006,9 +969,7 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { } private void changeAddressSetViaSubtractDialog(boolean isSource, - AddressSetPanel addressSetPanel, AddressSetView desiredAddressSet) { - ChooseAddressSetEditorPanel panel = - (ChooseAddressSetEditorPanel) TestUtils.getInstanceField("panel", addressSetPanel); + ChooseAddressSetEditorPanel panel, AddressSetView desiredAddressSet) { assertNotNull(panel); JButton addRangeButton = (JButton) TestUtils.getInstanceField("addRangeButton", panel); @@ -1128,11 +1089,11 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { VTWizardPanelAction wizardAction) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof SummaryPanel); - SummaryPanel summaryPanel = (SummaryPanel) currentWizardPanel; - + WizardStep currentStep = wizardDialog.getCurrentStep(); + assertNotNull(currentStep); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof SummaryPanel); + SummaryPanel summaryPanel = (SummaryPanel) component; JLabel labelLabel = (JLabel) TestUtils.getInstanceField("labelLabel", summaryPanel); assertNotNull(labelLabel); @@ -1180,7 +1141,7 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { private void checkWizardButtonEnablement(boolean backEnabled, boolean nextEnabled, boolean finishEnabled, boolean cancelEnabled) { - JComponent component = wizardManager.getComponent(); + JComponent component = wizardDialog.getComponent(); JButton backButton = findButtonByText(component, "<< Back"); JButton nextButton = findButtonByText(component, "Next >>"); JButton finishButton = findButtonByText(component, "Finish"); @@ -1198,16 +1159,16 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { public void invoke(VTWizardPanelAction wizardAction) { switch (wizardAction) { case BACK: - wizardManager.back(); + wizardModel.goBack(); break; case NEXT: - wizardManager.next(); + wizardModel.goNext(); break; case FINISH: - wizardManager.finish(); + wizardModel.finish(); break; case CANCEL: - wizardManager.close(); + wizardDialog.close(); break; } } @@ -1228,10 +1189,10 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest { return destinationProgram.getAddressFactory().getAddress(addressString); } - private void createWizardManager() { + private void createWizardDialog() { runSwing(() -> { - vtWizardManager = new VTAddToSessionWizardManager(controller); - wizardManager = new WizardManager("Version Tracking Wizard", true, vtWizardManager); + wizardModel = new VTAddToSessionWizardModel(controller); + wizardDialog = new WizardDialog(wizardModel); }); } } diff --git a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/gui/provider/AbstractVTCorrelatorTest.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/gui/provider/AbstractVTCorrelatorTest.java index a8e5015797..edf4997551 100644 --- a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/gui/provider/AbstractVTCorrelatorTest.java +++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/gui/provider/AbstractVTCorrelatorTest.java @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,8 +30,8 @@ import org.junit.After; import org.junit.Before; import docking.options.editor.OptionsEditorPanel; -import docking.wizard.WizardManager; -import docking.wizard.WizardPanel; +import docking.wizard.WizardDialog; +import docking.wizard.WizardStep; import generic.lsh.LSHMemoryModel; import generic.test.TestUtils; import ghidra.feature.vt.api.correlator.program.VTAbstractReferenceProgramCorrelatorFactory; @@ -42,7 +42,8 @@ import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.plugin.VTPlugin; import ghidra.feature.vt.gui.task.AcceptMatchTask; import ghidra.feature.vt.gui.task.ApplyMatchTask; -import ghidra.feature.vt.gui.wizard.*; +import ghidra.feature.vt.gui.wizard.add.*; +import ghidra.feature.vt.gui.wizard.session.SummaryPanel; import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; @@ -70,8 +71,8 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg protected Program destProg; protected VTController controller; protected VTPlugin plugin; - protected VTAddToSessionWizardManager vtWizardManager; - protected WizardManager wizardManager; + protected VTAddToSessionWizardModel wizardModel; + protected WizardDialog wizardDialog; public AbstractVTCorrelatorTest(String sourceProgLoc, String destProgLoc) { this.sourceProgLoc = sourceProgLoc; @@ -105,17 +106,17 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg private void setupWizardBeforeCorrelatorOptions(String correlatorName) { runSwing(() -> { - vtWizardManager = new VTAddToSessionWizardManager(controller); - wizardManager = new WizardManager("Version Tracking Wizard", true, vtWizardManager); - wizardManager.showWizard(controller.getParentComponent()); + wizardModel = new VTAddToSessionWizardModel(controller); + wizardDialog = new WizardDialog(wizardModel); + wizardDialog.show(controller.getParentComponent()); }, false); waitForSwing(); - waitForDialogComponent(WizardManager.class); - assertNotNull(wizardManager); + waitForDialogComponent(WizardDialog.class); + assertNotNull(wizardDialog); checkWizardButtonEnablement(false, false, false, true); - chooseFromCorrelationPanel(correlatorName, wizardManager::next); + chooseFromCorrelationPanel(correlatorName, wizardModel::goNext); checkWizardButtonEnablement(true, true, true, true); } @@ -123,7 +124,7 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg private void finishWizardAfterCorrelatorOptions(String correlatorName) { checkAddressSetOptionsPanel(false, false); checkWizardButtonEnablement(true, true, true, true); - changeAddressSetOptionsPanel(false, false, wizardManager::next); + changeAddressSetOptionsPanel(false, false, wizardModel::goNext); // Check the summary panel. checkWizardButtonEnablement(true, false, true, true); @@ -137,7 +138,7 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg String summaryString = "Add to Version Tracking Session
" + session.getName() + "
" + srcProg.getName() + "
" + destProg.getName() + "
" + correlatorName + "
" + "No
" + "Entire Source Program
" + "Entire Destination Program
" + ""; - checkSummaryPanel(labelString, summaryString, wizardManager::finish); + checkSummaryPanel(labelString, summaryString, wizardModel::finish); } public void runTestCorrelator(String correlatorName) { @@ -154,7 +155,7 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg long start = System.nanoTime(); setupWizardBeforeCorrelatorOptions(correlatorName); - useDefaultCorrelatorOptions(correlatorName, wizardManager::next); + useDefaultCorrelatorOptions(correlatorName, wizardModel::goNext); finishWizardAfterCorrelatorOptions(correlatorName); @@ -179,7 +180,7 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg setupWizardBeforeCorrelatorOptions(correlatorName); changeReferenceCorrelatorOptions(correlatorName, confidence, memoryModel, score, - refineResults, wizardManager::next); + refineResults, wizardModel::goNext); finishWizardAfterCorrelatorOptions(correlatorName); } @@ -385,7 +386,7 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg protected void checkWizardButtonEnablement(boolean backEnabled, boolean nextEnabled, boolean finishEnabled, boolean cancelEnabled) { - JComponent component = wizardManager.getComponent(); + JComponent component = wizardDialog.getComponent(); JButton backButton = findButtonByText(component, "<< Back"); JButton nextButton = findButtonByText(component, "Next >>"); JButton finishButton = findButtonByText(component, "Finish"); @@ -402,10 +403,10 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg protected void chooseFromCorrelationPanel(final String correlatorName, Runnable wizardAction) { - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof CorrelatorPanel); - CorrelatorPanel correlatorPanel = (CorrelatorPanel) currentWizardPanel; + WizardStep currentWizardStep = wizardModel.getCurrentStep(); + JComponent component = currentWizardStep.getComponent(); + assertTrue(component instanceof CorrelatorChooserPanel); + CorrelatorChooserPanel correlatorPanel = (CorrelatorChooserPanel) component; runSwing(() -> { GhidraTable table = (GhidraTable) TestUtils.getInstanceField("table", correlatorPanel); TableModel model = table.getModel(); @@ -422,9 +423,9 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg Runnable wizardAction) { // Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof OptionsPanel); + WizardStep currentWizardStep = wizardModel.getCurrentStep(); + JComponent component = currentWizardStep.getComponent(); + assertTrue(component instanceof ghidra.feature.vt.gui.wizard.add.OptionsPanel); runSwing(wizardAction); } @@ -496,10 +497,10 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg private OptionsEditorPanel getCorrelatorOptionsPanel(String correlatorName) { String desiredTitle = correlatorName + " Options"; - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof OptionsPanel); - OptionsPanel optionsPanel = (OptionsPanel) currentWizardPanel; + WizardStep currentStep = wizardModel.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof OptionsPanel); + OptionsPanel optionsPanel = (OptionsPanel) component; Object instanceField = TestUtils.getInstanceField("optionsEditorPanelList", optionsPanel); @SuppressWarnings("unchecked") List optionsEditorPanelList = (List) instanceField; @@ -523,17 +524,17 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg protected void checkAddressSetOptionsPanel(boolean excludeAccepted, boolean limitAddressSets) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof AddressSetOptionsPanel); - AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) currentWizardPanel; + WizardStep currentStep = wizardModel.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof AddressSetOptionsPanel); + AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) component; JCheckBox excludeCheckbox = (JCheckBox) TestUtils.getInstanceField("excludeCheckbox", addressSetOptionsPanel); assertNotNull(excludeCheckbox); JCheckBox showAddressSetPanelsCheckbox = (JCheckBox) TestUtils - .getInstanceField("showAddressSetPanelsCheckbox", addressSetOptionsPanel); + .getInstanceField("limitAddressSetsCheckbox", addressSetOptionsPanel); assertNotNull(showAddressSetPanelsCheckbox); assertEquals("Exclude Accepted Matches checkbox", excludeAccepted, @@ -546,17 +547,17 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg Runnable wizardAction) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof AddressSetOptionsPanel); - AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) currentWizardPanel; + WizardStep currentStep = wizardModel.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof AddressSetOptionsPanel); + AddressSetOptionsPanel addressSetOptionsPanel = (AddressSetOptionsPanel) component; JCheckBox excludeCheckbox = (JCheckBox) TestUtils.getInstanceField("excludeCheckbox", addressSetOptionsPanel); assertNotNull(excludeCheckbox); JCheckBox showAddressSetPanelsCheckbox = (JCheckBox) TestUtils - .getInstanceField("showAddressSetPanelsCheckbox", addressSetOptionsPanel); + .getInstanceField("limitAddressSetsCheckbox", addressSetOptionsPanel); assertNotNull(showAddressSetPanelsCheckbox); if (excludeCheckbox.isSelected() != excludeAccepted) { @@ -594,10 +595,10 @@ public abstract class AbstractVTCorrelatorTest extends AbstractGhidraHeadedInteg Runnable wizardAction) { // Address Set Options Panel - WizardPanel currentWizardPanel = wizardManager.getCurrentWizardPanel(); - assertNotNull(currentWizardPanel); - assertTrue(currentWizardPanel instanceof SummaryPanel); - SummaryPanel summaryPanel = (SummaryPanel) currentWizardPanel; + WizardStep currentStep = wizardModel.getCurrentStep(); + JComponent component = currentStep.getComponent(); + assertTrue(component instanceof SummaryPanel); + SummaryPanel summaryPanel = (SummaryPanel) component; JLabel labelLabel = (JLabel) TestUtils.getInstanceField("labelLabel", summaryPanel); assertNotNull(labelLabel); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractMageJPanel.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractMageJPanel.java deleted file mode 100644 index c1b995b94e..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractMageJPanel.java +++ /dev/null @@ -1,85 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import ghidra.util.HelpLocation; -import ghidra.util.datastruct.WeakDataStructureFactory; -import ghidra.util.datastruct.WeakSet; - -import java.awt.Component; -import java.awt.LayoutManager; - -import javax.swing.JPanel; - -public abstract class AbstractMageJPanel extends JPanel implements MagePanel { - protected WeakSet listeners = - WeakDataStructureFactory.createSingleThreadAccessWeakSet(); - - public AbstractMageJPanel() { - super(); - } - - public AbstractMageJPanel(LayoutManager layout) { - super(layout); - } - - @Override - public void addWizardPanelListener(WizardPanelListener l) { - listeners.add(l); - } - - @Override - public void removeWizardPanelListener(WizardPanelListener l) { - listeners.remove(l); - } - - /** - * Notification that something on the panel has changed. - */ - protected void notifyListenersOfValidityChanged() { - for (WizardPanelListener listener : listeners) { - listener.validityChanged(); - } - } - - /** - * Notification that a message should be displayed on the panel. - * - * @param msg - * message to display - */ - protected void notifyListenersOfStatusMessage(String msg) { - for (WizardPanelListener listener : listeners) { - listener.setStatusMessage(msg); - } - } - - @Override - public JPanel getPanel() { - return this; - } - - @Override - public Component getDefaultFocusComponent() { - return null; // no preferred focus component by default - } - - @Override - public HelpLocation getHelpLocation() { - return null; - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractMagePanelManager.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractMagePanelManager.java deleted file mode 100644 index 5d7dc9279d..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractMagePanelManager.java +++ /dev/null @@ -1,327 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import java.awt.*; -import java.util.*; -import java.util.List; - -import javax.swing.*; - -public abstract class AbstractMagePanelManager implements PanelManager { - - private List> panels; - private Stack panelPath; - private int currentIndex; - private WizardState state; - - protected AbstractMagePanelManager(WizardState initialState) { - panelPath = new Stack<>(); - this.state = initialState; - - } - - protected abstract Collection> createPanels(); - - protected WizardState getState() { - return state; - } - - private WizardManager wizardManager; - - @Override - public void cancel() { - // do nothing by default - } - - @Override - public final WizardManager getWizardManager() { - return wizardManager; - } - - @Override - public final void setWizardManager(WizardManager wm) { - this.wizardManager = wm; - } - - @Override - public Dimension getPanelSize() { - int minWidth = -1; - int minHeight = -1; - for (MagePanel panel : getPanels()) { - JComponent component = (JComponent) panel; - Dimension preferredSize = component.getPreferredSize(); - if (preferredSize.width > minWidth) { - minWidth = preferredSize.width; - } - if (preferredSize.height > minHeight) { - minHeight = preferredSize.height; - } - } - - // take into account the scrollbar size so we do not get horizontal scrollbars unnecessarily - Dimension dimension = new Dimension(minWidth, minHeight); - JScrollBar scrollBar = new JScrollBar(Adjustable.VERTICAL); - Dimension scrollBarSize = scrollBar.getMinimumSize(); - dimension.width = dimension.width + (scrollBarSize.width * 2); // add some fudge for borders - - return dimension; - } - - private String statusMessage; - - @Override - public final String getStatusMessage() { - String tmp = statusMessage; - statusMessage = null; - return tmp; - } - - protected final void setStatusMessage(String msg) { - this.statusMessage = msg; - wizardManager.setStatusMessage(msg); - } - - protected void initializeHook() { - // let subclasses add functionality to initialize without losing ours - } - - @Override - public final void initialize() { - for (MagePanel panel : getPanels()) { - panel.initialize(); - } - setCurrentIndex(-1); - panelPath.clear(); - statusMessage = null; - initializeHook(); - } - - protected final MagePanel getCurrentPanel() { - int index = getCurrentIndex(); - List> panelList = getPanels(); - if (index < 0 || index >= panelList.size()) { - return null; - } - return panelList.get(index); - } - - @Override - @SuppressWarnings("unchecked") - public final boolean hasNextPanel() { - MagePanel currentPanel = getCurrentPanel(); - WizardState pretendState = (WizardState) state.clone(); - if (currentPanel != null) { - currentPanel.updateStateObjectWithPanelInfo(pretendState); - } - MagePanel panel = null; - int index = getCurrentIndex() + 1; - while (index < getPanels().size()) { - panel = getPanels().get(index); - WizardPanelDisplayability displayability = - panel.getPanelDisplayabilityAndUpdateState(pretendState); - if (displayability == WizardPanelDisplayability.MUST_BE_DISPLAYED || - displayability == WizardPanelDisplayability.CAN_BE_DISPLAYED) { - return true; - } - ++index; - } - return false; - } - - @Override - public final boolean hasPreviousPanel() { - return panelPath.size() > 0; - } - - @Override - @SuppressWarnings("unchecked") - public final boolean canFinish() { - MagePanel currentPanel = getCurrentPanel(); - WizardState pretendState = (WizardState) state.clone(); - if (currentPanel != null) { - currentPanel.updateStateObjectWithPanelInfo(pretendState); - } - MagePanel panel = null; - int index = getCurrentIndex() + 1; - while (index < getPanels().size()) { - panel = getPanels().get(index); - WizardPanelDisplayability displayability = - panel.getPanelDisplayabilityAndUpdateState(pretendState); - if (displayability == WizardPanelDisplayability.MUST_BE_DISPLAYED) { - return false; - } - ++index; - } - return true; - } - - @Override - public final WizardPanel getNextPanel() throws IllegalPanelStateException { - Window window = getWindow(); - Cursor originalCursor = getCursor(window); - try { - setCursor(window, Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - return doGetNextPanel(); - } - finally { - setCursor(window, originalCursor); - } - } - - @Override - public WizardPanel getInitialPanel() throws IllegalPanelStateException { - Window window = getWindow(); - Cursor originalCursor = getCursor(window); - try { - setCursor(window, Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - panelPath.clear(); - MagePanel panel = panels.get(0); - panel.getPanelDisplayabilityAndUpdateState(state); - panel.enterPanel(state); - setCurrentIndex(0); - return panel; - } - finally { - setCursor(window, originalCursor); - } - } - - private WizardPanel doGetNextPanel() throws IllegalPanelStateException { - MagePanel currentPanel = getCurrentPanel(); - if (currentPanel != null) { - currentPanel.leavePanel(state); - panelPath.push(getCurrentIndex()); - } - MagePanel panel = null; - int index = getCurrentIndex() + 1; - while (index < getPanels().size()) { - panel = getPanels().get(index); - WizardPanelDisplayability displayability = - panel.getPanelDisplayabilityAndUpdateState(state); - if (displayability == WizardPanelDisplayability.MUST_BE_DISPLAYED || - displayability == WizardPanelDisplayability.CAN_BE_DISPLAYED) { - panel.enterPanel(state); - setCurrentIndex(index); - return panel; - } - ++index; - } - return null; - } - - private void setCursor(Window window, Cursor cursor) { - if (window == null) { - return; // shouldn't happen - } - - window.setCursor(cursor); - - if (window instanceof JWindow) { - JRootPane rootPane = ((JWindow) window).getRootPane(); - rootPane.paintImmediately(rootPane.getBounds()); - } - else if (window instanceof JDialog) { - JRootPane rootPane = ((JDialog) window).getRootPane(); - rootPane.paintImmediately(rootPane.getBounds()); - } - else if (window instanceof JFrame) { - JRootPane rootPane = ((JFrame) window).getRootPane(); - rootPane.paintImmediately(rootPane.getBounds()); - } - - } - - private Cursor getCursor(Window window) { - if (window == null) { - return null; - } - - return window.getCursor(); - } - - private Window getWindow() { - KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - return focusManager.getActiveWindow(); - } - - @Override - public final WizardPanel getPreviousPanel() throws IllegalPanelStateException { - Window window = getWindow(); - Cursor originalCursor = getCursor(window); - try { - setCursor(window, Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - return doGetPreviousPanel(); - } - finally { - setCursor(window, originalCursor); - } - } - - private WizardPanel doGetPreviousPanel() throws IllegalPanelStateException { - if (hasPreviousPanel()) { - MagePanel currentPanel = getCurrentPanel(); - if (currentPanel != null) { - currentPanel.leavePanel(state); - } - int previousIndex = panelPath.pop(); - MagePanel panel = getPanels().get(previousIndex); - panel.enterPanel(state); - setCurrentIndex(previousIndex); - return panel; - } - return null; - } - - protected abstract void doFinish() throws IllegalPanelStateException; - - @Override - public final void finish() throws IllegalPanelStateException { - getWizardManager().disableNavigation(); - MagePanel currentPanel = getCurrentPanel(); - if (currentPanel != null) { - currentPanel.leavePanel(state); - } - MagePanel panel = null; - int index = getCurrentIndex() + 1; - while (index < getPanels().size()) { - panel = getPanels().get(index); - panel.getPanelDisplayabilityAndUpdateState(state); - ++index; - } - doFinish(); - initialize(); // reset the panels - wizardManager.enableNavigation(); - } - - protected final List> getPanels() { - if (panels == null) { - panels = Collections.unmodifiableList(new ArrayList>(createPanels())); - for (MagePanel magePanel : this.panels) { - magePanel.addDependencies(this.state); - } - } - return panels; - } - - private int getCurrentIndex() { - return currentIndex; - } - - private void setCurrentIndex(int currentIndex) { - this.currentIndex = currentIndex; - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractWizardJPanel.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractWizardJPanel.java deleted file mode 100644 index 7e10501aa1..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/AbstractWizardJPanel.java +++ /dev/null @@ -1,119 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import ghidra.util.HelpLocation; - -import java.awt.Component; -import java.awt.LayoutManager; -import java.util.ArrayList; - -import javax.swing.JPanel; - -/** - * Base class that implements some methods of the WizardPanel, but not - * all. This class handles the notification of the listeners. - * - * - */ -public abstract class AbstractWizardJPanel extends JPanel implements WizardPanel { - - protected ArrayList listeners = new ArrayList(); - - /** - * Default constructor. - */ - public AbstractWizardJPanel() { - super(); - } - - /** - * @see javax.swing.JPanel#JPanel(boolean) - */ - public AbstractWizardJPanel(boolean isDoubleBuffered) { - super(isDoubleBuffered); - } - - /** - * @see javax.swing.JPanel#JPanel(LayoutManager) - */ - public AbstractWizardJPanel(LayoutManager layout) { - super(layout); - } - - /** - * @see javax.swing.JPanel#JPanel(LayoutManager, boolean) - */ - public AbstractWizardJPanel(LayoutManager layout, boolean isDoubleBuffered) { - super(layout, isDoubleBuffered); - } - - /** - * @see docking.wizard.WizardPanel#getPanel() - */ - public JPanel getPanel() { - return this; - } - - public Component getDefaultFocusComponent() { - return null; // no preferred focus component by default - } - - /** - * @see docking.wizard.WizardPanel#getHelpLocation() - */ - public HelpLocation getHelpLocation() { - return null; - } - - /** - * @see docking.wizard.WizardPanel#addWizardPanelListener(WizardPanelListener) - */ - public void addWizardPanelListener(WizardPanelListener l) { - if (!listeners.contains(l)) { - listeners.add(l); - } - } - - /** - * @see docking.wizard.WizardPanel#removeWizardPanelListener(WizardPanelListener) - */ - public void removeWizardPanelListener(WizardPanelListener l) { - listeners.remove(l); - } - - /** - * Notification that something on the panel has changed. - */ - public void notifyListenersOfValidityChanged() { - for (int i = 0; i < listeners.size() ; ++i) { - WizardPanelListener wpl = listeners.get(i); - wpl.validityChanged(); - } - } - - /** - * Notification that a message should be displayed on the panel. - * @param msg message to display - */ - public void notifyListenersOfStatusMessage(String msg) { - for (int i = 0; i < listeners.size() ; ++i) { - WizardPanelListener wpl = listeners.get(i); - wpl.setStatusMessage(msg); - } - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/IllegalPanelStateException.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/IllegalPanelStateException.java deleted file mode 100644 index e946ba5a89..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/IllegalPanelStateException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -/** - * IllegalPanelStateException allows unexpected IOExceptions and other errors - * to be thrown during Wizard panel transitions - */ -public class IllegalPanelStateException extends Exception { - - /** - * Constructor - * @param cause original cause of exception (required) - */ - public IllegalPanelStateException(Throwable cause) { - super(cause); - } - -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/MagePanel.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/MagePanel.java deleted file mode 100644 index cc60538ff0..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/MagePanel.java +++ /dev/null @@ -1,59 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -public interface MagePanel extends WizardPanel { - public void addDependencies(WizardState state); - - /** - * Enter and leave panel for pretend; take your state from the state object and then add - * whatever state you need into it to pretend finish the wizard (if possible). Return - * whether you must, can, or should not be displayed. - * @param state the state object - * @return displayability - */ - public WizardPanelDisplayability getPanelDisplayabilityAndUpdateState(WizardState state); - - /** - * Enter panel for real; take your state from the state object and then - * populate your external state accordingly. - * @param state the state object - * @throws IllegalPanelStateException indicates that something bad has happened and we should - * return to the very first panel - unless we are the first panel in which case we - * should abort the wizard. - */ - public void enterPanel(WizardState state) throws IllegalPanelStateException; - - /** - * Leave panel for real; inject your external state into the state object. - * @param state the state object - */ - public void leavePanel(WizardState state); - - /** - * Updates the state object, being passed as a parameter, with the current state information - * from this panel. Only state information that the panel is intended to set should be modified - * within the state object by this method. For example, a summary panel might display state - * information, but doesn't set it and therefore wouldn't change it in the state object. - * @param state the state object to update - */ - public void updateStateObjectWithPanelInfo(WizardState state); - - /** - * Called when the wizard is cancelled or otherwise finished being shown - */ - public void dispose(); -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/PanelManager.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/PanelManager.java deleted file mode 100644 index 0cba7303be..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/PanelManager.java +++ /dev/null @@ -1,106 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import java.awt.Dimension; - -/** - * Interface to define methods that control what panel is displayed in a - * wizard. - */ -public interface PanelManager { - /** - * Return true if the "finish" step can be completed. - * @return boolean true if ok to finish - */ - public boolean canFinish(); - - /** - * Return true if there is a "next" panel. - * @return boolean true means there is a next panel to display - */ - public boolean hasNextPanel(); - - /** - * Return true if there is a "previous" panel. - * @return boolean true means there is a previous panel to display - */ - public boolean hasPreviousPanel(); - - /** - * Get the next panel in the process. - * @return WizardPanel the next panel - * @throws IllegalPanelStateException if an IOException or other unexpected error occurs - */ - public WizardPanel getNextPanel() throws IllegalPanelStateException; - - /** - * Get the first panel in the process. - * @return WizardPanel the first panel - * @throws IllegalPanelStateException if an IOException or other unexpected error occurs - */ - public WizardPanel getInitialPanel() throws IllegalPanelStateException; - - /** - * Get the previous panel in the process. - * @return WizardPanel the previous panel - * @throws IllegalPanelStateException if an IOException or other unexpected error occurs - */ - public WizardPanel getPreviousPanel() throws IllegalPanelStateException; - - /** - * Get the status message for the current panel. - * @return String message to display; - * may be null if there is no message that should be displayed - */ - public String getStatusMessage(); - - /** - * Method called when the user wants to finish the process. - * @throws IllegalPanelStateException if an IOException or other unexpected error occurs - */ - public void finish() throws IllegalPanelStateException; - - /** - * Method called when the user wants to cancel the process. - */ - public void cancel(); - - /** - * Set up the panel process. This may also be called to clear the state of an existing panel, - * such as when the overall wizard is finished. - */ - public void initialize(); - - /** - * Get the size of the panels. - * @return Dimension size of the panel - */ - public Dimension getPanelSize(); - - /** - * Set the wizard manager for this panel manager. - * @param wm wizard manager that calls the methods on this panel - * manager - */ - public void setWizardManager(WizardManager wm); - - /** - * Get the wizard manager. - * @return WizardManager wizard manager for this panel manager - */ - public WizardManager getWizardManager(); -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardContext.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardContext.java deleted file mode 100644 index 13a9a12f35..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardContext.java +++ /dev/null @@ -1,29 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import java.util.EmptyStackException; - -public interface WizardContext { - public void checkpoint(); - - public void uncheckpoint() throws EmptyStackException; - - public int depth(); - - public WizardContext deepCopy(); -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardDialog.java new file mode 100644 index 0000000000..0bfa9e136f --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardDialog.java @@ -0,0 +1,286 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.wizard; + +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +import javax.swing.*; +import javax.swing.border.TitledBorder; + +import docking.DialogComponentProvider; +import docking.DockingWindowManager; +import docking.widgets.EmptyBorderButton; +import docking.widgets.label.GDLabel; +import generic.theme.GThemeDefaults.Colors.Messages; +import generic.theme.Gui; +import ghidra.util.HelpLocation; +import help.Help; +import help.HelpService; +import resources.Icons; + +/** + * A dialog for displaying a series of wizard panels used to collect data from the user before + * performing some task with the collected data. This dialog is generic and is used to display + * the wizard steps as defined by a {@link WizardModel}. + *

+ * To use this dialog, create an instance of a {@link WizardModel} and construct this dialog + * with that model, optionally specifying if the dialog is modal or not (default is modal). Then + * call either the {@link #show()} or {@link #show(Component)} method to display it. If the model's + * purpose is to create some object, get it from the model when done. + *

+ * For example, + *

+ * 		FooWizardModel model = new FooWizardModel();
+ * 		WizardDialog wizard = new WizardDialog(model);
+ * 		wizard.show();
+ * 		Foo foo = model.getFoo();
+ * 
+ * + */ +public class WizardDialog extends DialogComponentProvider { + /**Default text for the 'finish' button*/ + public static final String FINISH = "Finish"; + /**Default text for the 'next' button*/ + public static final String NEXT = "Next >>"; + /**Default text for the 'back' button*/ + public static final String BACK = "<< Back"; + + private final static String INIT_TITLE = "<< untitled >>"; + + private static final String FONT_ID = "font.wizard.border.title"; + + private WizardModel model; + private JButton backButton; + private JButton nextButton; + private JButton finishButton; + private JLabel titleLabel; + private JPanel containerPanel; + + /** + * Constructs a modal WizardDialog using the given model. + * @param model the wizard model + */ + public WizardDialog(WizardModel model) { + this(model, true); + } + + /** + * Constructs a WizardDialog using the given model. + * @param modal true if the wizard should be modal + * @param model the wizard model + */ + public WizardDialog(WizardModel model, boolean modal) { + super(model.getTitle(), modal, true, true, false); + this.model = model; + model.initialize(this); + addWorkPanel(buildWorkPanel()); + setRememberLocation(false); + setRememberSize(false); + createButtons(); + wizardStepChanged(getCurrentStep()); + } + + /** + * Returns the current status message being displayed in this dialog. + * @return the current status message being displayed in this dialog + */ + public String getStatusMessage() { + return getStatusText(); + } + + /** + * Sets the status message on the dialog + * @param message the message to display in the dialog + */ + public void setStatusMessage(String message) { + if (SwingUtilities.isEventDispatchThread()) { + setStatusText(message); + } + else { + Runnable r = () -> setStatusText(message); + SwingUtilities.invokeLater(r); + } + } + + /** + * Places focus on the 'next' button. + */ + public void focusNext() { + nextButton.requestFocus(); + } + + /** + * Places focus on the 'finish' button. + */ + public void focusFinish() { + finishButton.requestFocus(); + setDefaultButton(finishButton); + } + + /** + * Returns the current wizard panel. + * @return the current wizard panel + */ + public WizardStep getCurrentStep() { + return model.getCurrentStep(); + } + + /** + * Shows the wizard dialog. + */ + public void show() { + show(null); + } + + /** + * Shows the wizard dialog parented to the given component. + * @param parent the component to parent the dialog to + */ + public void show(Component parent) { + DockingWindowManager.showDialog(parent, this); + } + + @Override + public void dispose() { + model.dispose(); + } + + /** + * Cancels the wizard + */ + public void cancel() { + model.cancel(); + close(); + } + + @Override + protected void cancelCallback() { + cancel(); + } + + void statusChanged() { + backButton.setEnabled(model.canGoBack()); + nextButton.setEnabled(model.canGoNext()); + setStatusText(model.getStatusMessage()); + finishButton.setEnabled(model.canFinish()); + setCancelEnabled(model.canCancel()); + } + + void wizardStepChanged(WizardStep step) { + containerPanel.removeAll(); + JComponent component = step.getComponent(); + containerPanel.add(component); + containerPanel.repaint(); + + titleLabel.setText(step.getTitle()); + HelpLocation helpLocation = step.getHelpLocation(); + setHelpLocation(helpLocation); + HelpService help = Help.getHelpService(); + help.registerHelp(getComponent(), helpLocation); + statusChanged(); + } + + private void createButtons() { + backButton = new JButton(BACK); + backButton.setMnemonic('B'); + nextButton = new JButton(NEXT); + nextButton.setMnemonic('N'); + finishButton = new JButton(FINISH); + finishButton.setMnemonic('F'); + + backButton.addActionListener(evt -> model.goBack()); + nextButton.addActionListener(evt -> model.goNext()); + finishButton.addActionListener(evt -> model.finish()); + + addButton(backButton); + addButton(nextButton); + addButton(finishButton); + addCancelButton(); + + setDefaultButton(nextButton); + } + + private JPanel buildWorkPanel() { + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(createTitlePanel(), BorderLayout.NORTH); + panel.add(createContainerPanel(), BorderLayout.CENTER); + panel.setPreferredSize(model.getPreferredSize()); + return panel; + } + + private Component createTitlePanel() { + Icon wizardIcon = model.getIcon(); + titleLabel = (wizardIcon == null ? new GDLabel(INIT_TITLE) + : new GDLabel(INIT_TITLE, wizardIcon, SwingConstants.TRAILING)); + + EmptyBorderButton helpButton = new EmptyBorderButton(Icons.INFO_ICON); + helpButton.setToolTipText("Help (F1)"); + helpButton.addActionListener( + e -> DockingWindowManager.getHelpService().showHelp(rootPanel, false, rootPanel)); + + JPanel titlePanel = new JPanel(); + titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.X_AXIS)); + titlePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), + BorderFactory.createEmptyBorder(5, 5, 5, 5))); + titlePanel.add(titleLabel); + titlePanel.add(Box.createHorizontalGlue()); + titlePanel.add(helpButton); + return titlePanel; + } + + private JPanel createContainerPanel() { + containerPanel = new JPanel(new BorderLayout()); + Dimension panelSize = model.getPreferredSize(); + containerPanel.setPreferredSize(panelSize); + JComponent component = model.getCurrentStep().getComponent(); + containerPanel.add(component, BorderLayout.CENTER); + containerPanel.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + updateContainerPanelBorder(); + } + }); + + return containerPanel; + } + + private void updateContainerPanelBorder() { + Component component = containerPanel.getComponent(0); + Dimension size = component.getSize(); + Dimension minSize = component.getMinimumSize(); + if (size.height < minSize.height) { + TitledBorder titledBorder = + new TitledBorder(BorderFactory.createEmptyBorder(), "(Dialog too small!)"); + Gui.addThemeListener(e -> { + if (e.isFontChanged(FONT_ID)) { + titledBorder.setTitleFont(Gui.getFont(FONT_ID)); + } + }); + titledBorder.setTitleFont(Gui.getFont(FONT_ID)); + titledBorder.setTitleColor(Messages.NORMAL); + titledBorder.setTitlePosition(TitledBorder.BOTTOM); + titledBorder.setTitleJustification(TitledBorder.TRAILING); + containerPanel.setBorder(titledBorder); + } + else { + containerPanel.setBorder(BorderFactory.createEmptyBorder()); + } + } + +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardManager.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardManager.java deleted file mode 100644 index c0267a5b39..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardManager.java +++ /dev/null @@ -1,512 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.io.IOException; - -import javax.swing.*; -import javax.swing.border.TitledBorder; - -import docking.DockingWindowManager; -import docking.ReusableDialogComponentProvider; -import docking.widgets.EmptyBorderButton; -import docking.widgets.label.GDLabel; -import generic.theme.GThemeDefaults.Colors.Messages; -import generic.theme.Gui; -import ghidra.util.*; -import help.Help; -import help.HelpService; -import resources.Icons; - -/** - * A dialog that controls the panels for going to "Next" and "Previous" in some - * process that the user is being led through. - */ -public class WizardManager extends ReusableDialogComponentProvider implements WizardPanelListener { - /**Default text for the 'finish' button*/ - public static final String FINISH = "Finish"; - /**Default text for the 'next' button*/ - public static final String NEXT = "Next >>"; - /**Default text for the 'back' button*/ - public static final String BACK = "<< Back"; - - private final static String INIT_TITLE = "<< untitled >>"; - - private static final String FONT_ID = "font.wizard.border.title"; - - private PanelManager panelMgr; - private WizardPanel currWizPanel; - private JButton backButton; - private JButton nextButton; - private JButton finishButton; - private JLabel titleLabel; - private JPanel mainJPanel; - private JScrollPane scrollPane; - private JPanel currJPanel; - - /** - * Constructor - * @param title title of the dialog - * @param modal true if the wizard should be modal - * @param pmgr object that knows about the next and previous panels - */ - public WizardManager(String title, boolean modal, PanelManager pmgr) { - this(title, modal, pmgr, null); - } - - /** - * Constructor - * @param title title of the dialog - * @param modal true if the wizard should be modal - * @param pmgr object that knows about the next and previous panels - * @param wizardIcon icon to use for this dialog - */ - public WizardManager(String title, boolean modal, PanelManager pmgr, Icon wizardIcon) { - super(title, modal, true, true, false); - init(pmgr, wizardIcon); - } - - /** - * @see java.awt.Window#dispose() - */ - @Override - public void dispose() { - if (currWizPanel != null) { - currWizPanel.removeWizardPanelListener(this); - } - close(); - } - - /** - * - * @see docking.wizard.WizardPanelListener#validityChanged() - */ - @Override - public void validityChanged() { - clearStatusText(); - enableButtons(); - } - - /** - * Returns the current status message being displayed in this dialog. - * @return the current status message being displayed in this dialog - */ - public String getStatusMessage() { - return getStatusText(); - } - - /** - * @see docking.wizard.WizardPanelListener#setStatusMessage(String) - */ - @Override - public void setStatusMessage(final String msg) { - if (SwingUtilities.isEventDispatchThread()) { - setStatusText(msg); - } - else { - Runnable r = () -> setStatusText(msg); - SwingUtilities.invokeLater(r); - } - } - - /** - * Display this dialog. - */ - public void showWizard() { - showWizard(null); - } - - /** - * Display this dialog and parent it to the given component. - * @param parent parent - */ - public void showWizard(Component parent) { - panelMgr.initialize(); - - WizardPanel nextPanel = null; - try { - nextPanel = panelMgr.getNextPanel(); - if (nextPanel == null) { - Msg.showError(this, parent, getTitle() + " Error", "Failed to render wizard panel"); - return; - } - } - catch (IllegalPanelStateException e) { - handleIllegalStateException(e, true); - return; - } - - setCurrentPanel(nextPanel); - - DockingWindowManager.showDialog(parent, this); - } - - /** - * Notification that the wizard process is complete. - * @param success status of the process - */ - public void completed(boolean success) { - - if (!success) { - return; - } - - SystemUtilities.runSwingNow(() -> { - close(); - }); - } - - /** - * Enable the next, previous, and finish buttons according to the - * panel manager for this dialog. The panel manager is the object that - * knows the steps in the process and what buttons should be - * enabled. - */ - public void enableNavigation() { - enableButtons(); - super.setCancelEnabled(true); - } - - /** - * Disable the back, next, finish, and cancel buttons. - */ - public void disableNavigation() { - backButton.setEnabled(false); - nextButton.setEnabled(false); - finishButton.setEnabled(false); - super.setCancelEnabled(false); - } - - @Override - protected void cancelCallback() { - panelMgr.cancel(); - close(); - } - - private void init(PanelManager pmgr, Icon wizardIcon) { - this.panelMgr = pmgr; - this.panelMgr.setWizardManager(this); - - Dimension panelSize = panelMgr.getPanelSize(); - currJPanel = new JPanel(); - currJPanel.setPreferredSize(panelSize); - currJPanel.setMinimumSize(panelSize); - - scrollPane = new JScrollPane(); - scrollPane.setBorder(BorderFactory.createEmptyBorder()); - scrollPane.setPreferredSize(panelSize); - scrollPane.setMinimumSize(panelSize); - - scrollPane.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - updateScrollPaneBorder(currJPanel); - } - }); - - titleLabel = (wizardIcon == null ? new GDLabel(INIT_TITLE) - : new GDLabel(INIT_TITLE, wizardIcon, SwingConstants.TRAILING)); - - EmptyBorderButton helpButton = new EmptyBorderButton(Icons.INFO_ICON); - helpButton.setToolTipText("Help (F1)"); - helpButton.addActionListener( - e -> DockingWindowManager.getHelpService().showHelp(rootPanel, false, rootPanel)); - - JPanel titlePanel = new JPanel(); - titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.X_AXIS)); - titlePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), - BorderFactory.createEmptyBorder(5, 5, 5, 5))); - titlePanel.add(titleLabel); - titlePanel.add(Box.createHorizontalGlue()); - titlePanel.add(helpButton); - - mainJPanel = new JPanel(new BorderLayout()); - mainJPanel.add(titlePanel, BorderLayout.NORTH); - mainJPanel.add(scrollPane, BorderLayout.CENTER); - - backButton = new JButton(BACK); - backButton.setMnemonic('B'); - nextButton = new JButton(NEXT); - nextButton.setMnemonic('N'); - finishButton = new JButton(FINISH); - finishButton.setMnemonic('F'); - - backButton.addActionListener(evt -> back()); - nextButton.addActionListener(evt -> next()); - finishButton.addActionListener(evt -> finish()); - - addButton(backButton); - addButton(nextButton); - addButton(finishButton); - addCancelButton(); - - setDefaultButton(nextButton); - - addWorkPanel(mainJPanel); - setRememberLocation(false); - setRememberSize(false); - } - - private boolean handleIllegalStateException(IllegalPanelStateException e, boolean alwaysClose) { - Throwable cause = e.getCause(); - if (cause == null) { - cause = e; - } - boolean closeOnError = true; - if (!alwaysClose && cause instanceof IOException) { - closeOnError = false; - try { - panelMgr.initialize(); - setCurrentPanel(panelMgr.getInitialPanel()); - Msg.showError(this, null, getTitle() + " Error", cause.getMessage()); - enableNavigation(); - } - catch (IllegalPanelStateException e1) { - closeOnError = true; // close if unable to display first panel - } - } - if (closeOnError) { - if (cause instanceof IOException) { - Msg.showError(this, null, getTitle() + " Error", cause.getMessage()); - } - else { - String message = cause.getMessage(); - message = message != null ? message : cause.getClass().getSimpleName(); - Msg.showError(this, null, getTitle() + " Error", message, cause); - } - close(); - return false; - } - return true; - } - - /** - * Programmatically move the wizard back one panel. - * Simulates the user clicking on the 'back' button. - * Returns true if not on the first panel. - * @return true if not on the first panel - */ - public boolean back() { - if (backButton.isEnabled()) { - try { - setCurrentPanel(panelMgr.getPreviousPanel()); - } - catch (IllegalPanelStateException e) { - if (!handleIllegalStateException(e, false)) { - return false; - } - } - String msg = panelMgr.getStatusMessage(); - if (msg != null) { - setStatusMessage(msg); - } - return true; - } - return false; - } - - /** - * Programmatically move the wizard forward one panel. - * Simulates the user clicking on the 'next' button. - * Returns true if not on the last panel. - * @return true if not on the last panel - */ - public boolean next() { - if (nextButton.isEnabled()) { - try { - setCurrentPanel(panelMgr.getNextPanel()); - } - catch (IllegalPanelStateException e) { - if (!handleIllegalStateException(e, false)) { - return false; - } - } - String msg = panelMgr.getStatusMessage(); - if (msg != null) { - setStatusMessage(msg); - } - return true; - } - return false; - } - - /** - * Places focus on the 'next' button. - */ - public void focusNext() { - nextButton.requestFocus(); - } - - /** - * Places focus on the 'finish' button. - */ - public void focusFinish() { - finishButton.requestFocus(); - setDefaultButton(finishButton); - } - - /** - * Programmatically finished the wizard task. - * Returns true if the wizard can finish. - * @return true if the wizard can finish - */ - public boolean finish() { - if (finishButton.isEnabled()) { - try { - panelMgr.finish(); - } - catch (IllegalPanelStateException e) { - if (!handleIllegalStateException(e, false)) { - return false; - } - } - String msg = panelMgr.getStatusMessage(); - if (msg != null) { - setStatusMessage(msg); - } - return true; - } - return false; - } - - /** - * Returns the current wizard panel. - * @return the current wizard panel - */ - public WizardPanel getCurrentWizardPanel() { - return currWizPanel; - } - - /** - * Sets the current wizard panel. - * @param p the current panel - */ - private void setCurrentPanel(WizardPanel p) { - if (currWizPanel != null) { - currWizPanel.removeWizardPanelListener(this); - } - currWizPanel = p; - currWizPanel.addWizardPanelListener(this); - -/* todo: -// if this is the 1st time rendering this panel, -// then initialize it -// -if (!visitedMap.containsKey(currWizPanel)) { - visitedMap.put(currWizPanel, null); - currWizPanel.initialize(); -} -*/ - setPanel(currWizPanel.getPanel()); - setPanelTitle(currWizPanel.getTitle()); - setHelpLocation(currWizPanel.getHelpLocation()); - - HelpLocation hLoc = currWizPanel.getHelpLocation(); - HelpService help = Help.getHelpService(); - help.registerHelp(getComponent(), hLoc); - help.registerHelp(currWizPanel.getPanel(), hLoc); - - setStatusMessage(""); - enableButtons(); - - rootPanel.repaint(); - - requestFocusOnDefaultComponent(currWizPanel); - } - - private void requestFocusOnDefaultComponent(WizardPanel wizardPanel) { - Component defaultFocusComponent = wizardPanel.getDefaultFocusComponent(); - - // do this before the null check in order to clear out the last focus component - setFocusComponent(defaultFocusComponent); - - if (defaultFocusComponent == null) { - return; // nothing to do - } - - // this will have no effect if we are not showing, but the above call will handle that - // case - defaultFocusComponent.requestFocusInWindow(); - } - - private void setPanel(final JPanel panel) { - if (currJPanel != panel) { - scrollPane.setViewportView(panel); - currJPanel = panel; - - JScrollBar verticalBar = scrollPane.getVerticalScrollBar(); - verticalBar.setUnitIncrement(25); - scrollPane.validate(); - updateScrollPaneBorder(panel); - } - } - - private void updateScrollPaneBorder(JPanel panel) { - if (panel == null) { - return; - } - - scrollPane.setBackground(panel.getBackground()); - - if (scrollPane.getVerticalScrollBar().isShowing()) { - TitledBorder titledBorder = - new TitledBorder(BorderFactory.createEmptyBorder(), "(scroll for more options)"); - Gui.addThemeListener(e -> { - if (e.isFontChanged(FONT_ID)) { - titledBorder.setTitleFont(Gui.getFont(FONT_ID)); - } - }); - titledBorder.setTitleFont(Gui.getFont(FONT_ID)); - titledBorder.setTitleColor(Messages.NORMAL); - titledBorder.setTitlePosition(TitledBorder.BOTTOM); - titledBorder.setTitleJustification(TitledBorder.TRAILING); - - scrollPane.setBorder(titledBorder); - } - else { - scrollPane.setBorder(BorderFactory.createEmptyBorder()); - } - } - - private void setPanelTitle(String title) { - titleLabel.setText(title); - } - - private void enableButtons() { - boolean isValid = currWizPanel.isValidInformation(); - backButton.setEnabled(panelMgr.hasPreviousPanel()); - if (isValid) { - nextButton.setEnabled(panelMgr.hasNextPanel()); - finishButton.setEnabled(panelMgr.canFinish()); - } - else { - nextButton.setEnabled(false); - finishButton.setEnabled(false); - } - - // Update the default button that is executed when the user presses Enter. We always - // use the 'nextButton', unless that is disabled when the 'finishButton' is enabled - if (nextButton.isEnabled()) { - setDefaultButton(nextButton); - } - else if (finishButton.isEnabled()) { - setDefaultButton(finishButton); - } - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardModel.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardModel.java new file mode 100644 index 0000000000..9572f4926b --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardModel.java @@ -0,0 +1,395 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.wizard; + +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.Icon; +import javax.swing.JComponent; + +/** + * This is the main class for defining the steps and GUI for a {@link WizardDialog}. + *

+ * A wizard dialog is a dialog that uses multiple input Gui panels to gather all the + * information required before doing some complex action. Typically, a wizard dialog is used when + * the user input is best gathered in steps, so as to not overwhelm the user with an overly complex + * screen. Additionally, the information from one step may determine which follow-on steps + * are needed. + *

+ * To create a wizard dialog, the developer needs to create the following:
+ *

    + *
  1. A class that extends this WizardModel.
  2. + *
  3. One or more classes that extend {@link WizardStep}
    + *
  4. A data class that holds that data being collected by this wizard.
    + *
+ *

+ * Subclasses must at a minimum implement two methods. + *

    + *
  1. AddWizardSteps() - This is where the model defines the ordered list of + * {@link WizardStep} for this wizard. + *
  2. doFinish() - This is where the model should perform the main action of the wizard. This + * will be called when the user presses the Finish button and all the panels have + * had a chance to update the wizard data object.
  3. + *
+ *

+ * Optionally, there are several additional methods clients may want to override. + *

    + *
  1. dispose() - This will be called when the wizard is completed or cancelled and this is + * where any cleanup, if any, should be done, including cleaning up the data object if + * necessary. The super of this method will call dispose on each wizard step, so that + * won't be necessary as long as this overridden dispose() calls super.dispose();
  2. + *
  3. cancel() - This will only be called if the dialog is cancelled. This is a chance to + * perform cleanup that should only be done when the operation is cancelled. + * This is in addition to any cleanup in the dispose() call. This is not normally needed. + * An example of where this might be useful is suppose the purpose of the wizard is to + * pick and open two related files. If the wizard completes successfully, then the two + * files are supposed to remain open after the wizard is closed. However, suppose after + * one step that opened the first file, the user cancels the operation. Then you would + * want to close the first file that was opened in this cancelled cancel() call, because + * you don't want to do it in the dispose() since that will be called even if the wizard + * completed.
  4. + *
  5. getPreferredSize() - By default, this will return a preferred size that is the biggest + * width and height of all the preferred sizes of the step panels. Override this to + * simply specify the preferred size of the dialog.
  6. + * + * @param the data object for this wizard + */ +public abstract class WizardModel { + private List> wizardSteps = new ArrayList<>(); + private int currentStepIndex = 0; + protected T data; + protected WizardDialog wizardDialog; + private WizardStep currentStep; + private String title; + private Icon wizardIcon; + private boolean completed = false; + + // The busy flag is not used for threading. Is is simply set while performing the apply() + // calls which can be lengthy. Its only purpose is to for disabling the various dialog buttons + // while the performing these potentially expensive calls. It will only ever be set or + // checked on the swing thread. + private boolean busy; + + /** + * Constructor for a wizard model without an icon. + * @param title the title for the wizard dialog. + * @param data the data object that will be used to store wizard data. This will typically be + * a simple data container designed specifically for this wizard. + */ + protected WizardModel(String title, T data) { + this(title, data, null); + } + + /** + * Constructor for a wizard model with an icon + * @param title the title for the wizard dialog. + * @param data the data object that will be used to store wizard data. This will typically be + * a simple data container designed specifically for this wizard. + * @param wizardIcon the icon to display on the wizard + */ + protected WizardModel(String title, T data, Icon wizardIcon) { + this.title = title; + this.data = data; + this.wizardIcon = wizardIcon; + } + + /** + * This method defines the wizard step objects in the order that will be displayed. + * @param steps the wizard steps + */ + protected abstract void addWizardSteps(List> steps); + + /** + * This method is called when the user presses the "Finish" button and all the steps have + * completed their apply() methods successfully. + * @return true if the model successfully completes the wizard. If false is returned, the wizard + * will not be closed. + */ + protected abstract boolean doFinish(); + + /** + * Subclasses should override this method if they have special cleanup that only needs to + * be done if the wizard is cancelled. Otherwise, all cleanup should be done in the + * {@link #dispose()} method which is called whether or not the dialog is cancelled. + */ + protected void cancel() { + // For subclasses to clean up specifically if cancelled. The dispose() will also be called + // which, typically, is sufficient to clean up whether the wizard completed fully or was + // cancelled. + } + + /** + * Returns the title of this wizard. + * @return the title of this wizard + */ + public String getTitle() { + return title; + } + + /** + * Returns the icon for this wizard. + * @return the icon for this wizard + */ + public Icon getIcon() { + return wizardIcon; + } + + /** + * Returns the current status message for the wizard. + * @return the current status message for the wizard + */ + public String getStatusMessage() { + return getCurrentStep().getStatusMessage(); + } + + /** + * Completes the wizard. Gives each remaining panel a chance to validate and populate the data + * object before calling the {@link #doFinish()} method where subclasses can do the final task. + */ + public final void finish() { + if (!canFinish()) { + return; + } + int failedStep = applyRemainingSteps(); + + if (failedStep >= 0) { + setWizardStep(failedStep); + return; + } + boolean success = doFinish(); + if (success) { + completed = true; + wizardDialog.close(); + } + } + + /** + * Calls dispose() on all the wizard steps. Subclasses can override this do do additional + * cleanup if needed. + */ + public void dispose() { + for (WizardStep step : wizardSteps) { + step.dispose(data); + } + } + + /** + * Returns the data object which is populated by the various wizard steps as they completed. + * @return the data object + */ + public T getData() { + return data; + } + + /** + * Returns true if the wizard was cancelled. + * @return true if the wizard was cancelled + */ + public boolean wasCancelled() { + return !completed; + } + + /** + * Returns the current {@link WizardStep}. + * @return the current wizard step + */ + public WizardStep getCurrentStep() { + return currentStep; + } + + /** + * Returns true if the "Back" button should be enabled. + * @return true if the "Back" button should be enabled + */ + public boolean canGoBack() { + return !busy && currentStepIndex > 0; + } + + /** + * Returns true if the "Next" button should be enabled. + * @return true if the "Next" button should be enabled + */ + public boolean canGoNext() { + if (busy) { + return false; + } + currentStep.clearStatus(); + if (!currentStep.isValid()) { + return false; + } + currentStep.populateData(data); + return findNextApplicableStep() >= 0; + } + + /** + * Returns true if the "Finish" button should be enabled. + * @return true if the "Finish" button should be enabled + */ + public boolean canFinish() { + if (busy) { + return false; + } + if (!currentStep.isValid()) { + return false; + } + + // flush current step's gui info to data object so follow-on steps can see the current + // changes when deciding if they can finish + currentStep.populateData(data); + + // All follow-on steps should evaluate if the current data object has all the required + // info to finish. The follow-on steps can change the data, but should only do so based + // on the values in the data object. Anything in their gui data should be ignored in + // their canFinish() methods as it could be stale after the user backed up and made changes. + for (int i = currentStepIndex + 1; i < wizardSteps.size(); i++) { + WizardStep step = wizardSteps.get(i); + if (step.isApplicable(data) && !step.canFinish(data)) { + return false; + } + } + return true; + } + + /** + * Returns true if the cancel button should be enabled. The only time this is disabled is + * when the wizard is performing some expensive operation between steps. + * @return true if the cancel button should be enabled + */ + public boolean canCancel() { + return !busy; + } + + /** + * Returns the wizard back to the previous step. + */ + public void goBack() { + if (canGoBack()) { + setWizardStep(findPreviousApplicableStep()); + } + } + + /** + * Advances the wizard to the next step. + */ + public void goNext() { + if (!canGoNext()) { + return; + } + boolean success = apply(); + if (success) { + setWizardStep(findNextApplicableStep()); + } + else { + wizardDialog.statusChanged(); + } + } + + /** + * Returns the preferred size of the panels in the wizard dialog. By default the preferred + * size is the largest size of any panels that have been created at construction time + * (components are not required to be constructed until they are shown). + * Subclasses can override this method to just hard code a preferred size for the dialog. + * @return the preferred size of the panels in the wizard dialog + */ + protected Dimension getPreferredSize() { + int width = 300; + int height = 200; + for (WizardStep step : wizardSteps) { + JComponent c = step.getComponent(); + if (c != null) { + Dimension preferredSize = c.getPreferredSize(); + width = Math.max(width, preferredSize.width); + height = Math.max(height, preferredSize.height); + } + } + return new Dimension(width, height); + } + + void initialize(WizardDialog dialog) { + addWizardSteps(wizardSteps); + setWizardStep(0); + this.wizardDialog = dialog; + } + + private boolean apply() { + busy = true; + try { + return currentStep.apply(data); + } + finally { + busy = false; + } + } + + private int applyRemainingSteps() { + busy = true; + try { + for (int i = currentStepIndex; i < wizardSteps.size(); i++) { + WizardStep step = wizardSteps.get(i); + if (step.isApplicable(data) && !step.apply(data)) { + return i; // applicable step failed to apply, return failed step index + } + } + } + finally { + busy = false; + } + return -1; + } + + private void setWizardStep(int stepIndex) { + currentStepIndex = stepIndex; + currentStep = wizardSteps.get(currentStepIndex); + currentStep.initialize(data); + notifyWizardStepChanged(); + } + + private void notifyWizardStepChanged() { + if (wizardDialog != null) { + wizardDialog.wizardStepChanged(currentStep); + } + } + + protected void statusChanged(WizardStep step) { + if (wizardDialog != null && step == currentStep) { + wizardDialog.statusChanged(); + } + } + + protected void setStatusMessage(String statusMessage) { + currentStep.setStatusMessage(statusMessage); + } + + private int findNextApplicableStep() { + for (int i = currentStepIndex + 1; i < wizardSteps.size(); i++) { + if (wizardSteps.get(i).isApplicable(data)) { + return i; + } + } + return -1; + } + + private int findPreviousApplicableStep() { + for (int i = currentStepIndex - 1; i >= 1; i--) { + if (wizardSteps.get(i).isApplicable(data)) { + return i; + } + } + return 0; // step 0 is always applicable + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanel.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanel.java deleted file mode 100644 index 35e6fd6a27..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanel.java +++ /dev/null @@ -1,73 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import ghidra.util.HelpLocation; - -import java.awt.Component; - -import javax.swing.JPanel; - -/** - * Interface to define methods for panels to be shown in the wizard dialog. - */ -public interface WizardPanel { - /** - * Get the title for this panel. - * @return String title - */ - public String getTitle(); - /** - * Get the panel object - * @return JPanel panel - */ - public JPanel getPanel(); - /** - * Return true if the user entered valid information for this panel. - * @return boolean whether or not the info on the panel valid - */ - public boolean isValidInformation(); - /** - * Initialize the panel as though this is the first time it is - * being displayed. - */ - public void initialize(); - /** - * Add a listener to this panel. - * @param l listener to add - */ - public void addWizardPanelListener(WizardPanelListener l); - /** - * Remove the listener from this panel. - * @param l listener to remove - */ - public void removeWizardPanelListener(WizardPanelListener l); - /** - * Returns the help content location for this panel. - * - * @return String help location for this panel; return null if default help - * location should be used. - */ - public HelpLocation getHelpLocation(); - - /** - * Returns the component, if any, that should receive focus when this panel is shown. - * @return the component, if any, that should receive focus when this panel is shown; null - * if no preferred focus component exists. - */ - public Component getDefaultFocusComponent(); -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanelDisplayability.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanelDisplayability.java deleted file mode 100644 index 798fbadade..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanelDisplayability.java +++ /dev/null @@ -1,21 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -public enum WizardPanelDisplayability { - MUST_BE_DISPLAYED, CAN_BE_DISPLAYED, DO_NOT_DISPLAY; -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanelListener.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanelListener.java deleted file mode 100644 index d1d3356f6b..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardPanelListener.java +++ /dev/null @@ -1,34 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -/** - * Listener that is called when something on the WizardPanel has - * changed. - */ -public interface WizardPanelListener { - /** - * Notification that something on the panel changed. - */ - public void validityChanged(); - - /** - * Notification to set a status message. - * @param msg message - */ - public void setStatusMessage(String msg); -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardState.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardState.java deleted file mode 100644 index 1ee598239e..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardState.java +++ /dev/null @@ -1,106 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import java.util.*; -import java.util.Map.Entry; - -public class WizardState implements Cloneable { - private Map map = new HashMap(); - private Map> dependentMap = new HashMap>(); - - @Override - protected Object clone() { - WizardState anakin = new WizardState(); - anakin.map = new HashMap(map); - Set>> entrySet = dependentMap.entrySet(); - for (Entry> entry : entrySet) { - T key = entry.getKey(); - Set value = entry.getValue(); - anakin.dependentMap.put(key, new HashSet(value)); - } - return anakin; - } - - /** - * Gets the value for a property key. - * @param key the identifier for the property. Typically, it would be a string or enum. - * @return the value associated with the given property key or null if the property has no - * value. - */ - public Object get(T key) { - return map.get( key ); - } - - /** - * Sets the property value for a given property key. Also clears out the property values for - * any properties that depend on this property. - * @param key the propertyKey whose value is to be set or changed with the new value. - * @param value the new value for the property. - */ - public void put(T key, Object value) { - if (map.containsKey(key)) { - Object oldValue = map.get(key); - if (oldValue == value) { - return; - } - if (oldValue != null && oldValue.equals(value)) { - return; - } - } - map.put( key, value ); - clearDependents(key); - } - - /** - * Removes the property key,value pair from this wizard state. - * @param key the property key of the property to be cleared. - */ - public void clear(T key) { - Object removedValue = map.remove( key ); - if (removedValue != null) { - clearDependents(key); - } - } - - /** - * Defines a dependency from one property to another. A property dependency has the effect of - * clear the dependent's property value whenever the predecessor property is changed or cleared. - * @param dependent the property whose value is to be cleared when the predecessor property is - * changed or cleared. - * @param predecessor the property that, when changed or cleared, will cause the dependent property - * to be cleared. - */ - public void addDependency(T dependent, T predecessor) { - Set dependents = dependentMap.get( predecessor ); - if (dependents == null) { - dependents = new HashSet(); - dependentMap.put( predecessor, dependents ); - } - dependents.add(dependent); - } - - private void clearDependents(T key) { - Set dependencies = dependentMap.get( key ); - if (dependencies != null) { - for ( T dependent : dependencies ) { - clear(dependent); - } - } - } - -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardStateDependencyValidator.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardStateDependencyValidator.java deleted file mode 100644 index 69d3d3ccc9..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardStateDependencyValidator.java +++ /dev/null @@ -1,88 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking.wizard; - -import ghidra.util.SystemUtilities; - -import java.util.*; - -public class WizardStateDependencyValidator { - - private Set dependentSet = new HashSet(); - private Map> dependentMap = new HashMap>(); - private Map valueMap = new HashMap(); - - /** - * Registers a dependency from one property state to another. If the predecessor is null, then - * the dependent is registered such that a call to {@link #findAffectedDependants(WizardState)} - * will include that property key only if its cached value is null. (i.e. the first time it - * is called.) - * @param dependent the property key that depends on a previous property being set. - * @param predecessor the property key of the property that affects the dependent property. - */ - public void addDependency(T dependent, T predecessor) { - dependentSet.add(dependent); - if (predecessor != null) { - Set dependents = dependentMap.get( predecessor ); - if (dependents == null) { - dependents = new HashSet(); - dependentMap.put( predecessor, dependents ); - } - dependents.add(dependent); - } - } - - /** - * Returns a set of all property keys that need to have their values set because a predecessor - * property has been changed that may affect the valid values for this property. Also, any - * property keys that don't have a value in the local cache will be returned. - * @param globalState the global WizardState that is passed from one wizard panel to the next. - * @return the set of property keys whose values should be (re)computed. - */ - public Set findAffectedDependants(WizardState globalState) { - Set affectedDependendants = new HashSet(); - - for ( T predecessor : dependentMap.keySet() ) { - Object globalValue = globalState.get( predecessor ); - Object localValue = valueMap.get( predecessor ); - if (!SystemUtilities.isEqual( globalValue, localValue )) { - affectedDependendants.addAll( dependentMap.get( predecessor ) ); - } - } - for (T dependant : dependentSet) { - if (valueMap.get( dependant ) == null) { - affectedDependendants.add( dependant ); - } - } - - return affectedDependendants; - } - - /** - * Updates the local cache values for all the relevant properties. This method should be - * called from a wizard panel when the "next" action is invoked (i.e. the user values have been - * accepted). - * @param globalState The WizardState containing all the property values. - */ - public void updatePropertyValues(WizardState globalState) { - for ( T dependent : dependentSet ) { - valueMap.put( dependent, globalState.get( dependent ) ); - } - for ( T trigger : dependentMap.keySet()) { - valueMap.put( trigger, globalState.get( trigger )); - } - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardStep.java b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardStep.java new file mode 100644 index 0000000000..96405cc208 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/wizard/WizardStep.java @@ -0,0 +1,258 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.wizard; + +import java.awt.Component; + +import javax.swing.JComponent; + +import ghidra.util.HelpLocation; + +/** + * This is the base class for defining a step in a {@link WizardModel} to be displayed using + * a {@link WizardDialog}. + *

    + * A wizard dialog collects information from a user in a step by step process. Each step presents + * a Gui panel to the user that must be completed and validated before advancing to the next step. + *

    + * Each step in the wizard must implement several methods to support the wizard step's life cycle. + * The basic life cycle for a step that is shown is initialize(), getComponent(), then repeated + * calls to isValid() and populateData() while it is showing and being modified, followed by + * the apply() method when moving on to the next step. + *

    + * In addition, there are several methods that step's must implement that are called when the step + * is not showing and should not consider any Gui state that it may have (The Gui state may be stale + * if the user back tracked or it may never have been created or initialized) such as the + * isApplicable() and canFinish(). + *

    + * Each step must implement the following methods: + * + *

      + *
    • initialize(T data) - This method is called just before the step's Gui component + * is shown. This is were the step should use the information in the passed in data object + * to populate its Gui data fields. The component can be lazily created in this method + * if not created in the constructor, as the {@link #getComponent()} will not be called + * before the initialize method is called. Note that the initialize method can possibly + * be called multiple times if the user goes back to a previous panel and then forward + * again.
    • + * + *
    • isValid() - This method is called repeatedly while the step is showing as the + * step calls back to the model as any Gui component is modified. When the step reports + * back that it is valid, then the next and finish buttons can be enabled. Also, if + * valid, this step's {@link #populateData(Object)} will be called so its data can + * be seen by follow-on steps in their {@link #canFinish(Object)} calls.
    • + * + *
    • canFinish(T data) - This method is called on steps that follow the current step + * if the current step is valid. When implementing this method, the data in the step's Gui + * should be ignored (it may not have been initialized yet or it may be stale if the user + * back tracked) and its determination if it can finish should be done + * purely based on the information in the passed in data object. The idea is that if + * a step returns true for canFinish(), it does not need to be shown before the wizard + * can complete.
    • + *
    • + * populateData(T data) - This method is called on the current step whenever the + * isValid() method of the current step returns true. The step should simply transfer data + * from it's Gui component to the data component. It should not do any time consuming + * operations in this method.
    • + *
    • + * apply(T data) - This method is called on each step when it is the current step + * and the next button is pressed. It is also called on each follow-on step when the finish + * button is pressed. Expensive operations should be done here when a step is completed and + * moving to the next step. Typically, the implementer of the apply method should perform + * the operation in a task. One example, might be the user is picking files to open, the + * populateData() method might copy the file names to the data object, but the apply() + * method is used to actually open the files and put them into the data object. Most + * wizard steps should just return true here.
    • + * + *
    • + * isApplicable(T data) - this method is called to see if a step is applicable based + * on choices made in previous steps.
    • + *
    + * + * @param the custom data object for wizard + */ +public abstract class WizardStep { + private String title; + private HelpLocation helpLocation; + private WizardModel model; + private String statusMessage; + + /** + * Constructor + * @param model the wizard model + * @param title the title for the wizard step (can be null and set later) + * @param help the help location for the wizard step (can be null and set later) + */ + protected WizardStep(WizardModel model, String title, HelpLocation help) { + this.model = model; + this.title = title; + this.helpLocation = help; + } + + /** + * Sets the title for this step. Typically, this method is only used if the title is data + * dependent. + * @param title the new title for the step. + */ + protected void setTitle(String title) { + this.title = title; + } + + /** + * Sets the help location for this step. Typically, this method is only used if the help is data + * dependent. + * @param help the new help location for the step. + */ + protected void setHelpLocation(HelpLocation help) { + this.helpLocation = help; + } + + /** + * Initialize the panel as though this is the first time it is + * being displayed. This is where the step should initialize all Gui fields from the given + * data object. + *

    + * Creating the Gui component can be done lazily in this method if not done in + * the constructor, as the initialize() method will always be called before the getComponent() + * method is called. Just be careful as this method can be called multiple times if the user + * backtracks in the wizard dialog. + * @param data the custom wizard data containing the information from all previous steps. + */ + public abstract void initialize(T data); + + /** + * Checks if the Gui component has completed and has valid information. Typically, whenever the + * Gui state changes, it notifies the model using the statusChangedCallback, which in turn + * will call the isValid() method on the current step. If the current step is valid, it will + * in turn trigger additional calls to follow-on steps to see if the wizard can finish. + * @return true if the Gui component has completed and valid information and is eligible to + * continue to the next step. + */ + public abstract boolean isValid(); + + /** + * Reports true if the information in the given data object is sufficient enough that this + * step does not need to be shown in order to complete the wizard. It is only called on steps + * subsequent to the current step. Wizard steps should only make their decisions based on the + * information in the data object and not their internal GUI, which might not have even been + * initialized at this point. This method is only called on steps whose + * {@link #isApplicable(Object)} method returns true. + * @param data the custom wizard data containing the information from all previous steps. + * @return true if this step does not need to be shown before completing the wizard + */ + public abstract boolean canFinish(T data); + + /** + * This method should populate the given data object with information from its Gui component. + * @param data the custom wizard data containing the information from all previous steps. + */ + public abstract void populateData(T data); + + /** + * This method is called on the current step when advancing to the next step. It is also called + * on all subsequent steps when finishing the wizard as those steps are skipped because the + * finish button was pressed. This method is for steps to perform more extensive operations + * when moving on to subsequent steps. Most steps can just return true here as simple data + * will be added during the {@link #populateData(Object)} method. + * @param data the custom wizard data containing the information from all previous steps. + * @return true if the apply completes successfully. + */ + public abstract boolean apply(T data); + + /** + * Returns true if a step is applicable base on the information in the given data object. + * Data from previous steps may make a subsequent step applicable or not. + * @param data the custom wizard data containing the information from all previous steps. + * @return + */ + public boolean isApplicable(T data) { + return true; + } + + /** + * Get the panel object + * @return JPanel panel + */ + public abstract JComponent getComponent(); + + /** + * Get the title for this step. + * @return the title for this step + */ + public String getTitle() { + return title; + } + + /** + * Returns the help content location for this panel. + * + * @return String help location for this panel; return null if default help + * location should be used. + */ + public HelpLocation getHelpLocation() { + return helpLocation; + } + + /** + * Returns the component, if any, that should receive focus when this panel is shown. + * @return the component, if any, that should receive focus when this panel is shown; null + * if no preferred focus component exists. + */ + public Component getDefaultFocusComponent() { + return null; + } + + /** + * Returns the current status message to be displayed in the wizard dialog for this step. + * @return the current status message to be displayed in the wizard dialog for this step. + */ + protected String getStatusMessage() { + return statusMessage; + } + + /** + * Sets the current status message to be displayed in the wizard dialog for this step. + * @param message the message to display in the wizard dialog + */ + protected void setStatusMessage(String message) { + this.statusMessage = message; + } + + /** + * Clears the current status message for this step. + */ + protected void clearStatus() { + statusMessage = null; + } + + /** + * Subclasses can call this method to notify the wizard dialog that the user made some + * change to the current step's Gui state. This will trigger calls to the {@link #isValid()} + * and possibly {@link #populateData(Object)} + */ + protected void notifyStatusChanged() { + model.statusChanged(this); + } + + /** + * Called for steps to possibly do any clean up. + * @param data the custom data object + */ + protected void dispose(T data) { + // for sub-classes to override if needed + } + +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/wizard/package.html b/Ghidra/Framework/Docking/src/main/java/docking/wizard/package.html deleted file mode 100644 index 4148136a07..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/wizard/package.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - package ghidra.util.bean.wizard - - -Provides classes for a generic wizard that leads the user through steps in a process. - - diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/wiz/WizardTest.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/wiz/WizardTest.java new file mode 100644 index 0000000000..c0983772b0 --- /dev/null +++ b/Ghidra/Framework/Docking/src/test.slow/java/docking/wiz/WizardTest.java @@ -0,0 +1,608 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package docking.wiz; + +import static org.junit.Assert.*; + +import java.util.List; +import java.util.Objects; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.junit.Before; +import org.junit.Test; + +import docking.test.AbstractDockingTest; +import docking.widgets.textfield.IntegerTextField; +import docking.wizard.*; +import ghidra.util.layout.PairLayout; + +public class WizardTest extends AbstractDockingTest { + + private WizardDialog wizardDialog; + private TestWizardModel model; + private boolean stage1Disposed; + private boolean stage2Disposed; + private boolean stage3Disposed; + + @Before + public void setup() { + model = new TestWizardModel(); + wizardDialog = new WizardDialog(model); + showDialog(wizardDialog); + } + + @Test + public void testInitialState() { + assertFalse(canGoNext()); + assertFalse(canGoBack()); + assertFalse(canFinish()); + assertTrue(canCancel()); + assertEquals("Please enter a name", getStatusMessage()); + + } + + @Test + public void testAfterNameEntered() { + setName("Bob"); + + assertFalse(canGoNext()); + assertFalse(canGoBack()); + assertFalse(canFinish()); + assertTrue(canCancel()); + assertEquals("Please enter an address", getStatusMessage()); + + } + + @Test + public void testAfterNameAndAddressEntered() { + setName("Bob"); + setAddress("123 Main Street"); + assertTrue(canGoNext()); + assertFalse(canGoBack()); + assertFalse(canFinish()); + assertTrue(canCancel()); + assertNull(getStatusMessage()); + + } + + @Test + public void testPanel2State() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + assertFalse(canGoNext()); + assertTrue(canGoBack()); + assertFalse(canFinish()); + assertTrue(canCancel()); + assertEquals("Age must be >= 18", getStatusMessage()); + + } + + @Test + public void testPanel2StateAfterAge() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + + assertTrue(canGoNext()); + assertTrue(canGoBack()); + assertTrue(canFinish()); // phone is optional + assertTrue(canCancel()); + + } + + @Test + public void testPanel3State() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + goNext(); + + assertFalse(canGoNext()); + assertTrue(canGoBack()); + assertTrue(canFinish()); // phone is optional + assertTrue(canCancel()); + } + + @Test + public void testGoBack() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + assertTrue(canGoBack()); + goBack(); + assertTrue(canGoNext()); + assertTrue(canFinish()); + finish(); + assertEquals(32, model.getData().getAge()); + } + + @Test + public void testGoBackAndForwardDoesntResetStage2Data() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + assertTrue(canGoBack()); + goBack(); + assertTrue(canGoNext()); + goNext(); + finish(); + assertEquals(32, model.getData().getAge()); + } + + @Test + public void testGoBackAndChangingNameResetsAgeAndEnablement() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + assertTrue(canGoBack()); + goBack(); + setName("Joe"); + assertTrue(canGoNext()); + assertFalse(canFinish()); + } + + @Test + public void testFinishAfterStage2() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + finish(); + assertFalse(wizardDialog.isVisible()); + TestWizardData data = model.getData(); + assertEquals("Bob", data.getName()); + assertEquals("123 Main Street", data.getAddress()); + assertEquals(32, data.getAge()); + assertEquals(0, data.getPhoneNumber()); + } + + @Test + public void testFinishAfterStage3() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + goNext(); + setPhoneNumber(1234567890); + finish(); + assertFalse(wizardDialog.isVisible()); + TestWizardData data = model.getData(); + assertEquals("Bob", data.getName()); + assertEquals("123 Main Street", data.getAddress()); + assertEquals(32, data.getAge()); + assertEquals(1234567890, data.getPhoneNumber()); + } + + @Test + public void testDisposeGetsCalledWhenFinished() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + goNext(); + setPhoneNumber(1234567890); + finish(); + assertTrue(stage1Disposed); + assertTrue(stage2Disposed); + assertTrue(stage3Disposed); + + } + + @Test + public void testDisposeGetsCalledWhenCancelled() { + setName("Bob"); + setAddress("123 Main Street"); + goNext(); + setAge(32); + goNext(); + setPhoneNumber(1234567890); + cancel(); + assertTrue(stage1Disposed); + assertTrue(stage2Disposed); + assertTrue(stage3Disposed); + } + + private void goNext() { + runSwing(() -> model.goNext()); + } + + private void goBack() { + runSwing(() -> model.goBack()); + } + + private void finish() { + runSwing(() -> model.finish()); + } + + private void cancel() { + runSwing(() -> wizardDialog.cancel()); + } + + private void setName(String name) { + runSwing(() -> { + TestWizardStage1 stage = (TestWizardStage1) model.getCurrentStep(); + stage.setName(name); + }); + } + + private void setAddress(String address) { + runSwing(() -> { + TestWizardStage1 stage = (TestWizardStage1) model.getCurrentStep(); + stage.setAddress(address); + }); + } + + private void setAge(int age) { + runSwing(() -> { + TestWizardStage2 stage = (TestWizardStage2) model.getCurrentStep(); + stage.setAge(age); + }); + } + + private void setPhoneNumber(int phone) { + runSwing(() -> { + TestWizardStage3 stage = (TestWizardStage3) model.getCurrentStep(); + stage.setPhoneNumber(phone); + }); + } + + private boolean canGoBack() { + return runSwing(() -> model.canGoBack()); + } + + private boolean canGoNext() { + return runSwing(() -> model.canGoNext()); + } + + private boolean canFinish() { + return runSwing(() -> model.canFinish()); + } + + private boolean canCancel() { + return runSwing(() -> model.canCancel()); + } + + private String getStatusMessage() { + return runSwing(() -> model.getStatusMessage()); + } + + private void showDialog(WizardDialog dialog) { + // note: can't call runSwing with true, but use false and then waitForSwing() or + // else this thread blocks forever. + runSwing(() -> dialog.show(), false); + waitForSwing(); + } + + private static class TestWizardData { + private String name; + private String address; + private long phoneNumber; + private int age; + + public void setName(String name) { + if (!Objects.equals(this.name, name)) { + age = 0; // pretend age should be reset when name changes + } + this.name = name; + } + + public String getName() { + return name; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getAddress() { + return address; + } + + public long getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(long phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public void setAge(int age) { + this.age = age; + } + + public int getAge() { + return age; + } + } + + private class TestWizardStage1 extends WizardStep { + private JTextField nameField; + private JTextField addressField; + private JComponent component; + + protected TestWizardStage1(WizardModel model) { + super(model, "Name & Address", null); + component = buildComponent(); + } + + public void setName(String name) { + nameField.setText(name); + } + + public void setAddress(String name) { + addressField.setText(name); + } + + @Override + public void initialize(TestWizardData data) { + // nothing + + } + + @Override + protected void dispose(TestWizardData data) { + stage1Disposed = true; + } + + @Override + public boolean isValid() { + setStatusMessage(null); + String name = nameField.getText(); + String address = addressField.getText(); + if (name.isBlank()) { + setStatusMessage("Please enter a name"); + return false; + } + if (address.isBlank()) { + setStatusMessage("Please enter an address"); + return false; + } + return true; + } + + @Override + public void populateData(TestWizardData data) { + data.setName(nameField.getText()); + data.setAddress(addressField.getText()); + } + + @Override + public boolean apply(TestWizardData data) { + // pretend extra check when next button or finish is applied + if (data.getAddress().length() < 10) { + setStatusMessage("Address is too short"); + return false; + } + return true; + } + + @Override + public boolean canFinish(TestWizardData data) { + return true; + } + + @Override + public JComponent getComponent() { + return component; + } + + private JComponent buildComponent() { + JPanel panel = new JPanel(new PairLayout()); + nameField = new JTextField(20); + addressField = new JTextField(30); + panel = new JPanel(new PairLayout()); + panel.add(new JLabel("Name:")); + panel.add(nameField); + panel.add(new JLabel("Address:")); + panel.add(addressField); + DocumentListener listener = new DocumentListener() { + + @Override + public void removeUpdate(DocumentEvent e) { + notifyStatusChanged(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + notifyStatusChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + notifyStatusChanged(); + } + }; + nameField.getDocument().addDocumentListener(listener); + addressField.getDocument().addDocumentListener(listener); + return panel; + } + } + + private class TestWizardStage2 extends WizardStep { + private IntegerTextField ageField; + private JComponent component; + + protected TestWizardStage2(WizardModel model) { + super(model, "Age", null); + component = buildComponent(); + } + + public void setAge(int age) { + ageField.setValue(age); + } + + @Override + protected void dispose(TestWizardData data) { + stage2Disposed = true; + } + + @Override + public void initialize(TestWizardData data) { + int age = data.getAge(); + if (age == 0) { + ageField.setText(""); + } + else { + ageField.setValue(age); + } + } + + @Override + public boolean isValid() { + setStatusMessage(null); + int age = ageField.getIntValue(); + if (age < 18) { + setStatusMessage("Age must be >= 18"); + return false; + } + return true; + } + + @Override + public void populateData(TestWizardData data) { + data.setAge(ageField.getIntValue()); + } + + @Override + public boolean apply(TestWizardData data) { + return true; + } + + @Override + public JComponent getComponent() { + return component; + } + + @Override + public boolean canFinish(TestWizardData data) { + return data.getAge() >= 18; + } + + private JComponent buildComponent() { + JPanel panel = new JPanel(new PairLayout()); + ageField = new IntegerTextField(10); + panel = new JPanel(new PairLayout()); + panel.add(new JLabel("Age:")); + panel.add(ageField.getComponent()); + + ageField.addChangeListener(e -> notifyStatusChanged()); + return panel; + } + } + + private class TestWizardStage3 extends WizardStep { + private IntegerTextField phoneField; + private JComponent component; + + protected TestWizardStage3(WizardModel model) { + super(model, "Phone #", null); + component = buildComponent(); + } + + @Override + public void initialize(TestWizardData data) { + // nothing + + } + + public void setPhoneNumber(int phone) { + phoneField.setValue(phone); + } + + @Override + protected void dispose(TestWizardData data) { + stage3Disposed = true; + } + + @Override + public boolean isValid() { + setStatusMessage(null); + long phone = phoneField.getLongValue(); + if (phone == 0) { + // allow phone # to be optional + return true; + } + // if non zero, require to be 10 digits + String s = Long.toString(phone); + if (s.length() != 10) { + setStatusMessage("Phone # must be exactly 10 digits"); + return false; + } + return true; + } + + @Override + public void populateData(TestWizardData data) { + data.setPhoneNumber(phoneField.getLongValue()); + } + + @Override + public boolean apply(TestWizardData data) { + return true; + } + + @Override + public boolean canFinish(TestWizardData data) { + return true; + } + + @Override + public JComponent getComponent() { + return component; + } + + private JComponent buildComponent() { + JPanel panel = new JPanel(new PairLayout()); + phoneField = new IntegerTextField(10); + panel = new JPanel(new PairLayout()); + panel.add(new JLabel("Phone #:")); + panel.add(phoneField.getComponent()); + + phoneField.addChangeListener(e -> notifyStatusChanged()); + return panel; + } + } + + private class TestWizardModel extends WizardModel { + + protected TestWizardModel() { + super("Test Wizard", new TestWizardData()); + } + + @Override + protected boolean doFinish() { + return true; + } + + @Override + protected void addWizardSteps(List> wizardStages) { + wizardStages.add(new TestWizardStage1(this)); + wizardStages.add(new TestWizardStage2(this)); + wizardStages.add(new TestWizardStage3(this)); + + } + + } +} diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java index b67cc3d99a..6cd8ea33b9 100644 --- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java +++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java @@ -452,6 +452,12 @@ public class ToolOptions extends AbstractOptions { return true; } + @Override + public void dispose() { + super.dispose(); + listeners.clear(); + } + private class NotifyListenersRunnable implements Runnable { private String optionName; private Object oldValue; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FileActionManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FileActionManager.java index 85563bf3ab..f21c285d8e 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FileActionManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/FileActionManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,21 +17,20 @@ package ghidra.framework.main; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.List; -import javax.swing.Icon; import javax.swing.KeyStroke; import docking.ActionContext; import docking.action.*; +import docking.action.builder.ActionBuilder; import docking.tool.ToolConstants; import docking.widgets.OptionDialog; import docking.widgets.filechooser.GhidraFileChooser; -import docking.wizard.WizardManager; -import generic.theme.GIcon; -import ghidra.framework.client.ClientUtil; +import docking.wizard.WizardDialog; import ghidra.framework.client.RepositoryAdapter; +import ghidra.framework.main.wizard.project.ProjectWizardModel; import ghidra.framework.model.*; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.PluginTool; @@ -46,23 +45,16 @@ import ghidra.util.task.TaskLauncher; */ class FileActionManager { - private final static int NEW_ACCELERATOR = KeyEvent.VK_N; - private final static int OPEN_ACCELERATOR = KeyEvent.VK_O; private final static int CLOSE_ACCELERATOR = KeyEvent.VK_W; private final static int SAVE_ACCELERATOR = KeyEvent.VK_S; - private final static Icon NEW_PROJECT_ICON = new GIcon("icon.menu.file.new.project"); - private final static String LAST_SELECTED_PROJECT_DIRECTORY = "LastSelectedProjectDirectory"; private static final String DISPLAY_DATA = "DISPLAY_DATA"; private FrontEndTool tool; private FrontEndPlugin plugin; - private DockingAction newAction; - private DockingAction openAction; private DockingAction closeProjectAction; - private DockingAction deleteAction; private DockingAction saveAction; private List reopenList; @@ -80,32 +72,19 @@ class FileActionManager { * creates all the menu items for the File menu */ private void createActions() { - // create the menu items and their listeners - newAction = new DockingAction("New Project", plugin.getName()) { - @Override - public void actionPerformed(ActionContext context) { - newProject(); - } - }; - newAction.setEnabled(true); - newAction.setKeyBindingData( - new KeyBindingData(KeyStroke.getKeyStroke(NEW_ACCELERATOR, ActionEvent.CTRL_MASK))); - newAction.setMenuBarData( - new MenuData(new String[] { ToolConstants.MENU_FILE, "New Project..." }, "AProject")); - tool.addAction(newAction); + new ActionBuilder("New Project", plugin.getName()) + .menuPath(ToolConstants.MENU_FILE, "New Project...") + .menuGroup("AProject") + .keyBinding("ctrl N") + .onAction(c -> newProject()) + .buildAndInstall(tool); - openAction = new DockingAction("Open Project", plugin.getName()) { - @Override - public void actionPerformed(ActionContext context) { - openProject(); - } - }; - openAction.setEnabled(true); - openAction.setKeyBindingData( - new KeyBindingData(KeyStroke.getKeyStroke(OPEN_ACCELERATOR, ActionEvent.CTRL_MASK))); - openAction.setMenuBarData( - new MenuData(new String[] { ToolConstants.MENU_FILE, "Open Project..." }, "AProject")); - tool.addAction(openAction); + new ActionBuilder("Open Project", plugin.getName()) + .menuPath(ToolConstants.MENU_FILE, "Open Project...") + .menuGroup("AProject") + .keyBinding("ctrl O") + .onAction(c -> openProject()) + .buildAndInstall(tool); saveAction = new DockingAction("Save Project", plugin.getName()) { @Override @@ -134,16 +113,12 @@ class FileActionManager { new MenuData(new String[] { ToolConstants.MENU_FILE, "Close Project" }, "BProject")); tool.addAction(closeProjectAction); - deleteAction = new DockingAction("Delete Project", plugin.getName()) { - @Override - public void actionPerformed(ActionContext context) { - deleteProject(); - } - }; - deleteAction.setEnabled(true); - deleteAction.setMenuBarData(new MenuData( - new String[] { ToolConstants.MENU_FILE, "Delete Project..." }, "CProject")); - tool.addAction(deleteAction); + new ActionBuilder("Delete Project", plugin.getName()) + .menuPath(ToolConstants.MENU_FILE, "Delete Project...") + .menuGroup("CProject") + .onAction(c -> deleteProject()) + .buildAndInstall(tool); + } /** @@ -173,52 +148,25 @@ class FileActionManager { * Create a new project using a wizard to get the project information. */ void newProject() { - NewProjectPanelManager panelManager = new NewProjectPanelManager(tool); - WizardManager wm = new WizardManager("New Project", true, panelManager, NEW_PROJECT_ICON); - wm.showWizard(tool.getToolFrame()); - ProjectLocator newProjectLocator = panelManager.getNewProjectLocation(); - RepositoryAdapter newRepo = panelManager.getProjectRepository(); + ProjectWizardModel model = new ProjectWizardModel(tool); + WizardDialog dialog = new WizardDialog(model); + dialog.show(tool.getToolFrame()); - if (newProjectLocator == null) { + if (model.wasCancelled()) { + return; + } + + if (!closeProject(false)) { // false --> not exiting return; // user canceled } - Project newProject = null; - try { - // if all is well and we already have an active project, close it - Project activeProject = plugin.getActiveProject(); - if (activeProject != null) { - if (!closeProject(false)) { // false --> not exiting - return; // user canceled - } - } + ProjectLocator projectLocator = model.getProjectLocator(); + RepositoryAdapter repository = model.getRepository(); - if (newRepo != null) { - try { - if (newRepo.getServer().isConnected()) { - newRepo.connect(); - } - } - catch (IOException e) { - ClientUtil.handleException(newRepo, e, "Repository Connection", - tool.getToolFrame()); - } - } - - newProject = tool.getProjectManager().createProject(newProjectLocator, newRepo, true); - } - catch (Exception e) { - String msg = e.getMessage(); - if (msg == null) { - msg = e.toString(); - } - Msg.showError(this, tool.getToolFrame(), "Create Project Failed", - "Failed to create new project '" + newProjectLocator.getName() + "': " + msg, e); - } - finally { - if (newProject == null && newRepo != null) { - newRepo.disconnect(); - } + Project newProject = createProject(projectLocator, repository); + if (newProject == null && repository != null) { + repository.disconnect(); + return; } // make the new project the active one @@ -232,6 +180,17 @@ class FileActionManager { } } + private Project createProject(ProjectLocator locator, RepositoryAdapter repository) { + try { + return tool.getProjectManager().createProject(locator, repository, true); + } + catch (Exception e) { + Msg.showError(this, tool.getToolFrame(), "Create Project Failed", + "Failed to create new project '" + locator.getName() + "': " + e.getMessage(), e); + } + return null; + } + private void openProject() { ProjectLocator currentProjectLocator = null; Project activeProject = plugin.getActiveProject(); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/NewProjectPanelManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/NewProjectPanelManager.java deleted file mode 100644 index 6b9f2a6184..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/NewProjectPanelManager.java +++ /dev/null @@ -1,409 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.framework.main; - -import java.awt.Dimension; -import java.io.IOException; -import java.rmi.RemoteException; -import java.util.ArrayList; - -import javax.swing.BorderFactory; -import javax.swing.border.Border; - -import docking.wizard.*; -import ghidra.framework.client.*; -import ghidra.framework.model.*; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.preferences.Preferences; -import ghidra.framework.remote.User; -import ghidra.util.Msg; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.UserAccessException; - -/** - * Manage the panels for the "New Project" wizard. The wizard handles - * creating a local project and a "shared" project. - * If the project is shared, the panel order is - * (1) Server Info - * (2) Repository panel - * (3) Project access panel (if user has admin privileges AND user is - * creating a new repository) - * (4) Specify Project Location panel. - * If the project is not shared, the only other panel to show is the - * Specify Project Location panel. - * - */ -class NewProjectPanelManager implements PanelManager { - - private WizardManager wizardMgr; - private String[] knownUsers; - private ProjectTypePanel projectTypePanel; - private SelectProjectPanel selectProjectPanel; - private ServerInfoPanel serverPanel; - private RepositoryPanel repositoryPanel; - private ProjectAccessPanel projectAccessPanel; - private WizardPanel currentWizardPanel; - private boolean includeAnonymousAccessControl = false; - private ProjectManager projectMgr; - private RepositoryServerAdapter server; - private RepositoryAdapter repository; - private ServerInfo serverInfo; - private ProjectLocator newProjectLocator; - private String statusMessage; - private PluginTool tool; - - final static Border EMPTY_BORDER = BorderFactory.createEmptyBorder(80, 120, 0, 120); - - NewProjectPanelManager(FrontEndTool tool) { - projectTypePanel = new ProjectTypePanel(this); - selectProjectPanel = new SelectProjectPanel(this); - serverPanel = new ServerInfoPanel(this); - projectMgr = tool.getProjectManager(); - this.tool = tool; - } - - @Override - public boolean canFinish() { - - if (!projectTypePanel.isValidInformation()) { - return false; - } - if (!projectTypePanel.isSharedProject() && selectProjectPanel.isValidInformation()) { - return true; - } - if (repositoryPanel == null) { - return false; - } - if (repositoryPanel.isValidInformation() && - (projectAccessPanel == null || - projectAccessPanel != null && projectAccessPanel.isValidInformation()) && - selectProjectPanel.isValidInformation()) { - return true; - } - return false; - } - - @Override - public boolean hasNextPanel() { - if (currentWizardPanel == selectProjectPanel) { - if (selectProjectPanel.isValidInformation() && projectTypePanel.isValidInformation() && - !projectTypePanel.isSharedProject()) { - return false; - } - } - return currentWizardPanel != selectProjectPanel; - } - - @Override - public boolean hasPreviousPanel() { - return currentWizardPanel != projectTypePanel; - } - - @Override - public WizardPanel getInitialPanel() { - currentWizardPanel = projectTypePanel; - return currentWizardPanel; - } - - @Override - public WizardPanel getNextPanel() { - - if (currentWizardPanel == null) { - currentWizardPanel = projectTypePanel; - } - else if (currentWizardPanel == projectTypePanel) { - if (projectTypePanel.isSharedProject()) { - currentWizardPanel = serverPanel; - serverPanel.setServerInfo(projectMgr.getMostRecentServerInfo()); - } - else { - server = null; - serverInfo = null; - currentWizardPanel = selectProjectPanel; - } - } - else if (currentWizardPanel == serverPanel) { - String serverName = serverPanel.getServerName(); - int portNumber = serverPanel.getPortNumber(); - if (!isServerInfoValid(serverName, portNumber)) { - return serverPanel; - } - - try { - knownUsers = server.getAllUsers(); - String[] repositoryNames = server.getRepositoryNames(); - includeAnonymousAccessControl = server.anonymousAccessAllowed(); - if (repositoryPanel == null) { - repositoryPanel = - new RepositoryPanel(this, serverName, repositoryNames, server.isReadOnly()); - } - currentWizardPanel = repositoryPanel; - } - catch (RemoteException e) { - statusMessage = "Error accessing remote server on " + serverName; - } - catch (NotConnectedException e) { - statusMessage = e.getMessage(); - if (statusMessage == null) { - statusMessage = "Not Connected to server " + serverName; - } - } - catch (IOException e) { - statusMessage = "IOException: could not access remote server on " + serverName; - } - } - else if (currentWizardPanel == repositoryPanel) { - if (repository != null) { - repository.disconnect(); - repository = null; - } - String repositoryName = repositoryPanel.getRepositoryName(); - selectProjectPanel.setProjectName(repositoryName); - if (!repositoryPanel.createRepository()) { - currentWizardPanel = selectProjectPanel; - selectProjectPanel.setProjectName(repositoryName); - repository = server.getRepository(repositoryName); - statusMessage = selectProjectPanel.getStatusMessage(); - return currentWizardPanel; - } - - checkNewRepositoryAccessPanel(); - currentWizardPanel = projectAccessPanel; - } - else if (currentWizardPanel == projectAccessPanel) { - currentWizardPanel = selectProjectPanel; - statusMessage = selectProjectPanel.getStatusMessage(); - } - else { - currentWizardPanel = null; - } - return currentWizardPanel; - } - - /** - * Build repository access panel for new repository only. - * @throws IOException - */ - private void checkNewRepositoryAccessPanel() { - - String repositoryName = repositoryPanel.getRepositoryName(); - if (projectAccessPanel != null && - projectAccessPanel.getRepositoryName().equals(repositoryName)) { - return; - } - - ArrayList userList = new ArrayList<>(); - userList.add(new User(server.getUser(), User.ADMIN)); - - try { - projectAccessPanel = new ProjectAccessPanel(knownUsers, server.getUser(), userList, - repositoryName, server.anonymousAccessAllowed(), false, tool); - } - catch (IOException e) { - Msg.error(this, "Error creating project access panel"); - } - } - - @Override - public WizardPanel getPreviousPanel() { - if (currentWizardPanel == selectProjectPanel) { - if (projectTypePanel.isSharedProject()) { - if (repositoryPanel.createRepository()) { - currentWizardPanel = projectAccessPanel; - } - else { - currentWizardPanel = repositoryPanel; - } - } - else { - currentWizardPanel = projectTypePanel; - } - } - else if (currentWizardPanel == projectAccessPanel) { - currentWizardPanel = repositoryPanel; - } - else if (currentWizardPanel == repositoryPanel) { - currentWizardPanel = serverPanel; - } - else if (currentWizardPanel == serverPanel) { - currentWizardPanel = projectTypePanel; - } - else { - currentWizardPanel = null; - } - return currentWizardPanel; - } - - @Override - public String getStatusMessage() { - String msg = statusMessage; - statusMessage = null; - return msg; - } - - @Override - public void finish() { - - ProjectLocator projectLocator = selectProjectPanel.getProjectLocator(); - if (server != null) { - boolean createNewRepository = repositoryPanel.createRepository(); - if (!createNewRepository) { - if (repository == null) { - repository = server.getRepository(repositoryPanel.getRepositoryName()); - } - } - else { - try { - repository = server.createRepository(repositoryPanel.getRepositoryName()); - repository.setUserList(projectAccessPanel.getProjectUsers(), - projectAccessPanel.allowAnonymousAccess()); - } - catch (DuplicateNameException e) { - statusMessage = "Repository " + repositoryPanel.getRepositoryName() + " exists"; - } - catch (UserAccessException exc) { - statusMessage = "Could not update the user list: " + exc.getMessage(); - return; - } - catch (NotConnectedException e) { - statusMessage = e.getMessage(); - if (statusMessage == null) { - statusMessage = "Not connected to server " + serverInfo.getServerName(); - } - return; - } - catch (IOException exc) { - String msg = exc.getMessage(); - if (msg == null) { - msg = exc.toString(); - } - statusMessage = "Error occurred while updating the user list: " + msg; - return; - } - } - - } - Preferences.setProperty(Preferences.LAST_NEW_PROJECT_DIRECTORY, - projectLocator.getLocation()); - Preferences.store(); - - newProjectLocator = projectLocator; - wizardMgr.close(); - } - - @Override - public void cancel() { - currentWizardPanel = null; - repositoryPanel = null; - projectAccessPanel = null; - server = null; - if (repository != null) { - repository.disconnect(); - repository = null; - } - } - - @Override - public void initialize() { - currentWizardPanel = null; - selectProjectPanel.initialize(); - serverPanel.initialize(); -// serverPanel.setServerInfo(serverInfo); - if (repositoryPanel != null) { - repositoryPanel.initialize(); - } - if (projectAccessPanel != null) { - projectAccessPanel.initialize(); - } - } - - @Override - public Dimension getPanelSize() { - return getMyPanelSize(); - } - - @Override - public void setWizardManager(WizardManager wm) { - wizardMgr = wm; - } - - @Override - public WizardManager getWizardManager() { - return wizardMgr; - } - - /** - * Get the project that was created. - * @return null if no project was created - */ - ProjectLocator getNewProjectLocation() { - return newProjectLocator; - } - - /** - * Get the repository adapter associated with the new project. - * After displaying this panel, this method should be invoked to obtain the - * repository which will be opended for shared projects. If the repository is - * not used to create a new project, its disconnect method should be invoked. - * @return null if project is not shared - */ - RepositoryAdapter getProjectRepository() { - return repository; - } - - String getProjectRepositoryName() { - return repositoryPanel.getRepositoryName(); - } - - boolean isSharedProject() { - return projectTypePanel.isSharedProject(); - } - - /** - * Return true if a connection could be established using the given - * server name and port number. - */ - private boolean isServerInfoValid(String serverName, int portNumber) { - if (server != null && serverInfo != null && serverInfo.getServerName().equals(serverName) && - serverInfo.getPortNumber() == portNumber && server.isConnected()) { - return true; - } - - repositoryPanel = null; - - server = projectMgr.getRepositoryServerAdapter(serverName, portNumber, true); - if (server.isConnected()) { - serverInfo = projectMgr.getMostRecentServerInfo(); - return true; - } - - server = null; - serverInfo = null; - statusMessage = "Could not connect to server " + serverName + ", port " + portNumber; - return false; - } - - private Dimension getMyPanelSize() { - - ProjectAccessPanel panel1 = new ProjectAccessPanel(new String[] { "nobody" }, "user", - new ArrayList(), "MyRepository", true, false, tool); - RepositoryPanel panel2 = new RepositoryPanel(this, "ServerOne", - new String[] { "MyRepository", "NewStuff", "Repository_A", "Repository_B" }, false); - Dimension d1 = panel1.getPreferredSize(); - Dimension d2 = panel2.getPreferredSize(); - return new Dimension(Math.max(d1.width, d2.width), Math.max(d1.height, d2.height)); - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessDialog.java index 48f804bffc..8d21c35033 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,10 +31,10 @@ import ghidra.util.exception.UserAccessException; * */ class ProjectAccessDialog extends DialogComponentProvider { - + private RepositoryAdapter repository; private ProjectAccessPanel projectAccessPanel; - + /** * Creates a new dialog. * @@ -46,13 +46,14 @@ class ProjectAccessDialog extends DialogComponentProvider { * @throws IOException * @throws NotConnectedException */ - ProjectAccessDialog(Plugin plugin, RepositoryAdapter repHandle, String[] knownUsers, boolean allowEditing) + ProjectAccessDialog(Plugin plugin, RepositoryAdapter repHandle, String[] knownUsers, + boolean allowEditing) throws UserAccessException, IOException, NotConnectedException { super("Project Access List for " + repHandle.getName(), true); - + this.repository = repHandle; - + setHelpLocation(new HelpLocation(plugin.getName(), "Edit_Project_Access_List")); if (allowEditing) { @@ -61,9 +62,9 @@ class ProjectAccessDialog extends DialogComponentProvider { else { projectAccessPanel = new ViewProjectAccessPanel(repository, plugin.getTool()); } - + addWorkPanel(projectAccessPanel); - + if (allowEditing) { addOKButton(); setOkEnabled(true); @@ -74,7 +75,7 @@ class ProjectAccessDialog extends DialogComponentProvider { setCancelButtonText("Close"); } } - + @Override protected void cancelCallback() { close(); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessPanel.java index d57e4e625a..7bc1c18d09 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectAccessPanel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,10 +27,8 @@ import javax.swing.border.Border; import docking.widgets.checkbox.GCheckBox; import docking.widgets.list.GListCellRenderer; import docking.widgets.table.GTable; -import docking.wizard.AbstractWizardJPanel; import generic.theme.GColor; import generic.theme.GIcon; -import ghidra.app.util.GenericHelpTopics; import ghidra.framework.client.RepositoryAdapter; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.remote.User; @@ -53,7 +51,7 @@ import util.CollectionUtils; * be read-only. * */ -public class ProjectAccessPanel extends AbstractWizardJPanel { +public class ProjectAccessPanel extends JPanel { protected KnownUsersPanel knownUsersPanel; protected UserAccessPanel userAccessPanel; @@ -116,32 +114,6 @@ public class ProjectAccessPanel extends AbstractWizardJPanel { createMainPanel(knownUsers, anonymousServerAccessAllowed); } - @Override - public boolean isValidInformation() { - return true; - } - - @Override - public String getTitle() { - return "Specify Users for Repository " + repositoryName; - } - - @Override - public void initialize() { - userAccessPanel.resetUserList(); - if (anonymousAccessCB != null) { - anonymousAccessCB.setSelected(origAnonymousAccessEnabled); - } - } - - @Override - public HelpLocation getHelpLocation() { - if (helpLoc != null) { - return helpLoc; - } - return new HelpLocation(GenericHelpTopics.FRONT_END, "UserAccessList"); - } - /** * Sets the help location. * @@ -156,7 +128,7 @@ public class ProjectAccessPanel extends AbstractWizardJPanel { * * @return the list of users */ - User[] getProjectUsers() { + public User[] getProjectUsers() { return userAccessPanel.getProjectUsers(); } @@ -165,7 +137,7 @@ public class ProjectAccessPanel extends AbstractWizardJPanel { * * @return true if allowed */ - boolean allowAnonymousAccess() { + public boolean allowAnonymousAccess() { return anonymousAccessCB != null && anonymousAccessCB.isSelected(); } @@ -354,7 +326,7 @@ public class ProjectAccessPanel extends AbstractWizardJPanel { * * @param user the current user */ - UserAccessPanel(String user) { + public UserAccessPanel(String user) { setLayout(new BorderLayout()); tableModel = new UserAccessTableModel(user, origProjectUserList, tool); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectInfoDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectInfoDialog.java index 2f1bb07961..1956824b20 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectInfoDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectInfoDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,12 +30,12 @@ import docking.widgets.OptionDialog; import docking.widgets.button.GButton; import docking.widgets.label.GDLabel; import docking.widgets.label.GLabel; -import docking.wizard.WizardManager; -import generic.theme.GIcon; +import docking.wizard.WizardDialog; import ghidra.app.util.GenericHelpTopics; import ghidra.framework.client.*; import ghidra.framework.data.ConvertFileSystem; import ghidra.framework.data.TransientDataManager; +import ghidra.framework.main.wizard.project.ProjectChooseRepositoryWizardModel; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginToolAccessUtils; @@ -56,7 +56,6 @@ import help.HelpService; */ public class ProjectInfoDialog extends DialogComponentProvider { - private final static Icon CONVERT_ICON = new GIcon("icon.project.info.convert"); public final static String CHANGE = "Change Shared Project Info..."; final static String CONVERT = "Convert to Shared..."; @@ -354,15 +353,16 @@ public class ProjectInfoDialog extends DialogComponentProvider { if (!checkToolsClose()) { return; } + RepositoryAdapter currentRepository = project.getRepository(); + ServerInfo serverInfo = currentRepository.getServerInfo(); + ProjectChooseRepositoryWizardModel model = + new ProjectChooseRepositoryWizardModel(plugin.getTool(), + "Change Shared Project Information", serverInfo); + WizardDialog dialog = new WizardDialog(model); + dialog.show(getComponent()); + RepositoryAdapter rep = model.getRepository(); - SetupProjectPanelManager panelManager = - new SetupProjectPanelManager(plugin.getTool(), project.getRepository().getServerInfo()); - WizardManager wm = new WizardManager("Change Shared Project Information", true, - panelManager, CONVERT_ICON); - wm.showWizard(getComponent()); - RepositoryAdapter rep = panelManager.getProjectRepository(); if (rep != null) { - RepositoryAdapter currentRepository = project.getRepository(); if (currentRepository.getServerInfo().equals(rep.getServerInfo()) && currentRepository.getName().equals(rep.getName())) { Msg.showInfo(getClass(), getComponent(), "No Changes Made", @@ -458,12 +458,12 @@ public class ProjectInfoDialog extends DialogComponentProvider { if (!checkToolsClose()) { return; } + ProjectChooseRepositoryWizardModel model = + new ProjectChooseRepositoryWizardModel(plugin.getTool(), "Convert Project"); + WizardDialog dialog = new WizardDialog(model); + dialog.show(getComponent()); - SetupProjectPanelManager panelManager = - new SetupProjectPanelManager(plugin.getTool(), null); - WizardManager wm = new WizardManager("Convert Project", true, panelManager, CONVERT_ICON); - wm.showWizard(getComponent()); - RepositoryAdapter rep = panelManager.getProjectRepository(); + RepositoryAdapter rep = model.getRepository(); if (rep != null) { StringBuffer confirmMsg = new StringBuffer(); confirmMsg.append("All version history on your files will be\n" + diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectTypePanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectTypePanel.java deleted file mode 100644 index 3d4001c1c5..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ProjectTypePanel.java +++ /dev/null @@ -1,120 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.framework.main; - -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; - -import javax.swing.*; - -import docking.widgets.button.GRadioButton; -import docking.wizard.AbstractWizardJPanel; -import docking.wizard.PanelManager; -import ghidra.app.util.GenericHelpTopics; -import ghidra.util.HelpLocation; -import ghidra.util.layout.VerticalLayout; - -/** - * First panel shown in the New Project Wizard to get user input for what - * type of project to create: Shared, or not shared. - * - * - */ -class ProjectTypePanel extends AbstractWizardJPanel { - - private JRadioButton sharedRB; - private JRadioButton nonSharedRB; - private ButtonGroup buttonGroup; - private PanelManager panelManager; - - ProjectTypePanel(PanelManager panelManager) { - super(); - this.panelManager = panelManager; - buildPanel(); - setBorder(NewProjectPanelManager.EMPTY_BORDER); - } - - private void buildPanel() { - JPanel innerPanel = new JPanel(new VerticalLayout(10)); - innerPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - ItemListener listener = new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - panelManager.getWizardManager().validityChanged(); - } - }; - - nonSharedRB = new GRadioButton("Non-Shared Project", true); - nonSharedRB.addItemListener(listener); - nonSharedRB.setToolTipText("Create a project that is not shared with others"); - - sharedRB = new GRadioButton("Shared Project"); - sharedRB.addItemListener(listener); - sharedRB.setToolTipText("Create a project that can be shared with others"); - - buttonGroup = new ButtonGroup(); - buttonGroup.add(nonSharedRB); - buttonGroup.add(sharedRB); - - innerPanel.add(nonSharedRB); - innerPanel.add(sharedRB); - JPanel outerPanel = new JPanel(); - outerPanel.setBorder(BorderFactory.createEmptyBorder()); - outerPanel.add(innerPanel); - add(outerPanel); - } - - /* (non-Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getTitle() - */ - @Override - public String getTitle() { - return "Select Project Type"; - } - - /* (non-Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#initialize() - */ - @Override - public void initialize() { - buttonGroup.remove(sharedRB); - buttonGroup.remove(nonSharedRB); - sharedRB.setSelected(false); - sharedRB.setSelected(false); - buttonGroup.add(nonSharedRB); - buttonGroup.add(sharedRB); - } - - /** - * Return true if the user has entered a valid project file - */ - @Override - public boolean isValidInformation() { - return sharedRB.isSelected() || nonSharedRB.isSelected(); - } - - /* (non-Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getHelpLocation() - */ - @Override - public HelpLocation getHelpLocation() { - return new HelpLocation(GenericHelpTopics.FRONT_END, "SelectProjectType"); - } - - boolean isSharedProject() { - return sharedRB.isSelected(); - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SelectProjectPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SelectProjectPanel.java deleted file mode 100644 index ddc11c494a..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SelectProjectPanel.java +++ /dev/null @@ -1,355 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.framework.main; - -import java.awt.*; -import java.io.File; - -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.text.Document; - -import docking.widgets.button.BrowseButton; -import docking.widgets.filechooser.GhidraFileChooser; -import docking.widgets.filechooser.GhidraFileChooserMode; -import docking.widgets.label.GDLabel; -import docking.wizard.AbstractWizardJPanel; -import docking.wizard.WizardManager; -import ghidra.app.util.GenericHelpTopics; -import ghidra.framework.GenericRunInfo; -import ghidra.framework.model.ProjectLocator; -import ghidra.framework.preferences.Preferences; -import ghidra.util.HelpLocation; -import ghidra.util.NamingUtilities; -import ghidra.util.filechooser.GhidraFileChooserModel; -import ghidra.util.filechooser.GhidraFileFilter; -import ghidra.util.layout.VerticalLayout; - -/** - * Panel that allows the project directory and name to be specified for a - * new project. A checkbox indicates whether the project should be created - * as a shared project. - * - */ -class SelectProjectPanel extends AbstractWizardJPanel { - - //remove the "." from the extension - private static String PROJECT_EXTENSION = ProjectLocator.getProjectExtension().substring(1); - - private JTextField projectNameField; - private JTextField directoryField; - private JButton browseButton; - private ProjectLocator projectLocator; - private NewProjectPanelManager panelManager; - private DocumentListener docListener; - - /** - * Construct a new panel. - * @param panelManager manager for the "new project" set of panels - */ - public SelectProjectPanel(NewProjectPanelManager panelManager) { - super(new BorderLayout()); - this.panelManager = panelManager; - buildMainPanel(); - setBorder(BorderFactory.createEmptyBorder(80, 80, 0, 80)); - } - - /* (non Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getTitle() - */ - @Override - public String getTitle() { - if (panelManager.isSharedProject()) { - return "Select Local Project Location for Repository " + - panelManager.getProjectRepositoryName(); - } - return "Select Project Location"; - } - - /* (non Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#initialize() - */ - @Override - public void initialize() { - projectLocator = null; - Document doc = projectNameField.getDocument(); - doc.removeDocumentListener(docListener); - projectNameField.setText(""); - doc.addDocumentListener(docListener); - - } - - /** - * Return true if the user has entered a valid project file - */ - @Override - public boolean isValidInformation() { - return projectLocator != null; - } - - /* (non-Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getHelpLocation() - */ - @Override - public HelpLocation getHelpLocation() { - if (panelManager.isSharedProject()) { - return new HelpLocation(GenericHelpTopics.FRONT_END, "SelectProjectLocation"); - } - return new HelpLocation(GenericHelpTopics.FRONT_END, "CreateNonSharedProject"); - } - - ProjectLocator getProjectLocator() { - return projectLocator; - } - - void setProjectName(String projectName) { - projectNameField.setText(projectName); - } - - String getStatusMessage() { - if (projectLocator == null) { - return checkProjectFile(false); - } - return ""; - } - - private void buildMainPanel() { - - JPanel outerPanel = new JPanel(); - GridBagLayout gbl = new GridBagLayout(); - outerPanel.setLayout(gbl); - - JLabel dirLabel = new GDLabel("Project Directory:", SwingConstants.RIGHT); - directoryField = new JTextField(25); - directoryField.setName("Project Directory"); - - File projectDirectory = null; - String projectDirPath = Preferences.getProperty(Preferences.LAST_NEW_PROJECT_DIRECTORY); - if (projectDirPath != null) { - // if it exists, use last directory where project was created - projectDirectory = new File(projectDirPath); - if (!projectDirectory.isDirectory()) { - projectDirectory = null; - } - } - if (projectDirectory == null) { - // otherwise, use last project directory or default - projectDirectory = new File(GenericRunInfo.getProjectsDirPath()); - } - projectDirPath = projectDirectory.getAbsolutePath(); - directoryField.setText(projectDirPath); - directoryField.setCaretPosition(projectDirPath.length() - 1); - JLabel projectNameLabel = new GDLabel("Project Name:", SwingConstants.RIGHT); - projectNameField = new JTextField(25); - projectNameField.setName("Project Name"); - projectNameField.addActionListener(e -> setProjectFile()); - - docListener = new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - setProjectFile(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - setProjectFile(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - setProjectFile(); - } - }; - projectNameField.getDocument().addDocumentListener(docListener); - directoryField.getDocument().addDocumentListener(docListener); - - browseButton = new BrowseButton(); - browseButton.addActionListener(e -> displayFileChooser()); - -// sharedProjectCB = new GCheckBox("Project can be Shared with Others"); -// sharedProjectCB.addItemListener(new ItemListener() { -// public void itemStateChanged(ItemEvent e) { -// panelManager.getWizardManager().validityChanged(); -// checkProjectFile(false); // cause message to be displayed -// // if project name is invalid -// } -// }); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.EAST; - gbl.setConstraints(dirLabel, gbc); - outerPanel.add(dirLabel); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.insets.left = 5; - gbc.insets.bottom = 5; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbl.setConstraints(directoryField, gbc); - outerPanel.add(directoryField); - - gbc = new GridBagConstraints(); - gbc.gridx = 2; - gbc.insets.left = 5; - gbc.insets.bottom = 5; - gbc.anchor = GridBagConstraints.EAST; - gbl.setConstraints(browseButton, gbc); - outerPanel.add(browseButton); - - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 1; - gbc.insets.left = 5; - gbc.insets.bottom = 5; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbl.setConstraints(projectNameLabel, gbc); - outerPanel.add(projectNameLabel); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 1; - gbc.insets.left = 5; - gbc.insets.bottom = 5; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbl.setConstraints(projectNameField, gbc); - outerPanel.add(projectNameField); - - JPanel p = new JPanel(new VerticalLayout(5)); - p.add(outerPanel); - add(p, BorderLayout.CENTER); - } - - private void setProjectFile() { - checkProjectFile(true); - } - - /** - * Check the validity of the project file name. - */ - private String checkProjectFile(boolean showMessage) { - WizardManager wm = panelManager.getWizardManager(); - if (showMessage) { - wm.setStatusMessage(""); - } - projectLocator = null; - ProjectLocator locator = null; - String msg = null; - String dir = directoryField.getText().trim(); - if (dir.length() == 0) { - msg = "Please specify project directory"; - } - else if (!new File(dir).isDirectory()) { - msg = "Project directory does not exist."; - } - else { - String projectName = projectNameField.getText().trim(); - if (projectName.endsWith(PROJECT_EXTENSION)) { - projectName = - projectName.substring(0, projectName.length() - PROJECT_EXTENSION.length()); - } - if (!NamingUtilities.isValidProjectName(projectName)) { - msg = "Please specify valid project name"; - } - else { - try { - locator = new ProjectLocator(dir, projectName); - } - catch (IllegalArgumentException e) { - msg = e.getMessage(); - } - } - } - if (locator != null) { - File parentDir = new File(dir); - if (!parentDir.isDirectory()) { - msg = "Please specify a Project Directory"; - } - else if (locator.getMarkerFile().exists() || locator.getProjectDir().exists()) { - msg = getProjectName("A project named " + locator.getName() + - " already exists in " + parentDir.getAbsolutePath()); - } - else { - this.projectLocator = locator; - } - } - wm.validityChanged(); - if (showMessage) { - wm.setStatusMessage(msg); - } - return msg; - } - - private void displayFileChooser() { - GhidraFileChooser fileChooser = createFileChooser(); - fileChooser.setTitle("Select a Ghidra Project Directory"); - fileChooser.setApproveButtonText("Select Project Directory"); - fileChooser.setApproveButtonToolTipText("Select a Ghidra Project Directory"); - - File file = fileChooser.getSelectedFile(); - - if (file != null) { - directoryField.setText(file.getAbsolutePath()); - - WizardManager wm = panelManager.getWizardManager(); - wm.setStatusMessage(""); - wm.validityChanged(); - - checkProjectFile(true); - } - - fileChooser.dispose(); - } - - private String getProjectName(String name) { - if (name.endsWith(PROJECT_EXTENSION)) { - name = name.substring(0, name.indexOf(PROJECT_EXTENSION) - 1); - } - return name; - } - - private GhidraFileChooser createFileChooser() { - WizardManager wm = panelManager.getWizardManager(); - - GhidraFileChooser fileChooser = new GhidraFileChooser(wm.getComponent()); - File projectDirectory = new File(GenericRunInfo.getProjectsDirPath()); - String lastDirSelected = - Preferences.getProperty(Preferences.LAST_NEW_PROJECT_DIRECTORY, null, true); - if (lastDirSelected != null) { - projectDirectory = new File(lastDirSelected); - } - fileChooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY); - fileChooser.setFileFilter(new GhidraFileFilter() { - @Override - public String getDescription() { - return "All Directories"; - } - - @Override - public boolean accept(File f, GhidraFileChooserModel model) { - return model.isDirectory(f) && - !f.getName().endsWith(ProjectLocator.getProjectDirExtension()); - } - }); - fileChooser.setCurrentDirectory(projectDirectory);//start the browsing in the user's preferred project directory - return fileChooser; - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ServerInfoComponent.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ServerInfoComponent.java index 013563b046..ab18c119f9 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ServerInfoComponent.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ServerInfoComponent.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -42,6 +42,7 @@ public class ServerInfoComponent extends JPanel { private DocumentListener nameDocListener; private StatusListener statusListener; private ChangeListener listener; + private String statusMessage; public ServerInfoComponent() { super(new BorderLayout(10, 10)); @@ -161,6 +162,7 @@ public class ServerInfoComponent extends JPanel { } private void setStatus(String text) { + statusMessage = text; if (statusListener == null) { return; } @@ -172,6 +174,10 @@ public class ServerInfoComponent extends JPanel { } } + public String getStatusMessage() { + return statusMessage; + } + private void notifyChange() { if (listener != null) { listener.stateChanged(new ChangeEvent(this)); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ServerInfoPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ServerInfoPanel.java deleted file mode 100644 index c95093ad7e..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ServerInfoPanel.java +++ /dev/null @@ -1,116 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.framework.main; - -import ghidra.app.util.GenericHelpTopics; -import ghidra.framework.model.ServerInfo; -import ghidra.util.HelpLocation; - -import java.awt.BorderLayout; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import docking.wizard.*; - -/** - * Wizard panel that allows the user to specify the host name and port - * number for the remote repository server. - */ -public class ServerInfoPanel extends AbstractWizardJPanel { - - private ServerInfoComponent serverInfoComponent; - private PanelManager panelManager; - private HelpLocation helpLoc; - - public ServerInfoPanel(PanelManager panelManager) { - super(new BorderLayout(10, 10)); - this.panelManager = panelManager; - setBorder(NewProjectPanelManager.EMPTY_BORDER); - buildMainPanel(); - } - - /* (non Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getTitle() - */ - public String getTitle() { - return "Specify Server Information"; - } - - /* (non-Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getHelpLocation() - */ - @Override - public HelpLocation getHelpLocation() { - if (helpLoc != null) { - return helpLoc; - } - return new HelpLocation(GenericHelpTopics.FRONT_END, "ServerInfo"); - } - - /* (non Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#initialize() - */ - public void initialize() { - serverInfoComponent.setStatusListener(panelManager.getWizardManager()); - } - - /** - * Return whether the fields on this panel have valid information. - */ - public boolean isValidInformation() { - return serverInfoComponent.isValidInformation(); - } - - /** - * Get the server name. - */ - String getServerName() { - return serverInfoComponent.getServerName(); - } - - /** - * Get the port number. - */ - int getPortNumber() { - return serverInfoComponent.getPortNumber(); - } - - /** - * Set the field values using the given server info. - */ - public void setServerInfo(ServerInfo info) { - serverInfoComponent.setServerInfo(info); - } - - void setHelpLocation(HelpLocation helpLoc) { - this.helpLoc = helpLoc; - } - - private void buildMainPanel() { - serverInfoComponent = new ServerInfoComponent(); - serverInfoComponent.setChangeListener(new ChangeListener() { - public void stateChanged(ChangeEvent e) { - WizardManager wm = panelManager.getWizardManager(); - if (wm.getCurrentWizardPanel() != null) { - wm.validityChanged(); - } - } - }); - add(serverInfoComponent, BorderLayout.CENTER); - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SetupProjectPanelManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SetupProjectPanelManager.java deleted file mode 100644 index 564c7d313d..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/SetupProjectPanelManager.java +++ /dev/null @@ -1,354 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.framework.main; - -import java.awt.Dimension; -import java.io.IOException; -import java.rmi.RemoteException; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.BorderFactory; -import javax.swing.border.Border; - -import docking.wizard.*; -import ghidra.app.util.GenericHelpTopics; -import ghidra.framework.client.*; -import ghidra.framework.model.ProjectManager; -import ghidra.framework.model.ServerInfo; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.remote.User; -import ghidra.util.HelpLocation; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.UserAccessException; - -/** - * Manage the panels for the wizard that shows server info and repository panels. - * The panel order is - * (1) Server Info - * (2) Repository panel - * (3) Project access panel (if user is creating a new repository) - * This panel manager is used when the project is being converted to a shared project and - * when a shared project's information is to change. - */ -class SetupProjectPanelManager implements PanelManager { - - private WizardManager wizardMgr; - private String[] knownUsers; - private ServerInfoPanel serverPanel; - private RepositoryPanel repositoryPanel; - private ProjectAccessPanel projectAccessPanel; - private WizardPanel currentWizardPanel; - private boolean includeAnonymousAccessControl = false; - private ProjectManager projectMgr; - private RepositoryServerAdapter server; - private RepositoryAdapter repository; - private ServerInfo serverInfo; - private ServerInfo currentServerInfo; - private String statusMessage; - private PluginTool tool; - - final static Border EMPTY_BORDER = BorderFactory.createEmptyBorder(80, 120, 0, 120); - - SetupProjectPanelManager(PluginTool tool, ServerInfo serverInfo) { - serverPanel = new ServerInfoPanel(this); - serverPanel.setHelpLocation( - new HelpLocation(GenericHelpTopics.FRONT_END, "SetupServerInfo")); - projectMgr = tool.getProjectManager(); - currentServerInfo = serverInfo; - this.tool = tool; - } - - @Override - public boolean canFinish() { - - if (repositoryPanel == null) { - return false; - } - if (repositoryPanel.isValidInformation()) { - if (repositoryPanel.createRepository()) { - return projectAccessPanel == null || projectAccessPanel.isValidInformation(); - } - return true; - } - return false; - } - - @Override - public boolean hasNextPanel() { - if (currentWizardPanel == serverPanel) { - return true; - } - - if (currentWizardPanel == repositoryPanel && repositoryPanel.createRepository()) { - return true; - } - return false; - } - - @Override - public boolean hasPreviousPanel() { - return currentWizardPanel != serverPanel; - } - - @Override - public WizardPanel getInitialPanel() { - currentWizardPanel = serverPanel; - return currentWizardPanel; - } - - @Override - public WizardPanel getNextPanel() { - if (currentWizardPanel == null) { - currentWizardPanel = serverPanel; - if (currentServerInfo != null) { - serverPanel.setServerInfo(currentServerInfo); - } - else { - serverPanel.setServerInfo(projectMgr.getMostRecentServerInfo()); - } - } - else if (currentWizardPanel == serverPanel) { - String serverName = serverPanel.getServerName(); - int portNumber = serverPanel.getPortNumber(); - if (!isServerInfoValid(serverName, portNumber)) { - return serverPanel; - } - - try { - knownUsers = server.getAllUsers(); - String[] repositoryNames = server.getRepositoryNames(); - includeAnonymousAccessControl = server.anonymousAccessAllowed(); - if (repositoryPanel == null) { - repositoryPanel = - new RepositoryPanel(this, serverName, repositoryNames, server.isReadOnly()); - repositoryPanel.setHelpLocation( - new HelpLocation(GenericHelpTopics.FRONT_END, "ChangeRepository")); - } - currentWizardPanel = repositoryPanel; - } - catch (RemoteException e) { - statusMessage = "Error accessing remote server on " + serverName; - } - catch (NotConnectedException e) { - statusMessage = e.getMessage(); - if (statusMessage == null) { - statusMessage = "Not connected to server " + serverName + ": " + e; - } - } - catch (IOException e) { - statusMessage = "IOException: could not access remote server on " + serverName; - } - } - else if (currentWizardPanel == repositoryPanel) { - String repositoryName = repositoryPanel.getRepositoryName(); - if (!repositoryPanel.createRepository()) { - currentWizardPanel = null; - repository = server.getRepository(repositoryName); - return currentWizardPanel; - } - - checkNewRepositoryAccessPanel(); - currentWizardPanel = projectAccessPanel; - } - else if (currentWizardPanel == projectAccessPanel) { - currentWizardPanel = null; - } - return currentWizardPanel; - } - - @Override - public WizardPanel getPreviousPanel() { - if (currentWizardPanel == projectAccessPanel) { - currentWizardPanel = repositoryPanel; - } - else if (currentWizardPanel == repositoryPanel) { - currentWizardPanel = serverPanel; - } - else { - currentWizardPanel = null; - } - return currentWizardPanel; - } - - @Override - public String getStatusMessage() { - String msg = statusMessage; - statusMessage = null; - return msg; - } - - @Override - public void finish() { - - if (server != null) { - boolean createNewRepository = repositoryPanel.createRepository(); - if (!createNewRepository) { - if (repository == null) { - repository = server.getRepository(repositoryPanel.getRepositoryName()); - } - } - else { - try { - String repositoryName = repositoryPanel.getRepositoryName(); - boolean allowAnonymousAccess; - User[] accessList; - if (projectAccessPanel != null && - projectAccessPanel.getRepositoryName().equals(repositoryName)) { - accessList = projectAccessPanel.getProjectUsers(); - allowAnonymousAccess = projectAccessPanel.allowAnonymousAccess(); - } - else { - accessList = new User[] { new User(server.getUser(), User.ADMIN) }; - allowAnonymousAccess = false; - } - repository = server.createRepository(repositoryName); - repository.setUserList(accessList, allowAnonymousAccess); - } - catch (DuplicateNameException e) { - statusMessage = "Repository " + repositoryPanel.getRepositoryName() + " exists"; - } - catch (UserAccessException exc) { - statusMessage = "Could not update the user list: " + exc.getMessage(); - return; - } - catch (NotConnectedException e) { - statusMessage = e.getMessage(); - if (statusMessage == null) { - statusMessage = - "Not connected to server " + serverInfo.getServerName() + ": " + e; - } - return; - } - catch (IOException exc) { - String msg = exc.getMessage(); - if (msg == null) { - msg = exc.toString(); - } - statusMessage = "Error occurred while updating the user list: " + msg; - return; - } - } - } - wizardMgr.close(); - } - - @Override - public void cancel() { - currentWizardPanel = null; - repositoryPanel = null; - projectAccessPanel = null; - server = null; - if (repository != null) { - repository.disconnect(); - repository = null; - } - } - - @Override - public void initialize() { - currentWizardPanel = null; - if (repositoryPanel != null) { - repositoryPanel.initialize(); - } - if (projectAccessPanel != null) { - projectAccessPanel.initialize(); - } - } - - @Override - public Dimension getPanelSize() { - return getMyPanelSize(); - } - - @Override - public void setWizardManager(WizardManager wm) { - wizardMgr = wm; - } - - @Override - public WizardManager getWizardManager() { - return wizardMgr; - } - - /** - * Get the repository adapter associated with the new project. - * After displaying this panel, this method should be invoked to obtain the - * repository which will be opened for shared projects. If the repository is - * not used to create a new project, its disconnect method should be invoked. - * @return null if project is not shared - */ - RepositoryAdapter getProjectRepository() { - return repository; - } - - String getProjectRepositoryName() { - return repositoryPanel.getRepositoryName(); - } - - private void checkNewRepositoryAccessPanel() { - - String repositoryName = repositoryPanel.getRepositoryName(); - if (projectAccessPanel != null && - projectAccessPanel.getRepositoryName().equals(repositoryName)) { - return; - } - - List userList = new ArrayList<>(); - userList.add(new User(server.getUser(), User.ADMIN)); - - projectAccessPanel = new ProjectAccessPanel(knownUsers, server.getUser(), userList, - repositoryName, includeAnonymousAccessControl, false, tool); - - projectAccessPanel.setHelpLocation( - new HelpLocation(GenericHelpTopics.FRONT_END, "SetupUsers")); - } - - /** - * Return true if a connection could be established using the given - * server name and port number. - */ - private boolean isServerInfoValid(String serverName, int portNumber) { - if (server != null && serverInfo != null && serverInfo.getServerName().equals(serverName) && - serverInfo.getPortNumber() == portNumber && server.isConnected()) { - return true; - } - - server = null; - serverInfo = null; - repositoryPanel = null; - - server = projectMgr.getRepositoryServerAdapter(serverName, portNumber, true); - if (server.isConnected()) { - serverInfo = projectMgr.getMostRecentServerInfo(); - return true; - } - - statusMessage = "Could not connect to server " + serverName + ", port " + portNumber; - return false; - } - - private Dimension getMyPanelSize() { - - ProjectAccessPanel panel1 = new ProjectAccessPanel(new String[] { "nobody" }, "user", - new ArrayList(), "MyRepository", true, false, tool); - RepositoryPanel panel2 = new RepositoryPanel(this, "ServerOne", - new String[] { "MyRepository", "NewStuff", "Repository_A", "Repository_B" }, false); - Dimension d1 = panel1.getPreferredSize(); - Dimension d2 = panel2.getPreferredSize(); - return new Dimension(Math.max(d1.width, d2.width), Math.max(d1.height, d2.height)); - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/UserAccessTableModel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/UserAccessTableModel.java index eaa8937bc4..8f6cd23b88 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/UserAccessTableModel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/UserAccessTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,7 +29,7 @@ import ghidra.framework.remote.User; * as checkboxes that can be selected by users, provided they have admin access. * */ -class UserAccessTableModel extends GDynamicColumnTableModel> { +public class UserAccessTableModel extends GDynamicColumnTableModel> { private List users; private String currentUser; @@ -147,7 +147,7 @@ class UserAccessTableModel extends GDynamicColumnTableModel> { * * @param users the user list */ - void setUserList(List users) { + public void setUserList(List users) { this.users = users; refresh(); } @@ -155,20 +155,20 @@ class UserAccessTableModel extends GDynamicColumnTableModel> { /** * Remove a list of users from the table. * - * @param list list of User objects + * @param removedUsers list of User objects */ - void removeUsers(ArrayList users) { - this.users.removeAll(users); + public void removeUsers(List removedUsers) { + this.users.removeAll(removedUsers); refresh(); } /** * Add a list of users to the table. * - * @param users list of User objects + * @param addedUsers list of User objects */ - void addUsers(ArrayList users) { - this.users.addAll(users); + public void addUsers(List addedUsers) { + this.users.addAll(addedUsers); refresh(); } @@ -198,7 +198,7 @@ class UserAccessTableModel extends GDynamicColumnTableModel> { @Override public String getValue(User rowObject, Settings settings, List data, - ServiceProvider serviceProvider) throws IllegalArgumentException { + ServiceProvider p) throws IllegalArgumentException { return rowObject.getName(); } } @@ -215,7 +215,7 @@ class UserAccessTableModel extends GDynamicColumnTableModel> { @Override public Boolean getValue(User rowObject, Settings settings, List data, - ServiceProvider serviceProvider) throws IllegalArgumentException { + ServiceProvider p) throws IllegalArgumentException { return rowObject.isReadOnly(); } } @@ -232,7 +232,7 @@ class UserAccessTableModel extends GDynamicColumnTableModel> { @Override public Boolean getValue(User rowObject, Settings settings, List data, - ServiceProvider serviceProvider) throws IllegalArgumentException { + ServiceProvider p) throws IllegalArgumentException { return rowObject.hasWritePermission() && !rowObject.isAdmin(); } } @@ -249,9 +249,10 @@ class UserAccessTableModel extends GDynamicColumnTableModel> { @Override public Boolean getValue(User rowObject, Settings settings, List data, - ServiceProvider serviceProvider) throws IllegalArgumentException { + ServiceProvider p) throws IllegalArgumentException { return rowObject.isAdmin(); } + } @Override diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectAccessStep.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectAccessStep.java new file mode 100644 index 0000000000..4ecab47ac4 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectAccessStep.java @@ -0,0 +1,103 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import java.io.IOException; +import java.util.ArrayList; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.app.util.GenericHelpTopics; +import ghidra.framework.client.RepositoryServerAdapter; +import ghidra.framework.main.ProjectAccessPanel; +import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.remote.User; +import ghidra.util.HelpLocation; +import ghidra.util.Msg; + +/** + * Wizard step for configuring user access in a Ghidra server repository. Used by the + * "new project", the "convert to shared" and the "change repository" wizards. This step + * only gets shown if the user creates a new repository. + */ +public class ProjectAccessStep extends WizardStep { + private ProjectAccessPanel panel; + private PluginTool tool; + + public ProjectAccessStep(WizardModel model, PluginTool tool) { + // no title passed to constructor, it will be set later + super(model, "", new HelpLocation(GenericHelpTopics.FRONT_END, "UserAccessList")); + this.tool = tool; + } + + @Override + public void initialize(ProjectWizardData data) { + setTitle("Specify Users for Repository: " + data.getRepositoryName()); + String repositoryName = data.getRepositoryName(); + RepositoryServerAdapter server = data.getServer(); + + try { + String[] allUsers = server.getAllUsers(); + ArrayList userList = new ArrayList<>(); + userList.add(new User(server.getUser(), User.ADMIN)); + panel = new ProjectAccessPanel(allUsers, server.getUser(), userList, + repositoryName, server.anonymousAccessAllowed(), false, tool); + } + catch (IOException e) { + Msg.error(this, "Error creating project access panel"); + } + + } + + @Override + public boolean isApplicable(ProjectWizardData data) { + return data.isSharedProject() && data.isNewRepository(); + } + + @Override + public boolean isValid() { + if (panel == null) { + return false; + } + return true; + } + + @Override + public void populateData(ProjectWizardData data) { + User[] projectUsers = panel.getProjectUsers(); + boolean allowAnonymousAccess = panel.allowAnonymousAccess(); + data.setProjectUsers(projectUsers); + data.setAllowAnonymousAccess(allowAnonymousAccess); + } + + @Override + public JComponent getComponent() { + return panel; + } + + @Override + public boolean apply(ProjectWizardData data) { + return true; + } + + @Override + public boolean canFinish(ProjectWizardData data) { + return panel != null; + } + +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectChooseRepositoryWizardModel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectChooseRepositoryWizardModel.java new file mode 100644 index 0000000000..62e257e351 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectChooseRepositoryWizardModel.java @@ -0,0 +1,136 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import java.awt.Dimension; +import java.io.IOException; +import java.util.List; + +import javax.swing.Icon; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import generic.theme.GIcon; +import ghidra.framework.client.*; +import ghidra.framework.model.ServerInfo; +import ghidra.framework.plugintool.PluginTool; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.UserAccessException; + +/** + * Wizard model for either converting a non-shared project to a share project OR for moving + * a shared project to a different server/repository. + */ +public class ProjectChooseRepositoryWizardModel extends WizardModel { + private final static Icon CONVERT_ICON = new GIcon("icon.project.info.convert"); + + private PluginTool tool; + + public ProjectChooseRepositoryWizardModel(PluginTool tool, String title) { + this(tool, title, null); + } + + public ProjectChooseRepositoryWizardModel(PluginTool tool, String title, ServerInfo server) { + super(title, new ProjectWizardData(), CONVERT_ICON); + this.tool = tool; + + // this will allow server steps to be applicable + data.setIsSharedProject(true); + data.setServerInfo(server); + } + + @Override + protected void addWizardSteps(List> steps) { + steps.add(new ServerStep(this, tool.getProjectManager())); + steps.add(new RepositoryStep(this)); + steps.add(new ProjectAccessStep(this, tool)); + + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(500, 300); + } + + @Override + protected boolean doFinish() { + RepositoryAdapter repository = getOrCreateRepository(); + if (repository == null) { + return false; + } + try { + repository.connect(); + } + catch (IOException e) { + setStatusMessage("Can't connect to repository: " + data.getRepositoryName()); + return false; + } + + data.setRepository(repository); + return true; + } + + @Override + public void cancel() { + RepositoryAdapter repository = data.getRepository(); + if (repository != null) { + repository.disconnect(); + data.setRepository(null); + } + data.setServer(null); + } + + private RepositoryAdapter getOrCreateRepository() { + String repositoryName = data.getRepositoryName(); + RepositoryServerAdapter server = data.getServer(); + + if (!data.isNewRepository()) { + RepositoryAdapter repository = server.getRepository(repositoryName); + if (repository == null) { + setStatusMessage("Can't open repository: " + repositoryName); + } + return repository; + } + + try { + RepositoryAdapter repository = server.createRepository(repositoryName); + repository.setUserList(data.getProjectUsers(), data.allowAnonymousAccess()); + return repository; + } + catch (DuplicateNameException e) { + setStatusMessage("Repository " + repositoryName + " exists"); + } + catch (UserAccessException exc) { + setStatusMessage("Could not update the user list: " + exc.getMessage()); + } + catch (NotConnectedException e) { + String statusMessage = e.getMessage(); + if (statusMessage == null) { + String serverName = data.getServerInfo().getServerName(); + statusMessage = "Not connected to server " + serverName; + } + setStatusMessage(statusMessage); + } + catch (IOException e) { + setStatusMessage("Error occurred while updating the user list: " + e.getMessage()); + } + return null; + } + + public RepositoryAdapter getRepository() { + return data.getRepository(); + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectTypePanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectTypePanel.java new file mode 100644 index 0000000000..6acfaa60a2 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectTypePanel.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import java.awt.BorderLayout; + +import javax.swing.*; + +import docking.widgets.button.GRadioButton; +import ghidra.util.layout.VerticalLayout; + +/** + * Gui panel for choosing the project type in a new project wizard. Used by the + * {@link ProjectTypeStep}. + */ +public class ProjectTypePanel extends JPanel { + private JRadioButton sharedRB; + private JRadioButton nonSharedRB; + private ButtonGroup buttonGroup; + + ProjectTypePanel() { + super(new BorderLayout()); + setBorder(ProjectWizardModel.STANDARD_BORDER); + + JPanel innerPanel = new JPanel(new VerticalLayout(10)); + + nonSharedRB = new GRadioButton("Non-Shared Project", true); + nonSharedRB.setToolTipText("Create a project that is not shared with others"); + + sharedRB = new GRadioButton("Shared Project"); + sharedRB.setToolTipText("Create a project that can be shared with others"); + + buttonGroup = new ButtonGroup(); + buttonGroup.add(nonSharedRB); + buttonGroup.add(sharedRB); + + innerPanel.add(nonSharedRB); + innerPanel.add(sharedRB); + add(innerPanel); + } + + boolean isSharedProject() { + return sharedRB.isSelected(); + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectTypeStep.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectTypeStep.java new file mode 100644 index 0000000000..9cb83843f4 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectTypeStep.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.app.util.GenericHelpTopics; +import ghidra.util.HelpLocation; + +/** + * Wizard step in the new project wizard for choosing the type of project. + */ +public class ProjectTypeStep extends WizardStep { + private ProjectTypePanel panel; + + protected ProjectTypeStep(WizardModel model) { + super(model, "Select Project Type", + new HelpLocation(GenericHelpTopics.FRONT_END, "SelectProjectType")); + + panel = new ProjectTypePanel(); + } + + @Override + public void initialize(ProjectWizardData data) { + // do nothing + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public void populateData(ProjectWizardData data) { + data.setIsSharedProject(panel.isSharedProject()); + } + + @Override + public boolean apply(ProjectWizardData data) { + return true; + } + + @Override + public boolean canFinish(ProjectWizardData data) { + return true; + } + + @Override + public JComponent getComponent() { + return panel; + } + +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectWizardData.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectWizardData.java new file mode 100644 index 0000000000..db0efd558a --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectWizardData.java @@ -0,0 +1,113 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import ghidra.framework.client.RepositoryAdapter; +import ghidra.framework.client.RepositoryServerAdapter; +import ghidra.framework.model.ProjectLocator; +import ghidra.framework.model.ServerInfo; +import ghidra.framework.remote.User; + +/** + * Wizard data for the {@link ProjectWizardModel} and its steps for the "new project" wizard. It + * is also used by the {@link ProjectChooseRepositoryWizardModel} for the wizards to convert a + * non-shared project to shared and for changing the repository/server info of an existing + * shared project. + */ +public class ProjectWizardData { + private boolean isSharedProject = false; + private ServerInfo serverInfo; + private RepositoryServerAdapter server; + private String repositoryName; + private boolean isNewRepository; + private User[] projectUsers; + private boolean allowAnonymousAccess; + private ProjectLocator locator; + private RepositoryAdapter repository; + + public void setIsSharedProject(boolean b) { + this.isSharedProject = b; + } + + public boolean isSharedProject() { + return isSharedProject; + } + + public void setProjectLocator(ProjectLocator locator) { + this.locator = locator; + } + + public ProjectLocator getProjectLocator() { + return locator; + } + + public void setServerInfo(ServerInfo serverInfo) { + this.serverInfo = serverInfo; + } + + public ServerInfo getServerInfo() { + return serverInfo; + } + + public void setServer(RepositoryServerAdapter server) { + this.server = server; + } + + public RepositoryServerAdapter getServer() { + return server; + } + + public void setRepositoryName(String repositoryName) { + this.repositoryName = repositoryName; + } + + public String getRepositoryName() { + return repositoryName; + } + + public void setIsNewRepository(boolean b) { + isNewRepository = b; + } + + public boolean isNewRepository() { + return isNewRepository; + } + + public void setProjectUsers(User[] projectUsers) { + this.projectUsers = projectUsers; + } + + public User[] getProjectUsers() { + return projectUsers; + } + + public void setAllowAnonymousAccess(boolean allowAnonymousAccess) { + this.allowAnonymousAccess = allowAnonymousAccess; + } + + public boolean allowAnonymousAccess() { + return allowAnonymousAccess; + } + + public void setRepository(RepositoryAdapter repository) { + this.repository = repository; + } + + public RepositoryAdapter getRepository() { + return repository; + } + +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectWizardModel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectWizardModel.java new file mode 100644 index 0000000000..ce197941c9 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ProjectWizardModel.java @@ -0,0 +1,138 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import java.awt.Dimension; +import java.io.IOException; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.border.Border; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import generic.theme.GIcon; +import ghidra.framework.client.*; +import ghidra.framework.model.ProjectLocator; +import ghidra.framework.plugintool.PluginTool; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.UserAccessException; + +/** + * Wizard model for creating new Ghidra projects. + */ +public class ProjectWizardModel extends WizardModel { + private final static Icon NEW_PROJECT_ICON = new GIcon("icon.menu.file.new.project"); + public final static Border STANDARD_BORDER = BorderFactory.createEmptyBorder(60, 50, 0, 50); + private PluginTool tool; + + public ProjectWizardModel(PluginTool tool) { + super("New Project", new ProjectWizardData(), NEW_PROJECT_ICON); + this.tool = tool; + } + + @Override + protected void addWizardSteps(List> steps) { + steps.add(new ProjectTypeStep(this)); + steps.add(new ServerStep(this, tool.getProjectManager())); + steps.add(new RepositoryStep(this)); + steps.add(new ProjectAccessStep(this, tool)); + steps.add(new SelectProjectStep(this)); + + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(500, 300); + } + + @Override + protected boolean doFinish() { + if (data.isSharedProject()) { + RepositoryAdapter repository = getOrCreateRepository(); + if (repository == null) { + return false; + } + try { + repository.connect(); + } + catch (IOException e) { + setStatusMessage("Can't connect to repository: " + data.getRepositoryName()); + return false; + } + + data.setRepository(repository); + } + return true; + } + + @Override + public void cancel() { + RepositoryAdapter repository = data.getRepository(); + if (repository != null) { + repository.disconnect(); + data.setRepository(null); + } + data.setServer(null); + data.setProjectLocator(null); + } + + private RepositoryAdapter getOrCreateRepository() { + String repositoryName = data.getRepositoryName(); + RepositoryServerAdapter server = data.getServer(); + + if (!data.isNewRepository()) { + RepositoryAdapter repository = server.getRepository(repositoryName); + if (repository == null) { + setStatusMessage("Can't open repository: " + repositoryName); + } + return repository; + } + + try { + RepositoryAdapter repository = server.createRepository(repositoryName); + repository.setUserList(data.getProjectUsers(), data.allowAnonymousAccess()); + return repository; + } + catch (DuplicateNameException e) { + setStatusMessage("Repository " + repositoryName + " exists"); + } + catch (UserAccessException exc) { + setStatusMessage("Could not update the user list: " + exc.getMessage()); + } + catch (NotConnectedException e) { + String statusMessage = e.getMessage(); + if (statusMessage == null) { + String serverName = data.getServerInfo().getServerName(); + statusMessage = "Not connected to server " + serverName; + } + setStatusMessage(statusMessage); + } + catch (IOException e) { + setStatusMessage("Error occurred while updating the user list: " + e.getMessage()); + } + return null; + } + + public ProjectLocator getProjectLocator() { + return data.getProjectLocator(); + } + + public RepositoryAdapter getRepository() { + return data.getRepository(); + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/RepositoryPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/RepositoryPanel.java similarity index 58% rename from Ghidra/Framework/Project/src/main/java/ghidra/framework/main/RepositoryPanel.java rename to Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/RepositoryPanel.java index 79f247ad47..c376d2cdf6 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/RepositoryPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/RepositoryPanel.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.framework.main; +package ghidra.framework.main.wizard.project; import java.awt.BorderLayout; import java.awt.event.ActionEvent; @@ -25,20 +25,17 @@ import javax.swing.event.*; import docking.widgets.button.GRadioButton; import docking.widgets.label.GDLabel; import docking.widgets.list.GList; -import docking.wizard.*; -import ghidra.app.util.GenericHelpTopics; -import ghidra.util.HelpLocation; -import ghidra.util.NamingUtilities; import ghidra.util.layout.VerticalLayout; +import utility.function.Callback; /** * Panel that shows a list of existing repositories, or allows the user - * to enter the name of a new repository to be created. - * + * to enter the name of a new repository to be created. Used by the {@link RepositoryStep} of + * either the new project wizard, the "convert to shared" wizard, or the "change repository" + * wizard. */ -public class RepositoryPanel extends AbstractWizardJPanel { +public class RepositoryPanel extends JPanel { - private String serverName; private JRadioButton existingRepButton; private JRadioButton createRepButton; private ButtonGroup buttonGroup; @@ -46,89 +43,20 @@ public class RepositoryPanel extends AbstractWizardJPanel { private DefaultListModel listModel; private JTextField nameField; private JLabel nameLabel; - private PanelManager panelManager; - private HelpLocation helpLoc; + private Callback statusChangedCallback; - public RepositoryPanel(PanelManager panelManager, String serverName, String[] repositoryNames, + public RepositoryPanel(Callback statusChangedCallback, String[] repositoryNames, boolean readOnlyServerAccess) { super(new BorderLayout(5, 10)); + this.statusChangedCallback = Callback.dummyIfNull(statusChangedCallback); setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - this.panelManager = panelManager; - this.serverName = serverName; buildMainPanel(repositoryNames, readOnlyServerAccess); } - /* (non Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getTitle() - */ - @Override - public String getTitle() { - return "Specify Repository Name on " + serverName; - } - - /* (non Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#initialize() - */ - @Override - public void initialize() { - existingRepButton.setSelected(true); - nameList.clearSelection(); - nameField.setText(""); - } - - /** - * Return whether the user entry is valid - */ - @Override - public boolean isValidInformation() { - if (createRepButton.isSelected()) { - String name = nameField.getText(); - if (name.length() == 0) { - return false; - } - if (!NamingUtilities.isValidProjectName(name)) { - panelManager.getWizardManager().setStatusMessage("Invalid project repository name"); - return false; - } - // - return !listModel.contains(name); - } - if (nameList.getSelectedValue() != null) { - return true; - } - return false; - } - - /* (non-Javadoc) - * @see ghidra.util.bean.wizard.WizardPanel#getHelpLocation() - */ - @Override - public HelpLocation getHelpLocation() { - if (helpLoc != null) { - return helpLoc; - } - return new HelpLocation(GenericHelpTopics.FRONT_END, "SelectRepository"); - } - - void setHelpLocation(HelpLocation helpLoc) { - this.helpLoc = helpLoc; - } - - boolean createRepository() { + public boolean isCreateRepositorySelected() { return createRepButton.isSelected(); } - /** - * Get the name of the repository; it either one selected from the list, - * or the name that the user entered to create a new repository. - */ - String getRepositoryName() { - if (createRepButton.isSelected()) { - return nameField.getText(); - } - return nameList.getSelectedValue(); - } - private void buildMainPanel(String[] repositoryNames, boolean readOnlyServerAccess) { buttonGroup = new ButtonGroup(); @@ -145,17 +73,18 @@ public class RepositoryPanel extends AbstractWizardJPanel { } private JPanel createListPanel(String[] repositoryNames) { - JPanel panel = new JPanel(new VerticalLayout(5)); + JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createTitledBorder("Choose Existing Repository")); - existingRepButton = new GRadioButton("Existing Repository", (repositoryNames.length > 0)); - existingRepButton.setEnabled(repositoryNames.length > 0); - buttonGroup.add(existingRepButton); - JPanel innerPanel = new JPanel(new BorderLayout()); - JLabel label = new GDLabel("Repository Names", SwingConstants.LEFT); - label.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 5)); - innerPanel.add(label, BorderLayout.NORTH); + panel.add(createExistingRepoButton(repositoryNames), BorderLayout.NORTH); + panel.add(createScrollableRepoList(repositoryNames), BorderLayout.CENTER); + return panel; + } + + private JPanel createScrollableRepoList(String[] repositoryNames) { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); listModel = new DefaultListModel<>(); for (String repositoryName : repositoryNames) { listModel.addElement(repositoryName); @@ -163,14 +92,17 @@ public class RepositoryPanel extends AbstractWizardJPanel { nameList = new GList<>(listModel); nameList.setEnabled(existingRepButton.isSelected()); JScrollPane sp = new JScrollPane(nameList); - innerPanel.add(sp); - - panel.add(existingRepButton); - panel.add(innerPanel); - + panel.add(sp); return panel; } + private JComponent createExistingRepoButton(String[] repositoryNames) { + existingRepButton = new GRadioButton("Existing Repository", (repositoryNames.length > 0)); + existingRepButton.setEnabled(repositoryNames.length > 0); + buttonGroup.add(existingRepButton); + return existingRepButton; + } + private JPanel createNamePanel() { JPanel namePanel = new JPanel(); namePanel.setLayout(new VerticalLayout(5)); @@ -186,17 +118,17 @@ public class RepositoryPanel extends AbstractWizardJPanel { DocumentListener dl = new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { - validateName(); + statusChangedCallback.call(); } @Override public void removeUpdate(DocumentEvent e) { - validateName(); + statusChangedCallback.call(); } @Override public void changedUpdate(DocumentEvent e) { - validateName(); + statusChangedCallback.call(); } }; nameField.getDocument().addDocumentListener(dl); @@ -211,26 +143,6 @@ public class RepositoryPanel extends AbstractWizardJPanel { return namePanel; } - private void validateName() { - WizardManager wm = panelManager.getWizardManager(); - String msg = null; - if (createRepButton.isSelected()) { - String name = nameField.getText(); - if (name.length() != 0) { - if (!NamingUtilities.isValidProjectName(name)) { - msg = "Invalid project repository name"; - } - else if (listModel.contains(name)) { - msg = name + " already exists"; - } - } - } - wm.validityChanged(); - if (msg != null) { - wm.setStatusMessage(msg); - } - } - private void addListeners() { ActionListener listener = new ActionListener() { @Override @@ -246,7 +158,7 @@ public class RepositoryPanel extends AbstractWizardJPanel { if (!createRepSelected) { nameField.setText(""); } - validateName(); + statusChangedCallback.call(); } }; existingRepButton.addActionListener(listener); @@ -254,16 +166,21 @@ public class RepositoryPanel extends AbstractWizardJPanel { ListSelectionModel selModel = nameList.getSelectionModel(); selModel.addListSelectionListener(new ListSelectionListener() { - /* (non Javadoc) - * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent) - */ @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } - panelManager.getWizardManager().validityChanged(); + statusChangedCallback.call(); } }); } + + public String getRepositoryName() { + if (isCreateRepositorySelected()) { + return nameField.getText().trim(); + } + return nameList.getSelectedValue(); + } + } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/RepositoryStep.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/RepositoryStep.java new file mode 100644 index 0000000000..0de34ffb62 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/RepositoryStep.java @@ -0,0 +1,122 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import java.io.IOException; +import java.util.List; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.app.util.GenericHelpTopics; +import ghidra.framework.client.RepositoryServerAdapter; +import ghidra.framework.model.ServerInfo; +import ghidra.util.HelpLocation; +import ghidra.util.NamingUtilities; + +/** + * Wizard step in the new project wizard selecting or creating a new repository in a Ghidra server. + */ +public class RepositoryStep extends WizardStep { + private RepositoryPanel panel; + private RepositoryServerAdapter server; + private String[] repositoryNames; + + protected RepositoryStep(WizardModel model) { + super(model, "", new HelpLocation(GenericHelpTopics.FRONT_END, "SelectRepository")); + } + + @Override + public void initialize(ProjectWizardData data) { + ServerInfo serverInfo = data.getServerInfo(); + setTitle("Specify Repository Name from server: " + serverInfo.getServerName()); + server = data.getServer(); + repositoryNames = getRepositoryNames(); + if (panel == null) { + boolean readOnly = isServerReadOnly(); + panel = new RepositoryPanel(this::notifyStatusChanged, repositoryNames, readOnly); + } + } + + private boolean isServerReadOnly() { + try { + return server.isReadOnly(); + } + catch (IOException e) { + return true; + } + } + + private String[] getRepositoryNames() { + try { + return server.getRepositoryNames(); + } + catch (IOException e) { + return new String[0]; + } + } + + @Override + public boolean isValid() { + String repositoryName = panel.getRepositoryName(); + if (panel.isCreateRepositorySelected()) { + if (repositoryName.length() == 0) { + return false; + } + if (!NamingUtilities.isValidProjectName(repositoryName)) { + setStatusMessage("Invalid project repository name"); + return false; + } + if (List.of(repositoryNames).contains(repositoryName)) { + setStatusMessage("Repository " + repositoryName + " already exists"); + return false; + } + } + else if (repositoryName == null) { + setStatusMessage("Please select a repository"); + return false; + } + return true; + } + + @Override + public void populateData(ProjectWizardData data) { + data.setRepositoryName(panel.getRepositoryName()); + data.setIsNewRepository(panel.isCreateRepositorySelected()); + + } + + @Override + public JComponent getComponent() { + return panel; + } + + @Override + public boolean apply(ProjectWizardData data) { + return true; + } + + @Override + public boolean canFinish(ProjectWizardData data) { + return data.getRepositoryName() != null; + } + + @Override + public boolean isApplicable(ProjectWizardData data) { + return data.isSharedProject(); + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/SelectProjectPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/SelectProjectPanel.java new file mode 100644 index 0000000000..15bdc85ebb --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/SelectProjectPanel.java @@ -0,0 +1,194 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import java.awt.BorderLayout; +import java.io.File; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import docking.widgets.button.BrowseButton; +import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.filechooser.GhidraFileChooserMode; +import docking.widgets.label.GDLabel; +import ghidra.framework.GenericRunInfo; +import ghidra.framework.model.ProjectLocator; +import ghidra.framework.preferences.Preferences; +import ghidra.util.filechooser.GhidraFileChooserModel; +import ghidra.util.filechooser.GhidraFileFilter; +import ghidra.util.layout.PairLayout; +import utility.function.Callback; + +/** + * Panel that allows the project directory and name to be specified for a + * new project. A checkbox indicates whether the project should be created + * as a shared project. Used by the {@link SelectProjectStep} of the new project wizard. + * + */ +public class SelectProjectPanel extends JPanel { + + //remove the "." from the extension + private static String PROJECT_EXTENSION = ProjectLocator.getProjectExtension().substring(1); + + private JTextField projectNameField; + private JTextField directoryField; + private JButton browseButton; + + private Callback statusChangedCallback; + + /** + * Construct a new panel. + * @param statusChangedCallback + * @param panelManager manager for the "new project" set of panels + */ + public SelectProjectPanel(Callback statusChangedCallback) { + super(new PairLayout(10, 10)); + setBorder(ProjectWizardModel.STANDARD_BORDER); + + this.statusChangedCallback = statusChangedCallback; + buildMainPanel(); + } + + void setProjectName(String projectName) { + projectNameField.setText(projectName); + } + + String getProjectName() { + String name = projectNameField.getText().trim(); + if (name.endsWith(PROJECT_EXTENSION)) { + name = name.substring(0, name.length() - PROJECT_EXTENSION.length()); + } + return name; + } + + String getDirectoryName() { + return directoryField.getText().trim(); + } + + private void buildMainPanel() { + DocumentListener documentListener = createDocumentListener(); + + add(new GDLabel("Project Directory:", SwingConstants.RIGHT)); + add(createDirectoryPanel(documentListener)); + add(new GDLabel("Project Name:", SwingConstants.RIGHT)); + add(createProjectNameField(documentListener)); + } + + private JTextField createProjectNameField(DocumentListener documentListener) { + projectNameField = new JTextField(10); + projectNameField.setName("Project Name"); + projectNameField.addActionListener(e -> statusChangedCallback.call()); + projectNameField.getDocument().addDocumentListener(documentListener); + return projectNameField; + } + + private JPanel createDirectoryPanel(DocumentListener listener) { + JPanel panel = new JPanel(new BorderLayout()); + directoryField = new JTextField(10); + directoryField.getDocument().addDocumentListener(listener); + directoryField.setName("Project Directory"); + + File projectDirectory = null; + String projectDirPath = Preferences.getProperty(Preferences.LAST_NEW_PROJECT_DIRECTORY); + if (projectDirPath != null) { + // if it exists, use last directory where project was created + projectDirectory = new File(projectDirPath); + if (!projectDirectory.isDirectory()) { + projectDirectory = null; + } + } + if (projectDirectory == null) { + // otherwise, use last project directory or default + projectDirectory = new File(GenericRunInfo.getProjectsDirPath()); + } + projectDirPath = projectDirectory.getAbsolutePath(); + directoryField.setText(projectDirPath); + directoryField.setCaretPosition(projectDirPath.length() - 1); + + browseButton = new BrowseButton(); + browseButton.addActionListener(e -> displayFileChooser()); + JPanel buttonPanel = new JPanel(new BorderLayout()); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + buttonPanel.add(browseButton, BorderLayout.CENTER); + + panel.add(directoryField, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.EAST); + return panel; + } + + private DocumentListener createDocumentListener() { + return new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + statusChangedCallback.call(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + statusChangedCallback.call(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + statusChangedCallback.call(); + } + }; + } + + private void displayFileChooser() { + GhidraFileChooser fileChooser = createFileChooser(); + fileChooser.setTitle("Select a Ghidra Project Directory"); + fileChooser.setApproveButtonText("Select Project Directory"); + fileChooser.setApproveButtonToolTipText("Select a Ghidra Project Directory"); + + File file = fileChooser.getSelectedFile(); + + if (file != null) { + directoryField.setText(file.getAbsolutePath()); + statusChangedCallback.call(); + } + + fileChooser.dispose(); + } + + private GhidraFileChooser createFileChooser() { + + GhidraFileChooser fileChooser = new GhidraFileChooser(this); + File projectDirectory = new File(GenericRunInfo.getProjectsDirPath()); + String lastDirSelected = + Preferences.getProperty(Preferences.LAST_NEW_PROJECT_DIRECTORY, null, true); + if (lastDirSelected != null) { + projectDirectory = new File(lastDirSelected); + } + fileChooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY); + fileChooser.setFileFilter(new GhidraFileFilter() { + @Override + public String getDescription() { + return "All Directories"; + } + + @Override + public boolean accept(File f, GhidraFileChooserModel model) { + return model.isDirectory(f) && + !f.getName().endsWith(ProjectLocator.getProjectDirExtension()); + } + }); + fileChooser.setCurrentDirectory(projectDirectory);//start the browsing in the user's preferred project directory + return fileChooser; + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/SelectProjectStep.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/SelectProjectStep.java new file mode 100644 index 0000000000..6f43247be4 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/SelectProjectStep.java @@ -0,0 +1,132 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import static ghidra.app.util.GenericHelpTopics.*; + +import java.io.File; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.framework.model.ProjectLocator; +import ghidra.util.HelpLocation; +import ghidra.util.NamingUtilities; + +/** + * Wizard step in the new project wizard for choosing the new project's root folder location and + * naming the project. + */ +public class SelectProjectStep extends WizardStep { + private SelectProjectPanel panel; + + protected SelectProjectStep(WizardModel model) { + // title and help will be set later based on the data + super(model, "", null); + panel = new SelectProjectPanel(() -> notifyStatusChanged()); + } + + @Override + public void initialize(ProjectWizardData data) { + boolean isShared = data.isSharedProject(); + + if (isShared) { + String repositoryName = data.getRepositoryName(); + if (panel.getProjectName().isBlank()) { + panel.setProjectName(repositoryName); + } + setTitle("Select Local Project Location for Repository \"" + repositoryName + "\""); + setHelpLocation(new HelpLocation(FRONT_END, "SelectProjectLocation")); + } + else { + setTitle("Select Project Location"); + setHelpLocation(new HelpLocation(FRONT_END, "CreateNonSharedProject")); + } + } + + @Override + public boolean isValid() { + String dir = panel.getDirectoryName(); + String projectName = panel.getProjectName(); + return isValid(dir, projectName); + } + + private boolean isValid(String dir, String projectName) { + if (dir.isBlank()) { + setStatusMessage("Please specify project directory"); + return false; + } + File projectDir = new File(dir); + if (!projectDir.isDirectory()) { + setStatusMessage("Project directory does not exist."); + return false; + } + + if (!NamingUtilities.isValidProjectName(projectName)) { + setStatusMessage("Please specify valid project name"); + return false; + } + try { + ProjectLocator locator = new ProjectLocator(dir, projectName); + if (locator.getMarkerFile().exists() || locator.getProjectDir().exists()) { + setStatusMessage("A project named " + locator.getName() + + " already exists in " + projectDir.getAbsolutePath()); + return false; + } + } + catch (IllegalArgumentException e) { + setStatusMessage(e.getMessage()); + return false; + } + return true; + } + + @Override + public void populateData(ProjectWizardData data) { + String dir = panel.getDirectoryName(); + String projectName = panel.getProjectName(); + data.setProjectLocator(new ProjectLocator(dir, projectName)); + } + + @Override + public boolean canFinish(ProjectWizardData data) { + ProjectLocator projectLocator = data.getProjectLocator(); + if (projectLocator != null) { + return true; + } + String name = data.getRepositoryName(); + if (data.isSharedProject() && name != null) { + String dir = panel.getDirectoryName(); + if (isValid(dir, name)) { + data.setProjectLocator(new ProjectLocator(dir, name)); + return true; + } + } + return false; + } + + @Override + public boolean apply(ProjectWizardData data) { + return true; + } + + @Override + public JComponent getComponent() { + return panel; + } + +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ServerInfoPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ServerInfoPanel.java new file mode 100644 index 0000000000..07d3e1626a --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ServerInfoPanel.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import java.awt.BorderLayout; + +import javax.swing.JPanel; + +import ghidra.framework.main.ServerInfoComponent; +import ghidra.framework.model.ServerInfo; +import utility.function.Callback; + +/** + * Panel that allows the user to specify the host name and port + * number for the remote repository server. Used by the {@link ServerStep} of + * either the new project wizard, the "convert to shared" wizard, or the "change repository" + * wizard. + */ +public class ServerInfoPanel extends JPanel { + + private ServerInfoComponent serverInfoComponent; + + public ServerInfoPanel(Callback statusChangedCallback) { + super(new BorderLayout(10, 10)); + setBorder(ProjectWizardModel.STANDARD_BORDER); + serverInfoComponent = new ServerInfoComponent(); + add(serverInfoComponent, BorderLayout.CENTER); + serverInfoComponent.setChangeListener(e -> statusChangedCallback.call()); + } + + public boolean isValidInformation() { + return serverInfoComponent.isValidInformation(); + } + + public String getStatusMessge() { + return serverInfoComponent.getStatusMessage(); + } + + public String getServerName() { + return serverInfoComponent.getServerName(); + } + + public int getPortNumber() { + return serverInfoComponent.getPortNumber(); + } + + /** + * Set the field values using the given server info. + */ + public void setServerInfo(ServerInfo info) { + serverInfoComponent.setServerInfo(info); + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ServerStep.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ServerStep.java new file mode 100644 index 0000000000..83430f264d --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/wizard/project/ServerStep.java @@ -0,0 +1,107 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.main.wizard.project; + +import javax.swing.JComponent; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; +import ghidra.app.util.GenericHelpTopics; +import ghidra.framework.client.RepositoryServerAdapter; +import ghidra.framework.model.ProjectManager; +import ghidra.framework.model.ServerInfo; +import ghidra.util.HelpLocation; + +/** + * Wizard step in the new project wizard for choosing the Ghidra server when creating a shared + * project. + */ +public class ServerStep extends WizardStep { + private ServerInfoPanel panel; + private ProjectManager projectManager; + + protected ServerStep(WizardModel model, ProjectManager projectManager) { + super(model, "Specify Server Information", + new HelpLocation(GenericHelpTopics.FRONT_END, "SetupServerInfo")); + this.projectManager = projectManager; + panel = new ServerInfoPanel(this::notifyStatusChanged); + } + + @Override + public void initialize(ProjectWizardData data) { + if (panel.getServerName().isBlank()) { + ServerInfo info = getInitialServerInfo(data); + if (info != null) { + panel.setServerInfo(info); + } + } + } + + private ServerInfo getInitialServerInfo(ProjectWizardData data) { + if (data.getServerInfo() != null) { + return data.getServerInfo(); + } + return projectManager.getMostRecentServerInfo(); + } + + @Override + public boolean isValid() { + if (!panel.isValidInformation()) { + setStatusMessage(panel.getStatusMessge()); + return false; + } + return true; + } + + @Override + public void populateData(ProjectWizardData data) { + String serverName = panel.getServerName(); + int portNumber = panel.getPortNumber(); + data.setServerInfo(new ServerInfo(serverName, portNumber)); + } + + @Override + public boolean canFinish(ProjectWizardData data) { + return data.getServer() != null; + } + + @Override + public boolean apply(ProjectWizardData data) { + ServerInfo serverInfo = data.getServerInfo(); + String serverName = serverInfo.getServerName(); + int port = serverInfo.getPortNumber(); + RepositoryServerAdapter server = + projectManager.getRepositoryServerAdapter(serverName, port, true); + if (!server.isConnected()) { + setStatusMessage("Could not connect to server " + serverName + ", port " + port); + return false; + } + + data.setServer(server); + return true; + } + + @Override + public JComponent getComponent() { + return panel; + } + + @Override + public boolean isApplicable(ProjectWizardData data) { + return data.isSharedProject(); + } + +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/extensions/ExtensionTableModel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/extensions/ExtensionTableModel.java index 7b5cb2eda5..cdd813fc5b 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/extensions/ExtensionTableModel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/extensions/ExtensionTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -231,6 +231,9 @@ class ExtensionTableModel extends ThreadedTableModel { for (ExtensionDetails e : extensions) { Boolean wasInstalled = originalInstallStates.get(e.getName()); + if (wasInstalled == null) { + return false; + } if (e.isInstalled() != wasInstalled) { return true; } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java index b07ae0ebcb..b960285efc 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/FrontEndPluginScreenShots.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,6 +15,8 @@ */ package help.screenshot; +import static org.junit.Assert.*; + import java.awt.*; import java.io.File; import java.io.IOException; @@ -31,14 +33,14 @@ import docking.DockingDialog; import docking.widgets.*; import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.table.GTable; -import docking.wizard.WizardManager; -import docking.wizard.WizardPanel; +import docking.wizard.WizardDialog; import generic.theme.GThemeDefaults.Colors; import ghidra.app.plugin.core.archive.RestoreDialog; import ghidra.framework.Application; import ghidra.framework.data.DefaultProjectData; import ghidra.framework.data.GhidraFileData; import ghidra.framework.main.*; +import ghidra.framework.main.wizard.project.*; import ghidra.framework.model.*; import ghidra.framework.preferences.Preferences; import ghidra.framework.project.extensions.ExtensionTablePanel; @@ -57,7 +59,7 @@ import resources.MultiIcon; public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { private static final String OTHER_PROJECT = "Other_Project"; - Icon icon = (Icon) getInstanceField("CONVERT_ICON", ProjectInfoDialog.class); + Icon icon = (Icon) getInstanceField("CONVERT_ICON", ProjectChooseRepositoryWizardModel.class); public FrontEndPluginScreenShots() { super(); @@ -98,18 +100,20 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { @Test public void testChangeAccessList() { + String[] knownUsers = { "user1", "user2", "user3", "user4", "user5", "user6" }; ArrayList userList = new ArrayList<>(); userList.add(new User("user4", 2)); runSwing(() -> { - WizardPanel panel = - new ProjectAccessPanel(knownUsers, "Bob", userList, "Demo", false, false, tool); - TestDummyPanelManager panelMgr = - new TestDummyPanelManager(panel, true, false, true, 650, 250); - WizardManager wizard = - new WizardManager("Change Shared Project Information", false, panelMgr, icon); - wizard.showWizard(); + + ProjectAccessPanel panel = + new ProjectAccessPanel(knownUsers, "user4", userList, "Demo", false, false, tool); + TestDummyWizardModel panelMgr = + new TestDummyWizardModel<>(panel, true, false, true, + "Change Shared Project Information", 650, 250, new ProjectWizardData(), icon); + WizardDialog wizard = new WizardDialog(panelMgr, false); + wizard.show(); }); waitForSwing(); @@ -131,13 +135,15 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { @Test public void testChangeRepositoryPanel() { RepositoryPanel panel = - new RepositoryPanel(null, "MyServer", new String[] { "Demo", "Test", "Sample" }, false); + new RepositoryPanel(null, new String[] { "Demo", "Test", "Sample" }, false); - TestDummyPanelManager panelMgr = - new TestDummyPanelManager(panel, false, true, false, 600, 375); - WizardManager wizard = - new WizardManager("Change Shared Project Information", false, panelMgr, icon); - wizard.showWizard(); + TestDummyWizardModel panelMgr = + new TestDummyWizardModel(panel, false, true, false, + "Change Shared Project Information", 600, 375, + new ProjectWizardData(), icon); + + WizardDialog wizard = new WizardDialog(panelMgr, false); + wizard.show(); waitForSwing(); @@ -146,17 +152,16 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { @Test public void testChangeServerInfoPanel() { - TestDummyPanelManager panelMgr = - new TestDummyPanelManager(null, false, true, false, 600, 180); - ServerInfoPanel panel = new ServerInfoPanel(panelMgr); - panelMgr.setPanel(panel); + ServerInfoPanel panel = new ServerInfoPanel(null); - WizardManager wizard = - new WizardManager("Change Shared Project Information", false, panelMgr, icon); + ProjectWizardData data = new ProjectWizardData(); + data.setServerInfo(new ServerInfo("server1", 13100)); + TestDummyWizardModel panelMgr = + new TestDummyWizardModel<>(panel, false, true, false, + "Change Shared Project Information", 600, 180, data, icon); - panel.setServerInfo(new ServerInfo("server1", 13100)); - - wizard.showWizard(); + WizardDialog wizard = new WizardDialog(panelMgr, false); + wizard.show(); waitForSwing(); @@ -254,7 +259,7 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { @Test public void testDeleteProject() { performAction("Delete Project", "FrontEndPlugin", false); - captureDialog(); + captureDialog(600, 350); } @Test @@ -280,7 +285,7 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { runSwing(() -> { JPanel panel = - new ProjectAccessPanel(knownUsers, "Bob", userList, "What", false, false, tool); + new ProjectAccessPanel(knownUsers, "user2", userList, "What", false, false, tool); DummyDialogComponentProvider dialog = new DummyDialogComponentProvider("Edit Project Access List for Demo", panel); @@ -311,7 +316,7 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { @Test public void testOpenProject() { performAction("Open Project", "FrontEndPlugin", false); - captureDialog(); + captureDialog(600, 350); } @Test @@ -397,17 +402,17 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { @Test public void testRepositoryNamePanel() { - TestDummyPanelManager panelMgr = - new TestDummyPanelManager(null, false, true, true, 600, 375); + RepositoryPanel panel = + new RepositoryPanel(null, new String[] { "Demo", "Test", "Sample" }, false); - final RepositoryPanel panel = new RepositoryPanel(panelMgr, "Server1", - new String[] { "Demo", "Test", "Sample" }, false); - panelMgr.setPanel(panel); + TestDummyWizardModel panelMgr = + new TestDummyWizardModel(panel, false, true, false, + "Specify Repository Name on Server1", 600, 375, + new ProjectWizardData(), icon); - WizardManager wizard = - new WizardManager("Specify Repository Name on Server1", false, panelMgr, icon); + WizardDialog wizard = new WizardDialog(panelMgr, false); - wizard.showWizard(); + wizard.show(); runSwing(() -> { JList jlist = (JList) getInstanceField("nameList", panel); @@ -482,12 +487,14 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { public void testSelectProjectLocation() { performAction("New Project", "FrontEndPlugin", false); DialogComponentProvider dialog = getDialog(); - final WizardManager wm = (WizardManager) dialog; + WizardDialog wm = (WizardDialog) dialog; + JButton nextButton = findButtonByText(wm, "Next >>"); + pressButton(nextButton, true); + + SelectProjectPanel projPanel = findComponent(wm, SelectProjectPanel.class); + JTextField dirField = (JTextField) findComponentByName(projPanel, "Project Directory"); runSwing(() -> { - wm.next(); - WizardPanel panel = wm.getCurrentWizardPanel(); - JTextField textField = (JTextField) getInstanceField("directoryField", panel); - textField.setText("/Projects"); + dirField.setText("/Projects"); wm.setStatusText(""); }); @@ -504,14 +511,17 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { public void testSelectSharedProjectLocation() { performAction("New Project", "FrontEndPlugin", false); DialogComponentProvider dialog = getDialog(); - final WizardManager wm = (WizardManager) dialog; + WizardDialog wm = (WizardDialog) dialog; + JButton nextButton = findButtonByText(wm, "Next >>"); + pressButton(nextButton, true); + SelectProjectPanel projPanel = findComponent(wm, SelectProjectPanel.class); + JTextField nameField = (JTextField) findComponentByName(projPanel, "Project Name"); + JTextField dirField = (JTextField) findComponentByName(projPanel, "Project Directory"); + runSwing(() -> { - wm.next(); - WizardPanel panel = wm.getCurrentWizardPanel(); - JTextField textField = (JTextField) getInstanceField("directoryField", panel); - textField.setText("/Projects"); - textField = (JTextField) getInstanceField("projectNameField", panel); - textField.setText("Demo"); + + dirField.setText("/Projects"); + nameField.setText("Demo"); wm.setStatusText(""); JLabel label = (JLabel) getInstanceField("titleLabel", wm); label.setText("Select Local Project Location for Repository Demo"); @@ -523,32 +533,41 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { } @Test - public void testSelectSharedProjectType() { + public void testSelectSharedProjectType() throws Exception { performAction("New Project", "FrontEndPlugin", false); DialogComponentProvider dialog = getDialog(); - final WizardManager wm = (WizardManager) dialog; + WizardDialog wm = (WizardDialog) dialog; + final JRadioButton rb = + (JRadioButton) findAbstractButtonByText(dialog.getComponent(), "Shared Project"); + assertNotNull(rb); + assertTrue(!rb.isSelected()); + + SwingUtilities.invokeAndWait(() -> rb.setSelected(true)); + runSwing(() -> { - WizardPanel panel = wm.getCurrentWizardPanel(); - JRadioButton sharedRB = (JRadioButton) getInstanceField("sharedRB", panel); - sharedRB.setSelected(true); wm.setStatusText(""); }); captureDialog(700, 350); } @Test - public void testServerInfo() { + public void testServerInfo() throws Exception { performAction("New Project", "FrontEndPlugin", false); DialogComponentProvider dialog = getDialog(); - final WizardManager wm = (WizardManager) dialog; - runSwing(() -> { - WizardPanel panel = wm.getCurrentWizardPanel(); - JRadioButton sharedRB = (JRadioButton) getInstanceField("sharedRB", panel); - sharedRB.setSelected(true); - wm.next(); - panel = wm.getCurrentWizardPanel(); - Component comp = (Component) getInstanceField("serverInfoComponent", panel); + WizardDialog wm = (WizardDialog) dialog; + final JRadioButton rb = + (JRadioButton) findAbstractButtonByText(dialog.getComponent(), "Shared Project"); + assertNotNull(rb); + assertTrue(!rb.isSelected()); + + SwingUtilities.invokeAndWait(() -> rb.setSelected(true)); + JButton nextButton = findButtonByText(wm, "Next >>"); + pressButton(nextButton, true); + ServerInfoPanel serverPanel = findComponent(wm, ServerInfoPanel.class); + Component comp = (Component) getInstanceField("serverInfoComponent", serverPanel); + + runSwing(() -> { JTextField textField = (JTextField) getInstanceField("nameField", comp); textField.setText("Server1"); }); @@ -579,28 +598,6 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { captureDialog(400, 500); } - @Test - public void testEditProjectAccessPanel() { - String[] knownUsers = { "user1", "user2", "user3", "user4", "user5", "user6" }; - ArrayList userList = new ArrayList<>(); - userList.add(new User("user2", 2)); - userList.add(new User("user4", 0)); - userList.add(new User("user5", 1)); - - runSwing(() -> { - WizardPanel panel = - new ProjectAccessPanel(knownUsers, "user2", userList, "Demo", false, false, tool); - - TestDummyPanelManager panelMgr = - new TestDummyPanelManager(panel, true, false, true, 650, 250); - WizardManager wizard = new WizardManager("New Project", false, panelMgr, icon); - wizard.showWizard(); - }); - - waitForSwing(); - captureDialog(); - } - @Test public void testViewProjectAccessPanel() { String[] knownUsers = { "user1", "user2", "user3", "user4", "user5", "user6" }; @@ -649,13 +646,6 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator { captureIconAndText(multiIcon, "Example (1)"); } - @Test - public void testMemoryUsage() { - performFrontEndAction("Show VM memory", "MemoryUsagePlugin", false); - waitForVMMemoryInitialilzed();// this is asynchronous and takes a while - captureDialog(); - } - @Test public void testViewOtherProjects() throws IOException, LockException, InvalidNameException, CancelledException { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TestDummyPanelManager.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TestDummyPanelManager.java deleted file mode 100644 index f410138b7d..0000000000 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TestDummyPanelManager.java +++ /dev/null @@ -1,108 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package help.screenshot; - -import java.awt.Dimension; - -import docking.wizard.*; - -class TestDummyPanelManager implements PanelManager { - private WizardPanel panel; - private boolean canFinish; - private boolean hasNextPanel; - private boolean hasPreviousPanel; - private Dimension dimension; - private WizardManager wizard; - - TestDummyPanelManager(WizardPanel panel, boolean canFinish, boolean hasNextPanel, - boolean hasPreviousPanel, int width, int height) { - this.panel = panel; - this.canFinish = canFinish; - this.hasNextPanel = hasNextPanel; - this.hasPreviousPanel = hasPreviousPanel; - this.dimension = new Dimension(width, height); - } - - public void setPanel(WizardPanel panel) { - this.panel = panel; - } - - @Override - public boolean canFinish() { - return canFinish; - } - - @Override - public boolean hasNextPanel() { - return hasNextPanel; - } - - @Override - public boolean hasPreviousPanel() { - return hasPreviousPanel; - } - - @Override - public WizardPanel getNextPanel() { - return panel; - } - - @Override - public WizardPanel getInitialPanel() { - return panel; - } - - @Override - public WizardPanel getPreviousPanel() { - return panel; - } - - @Override - public String getStatusMessage() { - return null; - } - - @Override - public void finish() { - // stub - } - - @Override - public void cancel() { - // stub - } - - @Override - public void initialize() { - // stub - } - - @Override - public Dimension getPanelSize() { - return dimension; - } - - @Override - public void setWizardManager(WizardManager wm) { - this.wizard = wm; - } - - @Override - public WizardManager getWizardManager() { - return wizard; - } - -} diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TestDummyWizardModel.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TestDummyWizardModel.java new file mode 100644 index 0000000000..42315cc634 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/TestDummyWizardModel.java @@ -0,0 +1,131 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package help.screenshot; + +import java.awt.Dimension; +import java.util.List; + +import javax.swing.*; + +import docking.wizard.WizardModel; +import docking.wizard.WizardStep; + +/** + * Wizard model for testing + * + * @param the test data + */ +class TestDummyWizardModel extends WizardModel { + private JPanel panel; + private boolean canFinish; + private boolean hasNextPanel; + private boolean hasPreviousPanel; + private Dimension dimension; + private String title; + + TestDummyWizardModel(JPanel panel, boolean canFinish, boolean hasNextPanel, + boolean hasPreviousPanel, String title, int width, int height, T data, Icon icon) { + super("Test Wizard", data, icon); + this.panel = panel; + this.canFinish = canFinish; + this.hasNextPanel = hasNextPanel; + this.hasPreviousPanel = hasPreviousPanel; + this.title = title; + this.dimension = new Dimension(width, height); + } + + @Override + protected void addWizardSteps(List> steps) { + TestDummyWizardStep step = new TestDummyWizardStep<>(this, panel, title); + steps.add(step); + } + + @Override + public boolean canFinish() { + return canFinish; + } + + @Override + public Dimension getPreferredSize() { + return dimension; + } + + @Override + public boolean canGoNext() { + return hasNextPanel; + } + + @Override + public boolean canGoBack() { + return hasPreviousPanel; + } + + @Override + public String getStatusMessage() { + return null; + } + + @Override + public boolean doFinish() { + return true; + } + + @Override + public void cancel() { + // stub + } + + private static class TestDummyWizardStep extends WizardStep { + private JPanel panel; + + TestDummyWizardStep(WizardModel model, JPanel panel, String title) { + super(model, title, null); + this.panel = panel; + + } + + @Override + public void initialize(T data) { + // stub + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean canFinish(T data) { + return true; + } + + @Override + public void populateData(T data) { + // stub + } + + @Override + public boolean apply(T data) { + return false; + } + + @Override + public JComponent getComponent() { + return panel; + } + } + +} diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/NewProjectWizardTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/NewProjectWizardTest.java index 9fdc89f598..5bf7d47b49 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/NewProjectWizardTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/NewProjectWizardTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,11 +27,12 @@ import org.junit.*; import org.junit.experimental.categories.Category; import docking.action.DockingActionIf; -import docking.wizard.WizardManager; +import docking.wizard.WizardDialog; import generic.test.AbstractGenericTest; import generic.test.category.PortSensitiveCategory; import ghidra.framework.GenericRunInfo; import ghidra.framework.client.*; +import ghidra.framework.main.wizard.project.*; import ghidra.framework.model.Project; import ghidra.framework.model.ProjectLocator; import ghidra.framework.preferences.Preferences; @@ -130,8 +131,7 @@ public class NewProjectWizardTest extends AbstractGhidraHeadedIntegrationTest { performAction(action, false); waitForSwing(); - WizardManager wm = - waitForDialogComponent(frontEndTool.getToolFrame(), WizardManager.class, 2000); + WizardDialog wm = waitForDialogComponent(WizardDialog.class); assertNotNull(wm); ProjectTypePanel typePanel = findComponent(wm, ProjectTypePanel.class); @@ -193,8 +193,7 @@ public class NewProjectWizardTest extends AbstractGhidraHeadedIntegrationTest { performAction(action, false); waitForSwing(); - WizardManager wm = - waitForDialogComponent(frontEndTool.getToolFrame(), WizardManager.class, 2000); + WizardDialog wm = waitForDialogComponent(WizardDialog.class); assertNotNull(wm); ProjectTypePanel typePanel = findComponent(wm, ProjectTypePanel.class); @@ -253,8 +252,7 @@ public class NewProjectWizardTest extends AbstractGhidraHeadedIntegrationTest { performAction(action, false); waitForSwing(); - WizardManager wm = - waitForDialogComponent(frontEndTool.getToolFrame(), WizardManager.class, 2000); + WizardDialog wm = waitForDialogComponent(WizardDialog.class); assertNotNull(wm); ProjectTypePanel typePanel = findComponent(wm, ProjectTypePanel.class); @@ -393,8 +391,7 @@ public class NewProjectWizardTest extends AbstractGhidraHeadedIntegrationTest { performAction(action, false); waitForSwing(); - WizardManager wm = - waitForDialogComponent(frontEndTool.getToolFrame(), WizardManager.class, 2000); + WizardDialog wm = waitForDialogComponent(WizardDialog.class); assertNotNull(wm); ProjectTypePanel typePanel = findComponent(wm, ProjectTypePanel.class); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ProjectInfoDialogTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ProjectInfoDialogTest.java index bd75cbf002..9d622c8fd1 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ProjectInfoDialogTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ProjectInfoDialogTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,9 +27,11 @@ import org.junit.experimental.categories.Category; import docking.action.DockingActionIf; import docking.widgets.OptionDialog; -import docking.wizard.WizardManager; +import docking.wizard.WizardDialog; import generic.test.category.PortSensitiveCategory; import ghidra.framework.client.RepositoryAdapter; +import ghidra.framework.main.wizard.project.RepositoryPanel; +import ghidra.framework.main.wizard.project.ServerInfoPanel; import ghidra.framework.model.*; import ghidra.framework.preferences.Preferences; import ghidra.program.database.ProgramBuilder; @@ -377,7 +379,7 @@ public class ProjectInfoDialogTest extends AbstractGhidraHeadedIntegrationTest { private void stepThroughWizard(boolean doFinish, final String repositoryName) throws Exception { System.err.println(getClass().getName() + ".stepThroughWizard()..."); windowForComponent(dialog.getComponent()); - WizardManager wm = waitForDialogComponent(WizardManager.class); + WizardDialog wm = waitForDialogComponent(WizardDialog.class); assertNotNull(wm); JButton nextButton = findButtonByText(wm, "Next >>"); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/SharedProjectUtil.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/SharedProjectUtil.java index f681a8353f..2a76ed3a7b 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/SharedProjectUtil.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/SharedProjectUtil.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,10 +29,11 @@ import javax.swing.*; import docking.action.DockingActionIf; import docking.test.AbstractDockingTest; -import docking.wizard.WizardManager; +import docking.wizard.WizardDialog; import generic.test.AbstractGTest; import generic.test.AbstractGuiTest; import ghidra.framework.client.*; +import ghidra.framework.main.wizard.project.*; import ghidra.framework.model.*; import ghidra.server.remote.ServerTestUtil; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; @@ -73,7 +74,7 @@ public class SharedProjectUtil { AbstractDockingTest.performAction(action, false); AbstractGuiTest.waitForSwing(); - WizardManager wm = AbstractDockingTest.waitForDialogComponent(WizardManager.class); + WizardDialog wm = AbstractDockingTest.waitForDialogComponent(WizardDialog.class); ProjectTypePanel typePanel = AbstractDockingTest.findComponent(wm, ProjectTypePanel.class); final JRadioButton rb = @@ -126,12 +127,7 @@ public class SharedProjectUtil { projNameField.setText(projectName); }); - if (!finishButton.isEnabled()) { - String statusMessage = projPanel.getStatusMessage(); - System.err.println( - "Finish button is unexectedly disabled!!\n\t" + "Status message: " + statusMessage); - return false; - } + assertTrue("Finish button is unexpectedly disabled", finishButton.isEnabled()); AbstractGuiTest.pressButton(finishButton, true); AbstractGuiTest.waitForSwing(); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java index f836cff431..ec58b1cc71 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -35,7 +35,7 @@ import docking.widgets.OptionDialog; import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.table.GTable; import docking.widgets.tree.GTreeNode; -import docking.wizard.WizardManager; +import docking.wizard.WizardDialog; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.framework.GenericRunInfo; import ghidra.framework.ToolUtils; @@ -176,7 +176,7 @@ public class ToolActionManagerTest extends AbstractGhidraHeadedIntegrationTest { String toolNamePrefix = "TestCodeBrowser"; final File cbFile = ResourceManager - .getResourceFile("defaultTools/" + toolNamePrefix + ToolUtils.TOOL_EXTENSION); + .getResourceFile("defaultTools/" + toolNamePrefix + ToolUtils.TOOL_EXTENSION); assertNotNull(cbFile); DockingActionIf importAction = getAction("Import Tool"); @@ -334,7 +334,7 @@ public class ToolActionManagerTest extends AbstractGhidraHeadedIntegrationTest { // close the VT tool // we first have to close the wizard... - final WizardManager wizard = waitForDialogComponent(WizardManager.class); + WizardDialog wizard = waitForDialogComponent(WizardDialog.class); runSwing(() -> wizard.close()); // ...then the tool