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
- +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 colThe 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/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 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,
- Select the functions in the Functions window.
-- Right mouse click and select the Make - Selection option, OR select the button on +
- Right mouse click and select the Make + Selection option, OR select the button on the tool bar.
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
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 @@
- Click in the results table and press Ctrl+A.
-- Click on the in +
- Click on the in the tool bar, or right mouse click and choose Make Selection.
- The current selection will be set to the address of each result item.
@@ -93,7 +93,7 @@- Press and hold the right mouse button over the results table.
-- Click on the in +
- Click on the in the tool bar, or right mouse click and choose Make Selection.
- The current selection will be set to the address of all the highlighted items.
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
-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.
-
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 @@- Or, click the button in the Symbol +
- Or, click the button in the Symbol Table toolbar.
+Make Selection
-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. - *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,
- Select the rows containing the desired strings in the table.
-- Right mouse click and select the Make - Selection option, OR select the button on +
- Right mouse click and select the Make + Selection option, OR select the button on the tool bar.
- * 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 aCycleGroup
. */ -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 aCycleGroup
. */ -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; *- 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 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 DecompilerCodeComparisonPanelactions = - 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 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 TableAs 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) { + Setactions = 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 returntrue
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)"; - Listactions = - 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