diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index 8e3b8a2462..bf0d24af84 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -113,7 +113,6 @@ src/main/help/help/topics/BookmarkPlugin/images/BookmarksFilter.png||GHIDRA||||E src/main/help/help/topics/BookmarkPlugin/images/MarkerForBookmark.png||GHIDRA||||END| src/main/help/help/topics/BookmarkPlugin/images/NextSelectionBlock16.gif||GHIDRA||||END| src/main/help/help/topics/BookmarkPlugin/images/PreviousSelectionBlock16.gif||GHIDRA||||END| -src/main/help/help/topics/BookmarkPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/CParserPlugin/CParser.htm||GHIDRA||||END| src/main/help/help/topics/CParserPlugin/images/ParseCSource.png||GHIDRA||||END| src/main/help/help/topics/CParserPlugin/images/ParseError.png||GHIDRA||||END| @@ -167,7 +166,6 @@ src/main/help/help/topics/CodeBrowserPlugin/images/hoverOff.gif||GHIDRA||||END| src/main/help/help/topics/CodeBrowserPlugin/images/hoverOn.gif||GHIDRA||||END| src/main/help/help/topics/CommentWindowPlugin/comment_window.htm||GHIDRA||||END| src/main/help/help/topics/CommentWindowPlugin/images/CommentsWindow.png||GHIDRA||||END| -src/main/help/help/topics/CommentWindowPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/CommentsPlugin/Comments.htm||GHIDRA||||END| src/main/help/help/topics/CommentsPlugin/images/Comment.png||GHIDRA||||END| src/main/help/help/topics/CommentsPlugin/images/ShowCommentHistory.png||GHIDRA||||END| @@ -264,7 +262,6 @@ src/main/help/help/topics/DataTypePreviewPlugin/images/edit-delete.png||Oxygen I src/main/help/help/topics/DataWindowPlugin/data_window.htm||GHIDRA||||END| src/main/help/help/topics/DataWindowPlugin/images/DataWindow.png||GHIDRA||||END| src/main/help/help/topics/DataWindowPlugin/images/DataWindowFilter.png||GHIDRA||||END| -src/main/help/help/topics/DataWindowPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/DbViewerPlugin/DbViewer.htm||GHIDRA||reviewed||END| src/main/help/help/topics/DbViewerPlugin/images/DatabaseViewer.png||GHIDRA||||END| src/main/help/help/topics/DisassembledViewPlugin/DisassembledViewPlugin.htm||GHIDRA||||END| @@ -404,7 +401,6 @@ src/main/help/help/topics/FunctionTagPlugin/images/InputField.png||GHIDRA||||END src/main/help/help/topics/FunctionWindowPlugin/function_window.htm||GHIDRA||||END| src/main/help/help/topics/FunctionWindowPlugin/images/FunctionWindow.png||GHIDRA||||END| src/main/help/help/topics/FunctionWindowPlugin/images/page_white_c.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| -src/main/help/help/topics/FunctionWindowPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/GhidraScriptMgrPlugin/GhidraScriptMgrPlugin.htm||GHIDRA||||END| src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm||GHIDRA||||END| src/main/help/help/topics/GhidraScriptMgrPlugin/images/Assign_Key_Binding.png||GHIDRA||||END| @@ -470,7 +466,6 @@ src/main/help/help/topics/LocationReferencesPlugin/images/ReferencesToDialog.png src/main/help/help/topics/LocationReferencesPlugin/images/XRefLabelReferencesSample.png||GHIDRA||||END| src/main/help/help/topics/LocationReferencesPlugin/images/go-home.png||Tango Icons - Public Domain|||Tango|END| src/main/help/help/topics/LocationReferencesPlugin/images/tag_yellow.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| -src/main/help/help/topics/LocationReferencesPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm||GHIDRA||||END| src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png||GHIDRA||||END| src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png||GHIDRA||||END| @@ -598,7 +593,6 @@ src/main/help/help/topics/ReferencesPlugin/images/erase16.png||GHIDRA||||END| src/main/help/help/topics/ReferencesPlugin/images/go-home.png||Tango Icons - Public Domain|||Tango|END| src/main/help/help/topics/ReferencesPlugin/images/locationIn.gif||GHIDRA||||END| src/main/help/help/topics/ReferencesPlugin/images/locationOut.gif||GHIDRA||||END| -src/main/help/help/topics/ReferencesPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/ReferencesPlugin/images/unchecked.png||GHIDRA||||END| src/main/help/help/topics/RegisterPlugin/Registers.htm||GHIDRA||||END| src/main/help/help/topics/RegisterPlugin/images/ClearRegisterValues.png||GHIDRA||||END| @@ -609,7 +603,6 @@ src/main/help/help/topics/RegisterPlugin/images/edit-delete.png||Oxygen Icons - src/main/help/help/topics/RegisterPlugin/images/locationIn.gif||GHIDRA||||END| src/main/help/help/topics/RegisterPlugin/images/registerGroup.png||GHIDRA||||END| src/main/help/help/topics/RegisterPlugin/images/registerIcon.png||GHIDRA||||END| -src/main/help/help/topics/RegisterPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/RegisterPlugin/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/help/help/topics/RelocationTablePlugin/images/Relocation_Table.png||GHIDRA||||END| src/main/help/help/topics/RelocationTablePlugin/relocation_table.htm||GHIDRA||||END| @@ -712,7 +705,6 @@ src/main/help/help/topics/Search/images/page_white_copy.png||FAMFAMFAM Icons - C src/main/help/help/topics/Search/images/reload.png||Nuvola Icons - LGPL 2.1||||END| src/main/help/help/topics/Search/images/searchm_obj.gif||GHIDRA||||END| src/main/help/help/topics/Search/images/table_delete.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| -src/main/help/help/topics/Search/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/Search/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/help/help/topics/SelectBlockPlugin/Select_Block_Help.html||GHIDRA||||END| src/main/help/help/topics/SelectBlockPlugin/images/Dialog.png||GHIDRA||||END| @@ -749,7 +741,6 @@ src/main/help/help/topics/SymbolTablePlugin/images/edit-delete.png||Oxygen Icons src/main/help/help/topics/SymbolTablePlugin/images/references_to.gif||GHIDRA||||END| src/main/help/help/topics/SymbolTablePlugin/images/table.png||FAMFAMFAM Icons - CC 2.5|||silk|END| src/main/help/help/topics/SymbolTablePlugin/images/table_go.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| -src/main/help/help/topics/SymbolTablePlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/SymbolTablePlugin/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/help/help/topics/SymbolTablePlugin/symbol_references.htm||GHIDRA||||END| src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm||GHIDRA||||END| @@ -830,7 +821,6 @@ src/main/help/help/topics/VersionControl/images/vcUndoCheckOut.png||GHIDRA||||EN src/main/help/help/topics/VersionControl/project_repository.htm||GHIDRA||||END| src/main/help/help/topics/ViewStringsPlugin/ViewStringsPlugin.htm||GHIDRA||||END| src/main/help/help/topics/ViewStringsPlugin/images/Defined_String_Table.png||GHIDRA||||END| -src/main/help/help/topics/ViewStringsPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/java/ghidra/app/cmd/comments/package.html||GHIDRA||reviewed||END| src/main/java/ghidra/app/cmd/data/package.html||GHIDRA||||END| src/main/java/ghidra/app/cmd/disassemble/package.html||GHIDRA||||END| @@ -1162,7 +1152,6 @@ src/main/resources/images/table_delete.png||FAMFAMFAM Icons - CC 2.5|||famfamfam src/main/resources/images/table_go.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/table_row_delete.png||FAMFAMFAM Icons - CC 2.5|||silk|END| src/main/resources/images/tag_yellow.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| -src/main/resources/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/text_list_bullets.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/text_lowercase.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/textfield.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java index 83543933fb..214066b9a9 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java @@ -20,6 +20,7 @@ import java.io.*; import java.util.*; import docking.action.DockingActionIf; +import docking.actions.KeyBindingUtils; import ghidra.app.script.GhidraScript; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginTool; @@ -30,9 +31,9 @@ public class CreateHelpTemplateScript extends GhidraScript { @Override protected void run() throws Exception { PluginTool tool = state.getTool(); - Plugin[] plugins = getSortedPlugins(tool); + List plugins = getSortedPlugins(tool); Plugin selectedPlugin = - askChoice("Select Plugin To Use To Generate Help", "Plugin", plugins, plugins[0]); + askChoice("Select Plugin To Use To Generate Help", "Plugin", plugins, plugins.get(0)); if (selectedPlugin == null) { printerr("no plugin selected, no help template created."); return; @@ -101,7 +102,8 @@ public class CreateHelpTemplateScript extends GhidraScript { } private List getActions(PluginTool tool, Plugin plugin) { - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = KeyBindingUtils.getKeyBindingActionsForOwner(tool, plugin.getName()); + List list = new ArrayList<>(actions); Comparator comparator = (action1, action2) -> { try { return action1.getName().compareTo(action2.getName()); @@ -110,14 +112,12 @@ public class CreateHelpTemplateScript extends GhidraScript { return 0; } }; - Collections.sort(actions, comparator); - return actions; + Collections.sort(list, comparator); + return list; } - private Plugin[] getSortedPlugins(PluginTool tool) { + private List getSortedPlugins(PluginTool tool) { List list = tool.getManagedPlugins(); - Plugin[] plugins = new Plugin[list.size()]; - list.toArray(plugins); Comparator comparator = (plugin1, plugin2) -> { try { return plugin1.getName().compareTo(plugin2.getName()); @@ -126,8 +126,9 @@ public class CreateHelpTemplateScript extends GhidraScript { return 0; } }; - Arrays.sort(plugins, comparator); - return plugins; + + Collections.sort(list, comparator); + return list; } } diff --git a/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm b/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm index e26d04ba3b..8a8a117ba7 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/Bookmarks.htm @@ -305,7 +305,7 @@
  1. Select one or more rows in the Bookmarks table.
  2. -
  3. Click the Select Bookmark Locations +
  4. Click the Select Bookmark Locations button in the local toolbar.
  5. The corresponding addresses are selected in the browser.
  6. diff --git a/Ghidra/Features/Base/src/main/help/help/topics/CommentWindowPlugin/comment_window.htm b/Ghidra/Features/Base/src/main/help/help/topics/CommentWindowPlugin/comment_window.htm index a843f5c4e2..3c853d1933 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/CommentWindowPlugin/comment_window.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/CommentWindowPlugin/comment_window.htm @@ -42,7 +42,7 @@ "help/topics/Selection/Selecting.htm">select all of the code units in the Code Browser display corresponding to the selected rows  in the table. Since the table allows for multiple selections, any number of comment items may be selected. To make the selection, either - click on , or right mouse click in the table and choose + click on , or right mouse click in the table and choose Make Selection.

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/CommentWindowPlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/CommentWindowPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/CommentWindowPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataWindowPlugin/data_window.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataWindowPlugin/data_window.htm index 4d1a07da41..7a6a210932 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataWindowPlugin/data_window.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataWindowPlugin/data_window.htm @@ -36,13 +36,13 @@

    Make Selection

    -

    +

    The data window's tool bar has a button that will select all of the code units in the Code Browser display corresponding to the selected rows  in the table. Since the table allows for multiple selections, any number of data items may be selected. To make the selection, either - click on , or right mouse click in the table and choose + click on , or right mouse click in the table and choose Make Selection.

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataWindowPlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/DataWindowPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataWindowPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm b/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm index 6ccf5b336e..b5f09d3c3f 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/function_window.htm @@ -30,12 +30,12 @@ function's signature. Click on the top of a column to sort the list by that col

    Make Selection

    -

    The Functions window has an icon () +

    The Functions window has an icon () on the tool bar to make a selection in the Code Browser. To make a selection,

    1.  Select the functions in the Functions window.
    2. -
    3. Right mouse click and select the Make - Selection option, OR select the button on +
    4. Right mouse click and select the Make + Selection option, OR select the button on the tool bar.
    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/FunctionWindowPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html b/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html index f5522f8dd1..b744b46ac1 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html +++ b/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/Location_References.html @@ -121,7 +121,7 @@ become color filled, as it is here. You may push the button for a refresh in either state.

    -

    The button +

    The button will create a selection in the code browser with the reference entries selected in the table. You may also access this feature by right-clicking an item in the table and selecting Make Selection.

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/LocationReferencesPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ReferencesPlugin/References_from.htm b/Ghidra/Features/Base/src/main/help/help/topics/ReferencesPlugin/References_from.htm index deb80aaa14..6662431e9a 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/ReferencesPlugin/References_from.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/ReferencesPlugin/References_from.htm @@ -924,7 +924,7 @@ single reference row is selected.

    -

    Select +

    Select Memory Reference Destination - With one or more memory references selected in the table, invoking this action will cause the corresponding locations within the Listing to become selected.
    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ReferencesPlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/ReferencesPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/ReferencesPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/RegisterPlugin/Registers.htm b/Ghidra/Features/Base/src/main/help/help/topics/RegisterPlugin/Registers.htm index ed4b18f200..e80f70b2cf 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/RegisterPlugin/Registers.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/RegisterPlugin/Registers.htm @@ -85,7 +85,7 @@

    Deletes the register value associations for all the selected ranges in the table.

    -

    Creates a selection in the browser for all the address +

    Creates a selection in the browser for all the address ranges selected in the register values table.

    Filters out all registers in the register tree that diff --git a/Ghidra/Features/Base/src/main/help/help/topics/RegisterPlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/RegisterPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/RegisterPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ScalarSearchPlugin/The_Scalar_Table.htm b/Ghidra/Features/Base/src/main/help/help/topics/ScalarSearchPlugin/The_Scalar_Table.htm index ecb3e7229f..3875cef1ed 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/ScalarSearchPlugin/The_Scalar_Table.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/ScalarSearchPlugin/The_Scalar_Table.htm @@ -129,7 +129,7 @@

    Make Selection -

    +

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/Query_Results_Dialog.htm b/Ghidra/Features/Base/src/main/help/help/topics/Search/Query_Results_Dialog.htm index bcd0cc6a3c..077316e979 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Search/Query_Results_Dialog.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Search/Query_Results_Dialog.htm @@ -65,7 +65,7 @@ were found.

    -

    Make Selection

    +

    Make Selection

    A selection in the Listing can be created from the entries in the results table. @@ -79,7 +79,7 @@

    1. Click in the results table and press Ctrl+A.
    2. -
    3. Click on the in +
    4. Click on the in the tool bar, or right mouse click and choose Make Selection.
    5. The current selection will be set to the address of each result item.
    6. @@ -93,7 +93,7 @@
    7. Press and hold the right mouse button over the results table.
    8. -
    9. Click on the in +
    10. Click on the in the tool bar, or right mouse click and choose Make Selection.
    11. The current selection will be set to the address of all the highlighted items.
    12. diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_AddressTables.htm b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_AddressTables.htm index b54f7885dc..d46f8f3bf8 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_AddressTables.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_AddressTables.htm @@ -145,7 +145,7 @@
      -

      Make Selection

      +

      Make Selection

      diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_DirectReferences.htm b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_DirectReferences.htm index 51b25b4823..e137c0cd34 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_DirectReferences.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_DirectReferences.htm @@ -133,7 +133,7 @@

      -

      Make Selection

      +

      Make Selection

      diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_Strings.htm b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_Strings.htm index 1d4b3c222a..9b2ac6d82d 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_Strings.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_for_Strings.htm @@ -206,7 +206,7 @@ results.

      -

      Make Selection

      +

      Make Selection

      See Make diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm index 7c41cb9da1..8dcf07cf59 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm @@ -189,7 +189,7 @@ for more discussion on the use of the edit dialog).

      -

      Making a Selection 

      +

      Making a Selection 

      You can make a selection that corresponds to the symbol addresses that are selected in the @@ -205,7 +205,7 @@ Right-mouse-click and select "Make Selection" from the popup menu.

        -
      • Or, click the   button in the Symbol +
      • Or, click the   button in the Symbol Table toolbar.
      diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ViewStringsPlugin/ViewStringsPlugin.htm b/Ghidra/Features/Base/src/main/help/help/topics/ViewStringsPlugin/ViewStringsPlugin.htm index 4ae024f81e..d49b242d8b 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/ViewStringsPlugin/ViewStringsPlugin.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/ViewStringsPlugin/ViewStringsPlugin.htm @@ -53,12 +53,12 @@

      Make Selection

      -

      The Defined Strings window has an icon () +

      The Defined Strings window has an icon () on the tool bar to make a selection in the Code Browser. To make a selection,

      1.  Select the rows containing the desired strings in the table.
      2. -
      3. Right mouse click and select the Make - Selection option, OR select the button on +
      4. Right mouse click and select the Make + Selection option, OR select the button on the tool bar.
      diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ViewStringsPlugin/images/text_align_justify.png b/Ghidra/Features/Base/src/main/help/help/topics/ViewStringsPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/ViewStringsPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java index d4bcfb4fa4..f77905f39c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractFindReferencesDataTypeAction.java @@ -22,6 +22,8 @@ import javax.swing.KeyStroke; import docking.ActionContext; import docking.DockingUtils; +import docking.action.DockingAction; +import docking.action.KeyBindingData; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; import ghidra.framework.plugintool.PluginTool; @@ -29,11 +31,12 @@ import ghidra.program.model.data.Composite; import ghidra.program.model.data.DataType; import ghidra.util.*; -public abstract class AbstractFindReferencesDataTypeAction extends AbstractSharedKeybindingAction { +public abstract class AbstractFindReferencesDataTypeAction extends DockingAction { public static final String NAME = "Find References To"; public static final KeyStroke DEFAULT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_F, DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.SHIFT_DOWN_MASK); + private PluginTool tool; protected AbstractFindReferencesDataTypeAction(PluginTool tool, String name, String owner) { this(tool, name, owner, null); @@ -41,10 +44,13 @@ public abstract class AbstractFindReferencesDataTypeAction extends AbstractShare protected AbstractFindReferencesDataTypeAction(PluginTool tool, String name, String owner, KeyStroke defaultKeyStroke) { - super(tool, name, owner, defaultKeyStroke); + super(name, owner); + this.tool = tool; setHelpLocation(new HelpLocation("LocationReferencesPlugin", "Data_Types")); setDescription("Shows all uses of the selected data type"); + + initKeyStroke(defaultKeyStroke); } protected abstract DataType getDataType(ActionContext context); @@ -55,6 +61,19 @@ public abstract class AbstractFindReferencesDataTypeAction extends AbstractShare return null; } + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; + } + @Override public boolean isEnabledForContext(ActionContext context) { DataType dataType = getDataType(context); @@ -80,17 +99,15 @@ public abstract class AbstractFindReferencesDataTypeAction extends AbstractShare if (field != null && !(baseDataType instanceof Composite)) { Msg.error(this, "Somehow have a field without a Composite parent--searching " + "only for the parent type '" + dataType + "'; field '" + field + "'"); - SystemUtilities.runSwingLater( - () -> service.findAndDisplayAppliedDataTypeAddresses(dataType)); + Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dataType)); return; } if (field == null) { - SystemUtilities.runSwingLater( - () -> service.findAndDisplayAppliedDataTypeAddresses(dataType)); + Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dataType)); } else { - SystemUtilities.runSwingLater(() -> service.findAndDisplayAppliedDataTypeAddresses( + Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses( (Composite) baseDataType, field)); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractSharedKeybindingAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractSharedKeybindingAction.java deleted file mode 100644 index c585bf1c7b..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/actions/AbstractSharedKeybindingAction.java +++ /dev/null @@ -1,83 +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.app.actions; - -import javax.swing.KeyStroke; - -import docking.action.DockingAction; -import docking.action.KeyBindingData; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; - -/** - * An action that can be extended in order to share keybindings. - *

      - * Any group of actions that wish to share a keybinding must all use the same name and - * default keystroke value. - *

      - * As the end-user assigns keybindings, each subclass will update accordingly. - * - * @see DummyKeyBindingsOptionsAction - */ -public abstract class AbstractSharedKeybindingAction extends DockingAction - implements OptionsChangeListener { - - protected PluginTool tool; - - protected AbstractSharedKeybindingAction(PluginTool tool, String name, String owner, - KeyStroke defaultkeyStroke) { - - super(name, owner, false /* not keybinding managed--the dummy handles that */); - this.tool = tool; - - DockingAction action = new DummyKeyBindingsOptionsAction(name, defaultkeyStroke); - tool.addAction(action); - - // setup options to know when the dummy key binding is changed - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke optionsKeyStroke = options.getKeyStroke(action.getFullName(), defaultkeyStroke); - - if (defaultkeyStroke != null) { - if (!defaultkeyStroke.equals(optionsKeyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(optionsKeyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(optionsKeyStroke)); - } - } - else { - if (optionsKeyStroke != null) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(optionsKeyStroke)); - } - } - - - options.addOptionsChangeListener(this); - } - - @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - String actionName = getName(); - if (name.startsWith(actionName)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java index a1e9e64fc2..39fd756f18 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java @@ -834,12 +834,6 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain currentProgram = null; } - tool.removeLocalAction(this, recurseDepthAction); - tool.removeLocalAction(this, refreshAction); - tool.removeLocalAction(this, filterDuplicates); - tool.removeLocalAction(this, navigationOutgoingAction); - tool.removeLocalAction(this, navigateIncomingToggleAction); - recurseDepthAction.dispose(); refreshAction.dispose(); filterDuplicates.dispose(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java index 7b5577b51d..78660773b9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java @@ -15,28 +15,24 @@ */ package ghidra.app.plugin.core.commentwindow; -import javax.swing.ImageIcon; -import javax.swing.KeyStroke; - import docking.ActionContext; -import docking.action.*; +import docking.action.DockingAction; import ghidra.app.CorePluginPackage; import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.services.GoToService; import ghidra.framework.model.*; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.*; -import ghidra.framework.plugintool.util.*; +import ghidra.framework.plugintool.PluginInfo; +import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.model.address.Address; import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.Program; import ghidra.program.util.*; -import ghidra.util.table.GhidraTable; import ghidra.util.table.SelectionNavigationAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; import ghidra.util.task.SwingUpdateManager; -import resources.ResourceManager; /* * This plugin shows a filterable Ghidra table containing all the comments in the active program @@ -53,8 +49,7 @@ import resources.ResourceManager; servicesRequired = { GoToService.class } ) //@formatter:on -public class CommentWindowPlugin extends ProgramPlugin - implements DomainObjectListener, OptionsChangeListener { +public class CommentWindowPlugin extends ProgramPlugin implements DomainObjectListener { private DockingAction selectAction; private CommentWindowProvider provider; @@ -63,12 +58,7 @@ public class CommentWindowPlugin extends ProgramPlugin public CommentWindowPlugin(PluginTool tool) { super(tool, true, true); - reloadUpdateMgr = new SwingUpdateManager(1000, 60000, new Runnable() { - @Override - public void run() { - doReload(); - } - }); + reloadUpdateMgr = new SwingUpdateManager(1000, 60000, () -> doReload()); } @Override @@ -89,12 +79,6 @@ public class CommentWindowPlugin extends ProgramPlugin super.dispose(); } - //////////////////////////////////////////////////////////////////////////// - // - // Implementation of DomainObjectListener - // - //////////////////////////////////////////////////////////////////////////// - private int getCommentType(int type) { if (type == ChangeManager.DOCR_PRE_COMMENT_CHANGED) { return CodeUnit.PRE_COMMENT; @@ -169,9 +153,7 @@ public class CommentWindowPlugin extends ProgramPlugin provider.getComponent().repaint(); } } - } - } private void reload() { @@ -198,39 +180,18 @@ public class CommentWindowPlugin extends ProgramPlugin return currentProgram; } - // Junit access CommentWindowProvider getProvider() { return provider; } - /** - * Create the action objects for this plugin. - */ private void createActions() { - selectAction = new DockingAction("Make Selection", getName(), false) { + selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { @Override - public void actionPerformed(ActionContext context) { + protected void makeSelection(ActionContext context) { selectComment(provider.selectComment()); } - - @Override - public boolean isEnabledForContext(ActionContext context) { - if (!(context instanceof CommentWindowContext)) { - return false; - } - CommentWindowContext commentWindowContext = (CommentWindowContext) context; - GhidraTable table = commentWindowContext.getCommentTable(); - return table.getSelectedRows().length > 0; - } }; - selectAction.setEnabled(false); - ImageIcon icon = ResourceManager.loadImage("images/text_align_justify.png"); - selectAction.setPopupMenuData(new MenuData(new String[] { "Make Selection" }, icon)); - selectAction.setDescription("Selects currently selected comment in table"); - selectAction.setToolBarData(new ToolBarData(icon)); - - installDummyAction(selectAction); tool.addLocalAction(provider, selectAction); @@ -238,34 +199,10 @@ public class CommentWindowPlugin extends ProgramPlugin tool.addLocalAction(provider, selectionAction); } - private void installDummyAction(DockingAction action) { - DummyKeyBindingsOptionsAction dummyAction = - new DummyKeyBindingsOptionsAction(action.getName(), null); - tool.addAction(dummyAction); - - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - - KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null); - if (keyStroke != null) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - if (optionName.startsWith(selectAction.getName())) { - KeyStroke keyStroke = (KeyStroke) newValue; - selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - - void selectComment(ProgramSelection selection) { + private void selectComment(ProgramSelection selection) { ProgramSelectionPluginEvent pspe = new ProgramSelectionPluginEvent("Selection", selection, currentProgram); firePluginEvent(pspe); processEvent(pspe); } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java index ac36db02f2..381a661b98 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java @@ -21,9 +21,7 @@ import java.awt.datatransfer.Transferable; import java.awt.dnd.*; import java.awt.event.*; import java.math.BigInteger; -import java.util.Arrays; -import java.util.EventObject; -import java.util.List; +import java.util.*; import javax.swing.*; import javax.swing.border.Border; @@ -33,10 +31,10 @@ import javax.swing.table.*; import javax.swing.text.JTextComponent; import docking.action.DockingActionIf; +import docking.actions.KeyBindingUtils; import docking.dnd.*; import docking.help.Help; import docking.help.HelpService; -import docking.util.KeyBindingUtils; import docking.widgets.DropDownSelectionTextField; import docking.widgets.OptionDialog; import docking.widgets.fieldpanel.support.FieldRange; @@ -1519,17 +1517,18 @@ public abstract class CompositeEditorPanel extends JPanel @Override public boolean isKeyConsumed(KeyStroke keyStroke) { if (isEditing()) { + // don't let actions through when editing our table return true; } - // don't let actions through when editing our table + // TODO this should no longer be needed return !hasLocalActionForKeyStroke(keyStroke); } private boolean hasLocalActionForKeyStroke(KeyStroke keyStroke) { Plugin plugin = provider.getPlugin(); PluginTool tool = plugin.getTool(); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = tool.getDockingActionsByOwnerName(plugin.getName()); for (DockingActionIf action : actions) { if (!(action instanceof CompositeEditorTableAction)) { continue; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java index ad8107e3e3..090824993d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java @@ -206,11 +206,6 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter @Override public void dispose() { - CompositeEditorTableAction[] allActions = actionMgr.getAllActions(); - for (CompositeEditorTableAction allAction : allActions) { - tool.removeLocalAction(this, allAction); - } - tool.showComponentProvider(this, false); tool.removeComponentProvider(this); for (EditorListener el : listeners) { el.closed(this); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java index ab72912dd6..5b43585514 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CycleGroupAction.java @@ -18,56 +18,39 @@ package ghidra.app.plugin.core.compositeeditor; import javax.swing.KeyStroke; import docking.ActionContext; -import docking.action.DockingAction; import docking.action.KeyBindingData; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.data.CycleGroup; /** - * Action to apply a data type cycle group. - * For use in the composite data type editor. - * This action has help associated with it. + * Action to apply a data type cycle group. For use in the composite data type editor. */ -public class CycleGroupAction extends CompositeEditorTableAction implements OptionsChangeListener { +public class CycleGroupAction extends CompositeEditorTableAction { private final static String GROUP_NAME = CYCLE_ACTION_GROUP; private CycleGroup cycleGroup; - /** - * Creates an action for applying a data type cycle group. - * @param owner the plugin that owns this action - * @param cycleGroup the data type cycle group - */ public CycleGroupAction(CompositeEditorProvider provider, CycleGroup cycleGroup) { super(provider, cycleGroup.getName(), GROUP_NAME, new String[] { "Cycle", cycleGroup.getName() }, new String[] { "Cycle", cycleGroup.getName() }, null); this.cycleGroup = cycleGroup; - // register an action that allows users to edit keystrokes - DockingAction action = new DummyKeyBindingsOptionsAction(cycleGroup.getName(), - cycleGroup.getDefaultKeyStroke()); - tool.addAction(action); - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke defaultKeyStroke = cycleGroup.getDefaultKeyStroke(); - KeyStroke keyStroke = options.getKeyStroke(action.getFullName(), defaultKeyStroke); - options.addOptionsChangeListener(this); - - if (!defaultKeyStroke.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); - } - - adjustEnablement(); + initKeyStroke(cycleGroup.getDefaultKeyStroke()); + } + + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } - /** - * Gets the data type cycle group for this action. - */ public CycleGroup getCycleGroup() { return cycleGroup; } @@ -87,12 +70,4 @@ public class CycleGroupAction extends CompositeEditorTableAction implements Opti public String getHelpName() { return "Cycle"; } - - @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (name.startsWith(cycleGroup.getName())) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java index 8438ad3a27..5e04f76b7b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java @@ -1,6 +1,5 @@ /* ### * 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. @@ -16,11 +15,16 @@ */ package ghidra.app.plugin.core.data; +import java.awt.event.KeyEvent; + +import javax.swing.KeyStroke; + +import docking.ActionContext; +import docking.action.DockingAction; +import docking.action.KeyBindingData; import ghidra.app.context.ListingActionContext; import ghidra.app.util.datatype.DataTypeSelectionDialog; -import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; import ghidra.program.model.data.*; @@ -29,18 +33,10 @@ import ghidra.program.util.*; import ghidra.util.SystemUtilities; import ghidra.util.data.DataTypeParser.AllowedDataTypes; -import java.awt.event.KeyEvent; - -import javax.swing.KeyStroke; - -import docking.ActionContext; -import docking.action.DockingAction; -import docking.action.KeyBindingData; - /** * An action that allows the user to change or select a data type. */ -public class ChooseDataTypeAction extends DockingAction implements OptionsChangeListener { +public class ChooseDataTypeAction extends DockingAction { private DataPlugin plugin; private static final KeyStroke KEY_BINDING = KeyStroke.getKeyStroke(KeyEvent.VK_T, 0); @@ -48,34 +44,22 @@ public class ChooseDataTypeAction extends DockingAction implements OptionsChange public ChooseDataTypeAction(DataPlugin plugin) { super(ACTION_NAME, plugin.getName(), false); - this.plugin = plugin; - PluginTool tool = plugin.getTool(); - DockingAction action = new DummyKeyBindingsOptionsAction(ACTION_NAME, KEY_BINDING); - tool.addAction(action); + initKeyStroke(KEY_BINDING); + } - // setup options to know when the dummy key binding is changed - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke keyStroke = options.getKeyStroke(action.getFullName(), KEY_BINDING); - - if (!KEY_BINDING.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; } - options.addOptionsChangeListener(this); + setKeyBindingData(new KeyBindingData(keyStroke)); } @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (name.startsWith(ACTION_NAME)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } + public boolean usesSharedKeyBinding() { + return true; } @Override @@ -191,9 +175,8 @@ public class ChooseDataTypeAction extends DockingAction implements OptionsChange int defaultPointerSize) { PluginTool tool = plugin.getTool(); Data data = plugin.getDataUnit(context); - DataTypeSelectionDialog selectionDialog = - new DataTypeSelectionDialog(tool, data.getProgram().getDataTypeManager(), maxElements, - AllowedDataTypes.ALL); + DataTypeSelectionDialog selectionDialog = new DataTypeSelectionDialog(tool, + data.getProgram().getDataTypeManager(), maxElements, AllowedDataTypes.ALL); DataType currentDataType = data.getBaseDataType(); selectionDialog.setInitialDataType(currentDataType); tool.showDialog(selectionDialog); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CreateArrayAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CreateArrayAction.java index b865656f6b..552f51ee53 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CreateArrayAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CreateArrayAction.java @@ -27,16 +27,14 @@ import ghidra.app.cmd.data.CreateArrayCmd; import ghidra.app.cmd.data.CreateArrayInStructureCmd; import ghidra.app.context.ListingActionContext; import ghidra.framework.cmd.Command; -import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.util.*; -class CreateArrayAction extends DockingAction implements OptionsChangeListener { +class CreateArrayAction extends DockingAction { private static final KeyStroke DEFAULT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, 0); @@ -51,26 +49,21 @@ class CreateArrayAction extends DockingAction implements OptionsChangeListener { setPopupMenuData(new MenuData(CREATE_ARRAY_POPUP_MENU, "BasicData")); setEnabled(true); - initializeKeybinding(); + + initKeyStroke(DEFAULT_KEY_STROKE); } - private void initializeKeybinding() { - PluginTool tool = plugin.getTool(); - DockingAction dummyKeybindingsAction = - new DummyKeyBindingsOptionsAction(getName(), DEFAULT_KEY_STROKE); - tool.addAction(dummyKeybindingsAction); - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - KeyStroke keyStroke = - options.getKeyStroke(dummyKeybindingsAction.getFullName(), DEFAULT_KEY_STROKE); + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } - if (!DEFAULT_KEY_STROKE.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); - } + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } @Override @@ -342,12 +335,4 @@ class CreateArrayAction extends DockingAction implements OptionsChangeListener { return false; } - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (optionName.startsWith(getName())) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CycleGroupAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CycleGroupAction.java index 0af4c9aaaf..2d6f4fe020 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CycleGroupAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/CycleGroupAction.java @@ -18,13 +18,11 @@ package ghidra.app.plugin.core.data; import javax.swing.KeyStroke; import docking.ActionContext; -import docking.action.*; +import docking.action.DockingAction; +import docking.action.KeyBindingData; import ghidra.app.cmd.data.*; import ghidra.app.context.ListingActionContext; import ghidra.framework.cmd.BackgroundCommand; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.Address; import ghidra.program.model.data.CycleGroup; import ghidra.program.model.data.DataType; @@ -37,48 +35,32 @@ import ghidra.util.Msg; * CycleGroupAction cycles data through a series of data types * defined by a CycleGroup. */ -public class CycleGroupAction extends DockingAction implements OptionsChangeListener { +public class CycleGroupAction extends DockingAction { private DataPlugin plugin; private CycleGroup cycleGroup; - /** - * Creates a new instance of the action. - * - * @param plugin Data Plugin instance - */ CycleGroupAction(CycleGroup group, DataPlugin plugin) { super(group.getName(), plugin.getName(), false); this.plugin = plugin; this.cycleGroup = group; - // register an action that allows users to edit keystrokes - PluginTool tool = plugin.getTool(); - DockingAction action = new DummyKeyBindingsOptionsAction(cycleGroup.getName(), - cycleGroup.getDefaultKeyStroke()); - tool.addAction(action); - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke defaultKeyStroke = cycleGroup.getDefaultKeyStroke(); - KeyStroke keyStroke = options.getKeyStroke(action.getFullName(), defaultKeyStroke); - options.addOptionsChangeListener(this); - - setPopupMenuData( - new MenuData(new String[] { "Data", "Cycle", group.getName() }, null, null)); - - if (!defaultKeyStroke.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); - } - - setEnabled(true); + initKeyStroke(cycleGroup.getDefaultKeyStroke()); + } + + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } - /** - * @see ghidra.framework.plugintool.DockingAction#dispose() - */ @Override public void dispose() { cycleGroup = null; @@ -86,9 +68,6 @@ public class CycleGroupAction extends DockingAction implements OptionsChangeList super.dispose(); } - /* - * @see docking.DockableAction#isValidContext(java.lang.Object) - */ @Override public boolean isEnabledForContext(ActionContext context) { Object contextObject = context.getContextObject(); @@ -192,12 +171,4 @@ public class CycleGroupAction extends DockingAction implements OptionsChangeList } } } - - @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (name.startsWith(cycleGroup.getName())) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataAction.java index f41b37b753..baaf43939a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataAction.java @@ -17,51 +17,46 @@ package ghidra.app.plugin.core.data; import javax.swing.KeyStroke; -import docking.action.*; -import docking.tool.util.DockingToolConstants; +import docking.action.KeyBindingData; +import docking.action.MenuData; import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingContextAction; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; import ghidra.util.HelpLocation; /** * Base class for actions to create data types */ -class DataAction extends ListingContextAction implements OptionsChangeListener { +class DataAction extends ListingContextAction { protected DataType dataType; protected DataPlugin plugin; - private String actionName; - private DummyKeyBindingsOptionsAction dummyKeybindingsAction; public DataAction(DataType dataType, DataPlugin plugin) { this("Define " + dataType.getDisplayName(), "Data", dataType, plugin); } + /** + * Constructor + * + * @param name action name + * @param group the action's group + * @param dataType the data type used by this action + * @param plugin the plugin that owns this action + */ public DataAction(String name, String group, DataType dataType, DataPlugin plugin) { super(name, plugin.getName(), false); - this.actionName = name; this.plugin = plugin; this.dataType = dataType; setPopupMenuData(new MenuData(new String[] { "Data", dataType.getDisplayName() }, group)); assignHelpID(dataType); - - initializeKeybinding(); + initKeyStroke(getDefaultKeyStroke()); } - private void initializeKeybinding() { - PluginTool tool = plugin.getTool(); - dummyKeybindingsAction = - new DummyKeyBindingsOptionsAction(actionName, getDefaultKeyStroke()); - tool.addAction(dummyKeybindingsAction); - ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - KeyStroke keyStroke = - options.getKeyStroke(dummyKeybindingsAction.getFullName(), getDefaultKeyStroke()); - initKeyStroke(keyStroke); + @Override + public boolean usesSharedKeyBinding() { + return true; } protected KeyStroke getDefaultKeyStroke() { @@ -73,12 +68,11 @@ class DataAction extends ListingContextAction implements OptionsChangeListener { return; } - // we don't have a default keybinding, so any value implies user-defined - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); + setKeyBindingData(new KeyBindingData(keyStroke)); } - protected DockingAction getDummyKeyBindingAction() { - return dummyKeybindingsAction; + DataType getDataType() { + return dataType; } @Override @@ -99,19 +93,6 @@ class DataAction extends ListingContextAction implements OptionsChangeListener { return plugin.isCreateDataAllowed(context); } - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (optionName.startsWith(actionName)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - - DataType getDataType() { - return dataType; - } - // Set the help ID according to the data type private void assignHelpID(DataType dt) { String helpID = "Favorites"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToDataTypeAction.java index 5fbf5b37c1..129b68a9b7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToDataTypeAction.java @@ -15,13 +15,9 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; - import javax.swing.tree.TreePath; import docking.ActionContext; -import docking.action.KeyBindingData; import docking.action.MenuData; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; @@ -38,8 +34,6 @@ public class FindReferencesToDataTypeAction extends AbstractFindReferencesDataTy String menuGroup = "ZVeryLast"; // it's own group; on the bottom setPopupMenuData(new MenuData(new String[] { "Find Uses of" }, null, menuGroup)); - setKeyBindingData(new KeyBindingData(KeyEvent.VK_F, - InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java index ce49814f01..e51e9a8082 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java @@ -18,11 +18,8 @@ package ghidra.app.plugin.core.datawindow; import java.util.ArrayList; import java.util.Iterator; -import javax.swing.ImageIcon; -import javax.swing.KeyStroke; - import docking.ActionContext; -import docking.action.*; +import docking.action.DockingAction; import ghidra.app.CorePluginPackage; import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.events.ViewChangedPluginEvent; @@ -31,10 +28,9 @@ import ghidra.app.plugin.ProgramPlugin; import ghidra.app.services.GoToService; import ghidra.app.services.ProgramTreeService; import ghidra.framework.model.*; -import ghidra.framework.options.*; +import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.AddressRangeIterator; import ghidra.program.model.address.AddressSet; import ghidra.program.model.data.DataType; @@ -42,10 +38,9 @@ import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.program.util.*; -import ghidra.util.table.GhidraTable; import ghidra.util.table.SelectionNavigationAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; import ghidra.util.task.SwingUpdateManager; -import resources.ResourceManager; //@formatter:off @PluginInfo( @@ -60,10 +55,7 @@ import resources.ResourceManager; eventsConsumed = { ViewChangedPluginEvent.class } ) //@formatter:on -public class DataWindowPlugin extends ProgramPlugin - implements DomainObjectListener, OptionsChangeListener { - -// private final static String[] DISPLAY_MENU_PATH = { ToolConstants.MENU_VIEW, "Defined Data..." }; +public class DataWindowPlugin extends ProgramPlugin implements DomainObjectListener { private DockingAction selectAction; private FilterAction filterAction; @@ -73,24 +65,12 @@ public class DataWindowPlugin extends ProgramPlugin private SwingUpdateManager reloadUpdateMgr; private boolean resetTypesNeeded; - /////////////////////////////////////////////////////////// - public DataWindowPlugin(PluginTool tool) { super(tool, true, true); - resetUpdateMgr = new SwingUpdateManager(100, 60000, new Runnable() { - @Override - public void run() { - doReset(); - } - }); + resetUpdateMgr = new SwingUpdateManager(100, 60000, () -> doReset()); - reloadUpdateMgr = new SwingUpdateManager(100, 60000, new Runnable() { - @Override - public void run() { - doReload(); - } - }); + reloadUpdateMgr = new SwingUpdateManager(100, 60000, () -> doReload()); } @Override @@ -112,12 +92,6 @@ public class DataWindowPlugin extends ProgramPlugin super.dispose(); } - //////////////////////////////////////////////////////////////////////////// - // - // Implementation of DomainObjectListener - // - //////////////////////////////////////////////////////////////////////////// - @Override public void domainObjectChanged(DomainObjectChangedEvent ev) { if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) { @@ -153,8 +127,6 @@ public class DataWindowPlugin extends ProgramPlugin } } - //////////////////////////////////////////////////////////////////////////// - void reload() { reloadUpdateMgr.update(); } @@ -163,9 +135,6 @@ public class DataWindowPlugin extends ProgramPlugin provider.reload(); } - /* (non-Javadoc) - * @see ghidra.framework.plugintool.Plugin#processEvent(ghidra.framework.plugintool.PluginEvent) - */ @Override public void processEvent(PluginEvent event) { if (event instanceof ViewChangedPluginEvent) { @@ -193,8 +162,6 @@ public class DataWindowPlugin extends ProgramPlugin filterAction.programClosed(); } - //////////////////////////////////////////////////////////////////////////// - Program getProgram() { return currentProgram; } @@ -208,36 +175,17 @@ public class DataWindowPlugin extends ProgramPlugin return provider; } - //////////////////////////////////////////////////////////////////////////// - /** * Create the action objects for this plugin. */ private void createActions() { - selectAction = new DockingAction("Make Selection", getName(), false) { + selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { @Override - public void actionPerformed(ActionContext context) { + protected void makeSelection(ActionContext context) { selectData(provider.selectData()); } - - @Override - public boolean isEnabledForContext(ActionContext context) { - if (!(context instanceof DataWindowContext)) { - return false; - } - DataWindowContext dataWindowContext = (DataWindowContext) context; - GhidraTable table = dataWindowContext.getDataTable(); - return table.getSelectedRows().length > 0; - } }; - selectAction.setEnabled(false); - ImageIcon icon = ResourceManager.loadImage("images/text_align_justify.png"); - selectAction.setPopupMenuData(new MenuData(new String[] { "Make Selection" }, icon)); - selectAction.setDescription("Selects currently selected data in table"); - selectAction.setToolBarData(new ToolBarData(icon)); - - installDummyAction(selectAction); tool.addLocalAction(provider, selectAction); @@ -249,28 +197,6 @@ public class DataWindowPlugin extends ProgramPlugin tool.addLocalAction(provider, selectionAction); } - private void installDummyAction(DockingAction action) { - DummyKeyBindingsOptionsAction dummyAction = - new DummyKeyBindingsOptionsAction(action.getName(), null); - tool.addAction(dummyAction); - - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - - KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null); - if (keyStroke != null) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, Object newValue) { - if (optionName.startsWith(selectAction.getName())) { - KeyStroke keyStroke = (KeyStroke) newValue; - selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - void selectData(ProgramSelection selection) { ProgramSelectionPluginEvent pspe = new ProgramSelectionPluginEvent("Selection", selection, currentProgram); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/editor/TextEditorComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/editor/TextEditorComponentProvider.java index d13d865347..c5d706fa3a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/editor/TextEditorComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/editor/TextEditorComponentProvider.java @@ -32,8 +32,8 @@ import javax.swing.undo.UndoableEdit; import docking.ActionContext; import docking.ComponentProvider; import docking.action.*; +import docking.actions.KeyBindingUtils; import docking.options.editor.FontPropertyEditor; -import docking.util.KeyBindingUtils; import docking.widgets.OptionDialog; import docking.widgets.filechooser.GhidraFileChooser; import ghidra.framework.options.SaveState; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/ChooseDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/ChooseDataTypeAction.java index a764ffd967..da7d0add41 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/ChooseDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/ChooseDataTypeAction.java @@ -23,9 +23,7 @@ import docking.ActionContext; import docking.action.*; import ghidra.app.context.ListingActionContext; import ghidra.app.util.datatype.DataTypeSelectionDialog; -import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.listing.*; @@ -36,7 +34,7 @@ import ghidra.util.data.DataTypeParser.AllowedDataTypes; /** * An action that allows the user to change or select a data type. */ -public class ChooseDataTypeAction extends DockingAction implements OptionsChangeListener { +public class ChooseDataTypeAction extends DockingAction { private static final KeyStroke KEY_BINDING = KeyStroke.getKeyStroke(KeyEvent.VK_T, 0); private final static String ACTION_NAME = "Choose Data Type"; @@ -44,37 +42,24 @@ public class ChooseDataTypeAction extends DockingAction implements OptionsChange public ChooseDataTypeAction(FunctionPlugin plugin) { super(ACTION_NAME, plugin.getName(), false); - this.plugin = plugin; - // setup key binding management - PluginTool tool = plugin.getTool(); - DockingAction action = new DummyKeyBindingsOptionsAction(ACTION_NAME, KEY_BINDING); - tool.addAction(action); - - // setup options to know when the dummy key binding is changed - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke keyStroke = options.getKeyStroke(action.getFullName(), KEY_BINDING); - setPopupMenu(plugin.getDataActionMenuName(null), true); - - if (!KEY_BINDING.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); - } - - options.addOptionsChangeListener(this); setHelpLocation(new HelpLocation("DataTypeEditors", "DataTypeSelectionDialog")); + + initKeyStroke(KEY_BINDING); + } + + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); } @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (name.startsWith(ACTION_NAME)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } + public boolean usesSharedKeyBinding() { + return true; } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CreateArrayAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CreateArrayAction.java index aaac251cb6..d15c0f3a94 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CreateArrayAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CreateArrayAction.java @@ -19,20 +19,18 @@ import java.awt.event.KeyEvent; import javax.swing.KeyStroke; -import docking.action.*; +import docking.action.KeyBindingData; +import docking.action.MenuData; import docking.widgets.dialogs.NumberInputDialog; import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingContextAction; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.data.*; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Variable; import ghidra.program.util.*; import ghidra.util.HelpLocation; -class CreateArrayAction extends ListingContextAction implements OptionsChangeListener { +class CreateArrayAction extends ListingContextAction { private static final KeyStroke DEFAULT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, 0); @@ -44,26 +42,21 @@ class CreateArrayAction extends ListingContextAction implements OptionsChangeLis setPopupMenu(plugin.getDataActionMenuName(null)); setHelpLocation(new HelpLocation(plugin.getName(), "DataType")); - initializeKeybinding(); + + initKeyStroke(DEFAULT_KEY_STROKE); } - private void initializeKeybinding() { - PluginTool tool = plugin.getTool(); - DockingAction dummyKeybindingsAction = - new DummyKeyBindingsOptionsAction(getName(), DEFAULT_KEY_STROKE); - tool.addAction(dummyKeybindingsAction); - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - KeyStroke keyStroke = - options.getKeyStroke(dummyKeybindingsAction.getFullName(), DEFAULT_KEY_STROKE); + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } - if (!DEFAULT_KEY_STROKE.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); - } + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } private void setPopupMenu(String name) { @@ -141,13 +134,4 @@ class CreateArrayAction extends ListingContextAction implements OptionsChangeLis return dialog.getValue(); } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (optionName.startsWith(getName())) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CycleGroupAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CycleGroupAction.java index f9e7ab02ac..6e43b14c09 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CycleGroupAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/CycleGroupAction.java @@ -17,13 +17,11 @@ package ghidra.app.plugin.core.function; import javax.swing.KeyStroke; -import docking.action.*; +import docking.action.KeyBindingData; +import docking.action.MenuData; import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingContextAction; import ghidra.app.util.HelpTopics; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.data.CycleGroup; import ghidra.program.model.data.DataType; import ghidra.program.util.ProgramLocation; @@ -34,42 +32,33 @@ import ghidra.util.HelpLocation; * CycleGroupAction cycles data through a series * of data types defined by a CycleGroup. */ -public class CycleGroupAction extends ListingContextAction implements OptionsChangeListener { +public class CycleGroupAction extends ListingContextAction { private FunctionPlugin plugin; private CycleGroup cycleGroup; - /** - * Creates a new instance of the action. - * - * @param plugin Data Plugin instance - */ CycleGroupAction(CycleGroup group, FunctionPlugin plugin) { super(group.getName(), plugin.getName(), false); this.plugin = plugin; this.cycleGroup = group; + setPopupMenu(plugin.getDataActionMenuName(null), true); - - // register an action that allows users to edit keystrokes - PluginTool tool = plugin.getTool(); - DockingAction action = new DummyKeyBindingsOptionsAction(cycleGroup.getName(), - cycleGroup.getDefaultKeyStroke()); - tool.addAction(action); - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke defaultKeyStroke = cycleGroup.getDefaultKeyStroke(); - KeyStroke keyStroke = options.getKeyStroke(action.getFullName(), defaultKeyStroke); - - if (!defaultKeyStroke.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); - } - - options.addOptionsChangeListener(this); - setHelpLocation(new HelpLocation(HelpTopics.DATA, group.getName())); + + initKeyStroke(cycleGroup.getDefaultKeyStroke()); + } + + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } private void setPopupMenu(String name, boolean isSignatureAction) { @@ -95,9 +84,6 @@ public class CycleGroupAction extends ListingContextAction implements OptionsCha return false; } - /** - * @see ghidra.framework.plugintool.DockingAction#dispose() - */ @Override public void dispose() { cycleGroup = null; @@ -115,12 +101,4 @@ public class CycleGroupAction extends ListingContextAction implements OptionsCha } } } - - @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (name.startsWith(cycleGroup.getName())) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/DataAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/DataAction.java index a9e342f055..d712d91756 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/DataAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/DataAction.java @@ -17,12 +17,10 @@ package ghidra.app.plugin.core.function; import javax.swing.KeyStroke; -import docking.action.*; -import docking.tool.util.DockingToolConstants; +import docking.action.KeyBindingData; +import docking.action.MenuData; import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingContextAction; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.DataType; import ghidra.program.util.ProgramLocation; import ghidra.program.util.VariableLocation; @@ -31,13 +29,11 @@ import ghidra.util.HelpLocation; /** * Base class for actions to create data types */ -class DataAction extends ListingContextAction implements OptionsChangeListener { +class DataAction extends ListingContextAction { private final String group; protected DataType dataType; protected FunctionPlugin plugin; - private String actionName; - private DummyKeyBindingsOptionsAction dummyKeybindingsAction; public DataAction(DataType dataType, FunctionPlugin plugin) { this("Define " + dataType.getDisplayName(), "Function", dataType, plugin); @@ -46,26 +42,19 @@ class DataAction extends ListingContextAction implements OptionsChangeListener { public DataAction(String name, String group, DataType dataType, FunctionPlugin plugin) { super(name, plugin.getName(), false); - this.actionName = name; this.group = group; this.plugin = plugin; this.dataType = dataType; setPopupMenu(plugin.getDataActionMenuName(null), true); setHelpLocation(new HelpLocation(plugin.getName(), "DataType")); - initializeKeybinding(); + + initKeyStroke(getDefaultKeyStroke()); } - private void initializeKeybinding() { - PluginTool tool = plugin.getTool(); - dummyKeybindingsAction = - new DummyKeyBindingsOptionsAction(actionName, getDefaultKeyStroke()); - tool.addAction(dummyKeybindingsAction); - ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - KeyStroke keyStroke = - options.getKeyStroke(dummyKeybindingsAction.getFullName(), getDefaultKeyStroke()); - initKeyStroke(keyStroke); + @Override + public boolean usesSharedKeyBinding() { + return true; } protected KeyStroke getDefaultKeyStroke() { @@ -77,12 +66,7 @@ class DataAction extends ListingContextAction implements OptionsChangeListener { return; } - // we don't have a default keybinding, so any value implies user-defined - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - - protected DockingAction getDummyKeyBindingAction() { - return dummyKeybindingsAction; + setKeyBindingData(new KeyBindingData(keyStroke)); } void setPopupMenu(String name, boolean isSignatureAction) { @@ -98,11 +82,6 @@ class DataAction extends ListingContextAction implements OptionsChangeListener { super.dispose(); } - @Override - public void actionPerformed(ListingActionContext context) { - plugin.createData(dataType, context, true); - } - @Override protected boolean isEnabledForContext(ListingActionContext context) { if (context.hasSelection() || context.getAddress() == null) { @@ -121,11 +100,7 @@ class DataAction extends ListingContextAction implements OptionsChangeListener { } @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (optionName.startsWith(actionName)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } + public void actionPerformed(ListingActionContext context) { + plugin.createData(dataType, context, true); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionSignatureTextField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionSignatureTextField.java index 48a11725e9..f2b0f271ac 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionSignatureTextField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionSignatureTextField.java @@ -1,6 +1,5 @@ /* ### * 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. @@ -28,7 +27,7 @@ import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; class FunctionSignatureTextField extends JTextPane { private static final String ENTER_ACTION_NAME = "ENTER"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsAction.java index 3721b50477..2cd2242edb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/CompareFunctionsAction.java @@ -15,13 +15,9 @@ */ package ghidra.app.plugin.core.functioncompare; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; import java.util.ArrayList; import docking.ActionContext; -import docking.DockingUtils; -import docking.action.KeyBindingData; import docking.action.MenuData; import ghidra.app.context.*; import ghidra.program.model.listing.*; @@ -52,12 +48,6 @@ public class CompareFunctionsAction extends ProgramContextAction { FunctionComparisonPlugin.FUNCTION_MENU_SUBGROUP, MenuData.NO_MNEMONIC, "Z_End" /* See the FunctionPlugin for this value */)); - // TODO this binding needs to be revisited; probably needs to be more modified, like - // Ctrl-Shift-F; it currently interferes with other uses of Ctrl-F, usually when - // performing a find function. - setKeyBindingData(new KeyBindingData(KeyEvent.VK_F, - DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.SHIFT_DOWN_MASK)); - setHelpLocation(new HelpLocation("FunctionComparison", "Compare_Selected_Functions")); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java index 7d94fa0225..40c338757c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java @@ -18,7 +18,6 @@ package ghidra.app.plugin.core.functionwindow; import java.util.List; import javax.swing.ImageIcon; -import javax.swing.KeyStroke; import docking.ActionContext; import docking.action.*; @@ -30,11 +29,9 @@ import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider; import ghidra.app.plugin.core.functioncompare.FunctionComparisonProviderManager; import ghidra.framework.model.*; -import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.Address; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.Symbol; @@ -42,6 +39,7 @@ import ghidra.program.util.*; import ghidra.util.Msg; import ghidra.util.table.GhidraTable; import ghidra.util.table.SelectionNavigationAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; import ghidra.util.task.SwingUpdateManager; import resources.ResourceManager; @@ -55,8 +53,7 @@ import resources.ResourceManager; eventsConsumed = { ProgramClosedPluginEvent.class } ) //@formatter:on -public class FunctionWindowPlugin extends ProgramPlugin - implements DomainObjectListener, OptionsChangeListener { +public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectListener { private DockingAction selectAction; private DockingAction compareAction; @@ -64,19 +61,12 @@ public class FunctionWindowPlugin extends ProgramPlugin private SwingUpdateManager swingMgr; private FunctionComparisonProviderManager functionComparisonManager; - /////////////////////////////////////////////////////////// - public FunctionWindowPlugin(PluginTool tool) { super(tool, true, false); functionComparisonManager = new FunctionComparisonProviderManager(this); - swingMgr = new SwingUpdateManager(1000, new Runnable() { - @Override - public void run() { - provider.reload(); - } - }); + swingMgr = new SwingUpdateManager(1000, () -> provider.reload()); } @@ -98,12 +88,6 @@ public class FunctionWindowPlugin extends ProgramPlugin super.dispose(); } - //////////////////////////////////////////////////////////////////////////// - // - // Implementation of DomainObjectListener - // - //////////////////////////////////////////////////////////////////////////// - @Override public void domainObjectChanged(DomainObjectChangedEvent ev) { if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) { @@ -168,20 +152,18 @@ public class FunctionWindowPlugin extends ProgramPlugin provider.update(function); } break; - /*case ChangeManager.DOCR_SYMBOL_REMOVED: - rec = (ProgramChangeRecord)ev.getChangeRecord(i); - addr = (Address)rec.getObject(); - function = currentProgram.getListing().getFunctionAt(addr); - if (function != null) { - provider.functionChanged(function); - } - break;*/ + /*case ChangeManager.DOCR_SYMBOL_REMOVED: + rec = (ProgramChangeRecord)ev.getChangeRecord(i); + addr = (Address)rec.getObject(); + function = currentProgram.getListing().getFunctionAt(addr); + if (function != null) { + provider.functionChanged(function); + } + break;*/ } } } - //////////////////////////////////////////////////////////////////////////// - @Override protected void programActivated(Program program) { program.addListener(this); @@ -194,17 +176,10 @@ public class FunctionWindowPlugin extends ProgramPlugin provider.programClosed(); } - //////////////////////////////////////////////////////////////////////////// - Program getProgram() { return currentProgram; } - //////////////////////////////////////////////////////////////////////////// - - /** - * Create the action objects for this plugin. - */ private void createActions() { addSelectAction(); addCompareAction(); @@ -214,19 +189,13 @@ public class FunctionWindowPlugin extends ProgramPlugin } private void addSelectAction() { - selectAction = new DockingAction("Make Selection", getName(), false) { + + selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { @Override - public void actionPerformed(ActionContext context) { + protected void makeSelection(ActionContext context) { selectFunctions(provider.selectFunctions()); } }; - selectAction.setEnabled(false); - ImageIcon icon = ResourceManager.loadImage("images/text_align_justify.png"); - selectAction.setPopupMenuData(new MenuData(new String[] { "Make Selection" }, icon)); - selectAction.setDescription("Selects currently selected function(s) in table"); - selectAction.setToolBarData(new ToolBarData(icon)); - - installDummyAction(selectAction); tool.addLocalAction(provider, selectAction); } @@ -238,43 +207,15 @@ public class FunctionWindowPlugin extends ProgramPlugin compareSelectedFunctions(); } }; - compareAction.setEnabled(false); + ImageIcon icon = ResourceManager.loadImage("images/page_white_c.png"); compareAction.setPopupMenuData(new MenuData(new String[] { "Compare Functions" }, icon)); compareAction.setDescription("Compares the currently selected function(s) in the table."); compareAction.setToolBarData(new ToolBarData(icon)); - installDummyAction(compareAction); - tool.addLocalAction(provider, compareAction); } - private void installDummyAction(DockingAction action) { - DummyKeyBindingsOptionsAction dummyAction = - new DummyKeyBindingsOptionsAction(action.getName(), null); - tool.addAction(dummyAction); - - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - - KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null); - if (keyStroke != null) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, Object newValue) { - if (optionName.startsWith(selectAction.getName())) { - KeyStroke keyStroke = (KeyStroke) newValue; - selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - if (optionName.startsWith(compareAction.getName())) { - KeyStroke keyStroke = (KeyStroke) newValue; - compareAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - void setActionsEnabled(boolean enabled) { selectAction.setEnabled(enabled); compareAction.setEnabled(enabled); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/FindReferencesToAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/FindReferencesToAction.java index a14a2a332e..3e637bb5c5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/FindReferencesToAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/FindReferencesToAction.java @@ -17,20 +17,18 @@ package ghidra.app.plugin.core.navigation.locationreferences; import javax.swing.KeyStroke; -import docking.action.*; +import docking.action.KeyBindingData; +import docking.action.MenuData; import ghidra.app.actions.AbstractFindReferencesDataTypeAction; import ghidra.app.context.ListingActionContext; import ghidra.app.context.ListingContextAction; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.Address; import ghidra.program.util.ProgramLocation; /** * {@link LocationReferencesPlugin}'s action for finding references to a thing. */ -public class FindReferencesToAction extends ListingContextAction implements OptionsChangeListener { +public class FindReferencesToAction extends ListingContextAction { private LocationReferencesPlugin plugin; private int subGroupPosition; @@ -44,37 +42,21 @@ public class FindReferencesToAction extends ListingContextAction implements Opti setDescription("Shows references to the item under the cursor"); - // - // Shared keybinding setup - // KeyStroke defaultkeyStroke = AbstractFindReferencesDataTypeAction.DEFAULT_KEY_STROKE; - PluginTool tool = plugin.getTool(); - DockingAction action = new DummyKeyBindingsOptionsAction( - AbstractFindReferencesDataTypeAction.NAME, defaultkeyStroke); - tool.addAction(action); + initKeyStroke(defaultkeyStroke); + } - // setup options to know when the dummy key binding is changed - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke optionsKeyStroke = options.getKeyStroke(action.getFullName(), defaultkeyStroke); - - if (!defaultkeyStroke.equals(optionsKeyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(optionsKeyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(optionsKeyStroke)); + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; } - options.addOptionsChangeListener(this); + setKeyBindingData(new KeyBindingData(keyStroke)); } @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - String actionName = getName(); - if (name.startsWith(actionName)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } + public boolean usesSharedKeyBinding() { + return true; } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java index f7fafc4db4..970b697836 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin.java @@ -35,7 +35,7 @@ import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; import ghidra.util.HelpLocation; import ghidra.util.Msg; -import ghidra.util.table.DeleteTableRowAction; +import ghidra.util.table.actions.DeleteTableRowAction; /** * Plugin to show a list of references to the item represented by the location of the cursor. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java index 18671b107b..c6fb3e222e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java @@ -38,7 +38,9 @@ import ghidra.program.model.address.AddressSet; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; import ghidra.util.HelpLocation; -import ghidra.util.table.*; +import ghidra.util.table.GhidraTable; +import ghidra.util.table.SelectionNavigationAction; +import ghidra.util.table.actions.DeleteTableRowAction; import ghidra.util.task.SwingUpdateManager; import resources.Icons; import resources.ResourceManager; @@ -207,11 +209,6 @@ public class LocationReferencesProvider extends ComponentProviderAdapter tool.removeComponentProvider(this); - tool.removeLocalAction(this, homeAction); - tool.removeLocalAction(this, refreshAction); - tool.removeLocalAction(this, selectionAction); - tool.removeLocalAction(this, highlightAction); - homeAction.dispose(); refreshAction.dispose(); highlightAction.dispose(); @@ -441,7 +438,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter private class DeleteAction extends DeleteTableRowAction { DeleteAction(PluginTool tool, GTable table) { - super(tool, table, locationReferencesPlugin.getName()); + super(table, locationReferencesPlugin.getName()); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/MultiTabPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/MultiTabPanel.java index 2cfdbbcde9..db848d0af6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/MultiTabPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/MultiTabPanel.java @@ -24,7 +24,7 @@ import java.util.Map.Entry; import javax.swing.*; import javax.swing.border.*; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; import docking.widgets.label.GDLabel; import docking.widgets.label.GIconLabel; import generic.util.WindowUtilities; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java index 8f719016f8..f8e19a219f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java @@ -26,8 +26,8 @@ import javax.swing.*; import javax.swing.tree.*; import docking.DockingUtils; +import docking.actions.KeyBindingUtils; import docking.dnd.*; -import docking.util.KeyBindingUtils; import docking.widgets.table.AutoscrollAdapter; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java index f3576565a2..2da9e4d094 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java @@ -30,8 +30,8 @@ import javax.swing.tree.*; import docking.DockingUtils; import docking.action.DockingAction; +import docking.actions.KeyBindingUtils; import docking.dnd.DropTgtAdapter; -import docking.util.KeyBindingUtils; import docking.widgets.JTreeMouseListenerDelegate; import ghidra.app.util.SelectionTransferData; import ghidra.app.util.SelectionTransferable; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/InstructionPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/InstructionPanel.java index a49a831d84..7b76ffc81f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/InstructionPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/InstructionPanel.java @@ -28,9 +28,9 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import docking.action.DockingAction; +import docking.actions.KeyBindingUtils; import docking.dnd.DropTgtAdapter; import docking.dnd.Droppable; -import docking.util.KeyBindingUtils; import docking.widgets.label.GDLabel; import ghidra.app.util.*; import ghidra.app.util.viewer.field.BrowserCodeUnitFormat; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchPlugin.java index ef8d01d5fa..fc2323c378 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchPlugin.java @@ -33,7 +33,7 @@ import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.listing.Program; import ghidra.program.util.ChangeManager; import ghidra.util.HelpLocation; -import ghidra.util.table.DeleteTableRowAction; +import ghidra.util.table.actions.DeleteTableRowAction; import ghidra.util.task.SwingUpdateManager; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java index fe086c071d..ba6809d469 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java @@ -23,16 +23,14 @@ import javax.swing.*; import javax.swing.border.Border; import docking.*; -import docking.action.*; +import docking.action.DockingAction; import docking.help.HelpService; -import docking.tool.util.DockingToolConstants; import docking.widgets.label.GLabel; import docking.widgets.table.GTableFilterPanel; import docking.widgets.table.TableFilter; import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.plugin.core.scalartable.RangeFilterTextField.FilterType; import ghidra.app.services.GoToService; -import ghidra.framework.options.*; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; @@ -40,6 +38,8 @@ import ghidra.program.model.scalar.Scalar; import ghidra.program.util.ProgramSelection; import ghidra.util.HelpLocation; import ghidra.util.table.*; +import ghidra.util.table.actions.DeleteTableRowAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; import resources.ResourceManager; /** @@ -49,8 +49,7 @@ import resources.ResourceManager; *

    13. The range filter that allows the user to filter the scalar table via a min and max value. * */ -public class ScalarSearchProvider extends ComponentProviderAdapter - implements OptionsChangeListener { +public class ScalarSearchProvider extends ComponentProviderAdapter { public static final ImageIcon ICON = ResourceManager.loadImage("images/dataW.gif"); @@ -143,15 +142,6 @@ public class ScalarSearchProvider extends ComponentProviderAdapter return min.equals(Integer.toString(minValue)) && max.equals(Integer.toString(maxValue)); } - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - if (optionName.startsWith(selectAction.getName())) { - KeyStroke keyStroke = (KeyStroke) newValue; - selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - private void selectDataInProgramFromTable(ProgramSelection selection) { ProgramSelectionPluginEvent pspe = new ProgramSelectionPluginEvent("Selection", selection, plugin.getCurrentProgram()); @@ -268,30 +258,12 @@ public class ScalarSearchProvider extends ComponentProviderAdapter private void createActions() { - selectAction = new DockingAction("Make Selection", getName(), false) { + selectAction = new MakeProgramSelectionAction(getName(), getTable()) { @Override - public void actionPerformed(ActionContext context) { + protected void makeSelection(ActionContext context) { selectDataInProgramFromTable(getSelection()); } - - @Override - public boolean isEnabledForContext(ActionContext context) { - if (!(context instanceof ScalarSearchContext)) { - return false; - } - ScalarSearchContext scalarWindowContext = (ScalarSearchContext) context; - GhidraTable table = scalarWindowContext.getScalarTable(); - return table.getSelectedRows().length > 0; - } }; - selectAction.setEnabled(false); - ImageIcon icon = ResourceManager.loadImage("images/text_align_justify.png"); - selectAction.setPopupMenuData(new MenuData(new String[] { "Make Selection" }, icon)); - selectAction.setDescription("Selects currently selected scalar(s) in table"); - selectAction.setToolBarData(new ToolBarData(icon)); - selectAction.setHelpLocation(new HelpLocation(plugin.getName(), "Make_Selection")); - - installDummyAction(selectAction); tool.addLocalAction(this, selectAction); @@ -299,24 +271,10 @@ public class ScalarSearchProvider extends ComponentProviderAdapter tool.addLocalAction(this, selectionAction); GhidraTable table = threadedTablePanel.getTable(); - DockingAction removeItemsAction = new DeleteTableRowAction(tool, table, plugin.getName()); + DockingAction removeItemsAction = new DeleteTableRowAction(table, plugin.getName()); tool.addLocalAction(this, removeItemsAction); } - private void installDummyAction(DockingAction action) { - DummyKeyBindingsOptionsAction dummyAction = - new DummyKeyBindingsOptionsAction(action.getName(), null); - tool.addAction(dummyAction); - - ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - - KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null); - if (keyStroke != null) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - //================================================================================================== // TODO Delete - the custom filtering code below this line needs to be deleted, as it is now // replaced by the column filtering diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java index d904b2f23c..feaf7615f9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java @@ -33,8 +33,7 @@ import generic.jar.ResourceFile; import ghidra.app.script.GhidraScriptUtil; import ghidra.app.script.ScriptInfo; import ghidra.framework.Application; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.util.ToolConstants; import ghidra.util.*; import ghidra.util.task.*; @@ -44,7 +43,6 @@ import utilities.util.FileUtilities; class GhidraScriptActionManager { public static final String RERUN_LAST_SHARED_ACTION_NAME = "Rerun Last Script"; - public static final String GLOBAL_RERUN_LAST_SHARED_ACTION_NAME = "Global Rerun Last Script"; private static final KeyStroke RERUN_LAST_SCRIPT_KEYSTROKE = KeyStroke.getKeyStroke( KeyEvent.VK_R, DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.SHIFT_DOWN_MASK); private static final String SCRIPT_ACTIONS_KEY = "Scripts_Actions_Key"; @@ -120,6 +118,7 @@ class GhidraScriptActionManager { /** * This saves bindings that users have changed. These will overwrite those that may * be defined in the script. + * @param saveState the state into which bindings are saved */ void saveUserDefinedKeybindings(SaveState saveState) { Collection actions = actionMap.values(); @@ -147,6 +146,7 @@ class GhidraScriptActionManager { /** * This saves scripts that not only have keybindings, but that are also marked as "In Tool" * from the GUI. + * @param saveState the state into which the script info is saved */ void saveScriptsThatAreInTool(SaveState saveState) { Set actionScriptFiles = actionMap.keySet(); @@ -194,10 +194,9 @@ class GhidraScriptActionManager { runAction.setEnabled(false); plugin.getTool().addLocalAction(provider, runAction); - runLastAction = new RerunLastScriptAction(RERUN_LAST_SHARED_ACTION_NAME, runGroup); + runLastAction = new RerunLastScriptAction(runGroup); plugin.getTool().addLocalAction(provider, runLastAction); - globalRunLastAction = - new RerunLastScriptAction(GLOBAL_RERUN_LAST_SHARED_ACTION_NAME, "Xtra"); + globalRunLastAction = new RerunLastScriptAction("Xtra"); plugin.getTool().addAction(globalRunLastAction); // @@ -626,41 +625,32 @@ class GhidraScriptActionManager { } - private class RerunLastScriptAction extends DockingAction implements OptionsChangeListener { + private class RerunLastScriptAction extends DockingAction { - RerunLastScriptAction(String actionName, String toolbarGroup) { - super(actionName, plugin.getName(), false); + RerunLastScriptAction(String toolbarGroup) { + super(RERUN_LAST_SHARED_ACTION_NAME, plugin.getName(), false); setToolBarData( new ToolBarData(ResourceManager.loadImage("images/play_again.png"), toolbarGroup)); setDescription("Rerun the last run script"); setEnabled(false); - DockingAction action = new DummyKeyBindingsOptionsAction(RERUN_LAST_SHARED_ACTION_NAME, - RERUN_LAST_SCRIPT_KEYSTROKE); - PluginTool tool = plugin.getTool(); - tool.addAction(action); - - // setup options to know when the dummy key binding is changed - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke keyStroke = - options.getKeyStroke(getFullSharedActionName(), RERUN_LAST_SCRIPT_KEYSTROKE); - - if (!RERUN_LAST_SCRIPT_KEYSTROKE.equals(keyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(keyStroke)); - } - - options.addOptionsChangeListener(this); - setHelpLocation(new HelpLocation(plugin.getName(), "Run_Last")); + + initKeyStroke(RERUN_LAST_SCRIPT_KEYSTROKE); } - private String getFullSharedActionName() { - return RERUN_LAST_SHARED_ACTION_NAME + " (Tool)"; + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } @Override @@ -672,15 +662,5 @@ class GhidraScriptActionManager { public boolean isEnabledForContext(ActionContext context) { return provider.getLastRunScript() != null; } - - @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, - Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - if (name.startsWith(RERUN_LAST_SHARED_ACTION_NAME)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java index 966f0e3b47..acc70b91eb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java @@ -29,8 +29,8 @@ import javax.swing.undo.UndoableEdit; import docking.*; import docking.action.*; +import docking.actions.KeyBindingUtils; import docking.options.editor.FontPropertyEditor; -import docking.util.KeyBindingUtils; import docking.widgets.OptionDialog; import generic.jar.ResourceFile; import ghidra.app.script.GhidraScriptUtil; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java index 450547e47e..d778118e61 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java @@ -16,11 +16,9 @@ package ghidra.app.plugin.core.strings; import javax.swing.ImageIcon; -import javax.swing.KeyStroke; import docking.ActionContext; import docking.action.*; -import docking.tool.util.DockingToolConstants; import ghidra.app.CorePluginPackage; import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.plugin.PluginCategoryNames; @@ -28,7 +26,6 @@ import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.core.data.DataSettingsDialog; import ghidra.app.services.GoToService; import ghidra.framework.model.*; -import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; @@ -38,9 +35,9 @@ import ghidra.program.util.*; import ghidra.util.HelpLocation; import ghidra.util.exception.CancelledException; import ghidra.util.table.SelectionNavigationAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; import ghidra.util.task.SwingUpdateManager; import resources.Icons; -import resources.ResourceManager; /** * Plugin that provides the "Defined Strings" table, where all the currently defined @@ -58,8 +55,7 @@ import resources.ResourceManager; servicesRequired = { GoToService.class } ) //@formatter:on -public class ViewStringsPlugin extends ProgramPlugin - implements DomainObjectListener, OptionsChangeListener { +public class ViewStringsPlugin extends ProgramPlugin implements DomainObjectListener { private DockingAction selectAction; private DockingAction showSettingsAction; @@ -104,24 +100,13 @@ public class ViewStringsPlugin extends ProgramPlugin refreshAction.setHelpLocation(new HelpLocation("ViewStringsPlugin", "Refresh")); tool.addLocalAction(provider, refreshAction); - selectAction = new DockingAction("Make Selection", getName(), false) { + selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { + @Override - public void actionPerformed(ActionContext context) { + protected void makeSelection(ActionContext context) { selectData(provider.selectData()); } - - @Override - public boolean isEnabledForContext(ActionContext context) { - return provider.getSelectedRowCount() > 0; - } }; - ImageIcon selectActionIcon = ResourceManager.loadImage("images/text_align_justify.png"); - selectAction.setPopupMenuData( - new MenuData(new String[] { "Make Selection" }, selectActionIcon)); - selectAction.setDescription("Selects currently selected data in table"); - selectAction.setToolBarData(new ToolBarData(selectActionIcon)); - - installDummyAction(selectAction); tool.addLocalAction(provider, selectAction); @@ -175,29 +160,6 @@ public class ViewStringsPlugin extends ProgramPlugin } - private void installDummyAction(DockingAction action) { - DummyKeyBindingsOptionsAction dummyAction = - new DummyKeyBindingsOptionsAction(action.getName(), null); - tool.addAction(dummyAction); - - ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - - KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null); - if (keyStroke != null) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - if (optionName.startsWith(selectAction.getName())) { - KeyStroke keyStroke = (KeyStroke) newValue; - selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - private void selectData(ProgramSelection selection) { ProgramSelectionPluginEvent pspe = new ProgramSelectionPluginEvent("Selection", selection, currentProgram); @@ -245,7 +207,6 @@ public class ViewStringsPlugin extends ProgramPlugin else if (ev.containsEvent(ChangeManager.DOCR_CODE_ADDED)) { for (int i = 0; i < ev.numRecords(); ++i) { DomainObjectChangeRecord doRecord = ev.getChangeRecord(i); - Object oldValue = doRecord.getOldValue(); Object newValue = doRecord.getNewValue(); switch (doRecord.getEventType()) { case ChangeManager.DOCR_CODE_REMOVED: diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/ShowSymbolReferencesAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/ShowSymbolReferencesAction.java index f754612628..b98eed71eb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/ShowSymbolReferencesAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/ShowSymbolReferencesAction.java @@ -16,28 +16,26 @@ package ghidra.app.plugin.core.symboltree.actions; import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; import javax.swing.tree.TreePath; -import docking.action.*; +import docking.action.KeyBindingData; +import docking.action.MenuData; import ghidra.app.actions.AbstractFindReferencesDataTypeAction; import ghidra.app.nav.Navigatable; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService; import ghidra.app.plugin.core.symboltree.SymbolTreeActionContext; import ghidra.app.plugin.core.symboltree.nodes.*; import ghidra.app.services.CodeViewerService; -import ghidra.framework.options.*; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.ServiceListener; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.database.symbol.FunctionSymbol; import ghidra.program.model.symbol.Symbol; import ghidra.program.util.FunctionSignatureFieldLocation; import ghidra.program.util.ProgramLocation; import ghidra.util.Msg; +import ghidra.util.Swing; -public class ShowSymbolReferencesAction extends SymbolTreeContextAction - implements OptionsChangeListener { +public class ShowSymbolReferencesAction extends SymbolTreeContextAction { private PluginTool tool; @@ -53,7 +51,7 @@ public class ShowSymbolReferencesAction extends SymbolTreeContextAction public void serviceAdded(Class interfaceClass, Object service) { if (interfaceClass.equals(LocationReferencesService.class)) { setHelpLocation(((LocationReferencesService) service).getHelpLocation()); - SwingUtilities.invokeLater(() -> tool.removeServiceListener(this)); + Swing.runLater(() -> tool.removeServiceListener(this)); } } }; @@ -66,27 +64,21 @@ public class ShowSymbolReferencesAction extends SymbolTreeContextAction installHelpLocation(); - // - // Shared keybinding setup - // KeyStroke defaultkeyStroke = AbstractFindReferencesDataTypeAction.DEFAULT_KEY_STROKE; - DockingAction action = new DummyKeyBindingsOptionsAction( - AbstractFindReferencesDataTypeAction.NAME, defaultkeyStroke); - tool.addAction(action); + initKeyStroke(defaultkeyStroke); + } - // setup options to know when the dummy key binding is changed - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke optionsKeyStroke = options.getKeyStroke(action.getFullName(), defaultkeyStroke); - - if (!defaultkeyStroke.equals(optionsKeyStroke)) { - // user-defined keystroke - setUnvalidatedKeyBindingData(new KeyBindingData(optionsKeyStroke)); - } - else { - setKeyBindingData(new KeyBindingData(optionsKeyStroke)); + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; } - options.addOptionsChangeListener(this); + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } private void installHelpLocation() { @@ -102,15 +94,6 @@ public class ShowSymbolReferencesAction extends SymbolTreeContextAction setHelpLocation(locationReferencesService.getHelpLocation()); } - @Override - public void optionsChanged(ToolOptions options, String name, Object oldValue, Object newValue) { - KeyStroke keyStroke = (KeyStroke) newValue; - String actionName = getName(); - if (name.startsWith(actionName)) { - setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - @Override protected boolean isEnabledForContext(SymbolTreeActionContext context) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java index fc6a30e273..2db72d136a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java @@ -69,7 +69,7 @@ class SymbolProvider extends ComponentProviderAdapter { for (SymbolRowObject obj : rowObjects) { symbolIDs[index++] = obj.getKey(); } - return new ProgramSymbolActionContext(this, program, symbolIDs); + return new ProgramSymbolActionContext(this, program, symbolIDs, getTable()); } void deleteSymbols() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java index 34323df1d0..e11de4db46 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java @@ -20,7 +20,6 @@ import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; -import javax.swing.KeyStroke; import docking.ActionContext; import docking.action.*; @@ -33,9 +32,9 @@ import ghidra.app.services.BlockModelService; import ghidra.app.services.GoToService; import ghidra.app.util.SymbolInspector; import ghidra.framework.model.*; -import ghidra.framework.options.*; +import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.*; -import ghidra.framework.plugintool.util.*; +import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; @@ -45,6 +44,7 @@ import ghidra.program.util.ChangeManager; import ghidra.program.util.ProgramChangeRecord; import ghidra.util.table.GhidraTable; import ghidra.util.table.SelectionNavigationAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; import ghidra.util.task.SwingUpdateManager; import resources.Icons; import resources.ResourceManager; @@ -69,10 +69,7 @@ import resources.ResourceManager; eventsConsumed = { ProgramActivatedPluginEvent.class } ) //@formatter:on -public class SymbolTablePlugin extends Plugin - implements DomainObjectListener, OptionsChangeListener { - - private static final String PLUGIN_NAME = "SymbolTablePlugin"; +public class SymbolTablePlugin extends Plugin implements DomainObjectListener { final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR); final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); @@ -437,32 +434,14 @@ public class SymbolTablePlugin extends Plugin DockingAction editExternalLocationAction = new EditExternalLocationAction(this); tool.addLocalAction(symProvider, editExternalLocationAction); - makeSelectionAction = new DockingAction("Make Selection", getName(), false) { + makeSelectionAction = new MakeProgramSelectionAction(getName(), symProvider.getTable()) { @Override - public void actionPerformed(ActionContext context) { + protected void makeSelection(ActionContext context) { symProvider.makeSelection(); } - - @Override - public boolean isEnabledForContext(ActionContext context) { - GhidraTable table = symProvider.getTable(); - return table.getSelectedRowCount() > 0; - } - - @Override - public boolean isAddToPopup(ActionContext context) { - return true; - } }; - icon = ResourceManager.loadImage("images/text_align_justify.png"); - makeSelectionAction.setPopupMenuData( - new MenuData(new String[] { "Make Selection" }, icon, popupGroup)); - makeSelectionAction.setToolBarData(new ToolBarData(icon)); - makeSelectionAction.setDescription("Make a selection using selected Symbol addresses"); - makeSelectionAction.setEnabled(false); - - installDummyAction(makeSelectionAction); + makeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup); tool.addLocalAction(symProvider, makeSelectionAction); @@ -478,8 +457,6 @@ public class SymbolTablePlugin extends Plugin } }; icon = Icons.CONFIGURE_FILTER_ICON; - setFilterAction.setPopupMenuData( - new MenuData(new String[] { "Configure Symbol Filter" }, icon, popupGroup)); setFilterAction.setToolBarData(new ToolBarData(icon)); setFilterAction.setDescription("Configure Symbol Filter"); @@ -505,29 +482,6 @@ public class SymbolTablePlugin extends Plugin tool.addAction(clearPinnedAction); } - private void installDummyAction(DockingAction action) { - DummyKeyBindingsOptionsAction dummyAction = - new DummyKeyBindingsOptionsAction(action.getName(), null); - tool.addAction(dummyAction); - - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - options.addOptionsChangeListener(this); - - KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null); - if (keyStroke != null) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - if (optionName.startsWith(makeSelectionAction.getName())) { - KeyStroke keyStroke = (KeyStroke) newValue; - makeSelectionAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - private void createRefActions() { referencesToAction = new ToggleDockingAction("References To", getName()) { @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java index 8cf183c04f..884cc0c930 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java @@ -27,7 +27,8 @@ import javax.swing.event.TableModelListener; import docking.ActionContext; import docking.ComponentProviderActivationListener; -import docking.action.*; +import docking.action.DockingAction; +import docking.action.MenuData; import docking.widgets.table.AbstractSortedTableModel; import docking.widgets.table.GTable; import docking.widgets.table.threaded.GThreadedTablePanel; @@ -35,21 +36,19 @@ import ghidra.app.nav.Navigatable; import ghidra.app.nav.NavigatableRemovalListener; import ghidra.app.services.*; import ghidra.app.util.HelpTopics; -import ghidra.framework.options.OptionsChangeListener; -import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.Plugin; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.program.util.*; import ghidra.util.HelpLocation; -import ghidra.util.SystemUtilities; import ghidra.util.table.*; +import ghidra.util.table.actions.DeleteTableRowAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; import resources.ResourceManager; public class TableComponentProvider extends ComponentProviderAdapter - implements TableModelListener, NavigatableRemovalListener, OptionsChangeListener { + implements TableModelListener, NavigatableRemovalListener { private JPanel componentPanel; private GhidraThreadedTablePanel threadedPanel; @@ -158,28 +157,17 @@ public class TableComponentProvider extends ComponentProviderAdapter } private void createActions(final Plugin plugin) { - selectAction = new DockingAction(TableServicePlugin.MAKE_SELECTION_ACTION_NAME, - tableServicePlugin.getName(), false) { - @Override - public void actionPerformed(ActionContext context) { - makeSelection(plugin); - } + GhidraTable table = threadedPanel.getTable(); + selectAction = new MakeProgramSelectionAction(tableServicePlugin.getName(), table) { @Override - public boolean isEnabledForContext(ActionContext context) { - GhidraTable table = threadedPanel.getTable(); - return table.getSelectedRowCount() > 0; + protected void makeSelection(ActionContext context) { + doMakeSelection(plugin); } }; - selectAction.setDescription("Make a selection using selected rows"); - selectAction.setEnabled(false); - - ImageIcon icon = ResourceManager.loadImage("images/text_align_justify.png"); - selectAction.setToolBarData(new ToolBarData(icon, null)); - selectAction.setPopupMenuData(new MenuData(new String[] { "Make Selection" }, icon, null)); selectAction.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Make_Selection")); - selectionNavigationAction = new SelectionNavigationAction(plugin, threadedPanel.getTable()); + selectionNavigationAction = new SelectionNavigationAction(plugin, table); selectionNavigationAction.setHelpLocation( new HelpLocation(HelpTopics.SEARCH, "Selection_Navigation")); @@ -187,21 +175,20 @@ public class TableComponentProvider extends ComponentProviderAdapter new DockingAction("Go to External Location", getName(), false) { @Override public void actionPerformed(ActionContext context) { - gotoExternalAddress(getSlectedExternalAddress()); + gotoExternalAddress(getSelectedExternalAddress()); } @Override public boolean isEnabledForContext(ActionContext context) { - return getSlectedExternalAddress() != null && + return getSelectedExternalAddress() != null && tool.getService(GoToService.class) != null; } - private Address getSlectedExternalAddress() { - GhidraTable table = threadedPanel.getTable(); + private Address getSelectedExternalAddress() { if (table.getSelectedRowCount() != 1) { return null; } - ProgramSelection selection = threadedPanel.getTable().getProgramSelection(); + ProgramSelection selection = table.getProgramSelection(); Program modelProgram = model.getProgram(); if (modelProgram == null || selection.getNumAddresses() != 1) { return null; @@ -213,17 +200,14 @@ public class TableComponentProvider extends ComponentProviderAdapter externalGotoAction.setDescription("Go to an external location"); externalGotoAction.setEnabled(false); - icon = ResourceManager.loadImage("images/searchm_obj.gif"); + Icon icon = ResourceManager.loadImage("images/searchm_obj.gif"); externalGotoAction.setPopupMenuData( new MenuData(new String[] { "GoTo External Location" }, icon, null)); externalGotoAction.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Navigation")); - configureKeybinding(selectAction, null); - plugin.getTool().addLocalAction(this, selectAction); plugin.getTool().addLocalAction(this, selectionNavigationAction); plugin.getTool().addLocalAction(this, externalGotoAction); - } public void installRemoveItemsAction() { @@ -232,38 +216,11 @@ public class TableComponentProvider extends ComponentProviderAdapter } GhidraTable table = threadedPanel.getTable(); - removeItemsAction = new DeleteTableRowAction(tool, table, tableServicePlugin.getName()); + removeItemsAction = new DeleteTableRowAction(table, tableServicePlugin.getName()); tool.addLocalAction(this, removeItemsAction); } - private void configureKeybinding(DockingAction action, KeyStroke keyBinding) { - // setup options to know when the dummy key binding is changed - ToolOptions options = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke keyStroke = options.getKeyStroke( - action.getName() + TableServicePlugin.SHARED_ACTION_OWNER_SUFFIX, keyBinding); - - if (!SystemUtilities.isEqual(keyBinding, keyStroke)) { - // user-defined keystroke - action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - else { - action.setKeyBindingData(new KeyBindingData(keyStroke)); - } - - options.removeOptionsChangeListener(this); // don't double add - options.addOptionsChangeListener(this); - } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - if (optionName.startsWith(TableServicePlugin.MAKE_SELECTION_ACTION_NAME)) { - KeyStroke keyStroke = (KeyStroke) newValue; - selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); - } - } - private JPanel createFilterFieldPanel(JTable table, AbstractSortedTableModel sortedModel) { tableFilterPanel = new GhidraTableFilterPanel<>(table, sortedModel); tableFilterPanel.setToolTipText("Filter search results"); @@ -309,7 +266,7 @@ public class TableComponentProvider extends ComponentProviderAdapter } } - private void makeSelection(Plugin plugin) { + private void doMakeSelection(Plugin plugin) { ProgramSelection selection = threadedPanel.getTable().getProgramSelection(); Program modelProgram = model.getProgram(); if (modelProgram == null || selection.getNumAddresses() == 0) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java index b38559ce1b..55bb94b665 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java @@ -33,12 +33,11 @@ import ghidra.app.tablechooser.TableChooserExecutor; import ghidra.app.util.query.TableService; import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.framework.model.DomainObjectListener; -import ghidra.framework.options.DummyKeyBindingsOptionsAction; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.model.listing.Program; -import ghidra.util.table.DeleteTableRowAction; import ghidra.util.table.GhidraProgramTableModel; +import ghidra.util.table.actions.DeleteTableRowAction; import ghidra.util.task.SwingUpdateManager; //@formatter:off @@ -62,21 +61,14 @@ public class TableServicePlugin extends ProgramPlugin static final String SHARED_ACTION_OWNER_SUFFIX = " (Tool)"; private SwingUpdateManager updateMgr; - private Map>> programMap = - new HashMap>>(); + private Map>> programMap = new HashMap<>(); - private Map> programToDialogMap = - new HashMap>(); + private Map> programToDialogMap = new HashMap<>(); public TableServicePlugin(PluginTool tool) { super(tool, false, false); - updateMgr = new SwingUpdateManager(1000, new Runnable() { - @Override - public void run() { - updateProviders(); - } - }); + updateMgr = new SwingUpdateManager(1000, () -> updateProviders()); createActions(); } @@ -89,12 +81,6 @@ public class TableServicePlugin extends ProgramPlugin // providers are created, as they would only appear in the options at // that point. // - - DummyKeyBindingsOptionsAction dummyMakeSelectionAction = - new DummyKeyBindingsOptionsAction(MAKE_SELECTION_ACTION_NAME, null); - - tool.addAction(dummyMakeSelectionAction); - DeleteTableRowAction.registerDummy(tool); } @@ -105,9 +91,6 @@ public class TableServicePlugin extends ProgramPlugin super.dispose(); } - /* (non-Javadoc) - * @see ghidra.framework.plugintool.Plugin#processEvent(ghidra.framework.plugintool.PluginEvent) - */ @Override public void processEvent(PluginEvent event) { if (event instanceof ProgramClosedPluginEvent) { @@ -140,7 +123,7 @@ public class TableServicePlugin extends ProgramPlugin return; } // make a copy of the list because the provider updates the list - List> list = new ArrayList>(plist); + List> list = new ArrayList<>(plist); for (int i = 0; i < list.size(); i++) { ComponentProvider provider = list.get(i); provider.closeComponent(); @@ -154,7 +137,7 @@ public class TableServicePlugin extends ProgramPlugin return; } // make a copy of the list because the dialog updates the list - List list = new ArrayList(dlist); + List list = new ArrayList<>(dlist); for (int i = 0; i < list.size(); i++) { TableChooserDialog dialog = list.get(i); dialog.close(); @@ -174,9 +157,8 @@ public class TableServicePlugin extends ProgramPlugin Program program = model.getProgram(); - TableComponentProvider cp = - new TableComponentProvider(this, title, tableTypeName, model, - program.getDomainFile().getName(), gotoService, windowSubMenu, navigatable); + TableComponentProvider cp = new TableComponentProvider<>(this, title, tableTypeName, + model, program.getDomainFile().getName(), gotoService, windowSubMenu, navigatable); addProvider(program, cp); return cp; } @@ -195,10 +177,9 @@ public class TableServicePlugin extends ProgramPlugin MarkerService markerService = tool.getService(MarkerService.class); Program program = model.getProgram(); - TableComponentProvider cp = - new TableComponentProvider(this, title, tableTypeName, model, - program.getDomainFile().getName(), gotoService, markerService, markerColor, - markerIcon, windowSubMenu, navigatable); + TableComponentProvider cp = new TableComponentProvider<>(this, title, tableTypeName, + model, program.getDomainFile().getName(), gotoService, markerService, markerColor, + markerIcon, windowSubMenu, navigatable); addProvider(program, cp); return cp; } @@ -206,7 +187,7 @@ public class TableServicePlugin extends ProgramPlugin private void addProvider(Program program, TableComponentProvider provider) { List> list = programMap.get(program); if (list == null) { - list = new ArrayList>(); + list = new ArrayList<>(); programMap.put(program, list); } list.add(provider); @@ -241,7 +222,7 @@ public class TableServicePlugin extends ProgramPlugin } private List> getProviders() { - List> clist = new ArrayList>(); + List> clist = new ArrayList<>(); Iterator>> iter = programMap.values().iterator(); while (iter.hasNext()) { List> list = iter.next(); @@ -280,7 +261,7 @@ public class TableServicePlugin extends ProgramPlugin List list = programToDialogMap.get(program); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); programToDialogMap.put(program, list); } list.add(dialog); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/JavaHelpPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/JavaHelpPlugin.java index 43754a02b4..b6a9a83882 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/JavaHelpPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/JavaHelpPlugin.java @@ -25,12 +25,12 @@ import javax.swing.JButton; import docking.*; import docking.action.DockingAction; import docking.action.MenuData; +import docking.actions.SharedStubKeyBindingAction; import docking.help.*; import ghidra.app.DeveloperPluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.framework.main.FrontEndable; import ghidra.framework.model.Project; -import ghidra.framework.options.DummyKeyBindingsOptionsAction; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.ToolConstants; @@ -191,7 +191,7 @@ public class JavaHelpPlugin extends Plugin implements FrontEndable { private boolean shouldSkipHelpCheck(DockingAction action) { String actionName = action.getOwner() + " - " + action.getName(); - if (action instanceof DummyKeyBindingsOptionsAction) { + if (action instanceof SharedStubKeyBindingAction) { return true; } if (noHelpActions.contains(actionName)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java index 3da662eff0..85f3856976 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java @@ -93,7 +93,6 @@ public class PropertyManagerProvider extends ComponentProviderAdapter { } void dispose() { - tool.removeLocalAction(this, deleteAction); tool.removeComponentProvider(this); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/TitledPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/TitledPanel.java index d99bccbf48..1b2e2b928d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/TitledPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/TitledPanel.java @@ -28,7 +28,7 @@ import docking.widgets.label.GDLabel; * components (usually icon buttons) */ public class TitledPanel extends JPanel { - private JLabel title; + private JLabel title; // GDLabel or GHtmlLabel private JPanel titlePanel; private JPanel iconPanel; private JComponent bottomComp; @@ -37,16 +37,27 @@ public class TitledPanel extends JPanel { /** * Creates a new TitlePanel * @param name the name of the panel - * @param panel the component to wrap. - * @param margin the size of the margin to use. + * @param panel the component to wrap + * @param margin the size of the margin to use */ public TitledPanel(String name, JComponent panel, int margin) { + this(new GDLabel(name), panel, margin); + } + + /** + * Creates a new TitlePanel + * + * @param titleLabel the title label for the panel; this allow clients to provide HTML-based + * title text. Note: it is up to the client to escape this text as needed for safety + * @param panel the component to wrap + * @param margin the size of the margin to use + */ + public TitledPanel(JLabel titleLabel, JComponent panel, int margin) { super(new BorderLayout()); titlePanel = new JPanel(new BorderLayout()); iconPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 4, 1)); iconPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - title = new GDLabel(name); - title.setToolTipText(name); + title = titleLabel; JLabel filler = new GDLabel(); filler.setPreferredSize(new Dimension(margin, filler.getPreferredSize().height)); titlePanel.add(filler, BorderLayout.WEST); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java index e5b56ea77d..ad55183d67 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadedIntegrationTest.java @@ -15,21 +15,20 @@ */ package ghidra.test; -import java.awt.Window; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.Iterator; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import docking.*; +import docking.DialogComponentProvider; import docking.action.DockingActionIf; import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.listener.FieldMouseListener; import docking.widgets.fieldpanel.support.FieldLocation; import ghidra.GhidraTestApplicationLayout; -import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.framework.ApplicationConfiguration; import ghidra.framework.GhidraApplicationConfiguration; @@ -41,10 +40,10 @@ import ghidra.program.model.listing.Program; import ghidra.util.TaskUtilities; import ghidra.util.exception.AssertException; import junit.framework.AssertionFailedError; -import util.CollectionUtils; import utility.application.ApplicationLayout; -public abstract class AbstractGhidraHeadedIntegrationTest extends AbstractGhidraHeadlessIntegrationTest { +public abstract class AbstractGhidraHeadedIntegrationTest + extends AbstractGhidraHeadlessIntegrationTest { public AbstractGhidraHeadedIntegrationTest() { super(); @@ -113,42 +112,6 @@ public abstract class AbstractGhidraHeadedIntegrationTest extends AbstractGhidra return null; } - /** - * Finds the action by the given owner name and action name. - * If you do not know the owner name, then use - * the call {@link #getActions(DockingTool, String)} instead. - * - *

      Note: more specific test case subclasses provide other methods for finding actions - * when you have an owner name (which is usually the plugin name). - * - * @param tool the tool containing all system actions - * @param name the name to match - * @return the matching action; null if no matching action can be found - */ - public static DockingActionIf getAction(DockingTool tool, String owner, String name) { - String fullName = name + " (" + owner + ")"; - List actions = tool.getDockingActionsByFullActionName(fullName); - if (actions.isEmpty()) { - return null; - } - - if (actions.size() > 1) { - // This shouldn't happen - throw new AssertionFailedError( - "Found more than one action for name '" + fullName + "'"); - } - - return CollectionUtils.any(actions); - } - - public static DockingActionIf getAction(Plugin plugin, String actionName) { - return getAction(plugin.getTool(), plugin.getName(), actionName); - } - - public static DockingActionIf getLocalAction(ComponentProvider provider, String actionName) { - return getAction(provider.getTool(), provider.getName(), actionName); - } - public static PluginTool showTool(final PluginTool tool) { runSwing(() -> { boolean wasErrorGUIEnabled = isUseErrorGUI(); @@ -162,9 +125,7 @@ public abstract class AbstractGhidraHeadedIntegrationTest extends AbstractGhidra /** * Shows the given DialogComponentProvider using the given tool's - * {@link PluginTool#showDialog(DialogComponentProvider)} method. After calling show on a - * new thread the method will then wait for the dialog to be shown by calling - * {@link TestEnv#waitForDialogComponent(Window, Class, int)}. + * {@link PluginTool#showDialog(DialogComponentProvider)} method. * * @param tool The tool used to show the given provider. * @param provider The DialogComponentProvider to show. @@ -206,22 +167,8 @@ public abstract class AbstractGhidraHeadedIntegrationTest extends AbstractGhidra waitForSwing(); } - /** - * @deprecated use {@link #waitForBusyTool(PluginTool)} instead - */ - @Deprecated - public static void waitForAnalysis() { - @SuppressWarnings("unchecked") - Map programToManagersMap = - (Map) getInstanceField("managerMap", - AutoAnalysisManager.class); - - Collection managers = programToManagersMap.values(); - for (AutoAnalysisManager manager : managers) { - while (manager.isAnalyzing()) { - sleep(DEFAULT_WAIT_DELAY); - } - } + public static DockingActionIf getAction(Plugin plugin, String actionName) { + return getAction(plugin.getTool(), plugin.getName(), actionName); } /** @@ -230,6 +177,7 @@ public abstract class AbstractGhidraHeadedIntegrationTest extends AbstractGhidra * * @param project The project which with the tool is associated. * @param tool The tool to be saved + * @return the new tool */ public static PluginTool saveTool(final Project project, final PluginTool tool) { AtomicReference ref = new AtomicReference<>(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java index fa081657c5..63b7fbe3b7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java @@ -225,6 +225,10 @@ public class TestEnv { } public void closeTool(PluginTool toolToClose, boolean ignoreChanges) { + if (toolToClose == tool) { + tool = null; + } + extraTools.remove(toolToClose); AbstractGenericTest.executeOnSwingWithoutBlocking(() -> { if (ignoreChanges) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/DeleteTableRowAction.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java similarity index 84% rename from Ghidra/Features/Base/src/main/java/ghidra/util/table/DeleteTableRowAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java index 664f6ecd81..852a840f62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/DeleteTableRowAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java @@ -13,27 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.util.table; +package ghidra.util.table.actions; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; -import javax.swing.*; +import javax.swing.ImageIcon; +import javax.swing.KeyStroke; import javax.swing.table.TableModel; import docking.ActionContext; -import docking.action.MenuData; -import docking.action.ToolBarData; +import docking.action.*; import docking.widgets.table.GTable; import docking.widgets.table.RowObjectTableModel; import docking.widgets.table.threaded.ThreadedTableModel; -import ghidra.app.actions.AbstractSharedKeybindingAction; import ghidra.app.util.HelpTopics; -import ghidra.framework.options.DummyKeyBindingsOptionsAction; import ghidra.framework.plugintool.PluginTool; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; +import ghidra.util.*; import ghidra.util.exception.AssertException; import ghidra.util.timer.GTimer; import resources.ResourceManager; @@ -52,7 +49,7 @@ import resources.ResourceManager; * this action in the Tool's options so that user's can update keybindings, regardless of whether * they have ever shown one of your transient providers. */ -public class DeleteTableRowAction extends AbstractSharedKeybindingAction { +public class DeleteTableRowAction extends DockingAction { private static final KeyStroke DEFAULT_KEYSTROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0); @@ -72,28 +69,33 @@ public class DeleteTableRowAction extends AbstractSharedKeybindingAction { new DummyDeleteAction(tool); } - /** - * Constructor for stubs only. - * - * @param tool the tool needed to access options - */ - private DeleteTableRowAction(PluginTool tool) { - super(tool, NAME, DummyKeyBindingsOptionsAction.DEFAULT_OWNER, DEFAULT_KEYSTROKE); - } - - public DeleteTableRowAction(PluginTool tool, GTable table, String owner) { - this(tool, NAME, owner, DEFAULT_KEYSTROKE); + public DeleteTableRowAction(GTable table, String owner) { + this(NAME, owner, DEFAULT_KEYSTROKE); this.table = table; } - private DeleteTableRowAction(PluginTool tool, String name, String owner, - KeyStroke defaultkeyStroke) { - super(tool, name, owner, defaultkeyStroke); - + private DeleteTableRowAction(String name, String owner, KeyStroke defaultkeyStroke) { + super(name, owner); + setDescription("Remove the selected rows from the table"); setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Remove_Items")); setToolBarData(new ToolBarData(ICON, null)); setPopupMenuData(new MenuData(new String[] { "Remove Items" }, ICON, null)); + + initKeyStroke(defaultkeyStroke); + } + + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; } @Override @@ -122,7 +124,7 @@ public class DeleteTableRowAction extends AbstractSharedKeybindingAction { @SuppressWarnings("unchecked") RowObjectTableModel rowObjectModel = (RowObjectTableModel) model; int[] rows = table.getSelectedRows(); - List itemsToRemove = new ArrayList(); + List itemsToRemove = new ArrayList<>(); for (int row : rows) { itemsToRemove.add(rowObjectModel.getRowObject(row)); } @@ -163,7 +165,7 @@ public class DeleteTableRowAction extends AbstractSharedKeybindingAction { } private void selectRow(TableModel model, final int row) { - SwingUtilities.invokeLater(() -> { + Swing.runLater(() -> { if (checkForBusy(model)) { // Selecting rows whilst the model is processing deletes will cause the @@ -195,13 +197,20 @@ public class DeleteTableRowAction extends AbstractSharedKeybindingAction { //================================================================================================== private static class DummyDeleteAction extends DeleteTableRowAction { - DummyDeleteAction(PluginTool tool) { - super(tool); + + public DummyDeleteAction(PluginTool tool) { + super(NAME, "Tool", DEFAULT_KEYSTROKE); + tool.addAction(this); } @Override public void actionPerformed(ActionContext context) { // stub } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return false; // stub + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/MakeProgramSelectionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/MakeProgramSelectionAction.java new file mode 100644 index 0000000000..b37a5a84bf --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/MakeProgramSelectionAction.java @@ -0,0 +1,88 @@ +/* ### + * 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.util.table.actions; + +import javax.swing.JTable; +import javax.swing.KeyStroke; + +import docking.ActionContext; +import docking.action.*; +import ghidra.util.HelpLocation; +import resources.Icons; + +/** + * An action to make a program selection based on the given table's selection. The clients + * must implement the make selection code, as they know their own data. Also, for the context to + * work, the provider using this action must create an {@link ActionContext} that returns a + * context object that is the table passed to this action's constructor. + */ +public abstract class MakeProgramSelectionAction extends DockingAction { + + private JTable table; + + public MakeProgramSelectionAction(String owner, JTable table) { + super("Make Selection", owner); + this.table = table; + + setPopupMenuData( + new MenuData(new String[] { "Make Selection" }, Icons.MAKE_SELECTION_ICON)); + setToolBarData(new ToolBarData(Icons.MAKE_SELECTION_ICON)); + setDescription("Make a program selection from the seleted rows"); + + // this help location provides generic help; clients can override to point to their help + setHelpLocation(new HelpLocation("Search", "Make_Selection")); + + // null for now, but we may want a default binding in the future + initKeyStroke(null); + } + + private void initKeyStroke(KeyStroke keyStroke) { + if (keyStroke == null) { + return; + } + + setKeyBindingData(new KeyBindingData(keyStroke)); + } + + @Override + public boolean usesSharedKeyBinding() { + return true; + } + + @Override + public boolean isAddToPopup(ActionContext context) { + return true; + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + + Object contextObject = context.getContextObject(); + if (contextObject != table) { + return false; + } + + int n = table.getSelectedRowCount(); + return n > 0; + } + + @Override + public void actionPerformed(ActionContext context) { + makeSelection(context); + } + + protected abstract void makeSelection(ActionContext context); +} diff --git a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java index 5ef6b41bd5..6cb4822128 100644 --- a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java +++ b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java @@ -212,9 +212,8 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn public void performAction(String actionName, String owner, ComponentProvider contextProvider, boolean wait) { - String fullActionName = actionName + " (" + owner + ")"; - List action = tool.getDockingActionsByFullActionName(fullActionName); - performAction(action.get(0), contextProvider, wait); + DockingActionIf action = getAction(tool, owner, actionName); + performAction(action, contextProvider, wait); } public void showOptions(final String optionsCategoryName) { diff --git a/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java b/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java index 0c37a470a5..f384c050c2 100644 --- a/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java +++ b/Ghidra/Features/Base/src/main/java/help/screenshot/GhidraScreenShotGenerator.java @@ -359,12 +359,11 @@ public abstract class GhidraScreenShotGenerator extends AbstractScreenShotGenera } public void performFrontEndAction(String actionName, String owner, boolean wait) { - String fullActionName = actionName + " (" + owner + ")"; FrontEndTool frontEnd = getFrontEndTool(); - List action = frontEnd.getDockingActionsByFullActionName(fullActionName); + + DockingActionIf action = getAction(frontEnd, owner, actionName); ComponentProvider compProvider = (ComponentProvider) getInstanceField("compProvider", frontEnd); - performAction(action.get(0), compProvider, wait); + performAction(action, compProvider, wait); } - } diff --git a/Ghidra/Features/Base/src/main/resources/images/text_align_justify.png b/Ghidra/Features/Base/src/main/resources/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/Base/src/main/resources/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java index ca7346a2a4..61038d2b83 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java @@ -186,8 +186,8 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest { public DockingAction getKeyBindingAction() { DockingWindowManager dwm = DockingWindowManager.getInstance(tool.getToolFrame()); - DockingActionManager dockingActionManager = - (DockingActionManager) getInstanceField("actionManager", dwm); + ActionToGuiMapper dockingActionManager = + (ActionToGuiMapper) getInstanceField("actionManager", dwm); return (DockingAction) getInstanceField("keyBindingsAction", dockingActionManager); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/algorithmtree/ModuleAlgorithmPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/algorithmtree/ModuleAlgorithmPluginTest.java index 7f70fda78e..cb9e2dab9b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/algorithmtree/ModuleAlgorithmPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/algorithmtree/ModuleAlgorithmPluginTest.java @@ -18,7 +18,7 @@ package ghidra.app.plugin.core.algorithmtree; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import java.util.List; +import java.util.Set; import org.junit.*; @@ -33,6 +33,7 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.listing.ProgramModule; import ghidra.program.util.GroupPath; import ghidra.test.*; +import util.CollectionUtils; /** * Test the module algorithm plugin gui elements. @@ -43,7 +44,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe private PluginTool tool; private Program program; private ModuleAlgorithmPlugin plugin; - private List actions; + private Set actions; private ProgramTreeService service; private Object context; @@ -58,7 +59,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe tool.addPlugin(ProgramTreePlugin.class.getName()); tool.addPlugin(ModuleAlgorithmPlugin.class.getName()); plugin = env.getPlugin(ModuleAlgorithmPlugin.class); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); service = tool.getService(ProgramTreeService.class); } @@ -98,7 +99,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe getContextObject(vps); - performAction(actions.get(0), new ActionContext(null, context), true); + performAction(CollectionUtils.any(actions), new ActionContext(null, context), true); waitForTasks(); program.flushEvents(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java index ece63c3b39..a5e2a57ea3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java @@ -1090,10 +1090,9 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest { private DockingActionIf getAction(String actionName) { // make sure there is a provider from which to get actions getProvider(); - String fullActionName = actionName + " (CallTreePlugin)"; - List actions = tool.getDockingActionsByFullActionName(fullActionName); - Assert.assertTrue("Could not find action: " + fullActionName, actions.size() > 0); - return actions.get(0); + + DockingActionIf action = getAction(tool, "CallTreePlugin", actionName); + return action; } private void myWaitForTree(GTree gTree, CallTreeProvider treeProvider) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/AbstractDataActionTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/AbstractDataActionTest.java index a7784f42ac..53b97d2587 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/AbstractDataActionTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/AbstractDataActionTest.java @@ -165,7 +165,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra builtInDataTypesManager.setFavorite(root.getDataType("word"), true); } - protected void checkActions(List actions, boolean enabled, String caseStr) { + protected void checkActions(Set actions, boolean enabled, String caseStr) { checkAction(actions, CREATE_STRUCTURE, enabled, caseStr); checkAction(actions, EDIT_DATA_TYPE, enabled, caseStr); checkAction(actions, CREATE_ARRAY, enabled, caseStr); @@ -1063,7 +1063,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra ProgramSelection sel = getCurrentSelection(); boolean useSelection = (sel != null && !sel.isEmpty()); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); for (DockingActionIf element : actions) { MenuData menuBarData = element.getMenuBarData(); @@ -1113,31 +1113,6 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra } } -// if (useSelection) { -// -// checkAction(actions, CREATE_STRUCTURE, true, caseName); -// checkAction(actions, EDIT_STRUCTURE, false, caseName); -// checkAction(actions, CREATE_ARRAY, true, caseName); -// checkAction(actions, DEFAULT_DATA_SETTINGS, false, caseName); -// checkAction(actions, DATA_SETTINGS, false, caseName); -// checkAction(actions, CYCLE_FLOAT_DOUBLE, true, caseName); -// checkAction(actions, CYCLE_BYTE_WORD_DWORD_QWORD, true, caseName); -// checkAction(actions, CYCLE_CHAR_STRING_UNICODE, true, caseName); -// checkAction(actions, DEFINE_BYTE, true, caseName); -// checkAction(actions, DEFINE_WORD, true, caseName); -// checkAction(actions, DEFINE_DWORD, true, caseName); -// checkAction(actions, DEFINE_QWORD, true, caseName); -// checkAction(actions, DEFINE_FLOAT, true, caseName); -// checkAction(actions, DEFINE_DOUBLE, true, caseName); -// checkAction(actions, DEFINE_TERM_CSTRING, true, caseName); -// checkAction(actions, DEFINE_POINTER, true, caseName); -// -// PluginAction recentlyUsedAction = getAction(RECENTLY_USED); -// if (recentlyUsedAction != null) { -// checkAction(recentlyUsedAction, false, caseName); -// } -// return; -// } if (data != null) { DataType dt = data.getDataType(); @@ -1184,10 +1159,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra } - protected void checkOnUndefined(List actions) { + protected void checkOnUndefined(Set actions) { if (actions == null) { - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); } Data data = getContextData(); @@ -1223,10 +1198,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra } - protected void checkOnDefined(List actions, Class expectedDataType) { + protected void checkOnDefined(Set actions, Class expectedDataType) { if (actions == null) { - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); } String dtName = expectedDataType.getName(); @@ -1287,10 +1262,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra checkAction(actions, DEFINE_POINTER, true, caseName); } - protected void checkOnArray(List actions, DataType interiorDt, int arraySize) { + protected void checkOnArray(Set actions, DataType interiorDt, int arraySize) { if (actions == null) { - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); } Data d = getContextData(); @@ -1356,10 +1331,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra * @param actions * @param structSize structure size or -1 to disable size check */ - protected void checkOnStructure(List actions, int structSize) { + protected void checkOnStructure(Set actions, int structSize) { if (actions == null) { - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); } Data d = getContextData(); @@ -1400,7 +1375,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra } protected DockingActionIf getAction(String name) { - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); for (DockingActionIf element : actions) { String actionName = element.getName(); int pos = actionName.indexOf(" ("); @@ -1439,7 +1414,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra } - protected void checkAction(List actions, String name, boolean isEnabled, + protected void checkAction(Set actions, String name, boolean isEnabled, String caseName) { for (DockingActionIf element : actions) { String actionName = element.getName(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataAction4Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataAction4Test.java index db6b3712b9..2ea75aed19 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataAction4Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataAction4Test.java @@ -17,7 +17,7 @@ package ghidra.app.plugin.core.data; import static org.junit.Assert.*; -import java.util.List; +import java.util.Set; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -39,13 +39,13 @@ public class DataAction4Test extends AbstractDataActionTest { @Test public void testNotepadLocations() { - List actions; + Set actions; program.addConsumer(this); // allow program to survive close try { closeProgram(); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkActions(actions, false, "Start"); @@ -60,7 +60,7 @@ public class DataAction4Test extends AbstractDataActionTest { closeProgram(); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkActions(actions, false, "Start"); } @@ -73,13 +73,13 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_BYTE, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, ByteDataType.class); undo(program); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnUndefined(actions); gotoLocation(0x010069f2); @@ -107,7 +107,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_WORD, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, WordDataType.class); @@ -138,7 +138,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_DWORD, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, DWordDataType.class); @@ -169,7 +169,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_QWORD, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, QWordDataType.class); @@ -200,7 +200,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_FLOAT, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, FloatDataType.class); @@ -231,7 +231,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_DOUBLE, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, DoubleDataType.class); @@ -264,7 +264,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(CYCLE_CHAR_STRING_UNICODE, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, CharDataType.class); @@ -283,15 +283,15 @@ public class DataAction4Test extends AbstractDataActionTest { checkOnDefined(actions, CharDataType.class); doAction(CYCLE_CHAR_STRING_UNICODE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, StringDataType.class); doAction(CYCLE_CHAR_STRING_UNICODE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, UnicodeDataType.class); doAction(CYCLE_CHAR_STRING_UNICODE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, CharDataType.class); clearLocation(0x01006a00); @@ -326,7 +326,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, ByteDataType.class); @@ -345,19 +345,19 @@ public class DataAction4Test extends AbstractDataActionTest { checkOnDefined(actions, ByteDataType.class); doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, WordDataType.class); doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, DWordDataType.class); doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, QWordDataType.class); doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, ByteDataType.class); clearLocation(0x01006a00); @@ -387,19 +387,19 @@ public class DataAction4Test extends AbstractDataActionTest { // Test cycle when it does not fit gotoLocation(0x010069f0); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnUndefined(actions); doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, ByteDataType.class); doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, WordDataType.class); doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnUndefined(actions); } @@ -414,7 +414,7 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(CYCLE_FLOAT_DOUBLE, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(ACTION_COUNT, actions.size()); checkOnDefined(actions, FloatDataType.class); @@ -433,11 +433,11 @@ public class DataAction4Test extends AbstractDataActionTest { checkOnDefined(actions, FloatDataType.class); doAction(CYCLE_FLOAT_DOUBLE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, DoubleDataType.class); doAction(CYCLE_FLOAT_DOUBLE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, FloatDataType.class); clearLocation(0x01006a00); @@ -461,15 +461,15 @@ public class DataAction4Test extends AbstractDataActionTest { // Test cycle when it does not fit gotoLocation(0x010069ee); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnUndefined(actions); doAction(CYCLE_FLOAT_DOUBLE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, FloatDataType.class); doAction(CYCLE_FLOAT_DOUBLE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnUndefined(actions); } @@ -495,7 +495,7 @@ public class DataAction4Test extends AbstractDataActionTest { waitForPostedSwingRunnables(); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); checkOnArray(actions, null, 0x20); // Test action disablement on array element location @@ -524,7 +524,7 @@ public class DataAction4Test extends AbstractDataActionTest { waitForPostedSwingRunnables(); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnArray(actions, new ByteDataType(), 0x10); } @@ -541,7 +541,7 @@ public class DataAction4Test extends AbstractDataActionTest { clearSelection();// Remove selection to allow array check to work - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); checkOnArray(actions, null, 0x20); // Create Byte[0x10] array @@ -555,7 +555,7 @@ public class DataAction4Test extends AbstractDataActionTest { clearSelection();// Remove selection to allow array check to work - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnArray(actions, new ByteDataType(), 0x10); } @@ -565,10 +565,7 @@ public class DataAction4Test extends AbstractDataActionTest { gotoLocation(0x01006c00); - List actions = - tool.getDockingActionsByFullActionName(RECENTLY_USED + " (" + plugin.getName() + ")"); - assertEquals(1, actions.size()); - DockingActionIf recentlyUsedAction = actions.get(0); + DockingActionIf recentlyUsedAction = getAction(tool, plugin.getName(), RECENTLY_USED); String caseName = "On Structure at: " + getCurrentLocation(); checkAction(recentlyUsedAction, false, caseName); @@ -591,7 +588,7 @@ public class DataAction4Test extends AbstractDataActionTest { clearSelection(); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); checkOnStructure(actions, 0x20); gotoLocation(0x01006c00); @@ -708,14 +705,14 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_BYTE, true); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, ByteDataType.class); gotoLocation(0x01006a01, new int[] { 1 }); doAction(DEFINE_FLOAT, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, FloatDataType.class); Data pdata = getContextData().getParent(); @@ -757,7 +754,7 @@ public class DataAction4Test extends AbstractDataActionTest { waitForPostedSwingRunnables(); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); checkOnArray(actions, structDt, 5); // Expand structure @@ -772,14 +769,14 @@ public class DataAction4Test extends AbstractDataActionTest { doAction(DEFINE_BYTE, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, ByteDataType.class); gotoLocation(0x01006a01, new int[] { 0, 1 }); doAction(DEFINE_FLOAT, true); - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); checkOnDefined(actions, FloatDataType.class); Data pdata = getContextData().getParent(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java index 6a146a50e6..a4f2965880 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java @@ -37,7 +37,7 @@ import org.junit.*; import docking.DockingUtils; import docking.action.DockingActionIf; import docking.action.ToggleDockingActionIf; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; import docking.widgets.OptionDialog; import docking.widgets.combobox.GhidraComboBox; import docking.widgets.dialogs.InputWithChoicesDialog; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/AbstractEquatePluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/AbstractEquatePluginTest.java index 9a93e48da1..4379ea6433 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/AbstractEquatePluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/AbstractEquatePluginTest.java @@ -17,7 +17,7 @@ package ghidra.app.plugin.core.equate; import static org.junit.Assert.*; -import java.util.List; +import java.util.Set; import javax.swing.JTextField; @@ -115,11 +115,10 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest // for selection containing a function stack address builder.setBytes("0x01004bbd", "c2 08 00"); // return of previous function - builder.setBytes("0x01004bc0", - "53 8b 5c 24 08 56 8b 35 b8 10 00 01 57 ff 35 78 80 00 01 " + - "53 ff d6 8b 3d e0 10 00 01 53 ff d7 8d 5c 43 02 68 9c 13 00 01 53 ff d6 53 ff d7 " + - "ff 35 7c 80 00 01 8d 5c 43 02 53 ff d6 53 ff d7 8d 5c 43 02 68 e0 17 00 01 53 ff " + - "d6 53 ff d7 66 83 64 43 02 00 8d 44 43 02 5f 5e 5b c2 04 00"); + builder.setBytes("0x01004bc0", "53 8b 5c 24 08 56 8b 35 b8 10 00 01 57 ff 35 78 80 00 01 " + + "53 ff d6 8b 3d e0 10 00 01 53 ff d7 8d 5c 43 02 68 9c 13 00 01 53 ff d6 53 ff d7 " + + "ff 35 7c 80 00 01 8d 5c 43 02 53 ff d6 53 ff d7 8d 5c 43 02 68 e0 17 00 01 53 ff " + + "d6 53 ff d7 66 83 64 43 02 00 8d 44 43 02 5f 5e 5b c2 04 00"); builder.disassemble(new AddressSet(builder.getProgram(), builder.addr("0x01004bc0"), builder.addr("0x01004c1a")), true); builder.createFunction("0x01004bc0"); @@ -203,8 +202,6 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest env.dispose(); } - - //================================================================================================= // Private Methods //================================================================================================= @@ -218,8 +215,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest ComponentProvider provider = tool.getComponentProvider(PluginConstants.CODE_BROWSER); DockingActionIf action = getAction(equatePlugin, "Apply Enum"); performAction(action, provider, false); - ApplyEnumDialog d = - waitForDialogComponent(tool.getToolFrame(), ApplyEnumDialog.class, DEFAULT_WAIT_DELAY); + ApplyEnumDialog d = waitForDialogComponent(ApplyEnumDialog.class); return d; } @@ -231,7 +227,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest } protected void assertConvertActionsInPopup(boolean inPopup) { - List actions = tool.getDockingActionsByOwnerName("EquatePlugin"); + Set actions = getActionsByOwner(tool, "EquatePlugin"); for (DockingActionIf action : actions) { String actionName = action.getName(); if (actionName.startsWith("Convert")) { @@ -242,7 +238,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest } protected void assertNonFloatConvertActionsInPopup() { - List actions = tool.getDockingActionsByOwnerName("EquatePlugin"); + Set actions = getActionsByOwner(tool, "EquatePlugin"); for (DockingActionIf action : actions) { String actionName = action.getName(); if (actionName.startsWith("Convert")) { @@ -255,7 +251,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest } protected void assertConvertNonCharNonSignedActionsInPopup() { - List actions = tool.getDockingActionsByOwnerName("EquatePlugin"); + Set actions = getActionsByOwner(tool, "EquatePlugin"); for (DockingActionIf element : actions) { String name = element.getName(); if (name.startsWith("Convert") && @@ -266,7 +262,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest } protected void assertConvertNonSignedActionsInPopup() { - List actions = tool.getDockingActionsByOwnerName("EquatePlugin"); + Set actions = getActionsByOwner(tool, "EquatePlugin"); for (DockingActionIf action : actions) { String name = action.getName(); if (name.startsWith("Convert") && name.indexOf("Signed") < 0) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/EquatePlugin1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/EquatePlugin1Test.java index 97721e9604..d727512cca 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/EquatePlugin1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/equate/EquatePlugin1Test.java @@ -19,6 +19,7 @@ import static org.junit.Assert.*; import java.lang.reflect.InvocationTargetException; import java.util.List; +import java.util.Set; import javax.swing.*; import javax.swing.table.TableModel; @@ -841,7 +842,7 @@ public class EquatePlugin1Test extends AbstractEquatePluginTest { putCursorOnOperand(0x010064ae, 1); - List actions = tool.getDockingActionsByOwnerName("EquatePlugin"); + Set actions = getActionsByOwner(tool, "EquatePlugin"); int found = 0; for (DockingActionIf action : actions) { String name = action.getName(); @@ -892,7 +893,7 @@ public class EquatePlugin1Test extends AbstractEquatePluginTest { putCursorOnOperand(0x010064a3, 0); int found = 0; - List actions = tool.getDockingActionsByOwnerName("EquatePlugin"); + Set actions = getActionsByOwner(tool, "EquatePlugin"); for (DockingActionIf action : actions) { String name = action.getName(); if (!name.startsWith("Convert") || !action.isAddToPopup(getListingContext())) { @@ -944,7 +945,7 @@ public class EquatePlugin1Test extends AbstractEquatePluginTest { putCursorOnOperand(0x01003a94, 0); int found = 0; - List actions = tool.getDockingActionsByOwnerName("EquatePlugin"); + Set actions = getActionsByOwner(tool, "EquatePlugin"); for (DockingActionIf action : actions) { String name = action.getName(); if (!name.startsWith("Convert") || !action.isAddToPopup(getListingContext())) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/fallthrough/FallThroughActionTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/fallthrough/FallThroughActionTest.java index 29127c215f..dd861b4cfc 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/fallthrough/FallThroughActionTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/fallthrough/FallThroughActionTest.java @@ -17,7 +17,7 @@ package ghidra.app.plugin.core.fallthrough; import static org.junit.Assert.assertEquals; -import java.util.List; +import java.util.Set; import org.junit.*; @@ -37,7 +37,8 @@ import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramSelection; import ghidra.test.*; -public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest implements LocationCallback { +public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest + implements LocationCallback { private Program program; private TestEnv env; private PluginTool tool; @@ -70,7 +71,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i @Test public void testNotepadLocations() { - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); checkAction(actions, AUTO_OVERRIDE, false, "Start"); checkAction(actions, CLEAR_FALLTHROUGH, false, "Start"); @@ -96,7 +97,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i new ProgramSelectionPluginEvent("Test", selection, program); tool.firePluginEvent(ev); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); checkAction(actions, AUTO_OVERRIDE, true, "selection"); checkAction(actions, CLEAR_FALLTHROUGH, true, "selection"); @@ -110,7 +111,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i @Override public void locationGenerated(ProgramLocation loc) { tool.firePluginEvent(new ProgramLocationPluginEvent("test", loc, program)); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); ListingActionContext actionContext = (ListingActionContext) cb.getProvider().getActionContext(null); @@ -126,7 +127,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i } - private void checkAction(List actions, String name, boolean isValidContext, + private void checkAction(Set actions, String name, boolean isValidContext, String caseName) { for (DockingActionIf action : actions) { String actionName = action.getName(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java index cb867b88a4..e0b69ccf3f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java @@ -19,7 +19,7 @@ import static org.junit.Assert.*; import java.awt.*; import java.awt.event.MouseEvent; -import java.util.List; +import java.util.Set; import javax.swing.JTable; import javax.swing.JTextField; @@ -96,7 +96,7 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest { env.close(program); program = buildProgram("sdk"); env.open(program); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); for (DockingActionIf action : actions) { if (action.getName().equals("Add Block") || action.getName().equals("Set Image Base") || action.getName().equals("View Memory Map")) { @@ -114,7 +114,7 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest { env.close(program); JTable table = provider.getTable(); assertEquals(0, table.getModel().getRowCount()); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); for (DockingActionIf action : actions) { if (!action.getName().equals("View Memory Map")) { assertTrue(!action.isEnabledForContext(provider.getActionContext(null))); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java index 1b69fdfea0..528773a745 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java @@ -19,7 +19,6 @@ import static org.junit.Assert.*; import java.awt.*; import java.util.*; -import java.util.List; import javax.swing.*; import javax.swing.table.JTableHeader; @@ -117,7 +116,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest // select first row // all actions except "merge" should be enabled table.addRowSelectionInterval(0, 0); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); for (DockingActionIf action : actions) { if (action.getName().equals("Merge Blocks")) { assertTrue(!action.isEnabled()); @@ -133,7 +132,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest table.addRowSelectionInterval(0, 1); assertEquals(2, table.getSelectedRowCount()); - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); for (DockingActionIf action : actions) { String name = action.getName(); if (name.equals("Add Block") || name.equals("Merge Blocks") || diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java index 8a96da08f4..e96f571807 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java @@ -18,8 +18,7 @@ package ghidra.app.plugin.core.module; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.util.Arrays; -import java.util.List; +import java.util.*; import org.junit.*; @@ -42,7 +41,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest { private PluginTool tool; private Program program; private ModuleSortPlugin plugin; - private List actions; + private Set actions; private ProgramTreeService service; public ModuleSortPluginTest() { @@ -63,7 +62,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest { break; } } - actions = tool.getDockingActionsByOwnerName(plugin.getName()); + actions = getActionsByOwner(tool, plugin.getName()); service = tool.getService(ProgramTreeService.class); ProgramBuilder builder = new ProgramBuilder("notepad", ProgramBuilder._TOY); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/GoToPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/GoToPluginTest.java index ff85005e95..1002e1a333 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/GoToPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/GoToPluginTest.java @@ -17,8 +17,7 @@ package ghidra.app.plugin.core.navigation; import static org.junit.Assert.*; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import javax.swing.*; @@ -65,7 +64,7 @@ import ghidra.util.Msg; import ghidra.util.table.GhidraProgramTableModel; import ghidra.util.table.field.LabelTableColumn; import ghidra.util.task.TaskMonitor; -import ghidra.util.task.TaskMonitorAdapter; +import util.CollectionUtils; public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { private TestEnv env; @@ -104,20 +103,20 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testActionEnablement() throws Exception { - List actions = tool.getDockingActionsByOwnerName(plugin.getName()); + Set actions = getActionsByOwner(tool, plugin.getName()); assertEquals(1, actions.size()); - assertEquals("Go To Address/Label", actions.get(0).getName()); + assertEquals("Go To Address/Label", CollectionUtils.any(actions).getName()); ActionContext actionContext = getActionContext(); - assertTrue(!actions.get(0).isEnabledForContext(actionContext)); + assertTrue(!CollectionUtils.any(actions).isEnabledForContext(actionContext)); loadProgram("x86"); actionContext = getActionContext(); - assertTrue(actions.get(0).isEnabledForContext(actionContext)); + assertTrue(CollectionUtils.any(actions).isEnabledForContext(actionContext)); final ProgramManager pm = tool.getService(ProgramManager.class); - SwingUtilities.invokeAndWait(() -> pm.closeProgram(program, true)); + runSwing(() -> pm.closeProgram(program, true)); actionContext = getActionContext(); - assertTrue(!actions.get(0).isEnabledForContext(actionContext)); + assertTrue(!CollectionUtils.any(actions).isEnabledForContext(actionContext)); } @Test @@ -476,7 +475,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { performOkCallback(); assertEquals("No results for xyzabc*", dialog.getStatusText()); - SwingUtilities.invokeAndWait(() -> dialog.close()); + runSwing(() -> dialog.close()); } @Test @@ -561,7 +560,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { program.endTransaction(transactionID, true); final JCheckBox cb = findComponent(dialog, JCheckBox.class); - SwingUtilities.invokeAndWait(() -> { + runSwing(() -> { cb.setSelected(false); dialog.setText("COm*"); @@ -831,7 +830,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { Assert.assertNotNull(program); final ProgramManager pm = tool.getService(ProgramManager.class); - SwingUtilities.invokeAndWait(() -> pm.openProgram(program.getDomainFile())); + runSwing(() -> pm.openProgram(program.getDomainFile())); program.release(this); addrFactory = program.getAddressFactory(); } @@ -965,7 +964,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { try { Memory memory = program.getMemory(); return memory.createInitializedBlock(name, addr(address), size, (byte) 0, - TaskMonitorAdapter.DUMMY_MONITOR, true); + TaskMonitor.DUMMY, true); } finally { program.endTransaction(transactionID, true); @@ -1023,7 +1022,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { } private void setText(final String text) throws Exception { - SwingUtilities.invokeAndWait(() -> dialog.setText(text)); + runSwing(() -> dialog.setText(text)); } private void performOkCallback() throws Exception { @@ -1035,17 +1034,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest { } private void waitForOKCallback() { - int numWaits = 0; - while (++numWaits < 50 && !okButton.isEnabled()) { - try { - Thread.sleep(100); - } - catch (InterruptedException e) { - // no biggie, will try again - } - } - - Assert.assertNotEquals("Timed-out waiting for Go To dialog to finish", 50, numWaits); + waitForCondition(() -> runSwing(() -> okButton.isEnabled())); } private void assumeCurrentAddressSpace(boolean b) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java index e53faacc07..cdaedf7e2c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java @@ -20,8 +20,7 @@ import static org.junit.Assert.*; import java.awt.Window; import java.io.*; import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -60,6 +59,7 @@ import ghidra.util.exception.CancelledException; import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.task.*; +import util.CollectionUtils; import utilities.util.FileUtilities; public abstract class AbstractGhidraScriptMgrPluginTest @@ -272,10 +272,18 @@ public abstract class AbstractGhidraScriptMgrPluginTest assertTrue(message, fullText.contains(piece)); } - protected void assertRunLastActionEnabled(boolean enabled) { - final DockingActionIf runLastAction = getAction(plugin, "Rerun Last Script"); - assertNotNull(runLastAction); + private DockingActionIf getRunLastScriptAction() { + // note: this provider adds 2 versions of the same action--pick either + Set actions = + getActionsByOwnerAndName(plugin.getTool(), plugin.getName(), "Rerun Last Script"); + assertFalse(actions.isEmpty()); + DockingActionIf runLastAction = CollectionUtils.any(actions); + return runLastAction; + } + protected void assertRunLastActionEnabled(boolean enabled) { + + DockingActionIf runLastAction = getRunLastScriptAction(); final AtomicReference ref = new AtomicReference<>(); runSwing(() -> ref.set(runLastAction.isEnabledForContext(new ActionContext()))); assertEquals("Run Last Action not enabled as expected", enabled, ref.get()); @@ -557,17 +565,15 @@ public abstract class AbstractGhidraScriptMgrPluginTest } protected void pressRunLastScriptButton() { - DockingActionIf action = - getAction(plugin, GhidraScriptActionManager.RERUN_LAST_SHARED_ACTION_NAME); - performAction(action, false); + DockingActionIf runLastAction = getRunLastScriptAction(); + performAction(runLastAction, false); waitForSwing(); } protected void performGlobalRunLastScriptAction() { - DockingActionIf action = - getAction(plugin, GhidraScriptActionManager.GLOBAL_RERUN_LAST_SHARED_ACTION_NAME); - performAction(action, false); - waitForSwing(); + // note: this action used to be different from the 'run last script'; currently they are + // the same + pressRunLastScriptButton(); } protected KeyBindingInputDialog pressKeyBindingAction() { @@ -1293,7 +1299,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest protected void assertToolKeyBinding(KeyStroke ks) { String actionOwner = GhidraScriptMgrPlugin.class.getSimpleName(); PluginTool tool = env.getTool(); - List actions = tool.getDockingActionsByOwnerName(actionOwner); + Set actions = getActionsByOwner(tool, actionOwner); for (DockingActionIf action : actions) { KeyStroke keyBinding = action.getKeyBinding(); if (keyBinding == null) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin1Test.java index c3b8abb6e8..45bf465340 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/searchtext/SearchTextPlugin1Test.java @@ -684,8 +684,8 @@ public class SearchTextPlugin1Test extends AbstractGhidraHeadedIntegrationTest { final GTable table = threadedTablePanel.getTable(); Random random = new Random(); final int randomRow = random.nextInt(model.getRowCount()); - DockingActionIf deleteRowAction = - tool.getDockingActionsByFullActionName("Remove Items (TableServicePlugin)").get(0); + + DockingActionIf deleteRowAction = getAction(tool, "TableServicePlugin", "Remove Items"); ProgramLocation toBeDeleted = model.getRowObject(randomRow); runSwing(() -> table.setRowSelectionInterval(randomRow, randomRow)); performAction(deleteRowAction, true); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java index aeaf9066ab..785139cd12 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java @@ -1306,11 +1306,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest { private void setupSymbolTableFilterToShowParameters() throws Exception { // get the filter action - "Set Filter" - List actions = - tool.getDockingActionsByFullActionName("Set Filter (SymbolTablePlugin)"); - assertNotNull(actions); - assertTrue(actions.size() > 0); - DockingActionIf filterAction = actions.get(0); + + DockingActionIf filterAction = getAction(tool, "SymbolTablePlugin", "Set Filter"); // execute performAction(filterAction, false); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager1Test.java index c51c53f157..b28418ace8 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager1Test.java @@ -30,6 +30,7 @@ import org.junit.*; import docking.ActionContext; import docking.action.DockingActionIf; +import docking.test.AbstractDockingTest; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeRootNode; @@ -596,10 +597,9 @@ public class ActionManager1Test extends AbstractGhidraHeadedIntegrationTest { } private DockingActionIf getAction(String actionName) { - List a = - frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)"); - Assert.assertEquals(1, a.size()); - return a.get(0); + DockingActionIf action = + AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName); + return action; } private void expandTreePath(TreePath path) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager2Test.java index aadb59fa26..2a47bd67fe 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/ActionManager2Test.java @@ -30,6 +30,7 @@ import org.junit.*; import docking.ActionContext; import docking.action.DockingActionIf; import docking.action.ToggleDockingAction; +import docking.test.AbstractDockingTest; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeRootNode; @@ -60,10 +61,6 @@ public class ActionManager2Test extends AbstractGhidraHeadedIntegrationTest { private DomainFolder rootFolder; private GTreeRootNode rootNode; - public ActionManager2Test() { - super(); - } - @Before public void setUp() throws Exception { env = new TestEnv(); @@ -363,10 +360,9 @@ public class ActionManager2Test extends AbstractGhidraHeadedIntegrationTest { } private DockingActionIf getAction(String actionName) { - List a = - frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)"); - assertEquals(1, a.size()); - return a.get(0); + DockingActionIf action = + AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName); + return action; } private void setSelectionPath(final TreePath path) throws Exception { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java index c448072a6d..a8e580c61d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java @@ -30,9 +30,10 @@ import javax.swing.tree.TreePath; import org.junit.*; import docking.action.DockingActionIf; +import docking.actions.KeyBindingUtils; import docking.options.editor.OptionsDialog; import docking.options.editor.OptionsPanel; -import docking.util.KeyBindingUtils; +import docking.tool.util.DockingToolConstants; import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.tree.GTree; import generic.io.NullWriter; @@ -49,7 +50,6 @@ import ghidra.framework.options.Options; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.mgr.OptionsManager; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; import ghidra.util.Msg; @@ -65,7 +65,6 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { private static final String TEST_FILENAME = "KeyBindingUtilsTest_Test_Filename" + KeyBindingUtils.PREFERENCES_FILE_EXTENSION; - private static final String TEST_TOOL_NAME = "KeyBindingsUtilsTest_TestTool"; private Writer debug = new NullWriter(); @@ -137,7 +136,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testExportImportKeyBindings() throws Exception { debug("testExportImportKeyBindings()"); - ToolOptions defaultKeyBindings = tool.getOptions(ToolConstants.KEY_BINDINGS); + ToolOptions defaultKeyBindings = tool.getOptions(DockingToolConstants.KEY_BINDINGS); debug("a"); @@ -195,7 +194,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testImportExportWithGUI() throws Exception { - setUpDialog(); + setKeyBindingsUpDialog(); debug("a"); @@ -206,11 +205,11 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { // save and reload them to make sure they are the same File saveFile = exportOptions(toolKeyBindingOptions); - ToolOptions savedOptions = importOptions(saveFile); + ToolOptions originalOptions = importOptions(saveFile); assertOptionsMatch( "The Options objects do not contain different data after " + "changes have been made.", - toolKeyBindingOptions, savedOptions); + toolKeyBindingOptions, originalOptions); debug("c"); @@ -222,25 +221,25 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { // verify the changes are different than the original values assertOptionsDontMatch( "The Options objects do not contain different data after " + "changes have been made.", - toolKeyBindingOptions, savedOptions); + toolKeyBindingOptions, originalOptions); debug("e"); // import the original values file through the tool importOptionsWithGUI(saveFile, true); // get the updated values that have not been applied - Map optionsMap = (Map) getInstanceField("actionMap", panel); + Map keyStrokeMap = panel.getKeyStrokeMap(); debug("f"); // verify the data is the same as it was before the changes - boolean same = compareOptionsWithKeyStrokeMap(savedOptions, optionsMap); - assertTrue("The Options object contains different data than was " + "imported.", same); + boolean same = compareOptionsWithKeyStrokeMap(originalOptions, keyStrokeMap); + assertTrue("The Options object contains different data than was imported.", same); debug("g"); // close the tool *without* applying the changes - closeAllWindowsAndFrames(); + closeAllWindows(); env.dispose(); debug("h"); @@ -250,7 +249,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { // reload the tool and make sure the values are those of the changes // *before* the last import setUp(); - setUpDialog(); + setKeyBindingsUpDialog(); debug("i"); @@ -258,7 +257,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { assertOptionsMatch( "The options from the first tool instance have changed " + "in the second tool instance even though the testing changes were not applied.", - savedOptions, newlyLoadedDefaultOptions); + originalOptions, newlyLoadedDefaultOptions); debug("j"); @@ -281,44 +280,54 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { debug("n"); - saveTool(); - closeAllWindowsAndFrames(); - env.dispose(); - - debug("o"); - saveFile.delete(); // reload the tool and make sure the values are those of the changes // *after* the last import // reload with our saved tool - setUp(); - runSwing(() -> { - ToolServices services = tool.getProject().getToolServices(); - tool = (PluginTool) services.launchTool(TEST_TOOL_NAME, null); - }); + saveAndCloseTool(); + reopenTool(tool); debug("p"); + setKeyBindingsUpDialog(tool); + newlyLoadedDefaultOptions = (ToolOptions) getInstanceField("options", panel); assertOptionsDontMatch( - "The options are the same after making changes, applying, " + "closing and reloading.", - savedOptions, newlyLoadedDefaultOptions); + "The options are the same after making changes, applying, closing and reloading.", + originalOptions, newlyLoadedDefaultOptions); debug("q"); + closeAllWindows(); } - private void saveTool() { - executeOnSwingWithoutBlocking(() -> tool.saveTool()); + private void reopenTool(PluginTool tool2) { + runSwing(() -> { + ToolServices services = tool.getProject().getToolServices(); + tool = (PluginTool) services.launchTool(tool.getName(), null); + }); + assertNotNull(tool); } - // open the options dialog and show the key bindings editor - private void setUpDialog() throws Exception { - debug("setUpDialog()"); + private void saveAndCloseTool() { + runSwing(() -> { + ToolServices services = tool.getProject().getToolServices(); + services.saveTool(tool); + }); + env.closeTool(tool); + } + + private void setKeyBindingsUpDialog() throws Exception { env.showTool(); + setKeyBindingsUpDialog(tool); + } + + private void setKeyBindingsUpDialog(PluginTool pluginTool) throws Exception { + debug("setUpDialog()"); debug("aa"); - final OptionsManager optionsManager = (OptionsManager) getInstanceField("optionsMgr", tool); + final OptionsManager optionsManager = + (OptionsManager) getInstanceField("optionsMgr", pluginTool); debug("bb"); executeOnSwingWithoutBlocking(() -> { @@ -396,26 +405,26 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { } private void setKeyBinding(String keyText, int keyCode) throws Exception { - List list = tool.getAllActions(); - DockingActionIf action = null; - for (int i = 0; i < list.size(); i++) { - action = list.get(i); + Set list = tool.getAllActions(); + DockingActionIf arbitraryAction = null; + for (DockingActionIf action : list) { if (action.isKeyBindingManaged() && action.getKeyBinding() == null) { + arbitraryAction = action; break; } } - if (action == null) { + if (arbitraryAction == null) { Assert.fail("Unable to find an action for which to set a key binding."); } - selectRowForAction(action); + selectRowForAction(arbitraryAction); triggerText(keyField, keyText); assertEquals(keyText.toUpperCase(), keyField.getText()); runSwing(() -> panel.apply()); - assertEquals(KeyStroke.getKeyStroke(keyCode, 0), action.getKeyBinding()); + assertEquals(KeyStroke.getKeyStroke(keyCode, 0), arbitraryAction.getKeyBinding()); } private void selectRowForAction(DockingActionIf action) throws Exception { @@ -458,7 +467,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { } private void closeWarningDialog(boolean proceed) { - Window window = waitForWindowByTitleContaining(null, "Continue", DEFAULT_WINDOW_TIMEOUT); + Window window = waitForWindowByTitleContaining("Continue"); assertNotNull(window); String button = proceed ? "Yes" : "No"; @@ -488,7 +497,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { // locates the open file chooser and verifies its state private File findAndTestFileChooser(File path, String filename) throws Exception { // get the file chooser and set the file it will use - GhidraFileChooser fileChooser = waitForDialogComponent(null, GhidraFileChooser.class, 5000); + GhidraFileChooser fileChooser = waitForDialogComponent(GhidraFileChooser.class); if (fileChooser == null) { Msg.debug(this, "Couldn't find file chooser"); printOpenWindows(); @@ -524,20 +533,19 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { // compares the provided options with the mapping of property names to // keystrokes (the map is obtained from the key bindings panel after an // import is done). - private boolean compareOptionsWithKeyStrokeMap(Options options, Map optionsMap) { + private boolean compareOptionsWithKeyStrokeMap(Options options, + Map panelKeyStrokeMap) { List propertyNames = options.getOptionNames(); for (String element : propertyNames) { - boolean match = optionsMap.containsKey(element); + boolean match = panelKeyStrokeMap.containsKey(element); - Object value = invokeInstanceMethod("getKeyStroke", options, - new Class[] { String.class, KeyStroke.class }, new Object[] { element, null }); - Object value2 = optionsMap.get(element); + KeyStroke optionsKs = options.getKeyStroke(element, null); + KeyStroke panelKs = panelKeyStrokeMap.get(element); - // if the value is null, then it would not have been placed into the - // options map in the key bindings panel, so we only care about - // non-null values - if (value != null) { - match &= (value.equals(value2)); + // if the value is null, then it would not have been placed into the options map + // in the key bindings panel, so we only care about non-null values + if (optionsKs != null) { + match &= (optionsKs.equals(panelKs)); } else { match = true; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java index 3fb37ac4ea..480722bdb5 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java @@ -20,7 +20,7 @@ import static org.junit.Assert.*; import java.awt.Rectangle; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; -import java.util.List; +import java.util.Set; import javax.swing.*; import javax.swing.table.*; @@ -29,10 +29,10 @@ import org.junit.*; import docking.KeyEntryTextField; import docking.action.DockingActionIf; +import docking.tool.util.DockingToolConstants; import docking.widgets.MultiLineLabel; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; import ghidra.util.Msg; @@ -102,9 +102,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testManagedKeyBindings() { - List list = tool.getAllActions(); - for (int i = 0; i < list.size(); i++) { - DockingActionIf action = list.get(i); + Set list = tool.getAllActions(); + for (DockingActionIf action : list) { if (action.isKeyBindingManaged()) { assertTrue(actionInTable(action)); } @@ -128,10 +127,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testActionNotSelected() throws Exception { table.clearSelection(); - List list = tool.getAllActions(); - DockingActionIf action = null; - for (int i = 0; i < list.size(); i++) { - action = list.get(i); + Set list = tool.getAllActions(); + for (DockingActionIf action : list) { KeyStroke ks = getKeyStroke(action); if (isKeyBindingManaged(action) && ks != KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0)) { break; @@ -318,10 +315,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest { } private DockingActionIf getKeyBindingPluginAction() { - List list = tool.getAllActions(); - DockingActionIf action = null; - for (int i = 0; i < list.size(); i++) { - action = list.get(i); + Set list = tool.getAllActions(); + for (DockingActionIf action : list) { KeyStroke ks = action.getKeyBinding(); if (action.isKeyBindingManaged() && ks != null && ks != KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0)) { @@ -372,7 +367,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest { private void setUpDialog() throws Exception { runSwing(() -> { - panel = new KeyBindingsPanel(tool, tool.getOptions(ToolConstants.KEY_BINDINGS)); + panel = new KeyBindingsPanel(tool, tool.getOptions(DockingToolConstants.KEY_BINDINGS)); dialog = new JDialog(tool.getToolFrame(), "Test KeyBindings", false); dialog.getContentPane().add(panel); @@ -391,10 +386,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest { } private void grabActionsWithoutKeybinding() { - List list = tool.getAllActions(); - DockingActionIf action = null; - for (int i = 0; i < list.size(); i++) { - action = list.get(i); + Set list = tool.getAllActions(); + for (DockingActionIf action : list) { if (!action.isKeyBindingManaged()) { continue; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java index 72f606afe8..a402ec00e6 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java @@ -20,8 +20,6 @@ import static org.junit.Assert.*; import java.util.List; import java.util.Set; -import javax.swing.SwingUtilities; - import org.junit.*; import docking.action.DockingActionIf; @@ -74,7 +72,7 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest @After public void tearDown() throws Exception { - SwingUtilities.invokeAndWait(() -> { + runSwing(() -> { tool.setConfigChanged(false); provider.close(); }); @@ -99,33 +97,27 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest final Plugin p = getPlugin(tool, ArchivePlugin.class); assertNotNull(p); - SwingUtilities.invokeAndWait(() -> { + runSwing(() -> { provider.close(); tool.removePlugins(new Plugin[] { p }); }); showProvider(); - List actions = - tool.getDockingActionsByFullActionName("Save Project (" + plugin.getName() + ")"); - assertEquals(1, actions.size()); - performAction(actions.get(0), true); + DockingActionIf action = getAction(tool, plugin.getName(), "Save Project"); + performAction(action, true); - actions = - tool.getDockingActionsByFullActionName("Close Project (" + plugin.getName() + ")"); - assertEquals(1, actions.size()); - performAction(actions.get(0), true); + action = getAction(tool, plugin.getName(), "Close Project"); + performAction(action, true); assertTrue(!provider.isVisible()); } private void showProvider() throws Exception { - List actions = - tool.getDockingActionsByFullActionName("Configure Tool (Project Window)"); - assertEquals(1, actions.size()); - performAction(actions.get(0), true); + DockingActionIf action = getAction(tool, "Project Window", "Configure Tool"); + performAction(action, true); waitForPostedSwingRunnables(); - SwingUtilities.invokeAndWait(() -> tool.showConfig(false, false)); + runSwing(() -> tool.showConfig(false, false)); provider = tool.getManagePluginsDialog(); pluginManagerComponent = (PluginManagerComponent) getInstanceField("comp", provider); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java index bc498e4ac0..c4936a1f68 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java @@ -254,11 +254,9 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { } private void showProvider() { - List actions = - tool.getDockingActionsByFullActionName("Configure Tool (Tool)"); - assertEquals(1, actions.size()); - performAction(actions.get(0), true); + DockingActionIf action = getAction(tool, "Tool", "Configure Tool"); + performAction(action, true); waitForSwing(); provider = tool.getManagePluginsDialog(); pluginManagerComponent = (PluginManagerComponent) getInstanceField("comp", provider); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/CloseToolTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/CloseToolTest.java index 7fa2850852..f278ebe5cd 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/CloseToolTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/CloseToolTest.java @@ -18,7 +18,6 @@ package ghidra.framework.project.tool; import static org.junit.Assert.*; import java.awt.Window; -import java.util.List; import org.junit.*; @@ -56,7 +55,7 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest { public void tearDown() throws Exception { executeOnSwingWithoutBlocking(() -> env.dispose()); - closeAllWindowsAndFrames(); + closeAllWindows(); } @@ -138,7 +137,7 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest { closeTool(tool); // check for warning dialog - Window window = waitForWindow(tool.getToolFrame(), "Tool Busy", 2000); + Window window = waitForWindow("Tool Busy"); assertNotNull("Did not get tool busy dialog", window); closeWindow(window); @@ -146,7 +145,7 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest { closeProgram(tool, program); // check for warning dialog - window = waitForWindow(tool.getToolFrame(), "Close notepad Failed", 2000); + window = waitForWindow("Close notepad Failed"); assertNotNull("Did not get \"close failed\" dialog", window); closeWindow(window); @@ -169,11 +168,9 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest { } private void closeProgram(final PluginTool tool, final ProgramDB program) { - List actionList = - tool.getDockingActionsByFullActionName("Close File (ProgramManagerPlugin)"); - assertTrue(!actionList.isEmpty()); - performAction(actionList.get(0), new ProgramActionContext(null, program), false); + DockingActionIf action = getAction(tool, "ProgramManagerPlugin", "Close File"); + performAction(action, new ProgramActionContext(null, program), false); waitForPostedSwingRunnables(); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/DateEditorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/DateEditorTest.java index db0b77e019..5a1f35b9c6 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/DateEditorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/DateEditorTest.java @@ -201,10 +201,9 @@ public class DateEditorTest extends AbstractGhidraHeadedIntegrationTest { } private void showProgramOptions() { - List list = tool.getAllActions(); - for (int i = 0; i < list.size(); i++) { - - DockingActionIf action = list.get(i); + // TODO change to getAction("Program Options") + Set list = tool.getAllActions(); + for (DockingActionIf action : list) { if (action.getName().equals("Program Options")) { performAction(action, plugin.getProvider(), false); break; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java index b030baa382..9bc6c20a0a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java @@ -988,10 +988,9 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest { } private void showOptionsDialog(PluginTool pluginTool) throws Exception { - List list = pluginTool.getAllActions(); - for (int i = 0; i < list.size(); i++) { - - DockingActionIf action = list.get(i); + // TODO change to getAction("Edit Options") + Set list = pluginTool.getAllActions(); + for (DockingActionIf action : list) { if (action.getName().equals("Edit Options")) { performAction(action, false); break; diff --git a/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java b/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java index e9ae76ed6c..20b92f2c59 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java @@ -19,7 +19,7 @@ import java.awt.Window; import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.util.Collections; -import java.util.List; +import java.util.Set; import javax.swing.ImageIcon; import javax.swing.event.ChangeListener; @@ -438,18 +438,13 @@ public class DummyTool implements Tool { } @Override - public List getAllActions() { - return Collections.emptyList(); + public Set getAllActions() { + return Collections.emptySet(); } @Override - public List getDockingActionsByOwnerName(String owner) { - return Collections.emptyList(); - } - - @Override - public List getDockingActionsByFullActionName(String fullActionName) { - return Collections.emptyList(); + public Set getDockingActionsByOwnerName(String owner) { + return Collections.emptySet(); } @Override diff --git a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/framework/plugintool/dialog/SaveToolConfigDialogTest.java b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/framework/plugintool/dialog/SaveToolConfigDialogTest.java index a087a958ef..e76f632d8c 100644 --- a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/framework/plugintool/dialog/SaveToolConfigDialogTest.java +++ b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/framework/plugintool/dialog/SaveToolConfigDialogTest.java @@ -21,7 +21,6 @@ import java.awt.event.ActionListener; import java.io.File; import java.io.InputStream; import java.net.URL; -import java.util.List; import javax.swing.*; @@ -97,9 +96,9 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes tc.remove("MyTestTool"); tc.remove("TestTool"); - waitForPostedSwingRunnables(); + waitForSwing(); tool.setConfigChanged(false); - SwingUtilities.invokeAndWait(() -> saveDialog.close()); + runSwing(() -> saveDialog.close()); env.dispose(); } @@ -125,7 +124,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes pressButtonByText(saveDialog, "Save"); assertTrue(!tool.hasConfigChanged()); - waitForPostedSwingRunnables(); + waitForSwing(); assertTrue(!saveDialog.isVisible()); ToolChest tc = tool.getProject().getLocalToolChest(); ToolTemplate config = tc.getToolTemplate("MyTestTool"); @@ -142,7 +141,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes while (saveDialog.isVisible()) { Thread.sleep(5); } - waitForPostedSwingRunnables(); + waitForSwing(); assertEquals("Name cannot have spaces.", msg); } @@ -156,7 +155,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes pressButtonByText(saveDialog, "Save"); assertTrue(!tool.hasConfigChanged()); - waitForPostedSwingRunnables(); + waitForSwing(); assertTrue(!saveDialog.isVisible()); ToolChest tc = tool.getProject().getLocalToolChest(); ToolTemplate template = tc.getToolTemplate("MyTestTool"); @@ -180,7 +179,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes while (saveDialog.isVisible()) { Thread.sleep(5); } - waitForPostedSwingRunnables(); + waitForSwing(); } @Test @@ -208,8 +207,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes final JButton browseButton = (JButton) findComponentByName(saveDialog, "BrowseButton"); pressButton(browseButton, false); - final GhidraFileChooser chooser = - waitForDialogComponent(GhidraFileChooser.class); + final GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class); assertNotNull(chooser); runSwing(() -> chooser.setSelectedFile(destFile)); @@ -240,7 +238,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes while (tc.getToolTemplate("MyTestTool") == null) { Thread.sleep(10); } - waitForPostedSwingRunnables(); + waitForSwing(); setText(toolNameField, "MyTestTool", false); @@ -256,10 +254,9 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes JButton saveButton = findButtonByText(saveDialog, "Save"); saveButton.getActionListeners()[0].actionPerformed(null); }); - waitForPostedSwingRunnables(); + waitForSwing(); - final OptionDialog d = - waitForDialogComponent(tool.getToolFrame(), OptionDialog.class, 2000); + final OptionDialog d = waitForDialogComponent(OptionDialog.class); assertNotNull(d); assertEquals("Overwrite Tool?", d.getTitle()); pressButtonByText(d.getComponent(), "Overwrite"); @@ -267,7 +264,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes while (d.isVisible()) { Thread.sleep(10); } - waitForPostedSwingRunnables(); + waitForSwing(); assertTrue(!tool.hasConfigChanged()); } @@ -282,11 +279,11 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes while (tc.getToolTemplate("MyTestTool") == null) { Thread.sleep(10); } - waitForPostedSwingRunnables(); + waitForSwing(); setText(toolNameField, "MyTestTool", false); - SwingUtilities.invokeAndWait(() -> { + runSwing(() -> { // force a change to the tool config try { tool.addPlugin(ByteViewerPlugin.class.getName()); @@ -301,10 +298,9 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes JButton saveButton = findButtonByText(saveDialog, "Save"); saveButton.getActionListeners()[0].actionPerformed(null); }); - waitForPostedSwingRunnables(); + waitForSwing(); - final OptionDialog d = - waitForDialogComponent(OptionDialog.class); + final OptionDialog d = waitForDialogComponent(OptionDialog.class); assertNotNull(d); assertEquals("Overwrite Tool?", d.getTitle()); pressButtonByText(d.getComponent(), "Cancel"); @@ -312,17 +308,16 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes while (d.isVisible()) { Thread.sleep(10); } - waitForPostedSwingRunnables(); + waitForSwing(); assertTrue(tool.hasConfigChanged()); } - ///////////////////////////////////////////////////////////////////// private void showDialogs() throws Exception { - List actions = - tool.getDockingActionsByFullActionName("Save Tool As (Tool)"); - performAction(actions.get(0), false); - waitForPostedSwingRunnables(); + + DockingActionIf action = getAction(tool, "Tool", "Save Tool As"); + performAction(action, false); + waitForSwing(); saveDialog = waitForDialogComponent(SaveToolConfigDialog.class); @@ -335,7 +330,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes private void setText(final JTextField field, final String text, final boolean doAction) throws Exception { - SwingUtilities.invokeAndWait(() -> { + runSwing(() -> { field.setText(text); if (doAction) { ActionListener[] listeners = field.getActionListeners(); @@ -344,6 +339,6 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes } } }); - waitForPostedSwingRunnables(); + waitForSwing(); } } diff --git a/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/Decompiler.htm b/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/Decompiler.htm index 9c8d5c8509..70789f6748 100644 --- a/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/Decompiler.htm +++ b/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/Decompiler.htm @@ -854,6 +854,22 @@

      +

      Graph AST Control Flow

      + +
      +

      Selecting Graph AST Control Flow, + from the decompiler provider window toolbar, will generate an abstract syntax tree (AST) + control flow graph based upon the decompiler results and render the graph within the + current Graph Service.

      + +
      +

      If no Graph Service is + available then this action will not be present. +

      +
      + +
      +

      Export to C

      diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java index 2dc61af896..6daad2cb23 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerCodeComparisonPanel.java @@ -27,6 +27,7 @@ import docking.action.DockingAction; import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.internal.FieldPanelCoordinator; import docking.widgets.fieldpanel.support.FieldLocation; +import docking.widgets.label.GDHtmlLabel; import ghidra.app.decompiler.DecompileOptions; import ghidra.app.util.viewer.listingpanel.ProgramLocationListener; import ghidra.app.util.viewer.util.CodeComparisonPanel; @@ -255,8 +256,13 @@ public abstract class DecompilerCodeComparisonPanel actions = - tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)"); - assertEquals("Could not find action: " + name, 1, actions.size()); + DockingActionIf action = getAction(tool, graphPlugin.getName(), name); long start = System.currentTimeMillis(); - performAction(actions.get(0), false); + performAction(action, false); Window window = waitForWindow("Reset Graph?"); pressButtonByText(window, "Yes"); @@ -917,20 +916,16 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte private void toggleSatellite() { String name = "Display Satellite View"; - List actions = - tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)"); - assertEquals("Could not find action: " + name, 1, actions.size()); - ToggleDockingAction dockAction = (ToggleDockingAction) actions.get(0); + DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name); + ToggleDockingAction dockAction = (ToggleDockingAction) action; performAction(dockAction, true); } protected void undockSatellite() { String name = "Dock Satellite View"; - List actions = - tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)"); - assertEquals("Could not find action: " + name, 1, actions.size()); - ToggleDockingAction dockAction = (ToggleDockingAction) actions.get(0); + DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name); + ToggleDockingAction dockAction = (ToggleDockingAction) action; assertTrue(name + " action is not selected as expected", dockAction.isSelected()); performAction(dockAction, true); @@ -938,11 +933,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte protected void redockSatellite() { String name = "Dock Satellite View"; - List actions = - tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)"); - assertEquals("Could not find action: " + name, 1, actions.size()); - ToggleDockingAction dockAction = (ToggleDockingAction) actions.get(0); + DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name); + ToggleDockingAction dockAction = (ToggleDockingAction) action; assertFalse(name + " action is not selected as expected", dockAction.isSelected()); performAction(dockAction, true); @@ -1210,10 +1203,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte protected FGController cloneGraph() { - List actions = tool.getDockingActionsByFullActionName( - "Function Graph Clone (" + graphPlugin.getName() + ")"); - assertEquals(1, actions.size()); - DockingActionIf snapshotAction = actions.get(0); + DockingActionIf snapshotAction = + AbstractDockingTest.getAction(tool, graphPlugin.getName(), "Function Graph Clone"); performAction(snapshotAction, true); @SuppressWarnings("unchecked") @@ -1429,6 +1420,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte assertSelected(ungroupedVertices); } + // @formatter:on protected void doTestGroupingProperlyTranslatesEdgesFromGroupedVerticesToRealVertices() { // @@ -1437,7 +1429,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // need to test this functionality, but we don't have a jComplicatedTest, so we will do // it here. // - + // // Desired Behavior: We want to be able to group vertices, group grouped vertices and then // ungroup them in any order. For us to be able to do this, our group @@ -1457,7 +1449,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // // The fix is mentioned in the Desired Behavior section. // - + /* 0) Initial Graph @@ -1468,26 +1460,26 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte 5 */ - + create12345Graph(); - + // // Our graph maps from number to address like so: // - + FGVertex v1 = vertex("100415a"); FGVertex v2 = vertex("1004178"); FGVertex v3 = vertex("1004192"); FGVertex v4 = vertex("1004196"); FGVertex v5 = vertex("100419c"); - + // verify initial graph verifyEdge(v1, v2); verifyEdge(v2, v3); verifyEdge(v3, v4); verifyEdge(v3, v5); verifyEdgeCount(4); - + /* 1) Create two separate group vertices (A and B), such that A has an edge to B. @@ -1497,14 +1489,14 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte 5 */ - + GroupedFunctionGraphVertex groupA = group("A", v1, v2); GroupedFunctionGraphVertex groupB = group("B", v3, v4); - + verifyEdge(groupA, groupB); verifyEdge(groupB, v5); verifyEdgeCount(2);// no other edges - + /* 2) Create a third group vertex (Z) that contains a non-grouped vertex *and* one of the other groups (B). @@ -1515,12 +1507,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte ) */ - + GroupedFunctionGraphVertex groupZ = group("Z", groupB, v5); - + verifyEdge(groupA, groupZ); verifyEdgeCount(1); - + /* 3) Now, ungroup the 1 remaining originally grouped vertex (A). @@ -1530,13 +1522,13 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte ) */ - + ungroup(groupA); - + verifyEdge(v1, v2); verifyEdge(v2, groupZ); verifyEdgeCount(2); - + /* 4) Now, ungroup Z and go back to having one remaining group vertex (B) @@ -1547,14 +1539,14 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte 5 */ - + ungroup(groupZ); - + verifyEdge(v1, v2); verifyEdge(v2, groupB); verifyEdge(groupB, v5); verifyEdgeCount(3); - + /* 5) Finally, ungroup the last group and make sure the graph is restored @@ -1564,15 +1556,15 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte 5 */ - + ungroup(groupB); - + verifyEdge(v1, v2); verifyEdge(v2, v3); verifyEdge(v3, v4); verifyEdge(v3, v5); verifyEdgeCount(4); - + } private void doTestRestoringWhenCodeBlocksHaveChanged_DoesntRegroup() { @@ -1587,28 +1579,28 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // be found by the regrouping algorithm. Furthermore, the regrouping will not take place // if at least two vertices cannot be found. // - + // // Pick a function and group some nodes. // FGData graphData = graphFunction("01002cf5"); FunctionGraph functionGraph = graphData.getFunctionGraph(); - + Set ungroupedVertices = selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */); - + group(ungroupedVertices); - + // 5 edges expected: // -01002cf5: 2 out // -01002cf5: 2 in, 1 out int expectedGroupedEdgeCount = 5; GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices( functionGraph, ungroupedVertices, expectedGroupedEdgeCount); - + AddressSetView addresses = groupedVertex.getAddresses(); Address minAddress = addresses.getMinAddress(); - + // // Ideally, we would like to save, close and re-open the program so that we can get // a round-trip saving and reloading. However, in the test environment, we cannot save @@ -1618,12 +1610,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // graphFunction("0100415a"); clearCache(); - + // // Add a label to trigger a code block change // createLabel("01002d18");// in the middle of the LAB_01002d11 code block - + // // Relaunch the graph, which will use the above persisted group settings... // @@ -1646,18 +1638,18 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // be found by the regrouping algorithm. Furthermore, the regrouping *will* still // take place, as at least two vertices cannot be found. // - + // // Pick a function and group some nodes. // FGData graphData = graphFunction("01002cf5"); FunctionGraph functionGraph = graphData.getFunctionGraph(); - + Set ungroupedVertices = selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */, "01002d1f" /* MyLocal */); - + group(ungroupedVertices); - + // 5 edges expected: // -01002cf5: 2 out // -01002d11: 2 in, (1 out that was removed) @@ -1665,11 +1657,11 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte int expectedGroupedEdgeCount = 6; GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices( functionGraph, ungroupedVertices, expectedGroupedEdgeCount); - + AddressSetView addresses = groupedVertex.getAddresses(); Address minAddress = addresses.getMinAddress(); Address maxAddress = addresses.getMaxAddress(); - + // // Ideally, we would like to save, close and re-open the program so that we can get // a round-trip saving and reloading. However, in the test environment, we cannot save @@ -1679,12 +1671,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // graphFunction("0100415a"); clearCache(); - + // // Add a label to trigger a code block change // Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block - + // // Relaunch the graph, which will use the above persisted group settings... // @@ -1694,22 +1686,22 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte FGVertex expectedGroupVertex = functionGraph.getVertexForAddress(minAddress); assertTrue(expectedGroupVertex instanceof GroupedFunctionGraphVertex); assertEquals(maxAddress, expectedGroupVertex.getAddresses().getMaxAddress()); - + // ...we expect that the two original grouped vertices have again been grouped... FGVertex splitVertex = functionGraph.getVertexForAddress(getAddress("01002d11") /* LAB_01002d11 */); assertTrue("The split vertex should not have been regrouped", !(splitVertex instanceof GroupedFunctionGraphVertex)); - + FGVertex unchangedVertex = functionGraph.getVertexForAddress(getAddress("01002cf5") /* ghidra */); assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex, (unchangedVertex instanceof GroupedFunctionGraphVertex)); - + unchangedVertex = functionGraph.getVertexForAddress(getAddress("01002d1f") /* MyLocal */); assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex, (unchangedVertex instanceof GroupedFunctionGraphVertex)); - + // ...but the newly created code block has not FGVertex newlyCreatedVertex = functionGraph.getVertexForAddress(labelAddress); assertNotNull(newlyCreatedVertex); @@ -1722,30 +1714,30 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // However, if the affected vertex is grouped, then the FG will not split the node, but // should still show the 'stale' indicator. // - + // // Pick a function and group some nodes. // FGData graphData = graphFunction("01002cf5"); FunctionGraph functionGraph = graphData.getFunctionGraph(); - + Set ungroupedVertices = selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */); - + group(ungroupedVertices); - + // 5 edges expected: // -01002cf5: 2 out // -01002cf5: 2 in, 1 out int expectedGroupedEdgeCount = 5; GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices( functionGraph, ungroupedVertices, expectedGroupedEdgeCount); - + // // Add a label to trigger a code block change // Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block - + // // Make sure the newly created code block does not have a corresponding vertex // @@ -1792,7 +1784,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte Msg.debug(this, "\t" + v); } } - + Assert.fail("Did not find group vertex at " + address + ". Instead found " + vertex); } return (GroupedFunctionGraphVertex) vertex; @@ -1819,7 +1811,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte protected FGData graphFunction(String functionAddress) { // Find a good test function. goToAddress(functionAddress); - + FGData graphData = getFunctionGraphData(); assertNotNull(graphData); assertTrue("Unexpectedly received an empty FunctionGraphData", graphData.hasResults()); @@ -1838,22 +1830,22 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte DockingAction action = (DockingAction) TestUtils.getInstanceField("groupAction", component); performAction(action, graphProvider, false); waitForAnimation(); - + MultiLineInputDialog dialog = waitForDialogComponent(MultiLineInputDialog.class); if (groupVertexText != null) { final JTextArea inputTextArea = (JTextArea) getInstanceField("inputTextArea", dialog); runSwing(() -> inputTextArea.setText(groupVertexText)); } - + pressButtonByText(dialog.getComponent(), "OK"); - + if (groupVertexText != null) { String value = dialog.getValue(); assertEquals("Group vertex text was not set in the dialog", groupVertexText, value); } - + waitForAnimation(); - + FGController controller = getFunctionGraphController(); FGData data = controller.getFunctionGraphData(); FunctionGraph fg = data.getFunctionGraph(); @@ -1861,19 +1853,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte } protected GroupedFunctionGraphVertex group(String groupName, FGVertex... vertices) { - + HashSet set = new HashSet<>(); for (FGVertex v : vertices) { set.add(v); } - + pickVertices(set); GroupedFunctionGraphVertex groupVertex = group(set, groupName); - + // for debugging Object componentPanel = getComponent(groupVertex); setInstanceField("title", componentPanel, groupName); - + return groupVertex; } @@ -1887,23 +1879,22 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte runSwing(() -> { PickedState pickedState = getPickedState(); pickedState.clear(); - + for (FGVertex vertex : vertices) { pickedState.pick(vertex, true); } }); } - protected GroupedFunctionGraphVertex regroup(FGVertex vertex) { - + DockingActionIf regroupAction = getRegroupAction(vertex); if (regroupAction == null) { Assert.fail("Did not find the regroup action on vertex: " + vertex.getTitle()); } performAction(regroupAction, false); waitForBusyGraph(); - + FGController controller = getFunctionGraphController(); FGData data = controller.getFunctionGraphData(); FunctionGraph fg = data.getFunctionGraph(); @@ -1912,12 +1903,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte private void removeEdge(FunctionGraph functionGraph, Address startAddress, Address destinationAddress) { - + Graph graph = functionGraph; - + FGVertex startVertex = functionGraph.getVertexForAddress(startAddress); FGVertex destinationVertex = functionGraph.getVertexForAddress(destinationAddress); - + FGEdge edge = graph.findEdge(startVertex, destinationVertex); runSwing(() -> graph.removeEdge(edge)); FGController controller = getFunctionGraphController(); @@ -1927,10 +1918,10 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte protected void removeFromUncollapsedGroup(FGVertex... vertices) { FunctionGraph functionGraph = getFunctionGraph(); selectVertices(functionGraph, vertices); - + DockingActionIf action = getAction(graphPlugin, "Remove From Group"); assertNotNull(action); - + performAction(action, graphProvider, false); waitForBusyGraph(); } @@ -1944,35 +1935,32 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte } protected FGData reset() { - List actions = - tool.getDockingActionsByFullActionName("Reset Graph (FunctionGraphPlugin)"); - assertEquals(1, actions.size()); - DockingActionIf action = actions.get(0); - + + DockingActionIf action = getAction(tool, graphPlugin.getName(), "Reset Graph"); performAction(action, graphProvider, false); - + OptionDialog dialog = waitForDialogComponent(OptionDialog.class); pressButtonByText(dialog, "Yes"); - + // wait for the threaded graph layout code return getFunctionGraphData(); } private Set selectVertices(FunctionGraph functionGraph, FGVertex... vertices) { - + Set set = new HashSet<>(); for (FGVertex vertex : vertices) { set.add(vertex); } - + pickVertices(set); - + waitForSwing(); return set; } protected Set selectVertices(FunctionGraph functionGraph, String... addressString) { - + Set vertices = new HashSet<>(); for (String string : addressString) { Address vertexAddress = getAddress(string); @@ -1980,26 +1968,26 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte assertNotNull("No vertex for address: " + vertexAddress, vertex); vertices.add(vertex); } - + pickVertices(vertices); - + waitForSwing(); return vertices; } protected void setGroupText(GroupedFunctionGraphVertex group, final String newText) { - + final GroupedFunctionGraphVertex updatedGroup = update(group); runSwing(() -> updatedGroup.editLabel(null), false); - + Window window = waitForWindow("Enter Group Vertex Text"); assertNotNull(window); - + final JTextArea textArea = findComponent(window, JTextArea.class); runSwing(() -> textArea.setText(newText)); - + pressButtonByText(window, "OK"); - + waitForSwing(); } @@ -2018,9 +2006,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte action.setCurrentActionState(state); } } - + }); - + } protected FGData triggerPersistence(String functionAddress) { @@ -2033,7 +2021,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // graphFunction("0100415a"); clearCache(); - + // // Graph the original function and make sure that the previously grouped nodes is again // grouped. @@ -2046,9 +2034,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte // Unusual Code: for some of the regroup actions, group vertices are created and restored, // but are always equal. Thus, the Function Graph works correctly, but the // test can get out-of-sync, so we update before we use it - + groupedVertex = getGroupVertex(getFunctionGraph(), groupedVertex.getVertexAddress()); - + ungroup(groupedVertex); } @@ -2063,45 +2051,42 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte } protected void ungroupAll() { - List actions = - tool.getDockingActionsByFullActionName("Ungroup All Vertices (FunctionGraphPlugin)"); - assertEquals(1, actions.size()); - DockingActionIf action = actions.get(0); - + + DockingActionIf action = getAction(tool, "FunctionGraphPlugin", "Ungroup All Vertices"); performAction(action, graphProvider, false); - + OptionDialog dialog = waitForDialogComponent(OptionDialog.class); pressButtonByText(dialog, "Yes"); - + // wait for the threaded graph layout code waitForBusyGraph(); } - //================================================================================================== - // Private Methods - //================================================================================================== - - private GroupedFunctionGraphVertex update(GroupedFunctionGraphVertex group) { - // Unusual Code: for some of the regroup actions, group vertices are created and restored, - // but are always equal. Thus, the Function Graph works correctly, but the - // test can get out-of-sync, so we update before we use it - return getGroupVertex(getFunctionGraph(), group.getVertexAddress()); - } +//================================================================================================== +// Private Methods +//================================================================================================== + + private GroupedFunctionGraphVertex update(GroupedFunctionGraphVertex group) { + // Unusual Code: for some of the regroup actions, group vertices are created and restored, + // but are always equal. Thus, the Function Graph works correctly, but the + // test can get out-of-sync, so we update before we use it + return getGroupVertex(getFunctionGraph(), group.getVertexAddress()); + } protected GroupedFunctionGraphVertex validateNewGroupedVertexFromVertices( FunctionGraph functionGraph, Set vertices, int expectedGroupedEdgeCount) { - + FGVertex aVertex = vertices.iterator().next(); FGVertex currentVertex = functionGraph.getVertexForAddress(aVertex.getVertexAddress()); assertTrue(currentVertex instanceof GroupedFunctionGraphVertex); GroupedFunctionGraphVertex groupedVertex = (GroupedFunctionGraphVertex) currentVertex; - + // // make sure we have new edges // Graph graph = functionGraph; Collection groupedEdges = graph.getIncidentEdges(groupedVertex); - + assertEquals("Ungrouped edges not replaced with new edges for the grouped vertex", expectedGroupedEdgeCount, groupedEdges.size()); assertSelected(groupedVertex); @@ -2127,7 +2112,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte FGData data = getFunctionGraphData(); FunctionGraph functionGraph = data.getFunctionGraph(); Graph graph = functionGraph; - + FGEdge edge = graph.findEdge(start, destination); assertNotNull("No edge exists for vertices: " + start + " and " + destination, edge); } @@ -2166,7 +2151,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte ComponentProvider provider = tool.getComponentProvider(FGSatelliteUndockedProvider.NAME); assertUndockedProviderShowing(provider); } - + protected void assertUndockedProviderShowing(ComponentProvider satellite) { assertNotNull("Undocked provider is not installed when it should be", satellite); assertTrue("Undocked provider is not showing after being undocked", satellite.isVisible()); @@ -2176,7 +2161,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte Double scale = getGraphScale(getPrimaryGraphViewer()); int result = Double.compare(scale, 1.0); assertEquals("Graph not fully zoomed-in; scale: " + scale, 0, result); - + FGVertex v = getFocusedVertex(); Rectangle cursorBounds = v.getCursorBounds(); Window graphWindow = windowForComponent(getPrimaryGraphViewer()); @@ -2196,13 +2181,13 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte DockingAction action = (DockingAction) TestUtils.getInstanceField("chooseColorAction", setColorAction); performAction(action, graphProvider, false); - + Window chooserWindow = waitForWindow("Please Select Background Color"); Object colorChooserDialog = chooserWindow;// the name is the real type JColorChooser chooser = (JColorChooser) TestUtils.getInstanceField("chooserPane", colorChooserDialog); chooser.setColor(testColor); - + JButton okButton = findButtonByText(chooserWindow, "OK"); runSwing(() -> okButton.doClick()); waitForSwing(); @@ -2216,32 +2201,32 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte } protected void debugAction(DockingAction copyAction, ActionContext context) { - + Msg.debug(this, "Copy action not enabled at location " + codeBrowser.getCurrentLocation()); - + FGVertex focusedVertex = getFocusedVertex(); Msg.debug(this, "\tfocused vertex: " + focusedVertex); - + Msg.debug(this, "\tcontext: " + context); - + // Figure out which check in the action failed Object clipboardService = getInstanceField("clipboardService", copyAction); Msg.debug(this, "\tservice: " + clipboardService); - + Boolean result = (Boolean) invokeInstanceMethod("isValidContext", clipboardService, new Class[] { ActionContext.class }, new Object[] { context }); Msg.debug(this, "\tisValidContext()?: " + result); - + result = (Boolean) invokeInstanceMethod("canCopy", clipboardService); Msg.debug(this, "\tcanCopy: " + result); - + Boolean copyFromSelectionEnabled = (Boolean) getInstanceField("copyFromSelectionEnabled", clipboardService); Msg.debug(this, "\tcopyFromSelectionEnabled: " + copyFromSelectionEnabled); - + String stringContent = (String) getInstanceField("stringContent", clipboardService); Msg.debug(this, "\tstringContent: " + stringContent); - + Object location = getInstanceField("currentLocation", clipboardService); Msg.debug(this, "\tservice location: " + location); } @@ -2258,7 +2243,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte ProgramLocation location = getLocationForAddressString(startAddressString); assertTrue(graphData.containsLocation(location)); FunctionGraph functionGraph = graphData.getFunctionGraph(); - + // locate vertex with cursor FGVertex focusedVertex = getFocusVertex(functionGraph); assertNotNull("We did not start with a focused vertex", focusedVertex); @@ -2274,7 +2259,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte protected SetVertexMostRecentColorAction getSetMostRecentColorAction(FGVertex vertex) { // this action is odd in that it is not installed in the tool, but is owned by each // vertex directly - + JComponent internalGraphComponent = vertex.getComponent(); return (SetVertexMostRecentColorAction) getInstanceField("setVertexMostRecentAction", internalGraphComponent); @@ -2284,7 +2269,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte Address addr = getAddress(address); GoToService goToService = tool.getService(GoToService.class); goToService.goTo(addr); - + waitForBusyGraph(); } @@ -2299,11 +2284,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte protected void navigateBack() { String name = "Previous in History Buffer"; - List actions = - tool.getDockingActionsByFullActionName(name + " (NextPrevAddressPlugin)"); - assertEquals("Could not find action: " + name, 1, actions.size()); - - performAction(actions.get(0), true); + + DockingActionIf action = getAction(tool, "NextPrevAddressPlugin", name); + performAction(action, true); waitForBusyGraph(); } @@ -2320,18 +2303,16 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte } protected void setGraphWindowSize(final int width, final int height) { - + final Window graphWindow = windowForComponent(getPrimaryGraphViewer()); runSwing(() -> graphWindow.setSize(width, height)); } protected void toggleSatalliteVisible(boolean expectedVisible) { String name = "Display Satellite View"; - List actions = - tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)"); - assertEquals("Could not find action: " + name, 1, actions.size()); - - ToggleDockingAction displayAction = (ToggleDockingAction) actions.get(0); + + DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name); + ToggleDockingAction displayAction = (ToggleDockingAction) action; setToggleActionSelected(displayAction, new ActionContext(), expectedVisible); // // // make sure the action is not already in the state we expect diff --git a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin2Test.java b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin2Test.java index c41cc8923e..18a8872892 100644 --- a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin2Test.java +++ b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin2Test.java @@ -177,10 +177,8 @@ public class FunctionGraphPlugin2Test extends AbstractFunctionGraphTest { GraphPerspectiveInfo primaryPerspective = primaryController.getGraphPerspective(location); - List actions = tool.getDockingActionsByFullActionName( - "Function Graph Clone (" + graphPlugin.getName() + ")"); - assertEquals(1, actions.size()); - DockingActionIf snapshotAction = actions.get(0); + DockingActionIf snapshotAction = + getAction(tool, graphPlugin.getName(), "Function Graph Clone"); performAction(snapshotAction, true); assertEquals(1, disconnectedProviders.size()); diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java index 5beaf398c9..8c5e83da10 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java @@ -21,7 +21,7 @@ import java.awt.*; import java.awt.event.ActionListener; import java.io.IOException; import java.util.Date; -import java.util.List; +import java.util.Set; import javax.swing.*; @@ -589,7 +589,7 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest { } public static DockingActionIf getToolAction(PluginTool tool, String name) { - List actions = tool.getDockingActionsByOwnerName("Tool"); + Set actions = getActionsByOwner(tool, "Tool"); for (DockingActionIf action : actions) { if (name.equals(action.getName())) { return action; diff --git a/Ghidra/Features/VersionTracking/certification.manifest b/Ghidra/Features/VersionTracking/certification.manifest index 8a76937bd3..ff365641b7 100644 --- a/Ghidra/Features/VersionTracking/certification.manifest +++ b/Ghidra/Features/VersionTracking/certification.manifest @@ -33,7 +33,6 @@ src/main/help/help/topics/VersionTrackingPlugin/images/DataRefCorr_ExactSelectAl src/main/help/help/topics/VersionTrackingPlugin/images/DataRefCorr_options.png||GHIDRA||||END| src/main/help/help/topics/VersionTrackingPlugin/images/DataRefCorr_refined.png||GHIDRA||||END| src/main/help/help/topics/VersionTrackingPlugin/images/DataRefCorrelator_Setup.png||GHIDRA||||END| -src/main/help/help/topics/VersionTrackingPlugin/images/FuncRefCorr_MatchTableFilters.png||GHIDRA||||END| src/main/help/help/topics/VersionTrackingPlugin/images/FuncRefCorr_MatchTableFilters_AssocStatus.png||GHIDRA||||END| src/main/help/help/topics/VersionTrackingPlugin/images/FuncRefCorr_defaultOptions_results.png||GHIDRA||||END| src/main/help/help/topics/VersionTrackingPlugin/images/FuncRefCorr_lowerScoreThresh.png||GHIDRA||||END| @@ -67,7 +66,6 @@ src/main/help/help/topics/VersionTrackingPlugin/images/VersionTrackingTool.png|| src/main/help/help/topics/VersionTrackingPlugin/images/accepted_fully_applied.png||GHIDRA||reviewed||END| src/main/help/help/topics/VersionTrackingPlugin/images/accepted_fully_considered.png||GHIDRA||reviewed||END| src/main/help/help/topics/VersionTrackingPlugin/images/accepted_warning.png||GHIDRA||reviewed||END| -src/main/help/help/topics/VersionTrackingPlugin/images/add.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/VersionTrackingPlugin/images/application_tile_horizontal.png||FAMFAMFAM Icons - CC 2.5|||fam fam|END| src/main/help/help/topics/VersionTrackingPlugin/images/asterisk_orange.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/help/help/topics/VersionTrackingPlugin/images/cache.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set. LGPL 2.1. Copied from nuvola/16x16/cache.png.|END| @@ -94,7 +92,6 @@ src/main/help/help/topics/VersionTrackingPlugin/images/table_go.png||FAMFAMFAM I src/main/help/help/topics/VersionTrackingPlugin/images/tag_blue.png||FAMFAMFAM Icons - CC 2.5|||fam fam|END| src/main/help/help/topics/VersionTrackingPlugin/images/tag_blue_delete.png||FAMFAMFAM Icons - CC 2.5|||famfam|END| src/main/help/help/topics/VersionTrackingPlugin/images/tag_blue_edit.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set - Copied from famfamfam_silk/icons/tag_blue_edit.png|END| -src/main/help/help/topics/VersionTrackingPlugin/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/VersionTrackingPlugin/images/undo-apply.png||Nuvola Icons - LGPL 2.1|||originally named undo.png|END| src/main/help/help/topics/VersionTrackingPlugin/images/unknown.gif||GHIDRA||reviewed|Copied from GhidraIcons/unknown.gif|END| src/main/help/help/topics/VersionTrackingPlugin/images/view-filter.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/text_align_justify.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/text_align_justify.png deleted file mode 100644 index 2fbdd6920a..0000000000 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/text_align_justify.png and /dev/null differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Functions_Table.html b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Functions_Table.html index 751cc1da01..7637b86497 100644 --- a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Functions_Table.html +++ b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Functions_Table.html @@ -124,7 +124,7 @@

      Select Existing Match

      The - Select Existing Match action () will + Select Existing Match action () will select the existing match in the matches table. To use this action you must have one function selected in source table and one function selected in the destination table. Further, the action will only be enabled if a match exists for the two diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Matches_Table.html b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Matches_Table.html index 27ed653675..ee9a30a72c 100644 --- a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Matches_Table.html +++ b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Matches_Table.html @@ -319,7 +319,7 @@

      The Remove Match action will remove a manually created match from the matches table.

      -

      The Make Selections +

      The Make Selections action will create selections in the source and destination tools for all matches selected in the table.

      diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Related_Associations_Table.html b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Related_Associations_Table.html index 22de124a28..4c46eac2e5 100644 --- a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Related_Associations_Table.html +++ b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Related_Associations_Table.html @@ -112,7 +112,7 @@

      Select Match in VT - Matches Table

      + Matches Table

      As you select various matches in either Related Matches table, the other @@ -128,7 +128,7 @@

      • Click the toolbar button, .
      • + "Icons.MAKE_SELECTION_ICON">.
      • Right-click on a row in the Related Matches table to get a popup menu. From the popup menu choose Select Match in VT Matches Table.
      • 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 3ca1573024..a40c0351e8 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 @@ -17,9 +17,11 @@ package ghidra.feature.vt.gui.plugin; import java.net.URL; import java.util.List; +import java.util.Set; import javax.swing.*; +import docking.action.DockingActionIf; import docking.help.Help; import docking.help.HelpService; import docking.wizard.WizardManager; @@ -121,14 +123,29 @@ public class VTPlugin extends Plugin { createActions(); registerServiceProvided(VTController.class, controller); tool.setUnconfigurable(); - tool.removeAction(tool.getDockingActionsByFullActionName("Save Tool As (Tool)").get(0)); - tool.removeAction(tool.getDockingActionsByFullActionName("Export Tool (Tool)").get(0)); + + DockingActionIf saveAs = getToolAction("Save Tool As"); + tool.removeAction(saveAs); + + DockingActionIf export = getToolAction("Export Tool"); + tool.removeAction(export); + new MatchStatusUpdaterAssociationHook(controller); new ImpliedMatchAssociationHook(controller); initializeOptions(); } + private DockingActionIf getToolAction(String actionName) { + Set actions = tool.getDockingActionsByOwnerName("Tool"); + for (DockingActionIf action : actions) { + if (action.getName().equals(actionName)) { + return action; + } + } + throw new IllegalArgumentException("Unable to find Tool action '" + actionName + "'"); + } + private void initializeOptions() { Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY); options.registerOptionsEditor(new ListingDisplayOptionsEditor(this, options)); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTSubToolManager.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTSubToolManager.java index 87b9fa5876..2657030f55 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTSubToolManager.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTSubToolManager.java @@ -17,8 +17,7 @@ package ghidra.feature.vt.gui.plugin; import java.awt.Component; import java.io.*; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import javax.swing.KeyStroke; @@ -27,6 +26,7 @@ import org.jdom.output.XMLOutputter; import docking.ActionContext; import docking.action.*; +import docking.tool.util.DockingToolConstants; import docking.widgets.OptionDialog; import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.support.FieldSelection; @@ -153,17 +153,17 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList catch (PluginException e) { Msg.error(this, "Failed to create subordinate tool: " + toolName); } + newTool.setToolName(toolName); - newTool.removeAction(newTool.getDockingActionsByFullActionName("Save Tool (Tool)").get(0)); - newTool.removeAction( - newTool.getDockingActionsByFullActionName("Save Tool As (Tool)").get(0)); - // newTool.removeAction(newTool.getDockableActionsByFullActionName("Export - // Tool (Tool)").get(0)); + + DockingActionIf save = getToolAction(newTool, "Save Tool"); + newTool.removeAction(save); + createMarkupActions(newTool); newTool.setConfigChanged(false); - ToolOptions options = newTool.getOptions(ToolConstants.KEY_BINDINGS); + ToolOptions options = newTool.getOptions(DockingToolConstants.KEY_BINDINGS); options.addOptionsChangeListener(this); // custom VT actions @@ -172,6 +172,16 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList return newTool; } + private DockingActionIf getToolAction(Tool tool, String actionName) { + Set actions = tool.getDockingActionsByOwnerName("Tool"); + for (DockingActionIf action : actions) { + if (action.getName().equals(actionName)) { + return action; + } + } + throw new IllegalArgumentException("Unable to find Tool action '" + actionName + "'"); + } + @Override public void optionsChanged(ToolOptions options, String optionName, Object oldValue, Object newValue) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java index 89bf204e42..5902ab81b9 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java @@ -17,12 +17,11 @@ package docking; import java.awt.*; import java.util.*; -import java.util.List; import javax.swing.JFrame; import docking.action.DockingActionIf; -import docking.actions.DockingToolActionManager; +import docking.actions.ToolActions; import ghidra.framework.options.ToolOptions; import ghidra.util.SystemUtilities; @@ -33,7 +32,7 @@ import ghidra.util.SystemUtilities; public abstract class AbstractDockingTool implements DockingTool { protected DockingWindowManager winMgr; - protected DockingToolActionManager actionMgr; + protected ToolActions actionMgr; protected Map optionsMap = new HashMap<>(); protected boolean configChangedFlag; @@ -116,22 +115,16 @@ public abstract class AbstractDockingTool implements DockingTool { } @Override - public List getAllActions() { - return actionMgr.getAllActions(); - } - - @Override - public List getDockingActionsByOwnerName(String owner) { - List actions = actionMgr.getActions(owner); + public Set getAllActions() { + Set actions = actionMgr.getAllActions(); + ActionToGuiMapper am = winMgr.getActionManager(); + actions.addAll(am.getAllActions()); return actions; } @Override - public List getDockingActionsByFullActionName(String fullActionName) { - Set set = new HashSet<>(); - set.addAll(actionMgr.getDockingActionsByFullActionName(fullActionName)); - set.addAll(winMgr.getActions(fullActionName)); - return new ArrayList<>(set); + public Set getDockingActionsByOwnerName(String owner) { + return actionMgr.getActions(owner); } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiHelper.java b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiHelper.java new file mode 100644 index 0000000000..1400d776da --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiHelper.java @@ -0,0 +1,81 @@ +/* ### + * 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; + +import java.util.Iterator; + +import docking.action.DockingActionIf; + +/** + * A class that exists primarily to provide access to action-related package-level methods of the + * {@link DockingWindowManager}. This allows the manager's interface to hide methods that + * don't make sense for public consumption. + */ +public class ActionToGuiHelper { + + private DockingWindowManager windowManager; + + public ActionToGuiHelper(DockingWindowManager windowManager) { + this.windowManager = windowManager; + } + + /** + * Adds an action to the global menu or toolbar which appear in the main frame. If the action + * has a menu path, it will be in the menu. If it has an icon, it will appear in the toolbar. + * @param action the action to be added + */ + public void addToolAction(DockingActionIf action) { + windowManager.addToolAction(action); + } + + /** + * Removes the given action from the global menu and toolbar + * @param action the action to be removed + */ + public void removeToolAction(DockingActionIf action) { + windowManager.removeToolAction(action); + } + + /** + * Adds an action that will be associated with the given provider. These actions will + * appear in the local header for the component as a toolbar button or a drop-down menu + * item if it has an icon and menu path respectively. + * + * @param provider the provider whose header on which the action is to be placed + * @param action the action to add to the providers header bar + */ + public void addLocalAction(ComponentProvider provider, DockingActionIf action) { + windowManager.addLocalAction(provider, action); + } + + /** + * Get an iterator over the actions for the given provider + * @param provider the component provider for which to iterate over all its owned actions + * @return null if the provider does not exist in the window manager + */ + public Iterator getComponentActions(ComponentProvider provider) { + return windowManager.getComponentActions(provider); + } + + /** + * Removes the action from the given provider's header bar. + * @param provider the provider whose header bar from which the action should be removed. + * @param action the action to be removed from the provider's header bar. + */ + public void removeProviderAction(ComponentProvider provider, DockingActionIf action) { + windowManager.removeProviderAction(provider, action); + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java similarity index 80% rename from Ghidra/Framework/Docking/src/main/java/docking/DockingActionManager.java rename to Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java index 2a9f17469a..d70ac94189 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java @@ -28,7 +28,7 @@ import ghidra.util.*; /** * Manages the global actions for the menu and toolbar. */ -public class DockingActionManager { +public class ActionToGuiMapper { private HashSet globalActions = new LinkedHashSet<>(); @@ -44,12 +44,7 @@ public class DockingActionManager { private PopupActionManager popupActionManager; private DockingAction keyBindingsAction; - /** - * Constructs a new ActionManager - * @param frame the frame to contain the menu and toolbar. - * @param enableDiagnosticActions if true additional diagnostic actions will enabled - */ - DockingActionManager(DockingWindowManager winMgr) { + ActionToGuiMapper(DockingWindowManager winMgr) { menuGroupMap = new MenuGroupMap(); menuBarMenuHandler = new MenuBarMenuHandler(winMgr); @@ -90,9 +85,10 @@ public class DockingActionManager { /** * Register a specific Help content location for a component. * The DocWinListener will be notified with the help location if the specified - * component 'c' has focus and the help key is pressed. + * component 'c' has focus and the help key is pressed. + * * @param c component - * @param helpURL help content URL + * @param helpLocation the help location */ static void setHelpLocation(JComponent c, HelpLocation helpLocation) { DockingWindowManager.getHelpService().registerHelp(c, helpLocation); @@ -149,41 +145,18 @@ public class DockingActionManager { globalActions.remove(action); } - public List getAllDockingActionsByFullActionName(String fullActionName) { + public Set getAllActions() { // Note: this method is called by non-Swing test code. Synchronize access to the // data structures in this class in order to prevent concurrent mod exceptions. - List actions = new ArrayList<>(); + Set actions = new HashSet<>(); SystemUtilities.runSwingNow(() -> { - actions.addAll(getGlobalDockingActionsByFullActionName(fullActionName)); - actions.addAll(getLocalDockingActionsByFullActionName(fullActionName)); + actions.addAll(globalActions); + actions.addAll(keyBindingsManager.getLocalActions()); }); return actions; } - private List getGlobalDockingActionsByFullActionName(String fullActionName) { - List matchingActions = new ArrayList<>(); - ArrayList existingAction = new ArrayList<>(globalActions); - for (DockingActionIf action : existingAction) { - if (fullActionName.equals(action.getFullName())) { - matchingActions.add(action); - } - } - return matchingActions; - } - - private List getLocalDockingActionsByFullActionName(String fullActionName) { - List matchingActions = new ArrayList<>(); - ArrayList existingAction = - new ArrayList<>(keyBindingsManager.getLocalActions()); - for (DockingActionIf action : existingAction) { - if (fullActionName.equals(action.getFullName())) { - matchingActions.add(action); - } - } - return matchingActions; - } - public Action getDockingKeyAction(KeyStroke keyStroke) { return keyBindingsManager.getDockingKeyAction(keyStroke); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java index 70f06fca41..b9f35aa436 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java @@ -28,6 +28,8 @@ import org.jdesktop.animation.timing.TimingTargetAdapter; import docking.action.ActionContextProvider; import docking.action.DockingActionIf; +import docking.actions.ActionAdapter; +import docking.actions.KeyBindingUtils; import docking.event.mouse.GMouseListenerAdapter; import docking.menu.DockingToolbarButton; import docking.util.*; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java index 2e26661030..cc7270ca87 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java @@ -69,7 +69,7 @@ public class DialogComponentProviderPopupActionManager { return; } - DockingActionManager actionManager = dwm.getActionManager(); + ActionToGuiMapper actionManager = dwm.getActionManager(); MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap(); MenuManager menuMgr = new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java index 758d175f9d..ad7a2b5feb 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java @@ -52,7 +52,7 @@ public class DockableComponent extends JPanel implements ContainerListener { private JComponent providerComp; private Component focusedComponent; private DockingWindowManager winMgr; - private DockingActionManager actionMgr; + private ActionToGuiMapper actionMgr; private DropTarget dockableDropTarget; /** diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java index b37a79d643..b74d9deff5 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java @@ -60,7 +60,7 @@ class DockableToolBarManager { ComponentPlaceholder placeholder = dockableComp.getComponentWindowingPlaceholder(); DockingWindowManager winMgr = dockableComp.getComponentWindowingPlaceholder().getNode().winMgr; - DockingActionManager actionManager = winMgr.getActionManager(); + ActionToGuiMapper actionManager = winMgr.getActionManager(); menuGroupMap = actionManager.getMenuGroupMap(); MenuHandler menuHandler = actionManager.getMenuHandler(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java index bc8d59b66a..23c25fbc2c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java @@ -16,13 +16,17 @@ package docking; import java.awt.Window; -import java.util.List; +import java.util.Set; import javax.swing.ImageIcon; import docking.action.DockingActionIf; import ghidra.framework.options.ToolOptions; +/** + * Generic tool interface for managing {@link ComponentProvider}s and + * {@link DockingActionIf actions} + */ public interface DockingTool { /** @@ -125,25 +129,27 @@ public interface DockingTool { public void removeLocalAction(ComponentProvider componentProvider, DockingActionIf action); /** - * Return a list of all actions in the tool. - * @return list of all actions + * Return a set of all actions in the tool. + * + *

        Note: the result may contain conceptually duplicate actions, which is when multiple + * actions exist that share the same full name (the full name is the action name with the + * owner name, such as "My Action (MyPlugin)". + * + * @return set of all actions */ - public List getAllActions(); + public Set getAllActions(); /** * Returns all actions for the given owner + * + *

        Note: the result may contain conceptually duplicate actions, which is when multiple + * actions exist that share the same full name (the full name is the action name with the + * owner name, such as "My Action (MyPlugin)". + * * @param owner the action owner's name * @return the actions */ - public List getDockingActionsByOwnerName(String owner); - - /** - * Return an list of actions with the given full name - * @param fullActionName action name that includes the owner's name in - * parentheses, e.g. "MyAction (MyPlugin)" - * @return the actions - */ - public List getDockingActionsByFullActionName(String fullActionName); + public Set getDockingActionsByOwnerName(String owner); /** * Shows or hides the component provider in the tool diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 8b820875a4..7723085e4b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -60,8 +60,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder final static String COMPONENT_MENU_NAME = "Window"; - private final static List EMPTY_LIST = Collections.emptyList(); - private static DockingActionIf actionUnderMouse; private static Object objectUnderMouse; @@ -89,7 +87,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder private Map providerNameCache = new HashMap<>(); private Map preferenceStateMap = new HashMap<>(); private DockWinListener docListener; - private DockingActionManager actionManager; + private ActionToGuiMapper actionManager; private WeakSet contextListeners = WeakDataStructureFactory.createSingleThreadAccessWeakSet(); @@ -140,7 +138,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder } root = new RootNode(this, toolName, images, modal, factory); - actionManager = new DockingActionManager(this); + actionManager = new ActionToGuiMapper(this); KeyboardFocusManager km = KeyboardFocusManager.getCurrentKeyboardFocusManager(); km.addPropertyChangeListener("permanentFocusOwner", this); @@ -161,7 +159,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * @param enable */ public static void enableDiagnosticActions(boolean enable) { - DockingActionManager.enableDiagnosticActions(enable); + ActionToGuiMapper.enableDiagnosticActions(enable); } /** @@ -293,7 +291,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * @param helpLocation help content location */ public static void setHelpLocation(JComponent c, HelpLocation helpLocation) { - DockingActionManager.setHelpLocation(c, helpLocation); + ActionToGuiMapper.setHelpLocation(c, helpLocation); } /** @@ -338,7 +336,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return placeholderManager; } - DockingActionManager getActionManager() { + ActionToGuiMapper getActionManager() { return actionManager; } @@ -643,19 +641,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder placeholderManager.removeComponent(provider); } - /** - * Get an iterator over the actions for the given provider. - * @param provider the component provider for which to iterate over all its owned actions. - * @return null if the provider does not exist in this window manager - */ - public Iterator getComponentActions(ComponentProvider provider) { - ComponentPlaceholder placeholder = getActivePlaceholder(provider); - if (placeholder != null) { - return placeholder.getActions(); - } - return EMPTY_LIST.iterator(); - } - /** * Removes all components and actions associated with the given owner. * @param owner the name of the owner whose associated component and actions should be removed. @@ -666,14 +651,29 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder scheduleUpdate(); } - /** - * Adds an action that will be associated with the given provider. These actions will - * appear in the local header for the component as a toolbar button or a drop-down menu - * item if it has an icon and menu path respectively. - * @param provider the provider whose header on which the action is to be placed. - * @param action the action to add to the providers header bar. - */ - public void addLocalAction(ComponentProvider provider, DockingActionIf action) { +//================================================================================================== +// Package-level Action Methods +//================================================================================================== + + Iterator getComponentActions(ComponentProvider provider) { + ComponentPlaceholder placeholder = getActivePlaceholder(provider); + if (placeholder != null) { + return placeholder.getActions(); + } + + List emptyList = Collections.emptyList(); + return emptyList.iterator(); + } + + void removeProviderAction(ComponentProvider provider, DockingActionIf action) { + ComponentPlaceholder placeholder = getActivePlaceholder(provider); + if (placeholder != null) { + actionManager.removeLocalAction(action); + placeholder.removeAction(action); + } + } + + void addLocalAction(ComponentProvider provider, DockingActionIf action) { ComponentPlaceholder placeholder = getActivePlaceholder(provider); if (placeholder == null) { throw new IllegalArgumentException("Unknown component provider: " + provider); @@ -682,42 +682,19 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder actionManager.addLocalAction(action, provider); } - /** - * Removes the action from the given provider's header bar. - * @param provider the provider whose header bar from which the action should be removed. - * @param action the action to be removed from the provider's header bar. - */ - public void removeProviderAction(ComponentProvider provider, DockingActionIf action) { - ComponentPlaceholder placeholder = getActivePlaceholder(provider); - if (placeholder != null) { - actionManager.removeLocalAction(action); - placeholder.removeAction(action); - } - } - - /** - * Adds an action to the global menu or toolbar which appear in the main frame. If - * the action has a menu path, it will be in the menu. If it has an icon, it will - * appear in the toolbar. - * @param action the action to be added. - */ - public void addToolAction(DockingActionIf action) { + void addToolAction(DockingActionIf action) { actionManager.addToolAction(action); scheduleUpdate(); } - /** - * Removes the given action from the global menu and toolbar. - * @param action the action to be removed. - */ - public void removeToolAction(DockingActionIf action) { + void removeToolAction(DockingActionIf action) { actionManager.removeToolAction(action); scheduleUpdate(); } - public Collection getActions(String fullActionName) { - return actionManager.getAllDockingActionsByFullActionName(fullActionName); - } +//================================================================================================== +// End Package-level Methods +//================================================================================================== /** * Hides or shows the component associated with the given provider. diff --git a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java index 823dec5538..9be430b4dc 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java @@ -130,7 +130,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener { } private List getActionsForWindow(WindowNode windowNode) { - DockingActionManager actionManager = windowManager.getActionManager(); + ActionToGuiMapper actionManager = windowManager.getActionManager(); Collection globalActions = actionManager.getGlobalActions(); List actionsForWindow = new ArrayList<>(globalActions.size()); Set> contextTypes = windowNode.getContextTypes(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java b/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java index 59ff49f7f4..d1f6bffeb3 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java @@ -25,7 +25,7 @@ import java.awt.event.KeyListener; import javax.swing.*; import javax.swing.text.JTextComponent; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; import ghidra.util.bean.GGlassPane; import ghidra.util.exception.AssertException; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java index c91695342e..63fecf7ea9 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java @@ -51,11 +51,6 @@ import utilities.util.reflection.ReflectionUtilities; * method allows actions to manage their own enablement. Otherwise, the default behavior for this * method is to return the current enabled property of the action. This allows for the possibility * for plugins to manage the enablement of its actions. - *

        - * By default, actions that are not enabledForContext do not appear in the popup menu. To change - * that behavior, implementors can also override {@link #deleteThisContextMethod(ActionContext)}. - * This method is used to determine if the action should appear on the popup menu based on the given - * context. */ public abstract class DockingAction implements DockingActionIf { @@ -118,21 +113,6 @@ public abstract class DockingAction implements DockingActionIf { return getName() + " (" + getOwner() + ")"; } - @Override - public KeyBindingData getKeyBindingData() { - return keyBindingData; - } - - @Override - public KeyBindingData getDefaultKeyBindingData() { - return defaultKeyBindingData; - } - - @Override - public KeyStroke getKeyBinding() { - return keyBindingData == null ? null : keyBindingData.getKeyBinding(); - } - @Override public MenuData getMenuBarData() { return menuBarData; @@ -278,52 +258,21 @@ public abstract class DockingAction implements DockingActionIf { return menuItem; } -//================================================================================================== -// Non interface methods -//================================================================================================== - - /** - * Sets the {@link #MenuData} to be used to put this action on the tool's menu bar. - * @param newMenuData the MenuData to be used to put this action on the tool's menu bar. - */ - public void setMenuBarData(MenuData newMenuData) { - MenuBarData oldData = menuBarData; - MenuBarData newDataCopy = newMenuData == null ? null : new MenuBarData(this, newMenuData); - - menuBarData = newDataCopy; - firePropertyChanged(MENUBAR_DATA_PROPERTY, oldData, newDataCopy); + @Override + public KeyStroke getKeyBinding() { + return keyBindingData == null ? null : keyBindingData.getKeyBinding(); } - /** - * Sets the {@link #MenuData} to be used to put this action in the tool's popup menu. - * @param newMenuData the MenuData to be used to put this action on the tool's popup menu. - */ - public void setPopupMenuData(MenuData newMenuData) { - PopupMenuData oldData = popupMenuData; - PopupMenuData newDataCopy = - newMenuData == null ? null : new PopupMenuData(this, newMenuData); - popupMenuData = newDataCopy; - firePropertyChanged(POPUP_MENU_DATA_PROPERTY, oldData, newDataCopy); + @Override + public KeyBindingData getKeyBindingData() { + return keyBindingData; } - /** - * Sets the {@link #ToolBarData} to be used to put this action on the tool's toolbar. - * @param newToolBarData the ToolBarData to be used to put this action on the tool's toolbar. - */ - public void setToolBarData(ToolBarData newToolBarData) { - - ToolBarData oldData = toolBarData; - ToolBarData newToolBarDataCopy = newToolBarData == null ? null - : new ToolBarData(this, newToolBarData.getIcon(), newToolBarData.getToolBarGroup(), - newToolBarData.getToolBarSubGroup()); - toolBarData = newToolBarDataCopy; - firePropertyChanged(TOOLBAR_DATA_PROPERTY, oldData, newToolBarDataCopy); + @Override + public KeyBindingData getDefaultKeyBindingData() { + return defaultKeyBindingData; } - /** - * Sets the {@link #KeyBindingData} to be used to assign this action to a keybinding. - * @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding. - */ @Override public void setKeyBindingData(KeyBindingData newKeyBindingData) { KeyBindingData oldData = keyBindingData; @@ -336,14 +285,6 @@ public abstract class DockingAction implements DockingActionIf { firePropertyChanged(KEYBINDING_DATA_PROPERTY, oldData, keyBindingData); } - /** - * Users creating actions should not call this method, but should instead call - * {@link #setKeyBindingData(KeyBindingData)}. - * @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding. - * @param validate true signals that this method should convert keybindings to their - * OS-dependent form (for example, on Mac a Ctrl - * key is changed to the Command key). - */ @Override public void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData) { KeyBindingData oldData = keyBindingData; @@ -351,6 +292,48 @@ public abstract class DockingAction implements DockingActionIf { firePropertyChanged(KEYBINDING_DATA_PROPERTY, oldData, keyBindingData); } +//================================================================================================== +// Non interface methods +//================================================================================================== + + /** + * Sets the {@link MenuData} to be used to put this action on the tool's menu bar + * @param newMenuData the MenuData to be used to put this action on the tool's menu bar + */ + public void setMenuBarData(MenuData newMenuData) { + MenuBarData oldData = menuBarData; + MenuBarData newDataCopy = newMenuData == null ? null : new MenuBarData(this, newMenuData); + + menuBarData = newDataCopy; + firePropertyChanged(MENUBAR_DATA_PROPERTY, oldData, newDataCopy); + } + + /** + * Sets the {@link MenuData} to be used to put this action in the tool's popup menu + * @param newMenuData the MenuData to be used to put this action on the tool's popup menu + */ + public void setPopupMenuData(MenuData newMenuData) { + PopupMenuData oldData = popupMenuData; + PopupMenuData newDataCopy = + newMenuData == null ? null : new PopupMenuData(this, newMenuData); + popupMenuData = newDataCopy; + firePropertyChanged(POPUP_MENU_DATA_PROPERTY, oldData, newDataCopy); + } + + /** + * Sets the {@link ToolBarData} to be used to put this action on the tool's toolbar + * @param newToolBarData the ToolBarData to be used to put this action on the tool's toolbar + */ + public void setToolBarData(ToolBarData newToolBarData) { + + ToolBarData oldData = toolBarData; + ToolBarData newToolBarDataCopy = newToolBarData == null ? null + : new ToolBarData(this, newToolBarData.getIcon(), newToolBarData.getToolBarGroup(), + newToolBarData.getToolBarSubGroup()); + toolBarData = newToolBarDataCopy; + firePropertyChanged(TOOLBAR_DATA_PROPERTY, oldData, newToolBarDataCopy); + } + /** * Creates a reserved keybinding for this action. Reserved keybindings cannot be changed by * the user and have a special high precedence for being process before other actions. Also, @@ -364,7 +347,7 @@ public abstract class DockingAction implements DockingActionIf { /** * Sets the description to be used in the tooltip. - * @param description the description to be set. + * @param newDescription the description to be set. */ public void setDescription(String newDescription) { if (SystemUtilities.isEqual(newDescription, description)) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionIf.java b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionIf.java index dd14547d31..b763806f84 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionIf.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionIf.java @@ -33,27 +33,30 @@ public interface DockingActionIf extends HelpDescriptor { public static final String TOOLBAR_DATA_PROPERTY = "ToolBar"; /** - * Returns the name of the action. + * Returns the name of the action + * @return the name */ - public abstract String getName(); + public String getName(); /** - * Returns the owner of this action. + * Returns the owner of this action + * @return the owner */ - public abstract String getOwner(); + public String getOwner(); /** - * Returns a short description of this action. Generally used for a tooltip. + * Returns a short description of this action. Generally used for a tooltip + * @return the description */ - public abstract String getDescription(); + public String getDescription(); /** - * Adds a listener to be notified if any property changes. + * Adds a listener to be notified if any property changes * @param listener The property change listener that will be notified of * property change events. - * @see AbstractAction#addPropertyChangeListener(java.beans.PropertyChangeListener) + * @see Action#addPropertyChangeListener(java.beans.PropertyChangeListener) */ - public abstract void addPropertyChangeListener(PropertyChangeListener listener); + public void addPropertyChangeListener(PropertyChangeListener listener); /** * Removes a listener to be notified of property changes. @@ -61,15 +64,15 @@ public interface DockingActionIf extends HelpDescriptor { * @param listener The property change listener that will be notified of * property change events. * @see #addPropertyChangeListener(PropertyChangeListener) - * @see AbstractAction#addPropertyChangeListener(java.beans.PropertyChangeListener) + * @see Action#addPropertyChangeListener(java.beans.PropertyChangeListener) */ - public abstract void removePropertyChangeListener(PropertyChangeListener listener); + public void removePropertyChangeListener(PropertyChangeListener listener); /** - * Enables or disables the action. + * Enables or disables the action * - * @param newValue true to enable the action, false to - * disable it + * @param newValue true to enable the action, false to disable it + * @return the enabled value of the action after this call */ public boolean setEnabled(boolean newValue); @@ -111,6 +114,9 @@ public interface DockingActionIf extends HelpDescriptor { /** * Returns the default {@link KeyBindingData} to be used to assign this action to a * key binding. The KeyBindingData will be null if the action is not set to have a keyBinding. + * The value of this method is that which is set from a call to + * {@link #setKeyBindingData(KeyBindingData)}. + * * @return the {@link KeyBindingData} for the action or null if the action does not have a keyBinding. */ public KeyBindingData getDefaultKeyBindingData(); @@ -124,32 +130,34 @@ public interface DockingActionIf extends HelpDescriptor { /** * Returns the full name (the action name combined with the owner name) + * @return the full name */ - public abstract String getFullName(); + public String getFullName(); /** * method to actually perform the action logic for this action. * @param context the {@link ActionContext} object that provides information about where and how * this action was invoked. */ - public abstract void actionPerformed(ActionContext context); + public void actionPerformed(ActionContext context); /** * method is used to determine if this action should be displayed on the current popup. This * method will only be called if the action has popup {@link PopupMenuData} set. *

        * Generally, actions don't need to override this method as the default implementation will - * defer to the {@link #isEnabledForContext()}, which will have the effect of adding the - * action to the popup only if it is enabled for a given context. By overriding this method, + * defer to the {@link #isEnabledForContext(ActionContext)}, which will have the effect + * of adding the action to the popup only if it is enabled for a given context. + * By overriding this method, * you can change this behavior so that the action will be added to the popup, even if it is * disabled for the context, by having this method return true even if the - * {@link #isEnabledForContext()} method will return false, resulting in the action appearing - * in the popup menu, but begin disabled. + * {@link #isEnabledForContext(ActionContext)} method will return false, resulting in the + * action appearing in the popup menu, but begin disabled. * * @param context the {@link ActionContext} from the active provider. * @return true if this action is appropriate for the given context. */ - public abstract boolean isAddToPopup(ActionContext context); + public boolean isAddToPopup(ActionContext context); /** * Method that actions implement to indicate if this action is valid (knows how to work with, is @@ -162,7 +170,7 @@ public interface DockingActionIf extends HelpDescriptor { * @param context the {@link ActionContext} from the active provider. * @return true if this action is appropriate for the given context. */ - public abstract boolean isValidContext(ActionContext context); + public boolean isValidContext(ActionContext context); /** * Method that actions implement to indicate if this action is valid (knows how to work with, is @@ -172,10 +180,10 @@ public interface DockingActionIf extends HelpDescriptor { * If you want a global action to only work on the global context, then override this method * and return false. * - * @param context the global {@link ActionContext} from the active provider. + * @param globalContext the global {@link ActionContext} from the active provider. * @return true if this action is appropriate for the given context. */ - public abstract boolean isValidGlobalContext(ActionContext globalContext); + public boolean isValidGlobalContext(ActionContext globalContext); /** * Method used to determine if this action should be enabled for the given context. @@ -202,13 +210,14 @@ public interface DockingActionIf extends HelpDescriptor { * @param context the current {@link ActionContext} for the window. * @return true if the action should be enabled for the context or false otherwise. */ - public abstract boolean isEnabledForContext(ActionContext context); + public boolean isEnabledForContext(ActionContext context); /** - * Returns a string that includes source file and line number information of where this action was - * created. + * Returns a string that includes source file and line number information of where + * this action was created + * @return the inception information */ - public abstract String getInceptionInformation(); + public String getInceptionInformation(); /** * Returns a JButton that is suitable for this action. For example, It creates a ToggleButton @@ -250,7 +259,7 @@ public interface DockingActionIf extends HelpDescriptor { * @param keyBindingData if non-null, assigns a keybinding to the action. Otherwise, removes * any keybinding from the action. */ - public abstract void setKeyBindingData(KeyBindingData keyBindingData); + public void setKeyBindingData(KeyBindingData keyBindingData); /** * Users creating actions should not call this method, but should instead call @@ -258,13 +267,27 @@ public interface DockingActionIf extends HelpDescriptor { *

        * Call this method when you wish to bypass the validation of * {@link #setKeyBindingData(KeyBindingData)} so that keybindings are set exactly as they - * are given. + * are given (such as when set by the user and not by the programmer). * - * @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding. - * @param validate true signals that this method should convert keybindings to their - * OS-dependent form (for example, on Mac a Ctrl - * key is changed to the Command key). + * @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding */ - public abstract void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData); + public void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData); + /** + * Returns true if this action shares a keybinding with other actions. If this returns true, + * then this action, and any action that shares a name with this action, will be updated + * to the same key binding value whenever the key binding options change. + * + *

        This will be false for the vast majority of actions. If you are unsure if your action + * should use a shared keybinding, then do not set this value to true. + * + *

        This value is not meant to change over the life of the action. Thus, there is no + * set method to change this value. Rather, you should override this method + * to return true as desired. + * + * @return true to share a shared keybinding + */ + public default boolean usesSharedKeyBinding() { + return false; + } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java index a13e94eae6..fb8c979f7b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java @@ -16,16 +16,17 @@ package docking.action; import java.awt.Component; -import java.util.List; +import java.util.Set; import docking.*; +import docking.actions.KeyBindingUtils; import ghidra.util.Msg; import ghidra.util.ReservedKeyBindings; public class KeyBindingAction extends DockingAction { - private final DockingActionManager dockingActionManager; + private final ActionToGuiMapper dockingActionManager; - public KeyBindingAction(DockingActionManager dockingActionManager) { + public KeyBindingAction(ActionToGuiMapper dockingActionManager) { super("Set KeyBinding", DockingWindowManager.DOCKING_WINDOWS_OWNER); this.dockingActionManager = dockingActionManager; createReservedKeyBinding(ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY); @@ -51,20 +52,20 @@ public class KeyBindingAction extends DockingAction { if (!action.isKeyBindingManaged()) { Component parent = windowManager.getActiveComponent(); - Msg.showInfo(getClass(), parent, "Unable to Set Keybinding", "Action \"" + - getActionName(action) + "\" is not keybinding managed and thus a " + - "keybinding cannot be set."); + Msg.showInfo(getClass(), parent, "Unable to Set Keybinding", + "Action \"" + getActionName(action) + "\" is not keybinding managed and thus a " + + "keybinding cannot be set."); return; } KeyEntryDialog d = new KeyEntryDialog(action, dockingActionManager); - windowManager.showDialog(d); + DockingWindowManager.showDialog(d); } /** * Checks to see if the given action is key binding-managed by another action at the * tool-level and returns that tool-level action if found. - * @param dockableAction The action for which to check for tool-level actions + * @param dockingAction The action for which to check for tool-level actions * @return A tool-level action if one is found; otherwise, the original action */ private DockingActionIf maybeGetToolLevelAction(DockingActionIf dockingAction) { @@ -72,15 +73,13 @@ public class KeyBindingAction extends DockingAction { return dockingAction; } - // It is not key binding managed, which means that it may have tool-level representation - // that allows for key binding editing (think DummyKeyBindingsOptionsAction). - - // Bad form (code duplication)--code from DockingAction.getFullName() - String actionToolName = dockingAction.getName() + " (Tool)"; - List actions = - dockingActionManager.getAllDockingActionsByFullActionName(actionToolName); - for (DockingActionIf action : actions) { - return action; + // It is not key binding managed, which means that it may be a shared key binding + String actionName = dockingAction.getName(); + Set allActions = dockingActionManager.getAllActions(); + DockingActionIf sharedAction = + KeyBindingUtils.getSharedKeyBindingAction(allActions, actionName); + if (sharedAction != null) { + return sharedAction; } return dockingAction; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java index 717b1a562a..f415c740f9 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java @@ -23,6 +23,7 @@ import javax.swing.*; import javax.swing.text.*; import docking.*; +import docking.actions.KeyBindingUtils; import docking.widgets.label.GIconLabel; import ghidra.util.HelpLocation; import ghidra.util.ReservedKeyBindings; @@ -34,7 +35,7 @@ import resources.ResourceManager; */ public class KeyEntryDialog extends DialogComponentProvider { - private DockingActionManager actionManager; + private ActionToGuiMapper actionManager; private DockingActionIf action; private JPanel defaultPanel; private KeyEntryTextField keyEntryField; @@ -44,7 +45,7 @@ public class KeyEntryDialog extends DialogComponentProvider { private SimpleAttributeSet textAttrSet; private Color bgColor; - public KeyEntryDialog(DockingActionIf action, DockingActionManager actionManager) { + public KeyEntryDialog(DockingActionIf action, ActionToGuiMapper actionManager) { super("Set Key Binding for " + action.getName(), true); this.actionManager = actionManager; this.action = action; @@ -92,12 +93,9 @@ public class KeyEntryDialog extends DialogComponentProvider { labelPanel.add(pane); labelPanel.add(Box.createHorizontalStrut(5)); - keyEntryField = new KeyEntryTextField(20, new KeyEntryListener() { - @Override - public void processEntry(KeyStroke keyStroke) { - okButton.setEnabled(true); - updateCollisionPane(keyStroke); - } + keyEntryField = new KeyEntryTextField(20, keyStroke -> { + okButton.setEnabled(true); + updateCollisionPane(keyStroke); }); defaultPanel.add(labelPanel, BorderLayout.NORTH); @@ -146,8 +144,9 @@ public class KeyEntryDialog extends DialogComponentProvider { clearStatusText(); - List actions = - actionManager.getAllDockingActionsByFullActionName(action.getFullName()); + Set allActions = actionManager.getAllActions(); + Set actions = + KeyBindingUtils.getActions(allActions, action.getOwner(), action.getName()); for (DockingActionIf element : actions) { if (element.isKeyBindingManaged()) { element.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke)); @@ -160,7 +159,7 @@ public class KeyEntryDialog extends DialogComponentProvider { private void setUpAttributes() { textAttrSet = new SimpleAttributeSet(); textAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - textAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + textAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); textAttrSet.addAttribute(StyleConstants.Foreground, Color.BLUE); tabAttrSet = new SimpleAttributeSet(); @@ -207,7 +206,7 @@ public class KeyEntryDialog extends DialogComponentProvider { Map nameMap = new HashMap<>(list.size()); // the list may have multiple matches for a single owner, which we do not want (see - // DummyKeyBindingsOptionsAction) + // SharedStubKeyBindingAction) for (DockingActionIf dockableAction : list) { if (shouldAddAction(dockableAction)) { // this overwrites same named actions @@ -218,9 +217,6 @@ public class KeyEntryDialog extends DialogComponentProvider { return new ArrayList<>(nameMap.values()); } - /** - * Get the multiple key action for the given keystroke. - */ private MultipleKeyAction getMultipleKeyAction(KeyStroke ks) { Action keyAction = actionManager.getDockingKeyAction(ks); if (keyAction instanceof MultipleKeyAction) { @@ -230,6 +226,11 @@ public class KeyEntryDialog extends DialogComponentProvider { } private boolean shouldAddAction(DockingActionIf dockableAction) { - return dockableAction.isKeyBindingManaged(); + if (dockableAction.isKeyBindingManaged()) { + return true; + } + + // shared key bindings are handled specially + return !dockableAction.usesSharedKeyBinding(); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/util/ActionAdapter.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionAdapter.java similarity index 99% rename from Ghidra/Framework/Docking/src/main/java/docking/util/ActionAdapter.java rename to Ghidra/Framework/Docking/src/main/java/docking/actions/ActionAdapter.java index e160687404..e84079cb6f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/util/ActionAdapter.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionAdapter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package docking.util; +package docking.actions; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/DockingToolActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/DockingToolActionManager.java deleted file mode 100644 index 66030f504b..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/DockingToolActionManager.java +++ /dev/null @@ -1,288 +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.actions; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.*; - -import javax.swing.KeyStroke; - -import docking.*; -import docking.action.*; -import docking.tool.util.DockingToolConstants; -import ghidra.framework.options.OptionType; -import ghidra.framework.options.Options; -import ghidra.util.exception.AssertException; - -/** - * An class to manage actions registered with the tool - */ -public class DockingToolActionManager implements PropertyChangeListener { - - private DockingWindowManager winMgr; - private Map> actionMap; - private Options keyBindingOptions; - private DockingTool dockingTool; - - /** - * Construct an ActionManager. - * - * @param tool tool using this ActionManager - * @param windowManager manager of the "Docking" arrangement of a set of components - * and actions in the tool - */ - public DockingToolActionManager(DockingTool tool, DockingWindowManager windowManager) { - this.dockingTool = tool; - this.winMgr = windowManager; - actionMap = new HashMap<>(); - keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - } - - public void dispose() { - actionMap.clear(); - } - - private void addActionToMap(DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - List newList = new ArrayList<>(); - newList.add(action); - actionMap.put(name, newList); - } - else { - actionList.add(action); - } - } - - private void removeActionFromMap(DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - return; - } - - if (actionList.remove(action) && actionList.isEmpty()) { - actionMap.remove(name); - } - } - - /** - * Adds the action to the tool. - * @param action the action to be added. - */ - public synchronized void addToolAction(DockingActionIf action) { - action.addPropertyChangeListener(this); - addActionToMap(action); - if (action.isKeyBindingManaged()) { - KeyStroke ks = action.getKeyBinding(); - keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, - null, null); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - winMgr.addToolAction(action); - } - - /** - * Removes the given action from the tool - * @param action the action to be removed. - */ - public synchronized void removeToolAction(DockingActionIf action) { - action.removePropertyChangeListener(this); - removeActionFromMap(action); - winMgr.removeToolAction(action); - } - - /** - * Remove all actions that have the given owner. - * @param owner owner of the actions to remove - */ - public synchronized void removeToolActions(String owner) { - List actions = getActions(owner); - for (DockingActionIf action : actions) { - removeToolAction(action); - } - } - - /** - * Add an action that works specifically with a component provider. - * @param provider provider associated with the action - * @param action local action to the provider - */ - public synchronized void addLocalAction(ComponentProvider provider, DockingActionIf action) { - checkForAlreadyAddedAction(provider, action); - - action.addPropertyChangeListener(this); - addActionToMap(action); - if (action.isKeyBindingManaged()) { - KeyStroke ks = action.getKeyBinding(); - keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, - null, null); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - winMgr.addLocalAction(provider, action); - } - - private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - return; - } - if (actionList.contains(action)) { - throw new AssertException("Cannot add the same action more than once. Provider " + - provider.getName() + " - action: " + name); - } - } - - /** - * Remove an action that works specifically with a component provider. - * @param provider provider associated with the action - * @param action local action to the provider - */ - public synchronized void removeProviderAction(ComponentProvider provider, - DockingActionIf action) { - action.removePropertyChangeListener(this); - removeActionFromMap(action); - winMgr.removeProviderAction(provider, action); - } - - /** - * Get all actions that have the action name which includes the action owner's name. - * - * @param fullActionName full name for the action, e.g., "My Action (My Plugin)" - * @return list of actions; empty if no action exists with the given name - */ - public List getDockingActionsByFullActionName(String fullActionName) { - List list = actionMap.get(fullActionName); - if (list == null) { - return new ArrayList<>(); - } - return new ArrayList<>(list); - } - - /** - * Returns a list of actions whose owner matches the given owner or all actions if the given - * owner is null. - *

        - * This method will only return a single instance of any named action, even if multiple - * actions have been registered with the same name. - *

        - * Note: Actions with the same name are assumed to be different instances of the same action. - * - * @param owner The of the action, or null to get all actions - * @return a list of deduped actions. - */ - private List getUniqueActionList(String owner) { - List matchingActionList = new ArrayList<>(); - - for (List actionList : actionMap.values()) { - // we only want *one* instance of duplicate actions - DockingActionIf action = actionList.get(0); - if (owner == null || action.getOwner().equals(owner)) { - matchingActionList.add(action); - } - } - - return matchingActionList; - } - - /** - * Get all actions for the given owner. - * @param owner owner of the actions - * @return array of actions; zero length array is returned if no - * action exists with the given name - */ - public synchronized List getActions(String owner) { - List list = getUniqueActionList(owner); - return list; - } - - /** - * Get a list of all actions in the tool. - * @return list of PluginAction objects - */ - public List getAllActions() { - return getUniqueActionList(null); - } - - /** - * Get the keybindings for each action so that they are still registered - * as being used; otherwise the options will be removed because they - * are noted as not being used. - * - */ - public synchronized void restoreKeyBindings() { - keyBindingOptions = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS); - List actions = getAllActions(); - for (DockingActionIf action : actions) { - if (!action.isKeyBindingManaged()) { - continue; - } - KeyStroke ks = action.getKeyBinding(); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - } - - /** - * Get the actions for the given provider and remove them from the action map - * @param provider provider whose actions are to be removed - */ - public void removeComponentActions(ComponentProvider provider) { - Iterator iterator = winMgr.getComponentActions(provider); - while (iterator.hasNext()) { - DockingActionIf action = iterator.next(); - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList != null && actionList.remove(action) && actionList.isEmpty()) { - actionMap.remove(name); - } - } - - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) { - DockingAction action = (DockingAction) evt.getSource(); - if (!action.isKeyBindingManaged()) { - dockingTool.setConfigChanged(true); - return; - } - KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue(); - KeyStroke newKeyStroke = keyBindingData.getKeyBinding(); - Options opt = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS); - KeyStroke optKeyStroke = opt.getKeyStroke(action.getFullName(), null); - if (newKeyStroke == null) { - opt.removeOption(action.getFullName()); - } - else if (!newKeyStroke.equals(optKeyStroke)) { - opt.setKeyStroke(action.getFullName(), newKeyStroke); - dockingTool.setConfigChanged(true); - } - } - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/util/KeyBindingUtils.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingUtils.java similarity index 71% rename from Ghidra/Framework/Docking/src/main/java/docking/util/KeyBindingUtils.java rename to Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingUtils.java index 0b03560b7c..026782d5bf 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/util/KeyBindingUtils.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingUtils.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package docking.util; +package docking.actions; import java.awt.Component; import java.awt.KeyboardFocusManager; import java.io.*; +import java.util.*; import javax.swing.*; @@ -27,8 +28,10 @@ import org.jdom.*; import org.jdom.input.SAXBuilder; import org.jdom.output.XMLOutputter; -import docking.action.ActionContextProvider; -import docking.action.DockingAction; +import com.google.common.collect.Sets; + +import docking.DockingTool; +import docking.action.*; import docking.widgets.filechooser.GhidraFileChooser; import ghidra.framework.options.ToolOptions; import ghidra.framework.preferences.Preferences; @@ -37,6 +40,8 @@ import ghidra.util.filechooser.GhidraFileChooserModel; import ghidra.util.filechooser.GhidraFileFilter; import ghidra.util.xml.GenericXMLOutputter; import ghidra.util.xml.XmlUtilities; +import util.CollectionUtils; +import utilities.util.reflection.ReflectionUtilities; /** * A class to provide utilities for system key bindings, such as importing and @@ -81,6 +86,7 @@ public class KeyBindingUtils { * If there is a problem reading the data then the user will be shown an * error dialog. * + * @param inputStream the input stream from which to read options * @return An options object that is composed of key binding names and their * associated keystrokes. */ @@ -232,9 +238,8 @@ public class KeyBindingUtils { * @param keyStroke the keystroke for to which the action will be bound * @param action the action to execute when the given keystroke is triggered * @param focusCondition the focus condition under which to bind the action - * ({@link JComponent#getInputMap(int)}) - * @param focusCondition see {@link JComponent} for more info; the default - * is usually {@link JComponent#WHEN_FOCUSED} + * ({@link JComponent#getInputMap(int)}). See {@link JComponent} for more info; + * the default is usually {@link JComponent#WHEN_FOCUSED} */ public static void registerAction(JComponent component, KeyStroke keyStroke, Action action, int focusCondition) { @@ -324,6 +329,105 @@ public class KeyBindingUtils { return (binding == null) ? null : actionMap.get(binding); } + /** + * A utility method to get all key binding actions. This method will remove duplicate + * actions and will only return actions that are {@link DockingActionIf#isKeyBindingManaged()} + * + * @param tool the tool containing the actions + * @return the actions mapped by their full name (e.g., 'Name (OwnerName)') + */ + public static Map getAllActionsByFullName(DockingTool tool) { + + Map deduper = new HashMap<>(); + Set actions = tool.getAllActions(); + for (DockingActionIf action : actions) { + if (isIgnored(action)) { + // don't bother tracking non-keybinding actions; this would be a mistake due + // to the potential for a shared key binding action overwriting its + // SharedStubKeyBindingAction + continue; + } + + deduper.put(action.getFullName(), action); + } + + return deduper; + } + + /** + * A utility method to get all key binding actions that have the given owner. + * This method will remove duplicate actions and will only return actions + * that are {@link DockingActionIf#isKeyBindingManaged()} + * + * @param tool the tool containing the actions + * @param owner the action owner name + * @return the actions + */ + public static Set getKeyBindingActionsForOwner(DockingTool tool, + String owner) { + + Map deduper = new HashMap<>(); + Set actions = tool.getDockingActionsByOwnerName(owner); + for (DockingActionIf action : actions) { + if (isIgnored(action)) { + // don't bother tracking non-keybinding actions; this would be a mistake due + // to the potential for a shared key binding action overwriting its + // SharedStubKeyBindingAction + continue; + } + + deduper.put(action.getFullName(), action); + } + + return CollectionUtils.asSet(deduper.values()); + } + + /** + * Returns all actions that match the given owner and name + * + * @param allActions the universe of actions + * @param owner the owner + * @param name the name + * @return the actions + */ + public static Set getActions(Set allActions, String owner, + String name) { + + Set ownerMatch = + Sets.filter(allActions, action -> action.getOwner().equals(owner)); + return Sets.filter(ownerMatch, action -> action.getName().equals(name)); + } + + /** + * A method to locate the {@link SharedStubKeyBindingAction} representative for the given + * action name. This method is not useful to general clients. + * + * @param allActions all actions in the system + * @param sharedName the name of the shared action + * @return the shared action representative + */ + public static DockingActionIf getSharedKeyBindingAction(Set allActions, + String sharedName) { + + String owner = "Tool"; + for (DockingActionIf action : allActions) { + if (!(action instanceof SharedStubKeyBindingAction)) { + continue; + } + + if (action.getOwner().equals(owner) && action.getName().equals(sharedName)) { + return action; + } + } + return null; + } + + private static boolean isIgnored(DockingActionIf action) { + // not keybinding managed; a shared keybinding implies that this action should not be in + // the UI, as there will be a single proxy in place of all actions sharing that binding + return !action.isKeyBindingManaged() || action.usesSharedKeyBinding(); + } + /** * Takes the existing docking action and allows it to be registered with * Swing components @@ -341,6 +445,61 @@ public class KeyBindingUtils { return new ActionAdapter(action); } + /** + * Checks each action in the given collection against the given new action to make sure that + * they share the same default key binding. + * + * @param newAction the action to check + * @param existingActions the actions that have already been checked + */ + public static void assertSameDefaultKeyBindings(DockingActionIf newAction, + Collection existingActions) { + + KeyBindingData newDefaultBinding = newAction.getDefaultKeyBindingData(); + KeyStroke defaultKs = getKeyStroke(newDefaultBinding); + for (DockingActionIf action : existingActions) { + KeyBindingData existingDefaultBinding = action.getDefaultKeyBindingData(); + KeyStroke existingKs = getKeyStroke(existingDefaultBinding); + if (!Objects.equals(defaultKs, existingKs)) { + logDifferentKeyBindingsWarnigMessage(newAction, action, existingKs); + break; // one warning seems like enough + } + } + } + + /** + * Logs a warning message for the two given actions to signal that they do not share the + * same default key binding + * + * @param newAction the new action + * @param existingAction the action that has already been validated + * @param existingDefaultKs the current validated key stroke + */ + public static void logDifferentKeyBindingsWarnigMessage(DockingActionIf newAction, + DockingActionIf existingAction, KeyStroke existingDefaultKs) { + + //@formatter:off + String s = "Shared Key Binding Actions have different default values. These " + + "must be the same." + + "\n\tAction name: '"+existingAction.getName()+"'" + + "\n\tAction 1: " + existingAction.getInceptionInformation() + + "\n\t\tKey Binding: " + existingDefaultKs + + "\n\tAction 2: " + newAction.getInceptionInformation() + + "\n\t\tKey Binding: " + newAction.getKeyBinding() + + "\nUsing the " + + "first value set - " + existingDefaultKs; + //@formatter:on + + Msg.warn(KeyBindingUtils.class, s, ReflectionUtilities.createJavaFilteredThrowable()); + } + + private static KeyStroke getKeyStroke(KeyBindingData data) { + if (data == null) { + return null; + } + return data.getKeyBinding(); + } + //================================================================================================== // Private Methods //================================================================================================== diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedStubKeyBindingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedStubKeyBindingAction.java new file mode 100644 index 0000000000..f8362089da --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedStubKeyBindingAction.java @@ -0,0 +1,176 @@ +/* ### + * 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.actions; + +import java.util.*; +import java.util.Map.Entry; + +import javax.swing.KeyStroke; + +import docking.ActionContext; +import docking.DockingWindowManager; +import docking.action.*; +import ghidra.framework.options.OptionsChangeListener; +import ghidra.framework.options.ToolOptions; + +/** + * A stub action that allows key bindings to be edited through the key bindings options. This + * allows plugins to create actions that share keybindings without having to manage those + * keybindings themselves. + * + *

        Clients should not be using this class directly. + */ +public class SharedStubKeyBindingAction extends DockingAction implements OptionsChangeListener { + + static final String SHARED_OWNER = "Tool"; + + /** + * We save the client actions for later validate and options updating. We also need the + * default key binding data, which is stored in the value of this map. + * + * Note: This collection is weak; the actions will stay as long as they are + * registered in the tool. + */ + private WeakHashMap clientActions = new WeakHashMap<>(); + + private ToolOptions keyBindingOptions; + + /** + * Creates a new dummy action by the given name and default keystroke value + * + * @param name The name of the action--this will be displayed in the options as the name of + * key binding's action + * @param options the tool's key binding options + */ + SharedStubKeyBindingAction(String name, ToolOptions options) { + super(name, SHARED_OWNER); + this.keyBindingOptions = options; + + // Dummy keybinding actions don't have help--the real action does + DockingWindowManager.getHelpService().excludeFromHelp(this); + + // A listener to keep the shared, stub keybindings in sync with their clients + options.addOptionsChangeListener(this); + } + + void removeClientAction(DockingActionIf action) { + clientActions.remove(action); + } + + void addClientAction(DockingActionIf action) { + + // 1) Validate new action keystroke against existing actions + KeyStroke defaultKs = validateActionsHaveTheSameDefaultKeyStroke(action); + + // 2) Add the action and the validated keystroke, as this is the default keystroke + clientActions.put(action, defaultKs); + + // 3) Update the given action with the current option value. This allows clients to + // add and remove actions after the tool has been initialized. + updateActionKeyStrokeFromOptions(action, defaultKs); + } + + private KeyStroke validateActionsHaveTheSameDefaultKeyStroke(DockingActionIf newAction) { + + // this value may be null + KeyBindingData defaultBinding = newAction.getDefaultKeyBindingData(); + KeyStroke newDefaultKs = getKeyStroke(defaultBinding); + + Set> entries = clientActions.entrySet(); + for (Entry entry : entries) { + DockingActionIf existingAction = entry.getKey(); + KeyStroke existingDefaultKs = entry.getValue(); + if (Objects.equals(existingDefaultKs, newDefaultKs)) { + continue; + } + + KeyBindingUtils.logDifferentKeyBindingsWarnigMessage(newAction, existingAction, + existingDefaultKs); + + // + // Not sure which keystroke to prefer here--keep the first one that was set + // + + // set the new action's keystroke to be the winner + newAction.setKeyBindingData(new KeyBindingData(existingDefaultKs)); + + // one message is probably enough; + return existingDefaultKs; + } + + return newDefaultKs; + } + + private void updateActionKeyStrokeFromOptions(DockingActionIf action, KeyStroke defaultKs) { + + KeyStroke optionsKs = getKeyStrokeFromOptions(defaultKs); + if (!Objects.equals(defaultKs, optionsKs)) { + // we use the 'unvalidated' call since this value is provided by the user--we assume + // that user input is correct; we only validate programmer input + action.setUnvalidatedKeyBindingData(new KeyBindingData(optionsKs)); + } + } + + private KeyStroke getKeyStrokeFromOptions(KeyStroke validatedKeyStroke) { + KeyStroke ks = keyBindingOptions.getKeyStroke(getFullName(), validatedKeyStroke); + return ks; + } + + private KeyStroke getKeyStroke(KeyBindingData data) { + if (data == null) { + return null; + } + return data.getKeyBinding(); + } + + @Override + public void optionsChanged(ToolOptions options, String optionName, Object oldValue, + Object newValue) { + + if (!optionName.startsWith(getName())) { + return; // not my binding + } + + KeyStroke newKs = (KeyStroke) newValue; + for (DockingActionIf action : clientActions.keySet()) { + // we use the 'unvalidated' call since this value is provided by the user--we assume + // that user input is correct; we only validate programmer input + action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); + } + } + + @Override + public void actionPerformed(ActionContext context) { + // no-op; this is a dummy! + } + + @Override + public boolean isAddToPopup(ActionContext context) { + return false; + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return false; + } + + @Override + public void dispose() { + super.dispose(); + clientActions.clear(); + keyBindingOptions.removeOptionsChangeListener(this); + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java new file mode 100644 index 0000000000..545f20a659 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java @@ -0,0 +1,305 @@ +/* ### + * 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.actions; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.*; + +import javax.swing.KeyStroke; + +import org.apache.commons.collections4.map.LazyMap; + +import docking.*; +import docking.action.*; +import docking.tool.util.DockingToolConstants; +import ghidra.framework.options.*; +import ghidra.util.exception.AssertException; +import util.CollectionUtils; + +/** + * An class to manage actions registered with the tool + */ +public class ToolActions implements PropertyChangeListener { + + private DockingWindowManager winMgr; + private ActionToGuiHelper actionGuiHelper; + + /* + Map of Maps of Sets + + Owner Name -> + Action Name -> Set of Actions + */ + private Map>> actionsByNameByOwner = LazyMap.lazyMap( + new HashMap<>(), () -> LazyMap.lazyMap(new HashMap<>(), () -> new HashSet<>())); + + private Map sharedActionMap = new HashMap<>(); + + private ToolOptions keyBindingOptions; + private DockingTool dockingTool; + + /** + * Construct an ActionManager + * + * @param tool tool using this ActionManager + * @param windowManager manager of the "Docking" arrangement of a set of components + * and actions in the tool + */ + public ToolActions(DockingTool tool, DockingWindowManager windowManager) { + this.dockingTool = tool; + this.winMgr = windowManager; + this.actionGuiHelper = new ActionToGuiHelper(winMgr); + keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS); + } + + public void dispose() { + actionsByNameByOwner.clear(); + sharedActionMap.clear(); + } + + private void addActionToMap(DockingActionIf action) { + + Set actions = getActionStorage(action); + KeyBindingUtils.assertSameDefaultKeyBindings(action, actions); + actions.add(action); + } + + /** + * Add an action that works specifically with a component provider. + * @param provider provider associated with the action + * @param action local action to the provider + */ + public synchronized void addLocalAction(ComponentProvider provider, DockingActionIf action) { + checkForAlreadyAddedAction(provider, action); + + action.addPropertyChangeListener(this); + addActionToMap(action); + setKeyBindingOption(action); + actionGuiHelper.addLocalAction(provider, action); + } + + /** + * Adds the action to the tool. + * @param action the action to be added. + */ + public synchronized void addToolAction(DockingActionIf action) { + action.addPropertyChangeListener(this); + addActionToMap(action); + setKeyBindingOption(action); + actionGuiHelper.addToolAction(action); + } + + private void setKeyBindingOption(DockingActionIf action) { + + if (action.usesSharedKeyBinding()) { + installSharedKeyBinding(action); + return; + } + + if (!action.isKeyBindingManaged()) { + return; + } + + KeyStroke ks = action.getKeyBinding(); + keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, null, + null); + KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); + if (!Objects.equals(ks, newKs)) { + action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); + } + } + + private void installSharedKeyBinding(DockingActionIf action) { + String name = action.getName(); + KeyStroke defaultKeyStroke = action.getKeyBinding(); + + // get or create the stub to which we will add the action + SharedStubKeyBindingAction stub = sharedActionMap.computeIfAbsent(name, key -> { + + SharedStubKeyBindingAction newStub = + new SharedStubKeyBindingAction(name, keyBindingOptions); + keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE, + defaultKeyStroke, null, null); + return newStub; + }); + + stub.addClientAction(action); + } + + /** + * Removes the given action from the tool + * @param action the action to be removed. + */ + public synchronized void removeToolAction(DockingActionIf action) { + action.removePropertyChangeListener(this); + removeAction(action); + actionGuiHelper.removeToolAction(action); + } + + /** + * Remove all actions that have the given owner. + * @param owner owner of the actions to remove + */ + public synchronized void removeToolActions(String owner) { + + // remove from the outer map first, to prevent concurrent modification exceptions + Map> toCleanup = actionsByNameByOwner.remove(owner); + if (toCleanup == null) { + return; // no actions registered for this owner + } + + //@formatter:off + toCleanup.values() + .stream() + .flatMap(set -> set.stream()) + .forEach(action -> removeToolAction(action)) + ; + //@formatter:on + } + + private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) { + if (getActionStorage(action).contains(action)) { + throw new AssertException("Cannot add the same action more than once. Provider " + + provider.getName() + " - action: " + action.getFullName()); + } + } + + /** + * Get all actions for the given owner + * @param owner owner of the actions + * @return array of actions; zero length array is returned if no + * action exists with the given name + */ + public synchronized Set getActions(String owner) { + + Set result = new HashSet<>(); + Map> actionsByName = actionsByNameByOwner.get(owner); + for (Set actions : actionsByName.values()) { + result.addAll(actions); + } + + if (SharedStubKeyBindingAction.SHARED_OWNER.equals(owner)) { + result.addAll(sharedActionMap.values()); + } + + return result; + } + + /** + * Get a set of all actions in the tool + * @return the actions + */ + public synchronized Set getAllActions() { + + Set result = new HashSet<>(); + Collection>> maps = actionsByNameByOwner.values(); + for (Map> actionsByName : maps) { + for (Set actions : actionsByName.values()) { + result.addAll(actions); + } + } + + result.addAll(sharedActionMap.values()); + + return result; + } + + /** + * Get the keybindings for each action so that they are still registered as being used; + * otherwise the options will be removed because they are noted as not being used. + */ + public synchronized void restoreKeyBindings() { + keyBindingOptions = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS); + Set actions = getAllActions(); + for (DockingActionIf action : actions) { + if (!action.isKeyBindingManaged()) { + continue; + } + KeyStroke ks = action.getKeyBinding(); + KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); + if (ks != newKs) { + action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); + } + } + } + + /** + * Remove an action that works specifically with a component provider. + * @param provider provider associated with the action + * @param action local action to the provider + */ + public synchronized void removeProviderAction(ComponentProvider provider, + DockingActionIf action) { + action.removePropertyChangeListener(this); + removeAction(action); + actionGuiHelper.removeProviderAction(provider, action); + } + + /** + * Get the actions for the given provider and remove them from the action map + * @param provider provider whose actions are to be removed + */ + public synchronized void removeComponentActions(ComponentProvider provider) { + Iterator it = actionGuiHelper.getComponentActions(provider); + Set set = CollectionUtils.asSet(it); + for (DockingActionIf action : set) { + removeProviderAction(provider, action); + } + } + + private void removeAction(DockingActionIf action) { + getActionStorage(action).remove(action); + if (action.usesSharedKeyBinding()) { + SharedStubKeyBindingAction stub = sharedActionMap.get(action.getName()); + if (stub != null) { + stub.removeClientAction(action); + } + } + } + + private Set getActionStorage(DockingActionIf action) { + String owner = action.getOwner(); + String name = action.getName(); + return actionsByNameByOwner.get(owner).get(name); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) { + DockingAction action = (DockingAction) evt.getSource(); + if (!action.isKeyBindingManaged()) { + dockingTool.setConfigChanged(true); + return; + } + KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue(); + KeyStroke newKeyStroke = keyBindingData.getKeyBinding(); + Options opt = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS); + KeyStroke optKeyStroke = opt.getKeyStroke(action.getFullName(), null); + if (newKeyStroke == null) { + opt.removeOption(action.getFullName()); + } + else if (!newKeyStroke.equals(optKeyStroke)) { + opt.setKeyStroke(action.getFullName(), newKeyStroke); + dockingTool.setConfigChanged(true); + } + } + } + + DockingActionIf getSharedStubKeyBindingAction(String name) { + return sharedActionMap.get(name); + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpViewSearcher.java b/Ghidra/Framework/Docking/src/main/java/docking/help/HelpViewSearcher.java index d34b964fe2..0287ef964d 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/help/HelpViewSearcher.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/help/HelpViewSearcher.java @@ -32,7 +32,7 @@ import javax.swing.text.Document; import docking.DockingUtils; import docking.DockingWindowManager; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; import docking.widgets.*; import ghidra.util.Msg; import ghidra.util.exception.AssertException; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java b/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java index fa9ab2bcbe..b9cc998e54 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java @@ -38,6 +38,8 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.junit.*; +import com.google.common.collect.Sets; + import docking.*; import docking.action.DockingActionIf; import docking.action.ToggleDockingActionIf; @@ -483,6 +485,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest { /** * A convenience method to close all of the windows and frames that the current Java * windowing environment knows about + * * @deprecated instead call the new {@link #closeAllWindows()} */ @Deprecated @@ -1097,23 +1100,48 @@ public abstract class AbstractDockingTest extends AbstractGenericTest { * @param name the name to match * @return the matching actions; empty list if no matches */ - public static Set getActions(DockingTool tool, String name) { + public static Set getActionsByName(DockingTool tool, String name) { - Set actions = new HashSet<>(); + Set result = new HashSet<>(); - List toolActions = tool.getAllActions(); + Set toolActions = tool.getAllActions(); for (DockingActionIf action : toolActions) { if (action.getName().equals(name)) { - actions.add(action); + result.add(action); } } - return actions; + return result; + } + + /** + * A helper method to find all actions with the given owner's name + * + * @param tool the tool containing all system actions + * @param name the owner's name to match + * @return the matching actions; empty list if no matches + */ + public static Set getActionsByOwner(DockingTool tool, String name) { + return tool.getDockingActionsByOwnerName(name); + } + + /** + * A helper method to find all actions by name, with the given owner's name + * + * @param tool the tool containing all system actions + * @param owner the owner's name + * @param name the owner's name to match + * @return the matching actions; empty list if no matches + */ + public static Set getActionsByOwnerAndName(DockingTool tool, String owner, + String name) { + Set ownerActions = tool.getDockingActionsByOwnerName(owner); + return Sets.filter(ownerActions, action -> action.getName().equals(name)); } /** * Finds the singular tool action by the given name. If more than one action exists with * that name, then an exception is thrown. If you want more than one matching action, - * the call {@link #getActions(DockingTool, String)} instead. + * the call {@link #getActionsByName(DockingTool, String)} instead. * *

        Note: more specific test case subclasses provide other methods for finding actions * when you have an owner name (which is usually the plugin name). @@ -1124,7 +1152,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest { */ public static DockingActionIf getAction(DockingTool tool, String name) { - Set actions = getActions(tool, name); + Set actions = getActionsByName(tool, name); if (actions.isEmpty()) { return null; } @@ -1136,6 +1164,38 @@ public abstract class AbstractDockingTest extends AbstractGenericTest { return CollectionUtils.any(actions); } + /** + * Finds the action by the given owner name and action name. + * If you do not know the owner name, then use + * the call {@link #getActionsByName(DockingTool, String)} instead. + * + *

        Note: more specific test case subclasses provide other methods for finding actions + * when you have an owner name (which is usually the plugin name). + * + * @param tool the tool containing all system actions + * @param owner the owner of the action + * @param name the name to match + * @return the matching action; null if no matching action can be found + */ + public static DockingActionIf getAction(DockingTool tool, String owner, String name) { + Set actions = getActionsByOwnerAndName(tool, owner, name); + if (actions.isEmpty()) { + return null; + } + + if (actions.size() > 1) { + // This shouldn't happen + throw new AssertionFailedError( + "Found more than one action for name '" + name + " (" + owner + ")'"); + } + + return CollectionUtils.any(actions); + } + + public static DockingActionIf getLocalAction(ComponentProvider provider, String actionName) { + return getAction(provider.getTool(), provider.getName(), actionName); + } + /** * Returns the given dialog's action that has the given name * @@ -1417,8 +1477,8 @@ public abstract class AbstractDockingTest extends AbstractGenericTest { /** * Simulates a user initiated keystroke using the keybinding of the given action * - * @param destination the action's destination component - * @param action The action to simulate pressing. + * @param destination the component for the action being executed + * @param action The action to simulate pressing */ public static void triggerActionKey(Component destination, DockingActionIf action) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java index 61efa594e9..13f0a82ad7 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java @@ -442,7 +442,10 @@ public abstract class AbstractSortedTableModel extends AbstractGTableModel public int compare(T t1, T t2) { // at this point we compare the rows, since all of the sorting column values are equal - if (t1 instanceof Comparable) { + // (Warning: due to comparable being specific to the class upon which it is defined, + // we have to make sure the class is the same to prevent class cast + // exceptions when the table has mixed implementations of 'T') + if (t1 instanceof Comparable && t1.getClass().equals(t2.getClass())) { return ((Comparable) t1).compareTo(t2); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java index 3d31f624b7..ec812ca7f8 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java @@ -31,7 +31,7 @@ import javax.swing.table.*; import docking.*; import docking.action.*; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; import docking.widgets.OptionDialog; import docking.widgets.dialogs.SettingsDialog; import docking.widgets.filechooser.GhidraFileChooser; diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/actions/SharedKeyBindingDockingActionTest.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/actions/SharedKeyBindingDockingActionTest.java new file mode 100644 index 0000000000..3e387be964 --- /dev/null +++ b/Ghidra/Framework/Docking/src/test.slow/java/docking/actions/SharedKeyBindingDockingActionTest.java @@ -0,0 +1,458 @@ +/* ### + * 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.actions; + +import static org.junit.Assert.*; + +import java.awt.event.KeyEvent; +import java.util.List; +import java.util.Set; + +import javax.swing.JComponent; +import javax.swing.KeyStroke; + +import org.apache.commons.collections4.IterableUtils; +import org.junit.Before; +import org.junit.Test; + +import docking.*; +import docking.action.*; +import docking.test.AbstractDockingTest; +import docking.tool.util.DockingToolConstants; +import ghidra.framework.options.ToolOptions; +import ghidra.util.Msg; +import ghidra.util.SpyErrorLogger; + +public class SharedKeyBindingDockingActionTest extends AbstractDockingTest { + + private static final String SHARED_NAME = "Shared Action Name"; + private static final String SHARED_OWNER = SharedStubKeyBindingAction.SHARED_OWNER; + + // format: getName() + " (" + getOwner() + ")"; + private static final String SHARED_FULL_NAME = SHARED_NAME + " (" + SHARED_OWNER + ")"; + + private static final KeyStroke DEFAULT_KS_1 = KeyStroke.getKeyStroke(KeyEvent.VK_A, 0); + private static final KeyStroke DEFAULT_KS_DIFFERENT_THAN_1 = + KeyStroke.getKeyStroke(KeyEvent.VK_B, 0); + private static final String OWNER_1 = "Owner1"; + private static final String OWNER_2 = "Owner2"; + + private SpyErrorLogger spyLogger = new SpyErrorLogger(); + + private DockingTool tool; + + @Before + public void setUp() { + tool = new FakeDockingTool(); + + Msg.setErrorLogger(spyLogger); + } + + @Test + public void testSharedKeyBinding_SameDefaultKeyBindings() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + tool.addAction(action1); + tool.addAction(action2); + + assertNoLoggedMessages(); + assertKeyBinding(action1, DEFAULT_KS_1); + assertKeyBinding(action2, DEFAULT_KS_1); + assertSharedStubInTool(); + } + + @Test + public void testSharedKeyBinding_OptionsChange() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + tool.addAction(action1); + tool.addAction(action2); + + KeyStroke newKs = KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0); + setSharedKeyBinding(newKs); + + assertNoLoggedMessages(); + assertKeyBinding(action1, newKs); + assertKeyBinding(action2, newKs); + assertSharedStubInTool(); + } + + @Test + public void testSharedKeyBinding_DifferentDefaultKeyBindings() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_DIFFERENT_THAN_1); + + tool.addAction(action1); + tool.addAction(action2); + + // both bindings should keep the first one that was set when they are different + assertImproperDefaultBindingMessage(); + assertKeyBinding(action1, DEFAULT_KS_1); + assertKeyBinding(action2, DEFAULT_KS_1); + assertSharedStubInTool(); + } + + @Test + public void testSharedKeyBinding_NoDefaultKeyBindings() { + + TestAction action1 = new TestAction(OWNER_1, null); + TestAction action2 = new TestAction(OWNER_2, null); + + tool.addAction(action1); + tool.addAction(action2); + + // both bindings are null; this is allowed + assertNoLoggedMessages(); + assertKeyBinding(action1, null); + assertKeyBinding(action2, null); + assertSharedStubInTool(); + } + + @Test + public void testSharedKeyBinding_OneDefaultOneUndefinedDefaultKeyBinding() { + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, null); + + tool.addAction(action1); + tool.addAction(action2); + + // both bindings should keep the first one that was set when they are different + assertImproperDefaultBindingMessage(); + assertKeyBinding(action1, DEFAULT_KS_1); + assertKeyBinding(action2, DEFAULT_KS_1); + assertSharedStubInTool(); + } + + @Test + public void testSharedKeyBinding_RemoveAction() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + tool.addAction(action1); + tool.addAction(action2); + + tool.removeAction(action1); + + assertActionNotInTool(action1); + assertActionInTool(action2); + + tool.removeAction(action2); + assertActionNotInTool(action2); + + assertNoSharedKeyBindingStubInstalled(action1); + } + + @Test + public void testSharedKeyBinding_AddSameActionTwice() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + + tool.addAction(action1); + tool.addAction(action1); + + assertOnlyOneVersionOfActionInTool(action1); + + assertNoLoggedMessages(); + assertKeyBinding(action1, DEFAULT_KS_1); + assertSharedStubInTool(); + } + + @Test + public void testSharedKeyBinding_OnlyOneEntryInOptions() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + tool.addAction(action1); + tool.addAction(action2); + + // verify that the actions are not in the options, but that the shared action is + ToolOptions keyOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS); + List names = keyOptions.getOptionNames(); + assertTrue(names.contains(SHARED_FULL_NAME)); + assertFalse(names.contains(action1.getFullName())); + assertFalse(names.contains(action2.getFullName())); + } + + @Test + public void testSharedKeyBinding_AddActionAfterOptionHasChanged() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + tool.addAction(action1); + KeyStroke newKs = KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0); + setSharedKeyBinding(newKs); + + assertKeyBinding(action1, newKs); + + // verify the newly added keybinding gets the newly changed option + tool.addAction(action2); + assertKeyBinding(action2, newKs); + assertNoLoggedMessages(); + } + + @Test + public void testSharedKeyBinding_AddActionAfterOptionHasChanged_RepeatAddRemove() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + tool.addAction(action1); + KeyStroke newKs = KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0); + setSharedKeyBinding(newKs); + + assertKeyBinding(action1, newKs); + + // verify the newly added keybinding gets the newly changed option + tool.addAction(action2); + assertKeyBinding(action2, newKs); + assertNoLoggedMessages(); + + tool.removeAction(action2); + assertActionNotInTool(action2); + + tool.addAction(action2); + assertKeyBinding(action2, newKs); + assertNoLoggedMessages(); + } + + @Test + public void testSharedKeyBinding_SameDefaultKeyBindings_LocalAction() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + DummyComponentProvider provider = new DummyComponentProvider(); + tool.addLocalAction(provider, action1); + tool.addLocalAction(provider, action2); + + assertNoLoggedMessages(); + assertKeyBinding(action1, DEFAULT_KS_1); + assertKeyBinding(action2, DEFAULT_KS_1); + assertSharedStubInTool(); + } + + @Test + public void testSharedKeyBinding_RemoveAction_LocalAction() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + DummyComponentProvider provider = new DummyComponentProvider(); + tool.addLocalAction(provider, action1); + tool.addLocalAction(provider, action2); + + tool.removeLocalAction(provider, action1); + + assertActionNotInTool(action1); + assertActionInTool(action2); + + tool.removeLocalAction(provider, action2); + assertActionNotInTool(action2); + + assertNoSharedKeyBindingStubInstalled(action1); + } + + @Test + public void testSharedKeyBinding_RemoveComonentActions() { + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action2 = new TestAction(OWNER_2, DEFAULT_KS_1); + + DummyComponentProvider provider = new DummyComponentProvider(); + tool.addLocalAction(provider, action1); + tool.addLocalAction(provider, action2); + assertActionInTool(action1); + assertActionInTool(action2); + + tool.removeComponentProvider(provider); + + assertActionNotInTool(action1); + assertActionNotInTool(action2); + + assertNoSharedKeyBindingStubInstalled(action1); + } + + @Test + public void testNonSharedKeyBinding_SameActionAddedTwice() { + // + // We support adding the same action twice. (This can happen when a transient component + // provider is repeatedly shown, such as a search results provider.) Make sure we get + // a warning if the same action is added twice, but with different key bindings. + // + // Note: in this context, two actions are considered to be the same if they share the + // same name and owner. + // + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action1Copy = new TestAction(OWNER_1, DEFAULT_KS_1); + + tool.addAction(action1); + tool.addAction(action1Copy); + assertActionInTool(action1); + assertActionInTool(action1Copy); + + assertNoLoggedMessages(); + + tool.removeAction(action1); + assertActionNotInTool(action1); + assertActionInTool(action1Copy); + + tool.removeAction(action1Copy); + assertActionNotInTool(action1Copy); + } + + @Test + public void testNonSharedKeyBinding_DifferentActionsWithSameFullName() { + // + // We support adding the same action twice. (This can happen when a transient component + // provider is repeatedly shown, such as a search results provider.) Make sure we get + // a warning if the same action is added twice, but with different key bindings. + // + // Note: in this context, two actions are considered to be the same if they share the + // same name and owner. + // + + TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1); + TestAction action1Copy = new TestAction(OWNER_1, DEFAULT_KS_DIFFERENT_THAN_1); + + tool.addAction(action1); + tool.addAction(action1Copy); + assertActionInTool(action1); + assertActionInTool(action1Copy); + + assertImproperDefaultBindingMessage(); + + tool.removeAction(action1); + assertActionNotInTool(action1); + assertActionInTool(action1Copy); + + tool.removeAction(action1Copy); + assertActionNotInTool(action1Copy); + } + +//================================================================================================== +// Private Methods +//================================================================================================== + + private void assertSharedStubInTool() { + ToolActions actionManager = (ToolActions) getInstanceField("actionMgr", tool); + DockingActionIf action = actionManager.getSharedStubKeyBindingAction(SHARED_NAME); + assertNotNull("Shared action stub is not in the tool", action); + } + + private void assertOnlyOneVersionOfActionInTool(TestAction action) { + + // this method will fail if more than one action is registered + DockingActionIf registeredAction = getAction(tool, action.getOwner(), action.getName()); + assertNotNull("There should be only one instance of this action in the tool: " + action, + registeredAction); + } + + private void assertActionInTool(TestAction action) { + + Set actions = getActionsByName(tool, action.getName()); + for (DockingActionIf toolAction : actions) { + if (toolAction == action) { + return; + } + } + + fail("Action is not in the tool: " + action); + } + + private void assertActionNotInTool(TestAction action) { + Set actions = getActionsByName(tool, action.getName()); + for (DockingActionIf toolAction : actions) { + assertNotSame(toolAction, action); + } + } + + private void assertNoSharedKeyBindingStubInstalled(DockingActionIf action) { + + String name = action.getName(); + String owner = action.getOwner(); + DockingActionIf sharedAction = getAction(tool, owner, name); + assertNull("There should be no actions registered for '" + name + " (" + owner + ")'", + sharedAction); + } + + private void setSharedKeyBinding(KeyStroke newKs) { + ToolOptions options = getKeyBindingOptions(); + runSwing(() -> options.setKeyStroke(SHARED_FULL_NAME, newKs)); + waitForSwing(); + } + + private ToolOptions getKeyBindingOptions() { + return tool.getOptions(DockingToolConstants.KEY_BINDINGS); + } + + private void assertNoLoggedMessages() { + assertTrue("Spy logger not empty: " + spyLogger, IterableUtils.isEmpty(spyLogger)); + } + + private void assertImproperDefaultBindingMessage() { + spyLogger.assertLogMessage("shared", "key", "binding", "actions", "different", "default"); + } + + private void assertKeyBinding(TestAction action, KeyStroke expectedKs) { + assertEquals(expectedKs, action.getKeyBinding()); + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + + private class TestAction extends DockingAction { + + public TestAction(String owner, KeyStroke ks) { + super(SHARED_NAME, owner); + + if (ks != null) { + setKeyBindingData(new KeyBindingData(ks)); + } + } + + @Override + public boolean usesSharedKeyBinding() { + return true; + } + + @Override + public void actionPerformed(ActionContext context) { + fail("Action performed should not have been called"); + } + } + + private class DummyComponentProvider extends ComponentProvider { + public DummyComponentProvider() { + super(tool, "Dummy", "Dummy Owner"); + addToTool(); + } + + @Override + public JComponent getComponent() { + return null; + } + } +} diff --git a/Ghidra/Framework/Docking/src/test/java/docking/DockingKeybindingActionTest.java b/Ghidra/Framework/Docking/src/test/java/docking/DockingKeybindingActionTest.java deleted file mode 100644 index 1cd46caef0..0000000000 --- a/Ghidra/Framework/Docking/src/test/java/docking/DockingKeybindingActionTest.java +++ /dev/null @@ -1,94 +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; - -import static org.junit.Assert.assertEquals; - -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; - -import javax.swing.KeyStroke; - -import org.junit.Test; - -import generic.test.AbstractGenericTest; - -public class DockingKeybindingActionTest extends AbstractGenericTest { - - public DockingKeybindingActionTest() { - super(); - } - - @Test - public void testKeybinding_Unmodified() { - - KeyStroke javaKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, 0); - KeyStroke dockingKeyStroke = DockingKeyBindingAction.parseKeyStroke("C"); - assertEquals(javaKeyStroke, dockingKeyStroke); - } - - @Test - public void testKeybinding_Unmodified_MixedCase() { - - KeyStroke javaKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, 0); - KeyStroke dockingKeyStroke = DockingKeyBindingAction.parseKeyStroke("C"); - assertEquals(javaKeyStroke, dockingKeyStroke); - } - - @Test - public void testKeybinding_Modifier() { - - KeyStroke javaKeyStroke = - KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, InputEvent.CTRL_DOWN_MASK); - KeyStroke dockingKeyStroke = DockingKeyBindingAction.parseKeyStroke("ctrl COMMA"); - assertEquals(javaKeyStroke, dockingKeyStroke); - } - - @Test - public void testKeybinding_MultipleModifiers() { - - KeyStroke javaKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, - InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK); - KeyStroke dockingKeyStroke = DockingKeyBindingAction.parseKeyStroke("ctrl shift COMMA"); - assertEquals(javaKeyStroke, dockingKeyStroke); - } - - @Test - public void testKeybinding_MultipleModifiersOutOfOrder() { - - KeyStroke javaKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, - InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK); - KeyStroke dockingKeyStroke = DockingKeyBindingAction.parseKeyStroke("ctrl COMMA shift"); - assertEquals(javaKeyStroke, dockingKeyStroke); - } - - @Test - public void testKeybinding_MultipleModifiers_MixedCase() { - - KeyStroke javaKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, - InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK); - KeyStroke dockingKeyStroke = DockingKeyBindingAction.parseKeyStroke("ctrl SHIFT comma"); - assertEquals(javaKeyStroke, dockingKeyStroke); - } - - @Test - public void testKeybinding_InvalidControl() { - - KeyStroke javaKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK); - KeyStroke dockingKeyStroke = DockingKeyBindingAction.parseKeyStroke("control C"); - assertEquals(javaKeyStroke, dockingKeyStroke); - } -} diff --git a/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java b/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java index bcbb45e91b..0398267581 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java @@ -21,7 +21,7 @@ import java.util.List; import javax.swing.ImageIcon; import docking.action.DockingActionIf; -import docking.actions.DockingToolActionManager; +import docking.actions.ToolActions; import docking.framework.ApplicationInformationDisplayFactory; import ghidra.framework.options.ToolOptions; @@ -37,7 +37,7 @@ public class FakeDockingTool extends AbstractDockingTool { List windowIcons = ApplicationInformationDisplayFactory.getWindowIcons(); winMgr = new DockingWindowManager("EMPTY", windowIcons, listener, false /*isModal*/, true /*isDockable*/, true /*hasStatus*/, null /*DropTargetFactory*/); - actionMgr = new DockingToolActionManager(this, winMgr); + actionMgr = new ToolActions(this, winMgr); } @Override diff --git a/Ghidra/Framework/Generic/certification.manifest b/Ghidra/Framework/Generic/certification.manifest index a4f9a3084b..b6fae67926 100644 --- a/Ghidra/Framework/Generic/certification.manifest +++ b/Ghidra/Framework/Generic/certification.manifest @@ -61,6 +61,7 @@ src/main/resources/images/redDragon24.png||GHIDRA||||END| src/main/resources/images/redDragon32.png||GHIDRA||||END| src/main/resources/images/reload3.png||Crystal Clear Icons - LGPL 2.1||||END| src/main/resources/images/software-update-urgent.png||Tango Icons - Public Domain|||tango icon set|END| +src/main/resources/images/text_align_justify.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/video-x-generic16.png||Tango Icons - Public Domain|||tango|END| src/main/resources/log4j-appender-console-with-links.xml||GHIDRA||||END| src/main/resources/log4j-appender-console.xml||GHIDRA||||END| diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java index c8c70e539c..0d98af4381 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java @@ -18,6 +18,7 @@ package ghidra.util; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -846,27 +847,20 @@ public class StringUtilities { } /** - * Turn the given list into an attractive string, with the separator of you choosing. + * Turn the given data into an attractive string, with the separator of your choosing * - * @param list the list from which a string will be generated + * @param collection the data from which a string will be generated * @param separator the string used to separate elements * @return a string representation of the given list */ - public static String toString(List list, String separator) { - if (list == null) { + public static String toString(Collection collection, String separator) { + if (collection == null) { return null; } - StringBuffer buffer = new StringBuffer("[ "); - for (int i = 0; i < list.size(); i++) { - buffer.append(list.get(i).toString()); - if (i + 1 < list.size()) { - buffer.append(separator); - } - } - - buffer.append(" ]"); - return buffer.toString(); + String asString = + collection.stream().map(o -> o.toString()).collect(Collectors.joining(separator)); + return "[ " + asString + " ]"; } public static String toStringWithIndent(Object o) { diff --git a/Ghidra/Framework/Generic/src/main/java/resources/Icons.java b/Ghidra/Framework/Generic/src/main/java/resources/Icons.java index 4557609210..9dad8abb58 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/Icons.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/Icons.java @@ -79,6 +79,9 @@ public class Icons { public static final ImageIcon SAVE_AS = ResourceManager.getImageIcon( new DotDotDotIcon(ResourceManager.loadImage("images/Disk.png"))); + public static final ImageIcon MAKE_SELECTION_ICON = + ResourceManager.getImageIcon(ResourceManager.loadImage("images/text_align_justify.png")); + // Not necessarily re-usable, but this is needed for the help system; these should // probably be moved to the client that uses them, while updating the // help system to use them there. diff --git a/Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/images/text_align_justify.png b/Ghidra/Framework/Generic/src/main/resources/images/text_align_justify.png similarity index 100% rename from Ghidra/Features/Base/src/main/help/help/topics/BookmarkPlugin/images/text_align_justify.png rename to Ghidra/Framework/Generic/src/main/resources/images/text_align_justify.png diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java index e67d1dfae2..e345ccc3ec 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java @@ -22,7 +22,7 @@ import javax.swing.*; import javax.swing.tree.TreePath; import docking.DockingUtils; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; import docking.widgets.tree.*; import docking.widgets.tree.support.GTreeRenderer; import ghidra.framework.main.FrontEndTool; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/options/DummyKeyBindingsOptionsAction.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/options/DummyKeyBindingsOptionsAction.java deleted file mode 100644 index f322b6bd73..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/options/DummyKeyBindingsOptionsAction.java +++ /dev/null @@ -1,109 +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.options; - -import javax.swing.KeyStroke; - -import docking.ActionContext; -import docking.DockingWindowManager; -import docking.action.DockingAction; -import docking.action.KeyBindingData; - -/** - * A dummy action that allows key bindings to be edited through the key bindings options - * without requiring the user to implement a system action that will be added to the tool. - * Without this class the only editable tool key bindings are those that have corresponding - * {@link DockingAction}s added to the tool. - *

        - * A typical usage of this class: Suppose a plugin has an action that it adds to the tool, - * which is logically the same action (with the same name) that a second plugin adds to the tool. - * Both of these actions are - * logically equivalent and share the same default key binding. Since these actions are - * logically the same, then they should share the same key binding and only have one entry - * in the key binding options, instead of two. This class enables both actions to have key - * bindings assigned via one dummy action. To do this each of the above primary actions will set - * themselves to not manage key bindings, so they don't appear in the key bindings options, - * and will then create an instance of this class and register it with the tool. Then, each of - * those primary actions will listen for options changes to know when the user has edited - * the key binding of the dummy action. The following snippet is an example of this usage, - * taken from the constructor of a DockingAction: - *

        - *       // setup key binding management
        - *       setKeyBindingManaged( false ); // our dummy will handle this task, not us
        - *       KeyStroke keyStroke = ...;
        - *       PluginTool tool = plugin.getTool();
        - *       tool.addAction( new DummyKeyBindingsOptionsAction( ACTION_NAME, keyStroke ) );
        - *       
        - *       // setup options to know when the dummy key binding is changed
        - *       Options options = tool.getOptions(ToolConstants.KEY_BINDINGS);        
        - *       KeyStroke optionsKeyStroke = options.getKeyStroke( "Tool", ACTION_NAME, keyStroke );
        - *       
        - *       if (!keyStroke(optionsKeyStroke)) {
        - *           // user-defined keystroke
        - *           setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
        - *       }
        - *       else {
        - *           setKeyBindingData(new KeyBindingData(keyStroke));
        - *       }
        - *
        - *       options.addOptionsChangeListener( ... );
        - * 
        - * - * And for changes to the options keybinding value: - *
        - *  public void optionsChanged(Options options, String name, Object oldValue, Object newValue) {
        - *      KeyStroke keyStroke = (KeyStroke) newValue;
        - *      if (name.startsWith(KEY_BINDING_NAME)) {
        - *          setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
        - *      }
        - *  }
        - * 
        - */ -public class DummyKeyBindingsOptionsAction extends DockingAction { - public static final String DEFAULT_OWNER = "Tool"; - - /** - * Creates a new dummy action by the given name and default keystroke value. - * @param name The name of the action--this will be displayed in the options as the name of - * key binding's action. - * @param defaultKeyStroke The default keystroke value for this action. This value may be null. - */ - public DummyKeyBindingsOptionsAction(String name, KeyStroke defaultKeyStroke) { - super(name, DEFAULT_OWNER); - - if (defaultKeyStroke != null) { - setKeyBindingData(new KeyBindingData(defaultKeyStroke)); - } - - // Dummy keybinding actions don't have help--the real action does - DockingWindowManager.getHelpService().excludeFromHelp(this); - } - - @Override - public void actionPerformed(ActionContext context) { - // no-op; this is a dummy! - } - - @Override - public boolean isAddToPopup(ActionContext context) { - return false; - } - - @Override - public boolean isEnabledForContext(ActionContext context) { - return false; - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java index 775460bf49..df39ec4738 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java @@ -21,6 +21,7 @@ import javax.swing.Icon; import javax.swing.event.ChangeListener; import docking.action.DockingActionIf; +import docking.actions.KeyBindingUtils; import ghidra.framework.plugintool.util.*; import ghidra.util.Msg; import resources.ResourceManager; @@ -212,12 +213,12 @@ public class PluginConfigurationModel { * @param pluginDescription The description for which to find loaded actions. * @return all of the actions loaded by the Plugin represented by the given PluginDescription. */ - public List getActionsForPlugin(PluginDescription pluginDescription) { + public Set getActionsForPlugin(PluginDescription pluginDescription) { if (!isLoaded(pluginDescription)) { - return Collections.emptyList(); + return Collections.emptySet(); } - return tool.getDockingActionsByOwnerName(pluginDescription.getName()); + return KeyBindingUtils.getKeyBindingActionsForOwner(tool, pluginDescription.getName()); } /** diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java index 1e45ab40c7..91152002bd 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java @@ -33,7 +33,7 @@ import org.jdom.Element; import docking.*; import docking.action.*; -import docking.actions.DockingToolActionManager; +import docking.actions.ToolActions; import docking.framework.AboutDialog; import docking.framework.ApplicationInformationDisplayFactory; import docking.framework.SplashScreen; @@ -158,7 +158,7 @@ public abstract class PluginTool extends AbstractDockingTool eventMgr = new EventManager(this); serviceMgr = new ServiceManager(); installServices(); - actionMgr = new DockingToolActionManager(this, winMgr); + actionMgr = new ToolActions(this, winMgr); pluginMgr = new PluginManager(this, serviceMgr); dialogMgr = new DialogManager(this); initActions(); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java index 972488ab9d..9d6e39f9cc 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java @@ -20,6 +20,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import java.util.List; +import java.util.Map.Entry; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -30,7 +31,8 @@ import docking.DockingUtils; import docking.KeyEntryTextField; import docking.action.DockingActionIf; import docking.action.KeyBindingData; -import docking.util.KeyBindingUtils; +import docking.actions.KeyBindingUtils; +import docking.tool.util.DockingToolConstants; import docking.widgets.MultiLineLabel; import docking.widgets.OptionDialog; import docking.widgets.label.GIconLabel; @@ -38,9 +40,7 @@ import docking.widgets.table.*; import ghidra.framework.options.Options; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; -import ghidra.util.HTMLUtilities; -import ghidra.util.ReservedKeyBindings; +import ghidra.util.*; import ghidra.util.exception.AssertException; import ghidra.util.layout.PairLayout; import ghidra.util.layout.VerticalLayout; @@ -65,11 +65,13 @@ public class KeyBindingsPanel extends JPanel { private KeyBindingsTableModel tableModel; private ListSelectionModel selectionModel; private Options options; - private Map actionMap; // map action name to keystroke - private Map> keyMap; // map keystroke name to ArrayList of action names - private List actionList; - private Map originalValues; // original mapping for action name to - // keystroke (to know what changed) + + private Map actionsByFullName; + private Map> actionNamesByKeyStroke; + private Map keyStrokesByFullName; + private Map originalValues; // to know what has been changed + private List tableActions; + private KeyEntryTextField ksField; private boolean unappliedChanges; @@ -78,14 +80,10 @@ public class KeyBindingsPanel extends JPanel { private PropertyChangeListener propertyChangeListener; private GTableFilterPanel tableFilterPanel; - /** - * Constructor - * @param options options that have the key binding mappings. - */ public KeyBindingsPanel(PluginTool tool, Options options) { this.tool = tool; this.options = options; - actionList = new ArrayList<>(); + tableActions = new ArrayList<>(); create(); createActionMap(); addListeners(); @@ -104,10 +102,10 @@ public class KeyBindingsPanel extends JPanel { * Apply the changes to the actions. */ public void apply() { - Iterator iter = actionMap.keySet().iterator(); + Iterator iter = keyStrokesByFullName.keySet().iterator(); while (iter.hasNext()) { String actionName = iter.next(); - KeyStroke currentKeyStroke = actionMap.get(actionName); + KeyStroke currentKeyStroke = keyStrokesByFullName.get(actionName); KeyStroke originalKeyStroke = originalValues.get(actionName); updateOptions(actionName, originalKeyStroke, currentKeyStroke); } @@ -115,7 +113,7 @@ public class KeyBindingsPanel extends JPanel { changesMade(false); } - private boolean updateOptions(String actionName, KeyStroke currentKeyStroke, + private boolean updateOptions(String fullActionName, KeyStroke currentKeyStroke, KeyStroke newKeyStroke) { if ((currentKeyStroke != null && currentKeyStroke.equals(newKeyStroke)) || (currentKeyStroke == null && newKeyStroke == null)) { @@ -123,17 +121,17 @@ public class KeyBindingsPanel extends JPanel { } if (newKeyStroke != null) { - options.setKeyStroke(actionName, newKeyStroke); + options.setKeyStroke(fullActionName, newKeyStroke); } else { - options.removeOption(actionName); + options.removeOption(fullActionName); } - originalValues.put(actionName, newKeyStroke); - actionMap.put(actionName, newKeyStroke); + originalValues.put(fullActionName, newKeyStroke); + keyStrokesByFullName.put(fullActionName, newKeyStroke); - List actions = tool.getDockingActionsByFullActionName(actionName); + Set actions = tool.getAllActions(); for (DockingActionIf action : actions) { - if (action.isKeyBindingManaged()) { + if (action.getFullName().equals(fullActionName)) { action.setUnvalidatedKeyBindingData(new KeyBindingData(newKeyStroke)); } } @@ -149,46 +147,40 @@ public class KeyBindingsPanel extends JPanel { while (iter.hasNext()) { String actionName = iter.next(); KeyStroke originalKS = originalValues.get(actionName); - KeyStroke modifiedKS = actionMap.get(actionName); + KeyStroke modifiedKS = keyStrokesByFullName.get(actionName); if (modifiedKS != null && !modifiedKS.equals(originalKS)) { - actionMap.put(actionName, originalKS); + keyStrokesByFullName.put(actionName, originalKS); } } tableModel.fireTableDataChanged(); } public void reload() { - // run this after the current pending events in the swing - // thread so that the screen will repaint itself - SwingUtilities.invokeLater(() -> { - // clear the current user key stroke so that it does not - // appear as though the user is editing while restoring + Swing.runLater(() -> { + // clear the current user key stroke so that it does not appear as though the + // user is editing while restoring actionTable.clearSelection(); restoreDefaultKeybindings(); }); } - /** - * Create the maps for actions and names. - */ private void createActionMap() { - actionMap = new HashMap<>(); - keyMap = new HashMap<>(); + keyStrokesByFullName = new HashMap<>(); + actionNamesByKeyStroke = new HashMap<>(); originalValues = new HashMap<>(); String longestName = ""; - List actions = tool.getAllActions(); - for (DockingActionIf action : actions) { - if (!action.isKeyBindingManaged()) { - continue; - } + actionsByFullName = KeyBindingUtils.getAllActionsByFullName(tool); + Set> entries = actionsByFullName.entrySet(); + for (Entry entry : entries) { - String actionName = action.getFullName(); - actionList.add(action); + DockingActionIf action = entry.getValue(); + tableActions.add(action); + String actionName = entry.getKey(); KeyStroke ks = options.getKeyStroke(actionName, null); - actionMap.put(actionName, ks); + keyStrokesByFullName.put(actionName, ks); addToKeyMap(ks, actionName); originalValues.put(actionName, ks); @@ -326,11 +318,10 @@ public class KeyBindingsPanel extends JPanel { return; } - // run this after the current pending events in the swing - // thread so that the screen will repaint itself - SwingUtilities.invokeLater(() -> { - // clear the current user key stroke so that it does not - // appear as though the user is editing while importing + // give Swing a chance to repaint + Swing.runLater(() -> { + // clear the current user key stroke so that it does not appear as though the + // user is editing while importing actionTable.clearSelection(); processKeyBindingsFromOptions(KeyBindingUtils.importKeyBindings()); }); @@ -347,10 +338,9 @@ public class KeyBindingsPanel extends JPanel { return; } - // run this after the current pending events in the swing - // thread so that the screen will repaint itself - SwingUtilities.invokeLater(() -> { - ToolOptions keyBindingOptions = tool.getOptions(ToolConstants.KEY_BINDINGS); + // give Swing a chance to repaint + Swing.runLater(() -> { + ToolOptions keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS); KeyBindingUtils.exportKeyBindings(keyBindingOptions); }); }); @@ -419,16 +409,16 @@ public class KeyBindingsPanel extends JPanel { } private void restoreDefaultKeybindings() { - Iterator iter = actionMap.keySet().iterator(); + Iterator iter = keyStrokesByFullName.keySet().iterator(); while (iter.hasNext()) { String actionName = iter.next(); - List actions = tool.getDockingActionsByFullActionName(actionName); - if (actions.size() == 0) { + DockingActionIf action = actionsByFullName.get(actionName); + if (action == null) { throw new AssertException("No actions defined for " + actionName); } - KeyStroke currentKeyStroke = actionMap.get(actionName); - KeyBindingData defaultBinding = actions.get(0).getDefaultKeyBindingData(); + KeyStroke currentKeyStroke = keyStrokesByFullName.get(actionName); + KeyBindingData defaultBinding = action.getDefaultKeyBindingData(); KeyStroke newKeyStroke = (defaultBinding == null) ? null : defaultBinding.getKeyBinding(); @@ -447,21 +437,11 @@ public class KeyBindingsPanel extends JPanel { selectionModel.addListSelectionListener(new TableSelectionListener()); } - /** - * Update the keyMap and the actionMap and enable the apply button on - * the dialog. - * @param action plugin action could be null if ksName is not associated - * with a plugin action - * @param defaultActionName name of the action - * @param ksName keystroke name - * @return true if the old keystroke is different from the current - * keystroke - */ private boolean checkAction(String actionName, KeyStroke keyStroke) { String ksName = KeyEntryTextField.parseKeyStroke(keyStroke); // remove old keystroke for action name - KeyStroke oldKs = actionMap.get(actionName); + KeyStroke oldKs = keyStrokesByFullName.get(actionName); if (oldKs != null) { String oldName = KeyEntryTextField.parseKeyStroke(oldKs); if (oldName.equals(ksName)) { @@ -471,7 +451,7 @@ public class KeyBindingsPanel extends JPanel { } addToKeyMap(keyStroke, actionName); - actionMap.put(actionName, keyStroke); + keyStrokesByFullName.put(actionName, keyStroke); changesMade(true); return true; } @@ -494,7 +474,7 @@ public class KeyBindingsPanel extends JPanel { } int selectedRow = actionTable.getSelectedRow(); int modelRow = tableFilterPanel.getModelRow(selectedRow); - return actionList.get(modelRow).getFullName(); + return tableActions.get(modelRow).getFullName(); } /** @@ -505,10 +485,10 @@ public class KeyBindingsPanel extends JPanel { return; } String ksName = KeyEntryTextField.parseKeyStroke(ks); - List list = keyMap.get(ksName); + List list = actionNamesByKeyStroke.get(ksName); if (list == null) { list = new ArrayList<>(); - keyMap.put(ksName, list); + actionNamesByKeyStroke.put(ksName, list); } if (!list.contains(actionName)) { list.add(actionName); @@ -523,11 +503,11 @@ public class KeyBindingsPanel extends JPanel { return; } String ksName = KeyEntryTextField.parseKeyStroke(ks); - List list = keyMap.get(ksName); + List list = actionNamesByKeyStroke.get(ksName); if (list != null) { list.remove(actionName); if (list.isEmpty()) { - keyMap.remove(ksName); + actionNamesByKeyStroke.remove(ksName); } } } @@ -537,7 +517,7 @@ public class KeyBindingsPanel extends JPanel { * @param ksName name of Keystroke that has multiple actions mapped */ private void showActionMapped(String ksName) { - List list = keyMap.get(ksName); + List list = actionNamesByKeyStroke.get(ksName); if (list == null) { return; } @@ -603,7 +583,7 @@ public class KeyBindingsPanel extends JPanel { // prevent non-existing keybindings from being added to Ghidra (this can happen // when actions exist in the imported bindings, but have been removed from // Ghidra - if (!actionMap.containsKey(name)) { + if (!keyStrokesByFullName.containsKey(name)) { continue; } @@ -655,12 +635,10 @@ public class KeyBindingsPanel extends JPanel { char keyChar = keyStroke.getKeyChar(); if (Character.isWhitespace(keyChar) || Character.getType(keyChar) == Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE) { - // remove keystroke removeKeystroke(actionName); } else { - // check the action to see if is different than the current - // value + // check the action to see if is different than the current value return checkAction(actionName, keyStroke); } } @@ -671,20 +649,24 @@ public class KeyBindingsPanel extends JPanel { private void removeKeystroke(String selectedActionName) { ksField.setText(""); - if (actionMap.containsKey(selectedActionName)) { - KeyStroke stroke = actionMap.get(selectedActionName); + if (keyStrokesByFullName.containsKey(selectedActionName)) { + KeyStroke stroke = keyStrokesByFullName.get(selectedActionName); if (stroke == null) { // nothing to remove; nothing has changed return; } removeFromKeyMap(stroke, selectedActionName); - actionMap.put(selectedActionName, null); + keyStrokesByFullName.put(selectedActionName, null); tableModel.fireTableDataChanged(); changesMade(true); } } + Map getKeyStrokeMap() { + return keyStrokesByFullName; + } + //================================================================================================== // Inner Classes //================================================================================================== @@ -698,8 +680,12 @@ public class KeyBindingsPanel extends JPanel { return; } - String selectedAction = getSelectedAction(); - KeyStroke ks = actionMap.get(selectedAction); + String fullActionName = getSelectedAction(); + if (fullActionName == null) { + return; + } + + KeyStroke ks = keyStrokesByFullName.get(fullActionName); String ksName = ""; clearInfoPanel(); @@ -713,18 +699,12 @@ public class KeyBindingsPanel extends JPanel { statusLabel.setPreferredSize( new Dimension(statusLabel.getPreferredSize().width, STATUS_LABEL_HEIGHT)); - try { - List actions = - tool.getDockingActionsByFullActionName(selectedAction); - String description = actions.get(0).getDescription(); - if (description == null || description.trim().isEmpty()) { - description = actions.get(0).getName(); - } - statusLabel.setText("" + HTMLUtilities.escapeHTML(description)); - } - catch (Exception ex) { - statusLabel.setText(""); + DockingActionIf action = actionsByFullName.get(fullActionName); + String description = action.getDescription(); + if (description == null || description.trim().isEmpty()) { + description = action.getName(); } + statusLabel.setText("" + HTMLUtilities.escapeHTML(description)); } } @@ -748,7 +728,7 @@ public class KeyBindingsPanel extends JPanel { return action.getName(); case KEY_BINDING: - KeyStroke ks = actionMap.get(action.getFullName()); + KeyStroke ks = keyStrokesByFullName.get(action.getFullName()); if (ks != null) { return KeyEntryTextField.parseKeyStroke(ks); } @@ -761,7 +741,7 @@ public class KeyBindingsPanel extends JPanel { @Override public List getModelData() { - return actionList; + return tableActions; } @Override @@ -781,7 +761,7 @@ public class KeyBindingsPanel extends JPanel { @Override public int getRowCount() { - return actionList.size(); + return tableActions.size(); } } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java index b902d1b206..5fed0e4e1b 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java @@ -17,8 +17,7 @@ package ghidra.framework.plugintool.dialog; import java.awt.Color; import java.awt.Point; -import java.util.Collections; -import java.util.List; +import java.util.*; import javax.swing.KeyStroke; import javax.swing.text.SimpleAttributeSet; @@ -35,9 +34,9 @@ import ghidra.framework.plugintool.util.PluginStatus; * Panel that contains a JTextPane to show plugin description information. */ class PluginDetailsPanel extends AbstractDetailsPanel { - + private static SimpleAttributeSet nameAttrSet; - private static SimpleAttributeSet depNameAttrSet; + private static SimpleAttributeSet depNameAttrSet; private static SimpleAttributeSet descrAttrSet; private static SimpleAttributeSet categoriesAttrSet; private static SimpleAttributeSet classAttrSet; @@ -47,84 +46,62 @@ class PluginDetailsPanel extends AbstractDetailsPanel { private static SimpleAttributeSet noValueAttrSet; private final PluginConfigurationModel model; - + PluginDetailsPanel(PluginConfigurationModel model) { super(); this.model = model; createFieldAttributes(); createMainPanel(); } - + void setPluginDescription(PluginDescription pluginDescription) { - - textLabel.setText(""); - if (pluginDescription == null) { - return; - } - + + textLabel.setText(""); + if (pluginDescription == null) { + return; + } + List dependencies = model.getDependencies(pluginDescription); Collections.sort(dependencies, (pd1, pd2) -> pd1.getName().compareTo(pd2.getName())); StringBuilder buffer = new StringBuilder(""); - - buffer.append( "" ); - - insertRowTitle(buffer, "Name"); + + buffer.append("
        "); + + insertRowTitle(buffer, "Name"); insertRowValue(buffer, pluginDescription.getName(), !dependencies.isEmpty() ? depNameAttrSet : nameAttrSet); - insertRowTitle(buffer, "Description"); - insertRowValue(buffer, formatDescription(pluginDescription.getDescription()), descrAttrSet); + insertRowTitle(buffer, "Description"); + insertRowValue(buffer, formatDescription(pluginDescription.getDescription()), descrAttrSet); - insertRowTitle(buffer, "Status"); - insertRowValue(buffer, - pluginDescription.getStatus().getDescription(), - (pluginDescription.getStatus() == PluginStatus.RELEASED) ? - titleAttrSet :developerAttrSet); + insertRowTitle(buffer, "Status"); + insertRowValue(buffer, pluginDescription.getStatus().getDescription(), + (pluginDescription.getStatus() == PluginStatus.RELEASED) ? titleAttrSet + : developerAttrSet); - insertRowTitle(buffer, "Package"); - insertRowValue(buffer, pluginDescription.getPluginPackage().getName(), categoriesAttrSet); + insertRowTitle(buffer, "Package"); + insertRowValue(buffer, pluginDescription.getPluginPackage().getName(), categoriesAttrSet); - insertRowTitle(buffer, "Category"); - insertRowValue(buffer, pluginDescription.getCategory(), categoriesAttrSet); + insertRowTitle(buffer, "Category"); + insertRowValue(buffer, pluginDescription.getCategory(), categoriesAttrSet); - insertRowTitle(buffer, "Plugin Class"); - insertRowValue(buffer, pluginDescription.getPluginClass().getName(), classAttrSet); + insertRowTitle(buffer, "Plugin Class"); + insertRowValue(buffer, pluginDescription.getPluginClass().getName(), classAttrSet); - insertRowTitle(buffer, "Class Location"); - insertRowValue(buffer, pluginDescription.getSourceLocation(), locAttrSet); + insertRowTitle(buffer, "Class Location"); + insertRowValue(buffer, pluginDescription.getSourceLocation(), locAttrSet); insertRowTitle(buffer, "Used By"); - - buffer.append( "" ); - buffer.append( "" ); - - insertRowTitle(buffer, "Services Required"); buffer.append(""); buffer.append(""); - // - // Developer - // - // - // Optional: Actions loaded by this plugin - // - addLoadedActionsContent( buffer, pluginDescription ); - - buffer.append( "
        " ); - - if (dependencies.isEmpty()) { - insertHTMLLine("None", titleAttrSet, buffer); - } - else { - for (int i = 0; i < dependencies.size(); i++) { - insertHTMLString(dependencies.get(i).getPluginClass().getName(), dependencyAttrSet, - buffer); - if (i < dependencies.size() - 1) { - insertHTMLString("
        ", dependencyAttrSet, buffer); - } - } - insertHTMLLine("", titleAttrSet, buffer); // add a newline - } - buffer.append( "
        "); - List> servicesRequired = pluginDescription.getServicesRequired(); - if (servicesRequired.isEmpty()) { + if (dependencies.isEmpty()) { insertHTMLLine("None", titleAttrSet, buffer); } else { - for (int i = 0; i < servicesRequired.size(); i++) { - insertHTMLString(servicesRequired.get(i).getName(), dependencyAttrSet, + for (int i = 0; i < dependencies.size(); i++) { + insertHTMLString(dependencies.get(i).getPluginClass().getName(), dependencyAttrSet, buffer); if (i < dependencies.size() - 1) { insertHTMLString("
        ", dependencyAttrSet, buffer); @@ -135,165 +112,185 @@ class PluginDetailsPanel extends AbstractDetailsPanel { buffer.append("
        " ); + insertRowTitle(buffer, "Services Required"); - textLabel.setText( buffer.toString() ); - sp.getViewport().setViewPosition(new Point(0,0)); - } + buffer.append(""); + + List> servicesRequired = pluginDescription.getServicesRequired(); + if (servicesRequired.isEmpty()) { + insertHTMLLine("None", titleAttrSet, buffer); + } + else { + for (int i = 0; i < servicesRequired.size(); i++) { + insertHTMLString(servicesRequired.get(i).getName(), dependencyAttrSet, buffer); + if (i < dependencies.size() - 1) { + insertHTMLString("
        ", dependencyAttrSet, buffer); + } + } + insertHTMLLine("", titleAttrSet, buffer); // add a newline + } + buffer.append(""); + buffer.append(""); + + // + // Developer + // + // + // Optional: Actions loaded by this plugin + // + addLoadedActionsContent(buffer, pluginDescription); + + buffer.append(""); + + textLabel.setText(buffer.toString()); + sp.getViewport().setViewPosition(new Point(0, 0)); + } // creates an HTML table to display actions loaded by the plugin private void addLoadedActionsContent(StringBuilder buffer, PluginDescription pluginDescription) { - if ( !model.isLoaded( pluginDescription ) ) { - return; - } - - buffer.append( "" ); - buffer.append( "" ); - insertHTMLLine("Loaded Actions:", titleAttrSet, buffer); - buffer.append( "" ); + if (!model.isLoaded(pluginDescription)) { + return; + } - List actionList = model.getActionsForPlugin( pluginDescription ); - if ( actionList.size() == 0 ) { - buffer.append( "" ); - insertHTMLLine("No actions for plugin", noValueAttrSet, buffer); - buffer.append( "" ); - buffer.append( "" ); - return; - } - - buffer.append( "" ); - - buffer.append( "" ); - - for ( DockingActionIf dockableAction : actionList ) { - buffer.append( "" ); - - buffer.append( ""); + buffer.append(""); - if ( popupPath != null ) { - insertHTMLString( "(in a context popup menu)", noValueAttrSet, buffer ); - } - else { - insertHTMLString( "Not in a menu", noValueAttrSet, buffer ); - } - } - - buffer.append( "" ); - - buffer.append( "" ); - } - - buffer.append( "
        Action NameMenu PathKeybinding
        " ); - insertHTMLString( dockableAction.getName(), locAttrSet, buffer ); - buffer.append( "" ); - MenuData menuBarData = dockableAction.getMenuBarData(); - String[] menuPath = menuBarData == null ? null : menuBarData.getMenuPath(); - String menuPathString = createStringForMenuPath( menuPath ); - if ( menuPathString != null ) { - insertHTMLString( menuPathString, locAttrSet, buffer ); - } - else { - MenuData popupMenuData = dockableAction.getPopupMenuData(); - String[] popupPath = popupMenuData == null ? null : popupMenuData.getMenuPath(); + buffer.append("
        "); + insertHTMLLine("Loaded Actions:", titleAttrSet, buffer); + buffer.append("" ); - KeyStroke keyBinding = dockableAction.getKeyBinding(); - if ( keyBinding != null ) { - String keyStrokeString = DockingKeyBindingAction.parseKeyStroke( keyBinding ); - insertHTMLString( keyStrokeString, locAttrSet, buffer ); - } - else { - insertHTMLString( "No keybinding", noValueAttrSet, buffer ); - } - - buffer.append( "
        " ); - buffer.append( "" ); - buffer.append( "" ); + Set actions = model.getActionsForPlugin(pluginDescription); + if (actions.size() == 0) { + buffer.append(""); + insertHTMLLine("No actions for plugin", noValueAttrSet, buffer); + buffer.append(""); + buffer.append(""); + return; + } + + buffer.append(""); + + buffer.append( + ""); + + for (DockingActionIf dockableAction : actions) { + buffer.append(""); + + buffer.append(""); + + buffer.append(""); + } + + buffer.append("
        Action NameMenu PathKeybinding
        "); + insertHTMLString(dockableAction.getName(), locAttrSet, buffer); + buffer.append(""); + MenuData menuBarData = dockableAction.getMenuBarData(); + String[] menuPath = menuBarData == null ? null : menuBarData.getMenuPath(); + String menuPathString = createStringForMenuPath(menuPath); + if (menuPathString != null) { + insertHTMLString(menuPathString, locAttrSet, buffer); + } + else { + MenuData popupMenuData = dockableAction.getPopupMenuData(); + String[] popupPath = popupMenuData == null ? null : popupMenuData.getMenuPath(); + + if (popupPath != null) { + insertHTMLString("(in a context popup menu)", noValueAttrSet, buffer); + } + else { + insertHTMLString("Not in a menu", noValueAttrSet, buffer); + } + } + + buffer.append(""); + KeyStroke keyBinding = dockableAction.getKeyBinding(); + if (keyBinding != null) { + String keyStrokeString = DockingKeyBindingAction.parseKeyStroke(keyBinding); + insertHTMLString(keyStrokeString, locAttrSet, buffer); + } + else { + insertHTMLString("No keybinding", noValueAttrSet, buffer); + } + + buffer.append("
        "); + buffer.append(""); + buffer.append(""); } - - private String createStringForMenuPath( String[] path ) { - if ( path == null ) { - return null; - } - - StringBuffer buffy = new StringBuffer(); - for ( int i = 0; i < path.length; i++ ) { - buffy.append( path[i].replaceAll( "\\&", "" ) ); // strip off the mnemonic identifier '&' - if ( i != path.length-1 ) { - buffy.append( "->" ); - } - } - return buffy.toString(); + + private String createStringForMenuPath(String[] path) { + if (path == null) { + return null; + } + + StringBuffer buffy = new StringBuffer(); + for (int i = 0; i < path.length; i++) { + buffy.append(path[i].replaceAll("\\&", "")); // strip off the mnemonic identifier '&' + if (i != path.length - 1) { + buffy.append("->"); + } + } + return buffy.toString(); } - + @Override protected void createFieldAttributes() { - titleAttrSet = new SimpleAttributeSet(); + titleAttrSet = new SimpleAttributeSet(); titleAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - titleAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + titleAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); titleAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); titleAttrSet.addAttribute(StyleConstants.Foreground, new Color(140, 0, 0)); nameAttrSet = new SimpleAttributeSet(); nameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - nameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + nameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); nameAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); nameAttrSet.addAttribute(StyleConstants.Foreground, new Color(0, 204, 51)); depNameAttrSet = new SimpleAttributeSet(); depNameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - depNameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + depNameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); depNameAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); depNameAttrSet.addAttribute(StyleConstants.Foreground, Color.RED); - + descrAttrSet = new SimpleAttributeSet(); descrAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - descrAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + descrAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); descrAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); descrAttrSet.addAttribute(StyleConstants.Foreground, Color.BLUE); categoriesAttrSet = new SimpleAttributeSet(); categoriesAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - categoriesAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + categoriesAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); categoriesAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); categoriesAttrSet.addAttribute(StyleConstants.Foreground, new Color(204, 0, 204)); classAttrSet = new SimpleAttributeSet(); classAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced"); - classAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + classAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); classAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); classAttrSet.addAttribute(StyleConstants.Foreground, Color.BLACK); - + locAttrSet = new SimpleAttributeSet(); locAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - locAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + locAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); locAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); locAttrSet.addAttribute(StyleConstants.Foreground, Color.DARK_GRAY); - + developerAttrSet = new SimpleAttributeSet(); - developerAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - developerAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + developerAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); + developerAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); developerAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); developerAttrSet.addAttribute(StyleConstants.Foreground, new Color(230, 15, 85)); - + dependencyAttrSet = new SimpleAttributeSet(); - dependencyAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced"); - dependencyAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + dependencyAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced"); + dependencyAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); dependencyAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); dependencyAttrSet.addAttribute(StyleConstants.Foreground, new Color(23, 100, 30)); - + noValueAttrSet = new SimpleAttributeSet(); - noValueAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - noValueAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); - noValueAttrSet.addAttribute(StyleConstants.Italic, Boolean.TRUE); - noValueAttrSet.addAttribute(StyleConstants.Foreground, new Color(192, 192, 192)); + noValueAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); + noValueAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); + noValueAttrSet.addAttribute(StyleConstants.Italic, Boolean.TRUE); + noValueAttrSet.addAttribute(StyleConstants.Foreground, new Color(192, 192, 192)); } } - diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ProjectActionManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ProjectActionManager.java deleted file mode 100644 index 315f116a3b..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ProjectActionManager.java +++ /dev/null @@ -1,289 +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.plugintool.mgr; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.*; - -import javax.swing.KeyStroke; - -import docking.ComponentProvider; -import docking.DockingWindowManager; -import docking.action.*; -import ghidra.framework.options.OptionType; -import ghidra.framework.options.Options; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.ToolConstants; -import ghidra.util.exception.AssertException; - -/** - * Helper class to manage plugin actions for the tool. - */ -public class ProjectActionManager implements PropertyChangeListener { - private DockingWindowManager winMgr; - private Map> actionMap; - private Options keyBindingOptions; - private PluginTool tool; - - /** - * Construct an ActionManager. - * @param tool plugin tool using this ActionManager - * @param winMgr manager of the "Docking" arrangement - * of a set of components and actions in the tool - */ - public ProjectActionManager(PluginTool tool, DockingWindowManager winMgr) { - this.tool = tool; - this.winMgr = winMgr; - actionMap = new HashMap<>(); - keyBindingOptions = tool.getOptions(ToolConstants.KEY_BINDINGS); - } - - public void dispose() { - actionMap.clear(); - } - - private void addActionToMap(DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - List newList = new ArrayList<>(); - newList.add(action); - actionMap.put(name, newList); - } - else { - actionList.add(action); - } - } - - private void removeActionFromMap(DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - return; - } - - if (actionList.remove(action) && actionList.isEmpty()) { - actionMap.remove(name); - } - } - - /** - * Adds the action to the tool. - * @param action the action to be added. - */ - public synchronized void addToolAction(DockingActionIf action) { - action.addPropertyChangeListener(this); - addActionToMap(action); - if (action.isKeyBindingManaged()) { - KeyStroke ks = action.getKeyBinding(); - keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, - null, null); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - winMgr.addToolAction(action); - } - - /** - * Removes the given action from the tool - * @param action the action to be removed. - */ - public synchronized void removeToolAction(DockingActionIf action) { - action.removePropertyChangeListener(this); - removeActionFromMap(action); - winMgr.removeToolAction(action); - } - - /** - * Remove all actions that have the given owner. - * @param owner owner of the actions to remove - */ - public synchronized void removeToolActions(String owner) { - List actions = getActions(owner); - for (DockingActionIf action : actions) { - removeToolAction(action); - } - } - - /** - * Add an action that works specifically with a component provider. - * @param provider provider associated with the action - * @param action local action to the provider - */ - public synchronized void addLocalAction(ComponentProvider provider, DockingActionIf action) { - checkForAlreadyAddedAction(provider, action); - - action.addPropertyChangeListener(this); - addActionToMap(action); - if (action.isKeyBindingManaged()) { - KeyStroke ks = action.getKeyBinding(); - keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, - null, null); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - winMgr.addLocalAction(provider, action); - } - - private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - return; - } - if (actionList.contains(action)) { - throw new AssertException("Cannot add the same action more than once. Provider " + - provider.getName() + " - action: " + name); - } - } - - /** - * Remove an action that works specifically with a component provider. - * @param provider provider associated with the action - * @param action local action to the provider - */ - public synchronized void removeProviderAction(ComponentProvider provider, - DockingActionIf action) { - action.removePropertyChangeListener(this); - removeActionFromMap(action); - winMgr.removeProviderAction(provider, action); - } - - /** - * Get all actions that have the action name which includes the action owner's name. - * - * @param fullName full name for the action, e.g., "My Action (My Plugin)" - * @return list of actions; empty if no action exists with the given name - */ - public List getDockingActionsByFullActionName(String fullActionName) { - List list = actionMap.get(fullActionName); - if (list == null) { - return new ArrayList<>(); - } - return new ArrayList<>(list); - } - - /** - * Returns a list of actions whose owner matches the given owner or all actions if the given - * owner is null. - *

        - * This method will only return a single instance of any named action, even if multiple - * actions have been registered with the same name. - *

        - * Note: Actions with the same name are assumed to be different instances of the same action. - * - * @param owner The of the action, or null to get all actions - * @return a list of deduped actions. - */ - private List getUniqueActionList(String owner) { - List matchingActionList = new ArrayList<>(); - - for (List actionList : actionMap.values()) { - // we only want *one* instance of duplicate actions - DockingActionIf action = actionList.get(0); - if (owner == null || action.getOwner().equals(owner)) { - matchingActionList.add(action); - } - } - - return matchingActionList; - } - - /** - * Get all actions for the given owner. - * @param owner owner of the actions - * @return array of actions; zero length array is returned if no - * action exists with the given name - */ - public synchronized List getActions(String owner) { - List list = getUniqueActionList(owner); - return list; - } - - /** - * Get a list of all actions in the tool. - * @return list of PluginAction objects - */ - public List getAllActions() { - return getUniqueActionList(null); - } - - /** - * Get the keybindings for each action so that they are still registered - * as being used; otherwise the options will be removed because they - * are noted as not being used. - * - */ - public synchronized void restoreKeyBindings() { - keyBindingOptions = tool.getOptions(ToolConstants.KEY_BINDINGS); - List actions = getAllActions(); - for (DockingActionIf action : actions) { - if (!action.isKeyBindingManaged()) { - continue; - } - KeyStroke ks = action.getKeyBinding(); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - } - - /** - * Get the actions for the given provider and remove them from the - * actionMap; call the window manager to remove the provider. - * @param provider provider to be removed - */ - public void removeComponent(ComponentProvider provider) { - Iterator iterator = winMgr.getComponentActions(provider); - while (iterator.hasNext()) { - DockingActionIf action = iterator.next(); - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList != null && actionList.remove(action) && actionList.isEmpty()) { - actionMap.remove(name); - } - } - winMgr.removeComponent(provider); - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) { - DockingAction action = (DockingAction) evt.getSource(); - if (!action.isKeyBindingManaged()) { - tool.setConfigChanged(true); - return; - } - KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue(); - KeyStroke newKeyStroke = keyBindingData.getKeyBinding(); - Options opt = tool.getOptions(ToolConstants.KEY_BINDINGS); - KeyStroke optKeyStroke = opt.getKeyStroke(action.getFullName(), null); - if (newKeyStroke == null) { - opt.removeOption(action.getFullName()); - } - else if (!newKeyStroke.equals(optKeyStroke)) { - opt.setKeyStroke(action.getFullName(), newKeyStroke); - tool.setConfigChanged(true); - } - } - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java index c2cd8985ca..2656eda05e 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/ToolServicesImpl.java @@ -15,6 +15,12 @@ */ package ghidra.framework.project.tool; +import java.io.*; +import java.util.*; + +import org.jdom.Document; +import org.jdom.output.XMLOutputter; + import ghidra.framework.ToolUtils; import ghidra.framework.data.ContentHandler; import ghidra.framework.data.DomainObjectAdapter; @@ -26,12 +32,6 @@ import ghidra.util.Msg; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.xml.GenericXMLOutputter; -import java.io.*; -import java.util.*; - -import org.jdom.Document; -import org.jdom.output.XMLOutputter; - /** * Implementation of service used to manipulate tools. */ @@ -42,7 +42,7 @@ class ToolServicesImpl implements ToolServices { private ToolChest toolChest; private ToolManagerImpl toolManager; - private List listeners = new ArrayList(); + private List listeners = new ArrayList<>(); private ToolChestChangeListener toolChestChangeListener; private Set contentHandlers; @@ -189,7 +189,7 @@ class ToolServicesImpl implements ToolServices { @Override public Set getContentTypeToolAssociations() { - Set set = new HashSet(); + Set set = new HashSet<>(); // get all known content types Set handlers = getContentHandlers(); @@ -232,7 +232,7 @@ class ToolServicesImpl implements ToolServices { @Override public Set getCompatibleTools(Class domainClass) { - Map nameToTemplateMap = new HashMap(); + Map nameToTemplateMap = new HashMap<>(); // // First, get all compatible tools in the tool chest @@ -290,12 +290,12 @@ class ToolServicesImpl implements ToolServices { } } - return new HashSet(nameToTemplateMap.values()); + return new HashSet<>(nameToTemplateMap.values()); } private Set getCompatibleContentHandlers( Class domainClass) { - Set set = new HashSet(); + Set set = new HashSet<>(); Set handlers = getContentHandlers(); for (ContentHandler contentHandler : handlers) { Class handlerDomainClass = @@ -327,7 +327,7 @@ class ToolServicesImpl implements ToolServices { return contentHandlers; } - contentHandlers = new HashSet(); + contentHandlers = new HashSet<>(); Set instances = ClassSearcher.getInstances(ContentHandler.class); for (ContentHandler contentHandler : instances) { // a bit of validation @@ -392,7 +392,7 @@ class ToolServicesImpl implements ToolServices { private Tool[] getSameNamedRunningTools(Tool tool) { String toolName = tool.getToolName(); Tool[] tools = toolManager.getRunningTools(); - List toolList = new ArrayList(tools.length); + List toolList = new ArrayList<>(tools.length); for (Tool element : tools) { if (toolName.equals(element.getToolName())) { toolList.add(element); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AssemblerPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AssemblerPluginScreenShots.java index 165715f96e..703c8263e4 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AssemblerPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/AssemblerPluginScreenShots.java @@ -18,12 +18,10 @@ package help.screenshot; import java.awt.AWTException; import java.awt.Robot; import java.awt.event.KeyEvent; -import java.util.List; import org.junit.Test; import docking.action.DockingActionIf; -import ghidra.app.plugin.core.assembler.AssembleDockingAction; import ghidra.app.plugin.core.assembler.AssemblerPlugin; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.framework.plugintool.util.PluginException; @@ -37,9 +35,7 @@ public class AssemblerPluginScreenShots extends GhidraScreenShotGenerator { positionCursor(0x0040512e); tool.addPlugin(AssemblerPlugin.class.getName()); - String fullActionName = "Assemble (AssemblerPlugin)"; - List actions = tool.getDockingActionsByFullActionName(fullActionName); - AssembleDockingAction action = (AssembleDockingAction) actions.get(0); + DockingActionIf action = getAction(tool, "AssemblerPlugin", "Assemble"); performAction(action, true); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java index 264e738402..08c4736744 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java @@ -156,10 +156,10 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator @Test public void testPreviewWindow() { - String fullActionName = "Show Preview Window (DataTypeManagerPlugin)"; - List action = tool.getDockingActionsByFullActionName(fullActionName); - performAction(action.get(0)); -// performAction("Show Preview Window", "DataTypeManagerPlugin", false); + + DockingActionIf action = getAction(tool, "DataTypeManagerPlugin", "Show Preview Window"); + performAction(action); + DataTypesProvider provider = getProvider(DataTypesProvider.class); GTree tree = (GTree) getInstanceField("archiveGTree", provider); GTreeRootNode rootNode = tree.getRootNode(); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java index df1b4dc1f3..c239c5904e 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java @@ -281,12 +281,11 @@ public class ToolScreenShots extends GhidraScreenShotGenerator { tool = env.launchDefaultTool(); DockingWindowManager windowManager = tool.getWindowManager(); - DockingActionManager actionMgr = - (DockingActionManager) getInstanceField("actionManager", windowManager); - String fullActionName = "Delete Function" + " (" + "FunctionPlugin" + ")"; - List actions = tool.getDockingActionsByFullActionName(fullActionName); + ActionToGuiMapper actionMgr = + (ActionToGuiMapper) getInstanceField("actionManager", windowManager); - final KeyEntryDialog keyEntryDialog = new KeyEntryDialog(actions.get(0), actionMgr); + DockingActionIf action = getAction(tool, "FunctionPlugin", "Delete Function"); + final KeyEntryDialog keyEntryDialog = new KeyEntryDialog(action, actionMgr); runSwing(() -> tool.showDialog(keyEntryDialog), false); captureDialog(); 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 4cb1b3a938..3e4ac23c54 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 @@ -18,14 +18,12 @@ */ package ghidra.framework.main; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.List; import javax.swing.*; @@ -239,10 +237,10 @@ public class SharedProjectUtil { } private static DockingActionIf getAction(FrontEndTool frontEndTool, String actionName) { - List actions = - frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)"); - assertEquals(1, actions.size()); - return actions.get(0); + + DockingActionIf action = + AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName); + return action; } private static class UtilProjectListener implements ProjectListener { diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java index cdcc9c7638..8057736af8 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java @@ -23,8 +23,6 @@ import java.util.*; import javax.swing.*; import javax.swing.tree.TreePath; -import org.junit.Assert; - import docking.*; import docking.action.DockingActionIf; import docking.test.AbstractDockingTest; @@ -341,15 +339,10 @@ public class FrontEndTestEnv { return new ArrayList<>(Arrays.asList(tools)); } - public List getFrontEndActions() { - return frontEndTool.getDockingActionsByOwnerName("FrontEndPlugin"); - } - public DockingActionIf getAction(String actionName) { - List a = - frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)"); - Assert.assertEquals(1, a.size()); - return a.get(0); + DockingActionIf action = + AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName); + return action; } public void performFrontEndAction(DockingActionIf action) {