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 e41e20780f..01db055015 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java @@ -337,7 +337,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio // Note: there may be a test focus issue here. If this test fails sporadically due to // how the action context is generated (it depends on focus). It is only useful to fail // here in development mode. - triggerKey(tool.getToolFrame(), controlEsc); + triggerKey(provider.getComponent(), controlEsc); assertProviderIsHidden_InNonBatchMode(); } @@ -376,6 +376,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio provider.addToTool(); tool.showComponentProvider(provider, true); waitForSwing(); + waitForCondition(() -> provider.isVisible()); } private void hideProvider() { @@ -546,6 +547,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio // simulate the user mousing over the toolbar button DockingActionIf closeAction = getAction(tool, provider.getOwner(), "Close Window"); assertNotNull("Provider action not installed in toolbar", closeAction); + DockingWindowManager.setMouseOverAction(closeAction); performLaunchKeyStrokeDialogAction(); @@ -554,6 +556,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio runSwing(() -> dialog.setKeyStroke(ks)); pressButtonByText(dialog, "OK"); + waitForSwing(); assertFalse("Invalid key stroke: " + ks, runSwing(() -> dialog.isVisible())); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/tablechooser/TableChooserDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/tablechooser/TableChooserDialogTest.java index cdf73b6c38..304ad880d9 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/tablechooser/TableChooserDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/tablechooser/TableChooserDialogTest.java @@ -77,12 +77,13 @@ public class TableChooserDialogTest extends AbstractGhidraHeadedIntegrationTest tool.setVisible(true); Program program = new ToyProgramBuilder("Test", true).getProgram(); Navigatable navigatable = null; - dialog = new TableChooserDialog(tool, executor, program, "Title", navigatable); + dialog = new TableChooserDialog(tool, executor, program, "Dialog Title", navigatable); testAction = new TestAction(); dialog.addAction(testAction); dialog.show(); + waitForDialogComponent(TableChooserDialog.class); loadData(); } @@ -289,7 +290,8 @@ public class TableChooserDialogTest extends AbstractGhidraHeadedIntegrationTest KeyStroke newKs = KeyStroke.getKeyStroke('A', 0, false); setKeyBindingViaF4Dialog(testAction, newKs); triggerKey(dialog.getComponent(), newKs); - assertTrue(testAction.wasInvoked()); + assertTrue("Action was not invoked from the new key binding: " + newKs, + testAction.wasInvoked()); } @Test diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 13af9535c1..e954cc15b3 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -354,9 +354,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder } /** - * Get the window which contains the specified Provider's component. + * Get the window that contains the specified Provider's component * @param provider component provider - * @return window or null if component is not visible or not found. + * @return window or null if component is not visible or not found */ public Window getProviderWindow(ComponentProvider provider) { ComponentPlaceholder placeholder = getActivePlaceholder(provider); @@ -366,6 +366,24 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return null; } + /** + * Get the provider that contains the specified component + * @param c the component + * @return the provider; null if now containing provider is found + */ + public ComponentProvider getProvider(Component c) { + if (c != null) { + DockableComponent dc = getDockableComponent(c); + if (dc != null) { + ComponentPlaceholder placeholder = dc.getComponentWindowingPlaceholder(); + if (placeholder != null) { + return placeholder.getProvider(); + } + } + } + return null; + } + ComponentPlaceholder getActivePlaceholder(ComponentProvider provider) { return placeholderManager.getActivePlaceholder(provider); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java b/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java index e415fa7756..b34e3a0137 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/KeyBindingOverrideKeyEventDispatcher.java @@ -24,6 +24,7 @@ import java.awt.event.KeyListener; import javax.swing.*; import javax.swing.text.JTextComponent; +import docking.action.MultipleKeyAction; import docking.actions.KeyBindingUtils; import ghidra.util.bean.GGlassPane; import ghidra.util.exception.AssertException; @@ -136,7 +137,7 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher return false; } - KeyBindingPrecedence keyBindingPrecedence = action.getKeyBindingPrecedence(); + KeyBindingPrecedence keyBindingPrecedence = getValidKeyBindingPrecedence(action); if (keyBindingPrecedence == null) { // Odd Code: we use the precedence as a signal to say that, when it is null, there // are no valid bindings to be processed. We used to have a isValidContext() @@ -158,6 +159,15 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher // @formatter:on } + private KeyBindingPrecedence getValidKeyBindingPrecedence(DockingKeyBindingAction action) { + + if (action instanceof MultipleKeyAction) { + MultipleKeyAction multiAction = (MultipleKeyAction) action; + return multiAction.geValidKeyBindingPrecedence(focusProvider.getFocusOwner()); + } + return action.getKeyBindingPrecedence(); + } + /** * Returns true if the given key event should be blocked (i.e., not processed by us or Java). */ 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 cf522bc31c..64a0748462 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java @@ -15,6 +15,7 @@ */ package docking.action; +import java.awt.Component; import java.awt.Window; import java.awt.event.ActionEvent; import java.util.*; @@ -23,10 +24,11 @@ import javax.swing.*; import docking.*; import docking.actions.KeyBindingUtils; +import generic.util.WindowUtilities; import ghidra.util.Swing; /** - * Action that manages multiple PluginActions mapped to this action's key binding. + * Action that manages multiple {@link DockingAction}s mapped to a given key binding */ public class MultipleKeyAction extends DockingKeyBindingAction { private List actions = new ArrayList<>(); @@ -228,7 +230,19 @@ public class MultipleKeyAction extends DockingKeyBindingAction { @Override public KeyBindingPrecedence getKeyBindingPrecedence() { - List validActions = getActionsForCurrentContext(null); + return geValidKeyBindingPrecedence(null); + } + + /** + * This is a special version of {@link #getKeyBindingPrecedence()} that allows the internal + * key event processing to specify the source component when determining how precedence should + * be established for the actions contained herein. + * @param source the component; may be null + * @return the precedence; may be null + */ + public KeyBindingPrecedence geValidKeyBindingPrecedence(Component source) { + + List validActions = getActionsForCurrentContext(source); if (validActions.isEmpty()) { return null; // a signal that no actions are valid for the current context } @@ -245,7 +259,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction { private List getActionsForCurrentContext(Object eventSource) { DockingWindowManager dwm = tool.getWindowManager(); - Window window = dwm.getActiveWindow(); + Window window = getWindow(dwm, eventSource); if (window instanceof DockingDialog) { DockingDialog dockingDialog = (DockingDialog) window; DialogComponentProvider provider = dockingDialog.getDialogComponent(); @@ -258,13 +272,27 @@ public class MultipleKeyAction extends DockingKeyBindingAction { return validActions; } - ComponentProvider localProvider = dwm.getActiveComponentProvider(); + ComponentProvider localProvider = getProvider(dwm, eventSource); ActionContext localContext = getLocalContext(localProvider); localContext.setSourceObject(eventSource); List validActions = getValidContextActions(localContext); return validActions; } + private ComponentProvider getProvider(DockingWindowManager dwm, Object eventSource) { + if (eventSource instanceof Component) { + return dwm.getProvider((Component) eventSource); + } + return dwm.getActiveComponentProvider(); + } + + private Window getWindow(DockingWindowManager dwm, Object eventSource) { + if (eventSource instanceof Component) { + return WindowUtilities.windowForComponent((Component) eventSource); + } + return dwm.getActiveWindow(); + } + public List getActions() { List list = new ArrayList<>(actions.size()); for (ActionData actionData : actions) {