diff --git a/Ghidra/Features/Base/ghidra_scripts/DeleteFunctionDefaultPlates.java b/Ghidra/Features/Base/ghidra_scripts/DeleteFunctionDefaultPlatesScript.java similarity index 86% rename from Ghidra/Features/Base/ghidra_scripts/DeleteFunctionDefaultPlates.java rename to Ghidra/Features/Base/ghidra_scripts/DeleteFunctionDefaultPlatesScript.java index 70d29dd513..bdd851f67a 100644 --- a/Ghidra/Features/Base/ghidra_scripts/DeleteFunctionDefaultPlates.java +++ b/Ghidra/Features/Base/ghidra_scripts/DeleteFunctionDefaultPlatesScript.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. @@ -23,20 +22,18 @@ import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.FunctionIterator; -public class DeleteFunctionDefaultPlates extends GhidraScript { +public class DeleteFunctionDefaultPlatesScript extends GhidraScript { private static String DEFAULT_PLATE = " FUNCTION"; - /* (non-Javadoc) - * @see ghidra.app.script.GhidraScript#run() - */ + @Override - public void run() throws Exception { + public void run() throws Exception { AddressSetView set = currentProgram.getMemory(); if (currentSelection != null && !currentSelection.isEmpty()) { set = currentSelection; } - int updateCount=0; + int updateCount = 0; FunctionIterator iter = currentProgram.getFunctionManager().getFunctions(set, true); while (iter.hasNext()) { Function function = iter.next(); @@ -47,7 +44,7 @@ public class DeleteFunctionDefaultPlates extends GhidraScript { } } if (updateCount > 0) { - String cmt = updateCount > 1? "comments" : "comment"; + String cmt = updateCount > 1 ? "comments" : "comment"; println("Removed " + updateCount + " default plate " + cmt + "."); } else { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java index 57b6ea4eed..fd3574bc5a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreePlugin.java @@ -148,6 +148,14 @@ public class CallTreePlugin extends ProgramPlugin { tool.showComponentProvider(provider, true); } + CallTreeProvider getPrimaryProvider() { + return primaryProvider; + } + + DockingAction getShowCallTreeFromMenuAction() { + return showCallTreeFromMenuAction; + } + ProgramLocation getCurrentLocation() { return currentLocation; } 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 f61684e6e8..f66e9e4a8c 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 @@ -876,6 +876,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain // changes, which means we will get here while setting the location, but our program // will have been null'ed out. currentProgram = plugin.getCurrentProgram(); + currentProgram.addListener(this); } Function function = plugin.getFunction(location); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java index 084d6ec6e4..0d5259ff24 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java @@ -915,18 +915,13 @@ public class CodeBrowserPlugin extends Plugin } }; + // note: this action gets added later when the TableService is added tableFromSelectionAction.setEnabled(false); tableFromSelectionAction.setMenuBarData(new MenuData( new String[] { ToolConstants.MENU_SELECTION, "Create Table From Selection" }, null, "SelectUtils")); - tableFromSelectionAction - .setHelpLocation(new HelpLocation("CodeBrowserPlugin", "Selection_Table")); - - // don't add the actions initially if the service isn't there - TableService tableService = tool.getService(TableService.class); - if (tableService != null) { - tool.addAction(tableFromSelectionAction); - } + tableFromSelectionAction.setHelpLocation( + new HelpLocation("CodeBrowserPlugin", "Selection_Table")); } private GhidraProgramTableModel
createTableModel(CodeUnitIterator iterator, @@ -1001,8 +996,8 @@ public class CodeBrowserPlugin extends Plugin public boolean goToField(Address a, String fieldName, int occurrence, int row, int col, boolean scroll) { - boolean result = SystemUtilities - .runSwingNow(() -> doGoToField(a, fieldName, occurrence, row, col, scroll)); + boolean result = SystemUtilities.runSwingNow( + () -> doGoToField(a, fieldName, occurrence, row, col, scroll)); return result; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java index bee0678a85..430302b29a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java @@ -36,7 +36,6 @@ import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.HoverHandler; import docking.widgets.fieldpanel.internal.FieldPanelCoordinator; import docking.widgets.fieldpanel.support.*; -import ghidra.GhidraOptions; import ghidra.app.nav.*; import ghidra.app.plugin.core.clipboard.CodeBrowserClipboardProvider; import ghidra.app.plugin.core.codebrowser.actions.*; @@ -63,8 +62,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter private static final String TITLE = "Listing: "; - private static final String OPERAND_OPTIONS_PREFIX = GhidraOptions.OPERAND_GROUP_TITLE + "."; - private static final Icon LISTING_FORMAT_EXPAND_ICON = ResourceManager.loadImage("images/field.header.down.png"); private static final Icon LISTING_FORMAT_COLLAPSE_ICON = @@ -93,9 +90,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter private ProgramDropProvider curDropProvider; private ToggleDockingAction toggleHoverAction; - //TODO - Need to improve CodeUnit.getRepresentation format options interface before adding this - //TODO private ToggleOperandMarkupAction[] toggleOperandMarkupActions; - private ProgramLocation currentLocation; private ListingPanel otherPanel; @@ -976,9 +970,9 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter } } - class ToggleHoverAction extends ToggleDockingAction { + private class ToggleHoverAction extends ToggleDockingAction { ToggleHoverAction() { - super("Toggle Mouse Hover Popups", CodeViewerProvider.this.getName()); + super("Toggle Mouse Hover Popups", CodeViewerProvider.this.getOwner()); setEnabled(true); setToolBarData(new ToolBarData(HOVER_ON_ICON, "yyyz")); setSelected(true); @@ -998,31 +992,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter } } - class ToggleOperandMarkupAction extends ToggleDockingAction { - final String optionName; - - ToggleOperandMarkupAction(String operandOption) { - super(operandOption, CodeViewerProvider.this.getName()); - this.optionName = OPERAND_OPTIONS_PREFIX + operandOption; - boolean state = - tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS).getBoolean(optionName, true); - setMenuBarData(new MenuData(new String[] { operandOption })); - setSelected(state); - setEnabled(true); - setHelpLocation(new HelpLocation("CodeBrowserPlugin", "Operands_Field")); - } - - public Object getOptionName() { - return optionName; - } - - @Override - public void actionPerformed(ActionContext context) { - boolean state = isSelected(); - tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS).setBoolean(optionName, state); - } - } - /** * A class that allows clients to install transient highlighters while keeping the * middle-mouse highlighting on at the same time. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/CollapseAllDataAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/CollapseAllDataAction.java index aef5cd1983..b9e6e82b17 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/CollapseAllDataAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/CollapseAllDataAction.java @@ -39,7 +39,7 @@ public class CollapseAllDataAction extends ProgramLocationContextAction { private CodeViewerProvider provider; public CollapseAllDataAction(CodeViewerProvider provider) { - super("Collapse All Data", provider.getName()); + super("Collapse All Data", provider.getOwner()); this.provider = provider; setPopupMenuData(new MenuData(new String[] { "Collapse All Data" }, null, "Structure")); @@ -95,8 +95,7 @@ public class CollapseAllDataAction extends ProgramLocationContextAction { ProgramLocation location = context.getLocation(); Data data = getTopLevelComponentData(location); - TaskLauncher.launchModal("Collapse Data", - monitor -> model.closeAllData(data, monitor)); + TaskLauncher.launchModal("Collapse Data", monitor -> model.closeAllData(data, monitor)); } private ListingModel getModel() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ExpandAllDataAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ExpandAllDataAction.java index 3983a5138a..47e02c433e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ExpandAllDataAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ExpandAllDataAction.java @@ -36,7 +36,7 @@ public class ExpandAllDataAction extends ProgramLocationContextAction { private CodeViewerProvider provider; public ExpandAllDataAction(CodeViewerProvider provider) { - super("Expand All Data", provider.getName()); + super("Expand All Data", provider.getOwner()); this.provider = provider; setPopupMenuData(new MenuData(new String[] { "Expand All Data" }, null, "Structure")); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ToggleExpandCollapseDataAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ToggleExpandCollapseDataAction.java index 537455f59c..93ffe7558b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ToggleExpandCollapseDataAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/actions/ToggleExpandCollapseDataAction.java @@ -39,7 +39,7 @@ public class ToggleExpandCollapseDataAction extends ProgramLocationContextAction private CodeViewerProvider provider; public ToggleExpandCollapseDataAction(CodeViewerProvider provider) { - super("Toggle Expand/Collapse Data", provider.getName()); + super("Toggle Expand/Collapse Data", provider.getOwner()); this.provider = provider; setPopupMenuData( 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 a04d236cda..de3105d085 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 @@ -466,7 +466,6 @@ class GhidraScriptActionManager { if (action == null) { action = new ScriptAction(plugin, script); actionMap.put(script, action); - plugin.getTool().addAction(action); } return action; } 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 41be28e348..e6f57bbfd8 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 @@ -347,7 +347,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener { private void createSymActions() { String popupGroup = "1"; - openRefsAction = new DockingAction("Symbol References", getName()) { + openRefsAction = new DockingAction("Symbol References", getName(), KeyBindingType.SHARED) { @Override public void actionPerformed(ActionContext context) { refProvider.open(); @@ -359,7 +359,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener { new MenuData(new String[] { "Symbol References" }, icon, popupGroup)); openRefsAction.setToolBarData(new ToolBarData(icon)); - openRefsAction.setDescription("Symbol References"); + openRefsAction.setDescription("Display Symbol References"); tool.addLocalAction(symProvider, openRefsAction); deleteAction = new DockingAction("Delete Symbols", getName()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java index fb77119627..98fbd60360 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/PluginConstants.java @@ -62,7 +62,7 @@ public interface PluginConstants { */ char ANYSINGLECHAR_WILDCARD_CHAR = '?'; - String CODE_BROWSER = "CodeBrowserPlugin"; + String CODE_BROWSER = "Listing"; String MEMORY_MAP = "Memory Map"; String BYTE_VIEWER = "ByteViewerPlugin"; String BOOKMARKS = "Bookmarks"; diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java index 4878d33f0a..e2a716518c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java @@ -159,6 +159,25 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio assertMenuItemHasKeyStroke(newKs); } + @Test + public void testSetKeyBinding_ViaDialog_FromWindowMenu_ToAlreadyBoundAction() { + + // + // Test the conflicting key binding use case + // + + HasDefaultKeyBindingComponentProvider otherProvider = + new HasDefaultKeyBindingComponentProvider(tool); + otherProvider.setVisible(true); + + showProvider(); + + KeyStroke newKs = CONTROL_T; + applyBindingToDialog_FromWindowsMenu(newKs); + + assertCollisionsWithKeyStroke(); + } + @Test public void testSetKeyBinding_ViaOptions_WithoutToolbarAction() { @@ -235,6 +254,14 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio } } + @Test + public void testDefaultKeyBindingAppearsInWindowMenu() { + + provider.setDefaultKeyBinding(new KeyBindingData(CONTROL_T)); + showProvider(); + assertWindowMenuActionHasKeyBinding(CONTROL_T); + } + //================================================================================================== // Private Methods //================================================================================================== @@ -279,7 +306,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio .stream() .filter(a -> a.getOwner().equals(DockingWindowManager.DOCKING_WINDOWS_OWNER)) .findFirst() - .get() + .orElseGet(() -> null) ; //@formatter:on }); @@ -310,7 +337,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio } private void assertNoToolbarAction() { - assertNotNull("No toolbar action found for provider", getToolbarShowProviderAction()); + assertNull("No toolbar action found for provider", getToolbarShowProviderAction()); } private void assertToolbarAction() { @@ -341,6 +368,13 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio expected, action.getMenuBarData().getMenuIcon()); } + private void assertWindowMenuActionHasKeyBinding(KeyStroke ks) { + DockingActionIf action = getWindowMenuShowProviderAction(); + assertEquals( + "Windows menu key bindings for provider does not match the value set on the provider", + ks, action.getKeyBinding()); + } + private void assertCannotShowKeyBindingDialog_FromWindowsMenu() { // simulate the user mousing over the 'Window' menu's action DockingActionIf windowMenuAction = getWindowMenuShowProviderAction(); @@ -367,6 +401,24 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio assertFalse("Invalid key stroke: " + ks, runSwing(() -> dialog.isVisible())); } + private void applyBindingToDialog_FromWindowsMenu(KeyStroke ks) { + DockingActionIf windowMenuAction = getWindowMenuShowProviderAction(); + DockingWindowManager.setMouseOverAction(windowMenuAction); + + performLaunchKeyStrokeDialogAction(); + KeyEntryDialog dialog = waitForDialogComponent(KeyEntryDialog.class); + + runSwing(() -> dialog.setKeyStroke(ks)); + } + + private void assertCollisionsWithKeyStroke() { + KeyEntryDialog dialog = waitForDialogComponent(KeyEntryDialog.class); + + JTextPane collisionPane = (JTextPane) getInstanceField("collisionPane", dialog); + String collisionText = runSwing(() -> collisionPane.getText()); + assertTrue(collisionText.contains("Actions mapped to")); + } + private void assertMenuItemHasKeyStroke(KeyStroke expected) { DockingActionIf action = getWindowMenuShowProviderAction(); @@ -415,6 +467,21 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio public JComponent getComponent() { return component; } + } + private class HasDefaultKeyBindingComponentProvider extends ComponentProvider { + private JComponent component = new JTextField("Hey!"); + + HasDefaultKeyBindingComponentProvider(DockingTool tool) { + super(tool, HasDefaultKeyBindingComponentProvider.class.getSimpleName(), + "Fooberry Plugin"); + + setDefaultKeyBinding(new KeyBindingData(CONTROL_T)); + } + + @Override + public JComponent getComponent() { + return component; + } } } 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 d2014732bb..e5031119fc 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 @@ -18,6 +18,7 @@ package docking.action; import static org.junit.Assert.*; import java.awt.event.KeyEvent; +import java.util.Map; import javax.swing.*; @@ -25,6 +26,7 @@ import org.junit.*; 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; @@ -186,20 +188,26 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest { } public DockingAction getKeyBindingAction() { - DockingWindowManager dwm = DockingWindowManager.getInstance(tool.getToolFrame()); - ActionToGuiMapper dockingActionManager = - (ActionToGuiMapper) getInstanceField("actionManager", dwm); - return (DockingAction) getInstanceField("keyBindingsAction", dockingActionManager); + + ToolActions toolActions = tool.getToolActions(); + KeyBindingsManager kbm = + (KeyBindingsManager) getInstanceField("keyBindingsManager", toolActions); + Map
* Components are added via ComponentProviders. A ComponentProvider is an interface for getting
* a component and its related information. The docking window manager will get the component
* from the provider as needed. It is up to the provider if it wants to reuse the component or
- * recreate a new one when the component is requested. When the user "hides" (by using
- * the x button on the component area) a component, the docking window manager removes all
+ * recreate a new one when the component is requested. When the user hides a component (by using
+ * the x button on the component header), the docking window manager removes all
* knowledge of the component and will request it again from the provider if the component
- * becomes "unhidden". The provider is also notified whenever a component is hidden. Some
- * providers will use the notification to remove the provider from the docking window manager so
- * that the can not "unhide" the component using the built-in window menu.
+ * is again shown. The provider is also notified whenever a component is hidden and shown.
*/
public class DockingWindowManager implements PropertyChangeListener, PlaceholderInstaller {
@@ -712,8 +710,11 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
if (placeholder != null) {
showComponent(placeholder, visibleState, true);
+ return;
}
- else {
+
+ if (visibleState) {
+
// a null placeholder implies the client is trying to show a provider that has not
// been added to the tool
Msg.warn(this, "Attempting to show an unknown Component Provider '" +
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java b/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java
index 5fb3afc650..569a19691a 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java
@@ -76,11 +76,7 @@ class ShowComponentAction extends DockingAction implements Comparable