From 599728ec49fa44f84ab049c5dd917c5934e3faef Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Mon, 23 Dec 2019 11:09:21 -0500 Subject: [PATCH] GT-3411 - Actions - fixed key bindings being removed from options after tool restart --- .../compositeeditor/AddBitFieldAction.java | 6 +- .../core/compositeeditor/ApplyAction.java | 8 +- .../core/compositeeditor/ArrayAction.java | 51 ++++++------ .../core/compositeeditor/ClearAction.java | 60 ++++++-------- .../CompositeEditorActionManager.java | 26 ------ .../CompositeEditorTableAction.java | 7 +- .../CreateInternalStructureAction.java | 9 +-- .../compositeeditor/CycleGroupAction.java | 3 +- .../core/compositeeditor/DeleteAction.java | 11 ++- .../core/compositeeditor/DuplicateAction.java | 16 ++-- .../DuplicateMultipleAction.java | 10 +-- .../compositeeditor/EditBitFieldAction.java | 6 +- .../compositeeditor/EditComponentAction.java | 8 +- .../core/compositeeditor/EditFieldAction.java | 10 +-- .../compositeeditor/HexNumbersAction.java | 10 +-- .../InsertUndefinedAction.java | 17 ++-- .../core/compositeeditor/MoveDownAction.java | 56 +++++++------ .../core/compositeeditor/MoveUpAction.java | 57 +++++++------ .../core/compositeeditor/PointerAction.java | 59 +++++++------- .../ShowComponentPathAction.java | 24 +----- .../core/compositeeditor/UnpackageAction.java | 14 ++-- .../core/datamgr/DataTypeManagerPlugin.java | 1 - .../datamgr/editor/DataTypeEditorManager.java | 68 +++++++++++++--- .../LocationReferencesPlugin.java | 2 +- .../core/scalartable/ScalarSearchPlugin.java | 2 +- .../core/stackeditor/StackEditorProvider.java | 17 +++- .../plugin/core/table/TableServicePlugin.java | 2 +- .../table/actions/DeleteTableRowAction.java | 34 ++++---- .../docking/action/KeyEntryDialogTest.java | 37 +++++---- .../datamgr/DataTypeManagerPluginTest.java | 39 ++++++--- .../dialog/KeyBindingUtilsTest.java | 60 +++++++++++--- .../java/docking/actions/KeyEntryDialog.java | 19 +++-- .../docking/actions/SharedActionRegistry.java | 2 +- .../SharedDockingActionPlaceholder.java | 64 +++++++++++++++ .../actions/SharedStubKeyBindingAction.java | 79 ++++++++++++------- .../java/docking/actions/ToolActions.java | 71 +++++++++++------ .../main/java/docking/tool/ToolConstants.java | 8 ++ .../ghidra/framework/options/ToolOptions.java | 5 +- 38 files changed, 577 insertions(+), 401 deletions(-) create mode 100644 Ghidra/Framework/Docking/src/main/java/docking/actions/SharedDockingActionPlaceholder.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java index d46790873a..29db035dd2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java @@ -31,14 +31,14 @@ import ghidra.util.exception.AssertException; */ public class AddBitFieldAction extends CompositeEditorTableAction { - private final static String ACTION_NAME = "Add Bitfield"; + public final static String ACTION_NAME = "Add Bitfield"; private final static String GROUP_NAME = BITFIELD_ACTION_GROUP; private final static String DESCRIPTION = "Add a bitfield at the position of a selected component"; - private static String[] popupPath = new String[] { ACTION_NAME }; + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; public AddBitFieldAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, null); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, null); setDescription(DESCRIPTION); if (!(model instanceof CompEditorModel)) { throw new AssertException("unsupported use"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java index 9738b2fe4d..65948da6a4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java @@ -27,13 +27,13 @@ import resources.ResourceManager; */ public class ApplyAction extends CompositeEditorTableAction { + public final static String ACTION_NAME = "Apply Editor Changes"; private final static String GROUP_NAME = BASIC_ACTION_GROUP; - private final static ImageIcon APPLY_ICON = ResourceManager.loadImage("images/disk.png"); - private final static String[] popupPath = new String[] { "Apply Edits" }; + private final static ImageIcon ICON = ResourceManager.loadImage("images/disk.png"); + private final static String[] POPUP_PATH = new String[] { "Apply Edits" }; public ApplyAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + "Apply Editor Changes", GROUP_NAME, popupPath, null, - APPLY_ICON); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); setDescription("Apply editor changes"); adjustEnablement(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ArrayAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ArrayAction.java index 3bb3cdde82..2f94329ad0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ArrayAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ArrayAction.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,17 +15,15 @@ */ package ghidra.app.plugin.core.compositeeditor; -import ghidra.util.exception.UsrException; - import java.awt.event.KeyEvent; import javax.swing.ImageIcon; import javax.swing.KeyStroke; -import resources.ResourceManager; import docking.ActionContext; import docking.action.KeyBindingData; - +import ghidra.util.exception.UsrException; +import resources.ResourceManager; /** * Action for use in the composite data type editor. @@ -34,33 +31,33 @@ import docking.action.KeyBindingData; */ public class ArrayAction extends CompositeEditorTableAction { - private final static ImageIcon arrayIcon = ResourceManager.loadImage("images/Array.png"); - private final static String ACTION_NAME = "Create Array"; - private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; - private final static String DESCRIPTION = "Create an array"; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, 0); - private static String[] popupPath = new String[] { ACTION_NAME }; + private final static ImageIcon ICON = ResourceManager.loadImage("images/Array.png"); + public final static String ACTION_NAME = "Create Array"; + private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; + private final static String DESCRIPTION = "Create an array"; + private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, 0); + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; - public ArrayAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, arrayIcon); - setDescription(DESCRIPTION); - setKeyBindingData( new KeyBindingData( keyStroke ) ); + public ArrayAction(CompositeEditorProvider provider) { + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); + setDescription(DESCRIPTION); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); - } - - @Override - public void actionPerformed(ActionContext context) { + } + + @Override + public void actionPerformed(ActionContext context) { try { model.createArray(); - } catch (UsrException e1) { + } + catch (UsrException e1) { model.setStatus(e1.getMessage()); } requestTableFocus(); - } - - @Override - public void adjustEnablement() { - setEnabled(model.isArrayAllowed()); - } -} + } + @Override + public void adjustEnablement() { + setEnabled(model.isArrayAllowed()); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ClearAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ClearAction.java index ef98bf5a4a..f7ac6bf4c6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ClearAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ClearAction.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,63 +15,50 @@ */ package ghidra.app.plugin.core.compositeeditor; -import ghidra.util.Msg; -import ghidra.util.exception.UsrException; - import java.awt.event.KeyEvent; import javax.swing.ImageIcon; import javax.swing.KeyStroke; import docking.ActionContext; -import docking.action.*; - +import docking.action.KeyBindingData; +import ghidra.util.Msg; +import ghidra.util.exception.UsrException; import resources.ResourceManager; - public class ClearAction extends CompositeEditorTableAction { + public final static String ACTION_NAME = "Clear Components"; private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; - private final static ImageIcon CLEAR_ICON = ResourceManager.loadImage("images/erase16.png"); - private final static String[] popupPath = new String[] { "Clear" }; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, 0); + private final static ImageIcon ICON = ResourceManager.loadImage("images/erase16.png"); + private final static String[] POPUP_PATH = new String[] { "Clear" }; + private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C, 0); - /** - * - * @param owner - * @param cycleGroup - */ public ClearAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + "Clear Components", - GROUP_NAME, popupPath, null, CLEAR_ICON); - + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); + setDescription("Clear the selected components"); - setKeyBindingData(new KeyBindingData(keyStroke)); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); } - - /* (non-Javadoc) - * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) - */ - @Override - public void actionPerformed(ActionContext context) { + + @Override + public void actionPerformed(ActionContext context) { try { model.clearSelectedComponents(); - } catch (OutOfMemoryError memExc) { + } + catch (OutOfMemoryError memExc) { String errMsg = "Couldn't clear components. Out of memory."; Msg.showError(this, null, "Out of Memory", errMsg, memExc); - } catch (UsrException ue) { + } + catch (UsrException ue) { model.setStatus(ue.getMessage()); } requestTableFocus(); - } - - /* (non-Javadoc) - * @see ghidra.app.plugin.datamanager.editor.CompositeEditorAction#adjustEnablement() - */ - @Override - public void adjustEnablement() { - setEnabled(model.isClearAllowed()); - } - + } + + @Override + public void adjustEnablement() { + setEnabled(model.isClearAllowed()); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorActionManager.java index 23dbf18328..aaba8d57cc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorActionManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorActionManager.java @@ -17,11 +17,7 @@ package ghidra.app.plugin.core.compositeeditor; import java.util.*; -import javax.swing.KeyStroke; - -import docking.action.KeyBindingData; import ghidra.app.services.DataTypeManagerService; -import ghidra.framework.options.Options; import ghidra.program.model.data.*; /** @@ -247,26 +243,4 @@ public class CompositeEditorActionManager { listeners.get(i).actionsRemoved(cea); } } - - /* (non-Javadoc) - * @see ghidra.framework.options.OptionsChangeListener#optionsChanged(ghidra.framework.options.Options, java.lang.String, java.lang.Object, java.lang.Object) - */ - public void optionsChanged(Options options, String name, Object oldValue, Object newValue) { - // Update the editor actions here. - // The favorites and cycle groups get handled by stateChanged() and cyclegroupChanged(). - CompositeEditorTableAction[] actions = getEditorActions(); - for (CompositeEditorTableAction action : actions) { - String actionName = action.getFullName(); - if (actionName.equals(name)) { - KeyStroke actionKs = action.getKeyBinding(); - KeyStroke oldKs = (KeyStroke) oldValue; - KeyStroke newKs = (KeyStroke) newValue; - if (actionKs == oldKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - break; - } - } - } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java index 03cd7d54f1..a490c69e7b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java @@ -46,12 +46,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group, String[] popupPath, String[] menuPath, ImageIcon icon) { - this(provider, name, group, popupPath, menuPath, icon, KeyBindingType.INDIVIDUAL); - } - - public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group, - String[] popupPath, String[] menuPath, ImageIcon icon, KeyBindingType kbType) { - super(name, provider.plugin.getName(), kbType); + super(name, provider.plugin.getName(), KeyBindingType.SHARED); this.provider = provider; model = provider.getModel(); if (menuPath != null) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java index 0fce8f5c94..f845f33353 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CreateInternalStructureAction.java @@ -33,17 +33,16 @@ import resources.ResourceManager; */ public class CreateInternalStructureAction extends CompositeEditorTableAction { - private final static ImageIcon createInternalStructureIcon = + private final static ImageIcon ICON = ResourceManager.loadImage("images/cstruct.png"); - private final static String ACTION_NAME = "Create Structure From Selection"; + public final static String ACTION_NAME = "Create Structure From Selection"; private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; private final static String DESCRIPTION = "Create a new structure from the selected components and replace them with it."; - private static String[] popupPath = new String[] { ACTION_NAME }; + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; public CreateInternalStructureAction(StructureEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, - createInternalStructureIcon); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); setDescription(DESCRIPTION); adjustEnablement(); } 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 468f726098..234d5becb4 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 @@ -19,7 +19,6 @@ import javax.swing.KeyStroke; import docking.ActionContext; import docking.action.KeyBindingData; -import docking.action.KeyBindingType; import ghidra.program.model.data.CycleGroup; /** @@ -33,7 +32,7 @@ public class CycleGroupAction extends CompositeEditorTableAction { public CycleGroupAction(CompositeEditorProvider provider, CycleGroup cycleGroup) { super(provider, cycleGroup.getName(), GROUP_NAME, new String[] { "Cycle", cycleGroup.getName() }, - new String[] { "Cycle", cycleGroup.getName() }, null, KeyBindingType.SHARED); + new String[] { "Cycle", cycleGroup.getName() }, null); this.cycleGroup = cycleGroup; getPopupMenuData().setParentMenuGroup(GROUP_NAME); initKeyStroke(cycleGroup.getDefaultKeyStroke()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java index d8aa270d8f..95252ee9fa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java @@ -30,17 +30,16 @@ import resources.ResourceManager; public class DeleteAction extends CompositeEditorTableAction { + public final static String ACTION_NAME = "Delete Components"; private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; - private final static ImageIcon DELETE_ICON = - ResourceManager.loadImage("images/edit-delete.png"); + private final static ImageIcon ICON = ResourceManager.loadImage("images/edit-delete.png"); private final static String[] popupPath = new String[] { "Delete" }; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0); + private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0); public DeleteAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + "Delete Components", GROUP_NAME, popupPath, null, - DELETE_ICON); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, ICON); - setKeyBindingData(new KeyBindingData(keyStroke)); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); setDescription("Delete the selected components"); adjustEnablement(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java index bf136bb86a..9fdfe0393b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateAction.java @@ -32,19 +32,19 @@ import resources.ResourceManager; */ public class DuplicateAction extends CompositeEditorTableAction { - private final static ImageIcon duplicateDataIcon = - ResourceManager.loadImage("images/DuplicateData.png"); - private final static String ACTION_NAME = "Duplicate Component"; + private final static ImageIcon ICON = ResourceManager.loadImage("images/DuplicateData.png"); + public final static String ACTION_NAME = "Duplicate Component"; private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; private final static String DESCRIPTION = "Duplicate the selected component"; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.ALT_DOWN_MASK); - private static String[] popupPath = new String[] { ACTION_NAME }; + private final static String[] POPUP_PATH = new String[] { ACTION_NAME }; + private final static KeyStroke KEY_STROKE = + KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.ALT_DOWN_MASK); public DuplicateAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, - duplicateDataIcon); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, + ICON); setDescription(DESCRIPTION); - setKeyBindingData(new KeyBindingData(keyStroke)); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java index 3cc591ccba..4ae608dd00 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DuplicateMultipleAction.java @@ -36,17 +36,17 @@ import resources.ResourceManager; */ public class DuplicateMultipleAction extends CompositeEditorTableAction { - private final static ImageIcon duplicateMultipleIcon = + private final static ImageIcon ICON = ResourceManager.loadImage("images/MultiDuplicateData.png"); - private final static String ACTION_NAME = "Duplicate Multiple of Component"; + public final static String ACTION_NAME = "Duplicate Multiple of Component"; private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; private final static String DESCRIPTION = "Duplicate multiple of the selected component"; + private final static String[] POPUP_PATH = new String[] { ACTION_NAME }; + private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_M, InputEvent.ALT_DOWN_MASK); - private static String[] popupPath = new String[] { ACTION_NAME }; public DuplicateMultipleAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, - duplicateMultipleIcon); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); setDescription(DESCRIPTION); setKeyBindingData(new KeyBindingData(keyStroke)); adjustEnablement(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java index 5146aadc11..f6914843de 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java @@ -33,13 +33,13 @@ import ghidra.util.exception.AssertException; */ public class EditBitFieldAction extends CompositeEditorTableAction { - private final static String ACTION_NAME = "Edit Bitfield"; + public final static String ACTION_NAME = "Edit Bitfield"; private final static String GROUP_NAME = BITFIELD_ACTION_GROUP; private final static String DESCRIPTION = "Edit an existing bitfield"; - private static String[] popupPath = new String[] { ACTION_NAME }; + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; public EditBitFieldAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, null); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, null); setDescription(DESCRIPTION); if (!(model instanceof CompEditorModel)) { throw new AssertException("unsupported use"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java index 8d70302f48..89ede6f036 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditComponentAction.java @@ -26,15 +26,15 @@ import ghidra.program.model.data.Enum; */ public class EditComponentAction extends CompositeEditorTableAction { - private final static String ACTION_NAME = "Edit Component"; + public final static String ACTION_NAME = "Edit Component"; private final static String GROUP_NAME = BASIC_ACTION_GROUP; private final static String DESCRIPTION = "Edit the selected component"; - private static String[] popupPath = new String[] { ACTION_NAME }; - private static String[] menuPath = new String[] { ACTION_NAME }; + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; + private static String[] MENU_PATH = new String[] { ACTION_NAME }; private DataTypeManagerService dtmService; public EditComponentAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, menuPath, null); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null); this.dtmService = provider.dtmService; setDescription(DESCRIPTION); adjustEnablement(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java index a923f8535a..efdab915c9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditFieldAction.java @@ -32,14 +32,14 @@ public class EditFieldAction extends CompositeEditorTableAction { private final static String GROUP_NAME = BASIC_ACTION_GROUP; private final static String DESCRIPTION = "Edit the first editable field of the selected component."; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0); - private static String[] popupPath = new String[] { ACTION_NAME }; - private static String[] menuPath = new String[] { ACTION_NAME }; + private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0); + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; + private static String[] MENU_PATH = new String[] { ACTION_NAME }; public EditFieldAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, menuPath, null); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null); setDescription(DESCRIPTION); - setKeyBindingData(new KeyBindingData(keyStroke)); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java index 10d5223bc0..c4eaba4eaf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/HexNumbersAction.java @@ -28,16 +28,16 @@ import docking.menu.DockingCheckboxMenuItemUI; */ public class HexNumbersAction extends CompositeEditorTableAction implements ToggleDockingActionIf { - private final static String ACTION_NAME = "Show Numbers In Hex"; + public final static String ACTION_NAME = "Show Numbers In Hex"; private final static String GROUP_NAME = DATA_ACTION_GROUP; - private final static String defaultDescription = "Show Numbers in Hexadecimal"; - private static String[] defaultPath = new String[] { defaultDescription }; + private final static String DESCRIPTION = "Show Numbers in Hexadecimal"; + private static String[] PATH = new String[] { DESCRIPTION }; private boolean isSelected; public HexNumbersAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, defaultPath, defaultPath, + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, PATH, PATH, null); - setDescription(defaultDescription); + setDescription(DESCRIPTION); setEnabled(true); setSelected(model.isShowingNumbersInHex()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java index 75d248d859..8915ea41eb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java @@ -15,7 +15,7 @@ */ package ghidra.app.plugin.core.compositeeditor; -import java.awt.Event; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; @@ -33,19 +33,20 @@ import resources.ResourceManager; */ public class InsertUndefinedAction extends CompositeEditorTableAction { - private final static ImageIcon insertUndefinedIcon = + private final static ImageIcon ICON = ResourceManager.loadImage("images/Plus.png"); - private final static String ACTION_NAME = "Insert Undefined Byte"; + public final static String ACTION_NAME = "Insert Undefined Byte"; private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; private final static String DESCRIPTION = "Insert an undefined byte before the selection"; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U, Event.ALT_MASK); - private static String[] popupPath = new String[] { ACTION_NAME }; + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; + + private final static KeyStroke KEY_STROKE = + KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.ALT_DOWN_MASK); public InsertUndefinedAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, - insertUndefinedIcon); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); setDescription(DESCRIPTION); - setKeyBindingData(new KeyBindingData(keyStroke)); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.java index c84eb2b567..8758059816 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveDownAction.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,18 +15,16 @@ */ package ghidra.app.plugin.core.compositeeditor; -import ghidra.util.exception.UsrException; - -import java.awt.Event; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; import javax.swing.KeyStroke; -import resources.ResourceManager; import docking.ActionContext; import docking.action.KeyBindingData; - +import ghidra.util.exception.UsrException; +import resources.ResourceManager; /** * Action for use in the composite data type editor. @@ -35,34 +32,35 @@ import docking.action.KeyBindingData; */ public class MoveDownAction extends CompositeEditorTableAction { - private final static ImageIcon moveDownIcon = ResourceManager.loadImage("images/down.png"); - private final static String ACTION_NAME = "Move Components Down"; - private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; - private final static String DESCRIPTION = "Move the selected components down"; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.ALT_MASK); - private static String[] popupPath = new String[] { ACTION_NAME }; + private final static ImageIcon ICON = ResourceManager.loadImage("images/down.png"); + public final static String ACTION_NAME = "Move Components Down"; + private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; + private final static String DESCRIPTION = "Move the selected components down"; + private final static String[] POPUP_PATH = new String[] { ACTION_NAME }; - public MoveDownAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, moveDownIcon); - setDescription(DESCRIPTION); - setKeyBindingData( new KeyBindingData( keyStroke ) ); + private final static KeyStroke KEY_STROKE = + KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK); + + public MoveDownAction(CompositeEditorProvider provider) { + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); + setDescription(DESCRIPTION); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); - } - - @Override - public void actionPerformed(ActionContext context) { + } + + @Override + public void actionPerformed(ActionContext context) { try { model.moveDown(); - } catch (UsrException e1) { + } + catch (UsrException e1) { model.setStatus(e1.getMessage(), true); } requestTableFocus(); - } - - @Override - public void adjustEnablement() { - setEnabled(model.isMoveDownAllowed()); - } - -} + } + @Override + public void adjustEnablement() { + setEnabled(model.isMoveDownAllowed()); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.java index 0e56e6ad37..928b6e2e76 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/MoveUpAction.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,18 +15,16 @@ */ package ghidra.app.plugin.core.compositeeditor; -import ghidra.util.exception.UsrException; - -import java.awt.Event; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; import javax.swing.KeyStroke; -import resources.ResourceManager; import docking.ActionContext; import docking.action.KeyBindingData; - +import ghidra.util.exception.UsrException; +import resources.ResourceManager; /** * Action for use in the composite data type editor. @@ -35,34 +32,36 @@ import docking.action.KeyBindingData; */ public class MoveUpAction extends CompositeEditorTableAction { - private final static ImageIcon moveUpIcon = ResourceManager.loadImage("images/up.png"); - private final static String ACTION_NAME = "Move Components Up"; - private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; - private final static String DESCRIPTION = "Move selected components up"; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.ALT_MASK); - private static String[] popupPath = new String[] { ACTION_NAME }; + private final static ImageIcon ICON = ResourceManager.loadImage("images/up.png"); + public final static String ACTION_NAME = "Move Components Up"; + private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; + private final static String DESCRIPTION = "Move selected components up"; + private final static String[] POPUP_PATH = new String[] { ACTION_NAME }; - public MoveUpAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, moveUpIcon); - setDescription(DESCRIPTION); - setKeyBindingData( new KeyBindingData( keyStroke ) ); + private final static KeyStroke KEY_STROKE = + KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK); + + public MoveUpAction(CompositeEditorProvider provider) { + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); + setDescription(DESCRIPTION); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); - } - - @Override - public void actionPerformed(ActionContext context) { + } + + @Override + public void actionPerformed(ActionContext context) { try { model.moveUp(); - } catch (UsrException e1) { + } + catch (UsrException e1) { model.setStatus(e1.getMessage(), true); } requestTableFocus(); - } - - @Override - public void adjustEnablement() { - setEnabled(model.isMoveUpAllowed()); - } - -} + } + @Override + public void adjustEnablement() { + setEnabled(model.isMoveUpAllowed()); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.java index d893949757..8016580301 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/PointerAction.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,10 +15,7 @@ */ package ghidra.app.plugin.core.compositeeditor; -import static docking.KeyBindingPrecedence.DefaultLevel; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.PointerDataType; -import ghidra.util.exception.UsrException; +import static docking.KeyBindingPrecedence.*; import java.awt.event.KeyEvent; @@ -27,6 +23,9 @@ import javax.swing.KeyStroke; import docking.ActionContext; import docking.action.KeyBindingData; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.PointerDataType; +import ghidra.util.exception.UsrException; /** * Action for use in the composite data type editor. @@ -34,45 +33,43 @@ import docking.action.KeyBindingData; */ public class PointerAction extends CompositeEditorTableAction { - private final static String ACTION_NAME = "Create Pointer"; - private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; - private final static String DESCRIPTION = "Create a pointer(s) on the selection"; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P, 0); - private static DataType POINTER_DT = new PointerDataType(); + public final static String ACTION_NAME = "Create Pointer"; + private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; + private final static String DESCRIPTION = "Create a pointer(s) on the selection"; + private final static DataType POINTER_DT = new PointerDataType(); + private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_P, 0); + + public PointerAction(CompositeEditorProvider provider) { + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, null, null, null); + setDescription(DESCRIPTION); + setKeyBindingData(new KeyBindingData(KEY_STROKE, DefaultLevel)); + adjustEnablement(); + } - public PointerAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, null, null, null); - setDescription(DESCRIPTION); - setKeyBindingData( new KeyBindingData( keyStroke, DefaultLevel ) ); - adjustEnablement(); - } - @Override - public void actionPerformed(ActionContext context) { + public void actionPerformed(ActionContext context) { try { model.add(POINTER_DT); - } catch (UsrException e1) { + } + catch (UsrException e1) { model.setStatus(e1.getMessage()); } requestTableFocus(); - } - + } + @Override public boolean isEnabledForContext(ActionContext context) { // Do nothing since we always want it enabled so the user gets a "doesn't fit" message. - return model.getRowCount() > 0 - && model.hasSelection() - && model.isContiguousSelection(); + return model.getRowCount() > 0 && model.hasSelection() && model.isContiguousSelection(); } - + @Override - public void adjustEnablement() { + public void adjustEnablement() { // Allow the user to get a "doesn't fit" message on contiguous selection. // Also allow message indicating you must have a selection. boolean hasSelection = model.hasSelection(); - boolean enable = model.getRowCount() > 0 - && (!hasSelection || (hasSelection && model.isContiguousSelection())); - setEnabled(enable); - } + boolean enable = model.getRowCount() > 0 && + (!hasSelection || (hasSelection && model.isContiguousSelection())); + setEnabled(enable); + } } - diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java index b7dce83a33..a21112a682 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowComponentPathAction.java @@ -25,32 +25,19 @@ import ghidra.program.model.data.DataTypeComponent; */ public class ShowComponentPathAction extends CompositeEditorTableAction { - private final static String ACTION_NAME = "Show Component Path"; + public final static String ACTION_NAME = "Show Component Path"; private final static String GROUP_NAME = BASIC_ACTION_GROUP; private final static String DESCRIPTION = "Show the category for the selected component's data type"; - private static String[] popupPath = new String[] { ACTION_NAME }; - private static String[] menuPath = new String[] { ACTION_NAME }; + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; + private static String[] MENU_PATH = new String[] { ACTION_NAME }; - /** - * @param name - * @param group - * @param owner - * @param popupPath - * @param menuPath - * @param icon - * @param useToolbar - * @param checkBox - */ public ShowComponentPathAction(CompositeEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, menuPath, null); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, MENU_PATH, null); setDescription(DESCRIPTION); adjustEnablement(); } - /* (non-Javadoc) - * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) - */ @Override public void actionPerformed(ActionContext context) { String message = " "; @@ -66,9 +53,6 @@ public class ShowComponentPathAction extends CompositeEditorTableAction { requestTableFocus(); } - /* (non-Javadoc) - * @see ghidra.app.plugin.compositeeditor.CompositeEditorAction#adjustEnablement() - */ @Override public void adjustEnablement() { setEnabled(model.isSingleComponentRowSelection()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java index d3b42829e0..eeaed26cde 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnpackageAction.java @@ -35,19 +35,17 @@ import resources.ResourceManager; */ public class UnpackageAction extends CompositeEditorTableAction { - private final static ImageIcon unpackageIcon = - ResourceManager.loadImage("images/Unpackage.gif"); - private final static String ACTION_NAME = "Unpackage Component"; + private final static ImageIcon ICON = ResourceManager.loadImage("images/Unpackage.gif"); + public final static String ACTION_NAME = "Unpackage Component"; private final static String GROUP_NAME = COMPONENT_ACTION_GROUP; private final static String DESCRIPTION = "Replace the selected composite with its components"; - private KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0); - private static String[] popupPath = new String[] { ACTION_NAME }; + private final static KeyStroke KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0); + private static String[] POPUP_PATH = new String[] { ACTION_NAME }; public UnpackageAction(StructureEditorProvider provider) { - super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, - unpackageIcon); + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, POPUP_PATH, null, ICON); setDescription(DESCRIPTION); - setKeyBindingData(new KeyBindingData(keyStroke)); + setKeyBindingData(new KeyBindingData(KEY_STROKE)); adjustEnablement(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java index da5707d7d8..1f6e6ad4ac 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java @@ -167,7 +167,6 @@ public class DataTypeManagerPlugin extends ProgramPlugin tool.setMenuGroup(new String[] { DisassociateAction.MENU_NAME }, "SYNC"); tool.setMenuGroup(new String[] { RECENTLY_OPENED_MENU }, "Recent"); tool.setMenuGroup(new String[] { STANDARD_ARCHIVE_MENU }, "Recent"); - } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java index b669c5486c..72de637ed7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java @@ -22,6 +22,8 @@ import javax.swing.ComboBoxModel; import javax.swing.JPanel; import docking.ComponentProvider; +import docking.actions.SharedDockingActionPlaceholder; +import docking.actions.ToolActions; import docking.widgets.checkbox.GCheckBox; import docking.widgets.combobox.GhidraComboBox; import docking.widgets.label.GLabel; @@ -88,9 +90,8 @@ public class DataTypeEditorManager * @param dt data type to be edited * @return true if this service can invoke an editor for changing the data type. */ - public boolean isEditable(DataType dataType) { - if ((dataType instanceof Enum) || (dataType instanceof Union) || - (dataType instanceof Structure)) { + public boolean isEditable(DataType dt) { + if ((dt instanceof Enum) || (dt instanceof Union) || (dt instanceof Structure)) { return true; } return false; @@ -138,6 +139,34 @@ public class DataTypeEditorManager editorList.add(editor); } + private void installEditorActions() { + + registerAction(ApplyAction.ACTION_NAME); + registerAction(InsertUndefinedAction.ACTION_NAME); + registerAction(MoveUpAction.ACTION_NAME); + registerAction(MoveDownAction.ACTION_NAME); + registerAction(ClearAction.ACTION_NAME); + registerAction(DuplicateAction.ACTION_NAME); + registerAction(DuplicateMultipleAction.ACTION_NAME); + registerAction(DeleteAction.ACTION_NAME); + registerAction(PointerAction.ACTION_NAME); + registerAction(ArrayAction.ACTION_NAME); + registerAction(FindReferencesToField.ACTION_NAME); + registerAction(UnpackageAction.ACTION_NAME); + registerAction(EditComponentAction.ACTION_NAME); + registerAction(EditFieldAction.ACTION_NAME); + registerAction(HexNumbersAction.ACTION_NAME); + registerAction(CreateInternalStructureAction.ACTION_NAME); + registerAction(ShowComponentPathAction.ACTION_NAME); + registerAction(AddBitFieldAction.ACTION_NAME); + registerAction(EditBitFieldAction.ACTION_NAME); + } + + private void registerAction(String name) { + ToolActions toolActions = plugin.getTool().getToolActions(); + toolActions.registerSharedActionPlaceholder(new DtSharedActionPlaceholder(name)); + } + /** * Checks for editor changes that have not been saved to the data type and prompts the user to save * them if necessary. It then closes the editor. @@ -155,7 +184,8 @@ public class DataTypeEditorManager } /** - * Get a list of data type path names for data types that are currently being edited. + * Get a list of data type path names for data types that are currently being edited + * @return a list of data type path names for data types that are currently being edited. */ public List getEditsInProgress() { List paths = new ArrayList<>(); @@ -168,7 +198,7 @@ public class DataTypeEditorManager /** * Get the category for the data type being edited; the data type * may be new and not yet added to the category - * @param dataTypePathname the full path name of the data type that is being + * @param dataTypePath the full path name of the data type that is being * edited if it were written to the category for this editor. * @return category associated with the data type or null. */ @@ -325,9 +355,6 @@ public class DataTypeEditorManager return false; } - /** - * Notifies all editors that a domain object restore has occurred. - */ public void domainObjectRestored(DataTypeManagerDomainObject domainObject) { // Create a copy of the list since restore may remove an editor from the original list. ArrayList list = new ArrayList<>(editorList); @@ -352,7 +379,6 @@ public class DataTypeEditorManager /** * If the specified data type is being edited for the indicated category, this gets that editor. * @param dataType the data type - * @param category the category where the edited data type is to be written (saved). * @return the editor or null. */ public EditorProvider getEditor(DataType dataType) { @@ -373,6 +399,8 @@ public class DataTypeEditorManager private void initialize() { editorList = new ArrayList<>(); editorOptionMgr = new EditorOptionManager(plugin); + + installEditorActions(); } /** @@ -568,7 +596,7 @@ public class DataTypeEditorManager @Override protected void installCallingConventionWidget(JPanel parentPanel) { - callingConventionComboBox = new GhidraComboBox(); + callingConventionComboBox = new GhidraComboBox<>(); GenericCallingConvention[] values = GenericCallingConvention.values(); String[] choices = new String[values.length]; for (int i = 0; i < values.length; i++) { @@ -662,4 +690,24 @@ public class DataTypeEditorManager } } + // small class to register actions by name before the various editors have been shown + private class DtSharedActionPlaceholder implements SharedDockingActionPlaceholder { + + private String name; + + DtSharedActionPlaceholder(String name) { + this.name = CompositeEditorTableAction.EDIT_ACTION_PREFIX + name; + } + + @Override + public String getOwner() { + // all of our shared actions belong to the plugin + return plugin.getName(); + } + + @Override + public String getName() { + return name; + } + } } 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 f79cba063a..23116c835f 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 @@ -103,7 +103,7 @@ public class LocationReferencesPlugin extends Plugin // providers are created, as they would only appear in the options at // that point. // - DeleteTableRowAction.registerDummy(tool); + DeleteTableRowAction.registerDummy(tool, getName()); } void displayProvider(ListingActionContext context) { 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 97087bfe10..31c0f86fae 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 @@ -163,6 +163,6 @@ public class ScalarSearchPlugin extends ProgramPlugin implements DomainObjectLis // providers are created, as they would only appear in the options at // that point. // - DeleteTableRowAction.registerDummy(tool); + DeleteTableRowAction.registerDummy(tool, getName()); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java index 738c40d288..9a20258803 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorProvider.java @@ -89,10 +89,19 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma @Override protected CompositeEditorTableAction[] createActions() { - return new CompositeEditorTableAction[] { new ApplyAction(this), new ClearAction(this), - new DeleteAction(this), new PointerAction(this), new ArrayAction(this), - new ShowComponentPathAction(this), new EditComponentAction(this), - new EditFieldAction(this), new HexNumbersAction(this) }; + //@formatter:off + return new CompositeEditorTableAction[] { + new ApplyAction(this), + new ClearAction(this), + new DeleteAction(this), + new PointerAction(this), + new ArrayAction(this), + new ShowComponentPathAction(this), + new EditComponentAction(this), + new EditFieldAction(this), + new HexNumbersAction(this) + }; + //@formatter:on } /** 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 992b1976ee..5dc7838e38 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 @@ -76,7 +76,7 @@ public class TableServicePlugin extends ProgramPlugin // providers are created, as they would only appear in the options at // that point. // - DeleteTableRowAction.registerDummy(tool); + DeleteTableRowAction.registerDummy(tool, getName()); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java index 830b4e4b1a..189cfa81b5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java @@ -25,7 +25,7 @@ import javax.swing.table.TableModel; import docking.ActionContext; import docking.action.*; -import docking.tool.ToolConstants; +import docking.actions.SharedDockingActionPlaceholder; import docking.widgets.table.GTable; import docking.widgets.table.RowObjectTableModel; import docking.widgets.table.threaded.ThreadedTableModel; @@ -46,7 +46,7 @@ import resources.ResourceManager; * not altering the database. *

* Tip: if you are a plugin that uses transient providers, then use - * {@link #registerDummy(PluginTool)} at creation time to install a dummy representative of + * {@link #registerDummy(PluginTool, String)} at creation time to install a dummy representative of * 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. */ @@ -65,9 +65,10 @@ public class DeleteTableRowAction extends DockingAction { * at the time the plugin is loaded. * * @param tool the tool whose options will updated with a dummy keybinding + * @param owner the owner of the action that may be installed */ - public static void registerDummy(PluginTool tool) { - new DummyDeleteAction(tool); + public static void registerDummy(PluginTool tool, String owner) { + tool.getToolActions().registerSharedActionPlaceholder(new DeleteActionPlaceholder(owner)); } public DeleteTableRowAction(GTable table, String owner) { @@ -192,26 +193,27 @@ public class DeleteTableRowAction extends DockingAction { // Inner Classes //================================================================================================== - private static class DummyDeleteAction extends DeleteTableRowAction { + private static class DeleteActionPlaceholder implements SharedDockingActionPlaceholder { - public DummyDeleteAction(PluginTool tool) { - super(NAME, ToolConstants.TOOL_OWNER, DEFAULT_KEYSTROKE); + private String owner; - // prevent this action from appearing in the toolbar, menus, etc - setToolBarData(null); - setPopupMenuData(null); - - tool.addAction(this); + public DeleteActionPlaceholder(String owner) { + this.owner = owner; } @Override - public void actionPerformed(ActionContext context) { - // stub + public String getName() { + return NAME; } @Override - public boolean isEnabledForContext(ActionContext context) { - return false; // stub + public String getOwner() { + return owner; + } + + @Override + public KeyStroke getKeyBinding() { + return DEFAULT_KEYSTROKE; } } } diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java index e5031119fc..f8eaebce66 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java @@ -28,11 +28,7 @@ import docking.*; import docking.actions.KeyEntryDialog; import docking.actions.ToolActions; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; -import ghidra.app.plugin.core.data.DataPlugin; -import ghidra.app.plugin.core.function.FunctionPlugin; -import ghidra.app.plugin.core.memory.MemoryMapPlugin; import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin; -import ghidra.app.plugin.core.navigation.NavigationHistoryPlugin; import ghidra.framework.plugintool.PluginTool; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; @@ -47,21 +43,13 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest { @Before public void setUp() throws Exception { - env = new TestEnv(); - tool = env.getTool(); - tool.addPlugin(NavigationHistoryPlugin.class.getName()); - tool.addPlugin(CodeBrowserPlugin.class.getName()); - tool.addPlugin(MemoryMapPlugin.class.getName()); - tool.addPlugin(GoToAddressLabelPlugin.class.getName()); - tool.addPlugin(DataPlugin.class.getName()); - tool.addPlugin(FunctionPlugin.class.getName()); - - env.showTool(); + tool = env.launchDefaultTool(); } @After public void tearDown() throws Exception { + close(keyEntryDialog); env.dispose(); } @@ -163,6 +151,26 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(acceleratorKey.getKeyCode(), KeyEvent.VK_G); } + @Test + public void testPlaceholderActionsAppearInDialog() throws Exception { + + DockingAction unboundAction = getUnboundAction(); + showDialog(unboundAction); + + int modifiers = 0; + int keyCode = KeyEvent.VK_DELETE; + triggerActionKey(keyEntryField, modifiers, keyCode); + + String placeholderText = "Remove Items"; + assertTrue("Placeholder action is not registered with the KeyEntryDialog", + collisionPane.getText().contains(placeholderText)); + + // this can be any of the plugins that register this action placeholder + placeholderText = "TableServicePlugin"; + assertTrue("Placeholder action is not registered with the KeyEntryDialog", + collisionPane.getText().contains(placeholderText)); + } + //================================================================================================== // Private methods //================================================================================================== @@ -192,6 +200,7 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest { ToolActions toolActions = tool.getToolActions(); KeyBindingsManager kbm = (KeyBindingsManager) getInstanceField("keyBindingsManager", toolActions); + @SuppressWarnings("unchecked") Map dockingKeyMap = (Map) getInstanceField("dockingKeyMap", kbm); KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java index 75c6d761b5..1435fbc126 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java @@ -39,9 +39,13 @@ import docking.DockingUtils; import docking.action.DockingActionIf; import docking.action.ToggleDockingActionIf; import docking.actions.KeyBindingUtils; +import docking.tool.ToolConstants; +import docking.tool.util.DockingToolConstants; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeNode; import ghidra.app.context.ProgramActionContext; +import ghidra.app.plugin.core.compositeeditor.ApplyAction; +import ghidra.app.plugin.core.compositeeditor.CompositeEditorTableAction; import ghidra.app.plugin.core.datamgr.actions.CreateTypeDefDialog; import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; @@ -50,6 +54,7 @@ import ghidra.app.plugin.core.function.EditFunctionSignatureDialog; import ghidra.app.plugin.core.programtree.ProgramTreePlugin; import ghidra.app.services.ProgramManager; import ghidra.app.util.datatype.DataTypeSelectionEditor; +import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; @@ -57,7 +62,6 @@ import ghidra.program.database.ProgramDB; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.data.*; import ghidra.test.*; -import ghidra.util.Msg; import ghidra.util.classfinder.ClassFilter; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.task.TaskMonitor; @@ -749,9 +753,27 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe assertEquals("Tom", fun.getArguments()[2].getName()); } - //================================================================================================== - // Private methods - //================================================================================================== + @Test + public void testEditorActionsGetRegisteredWithoutEditing() { + + // the owner for the action is the tool, since the registered item is just a placeholder + // because the editor actions are shared actions + String owner = " (" + ToolConstants.SHARED_OWNER + ')'; + String actionName = CompositeEditorTableAction.EDIT_ACTION_PREFIX + ApplyAction.ACTION_NAME; + String optionName = actionName + owner; + ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS); + + String message = "Editor action was not registered before editor was shown"; + assertTrue(message, options.isRegistered(optionName)); + + DockingActionIf action = getAction(tool, ToolConstants.SHARED_OWNER, actionName); + assertNotNull(message, action); + } + +//================================================================================================== +// Private methods +//================================================================================================== + private void editSignature(String name, String newSignature) { expandNode(programNode); GTreeNode child = programNode.getChild(name); @@ -986,7 +1008,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe /** * This directory is bin in eclipse; it will be a resources directory in the classpath when run - * in batch mode. + * in batch mode. The directory is one specifically created by and for this test. * @return class output directory * @throws FileNotFoundException Could not find class output directory */ @@ -1008,12 +1030,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe try { File binDir = getClassesDirectory(); if (binDir.isDirectory()) { - Msg.debug(this, "\tdeleting the bin dir..."); - boolean success = FileUtilities.deleteDir(binDir); - Msg.debug(this, "\tsuccess?: " + success); - } - else { - Msg.debug(this, "NOT a directory - not deleting!"); + FileUtilities.deleteDir(binDir); } } catch (FileNotFoundException e) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java index 29137b33aa..d872ad9828 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java @@ -34,6 +34,7 @@ import docking.action.DockingActionIf; import docking.actions.KeyBindingUtils; import docking.options.editor.OptionsDialog; import docking.options.editor.OptionsPanel; +import docking.tool.ToolConstants; import docking.tool.util.DockingToolConstants; import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.tree.GTree; @@ -322,6 +323,44 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { closeAllWindows(); } + @Test + public void testSharedKeyBindingGetsRestoredWhenToolIsRestarted() throws Exception { + + setKeyBindingsUpDialog(); + + // this action is known to be a 'Shared' action + // Remove Items (Shared) + String actionName = "Remove Items"; + DockingActionIf action = getAction(tool, ToolConstants.SHARED_OWNER, actionName); + assertNotNull(action); + KeyStroke defaultBinding = action.getKeyBinding(); + KeyStroke newBinding = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0); + assertNotEquals(defaultBinding, newBinding); + setKeyBinding(action, "x", newBinding.getKeyCode()); + + KeyStroke appliedBinding = action.getKeyBinding(); + assertEquals(newBinding, appliedBinding); + + // reload the tool and make sure the values are those of the changes get restored + saveAndCloseTool(); + + reopenTool(tool); + + KeyStroke restoredBinding = action.getKeyBinding(); + assertEquals(newBinding, restoredBinding); + + setKeyBindingsUpDialog(tool); + ToolOptions options = (ToolOptions) getInstanceField("options", panel); + KeyStroke optionBinding = options.getKeyStroke(action.getFullName(), null); + assertEquals(appliedBinding, optionBinding); + + closeAllWindows(); + } + +//================================================================================================== +// Private Methods +//================================================================================================== + private void reopenTool(PluginTool tool2) { runSwing(() -> { ToolServices services = tool.getProject().getToolServices(); @@ -438,14 +477,19 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { } assertNotNull("Unable to find an action for which to set a key binding", arbitraryAction); + setKeyBinding(arbitraryAction, keyText, keyCode); + } - selectRowForAction(arbitraryAction); + private void setKeyBinding(DockingActionIf action, String keyText, int keyCode) + throws Exception { + + selectRowForAction(action); triggerText(keyField, keyText); assertEquals(keyText.toUpperCase(), keyField.getText()); runSwing(() -> panel.apply()); - assertEquals(KeyStroke.getKeyStroke(keyCode, 0), arbitraryAction.getKeyBinding()); + assertEquals(KeyStroke.getKeyStroke(keyCode, 0), action.getKeyBinding()); } private void selectRowForAction(DockingActionIf action) throws Exception { @@ -557,11 +601,11 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { private boolean compareOptionsWithKeyStrokeMap(Options oldOptions, Map panelKeyStrokeMap) { List propertyNames = oldOptions.getOptionNames(); - for (String element : propertyNames) { + for (String name : propertyNames) { - boolean match = panelKeyStrokeMap.containsKey(element); - KeyStroke optionsKs = oldOptions.getKeyStroke(element, null); - KeyStroke panelKs = panelKeyStrokeMap.get(element); + boolean match = panelKeyStrokeMap.containsKey(name); + KeyStroke optionsKs = oldOptions.getKeyStroke(name, null); + KeyStroke panelKs = panelKeyStrokeMap.get(name); // if the value is null, then it would not have been placed into the options map // in the key bindings panel, so we only care about non-null values @@ -583,8 +627,6 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { private void assertOptionsMatch(String message, ToolOptions options1, ToolOptions options2) { -// System.out.println("assertOptionsMatch()"); - List propertyNames = getOptionsNamesWithValues(options1); List otherPropertyNames = getOptionsNamesWithValues(options2); @@ -610,8 +652,6 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest { private void assertOptionsDontMatch(String message, ToolOptions options1, ToolOptions options2) { -// System.out.println("assertOptionsDontMatch()"); - List propertyNames = getOptionsNamesWithValues(options1); List otherPropertyNames = getOptionsNamesWithValues(options2); if (propertyNames.size() != otherPropertyNames.size()) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java index 72b6afd794..030ca16eb6 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyEntryDialog.java @@ -119,16 +119,19 @@ public class KeyEntryDialog extends DialogComponentProvider { } private JPanel createCollisionPanel() { - JPanel p = new JPanel(new BorderLayout()); + JPanel parent = new JPanel(new BorderLayout()); + + JPanel noWrapPanel = new JPanel(new BorderLayout()); collisionPane = new JTextPane(); collisionPane.setEditable(false); collisionPane.setBackground(bgColor); doc = collisionPane.getStyledDocument(); - JScrollPane sp = new JScrollPane(collisionPane); + noWrapPanel.add(collisionPane, BorderLayout.CENTER); + JScrollPane sp = new JScrollPane(noWrapPanel); Dimension d = defaultPanel.getPreferredSize(); sp.setPreferredSize(new Dimension(sp.getPreferredSize().width, d.height)); - p.add(sp, BorderLayout.CENTER); - return p; + parent.add(sp, BorderLayout.CENTER); + return parent; } /** @@ -194,13 +197,19 @@ public class KeyEntryDialog extends DialogComponentProvider { return; } + list.sort((a1, a2) -> { + String s1 = a1.getName() + a1.getOwnerDescription(); + String s2 = a2.getName() + a2.getOwnerDescription(); + return s1.compareToIgnoreCase(s2); + }); + String ksName = KeyBindingUtils.parseKeyStroke(ks); try { doc.insertString(0, "Actions mapped to " + ksName + "\n\n", textAttrSet); for (int i = 0; i < list.size(); i++) { DockingActionIf a = list.get(i); - String collisionStr = "\t" + a.getName() + " (" + a.getOwnerDescription() + ")\n"; + String collisionStr = "\t" + a.getName() + " (" + a.getOwnerDescription() + ")\n"; int offset = doc.getLength(); doc.insertString(offset, collisionStr, textAttrSet); doc.setParagraphAttributes(offset, 1, tabAttrSet, false); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedActionRegistry.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedActionRegistry.java index 0e6920a6f0..1d8670a44a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedActionRegistry.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedActionRegistry.java @@ -34,6 +34,6 @@ public class SharedActionRegistry { * @param toolActions the tool action manager */ public static void installSharedActions(DockingTool tool, ToolActions toolActions) { - GTable.createSharedActions(tool, toolActions, ToolConstants.TOOL_OWNER); + GTable.createSharedActions(tool, toolActions, ToolConstants.SHARED_OWNER); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedDockingActionPlaceholder.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedDockingActionPlaceholder.java new file mode 100644 index 0000000000..23821ce9da --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedDockingActionPlaceholder.java @@ -0,0 +1,64 @@ +/* ### + * 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 javax.swing.KeyStroke; + +import docking.tool.ToolConstants; + +/** + * A marker interface to signal that the implementing action serves as an action that should + * not be itself used in the tool, but should only be used to register and manager keybindings. + * + * + *

This action is merely a tool by which transient components can ensure that their actions + * are correctly managed when the component is created. Normal actions will get registered when + * the tool first starts-up. Alternatively, transient components only appear when called upon + * by some event, such as a user request. The issue heretofore was that the tool will remove + * any options that are not longer used. Thus, if an action belonging to a transient component + * does not get registered every time the tool is used, then the options (and key bindings) for + * that action are removed from the too. This interface allows a second-party to register + * an action on behalf of a transient provider, thus preventing the tool from removing any + * previously applied options. + */ +public interface SharedDockingActionPlaceholder { + + /** + * The action name. This name must exactly match the name of the action represented by + * this placeholder. + * @return the name + */ + public String getName(); + + /** + * Returns an owner name to use in place of {@value ToolConstants#SHARED_OWNER}. + * This should only be used when the client knows for certain that all shared actions are + * shared by a single owner. This is not typical for shared actions. This can happen when one + * owner (such as a plugin) has multiple component providers that share action key bindings. + * @return the owner + */ + public default String getOwner() { + return ToolConstants.SHARED_OWNER; + } + + /** + * The default key binding for the action represented by this placeholder + * @return the key binding; may be null + */ + public default KeyStroke getKeyBinding() { + return null; + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedStubKeyBindingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedStubKeyBindingAction.java index 7fa201c994..bf0c5e487a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedStubKeyBindingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/SharedStubKeyBindingAction.java @@ -20,6 +20,8 @@ import java.util.Map.Entry; import javax.swing.KeyStroke; +import org.apache.commons.collections4.Bag; +import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.lang3.StringUtils; import docking.ActionContext; @@ -34,11 +36,25 @@ import ghidra.framework.options.ToolOptions; * allows plugins to create actions that share keybindings without having to manage those * keybindings themselves. * + *

Some ways this class is used: + *

    + *
  1. As a central action to manage key bindings for multiple actions from different clients + * (plugins) that are conceptually the same. When the plugins are loaded + * these actions get registered and are wired to listen to key binding changes to this stub. + *
  2. + *
  3. As a placeholder action to manage key bindings for actions that have not yet been + * registered and may not get registered during the lifetime of a single tool session. + * This can happen when a plugin has transient component providers that only get shown + * upon a user request. This stub allows the key binding for those actions to be managed, + * even if they do not get registered when the tool is shown. + *
  4. + *
+ * *

Clients should not be using this class directly. */ public class SharedStubKeyBindingAction extends DockingAction implements OptionsChangeListener { - static final String SHARED_OWNER = ToolConstants.TOOL_OWNER; + static final String SHARED_OWNER = ToolConstants.SHARED_OWNER; /** * We save the client actions for later validate and options updating. We also need the @@ -50,27 +66,54 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options private WeakHashMap clientActions = new WeakHashMap<>(); private ToolOptions keyBindingOptions; + private Bag actionOwners = new HashBag(); /** * 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 defaultKs the default key stroke for this stub. The key stroke will be validated + * each time an action is added to this stub to ensure that the defaults are in sync. * @param options the tool's key binding options */ - SharedStubKeyBindingAction(String name, ToolOptions options) { - super(name, SHARED_OWNER); + SharedStubKeyBindingAction(String name, KeyStroke defaultKs, ToolOptions options) { + // Note: we need to have this stub registered to use key bindings so that the options will + // restore the saved key binding to this class, which will then notify any of the + // shared actions using this stub. + super(name, SHARED_OWNER, KeyBindingType.INDIVIDUAL); this.keyBindingOptions = options; // Dummy keybinding actions don't have help--the real action does DockingWindowManager.getHelpService().excludeFromHelp(this); + setUnvalidatedKeyBindingData(new KeyBindingData(defaultKs)); + // A listener to keep the shared, stub keybindings in sync with their clients options.addOptionsChangeListener(this); } + /** + * Adds the given owner name to this stub. This is used to display all known clients of + * the action represented by this stub. Normally, when this class has actions, the names + * of each action's owner would be used directly. However, this class can also be used as + * a placeholder, when no actions have yet been registered. In that case, the owner has + * to be set directly on this stub. + * + * @param owner the name of the client that owns the actions that may get registered with + * this stub + */ + void addActionOwner(String owner) { + if (DockingWindowManager.DOCKING_WINDOWS_OWNER.equals(owner)) { + // Special case: special system-level action owner; the user does not need to see + return; + } + actionOwners.add(owner); + } + void removeClientAction(DockingActionIf action) { clientActions.remove(action); + actionOwners.remove(action.getOwner()); } void addClientAction(DockingActionIf action) { @@ -88,44 +131,20 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options @Override public String getOwnerDescription() { - List owners = getDistinctOwners(); + List owners = new LinkedList<>(actionOwners.uniqueSet()); if (owners.size() == 1) { return owners.get(0); } - boolean hasTool = owners.remove(ToolConstants.TOOL_OWNER); + boolean hasTool = owners.remove(SHARED_OWNER); Collections.sort(owners); if (hasTool) { - owners.add(0, ToolConstants.TOOL_OWNER); + owners.add(0, SHARED_OWNER); } return StringUtils.join(owners, ", "); } - private List getDistinctOwners() { - List results = new ArrayList<>(); - Set actions = clientActions.keySet(); - for (DockingActionIf action : actions) { - String owner = action.getOwner(); - if (DockingWindowManager.DOCKING_WINDOWS_OWNER.equals(owner)) { - // special case: this is the owner for special system-level actions - continue; - } - - if (!results.contains(owner)) { - results.add(owner); - } - } - - if (results.isEmpty()) { - // This implies we have an action owned by the DockingWindowManager - // (the DOCKING_WINDOWS_OWNER). In this case, use the Tool as the owner. - results.add(SHARED_OWNER); - } - - return results; - } - private KeyStroke validateActionsHaveTheSameDefaultKeyStroke(DockingActionIf newAction) { // this value may be null diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java index 7db8dabd49..6eed8e58cd 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java @@ -18,6 +18,7 @@ package docking.actions; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; +import java.util.Map.Entry; import javax.swing.Action; import javax.swing.KeyStroke; @@ -142,6 +143,13 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { } KeyStroke ks = action.getKeyBinding(); + loadKeyBindingFromOptions(action, ks); + + keyBindingsManager.addAction(provider, action); + } + + private void loadKeyBindingFromOptions(DockingActionIf action, KeyStroke ks) { + String description = "Keybinding for " + action.getFullName(); keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, null, description); @@ -149,8 +157,6 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { if (!Objects.equals(ks, newKs)) { action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); } - - keyBindingsManager.addAction(provider, action); } private void installSharedKeyBinding(ComponentProvider provider, DockingActionIf action) { @@ -161,11 +167,13 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { SharedStubKeyBindingAction stub = sharedActionMap.computeIfAbsent(name, key -> { SharedStubKeyBindingAction newStub = - new SharedStubKeyBindingAction(name, keyBindingOptions); + new SharedStubKeyBindingAction(name, defaultKeyStroke, keyBindingOptions); registerStub(newStub, defaultKeyStroke); return newStub; }); + String owner = action.getOwner(); + stub.addActionOwner(owner); stub.addClientAction(action); if (!(action instanceof AutoGeneratedDockingAction)) { @@ -176,16 +184,12 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { private void registerStub(SharedStubKeyBindingAction stub, KeyStroke defaultKeyStroke) { stub.addPropertyChangeListener(this); - String description = "Keybinding for Stub action: " + stub.getFullName(); - keyBindingOptions.registerOption(stub.getFullName(), OptionType.KEYSTROKE_TYPE, - defaultKeyStroke, null, description); + + loadKeyBindingFromOptions(stub, defaultKeyStroke); + keyBindingsManager.addAction(null, stub); } - /** - * Removes the given action from the tool - * @param action the action to be removed. - */ @Override public synchronized void removeGlobalAction(DockingActionIf action) { action.removePropertyChangeListener(this); @@ -230,12 +234,6 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { } } - /** - * 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 - */ @Override public synchronized Set getActions(String owner) { @@ -245,18 +243,18 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { result.addAll(actions); } - if (SharedStubKeyBindingAction.SHARED_OWNER.equals(owner)) { - result.addAll(sharedActionMap.values()); + Set> entries = sharedActionMap.entrySet(); + for (Entry entry : entries) { + SharedStubKeyBindingAction stub = entry.getValue(); + String stubOwner = stub.getOwner(); + if (stubOwner.equals(owner)) { + result.add(stub); + } } return result; } - /** - * Get a set of all actions in the tool - * - * @return a new set of the existing actions - */ @Override public synchronized Set getAllActions() { @@ -418,4 +416,31 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { return sharedActionMap.get(name); } + /** + * Allows clients to register an action by using a placeholder. This is useful when + * an API wishes to have a central object (like a plugin) register actions for transient + * providers, that may not be loaded until needed. + * + *

This method may be called multiple times with the same conceptual placeholder--the + * placeholder will only be added once. + * + * @param placeholder the placeholder containing information related to the action it represents + */ + public void registerSharedActionPlaceholder(SharedDockingActionPlaceholder placeholder) { + + String name = placeholder.getName(); + KeyStroke defaultKeyStroke = placeholder.getKeyBinding(); + + SharedStubKeyBindingAction stub = sharedActionMap.computeIfAbsent(name, key -> { + + SharedStubKeyBindingAction newStub = + new SharedStubKeyBindingAction(name, defaultKeyStroke, keyBindingOptions); + registerStub(newStub, defaultKeyStroke); + return newStub; + }); + + String owner = placeholder.getOwner(); + stub.addActionOwner(owner); + } + } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/tool/ToolConstants.java b/Ghidra/Framework/Docking/src/main/java/docking/tool/ToolConstants.java index f093ef7aba..cfe4672c2f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/tool/ToolConstants.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/tool/ToolConstants.java @@ -15,6 +15,7 @@ */ package docking.tool; +import docking.action.KeyBindingType; import docking.tool.util.DockingToolConstants; /** @@ -93,6 +94,13 @@ public interface ToolConstants extends DockingToolConstants { */ public static final String TOOL_OWNER = "Tool"; + /** + * This is used when many actions wish to share a key binding. + * + * @see KeyBindingType#SHARED + */ + public static final String SHARED_OWNER = "Shared"; + /** * Name of options for a tool */ diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ToolOptions.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ToolOptions.java index 6d53fa321d..46661215b7 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ToolOptions.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/ToolOptions.java @@ -19,6 +19,7 @@ import java.awt.Color; import java.awt.Font; import java.beans.PropertyEditor; import java.io.File; +import java.lang.reflect.Constructor; import java.util.*; import javax.swing.KeyStroke; @@ -98,9 +99,9 @@ public class ToolOptions extends AbstractOptions { try { Element elem = (Element) iter.next(); String optionName = elem.getAttributeValue("NAME"); - Class c = Class.forName(elem.getAttributeValue("CLASS")); - WrappedOption wo = (WrappedOption) c.newInstance(); + Constructor constructor = c.getDeclaredConstructor(); + WrappedOption wo = (WrappedOption) constructor.newInstance(); wo.readState(new SaveState(elem)); Option option = createUnregisteredOption(optionName, wo.getOptionType(), null); option.doSetCurrentValue(wo.getObject());// use doSet versus set so that it is not registered