GT-2925-2 - Key Bindings - Support Window Menu Provider Key Bindings -

test and review fixes
This commit is contained in:
dragonmacher 2019-07-17 16:11:45 -04:00
parent 21db705ecc
commit dabdc38ea9
11 changed files with 90 additions and 21 deletions

View file

@ -43,7 +43,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
private final Icon ICON = ResourceManager.loadImage("images/refresh.png"); private final Icon ICON = ResourceManager.loadImage("images/refresh.png");
private static final String PROVIDER_NAME = "Test Action Provider"; private static final String PROVIDER_NAME = "Test Action Provider";
private static final KeyStroke CONTROL_T = 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 TestEnv env;
private PluginTool tool; private PluginTool tool;
@ -160,6 +160,19 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
assertMenuItemHasKeyStroke(newKs); assertMenuItemHasKeyStroke(newKs);
} }
@Test
public void testSetKeyBinding_ViaDialog_FromWindowMenu_ThenFireKeyEventToShowProvider() {
showProvider();
KeyStroke newKs = CONTROL_T;
setKeyBindingViaF4Dialog_FromWindowsMenu(newKs);
hideProvider();
pressKey(CONTROL_T);
assertProviderIsActive();
}
@Test @Test
public void testSetKeyBinding_ViaDialog_FromWindowMenu_ToAlreadyBoundAction() { public void testSetKeyBinding_ViaDialog_FromWindowMenu_ToAlreadyBoundAction() {
@ -326,6 +339,11 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
waitForSwing(); waitForSwing();
} }
private void hideProvider() {
tool.showComponentProvider(provider, false);
waitForSwing();
}
private void setDefaultKeyBinding(KeyStroke defaultKs) { private void setDefaultKeyBinding(KeyStroke defaultKs) {
runSwing(() -> provider.setKeyBinding(new KeyBindingData(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() { private DockingActionIf getShowProviderAction() {
DockingActionIf showProviderAction = DockingActionIf showProviderAction =
@ -389,6 +415,11 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
waitForSwing(); waitForSwing();
} }
private void assertProviderIsActive() {
assertTrue("The test provider is not showing and focused",
runSwing(() -> tool.isActive(provider)));
}
private void assertNoToolbarAction() { private void assertNoToolbarAction() {
assertNull("No toolbar action found for provider", getToolbarShowProviderAction()); assertNull("No toolbar action found for provider", getToolbarShowProviderAction());
} }

View file

@ -29,7 +29,6 @@ import docking.action.DockingActionIf;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.test.AbstractProgramBasedTest; import ghidra.test.AbstractProgramBasedTest;
import ghidra.util.Msg;
import ghidra.util.datastruct.WeakSet; import ghidra.util.datastruct.WeakSet;
public class ProviderNavigationPluginTest extends AbstractProgramBasedTest { public class ProviderNavigationPluginTest extends AbstractProgramBasedTest {
@ -165,9 +164,6 @@ public class ProviderNavigationPluginTest extends AbstractProgramBasedTest {
@Override @Override
public void accept(ComponentProvider c) { public void accept(ComponentProvider c) {
Msg.out("Spy - activated: " + c);
lastActivated = c; lastActivated = c;
forceActivate(c); forceActivate(c);
} }

View file

@ -80,7 +80,7 @@ public class ActionDialog extends DialogComponentProvider {
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
ExecutableKeyActionAdapter actionProxy = list.get(i); ExecutableKeyActionAdapter actionProxy = list.get(i);
DockingActionIf action = actionProxy.getAction(); DockingActionIf action = actionProxy.getAction();
listModel.addElement(action.getName()); listModel.addElement(action.getName() + " (" + action.getOwnerDescription() + ")");
} }
actionList.setSelectedIndex(0); actionList.setSelectedIndex(0);
} }
@ -144,7 +144,7 @@ public class ActionDialog extends DialogComponentProvider {
@Override @Override
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
if (e.getModifiers() != InputEvent.BUTTON1_MASK) { if (e.getModifiersEx() != InputEvent.BUTTON1_DOWN_MASK) {
return; return;
} }
int clickCount = e.getClickCount(); int clickCount = e.getClickCount();

View file

@ -36,8 +36,6 @@ public class ShowAllComponentsAction extends ShowComponentAction {
winMgr.doSetMenuGroup(new String[] { MENU_WINDOW, subMenuName }, "Permanent"); winMgr.doSetMenuGroup(new String[] { MENU_WINDOW, subMenuName }, "Permanent");
setHelpLocation(new HelpLocation("DockingWindows", "Windows_Menu")); setHelpLocation(new HelpLocation("DockingWindows", "Windows_Menu"));
this.winMgr = winMgr;
} }
@Override @Override

View file

@ -19,6 +19,7 @@ import javax.swing.Icon;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import docking.action.*; import docking.action.*;
import docking.actions.AutoGeneratedDockingAction;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import resources.ResourceManager; import resources.ResourceManager;
@ -26,7 +27,8 @@ import resources.ResourceManager;
* Action for showing components. If the component is hidden it will be made visible. * 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. * If it is tabbed, it will become the top tab. In all cases it will receive focus.
*/ */
class ShowComponentAction extends DockingAction implements Comparable<ShowComponentAction> { class ShowComponentAction extends DockingAction
implements AutoGeneratedDockingAction, Comparable<ShowComponentAction> {
private static final int MAX_LENGTH = 40; private static final int MAX_LENGTH = 40;
protected static final ImageIcon EMPTY_ICON = protected static final ImageIcon EMPTY_ICON =
@ -47,6 +49,7 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
protected ShowComponentAction(DockingWindowManager winMgr, String name, String subMenuName) { protected ShowComponentAction(DockingWindowManager winMgr, String name, String subMenuName) {
super(truncateTitleAsNeeded(name), DockingWindowManager.DOCKING_WINDOWS_OWNER); super(truncateTitleAsNeeded(name), DockingWindowManager.DOCKING_WINDOWS_OWNER);
this.winMgr = winMgr;
} }
ShowComponentAction(DockingWindowManager winMgr, ComponentPlaceholder placeholder, ShowComponentAction(DockingWindowManager winMgr, ComponentPlaceholder placeholder,

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,15 +15,16 @@
*/ */
package docking; package docking;
import ghidra.util.HelpLocation;
import javax.swing.Icon; import javax.swing.Icon;
import resources.ResourceManager;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.MenuData; import docking.action.MenuData;
import docking.actions.AutoGeneratedDockingAction;
import ghidra.util.HelpLocation;
import resources.ResourceManager;
class ShowWindowAction extends DockingAction implements Comparable<ShowWindowAction> { class ShowWindowAction extends DockingAction
implements AutoGeneratedDockingAction, Comparable<ShowWindowAction> {
private static final Icon ICON = ResourceManager.loadImage("images/application_xp.png"); private static final Icon ICON = ResourceManager.loadImage("images/application_xp.png");
private static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME; private static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME;

View file

@ -27,10 +27,17 @@ import docking.*;
import ghidra.util.ReservedKeyBindings; import ghidra.util.ReservedKeyBindings;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
/**
* A class that organizes system key bindings by mapping them to assigned {@link DockingActionIf}s.
*
* <p>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 { public class KeyBindingsManager implements PropertyChangeListener {
protected Map<KeyStroke, DockingKeyBindingAction> dockingKeyMap; // this map exists to update the MultiKeyBindingAction when the key binding changes
protected Map<DockingActionIf, ComponentProvider> actionToProviderMap; private Map<DockingActionIf, ComponentProvider> actionToProviderMap;
private Map<KeyStroke, DockingKeyBindingAction> dockingKeyMap;
private DockingTool tool; private DockingTool tool;
public KeyBindingsManager(DockingTool tool) { public KeyBindingsManager(DockingTool tool) {

View file

@ -277,7 +277,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
@Override @Override
public String toString() { public String toString() {
return provider.toString() + " - " + action; String providerString = provider == null ? "" : provider.toString() + " - ";
return providerString + action;
} }
} }
} }

View file

@ -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
}

View file

@ -164,13 +164,18 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
newStub.addPropertyChangeListener(this); newStub.addPropertyChangeListener(this);
keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE, keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE,
defaultKeyStroke, null, null); defaultKeyStroke, null, null);
keyBindingsManager.addAction(provider, newStub);
return newStub; return newStub;
}); });
stub.addClientAction(action); stub.addClientAction(action);
// note: only put the stub in the manager, not the actual action if (!(action instanceof AutoGeneratedDockingAction)) {
keyBindingsManager.addAction(provider, stub); // Auto-generated actions are temporary and should not receive key events
keyBindingsManager.addAction(provider, action);
}
} }
/** /**

View file

@ -55,7 +55,8 @@ public class TestKeyEventDispatcher {
// //
focusProvider.focusOwner = event.getComponent(); focusProvider.focusOwner = event.getComponent();
try { try {
return systemDispatcher.dispatchKeyEvent(event); boolean success = systemDispatcher.dispatchKeyEvent(event);
return success;
} }
finally { finally {
focusProvider.focusOwner = null; focusProvider.focusOwner = null;
@ -105,6 +106,9 @@ public class TestKeyEventDispatcher {
return null; return null;
} }
if (focusOwner instanceof Window) {
return (Window) focusOwner;
}
return SwingUtilities.windowForComponent(focusOwner); return SwingUtilities.windowForComponent(focusOwner);
} }