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 eef801df44..128347a898 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java @@ -43,7 +43,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio private final Icon ICON = ResourceManager.loadImage("images/refresh.png"); private static final String PROVIDER_NAME = "Test Action Provider"; private static final KeyStroke CONTROL_T = - KeyStroke.getKeyStroke(Character.valueOf('t'), DockingUtils.CONTROL_KEY_MODIFIER_MASK); + KeyStroke.getKeyStroke(Character.valueOf('T'), DockingUtils.CONTROL_KEY_MODIFIER_MASK); private TestEnv env; private PluginTool tool; @@ -160,6 +160,19 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio assertMenuItemHasKeyStroke(newKs); } + @Test + public void testSetKeyBinding_ViaDialog_FromWindowMenu_ThenFireKeyEventToShowProvider() { + + showProvider(); + + KeyStroke newKs = CONTROL_T; + setKeyBindingViaF4Dialog_FromWindowsMenu(newKs); + + hideProvider(); + pressKey(CONTROL_T); + assertProviderIsActive(); + } + @Test public void testSetKeyBinding_ViaDialog_FromWindowMenu_ToAlreadyBoundAction() { @@ -326,6 +339,11 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio waitForSwing(); } + private void hideProvider() { + tool.showComponentProvider(provider, false); + waitForSwing(); + } + private void setDefaultKeyBinding(KeyStroke defaultKs) { runSwing(() -> provider.setKeyBinding(new KeyBindingData(defaultKs))); } @@ -341,6 +359,14 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio }); } + private void pressKey(KeyStroke ks) { + int modifiers = ks.getModifiers(); + char keyChar = ks.getKeyChar(); + int keyCode = ks.getKeyCode(); + JFrame toolFrame = tool.getToolFrame(); + triggerKey(toolFrame, modifiers, keyCode, keyChar); + } + private DockingActionIf getShowProviderAction() { DockingActionIf showProviderAction = @@ -389,6 +415,11 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio waitForSwing(); } + private void assertProviderIsActive() { + assertTrue("The test provider is not showing and focused", + runSwing(() -> tool.isActive(provider))); + } + private void assertNoToolbarAction() { assertNull("No toolbar action found for provider", getToolbarShowProviderAction()); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/ProviderNavigationPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/ProviderNavigationPluginTest.java index f5a6024964..ae2ad5ef24 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/ProviderNavigationPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/ProviderNavigationPluginTest.java @@ -29,7 +29,6 @@ import docking.action.DockingActionIf; import ghidra.program.database.ProgramBuilder; import ghidra.program.model.listing.Program; import ghidra.test.AbstractProgramBasedTest; -import ghidra.util.Msg; import ghidra.util.datastruct.WeakSet; public class ProviderNavigationPluginTest extends AbstractProgramBasedTest { @@ -165,9 +164,6 @@ public class ProviderNavigationPluginTest extends AbstractProgramBasedTest { @Override public void accept(ComponentProvider c) { - - Msg.out("Spy - activated: " + c); - lastActivated = c; forceActivate(c); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ActionDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/ActionDialog.java index b1ca6bc9f4..e1ee05410f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ActionDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ActionDialog.java @@ -80,7 +80,7 @@ public class ActionDialog extends DialogComponentProvider { for (int i = 0; i < list.size(); i++) { ExecutableKeyActionAdapter actionProxy = list.get(i); DockingActionIf action = actionProxy.getAction(); - listModel.addElement(action.getName()); + listModel.addElement(action.getName() + " (" + action.getOwnerDescription() + ")"); } actionList.setSelectedIndex(0); } @@ -144,7 +144,7 @@ public class ActionDialog extends DialogComponentProvider { @Override public void mouseClicked(MouseEvent e) { - if (e.getModifiers() != InputEvent.BUTTON1_MASK) { + if (e.getModifiersEx() != InputEvent.BUTTON1_DOWN_MASK) { return; } int clickCount = e.getClickCount(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java b/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java index 9d60c0ba54..192c68da30 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ShowAllComponentsAction.java @@ -36,8 +36,6 @@ public class ShowAllComponentsAction extends ShowComponentAction { winMgr.doSetMenuGroup(new String[] { MENU_WINDOW, subMenuName }, "Permanent"); setHelpLocation(new HelpLocation("DockingWindows", "Windows_Menu")); - - this.winMgr = winMgr; } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java b/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java index 569a19691a..ed604cab9d 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ShowComponentAction.java @@ -19,6 +19,7 @@ import javax.swing.Icon; import javax.swing.ImageIcon; import docking.action.*; +import docking.actions.AutoGeneratedDockingAction; import ghidra.util.HelpLocation; import resources.ResourceManager; @@ -26,7 +27,8 @@ import resources.ResourceManager; * Action for showing components. If the component is hidden it will be made visible. * If it is tabbed, it will become the top tab. In all cases it will receive focus. */ -class ShowComponentAction extends DockingAction implements Comparable { +class ShowComponentAction extends DockingAction + implements AutoGeneratedDockingAction, Comparable { private static final int MAX_LENGTH = 40; protected static final ImageIcon EMPTY_ICON = @@ -47,6 +49,7 @@ class ShowComponentAction extends DockingAction implements Comparable { +class ShowWindowAction extends DockingAction + implements AutoGeneratedDockingAction, Comparable { private static final Icon ICON = ResourceManager.loadImage("images/application_xp.png"); private static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingsManager.java b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingsManager.java index 17e59d9247..780d2f5527 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingsManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingsManager.java @@ -27,10 +27,17 @@ import docking.*; import ghidra.util.ReservedKeyBindings; import ghidra.util.exception.AssertException; +/** + * A class that organizes system key bindings by mapping them to assigned {@link DockingActionIf}s. + * + *

This class understands reserved system key bindings. For non-reserved key bindings, this + * class knows how to map a single key binding to multiple actions. + */ public class KeyBindingsManager implements PropertyChangeListener { - protected Map dockingKeyMap; - protected Map actionToProviderMap; + // this map exists to update the MultiKeyBindingAction when the key binding changes + private Map actionToProviderMap; + private Map dockingKeyMap; private DockingTool tool; public KeyBindingsManager(DockingTool tool) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java index d7bc3a5395..1be12da4f3 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java @@ -277,7 +277,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction { @Override public String toString() { - return provider.toString() + " - " + action; + String providerString = provider == null ? "" : provider.toString() + " - "; + return providerString + action; } } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/AutoGeneratedDockingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/AutoGeneratedDockingAction.java new file mode 100644 index 0000000000..6dd760df75 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/AutoGeneratedDockingAction.java @@ -0,0 +1,24 @@ +/* ### + * 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; + +/** + * A marker interface to signal that the implementing action is temporary and gets built + * automatically by the tool + */ +public interface AutoGeneratedDockingAction { + // marker interface +} 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 12d701837e..1b59db3f7b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java @@ -164,13 +164,18 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener { newStub.addPropertyChangeListener(this); keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE, defaultKeyStroke, null, null); + + keyBindingsManager.addAction(provider, newStub); + return newStub; }); stub.addClientAction(action); - // note: only put the stub in the manager, not the actual action - keyBindingsManager.addAction(provider, stub); + if (!(action instanceof AutoGeneratedDockingAction)) { + // Auto-generated actions are temporary and should not receive key events + keyBindingsManager.addAction(provider, action); + } } /** diff --git a/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java b/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java index 3f05bb5890..1c7be7b8ec 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java @@ -55,7 +55,8 @@ public class TestKeyEventDispatcher { // focusProvider.focusOwner = event.getComponent(); try { - return systemDispatcher.dispatchKeyEvent(event); + boolean success = systemDispatcher.dispatchKeyEvent(event); + return success; } finally { focusProvider.focusOwner = null; @@ -105,6 +106,9 @@ public class TestKeyEventDispatcher { return null; } + if (focusOwner instanceof Window) { + return (Window) focusOwner; + } return SwingUtilities.windowForComponent(focusOwner); }