mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-4317 - Removing the 'reserved' concept
This commit is contained in:
parent
52e6360d96
commit
e44daf55aa
40 changed files with 1143 additions and 834 deletions
|
@ -30,7 +30,7 @@ import docking.actions.KeyBindingUtils;
|
|||
*/
|
||||
public abstract class DockingKeyBindingAction extends AbstractAction {
|
||||
|
||||
private DockingActionIf dockingAction;
|
||||
protected DockingActionIf dockingAction;
|
||||
|
||||
protected final KeyStroke keyStroke;
|
||||
protected final Tool tool;
|
||||
|
@ -54,7 +54,7 @@ public abstract class DockingKeyBindingAction extends AbstractAction {
|
|||
|
||||
public abstract KeyBindingPrecedence getKeyBindingPrecedence();
|
||||
|
||||
public boolean isReservedKeybindingPrecedence() {
|
||||
public boolean isSystemKeybindingPrecedence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,8 +127,8 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
return false; // let the normal event flow continue
|
||||
}
|
||||
|
||||
// *Special*, reserved key bindings--these can always be processed
|
||||
if (processReservedKeyActionsPrecedence(action, event)) {
|
||||
// *Special*, System key bindings--these can always be processed and are a higher priority
|
||||
if (processSystemActionPrecedence(action, event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -245,6 +245,16 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
return true; // default case; allow it through
|
||||
}
|
||||
|
||||
private boolean isSettingKeyBindings(KeyEvent event) {
|
||||
Component destination = event.getComponent();
|
||||
if (destination == null) {
|
||||
Component focusOwner = focusProvider.getFocusOwner();
|
||||
destination = focusOwner;
|
||||
}
|
||||
|
||||
return destination instanceof KeyEntryTextField;
|
||||
}
|
||||
|
||||
private boolean willBeHandledByTextComponent(KeyEvent event) {
|
||||
|
||||
Component destination = event.getComponent();
|
||||
|
@ -301,10 +311,16 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
|||
throw new AssertException("New precedence added to KeyBindingPrecedence?");
|
||||
}
|
||||
|
||||
private boolean processReservedKeyActionsPrecedence(DockingKeyBindingAction action,
|
||||
private boolean processSystemActionPrecedence(DockingKeyBindingAction action,
|
||||
KeyEvent event) {
|
||||
|
||||
if (!action.isReservedKeybindingPrecedence()) {
|
||||
if (isSettingKeyBindings(event)) {
|
||||
// This means the user is setting keybindings. Do not process System actions during
|
||||
// this operation so that the user can assign those keybindings.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!action.isSystemKeybindingPrecedence()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,16 +23,16 @@ import java.awt.event.KeyEvent;
|
|||
* key events see {@link KeyBindingOverrideKeyEventDispatcher#dispatchKeyEvent(KeyEvent)}.
|
||||
*/
|
||||
public enum KeyBindingPrecedence {
|
||||
|
||||
/** Actions at this level will be processed before all others, including Java components'. */
|
||||
ReservedActionsLevel,
|
||||
|
||||
|
||||
/** Actions at this level will be processed before all others, including Java components'. */
|
||||
SystemActionsLevel,
|
||||
|
||||
/** Actions with this precedence will be processed before key listener on Java components. */
|
||||
KeyListenerLevel,
|
||||
|
||||
|
||||
/** Actions with this precedence will be processed before actions on Java components. */
|
||||
ActionMapLevel,
|
||||
|
||||
|
||||
/** This level of precedence is the default level of precedence and gets processed after
|
||||
* Java components' key listeners and actions. */
|
||||
DefaultLevel
|
||||
|
|
|
@ -57,18 +57,7 @@ public class KeyEntryTextField extends JTextField {
|
|||
*/
|
||||
public void setKeyStroke(KeyStroke ks) {
|
||||
processEntry(ks);
|
||||
setText(parseKeyStroke(ks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the toString() form of the keyStroke, e.g., Ctrl-M is returned as
|
||||
* "keyCode CtrlM-P" and we want it to look like: "Ctrl-M"
|
||||
*
|
||||
* @param ks the keystroke to parse
|
||||
* @return the parse string for the keystroke
|
||||
*/
|
||||
public static String parseKeyStroke(KeyStroke ks) {
|
||||
return KeyBindingUtils.parseKeyStroke(ks);
|
||||
setText(KeyBindingUtils.parseKeyStroke(ks));
|
||||
}
|
||||
|
||||
public void clearField() {
|
||||
|
@ -114,23 +103,14 @@ public class KeyEntryTextField extends JTextField {
|
|||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
int keyCode = e.getKeyCode();
|
||||
if (isHelpKey(keyCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyStroke keyStroke = null;
|
||||
if (!isClearKey(keyCode) && !isModifiersOnly(e)) {
|
||||
keyStroke = KeyStroke.getKeyStroke(keyCode, e.getModifiersEx());
|
||||
}
|
||||
|
||||
processEntry(keyStroke);
|
||||
e.consume();
|
||||
}
|
||||
|
||||
private boolean isHelpKey(int keyCode) {
|
||||
return keyCode == KeyEvent.VK_F1 || keyCode == KeyEvent.VK_HELP;
|
||||
}
|
||||
|
||||
private boolean isClearKey(int keyCode) {
|
||||
return keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_ENTER;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/* ###
|
||||
* 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.action;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import help.HelpService;
|
||||
|
||||
/**
|
||||
* A base system action used for actions that show help information.
|
||||
*/
|
||||
public abstract class AbstractHelpAction extends DockingAction {
|
||||
|
||||
public AbstractHelpAction(String name, KeyStroke keyStroke, boolean isPrimary) {
|
||||
super(name, DockingWindowManager.DOCKING_WINDOWS_OWNER, isPrimary);
|
||||
|
||||
// Only the primary action will appear in the tool' key binding settings UI. The primary
|
||||
// action can be managed by the users. The secondary action is not managed at this time.
|
||||
if (isPrimary) {
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
}
|
||||
else {
|
||||
createSystemKeyBinding(keyStroke);
|
||||
}
|
||||
|
||||
setEnabled(true);
|
||||
|
||||
// Help actions don't have help
|
||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||
}
|
||||
|
||||
protected abstract boolean isInfo();
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DockingActionIf mouseOverAction = DockingWindowManager.getMouseOverAction();
|
||||
if (mouseOverAction != null) {
|
||||
showHelp(mouseOverAction);
|
||||
return;
|
||||
}
|
||||
|
||||
Object mouseOverObject = DockingWindowManager.getMouseOverObject();
|
||||
Object helpObject = getFirstAvailableObjectThatHasHelp(mouseOverObject);
|
||||
if (helpObject != null) {
|
||||
showHelp(helpObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// some components are special in that they have help registered just for them
|
||||
Object eventSource = context.getSourceObject();
|
||||
helpObject = getFirstAvailableObjectThatHasHelp(eventSource);
|
||||
if (helpObject != null) {
|
||||
showHelp(helpObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// dialogs help is handled differently than core Ghidra components
|
||||
DialogComponentProvider dialogProvider = findDialogComponentProvider();
|
||||
if (dialogProvider != null) {
|
||||
showHelp(dialogProvider.getComponent());
|
||||
return;
|
||||
}
|
||||
|
||||
// handle our 'normal' CompentProviders...just use the focused provider
|
||||
DockingWindowManager windowManager = DockingWindowManager.getActiveInstance();
|
||||
ComponentPlaceholder info = windowManager.getFocusedComponent();
|
||||
if (info != null) {
|
||||
ComponentProvider componentProvider = info.getProvider();
|
||||
showHelp(componentProvider);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void showHelp(Object helpObject) {
|
||||
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
DockingWindowManager windowManager = DockingWindowManager.getActiveInstance();
|
||||
Component component = windowManager.getActiveComponent();
|
||||
DockingWindowManager.getHelpService().showHelp(helpObject, isInfo(), component);
|
||||
});
|
||||
}
|
||||
|
||||
private DialogComponentProvider findDialogComponentProvider() {
|
||||
KeyboardFocusManager keyboardFocusManager =
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Window activeWindow = keyboardFocusManager.getActiveWindow();
|
||||
if (activeWindow instanceof DockingDialog) {
|
||||
DockingDialog dockingDialog = (DockingDialog) activeWindow;
|
||||
return dockingDialog.getDialogComponent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object getFirstAvailableObjectThatHasHelp(Object startingHelpObject) {
|
||||
if (startingHelpObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// First see if help exists for the given component directly...
|
||||
HelpService helpService = DockingWindowManager.getHelpService();
|
||||
HelpLocation helpLocation = helpService.getHelpLocation(startingHelpObject);
|
||||
if (helpLocation != null) {
|
||||
return startingHelpObject;
|
||||
}
|
||||
|
||||
// Second, with no help registered for the starting component, start looking for a suitable
|
||||
// help proxy.
|
||||
//
|
||||
// For Components, we can walk their containment hierarchy to find a potential help object
|
||||
if (!(startingHelpObject instanceof Component)) {
|
||||
// not a Component; don't know how to find a better help object
|
||||
return null;
|
||||
}
|
||||
|
||||
return getFirstAvailableComponentThatHasHelp((Component) startingHelpObject);
|
||||
}
|
||||
|
||||
private Component getFirstAvailableComponentThatHasHelp(Component component) {
|
||||
HelpService helpService = DockingWindowManager.getHelpService();
|
||||
HelpLocation helpLocation = helpService.getHelpLocation(component);
|
||||
if (helpLocation != null) {
|
||||
return component;
|
||||
}
|
||||
|
||||
Container parent = component.getParent();
|
||||
if (parent == null) {
|
||||
// nothing else to check
|
||||
return null;
|
||||
}
|
||||
|
||||
return getFirstAvailableComponentThatHasHelp(parent);
|
||||
}
|
||||
}
|
|
@ -29,14 +29,14 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import generic.theme.GColor;
|
||||
import generic.util.action.ReservedKeyBindings;
|
||||
import generic.util.action.SystemKeyBindings;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class ComponentThemeInspectorAction extends DockingAction {
|
||||
|
||||
public ComponentThemeInspectorAction() {
|
||||
super("Component Theme Inspector", DockingWindowManager.DOCKING_WINDOWS_OWNER, false);
|
||||
createReservedKeyBinding(ReservedKeyBindings.COMPONENT_THEME_INFO_KEY);
|
||||
super("Component Theme Inspector", DockingWindowManager.DOCKING_WINDOWS_OWNER, true);
|
||||
createSystemKeyBinding(SystemKeyBindings.COMPONENT_THEME_INFO_KEY);
|
||||
|
||||
// System action; no help needed
|
||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||
|
@ -99,9 +99,6 @@ public class ComponentThemeInspectorAction extends DockingAction {
|
|||
next = new Entry(component);
|
||||
}
|
||||
|
||||
if (entry != null) {
|
||||
entry.parent = next;
|
||||
}
|
||||
entry = next;
|
||||
|
||||
tree.add(entry);
|
||||
|
@ -199,7 +196,6 @@ public class ComponentThemeInspectorAction extends DockingAction {
|
|||
|
||||
private class Entry {
|
||||
|
||||
private Entry parent;
|
||||
protected Component component;
|
||||
protected Point mouseLocation;
|
||||
|
||||
|
|
|
@ -372,8 +372,8 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
precedence = kbData.getKeyBindingPrecedence();
|
||||
}
|
||||
|
||||
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
|
||||
return true; // reserved actions are special
|
||||
if (precedence == KeyBindingPrecedence.SystemActionsLevel) {
|
||||
return true; // system actions are special
|
||||
}
|
||||
|
||||
// log a trace message instead of throwing an exception, as to not break any legacy code
|
||||
|
@ -453,8 +453,8 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
* other actions are prevented from using the same KeyStroke as a reserved keybinding.
|
||||
* @param keyStroke the keystroke to be used for the keybinding
|
||||
*/
|
||||
void createReservedKeyBinding(KeyStroke keyStroke) {
|
||||
KeyBindingData data = KeyBindingData.createReservedKeyBindingData(keyStroke);
|
||||
void createSystemKeyBinding(KeyStroke keyStroke) {
|
||||
KeyBindingData data = KeyBindingData.createSystemKeyBindingData(keyStroke);
|
||||
setKeyBindingData(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public class GlobalFocusTraversalAction extends DockingAction {
|
|||
DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
|
||||
this.forward = forward;
|
||||
createReservedKeyBinding(keybinding);
|
||||
createSystemKeyBinding(keybinding);
|
||||
setEnabled(isGlobalFocusTraversalEnabled());
|
||||
}
|
||||
|
||||
|
|
|
@ -15,131 +15,16 @@
|
|||
*/
|
||||
package docking.action;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import help.HelpService;
|
||||
public class HelpAction extends AbstractHelpAction {
|
||||
|
||||
public class HelpAction extends DockingAction {
|
||||
|
||||
private boolean infoOnly;
|
||||
|
||||
public HelpAction(boolean infoOnly, KeyStroke keybinding) {
|
||||
super(infoOnly ? "HelpInfo" : "Help", DockingWindowManager.DOCKING_WINDOWS_OWNER, false);
|
||||
createReservedKeyBinding(keybinding);
|
||||
setEnabled(true);
|
||||
this.infoOnly = infoOnly;
|
||||
|
||||
// Help actions don't have help
|
||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||
public HelpAction(KeyStroke keyStroke, boolean isPrimary) {
|
||||
super("Context Help", keyStroke, isPrimary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DockingActionIf mouseOverAction = DockingWindowManager.getMouseOverAction();
|
||||
if (mouseOverAction != null) {
|
||||
showHelp(mouseOverAction);
|
||||
return;
|
||||
}
|
||||
|
||||
Object mouseOverObject = DockingWindowManager.getMouseOverObject();
|
||||
Object helpObject = getFirstAvailableObjectThatHasHelp(mouseOverObject);
|
||||
if (helpObject != null) {
|
||||
showHelp(helpObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// some components are special in that they have help registered just for them
|
||||
Object eventSource = context.getSourceObject();
|
||||
helpObject = getFirstAvailableObjectThatHasHelp(eventSource);
|
||||
if (helpObject != null) {
|
||||
showHelp(helpObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// dialogs help is handled differently than core Ghidra components
|
||||
DialogComponentProvider dialogProvider = findDialogComponentProvider();
|
||||
if (dialogProvider != null) {
|
||||
showHelp(dialogProvider.getComponent());
|
||||
return;
|
||||
}
|
||||
|
||||
// handle our 'normal' CompentProviders...just use the focused provider
|
||||
DockingWindowManager windowManager = DockingWindowManager.getActiveInstance();
|
||||
ComponentPlaceholder info = windowManager.getFocusedComponent();
|
||||
if (info != null) {
|
||||
ComponentProvider componentProvider = info.getProvider();
|
||||
showHelp(componentProvider);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void showHelp(final Object helpObject) {
|
||||
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
DockingWindowManager windowManager = DockingWindowManager.getActiveInstance();
|
||||
Component component = windowManager.getActiveComponent();
|
||||
DockingWindowManager.getHelpService().showHelp(helpObject, infoOnly, component);
|
||||
});
|
||||
}
|
||||
|
||||
private DialogComponentProvider findDialogComponentProvider() {
|
||||
KeyboardFocusManager keyboardFocusManager =
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Window activeWindow = keyboardFocusManager.getActiveWindow();
|
||||
if (activeWindow instanceof DockingDialog) {
|
||||
DockingDialog dockingDialog = (DockingDialog) activeWindow;
|
||||
return dockingDialog.getDialogComponent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object getFirstAvailableObjectThatHasHelp(Object startingHelpObject) {
|
||||
if (startingHelpObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// First see if help exists for the given component directly...
|
||||
HelpService helpService = DockingWindowManager.getHelpService();
|
||||
HelpLocation helpLocation = helpService.getHelpLocation(startingHelpObject);
|
||||
if (helpLocation != null) {
|
||||
return startingHelpObject;
|
||||
}
|
||||
|
||||
// Second, with no help registered for the starting component, start looking for a suitable
|
||||
// help proxy.
|
||||
//
|
||||
// For Components, we can walk their containment hierarchy to find a potential help object
|
||||
if (!(startingHelpObject instanceof Component)) {
|
||||
// not a Component; don't know how to find a better help object
|
||||
return null;
|
||||
}
|
||||
|
||||
return getFirstAvailableComponentThatHasHelp((Component) startingHelpObject);
|
||||
}
|
||||
|
||||
private Component getFirstAvailableComponentThatHasHelp(Component component) {
|
||||
HelpService helpService = DockingWindowManager.getHelpService();
|
||||
HelpLocation helpLocation = helpService.getHelpLocation(component);
|
||||
if (helpLocation != null) {
|
||||
return component;
|
||||
}
|
||||
|
||||
Container parent = component.getParent();
|
||||
if (parent == null) {
|
||||
// nothing else to check
|
||||
return null;
|
||||
}
|
||||
|
||||
return getFirstAvailableComponentThatHasHelp(parent);
|
||||
public boolean isInfo() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,21 +17,14 @@ package docking.action;
|
|||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.*;
|
||||
public class HelpInfoAction extends AbstractHelpAction {
|
||||
|
||||
class ReservedKeyBindingAction extends DockingKeyBindingAction {
|
||||
|
||||
ReservedKeyBindingAction(Tool tool, DockingActionIf action, KeyStroke keyStroke) {
|
||||
super(tool, action, keyStroke);
|
||||
public HelpInfoAction(KeyStroke keybinding) {
|
||||
super("Help Info", keybinding, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReservedKeybindingPrecedence() {
|
||||
public boolean isInfo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||
return KeyBindingPrecedence.ReservedActionsLevel;
|
||||
}
|
||||
}
|
|
@ -63,9 +63,9 @@ public class KeyBindingData {
|
|||
}
|
||||
|
||||
public KeyBindingData(KeyStroke keyStroke, KeyBindingPrecedence precedence) {
|
||||
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
|
||||
if (precedence == KeyBindingPrecedence.SystemActionsLevel) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't set precedence to Reserved KeyBindingPrecedence");
|
||||
"Can't set precedence to System KeyBindingPrecedence");
|
||||
}
|
||||
this.keyStroke = keyStroke;
|
||||
this.keyBindingPrecedence = precedence;
|
||||
|
@ -93,9 +93,9 @@ public class KeyBindingData {
|
|||
keyBindingPrecedence + "]";
|
||||
}
|
||||
|
||||
static KeyBindingData createReservedKeyBindingData(KeyStroke keyStroke) {
|
||||
static KeyBindingData createSystemKeyBindingData(KeyStroke keyStroke) {
|
||||
KeyBindingData keyBindingData = new KeyBindingData(keyStroke);
|
||||
keyBindingData.keyBindingPrecedence = KeyBindingPrecedence.ReservedActionsLevel;
|
||||
keyBindingData.keyBindingPrecedence = KeyBindingPrecedence.SystemActionsLevel;
|
||||
return keyBindingData;
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ public class KeyBindingData {
|
|||
* @param newKeyBindingData the data to validate
|
||||
* @return the potentially changed data
|
||||
*/
|
||||
public static KeyBindingData validateKeyBindingData(KeyBindingData newKeyBindingData) {
|
||||
static KeyBindingData validateKeyBindingData(KeyBindingData newKeyBindingData) {
|
||||
if (newKeyBindingData == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -117,8 +117,8 @@ public class KeyBindingData {
|
|||
}
|
||||
|
||||
KeyBindingPrecedence precedence = newKeyBindingData.getKeyBindingPrecedence();
|
||||
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
|
||||
return createReservedKeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding));
|
||||
if (precedence == KeyBindingPrecedence.SystemActionsLevel) {
|
||||
return createSystemKeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding));
|
||||
}
|
||||
return new KeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding), precedence);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ import javax.swing.Action;
|
|||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.*;
|
||||
import generic.util.action.ReservedKeyBindings;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
|
@ -36,14 +37,14 @@ import ghidra.util.exception.AssertException;
|
|||
public class KeyBindingsManager implements PropertyChangeListener {
|
||||
|
||||
// this map exists to update the MultiKeyBindingAction when the key binding changes
|
||||
private Map<DockingActionIf, ComponentProvider> actionToProviderMap;
|
||||
private Map<KeyStroke, DockingKeyBindingAction> dockingKeyMap;
|
||||
private Map<DockingActionIf, ComponentProvider> actionToProviderMap = new HashMap<>();
|
||||
private Map<KeyStroke, DockingKeyBindingAction> dockingKeyMap = new HashMap<>();
|
||||
private Map<DockingActionIf, SystemKeyBindingAction> dockingActionToSystemActionMap =
|
||||
new HashMap<>();
|
||||
private Tool tool;
|
||||
|
||||
public KeyBindingsManager(Tool tool) {
|
||||
this.tool = tool;
|
||||
dockingKeyMap = new HashMap<>();
|
||||
actionToProviderMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void addAction(ComponentProvider optionalProvider, DockingActionIf action) {
|
||||
|
@ -59,14 +60,17 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
public void addReservedAction(DockingActionIf action) {
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
Objects.requireNonNull(keyBinding);
|
||||
addReservedKeyBinding(action, keyBinding);
|
||||
}
|
||||
public void addSystemAction(DockingActionIf action) {
|
||||
KeyStroke keyStroke = action.getKeyBinding();
|
||||
Objects.requireNonNull(keyStroke);
|
||||
DockingKeyBindingAction existingAction = dockingKeyMap.get(keyStroke);
|
||||
if (existingAction != null) {
|
||||
throw new AssertException("Attempting to add more than one reserved " +
|
||||
"action to a given keystroke: " + keyStroke);
|
||||
}
|
||||
|
||||
public void addReservedAction(DockingActionIf action, KeyStroke ks) {
|
||||
addReservedKeyBinding(action, ks);
|
||||
addSystemKeyBinding(action, keyStroke);
|
||||
action.addPropertyChangeListener(this);
|
||||
}
|
||||
|
||||
public void removeAction(DockingActionIf action) {
|
||||
|
@ -78,17 +82,54 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
private void addKeyBinding(ComponentProvider provider, DockingActionIf action,
|
||||
KeyStroke keyStroke) {
|
||||
|
||||
if (ReservedKeyBindings.isReservedKeystroke(keyStroke)) {
|
||||
throw new AssertException("Cannot assign action to a reserved keystroke. " +
|
||||
"Action: " + action.getName() + " - Keystroke: " + keyStroke);
|
||||
String errorMessage = validateActionKeyBinding(action, keyStroke);
|
||||
if (errorMessage != null) {
|
||||
// Getting here should not be possible from the UI, but may happen if a developer sets
|
||||
// an incorrect keybinding
|
||||
Msg.error(this, errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// map standard keybinding to action
|
||||
// map standard keystroke to action
|
||||
doAddKeyBinding(provider, action, keyStroke);
|
||||
|
||||
// map workaround keystroke to action
|
||||
fixupAltGraphKeyStrokeMapping(provider, action, keyStroke);
|
||||
}
|
||||
|
||||
public String validateActionKeyBinding(DockingActionIf dockingAction, KeyStroke ks) {
|
||||
|
||||
if (ks == null) {
|
||||
return null; // clearing the key stroke
|
||||
}
|
||||
|
||||
//
|
||||
// 1) Handle case with given key stroke already in use by a system action
|
||||
//
|
||||
Action existingAction = dockingKeyMap.get(ks);
|
||||
if (existingAction instanceof SystemKeyBindingAction systemAction) {
|
||||
|
||||
DockingActionIf systemDockingAction = systemAction.getAction();
|
||||
if (dockingAction == systemDockingAction) {
|
||||
return null; // same key stroke; not sure if this can happen
|
||||
}
|
||||
|
||||
String ksString = KeyBindingUtils.parseKeyStroke(ks);
|
||||
return ksString + " in use by System action '" + systemDockingAction.getName() + "'";
|
||||
}
|
||||
|
||||
//
|
||||
// 2) Handle the case where a system action key stroke is being set to something that is
|
||||
// already in-use by some other action
|
||||
//
|
||||
SystemKeyBindingAction systemAction = dockingActionToSystemActionMap.get(dockingAction);
|
||||
if (systemAction != null && existingAction != null) {
|
||||
return "System action cannot be set to in-use key stroke";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void fixupAltGraphKeyStrokeMapping(ComponentProvider provider, DockingActionIf action,
|
||||
KeyStroke keyStroke) {
|
||||
|
||||
|
@ -116,55 +157,64 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
KeyStroke mappingKeyStroke, KeyStroke actionKeyStroke) {
|
||||
|
||||
DockingKeyBindingAction existingAction = dockingKeyMap.get(mappingKeyStroke);
|
||||
if (existingAction == null) {
|
||||
dockingKeyMap.put(mappingKeyStroke,
|
||||
new MultipleKeyAction(tool, provider, action, actionKeyStroke));
|
||||
if (existingAction instanceof MultipleKeyAction) {
|
||||
MultipleKeyAction multipleKeyction = (MultipleKeyAction) existingAction;
|
||||
multipleKeyction.addAction(provider, action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(existingAction instanceof MultipleKeyAction)) {
|
||||
return; // reserved binding; nothing to do
|
||||
if (existingAction instanceof SystemKeyBindingAction) {
|
||||
// This should not happen due to protections in the UI
|
||||
Msg.error(this, "Attempted to use the same keybinding for an existing System action: " +
|
||||
existingAction + ". Keystroke: " + mappingKeyStroke);
|
||||
return;
|
||||
}
|
||||
|
||||
MultipleKeyAction multipleKeyction = (MultipleKeyAction) existingAction;
|
||||
multipleKeyction.addAction(provider, action);
|
||||
SystemKeyBindingAction systemAction = dockingActionToSystemActionMap.get(action);
|
||||
if (systemAction != null) {
|
||||
// the user has updated the binding for a System action; re-install it
|
||||
registerSystemKeyBinding(action, mappingKeyStroke);
|
||||
return;
|
||||
}
|
||||
|
||||
// assume existingAction == null
|
||||
dockingKeyMap.put(mappingKeyStroke,
|
||||
new MultipleKeyAction(tool, provider, action, actionKeyStroke));
|
||||
}
|
||||
|
||||
private void addReservedKeyBinding(DockingActionIf action, KeyStroke keyStroke) {
|
||||
DockingKeyBindingAction existingAction = dockingKeyMap.get(keyStroke);
|
||||
if (existingAction != null) {
|
||||
throw new AssertException("Attempting to add more than one reserved " +
|
||||
"action to a given keystroke: " + keyStroke);
|
||||
}
|
||||
|
||||
KeyBindingData binding = KeyBindingData.createReservedKeyBindingData(keyStroke);
|
||||
private void addSystemKeyBinding(DockingActionIf action, KeyStroke keyStroke) {
|
||||
KeyBindingData binding = KeyBindingData.createSystemKeyBindingData(keyStroke);
|
||||
action.setKeyBindingData(binding);
|
||||
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(tool, action, keyStroke));
|
||||
registerSystemKeyBinding(action, keyStroke);
|
||||
}
|
||||
|
||||
private void registerSystemKeyBinding(DockingActionIf action, KeyStroke keyStroke) {
|
||||
SystemKeyBindingAction systemAction = new SystemKeyBindingAction(tool, action, keyStroke);
|
||||
dockingKeyMap.put(keyStroke, systemAction);
|
||||
dockingActionToSystemActionMap.put(action, systemAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the keystroke binding from the root pane's input map
|
||||
* using keystroke specified instead of that specified by the action
|
||||
*/
|
||||
private void removeKeyBinding(KeyStroke keyStroke, DockingActionIf action) {
|
||||
if (keyStroke == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReservedKeyBindings.isReservedKeystroke(keyStroke)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DockingKeyBindingAction existingAction = dockingKeyMap.get(keyStroke);
|
||||
if (existingAction == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MultipleKeyAction mkAction = (MultipleKeyAction) existingAction;
|
||||
mkAction.removeAction(action);
|
||||
if (mkAction.isEmpty()) {
|
||||
if (existingAction instanceof SystemKeyBindingAction) {
|
||||
dockingKeyMap.remove(keyStroke);
|
||||
}
|
||||
else if (existingAction instanceof MultipleKeyAction) {
|
||||
|
||||
MultipleKeyAction mkAction = (MultipleKeyAction) existingAction;
|
||||
mkAction.removeAction(action);
|
||||
if (mkAction.isEmpty()) {
|
||||
dockingKeyMap.remove(keyStroke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -196,8 +246,13 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
return dockingKeyMap.get(keyStroke);
|
||||
}
|
||||
|
||||
public Set<DockingActionIf> getSystemActions() {
|
||||
return new HashSet<>(dockingActionToSystemActionMap.keySet());
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
dockingKeyMap.clear();
|
||||
actionToProviderMap.clear();
|
||||
dockingActionToSystemActionMap.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,8 +142,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
}
|
||||
|
||||
private boolean ignoreActionWhileMenuShowing() {
|
||||
if (getKeyBindingPrecedence() == KeyBindingPrecedence.ReservedActionsLevel) {
|
||||
return false; // allow reserved bindings through "no matter what!"
|
||||
if (getKeyBindingPrecedence() == KeyBindingPrecedence.SystemActionsLevel) {
|
||||
return false; // allow system bindings through "no matter what!"
|
||||
}
|
||||
|
||||
MenuSelectionManager menuManager = MenuSelectionManager.defaultManager();
|
||||
|
@ -238,8 +238,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isReservedKeybindingPrecedence() {
|
||||
return false; // MultipleKeyActions can never be reserved
|
||||
public boolean isSystemKeybindingPrecedence() {
|
||||
return false; // MultipleKeyActions can never be 'system'
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,7 @@ public class NextPreviousWindowAction extends DockingAction {
|
|||
DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
|
||||
this.forward = forward;
|
||||
createReservedKeyBinding(keybinding);
|
||||
createSystemKeyBinding(keybinding);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,18 @@ import ghidra.util.Swing;
|
|||
*/
|
||||
public class ShowContextMenuAction extends DockingAction {
|
||||
|
||||
public ShowContextMenuAction(KeyStroke keyStroke) {
|
||||
super("Show Context Menu", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
public ShowContextMenuAction(KeyStroke keyStroke, boolean isPrimary) {
|
||||
super(isPrimary ? "Show Context Menu" : "Show Context Menu Alternate",
|
||||
DockingWindowManager.DOCKING_WINDOWS_OWNER, isPrimary);
|
||||
|
||||
// Only the primary action will appear in the tool' key binding settings UI. The primary
|
||||
// action can be managed by the users. The secondary action is not managed at this time.
|
||||
if (isPrimary) {
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
}
|
||||
else {
|
||||
createSystemKeyBinding(keyStroke);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,14 +24,14 @@ import org.apache.logging.log4j.Logger;
|
|||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import generic.util.action.ReservedKeyBindings;
|
||||
import generic.util.action.SystemKeyBindings;
|
||||
|
||||
public class ShowFocusCycleAction extends DockingAction {
|
||||
static final Logger log = LogManager.getLogger(ShowFocusCycleAction.class);
|
||||
|
||||
public ShowFocusCycleAction() {
|
||||
super("Show Focus Cycle", DockingWindowManager.DOCKING_WINDOWS_OWNER, false);
|
||||
createReservedKeyBinding(ReservedKeyBindings.FOCUS_CYCLE_INFO_KEY);
|
||||
super("Show Focus Cycle", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
createSystemKeyBinding(SystemKeyBindings.FOCUS_CYCLE_INFO_KEY);
|
||||
setEnabled(true);
|
||||
|
||||
// System action; no help needed
|
||||
|
|
|
@ -25,14 +25,14 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import docking.*;
|
||||
import generic.util.action.ReservedKeyBindings;
|
||||
import generic.util.action.SystemKeyBindings;
|
||||
|
||||
public class ShowFocusInfoAction extends DockingAction {
|
||||
static final Logger log = LogManager.getLogger(ShowFocusInfoAction.class);
|
||||
|
||||
public ShowFocusInfoAction() {
|
||||
super("Show Focus Info", DockingWindowManager.DOCKING_WINDOWS_OWNER, false);
|
||||
createReservedKeyBinding(ReservedKeyBindings.FOCUS_INFO_KEY);
|
||||
super("Show Focus Info", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
createSystemKeyBinding(SystemKeyBindings.FOCUS_INFO_KEY);
|
||||
setEnabled(true);
|
||||
|
||||
// System action; no help needed
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* ###
|
||||
* 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.action;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.*;
|
||||
|
||||
/**
|
||||
* An {@link DockingKeyBindingAction} to signal that the given {@link DockingAction} gets priority
|
||||
* over all other non-system actions in the system.
|
||||
*/
|
||||
public class SystemKeyBindingAction extends DockingKeyBindingAction {
|
||||
|
||||
SystemKeyBindingAction(Tool tool, DockingActionIf action, KeyStroke keyStroke) {
|
||||
super(tool, action, keyStroke);
|
||||
}
|
||||
|
||||
public DockingActionIf getAction() {
|
||||
return dockingAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemKeybindingPrecedence() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||
return KeyBindingPrecedence.SystemActionsLevel;
|
||||
}
|
||||
}
|
|
@ -87,7 +87,7 @@ public interface DockingToolActions {
|
|||
public Set<DockingActionIf> getActions(String owner);
|
||||
|
||||
/**
|
||||
* Returns all actions known to the tool
|
||||
* Returns all actions known to the tool.
|
||||
* @return the actions
|
||||
*/
|
||||
public Set<DockingActionIf> getAllActions();
|
||||
|
@ -103,4 +103,5 @@ public interface DockingToolActions {
|
|||
* @param placeholder the placeholder containing information related to the action it represents
|
||||
*/
|
||||
public void registerSharedActionPlaceholder(SharedDockingActionPlaceholder placeholder);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
/* ###
|
||||
* 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 java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.Tool;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.KeyBindingData;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* An object that maps actions to key strokes.
|
||||
* <p>
|
||||
* This class knows how to load all system actions and how to load any key bindings for those
|
||||
* actions from the tool's options. Clients can make changes to the state of this class that can
|
||||
* then be applied to the system by calling {@link #applyChanges()}.
|
||||
*/
|
||||
public class KeyBindings {
|
||||
|
||||
private Tool tool;
|
||||
|
||||
private Map<String, List<DockingActionIf>> actionsByFullName;
|
||||
private Map<String, List<String>> actionNamesByKeyStroke = new HashMap<>();
|
||||
private Map<String, KeyStroke> keyStrokesByFullName = new HashMap<>();
|
||||
private List<DockingActionIf> uniqueActions = new ArrayList<>();
|
||||
|
||||
// to know what has been changed
|
||||
private Map<String, KeyStroke> originalKeyStrokesByFullName = new HashMap<>();
|
||||
private String longestActionName = "";
|
||||
|
||||
private ToolOptions options;
|
||||
|
||||
public KeyBindings(Tool tool) {
|
||||
this.tool = tool;
|
||||
|
||||
options = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public List<DockingActionIf> getUniqueActions() {
|
||||
return Collections.unmodifiableList(uniqueActions);
|
||||
}
|
||||
|
||||
public Map<String, KeyStroke> getKeyStrokesByFullActionName() {
|
||||
return Collections.unmodifiableMap(keyStrokesByFullName);
|
||||
}
|
||||
|
||||
public boolean containsAction(String fullName) {
|
||||
return actionsByFullName.containsKey(fullName);
|
||||
}
|
||||
|
||||
public KeyStroke getKeyStroke(String fullName) {
|
||||
return keyStrokesByFullName.get(fullName);
|
||||
}
|
||||
|
||||
public String getActionsForKeyStrokeText(String keyStrokeText) {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
List<String> names = actionNamesByKeyStroke.get(keyStrokeText);
|
||||
if (CollectionUtils.isBlank(names)) {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
names.sort((n1, n2) -> {
|
||||
return n1.compareToIgnoreCase(n2);
|
||||
});
|
||||
|
||||
sb.append("Actions mapped to key " + keyStrokeText + ":\n");
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
sb.append(" ");
|
||||
|
||||
String name = names.get(i);
|
||||
List<DockingActionIf> actions = actionsByFullName.get(name);
|
||||
DockingActionIf action = actions.get(0);
|
||||
sb.append(action.getName());
|
||||
sb.append(" (").append(action.getOwnerDescription()).append(')');
|
||||
if (i < names.size() - 1) {
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getLongestActionName() {
|
||||
return longestActionName;
|
||||
}
|
||||
|
||||
public boolean setActionKeyStroke(String actionName, KeyStroke keyStroke) {
|
||||
String ksName = KeyBindingUtils.parseKeyStroke(keyStroke);
|
||||
|
||||
// remove old keystroke for action name
|
||||
KeyStroke oldKs = keyStrokesByFullName.get(actionName);
|
||||
if (oldKs != null) {
|
||||
String oldName = KeyBindingUtils.parseKeyStroke(oldKs);
|
||||
if (oldName.equals(ksName)) {
|
||||
return false;
|
||||
}
|
||||
removeFromKeyMap(oldKs, actionName);
|
||||
}
|
||||
addActionKeyStroke(keyStroke, actionName);
|
||||
|
||||
keyStrokesByFullName.put(actionName, keyStroke);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeKeyStroke(String actionName) {
|
||||
if (keyStrokesByFullName.containsKey(actionName)) {
|
||||
KeyStroke stroke = keyStrokesByFullName.get(actionName);
|
||||
if (stroke == null) {
|
||||
// nothing to remove; nothing has changed
|
||||
return false;
|
||||
}
|
||||
|
||||
removeFromKeyMap(stroke, actionName);
|
||||
keyStrokesByFullName.put(actionName, null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the tool options key bindings to the default values originally loaded when the
|
||||
* system started.
|
||||
*/
|
||||
public void restoreOptions() {
|
||||
|
||||
Set<Entry<String, List<DockingActionIf>>> entries = actionsByFullName.entrySet();
|
||||
for (Entry<String, List<DockingActionIf>> entry : entries) {
|
||||
List<DockingActionIf> actions = entry.getValue();
|
||||
|
||||
// pick one action, they are all conceptually the same
|
||||
DockingActionIf action = actions.get(0);
|
||||
String actionName = entry.getKey();
|
||||
KeyStroke currentKeyStroke = keyStrokesByFullName.get(actionName);
|
||||
KeyBindingData defaultBinding = action.getDefaultKeyBindingData();
|
||||
KeyStroke newKeyStroke =
|
||||
(defaultBinding == null) ? null : defaultBinding.getKeyBinding();
|
||||
|
||||
updateOptions(actionName, currentKeyStroke, newKeyStroke);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels any pending changes that have not yet been applied.
|
||||
*/
|
||||
public void cancelChanges() {
|
||||
Iterator<String> iter = originalKeyStrokesByFullName.keySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
String actionName = iter.next();
|
||||
KeyStroke originalKS = originalKeyStrokesByFullName.get(actionName);
|
||||
KeyStroke modifiedKS = keyStrokesByFullName.get(actionName);
|
||||
if (modifiedKS != null && !modifiedKS.equals(originalKS)) {
|
||||
keyStrokesByFullName.put(actionName, originalKS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies any pending changes.
|
||||
*/
|
||||
public void applyChanges() {
|
||||
Iterator<String> iter = keyStrokesByFullName.keySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
String actionName = iter.next();
|
||||
KeyStroke currentKeyStroke = keyStrokesByFullName.get(actionName);
|
||||
KeyStroke originalKeyStroke = originalKeyStrokesByFullName.get(actionName);
|
||||
updateOptions(actionName, originalKeyStroke, currentKeyStroke);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromKeyMap(KeyStroke ks, String actionName) {
|
||||
if (ks == null) {
|
||||
return;
|
||||
}
|
||||
String ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
List<String> list = actionNamesByKeyStroke.get(ksName);
|
||||
if (list != null) {
|
||||
list.remove(actionName);
|
||||
if (list.isEmpty()) {
|
||||
actionNamesByKeyStroke.remove(ksName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOptions(String fullActionName, KeyStroke currentKeyStroke,
|
||||
KeyStroke newKeyStroke) {
|
||||
|
||||
if (Objects.equals(currentKeyStroke, newKeyStroke)) {
|
||||
return;
|
||||
}
|
||||
|
||||
options.setKeyStroke(fullActionName, newKeyStroke);
|
||||
originalKeyStrokesByFullName.put(fullActionName, newKeyStroke);
|
||||
keyStrokesByFullName.put(fullActionName, newKeyStroke);
|
||||
|
||||
List<DockingActionIf> actions = actionsByFullName.get(fullActionName);
|
||||
for (DockingActionIf action : actions) {
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKeyStroke));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
actionsByFullName = KeyBindingUtils.getAllActionsByFullName(tool);
|
||||
Set<Entry<String, List<DockingActionIf>>> entries = actionsByFullName.entrySet();
|
||||
for (Entry<String, List<DockingActionIf>> entry : entries) {
|
||||
|
||||
// pick one action, they are all conceptually the same
|
||||
List<DockingActionIf> actions = entry.getValue();
|
||||
DockingActionIf action = actions.get(0);
|
||||
uniqueActions.add(action);
|
||||
|
||||
String actionName = entry.getKey();
|
||||
KeyStroke ks = options.getKeyStroke(actionName, null);
|
||||
keyStrokesByFullName.put(actionName, ks);
|
||||
addActionKeyStroke(ks, actionName);
|
||||
originalKeyStrokesByFullName.put(actionName, ks);
|
||||
|
||||
String shortName = action.getName();
|
||||
if (shortName.length() > longestActionName.length()) {
|
||||
longestActionName = shortName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addActionKeyStroke(KeyStroke ks, String actionName) {
|
||||
if (ks == null) {
|
||||
return;
|
||||
}
|
||||
String ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
List<String> list = actionNamesByKeyStroke.get(ksName);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
actionNamesByKeyStroke.put(ksName, list);
|
||||
}
|
||||
if (!list.contains(actionName)) {
|
||||
list.add(actionName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -16,42 +16,43 @@
|
|||
package docking.actions;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.KeyEntryTextField;
|
||||
import docking.action.*;
|
||||
import docking.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.KeyBindingData;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.util.action.ReservedKeyBindings;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.Icons;
|
||||
|
||||
/**
|
||||
* Dialog to set the key binding on an action; it is popped up when the F4 key
|
||||
* is hit.
|
||||
* Dialog to set the key binding on an action. It is triggered by the F4 key.
|
||||
*/
|
||||
public class KeyEntryDialog extends DialogComponentProvider {
|
||||
|
||||
private KeyBindings keyBindings;
|
||||
private ToolActions toolActions;
|
||||
private DockingActionIf action;
|
||||
|
||||
private JPanel defaultPanel;
|
||||
private KeyEntryTextField keyEntryField;
|
||||
private JTextPane collisionPane;
|
||||
private StyledDocument doc;
|
||||
private SimpleAttributeSet tabAttrSet;
|
||||
private SimpleAttributeSet textAttrSet;
|
||||
private Color bgColor;
|
||||
|
||||
public KeyEntryDialog(DockingActionIf action, ToolActions actions) {
|
||||
public KeyEntryDialog(Tool tool, DockingActionIf action) {
|
||||
super("Set Key Binding for " + action.getName(), true);
|
||||
this.action = action;
|
||||
this.toolActions = actions;
|
||||
this.toolActions = (ToolActions) tool.getToolActions();
|
||||
|
||||
this.keyBindings = new KeyBindings(tool);
|
||||
|
||||
setUpAttributes();
|
||||
createPanel();
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
|
@ -150,21 +151,22 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
KeyStroke newKeyStroke = keyEntryField.getKeyStroke();
|
||||
if (newKeyStroke != null && ReservedKeyBindings.isReservedKeystroke(newKeyStroke)) {
|
||||
setStatusText(keyEntryField.getText() + " is a reserved keystroke");
|
||||
KeyStroke newKs = keyEntryField.getKeyStroke();
|
||||
String errorMessage = toolActions.validateActionKeyBinding(action, newKs);
|
||||
if (errorMessage != null) {
|
||||
setStatusText(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
clearStatusText();
|
||||
|
||||
KeyStroke existingKeyStroke = action.getKeyBinding();
|
||||
if (Objects.equals(existingKeyStroke, newKeyStroke)) {
|
||||
if (Objects.equals(existingKeyStroke, newKs)) {
|
||||
setStatusText("Key binding unchanged");
|
||||
return;
|
||||
}
|
||||
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKeyStroke));
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
|
||||
|
||||
close();
|
||||
}
|
||||
|
@ -174,10 +176,6 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
textAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||
textAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
|
||||
textAttrSet.addAttribute(StyleConstants.Foreground, Messages.NORMAL);
|
||||
|
||||
tabAttrSet = new SimpleAttributeSet();
|
||||
TabStop tabs = new TabStop(20, StyleConstants.ALIGN_LEFT, TabStop.LEAD_NONE);
|
||||
StyleConstants.setTabSet(tabAttrSet, new TabSet(new TabStop[] { tabs }));
|
||||
}
|
||||
|
||||
private void updateCollisionPane(KeyStroke ks) {
|
||||
|
@ -193,26 +191,10 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
List<DockingActionIf> list = getManagedActionsForKeyStroke(ks);
|
||||
if (list.size() == 0) {
|
||||
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);
|
||||
String text = keyBindings.getActionsForKeyStrokeText(ksName);
|
||||
try {
|
||||
doc.insertString(0, "Actions mapped to " + ksName + "\n\n", textAttrSet);
|
||||
for (DockingActionIf a : list) {
|
||||
String collisionStr = "\t" + a.getName() + " (" + a.getOwnerDescription() + ")\n";
|
||||
int offset = doc.getLength();
|
||||
doc.insertString(offset, collisionStr, textAttrSet);
|
||||
doc.setParagraphAttributes(offset, 1, tabAttrSet, false);
|
||||
}
|
||||
doc.insertString(0, text, textAttrSet);
|
||||
collisionPane.setCaretPosition(0);
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
|
@ -220,37 +202,4 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private List<DockingActionIf> getManagedActionsForKeyStroke(KeyStroke keyStroke) {
|
||||
MultipleKeyAction multiAction = getMultipleKeyAction(keyStroke);
|
||||
if (multiAction == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<DockingActionIf> list = multiAction.getActions();
|
||||
Map<String, DockingActionIf> nameMap = new HashMap<>(list.size());
|
||||
|
||||
// the list may have multiple matches for a single owner, which we do not want (see
|
||||
// SharedStubKeyBindingAction)
|
||||
for (DockingActionIf dockableAction : list) {
|
||||
if (shouldAddAction(dockableAction)) {
|
||||
// this overwrites same named actions
|
||||
nameMap.put(dockableAction.getName() + dockableAction.getOwner(), dockableAction);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(nameMap.values());
|
||||
}
|
||||
|
||||
private MultipleKeyAction getMultipleKeyAction(KeyStroke ks) {
|
||||
Action keyAction = toolActions.getAction(ks);
|
||||
if (keyAction instanceof MultipleKeyAction) {
|
||||
return (MultipleKeyAction) keyAction;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean shouldAddAction(DockingActionIf dockableAction) {
|
||||
return dockableAction.getKeyBindingType().isManaged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,19 +17,22 @@ package docking.actions;
|
|||
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class KeyBindingAction extends DockingAction {
|
||||
public class SetKeyBindingAction extends DockingAction {
|
||||
|
||||
public static String NAME = "Set KeyBinding";
|
||||
private ToolActions toolActions;
|
||||
private Tool tool;
|
||||
|
||||
public KeyBindingAction(ToolActions toolActions) {
|
||||
public SetKeyBindingAction(Tool tool, KeyStroke keyStroke) {
|
||||
super(NAME, DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
this.toolActions = toolActions;
|
||||
this.tool = tool;
|
||||
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
|
||||
// Help actions don't have help
|
||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||
|
@ -56,7 +59,7 @@ public class KeyBindingAction extends DockingAction {
|
|||
return;
|
||||
}
|
||||
|
||||
KeyEntryDialog d = new KeyEntryDialog(action, toolActions);
|
||||
KeyEntryDialog d = new KeyEntryDialog(tool, action);
|
||||
DockingWindowManager.showDialog(d);
|
||||
}
|
||||
|
||||
|
@ -72,6 +75,7 @@ public class KeyBindingAction extends DockingAction {
|
|||
|
||||
// It is not key binding managed, which means that it may be a shared key binding
|
||||
String actionName = dockingAction.getName();
|
||||
ToolActions toolActions = (ToolActions) tool.getToolActions();
|
||||
DockingActionIf sharedAction = toolActions.getSharedStubKeyBindingAction(actionName);
|
||||
if (sharedAction != null) {
|
||||
return sharedAction;
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package docking.actions;
|
||||
|
||||
import static generic.util.action.SystemKeyBindings.*;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.*;
|
||||
|
@ -32,7 +34,6 @@ import org.apache.commons.collections4.map.LazyMap;
|
|||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import generic.util.action.ReservedKeyBindings;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
@ -60,7 +61,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
private Map<String, SharedStubKeyBindingAction> sharedActionMap = new HashMap<>();
|
||||
|
||||
private ToolOptions keyBindingOptions;
|
||||
private Tool dockingTool;
|
||||
private Tool tool;
|
||||
private KeyBindingsManager keyBindingsManager;
|
||||
private OptionsChangeListener optionChangeListener = (options, optionName, oldValue,
|
||||
newValue) -> updateKeyBindingsFromOptions(options, optionName, (KeyStroke) newValue);
|
||||
|
@ -72,45 +73,49 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
* @param actionToGuiHelper the class that takes actions and maps them to GUI widgets
|
||||
*/
|
||||
public ToolActions(Tool tool, ActionToGuiHelper actionToGuiHelper) {
|
||||
this.dockingTool = tool;
|
||||
this.tool = tool;
|
||||
this.actionGuiHelper = actionToGuiHelper;
|
||||
this.keyBindingsManager = new KeyBindingsManager(tool);
|
||||
this.keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
this.keyBindingOptions.addOptionsChangeListener(optionChangeListener);
|
||||
|
||||
createReservedKeyBindings();
|
||||
createSystemActions();
|
||||
SharedActionRegistry.installSharedActions(tool, this);
|
||||
}
|
||||
|
||||
private void createReservedKeyBindings() {
|
||||
KeyBindingAction keyBindingAction = new KeyBindingAction(this);
|
||||
keyBindingsManager.addReservedAction(keyBindingAction,
|
||||
ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY);
|
||||
private void createSystemActions() {
|
||||
|
||||
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY1));
|
||||
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY2));
|
||||
keyBindingsManager
|
||||
.addReservedAction(new HelpAction(true, ReservedKeyBindings.HELP_INFO_KEY));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new ShowContextMenuAction(ReservedKeyBindings.CONTEXT_MENU_KEY1));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new ShowContextMenuAction(ReservedKeyBindings.CONTEXT_MENU_KEY2));
|
||||
addSystemAction(new SetKeyBindingAction(tool, UPDATE_KEY_BINDINGS_KEY));
|
||||
|
||||
keyBindingsManager.addReservedAction(
|
||||
new NextPreviousWindowAction(ReservedKeyBindings.FOCUS_NEXT_WINDOW_KEY, true));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new NextPreviousWindowAction(ReservedKeyBindings.FOCUS_PREVIOUS_WINDOW_KEY, false));
|
||||
addSystemAction(new HelpAction(HELP_KEY1, false));
|
||||
addSystemAction(new HelpAction(HELP_KEY2, true));
|
||||
addSystemAction(new HelpInfoAction(HELP_INFO_KEY));
|
||||
addSystemAction(new ShowContextMenuAction(CONTEXT_MENU_KEY1, true));
|
||||
addSystemAction(new ShowContextMenuAction(CONTEXT_MENU_KEY2, false));
|
||||
|
||||
keyBindingsManager.addReservedAction(
|
||||
new GlobalFocusTraversalAction(ReservedKeyBindings.FOCUS_NEXT_COMPONENT_KEY, true));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new GlobalFocusTraversalAction(ReservedKeyBindings.FOCUS_PREVIOUS_COMPONENT_KEY,
|
||||
false));
|
||||
addSystemAction(new NextPreviousWindowAction(FOCUS_NEXT_WINDOW_KEY, true));
|
||||
addSystemAction(new NextPreviousWindowAction(FOCUS_PREVIOUS_WINDOW_KEY, false));
|
||||
|
||||
addSystemAction(new GlobalFocusTraversalAction(FOCUS_NEXT_COMPONENT_KEY, true));
|
||||
addSystemAction(new GlobalFocusTraversalAction(FOCUS_PREVIOUS_COMPONENT_KEY, false));
|
||||
|
||||
// helpful debugging actions
|
||||
keyBindingsManager.addReservedAction(new ShowFocusInfoAction());
|
||||
keyBindingsManager.addReservedAction(new ShowFocusCycleAction());
|
||||
keyBindingsManager.addReservedAction(new ComponentThemeInspectorAction());
|
||||
addSystemAction(new ShowFocusInfoAction());
|
||||
addSystemAction(new ShowFocusCycleAction());
|
||||
addSystemAction(new ComponentThemeInspectorAction());
|
||||
}
|
||||
|
||||
private void addSystemAction(DockingAction action) {
|
||||
|
||||
// Some System actions support changing the keybinding. In the future, all System actions
|
||||
// may support this.
|
||||
if (action.getKeyBindingType().isManaged()) {
|
||||
KeyBindingData kbd = action.getKeyBindingData();
|
||||
KeyStroke ks = kbd.getKeyBinding();
|
||||
loadKeyBindingFromOptions(action, ks);
|
||||
}
|
||||
|
||||
keyBindingsManager.addSystemAction(action);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
|
@ -170,7 +175,6 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
}
|
||||
|
||||
private void loadKeyBindingFromOptions(DockingActionIf action, KeyStroke ks) {
|
||||
|
||||
String description = "Keybinding for " + action.getFullName();
|
||||
keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, null,
|
||||
description);
|
||||
|
@ -288,6 +292,8 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
|
||||
result.addAll(sharedActionMap.values());
|
||||
|
||||
result.addAll(keyBindingsManager.getSystemActions());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -311,7 +317,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
* otherwise the options will be removed because they are noted as not being used.
|
||||
*/
|
||||
public synchronized void restoreKeyBindings() {
|
||||
keyBindingOptions = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
Iterator<DockingActionIf> it = getKeyBindingActionsIterator();
|
||||
for (DockingActionIf action : CollectionUtils.asIterable(it)) {
|
||||
|
@ -406,8 +412,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
}
|
||||
|
||||
DockingActionIf action = (DockingActionIf) evt.getSource();
|
||||
if (!action.getKeyBindingType()
|
||||
.isManaged()) {
|
||||
if (!action.getKeyBindingType().isManaged()) {
|
||||
// this reads unusually, but we need to notify the tool to rebuild its 'Window' menu
|
||||
// in the case that this action is one of the tool's special actions
|
||||
keyBindingsChanged();
|
||||
|
@ -429,7 +434,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
|
||||
// triggered by a user-initiated action; called by propertyChange()
|
||||
private void keyBindingsChanged() {
|
||||
dockingTool.setConfigChanged(true);
|
||||
tool.setConfigChanged(true);
|
||||
actionGuiHelper.keyBindingsChanged();
|
||||
}
|
||||
|
||||
|
@ -447,6 +452,17 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given key stroke can be used for the given action for restrictions such as
|
||||
* those for System level actions.
|
||||
* @param action the action; may be null
|
||||
* @param ks the key stroke
|
||||
* @return A null value if valid; a non-null error message if invalid
|
||||
*/
|
||||
public String validateActionKeyBinding(DockingActionIf action, KeyStroke ks) {
|
||||
return keyBindingsManager.validateActionKeyBinding(action, ks);
|
||||
}
|
||||
|
||||
public Action getAction(KeyStroke ks) {
|
||||
return keyBindingsManager.getDockingKeyAction(ks);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue