mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Merge remote-tracking branch 'origin/GT-2925-dragonmacher-window-menu-key-bindings'
This commit is contained in:
commit
10372c2e13
200 changed files with 3955 additions and 3195 deletions
|
@ -23,7 +23,7 @@ import javax.swing.JFrame;
|
|||
import docking.action.DockingActionIf;
|
||||
import docking.actions.ToolActions;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
* A partial implementation of {@link DockingTool} that serves as a place to share common
|
||||
|
@ -32,7 +32,7 @@ import ghidra.util.SystemUtilities;
|
|||
public abstract class AbstractDockingTool implements DockingTool {
|
||||
|
||||
protected DockingWindowManager winMgr;
|
||||
protected ToolActions actionMgr;
|
||||
protected ToolActions toolActions;
|
||||
protected Map<String, ToolOptions> optionsMap = new HashMap<>();
|
||||
protected boolean configChangedFlag;
|
||||
|
||||
|
@ -57,17 +57,21 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
|
||||
@Override
|
||||
public void addComponentProvider(ComponentProvider provider, boolean show) {
|
||||
Runnable r = () -> winMgr.addComponent(provider, show);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Runnable r = () -> {
|
||||
winMgr.addComponent(provider, show);
|
||||
toolActions.addGlobalAction(provider.getShowProviderAction());
|
||||
};
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeComponentProvider(ComponentProvider provider) {
|
||||
Runnable r = () -> {
|
||||
actionMgr.removeComponentActions(provider);
|
||||
toolActions.removeGlobalAction(provider.getShowProviderAction());
|
||||
toolActions.removeActions(provider);
|
||||
winMgr.removeComponent(provider);
|
||||
};
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,41 +100,43 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
|
||||
@Override
|
||||
public void addAction(DockingActionIf action) {
|
||||
actionMgr.addToolAction(action);
|
||||
toolActions.addGlobalAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAction(DockingActionIf action) {
|
||||
actionMgr.removeToolAction(action);
|
||||
toolActions.removeGlobalAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||
actionMgr.addLocalAction(provider, action);
|
||||
toolActions.addLocalAction(provider, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||
actionMgr.removeProviderAction(provider, action);
|
||||
toolActions.removeLocalAction(provider, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DockingActionIf> getAllActions() {
|
||||
Set<DockingActionIf> actions = actionMgr.getAllActions();
|
||||
ActionToGuiMapper am = winMgr.getActionManager();
|
||||
actions.addAll(am.getAllActions());
|
||||
return actions;
|
||||
return toolActions.getAllActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner) {
|
||||
return actionMgr.getActions(owner);
|
||||
return toolActions.getActions(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentProvider getActiveComponentProvider() {
|
||||
return winMgr.getActiveComponentProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showComponentProvider(ComponentProvider provider, boolean visible) {
|
||||
Runnable r = () -> winMgr.showComponent(provider, visible);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,7 +156,7 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
@Override
|
||||
public void toFront(ComponentProvider provider) {
|
||||
Runnable r = () -> winMgr.toFront(provider);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -173,6 +179,21 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
winMgr.contextChanged(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getGlobalContext() {
|
||||
return winMgr.getGlobalContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContextListener(DockingContextListener listener) {
|
||||
winMgr.addContextListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeContextListener(DockingContextListener listener) {
|
||||
winMgr.removeContextListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockingWindowManager getWindowManager() {
|
||||
return winMgr;
|
||||
|
@ -187,4 +208,9 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
public boolean hasConfigChanged() {
|
||||
return configChangedFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToolActions getToolActions() {
|
||||
return toolActions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,4 +78,11 @@ public class ActionToGuiHelper {
|
|||
public void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
||||
windowManager.removeProviderAction(provider, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to signal that key bindings for one or more actions have changed
|
||||
*/
|
||||
public void keyBindingsChanged() {
|
||||
windowManager.scheduleUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,70 +16,38 @@
|
|||
package docking;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.MenuSelectionManager;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.menu.MenuGroupMap;
|
||||
import docking.menu.MenuHandler;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* Manages the global actions for the menu and toolbar.
|
||||
*/
|
||||
public class ActionToGuiMapper {
|
||||
|
||||
private HashSet<DockingActionIf> globalActions = new LinkedHashSet<>();
|
||||
private Set<DockingActionIf> globalActions = new LinkedHashSet<>();
|
||||
|
||||
private MenuHandler menuBarMenuHandler;
|
||||
private MenuGroupMap menuGroupMap;
|
||||
|
||||
private static boolean enableDiagnosticActions;
|
||||
|
||||
private KeyBindingsManager keyBindingsManager;
|
||||
|
||||
private GlobalMenuAndToolBarManager menuAndToolBarManager;
|
||||
|
||||
private PopupActionManager popupActionManager;
|
||||
private DockingAction keyBindingsAction;
|
||||
|
||||
ActionToGuiMapper(DockingWindowManager winMgr) {
|
||||
menuGroupMap = new MenuGroupMap();
|
||||
|
||||
menuBarMenuHandler = new MenuBarMenuHandler(winMgr);
|
||||
|
||||
keyBindingsManager = new KeyBindingsManager(winMgr);
|
||||
menuAndToolBarManager =
|
||||
new GlobalMenuAndToolBarManager(winMgr, menuBarMenuHandler, menuGroupMap);
|
||||
popupActionManager = new PopupActionManager(winMgr, menuGroupMap);
|
||||
|
||||
initializeHelpActions();
|
||||
}
|
||||
|
||||
private void initializeHelpActions() {
|
||||
DockingWindowsContextSensitiveHelpListener.install();
|
||||
|
||||
keyBindingsAction = new KeyBindingAction(this);
|
||||
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(keyBindingsAction);
|
||||
|
||||
if (enableDiagnosticActions) {
|
||||
keyBindingsManager.addReservedAction(new ShowFocusInfoAction());
|
||||
keyBindingsManager.addReservedAction(new ShowFocusCycleAction());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A static initializer allowing additional diagnostic actions
|
||||
* to be added to all frame and dialog windows.
|
||||
* @param enable
|
||||
*/
|
||||
static void enableDiagnosticActions(boolean enable) {
|
||||
enableDiagnosticActions = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,41 +62,12 @@ public class ActionToGuiMapper {
|
|||
DockingWindowManager.getHelpService().registerHelp(c, helpLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all actions associated with the given owner
|
||||
* @param owner the owner of all actions to be removed.
|
||||
*/
|
||||
void removeAll(String owner) {
|
||||
Iterator<DockingActionIf> iter = new ArrayList<>(globalActions).iterator();
|
||||
List<DockingActionIf> removedList = new ArrayList<>();
|
||||
while (iter.hasNext()) {
|
||||
DockingActionIf action = iter.next();
|
||||
if (owner.equals(action.getOwner())) {
|
||||
keyBindingsManager.removeAction(action);
|
||||
menuAndToolBarManager.removeAction(action);
|
||||
popupActionManager.removeAction(action);
|
||||
removedList.add(action);
|
||||
}
|
||||
}
|
||||
|
||||
globalActions.removeAll(removedList);
|
||||
}
|
||||
|
||||
void addLocalAction(DockingActionIf action, ComponentProvider provider) {
|
||||
keyBindingsManager.addAction(action, provider);
|
||||
}
|
||||
|
||||
void removeLocalAction(DockingActionIf action) {
|
||||
keyBindingsManager.removeAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given Global action to the menu and/or toolbar.
|
||||
* @param action the action to be added.
|
||||
*/
|
||||
void addToolAction(DockingActionIf action) {
|
||||
if (globalActions.add(action)) {
|
||||
keyBindingsManager.addAction(action, null);
|
||||
popupActionManager.addAction(action);
|
||||
menuAndToolBarManager.addAction(action);
|
||||
}
|
||||
|
@ -139,28 +78,11 @@ public class ActionToGuiMapper {
|
|||
* @param action the action to be removed.
|
||||
*/
|
||||
void removeToolAction(DockingActionIf action) {
|
||||
keyBindingsManager.removeAction(action);
|
||||
popupActionManager.removeAction(action);
|
||||
menuAndToolBarManager.removeAction(action);
|
||||
globalActions.remove(action);
|
||||
}
|
||||
|
||||
public Set<DockingActionIf> getAllActions() {
|
||||
|
||||
// Note: this method is called by non-Swing test code. Synchronize access to the
|
||||
// data structures in this class in order to prevent concurrent mod exceptions.
|
||||
Set<DockingActionIf> actions = new HashSet<>();
|
||||
SystemUtilities.runSwingNow(() -> {
|
||||
actions.addAll(globalActions);
|
||||
actions.addAll(keyBindingsManager.getLocalActions());
|
||||
});
|
||||
return actions;
|
||||
}
|
||||
|
||||
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
||||
return keyBindingsManager.getDockingKeyAction(keyStroke);
|
||||
}
|
||||
|
||||
Set<DockingActionIf> getGlobalActions() {
|
||||
return globalActions;
|
||||
}
|
||||
|
@ -173,28 +95,16 @@ public class ActionToGuiMapper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all menus (includes popup menus)
|
||||
*/
|
||||
static void dismissMenus() {
|
||||
private void dismissMenus() {
|
||||
MenuSelectionManager.defaultManager().clearSelectedPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the menu and toolbar to reflect any changes in the set of actions.
|
||||
*
|
||||
*/
|
||||
void update() {
|
||||
menuAndToolBarManager.update();
|
||||
contextChangedAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all resources and makes this object unavailable for future use.
|
||||
*
|
||||
*/
|
||||
void dispose() {
|
||||
keyBindingsManager.dispose();
|
||||
popupActionManager.dispose();
|
||||
menuAndToolBarManager.dispose();
|
||||
globalActions.clear();
|
||||
|
|
|
@ -326,7 +326,7 @@ class ComponentNode extends Node {
|
|||
DockingTabRenderer tabRenderer) {
|
||||
|
||||
final ComponentProvider provider = placeholder.getProvider();
|
||||
if (!provider.isTransient()) {
|
||||
if (!provider.isTransient() || provider.isSnapshot()) {
|
||||
return; // don't muck with the title of 'real' providers--only transients, like search
|
||||
}
|
||||
|
||||
|
|
|
@ -449,6 +449,16 @@ public class ComponentPlaceholder {
|
|||
}
|
||||
}
|
||||
|
||||
void removeAllActions() {
|
||||
if (comp != null) {
|
||||
for (DockingActionIf action : actions) {
|
||||
comp.actionRemoved(action);
|
||||
}
|
||||
}
|
||||
|
||||
actions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an action from this component
|
||||
* @param action the action to be removed.
|
||||
|
|
|
@ -25,9 +25,9 @@ import javax.swing.*;
|
|||
import docking.action.*;
|
||||
import docking.help.HelpDescriptor;
|
||||
import docking.help.HelpService;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.UniversalIdGenerator;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
/**
|
||||
* Abstract base class for creating dockable GUI components within a tool.
|
||||
|
@ -61,13 +61,28 @@ import ghidra.util.exception.AssertException;
|
|||
* <li>{@link #componentActivated()} and {@link #componentDeactived()}
|
||||
* <li>{@link #componentHidden()} and {@link #componentShown()}
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Note: This class was created so that implementors could add local actions within the constructor
|
||||
* <b><u>Show Provider Action</u></b> - Each provider has an action to show the provider. For
|
||||
* typical, non-transient providers (see {@link #setTransient()}) the action will appear in
|
||||
* the tool's <b>Window</b> menu. You can have your provider also appear in the tool's toolbar
|
||||
* by calling {@link #setIcon(Icon, boolean)}, passing <code>true</code> for
|
||||
* <code>isToolbarAction</code>.
|
||||
* <p>
|
||||
* Historical Note: This class was created so that implementors could add local actions within the constructor
|
||||
* without having to understand that they must first add themselves to the WindowManager.
|
||||
*/
|
||||
|
||||
public abstract class ComponentProvider implements HelpDescriptor, ActionContextProvider {
|
||||
private static final String TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE =
|
||||
"Transient providers are not added to the toolbar";
|
||||
|
||||
private static final String TRANSIENT_PROVIDER_KEY_BINDING_WARNING_MESSAGE =
|
||||
"Transient providers cannot have key bindings";
|
||||
|
||||
public static final String DEFAULT_WINDOW_GROUP = "Default";
|
||||
|
||||
private static final String TOOLBAR_GROUP = "View";
|
||||
|
||||
// maps for mapping old provider names and owner to new names and/or owner
|
||||
private static Map<String, String> oldOwnerMap = new HashMap<>();
|
||||
private static Map<String, String> oldNameMap = new HashMap<>();
|
||||
|
@ -77,19 +92,29 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
private String title;
|
||||
private String subTitle;
|
||||
private String tabText;
|
||||
private Icon icon;
|
||||
|
||||
private Set<DockingActionIf> actionSet = new LinkedHashSet<>();
|
||||
private String windowMenuGroup;
|
||||
|
||||
/** True if this provider's action should appear in the toolbar */
|
||||
private boolean addToolbarAction;
|
||||
private boolean isTransient;
|
||||
private HelpLocation helpLocation;
|
||||
private KeyBindingData defaultKeyBindingData;
|
||||
private Icon icon;
|
||||
|
||||
private String windowMenuGroup;
|
||||
private String group = DEFAULT_WINDOW_GROUP;
|
||||
private WindowPosition defaultWindowPosition = WindowPosition.WINDOW;
|
||||
private WindowPosition defaultIntraGroupPosition = WindowPosition.STACK;
|
||||
private DockingAction showProviderAction;
|
||||
|
||||
private HelpLocation helpLocation;
|
||||
private final Class<?> contextType;
|
||||
|
||||
private long instanceID = UniversalIdGenerator.nextID().getValue();
|
||||
private boolean instanceIDHasBeenInitialized;
|
||||
|
||||
private String inceptionInformation;
|
||||
|
||||
/**
|
||||
* Creates a new component provider with a default location of {@link WindowPosition#WINDOW}.
|
||||
* @param tool The tool will manage and show this provider
|
||||
|
@ -116,6 +141,32 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
this.owner = owner;
|
||||
this.title = name;
|
||||
this.contextType = contextType;
|
||||
|
||||
recordInception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action used to show this provider
|
||||
* @return the action
|
||||
*/
|
||||
DockingActionIf getShowProviderAction() {
|
||||
createShowProviderAction();
|
||||
return showProviderAction;
|
||||
}
|
||||
|
||||
private void createShowProviderAction() {
|
||||
if (showProviderAction != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (addToolbarAction) {
|
||||
Objects.requireNonNull(icon,
|
||||
"The provider's icon cannot be null when requesting the provider's action " +
|
||||
"appear in the toolbar");
|
||||
}
|
||||
|
||||
boolean supportsKeyBindings = !isTransient;
|
||||
showProviderAction = new ShowProviderAction(supportsKeyBindings);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -361,6 +412,10 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
this.helpLocation = helpLocation;
|
||||
HelpService helpService = DockingWindowManager.getHelpService();
|
||||
helpService.registerHelp(this, helpLocation);
|
||||
|
||||
if (showProviderAction != null) {
|
||||
showProviderAction.setHelpLocation(helpLocation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -449,13 +504,56 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience method for setting the provider's icon.
|
||||
* @param icon the icon to use for this provider.
|
||||
* Sets the default key binding that will show this provider when pressed. This value can
|
||||
* be changed by the user and saved as part of the Tool options.
|
||||
*
|
||||
* @param kbData the key binding
|
||||
*/
|
||||
public void setIcon(Icon icon) {
|
||||
this.icon = icon;
|
||||
protected void setKeyBinding(KeyBindingData kbData) {
|
||||
|
||||
if (isInTool()) {
|
||||
dockingTool.getWindowManager().setIcon(this, icon);
|
||||
throw new IllegalStateException(
|
||||
"Cannot set the default key binding after the provider is added to the tool");
|
||||
}
|
||||
|
||||
this.defaultKeyBindingData = kbData;
|
||||
|
||||
if (isTransient && kbData != null) {
|
||||
Msg.error(this, TRANSIENT_PROVIDER_KEY_BINDING_WARNING_MESSAGE,
|
||||
ReflectionUtilities.createJavaFilteredThrowable());
|
||||
this.defaultKeyBindingData = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for setting the provider's icon
|
||||
* @param icon the icon to use for this provider
|
||||
*/
|
||||
protected void setIcon(Icon icon) {
|
||||
this.icon = icon;
|
||||
if (!isInTool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (addToolbarAction && showProviderAction != null) {
|
||||
Objects.requireNonNull(icon, "Icon cannot be set to null when using a toolbar action");
|
||||
showProviderAction.setToolBarData(new ToolBarData(icon));
|
||||
}
|
||||
|
||||
dockingTool.getWindowManager().setIcon(this, icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that this provider's action for showing the provider should appear in the main
|
||||
* toolbar
|
||||
*/
|
||||
protected void addToToolbar() {
|
||||
this.addToolbarAction = true;
|
||||
|
||||
if (isTransient) {
|
||||
Msg.error(this, TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE,
|
||||
ReflectionUtilities.createJavaFilteredThrowable());
|
||||
addToolbarAction = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,7 +563,6 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
* @return the menu group for this provider or null if this provider should appear in the
|
||||
* top-level menu.
|
||||
*/
|
||||
|
||||
public String getWindowSubMenuName() {
|
||||
return windowMenuGroup;
|
||||
}
|
||||
|
@ -473,10 +570,20 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
/**
|
||||
* Returns true if this component goes away during a user session (most providers remain in
|
||||
* the tool all session long, visible or not)
|
||||
* @return true if transitent
|
||||
* @return true if transient
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
return isTransient;
|
||||
return isTransient || isSnapshot();
|
||||
}
|
||||
|
||||
/**
|
||||
* A special marker that indicates this provider is a snapshot of a primary provider,
|
||||
* somewhat like a picture of the primary provider.
|
||||
*
|
||||
* @return true if a snapshot
|
||||
*/
|
||||
public boolean isSnapshot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -485,6 +592,25 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
*/
|
||||
protected void setTransient() {
|
||||
isTransient = true;
|
||||
|
||||
if (isInTool()) {
|
||||
throw new IllegalStateException(
|
||||
"A component provider cannot be marked as 'transient' " +
|
||||
"after it is added to the tool");
|
||||
}
|
||||
|
||||
// avoid visually disturbing the user by adding/removing toolbar actions for temp providers
|
||||
if (addToolbarAction) {
|
||||
addToolbarAction = false;
|
||||
Msg.error(this, TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE,
|
||||
ReflectionUtilities.createJavaFilteredThrowable());
|
||||
}
|
||||
|
||||
if (defaultKeyBindingData != null) {
|
||||
defaultKeyBindingData = null;
|
||||
Msg.error(this, TRANSIENT_PROVIDER_KEY_BINDING_WARNING_MESSAGE,
|
||||
ReflectionUtilities.createJavaFilteredThrowable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -591,6 +717,22 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
return name + " - " + getTitle() + " - " + getSubTitle();
|
||||
}
|
||||
|
||||
private void recordInception() {
|
||||
if (!SystemUtilities.isInDevelopmentMode()) {
|
||||
inceptionInformation = "";
|
||||
return;
|
||||
}
|
||||
|
||||
inceptionInformation = getInceptionFromTheFirstClassThatIsNotUs();
|
||||
}
|
||||
|
||||
private String getInceptionFromTheFirstClassThatIsNotUs() {
|
||||
Throwable t = ReflectionUtilities.createThrowableWithStackOlderThan(getClass());
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
String classInfo = trace[0].toString();
|
||||
return classInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any registered new provider name for the oldName/oldOwner pair.
|
||||
* @param oldOwner the old owner name
|
||||
|
@ -633,4 +775,38 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
return "owner=" + oldOwner + "name=" + oldName;
|
||||
}
|
||||
|
||||
private class ShowProviderAction extends DockingAction {
|
||||
|
||||
ShowProviderAction(boolean supportsKeyBindings) {
|
||||
super(name, owner,
|
||||
supportsKeyBindings ? KeyBindingType.SHARED : KeyBindingType.UNSUPPORTED);
|
||||
|
||||
if (addToolbarAction) {
|
||||
setToolBarData(new ToolBarData(icon, TOOLBAR_GROUP));
|
||||
}
|
||||
|
||||
if (supportsKeyBindings && defaultKeyBindingData != null) {
|
||||
// this action itself is not 'key binding managed', but the system *will* use
|
||||
// any key binding value we set when connecting 'shared' actions
|
||||
setKeyBindingData(defaultKeyBindingData);
|
||||
}
|
||||
|
||||
setDescription("Display " + name);
|
||||
HelpLocation providerHelp = ComponentProvider.this.getHelpLocation();
|
||||
if (providerHelp != null) {
|
||||
setHelpLocation(providerHelp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
dockingTool.showComponentProvider(ComponentProvider.this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInceptionFromTheFirstClassThatIsNotUs() {
|
||||
// overridden to show who created the provider, as that is what this action represents
|
||||
return inceptionInformation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class DialogComponentProviderPopupActionManager {
|
|||
return;
|
||||
}
|
||||
|
||||
ActionToGuiMapper actionManager = dwm.getActionManager();
|
||||
ActionToGuiMapper actionManager = dwm.getActionToGuiMapper();
|
||||
MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap();
|
||||
MenuManager menuMgr =
|
||||
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
|
||||
|
@ -102,7 +102,7 @@ public class DialogComponentProviderPopupActionManager {
|
|||
Object source = actionContext.getSourceObject();
|
||||
if (source instanceof DockingActionProviderIf) {
|
||||
DockingActionProviderIf actionProvider = (DockingActionProviderIf) source;
|
||||
List<DockingActionIf> dockingActions = actionProvider.getDockingActions(actionContext);
|
||||
List<DockingActionIf> dockingActions = actionProvider.getDockingActions();
|
||||
for (DockingActionIf action : dockingActions) {
|
||||
MenuData popupMenuData = action.getPopupMenuData();
|
||||
if (popupMenuData != null && action.isValidContext(actionContext) &&
|
||||
|
|
|
@ -65,7 +65,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
|
|||
this.componentInfo = placeholder;
|
||||
|
||||
winMgr = placeholder.getNode().winMgr;
|
||||
actionMgr = winMgr.getActionManager();
|
||||
actionMgr = winMgr.getActionToGuiMapper();
|
||||
|
||||
popupListener = new MouseAdapter() {
|
||||
@Override
|
||||
|
|
|
@ -87,13 +87,12 @@ public class DockableHeader extends GenericHeader
|
|||
private boolean isDocking;
|
||||
|
||||
private Animator focusAnimator;
|
||||
private int focusToggle = -1;
|
||||
|
||||
/**
|
||||
* Constructs a new DockableHeader for the given dockableComponent.
|
||||
*
|
||||
* @param dockableComp
|
||||
* the dockableComponent that this header is for.
|
||||
* @param dockableComp the dockableComponent that this header is for.
|
||||
* @param isDocking true means this widget can be dragged and docked by the user
|
||||
*/
|
||||
DockableHeader(DockableComponent dockableComp, boolean isDocking) {
|
||||
this.dockComp = dockableComp;
|
||||
|
@ -173,8 +172,11 @@ public class DockableHeader extends GenericHeader
|
|||
}
|
||||
|
||||
protected Animator createEmphasizingAnimator(JFrame parentFrame) {
|
||||
focusToggle += 1;
|
||||
switch (focusToggle) {
|
||||
|
||||
double random = Math.random();
|
||||
int choices = 4;
|
||||
int value = (int) (choices * random);
|
||||
switch (value) {
|
||||
case 0:
|
||||
return AnimationUtils.shakeComponent(component);
|
||||
case 1:
|
||||
|
@ -182,7 +184,6 @@ public class DockableHeader extends GenericHeader
|
|||
case 2:
|
||||
return raiseComponent(parentFrame);
|
||||
default:
|
||||
focusToggle = -1;
|
||||
return AnimationUtils.pulseComponent(component);
|
||||
}
|
||||
}
|
||||
|
@ -226,12 +227,12 @@ public class DockableHeader extends GenericHeader
|
|||
if (!isDocking) {
|
||||
return;
|
||||
}
|
||||
// check input event: if any button other than MB1 is pressed,
|
||||
// don't attempt to process the drag and drop event.
|
||||
|
||||
// if any button other than MB1 is pressed, don't attempt to process the drag and drop event
|
||||
InputEvent ie = event.getTriggerEvent();
|
||||
int modifiers = ie.getModifiers();
|
||||
if ((modifiers & InputEvent.BUTTON2_MASK) != 0 ||
|
||||
(modifiers & InputEvent.BUTTON3_MASK) != 0) {
|
||||
int modifiers = ie.getModifiersEx();
|
||||
if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0 ||
|
||||
(modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0) {
|
||||
return;
|
||||
}
|
||||
DockableComponent.DROP_CODE = DockableComponent.DropCode.WINDOW;
|
||||
|
|
|
@ -60,7 +60,7 @@ class DockableToolBarManager {
|
|||
ComponentPlaceholder placeholder = dockableComp.getComponentWindowingPlaceholder();
|
||||
DockingWindowManager winMgr =
|
||||
dockableComp.getComponentWindowingPlaceholder().getNode().winMgr;
|
||||
ActionToGuiMapper actionManager = winMgr.getActionManager();
|
||||
ActionToGuiMapper actionManager = winMgr.getActionToGuiMapper();
|
||||
menuGroupMap = actionManager.getMenuGroupMap();
|
||||
|
||||
MenuHandler menuHandler = actionManager.getMenuHandler();
|
||||
|
@ -168,7 +168,8 @@ class DockableToolBarManager {
|
|||
private DockableComponent dockableComponent;
|
||||
|
||||
ToolBarCloseAction(DockableComponent dockableComponent) {
|
||||
super("Close Window", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
super("Close Window", DockingWindowManager.DOCKING_WINDOWS_OWNER,
|
||||
KeyBindingType.UNSUPPORTED);
|
||||
this.dockableComponent = dockableComponent;
|
||||
setDescription("Close Window");
|
||||
setToolBarData(new ToolBarData(closeIcon, null));
|
||||
|
|
|
@ -200,8 +200,8 @@ public class DockingActionProxy
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeyBindingManaged() {
|
||||
return dockingAction.isKeyBindingManaged();
|
||||
public KeyBindingType getKeyBindingType() {
|
||||
return dockingAction.getKeyBindingType();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +15,18 @@
|
|||
*/
|
||||
package docking;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
|
||||
/**
|
||||
* A listener to be notified when the tool's context changes. Normally context is used to
|
||||
* manage {@link DockingActionIf} enablement directly by the system. This class allows
|
||||
* clients to listen to context change as well.
|
||||
*/
|
||||
public interface DockingContextListener {
|
||||
void contextChanged(ActionContext context);
|
||||
|
||||
/**
|
||||
* Called when the context changes
|
||||
* @param context the context
|
||||
*/
|
||||
public void contextChanged(ActionContext context);
|
||||
}
|
||||
|
|
|
@ -15,42 +15,28 @@
|
|||
*/
|
||||
package docking;
|
||||
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.StringUtilities;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
/**
|
||||
* A class that can be used as an interface for using actions associated with keybindings. This
|
||||
* class is meant to only by used by internal Ghidra key event processing.
|
||||
*/
|
||||
public class DockingKeyBindingAction extends AbstractAction {
|
||||
|
||||
private static final String RELEASED = "released";
|
||||
private static final String TYPED = "typed";
|
||||
private static final String PRESSED = "pressed";
|
||||
|
||||
private static final String SHIFT = "Shift";
|
||||
private static final String CTRL = "Ctrl";
|
||||
private static final String CONTROL = "Control";
|
||||
private static final String ALT = "Alt";
|
||||
private static final String META = "Meta";
|
||||
private static final String MODIFIER_SEPARATOR = "-";
|
||||
public abstract class DockingKeyBindingAction extends AbstractAction {
|
||||
|
||||
private DockingActionIf docakbleAction;
|
||||
|
||||
protected KeyStroke keyStroke;
|
||||
protected final DockingWindowManager winMgr;
|
||||
protected final KeyStroke keyStroke;
|
||||
protected final DockingTool tool;
|
||||
|
||||
public DockingKeyBindingAction(DockingWindowManager winMgr, DockingActionIf action,
|
||||
KeyStroke keyStroke) {
|
||||
super(parseKeyStroke(keyStroke));
|
||||
this.winMgr = winMgr;
|
||||
public DockingKeyBindingAction(DockingTool tool, DockingActionIf action, KeyStroke keyStroke) {
|
||||
super(KeyBindingUtils.parseKeyStroke(keyStroke));
|
||||
this.tool = tool;
|
||||
this.docakbleAction = action;
|
||||
this.keyStroke = keyStroke;
|
||||
}
|
||||
|
@ -65,18 +51,16 @@ public class DockingKeyBindingAction extends AbstractAction {
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean isReservedKeybindingPrecedence() {
|
||||
return getKeyBindingPrecedence() == KeyBindingPrecedence.ReservedActionsLevel;
|
||||
}
|
||||
public abstract KeyBindingPrecedence getKeyBindingPrecedence();
|
||||
|
||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||
return KeyBindingPrecedence.ReservedActionsLevel;
|
||||
public boolean isReservedKeybindingPrecedence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
winMgr.setStatusText("");
|
||||
ComponentProvider provider = winMgr.getActiveComponentProvider();
|
||||
tool.setStatusInfo("");
|
||||
ComponentProvider provider = tool.getActiveComponentProvider();
|
||||
ActionContext context = getLocalContext(provider);
|
||||
context.setSource(e.getSource());
|
||||
docakbleAction.actionPerformed(context);
|
||||
|
@ -94,147 +78,4 @@ public class DockingKeyBindingAction extends AbstractAction {
|
|||
|
||||
return new ActionContext(localProvider, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the toString() form of the keyStroke.
|
||||
* <br>In Java 1.4.2 & earlier, Ctrl-M is returned as "keyCode CtrlM-P"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
* <br>In Java 1.5.0, Ctrl-M is returned as "ctrl pressed M"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
*/
|
||||
public static String parseKeyStroke(KeyStroke keyStroke) {
|
||||
final String keyPressSuffix = "-P";
|
||||
|
||||
String keyString = keyStroke.toString();
|
||||
int type = keyStroke.getKeyEventType();
|
||||
if (type == KeyEvent.KEY_TYPED) {
|
||||
return String.valueOf(keyStroke.getKeyChar());
|
||||
}
|
||||
|
||||
// get the character used in the key stroke
|
||||
int firstIndex = keyString.lastIndexOf(' ') + 1;
|
||||
int ctrlIndex = keyString.indexOf(CTRL, firstIndex);
|
||||
if (ctrlIndex >= 0) {
|
||||
firstIndex = ctrlIndex + CTRL.length();
|
||||
}
|
||||
int altIndex = keyString.indexOf(ALT, firstIndex);
|
||||
if (altIndex >= 0) {
|
||||
firstIndex = altIndex + ALT.length();
|
||||
}
|
||||
int shiftIndex = keyString.indexOf(SHIFT, firstIndex);
|
||||
if (shiftIndex >= 0) {
|
||||
firstIndex = shiftIndex + SHIFT.length();
|
||||
}
|
||||
int metaIndex = keyString.indexOf(META, firstIndex);
|
||||
if (metaIndex >= 0) {
|
||||
firstIndex = metaIndex + META.length();
|
||||
}
|
||||
|
||||
int lastIndex = keyString.length();
|
||||
if (keyString.endsWith(keyPressSuffix)) {
|
||||
lastIndex -= keyPressSuffix.length();
|
||||
}
|
||||
if (lastIndex >= 0) {
|
||||
keyString = keyString.substring(firstIndex, lastIndex);
|
||||
}
|
||||
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
|
||||
buffer.insert(0, SHIFT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if ((modifiers & InputEvent.ALT_MASK) != 0) {
|
||||
buffer.insert(0, ALT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if ((modifiers & InputEvent.CTRL_MASK) != 0) {
|
||||
buffer.insert(0, CTRL + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if ((modifiers & InputEvent.META_MASK) != 0) {
|
||||
buffer.insert(0, META + MODIFIER_SEPARATOR);
|
||||
}
|
||||
buffer.append(keyString);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given text into a KeyStroke. This method relies upon
|
||||
* {@link KeyStroke#getKeyStroke(String)} for parsing. Before making that call, this method
|
||||
* will perform fixup on the given text for added flexibility. For example, the given
|
||||
* text may contain spaces or dashes as the separators between parts in the string. Also,
|
||||
* the text is converted such that it is not case-sensitive. So, the following example
|
||||
* formats are allowed:
|
||||
* <pre>
|
||||
* Alt-F
|
||||
* alt p
|
||||
* Ctrl-Alt-Z
|
||||
* ctrl Z
|
||||
* </pre>
|
||||
*
|
||||
* @param keyStroke
|
||||
* @return
|
||||
*/
|
||||
public static KeyStroke parseKeyStroke(String keyStroke) {
|
||||
List<String> pieces = new ArrayList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(keyStroke, "- ");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String token = tokenizer.nextToken();
|
||||
if (!pieces.contains(token)) {
|
||||
pieces.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuffer keyStrokeBuff = new StringBuffer();
|
||||
for (Iterator<String> iterator = pieces.iterator(); iterator.hasNext();) {
|
||||
String piece = iterator.next();
|
||||
if (StringUtilities.indexOfIgnoreCase(piece, SHIFT) != -1) {
|
||||
keyStrokeBuff.append("shift ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, CTRL) != -1) {
|
||||
keyStrokeBuff.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, CONTROL) != -1) {
|
||||
keyStrokeBuff.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, ALT) != -1) {
|
||||
keyStrokeBuff.append("alt ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, META) != -1) {
|
||||
keyStrokeBuff.append("meta ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, PRESSED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, TYPED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, RELEASED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
keyStrokeBuff.append(PRESSED).append(' ');
|
||||
|
||||
// at this point we should only have left one piece--the key ID
|
||||
int leftover = pieces.size();
|
||||
if (leftover > 1 || leftover == 0) {
|
||||
Msg.warn(DockingKeyBindingAction.class, "Invalid keystroke string found. Expected " +
|
||||
"format of '[modifier] ... key'. Found: '" + keyStroke + "'");
|
||||
|
||||
if (leftover == 0) {
|
||||
return null; // nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
String key = pieces.get(0);
|
||||
keyStrokeBuff.append(key.toUpperCase());
|
||||
|
||||
return KeyStroke.getKeyStroke(keyStrokeBuff.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Set;
|
|||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.DockingToolActions;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
|
||||
/**
|
||||
|
@ -151,6 +152,12 @@ public interface DockingTool {
|
|||
*/
|
||||
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner);
|
||||
|
||||
/**
|
||||
* Returns the active component provider, that which has focus
|
||||
* @return the active provider
|
||||
*/
|
||||
public ComponentProvider getActiveComponentProvider();
|
||||
|
||||
/**
|
||||
* Shows or hides the component provider in the tool
|
||||
* @param componentProvider the provider to either show or hide.
|
||||
|
@ -209,6 +216,27 @@ public interface DockingTool {
|
|||
*/
|
||||
public void contextChanged(ComponentProvider provider);
|
||||
|
||||
/**
|
||||
* Returns this tool's notion of the current action context, which is based upon the active
|
||||
* {@link ComponentProvider}. If there is not active provider, then a generic context will
|
||||
* be returned.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
public ActionContext getGlobalContext();
|
||||
|
||||
/**
|
||||
* Adds the given context listener to this tool
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public void addContextListener(DockingContextListener listener);
|
||||
|
||||
/**
|
||||
* Removes the given context listener to this tool
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public void removeContextListener(DockingContextListener listener);
|
||||
|
||||
/**
|
||||
* Returns the DockingWindowManger for this tool.
|
||||
* @return the DockingWindowManger for this tool.
|
||||
|
@ -235,4 +263,14 @@ public interface DockingTool {
|
|||
*/
|
||||
public boolean hasConfigChanged();
|
||||
|
||||
/**
|
||||
* Returns the class that manages actions for the tool.
|
||||
*
|
||||
* <p>Most clients will not need to use this methods. Instead, actions should be added to
|
||||
* the tool via {@link #addAction(DockingActionIf)} and
|
||||
* {@link #addLocalAction(ComponentProvider, DockingActionIf)}.
|
||||
*
|
||||
* @return the action manager
|
||||
*/
|
||||
public DockingToolActions getToolActions();
|
||||
}
|
||||
|
|
|
@ -26,16 +26,18 @@ import java.util.Map.Entry;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.DockingToolActions;
|
||||
import docking.actions.ToolActions;
|
||||
import docking.help.HelpService;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
import ghidra.framework.options.PreferenceState;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
|
@ -45,16 +47,14 @@ import util.CollectionUtils;
|
|||
* Manages the "Docking" arrangement of a set of components and actions. The components can be "docked"
|
||||
* together or exist in their own window. Actions can be associated with components so they
|
||||
* "move" with the component as it moved from one location to another.
|
||||
*
|
||||
* <P>
|
||||
* Components are added via ComponentProviders. A ComponentProvider is an interface for getting
|
||||
* a component and its related information. The docking window manager will get the component
|
||||
* from the provider as needed. It is up to the provider if it wants to reuse the component or
|
||||
* recreate a new one when the component is requested. When the user "hides" (by using
|
||||
* the x button on the component area) a component, the docking window manager removes all
|
||||
* recreate a new one when the component is requested. When the user hides a component (by using
|
||||
* the x button on the component header), the docking window manager removes all
|
||||
* knowledge of the component and will request it again from the provider if the component
|
||||
* becomes "unhidden". The provider is also notified whenever a component is hidden. Some
|
||||
* providers will use the notification to remove the provider from the docking window manager so
|
||||
* that the can not "unhide" the component using the built-in window menu.
|
||||
* is again shown. The provider is also notified whenever a component is hidden and shown.
|
||||
*/
|
||||
public class DockingWindowManager implements PropertyChangeListener, PlaceholderInstaller {
|
||||
|
||||
|
@ -73,6 +73,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
|
||||
private static List<DockingWindowManager> instanceList = new ArrayList<>();
|
||||
|
||||
private DockingTool tool;
|
||||
private RootNode root;
|
||||
|
||||
private PlaceholderManager placeholderManager;
|
||||
|
@ -87,7 +88,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
private Map<String, ComponentProvider> providerNameCache = new HashMap<>();
|
||||
private Map<String, PreferenceState> preferenceStateMap = new HashMap<>();
|
||||
private DockWinListener docListener;
|
||||
private ActionToGuiMapper actionManager;
|
||||
private ActionToGuiMapper actionToGuiMapper;
|
||||
|
||||
private WeakSet<DockingContextListener> contextListeners =
|
||||
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
|
||||
|
@ -105,18 +106,18 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
|
||||
/**
|
||||
* Constructs a new DockingWindowManager
|
||||
* @param toolName the name of the tool.
|
||||
* @param tool the tool
|
||||
* @param images the images to use for windows in this window manager
|
||||
* @param docListener the listener to be notified when the user closes the manager.
|
||||
* @param docListener the listener to be notified when the user closes the manager
|
||||
*/
|
||||
public DockingWindowManager(String toolName, List<Image> images, DockWinListener docListener) {
|
||||
this(toolName, images, docListener, false, true, true, null);
|
||||
public DockingWindowManager(DockingTool tool, List<Image> images, DockWinListener docListener) {
|
||||
this(tool, images, docListener, false, true, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DockingWindowManager
|
||||
*
|
||||
* @param toolName the name of the tool
|
||||
* @param tool the tool
|
||||
* @param images the list of icons to set on the window
|
||||
* @param docListener the listener to be notified when the user closes the manager
|
||||
* @param modal if true then the root window will be a modal dialog instead of a frame
|
||||
|
@ -125,11 +126,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* @param hasStatusBar if true a status bar will be created for the main window
|
||||
* @param factory the drop target factory
|
||||
*/
|
||||
public DockingWindowManager(String toolName, List<Image> images, DockWinListener docListener,
|
||||
public DockingWindowManager(DockingTool tool, List<Image> images, DockWinListener docListener,
|
||||
boolean modal, boolean isDocking, boolean hasStatusBar, DropTargetFactory factory) {
|
||||
|
||||
KeyBindingOverrideKeyEventDispatcher.install();
|
||||
|
||||
this.tool = tool;
|
||||
this.docListener = docListener;
|
||||
this.isDocking = isDocking;
|
||||
this.hasStatusBar = hasStatusBar;
|
||||
|
@ -137,8 +139,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
images = new ArrayList<>();
|
||||
}
|
||||
|
||||
root = new RootNode(this, toolName, images, modal, factory);
|
||||
actionManager = new ActionToGuiMapper(this);
|
||||
root = new RootNode(this, tool.getName(), images, modal, factory);
|
||||
|
||||
KeyboardFocusManager km = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
km.addPropertyChangeListener("permanentFocusOwner", this);
|
||||
|
@ -146,6 +147,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
addInstance(this);
|
||||
|
||||
placeholderManager = new PlaceholderManager(this);
|
||||
actionToGuiMapper = new ActionToGuiMapper(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,15 +155,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return "DockingWindowManager: " + root.getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* A static initializer allowing additional diagnostic actions
|
||||
* to be enabled added to all frame and dialog windows.
|
||||
* @param enable
|
||||
*/
|
||||
public static void enableDiagnosticActions(boolean enable) {
|
||||
ActionToGuiMapper.enableDiagnosticActions(enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the help service for the all docking window managers.
|
||||
* @param helpSvc the help service to use.
|
||||
|
@ -311,17 +304,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
root.setIcon(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any action that is bound to the given keystroke for the tool associated with this
|
||||
* DockingWindowManager instance.
|
||||
* @param keyStroke The keystroke to check for key bindings.
|
||||
* @return The action that is bound to the keystroke, or null of there is no binding for the
|
||||
* given keystroke.
|
||||
*/
|
||||
Action getActionForKeyStroke(KeyStroke keyStroke) {
|
||||
return actionManager.getDockingKeyAction(keyStroke);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this manager contains the given provider.
|
||||
*
|
||||
|
@ -336,8 +318,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return placeholderManager;
|
||||
}
|
||||
|
||||
ActionToGuiMapper getActionManager() {
|
||||
return actionManager;
|
||||
ActionToGuiMapper getActionToGuiMapper() {
|
||||
return actionToGuiMapper;
|
||||
}
|
||||
|
||||
RootNode getRootNode() {
|
||||
|
@ -363,6 +345,13 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
defaultProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this tool's notion of the current action context, which is based upon the active
|
||||
* {@link ComponentProvider}. If there is not active provider, then a generic context will
|
||||
* be returned.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
public ActionContext getGlobalContext() {
|
||||
if (defaultProvider != null) {
|
||||
ActionContext actionContext = defaultProvider.getActionContext(null);
|
||||
|
@ -641,16 +630,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
placeholderManager.removeComponent(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all components and actions associated with the given owner.
|
||||
* @param owner the name of the owner whose associated component and actions should be removed.
|
||||
*/
|
||||
public void removeAll(String owner) {
|
||||
actionManager.removeAll(owner);
|
||||
placeholderManager.removeAll(owner);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Package-level Action Methods
|
||||
//==================================================================================================
|
||||
|
@ -668,7 +647,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
||||
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
||||
if (placeholder != null) {
|
||||
actionManager.removeLocalAction(action);
|
||||
placeholder.removeAction(action);
|
||||
}
|
||||
}
|
||||
|
@ -679,22 +657,43 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
throw new IllegalArgumentException("Unknown component provider: " + provider);
|
||||
}
|
||||
placeholder.addAction(action);
|
||||
actionManager.addLocalAction(action, provider);
|
||||
}
|
||||
|
||||
void addToolAction(DockingActionIf action) {
|
||||
actionManager.addToolAction(action);
|
||||
actionToGuiMapper.addToolAction(action);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
void removeToolAction(DockingActionIf action) {
|
||||
actionManager.removeToolAction(action);
|
||||
actionToGuiMapper.removeToolAction(action);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any action that is bound to the given keystroke for the tool associated with this
|
||||
* DockingWindowManager instance.
|
||||
* @param keyStroke The keystroke to check for key bindings.
|
||||
* @return The action that is bound to the keystroke, or null of there is no binding for the
|
||||
* given keystroke.
|
||||
*/
|
||||
Action getActionForKeyStroke(KeyStroke keyStroke) {
|
||||
DockingToolActions toolActions = tool.getToolActions();
|
||||
if (toolActions instanceof ToolActions) {
|
||||
// Using a cast here; it didn't make sense to include this 'getAction' on the
|
||||
// DockingToolActions
|
||||
return ((ToolActions) toolActions).getAction(keyStroke);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// End Package-level Methods
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
public void ownerRemoved(String owner) {
|
||||
placeholderManager.removeAll(owner);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides or shows the component associated with the given provider.
|
||||
|
@ -711,6 +710,15 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
||||
if (placeholder != null) {
|
||||
showComponent(placeholder, visibleState, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (visibleState) {
|
||||
|
||||
// a null placeholder implies the client is trying to show a provider that has not
|
||||
// been added to the tool
|
||||
Msg.warn(this, "Attempting to show an unknown Component Provider '" +
|
||||
provider.getName() + "' - " + "check that the provider has been added to the tool");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -791,7 +799,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
mgr.removePropertyChangeListener("permanentFocusOwner", this);
|
||||
|
||||
actionManager.dispose();
|
||||
actionToGuiMapper.dispose();
|
||||
root.dispose();
|
||||
|
||||
placeholderManager.disposePlaceholders();
|
||||
|
@ -990,11 +998,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
}
|
||||
|
||||
private void disposePlaceholder(ComponentPlaceholder placeholder, boolean keepAround) {
|
||||
Iterator<DockingActionIf> iter = placeholder.getActions();
|
||||
while (iter.hasNext()) {
|
||||
DockingActionIf action = iter.next();
|
||||
actionManager.removeLocalAction(action);
|
||||
}
|
||||
placeholder.removeAllActions();
|
||||
|
||||
ComponentNode node = placeholder.getNode();
|
||||
if (node == null) {
|
||||
|
@ -1084,10 +1088,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return;
|
||||
}
|
||||
|
||||
actionManager.removeAll(DOCKING_WINDOWS_OWNER);
|
||||
tool.getToolActions().removeActions(DOCKING_WINDOWS_OWNER);
|
||||
|
||||
Map<String, List<ComponentPlaceholder>> permanentMap = new HashMap<>();
|
||||
Map<String, List<ComponentPlaceholder>> transientMap = new HashMap<>();
|
||||
Map<String, List<ComponentPlaceholder>> permanentMap =
|
||||
LazyMap.lazyMap(new HashMap<>(), menuName -> new ArrayList<>());
|
||||
Map<String, List<ComponentPlaceholder>> transientMap =
|
||||
LazyMap.lazyMap(new HashMap<>(), menuName -> new ArrayList<>());
|
||||
|
||||
Map<ComponentProvider, ComponentPlaceholder> map =
|
||||
placeholderManager.getActiveProvidersToPlaceholders();
|
||||
|
@ -1097,21 +1103,21 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
ComponentPlaceholder placeholder = entry.getValue();
|
||||
|
||||
String subMenuName = provider.getWindowSubMenuName();
|
||||
if (provider.isTransient()) {
|
||||
addToMap(transientMap, subMenuName, placeholder);
|
||||
if (provider.isTransient() && !provider.isSnapshot()) {
|
||||
transientMap.get(subMenuName).add(placeholder);
|
||||
}
|
||||
else {
|
||||
addToMap(permanentMap, subMenuName, placeholder);
|
||||
permanentMap.get(subMenuName).add(placeholder);
|
||||
}
|
||||
}
|
||||
promoteSingleMenuGroups(permanentMap);
|
||||
promoteSingleMenuGroups(transientMap);
|
||||
|
||||
createActions(transientMap, true);
|
||||
createActions(permanentMap, false);
|
||||
createActions(transientMap);
|
||||
createActions(permanentMap);
|
||||
createWindowActions();
|
||||
|
||||
actionManager.update();
|
||||
actionToGuiMapper.update();
|
||||
}
|
||||
|
||||
private boolean isWindowMenuShowing() {
|
||||
|
@ -1138,47 +1144,45 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return null;
|
||||
}
|
||||
|
||||
private void createActions(Map<String, List<ComponentPlaceholder>> map, boolean isTransient) {
|
||||
private void createActions(Map<String, List<ComponentPlaceholder>> map) {
|
||||
List<ShowComponentAction> actionList = new ArrayList<>();
|
||||
for (String subMenuName : map.keySet()) {
|
||||
List<ComponentPlaceholder> placeholders = map.get(subMenuName);
|
||||
for (ComponentPlaceholder placeholder : placeholders) {
|
||||
ComponentProvider provider = placeholder.getProvider();
|
||||
boolean isTransient = provider.isTransient();
|
||||
actionList.add(
|
||||
new ShowComponentAction(this, placeholder, subMenuName, isTransient));
|
||||
}
|
||||
|
||||
if (subMenuName != null) {
|
||||
// add an 'add all' action for the sub-menu
|
||||
actionList.add(new ShowAllComponentsAction(this, placeholders, subMenuName));
|
||||
}
|
||||
}
|
||||
|
||||
DockingToolActions toolActions = tool.getToolActions();
|
||||
Collections.sort(actionList);
|
||||
for (ShowComponentAction action : actionList) {
|
||||
actionManager.addToolAction(action);
|
||||
toolActions.addGlobalAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
private void promoteSingleMenuGroups(Map<String, List<ComponentPlaceholder>> map) {
|
||||
List<String> lists = new ArrayList<>(map.keySet());
|
||||
private void promoteSingleMenuGroups(Map<String, List<ComponentPlaceholder>> lazyMap) {
|
||||
List<String> lists = new ArrayList<>(lazyMap.keySet());
|
||||
for (String key : lists) {
|
||||
List<ComponentPlaceholder> list = map.get(key);
|
||||
if (key != null && list.size() == 1) {
|
||||
addToMap(map, null, list.get(0));
|
||||
map.remove(key);
|
||||
if (key == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<ComponentPlaceholder> list = lazyMap.get(key);
|
||||
if (list.size() == 1) {
|
||||
lazyMap.get(null /*submenu name*/).add(list.get(0));
|
||||
lazyMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addToMap(Map<String, List<ComponentPlaceholder>> map, String menuGroup,
|
||||
ComponentPlaceholder placeholder) {
|
||||
|
||||
List<ComponentPlaceholder> list = map.get(menuGroup);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
map.put(menuGroup, list);
|
||||
}
|
||||
list.add(placeholder);
|
||||
}
|
||||
|
||||
private void createWindowActions() {
|
||||
List<DetachedWindowNode> windows = root.getDetachedWindows();
|
||||
List<ShowWindowAction> actions = new ArrayList<>();
|
||||
|
@ -1189,9 +1193,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
}
|
||||
}
|
||||
|
||||
DockingToolActions toolActions = tool.getToolActions();
|
||||
Collections.sort(actions);
|
||||
for (ShowWindowAction action : actions) {
|
||||
actionManager.addToolAction(action);
|
||||
toolActions.addGlobalAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1199,6 +1204,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* Notifies the window manager that an update is needed
|
||||
*/
|
||||
void scheduleUpdate() {
|
||||
if (rebuildUpdater.isBusy()) {
|
||||
return;
|
||||
}
|
||||
rebuildUpdater.updateLater();
|
||||
}
|
||||
|
||||
|
@ -1216,7 +1224,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
}
|
||||
|
||||
root.update(); // do this before rebuilding the menu, as new windows may be opened
|
||||
|
||||
buildComponentMenu();
|
||||
SystemUtilities.runSwingLater(() -> updateFocus());
|
||||
}
|
||||
|
@ -1387,7 +1394,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
actionManager.setActive(active);
|
||||
actionToGuiMapper.setActive(active);
|
||||
if (active) {
|
||||
setActiveManager(this);
|
||||
if (focusedPlaceholder != null && root.getWindow(focusedPlaceholder) == window) {
|
||||
|
@ -1938,7 +1945,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* when we know that an update is not need, as we are in the middle of an update.
|
||||
*/
|
||||
void doSetMenuGroup(String[] menuPath, String group) {
|
||||
actionManager.setMenuGroup(menuPath, group);
|
||||
actionToGuiMapper.setMenuGroup(menuPath, group);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1954,7 +1961,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* its level
|
||||
*/
|
||||
public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) {
|
||||
actionManager.setMenuGroup(menuPath, group, menuSubGroup);
|
||||
actionToGuiMapper.setMenuGroup(menuPath, group, menuSubGroup);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
|
@ -2060,7 +2067,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
|
||||
public void contextChanged(ComponentProvider provider) {
|
||||
if (provider == null) {
|
||||
actionManager.contextChangedAll(); // update all windows;
|
||||
actionToGuiMapper.contextChangedAll(); // update all windows;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2069,7 +2076,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return;
|
||||
}
|
||||
placeHolder.contextChanged();
|
||||
actionManager.contextChanged(placeHolder);
|
||||
actionToGuiMapper.contextChanged(placeHolder);
|
||||
}
|
||||
|
||||
public void addContextListener(DockingContextListener listener) {
|
||||
|
@ -2080,8 +2087,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
contextListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void notifyContextListeners(ComponentPlaceholder placeHolder,
|
||||
ActionContext actionContext) {
|
||||
void notifyContextListeners(ComponentPlaceholder placeHolder, ActionContext actionContext) {
|
||||
|
||||
if (placeHolder == focusedPlaceholder) {
|
||||
for (DockingContextListener listener : contextListeners) {
|
||||
|
|
|
@ -17,11 +17,10 @@ package docking;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.menu.MenuGroupMap;
|
||||
import docking.menu.MenuHandler;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
||||
|
||||
|
@ -66,6 +65,17 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
|||
}
|
||||
}
|
||||
|
||||
public DockingActionIf getToolbarAction(String actionName) {
|
||||
|
||||
for (WindowActionManager actionManager : windowToActionManagerMap.values()) {
|
||||
DockingActionIf action = actionManager.getToolbarAction(actionName);
|
||||
if (action != null) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
for (WindowActionManager actionManager : windowToActionManagerMap.values()) {
|
||||
actionManager.dispose();
|
||||
|
@ -130,7 +140,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
|||
}
|
||||
|
||||
private List<DockingActionIf> getActionsForWindow(WindowNode windowNode) {
|
||||
ActionToGuiMapper actionManager = windowManager.getActionManager();
|
||||
ActionToGuiMapper actionManager = windowManager.getActionToGuiMapper();
|
||||
Collection<DockingActionIf> globalActions = actionManager.getGlobalActions();
|
||||
List<DockingActionIf> actionsForWindow = new ArrayList<>(globalActions.size());
|
||||
Set<Class<?>> contextTypes = windowNode.getContextTypes();
|
||||
|
@ -143,12 +153,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
|||
}
|
||||
|
||||
public void contextChangedAll() {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
updateAllWindowActions();
|
||||
}
|
||||
else {
|
||||
SwingUtilities.invokeLater(() -> updateAllWindowActions());
|
||||
}
|
||||
Swing.runIfSwingOrRunLater(this::updateAllWindowActions);
|
||||
}
|
||||
|
||||
private void updateAllWindowActions() {
|
||||
|
|
|
@ -425,7 +425,9 @@ class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher {
|
|||
}
|
||||
|
||||
KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(event);
|
||||
return (DockingKeyBindingAction) activeManager.getActionForKeyStroke(keyStroke);
|
||||
DockingKeyBindingAction bindingAction =
|
||||
(DockingKeyBindingAction) activeManager.getActionForKeyStroke(keyStroke);
|
||||
return bindingAction;
|
||||
}
|
||||
|
||||
private DockingWindowManager getActiveDockingWindowManager() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -22,6 +21,8 @@ import java.awt.event.KeyListener;
|
|||
import javax.swing.JTextField;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
/**
|
||||
* Text field captures key strokes and notifies a listener to process the key entry.
|
||||
*/
|
||||
|
@ -43,21 +44,31 @@ public class KeyEntryTextField extends JTextField {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the current key stroke.
|
||||
* Get the current key stroke
|
||||
* @return the key stroke
|
||||
*/
|
||||
public KeyStroke getCurrentKeyStroke() {
|
||||
public KeyStroke getKeyStroke() {
|
||||
return currentKeyStroke;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Sets the current key stroke
|
||||
* @param ks the new key stroke
|
||||
*/
|
||||
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 DockingKeyBindingAction.parseKeyStroke(ks);
|
||||
return KeyBindingUtils.parseKeyStroke(ks);
|
||||
}
|
||||
|
||||
public void clearField() {
|
||||
|
@ -66,8 +77,6 @@ public class KeyEntryTextField extends JTextField {
|
|||
currentKeyStroke = null;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void processEntry(KeyStroke ks) {
|
||||
ksName = null;
|
||||
|
||||
|
@ -78,7 +87,7 @@ public class KeyEntryTextField extends JTextField {
|
|||
char keyChar = ks.getKeyChar();
|
||||
if (!Character.isWhitespace(keyChar) &&
|
||||
Character.getType(keyChar) != Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE) {
|
||||
ksName = DockingKeyBindingAction.parseKeyStroke(ks);
|
||||
ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
}
|
||||
}
|
||||
listener.processEntry(ks);
|
||||
|
@ -111,7 +120,7 @@ public class KeyEntryTextField extends JTextField {
|
|||
|
||||
KeyStroke keyStroke = null;
|
||||
if (!isClearKey(keyCode) && !isModifiersOnly(e)) {
|
||||
keyStroke = KeyStroke.getKeyStroke(keyCode, e.getModifiersEx() | e.getModifiers());
|
||||
keyStroke = KeyStroke.getKeyStroke(keyCode, e.getModifiersEx());
|
||||
}
|
||||
|
||||
processEntry(keyStroke);
|
||||
|
|
|
@ -101,7 +101,7 @@ public class PopupActionManager implements PropertyChangeListener {
|
|||
Object source = actionContext.getSourceObject();
|
||||
if (source instanceof DockingActionProviderIf) {
|
||||
DockingActionProviderIf actionProvider = (DockingActionProviderIf) source;
|
||||
List<DockingActionIf> dockingActions = actionProvider.getDockingActions(actionContext);
|
||||
List<DockingActionIf> dockingActions = actionProvider.getDockingActions();
|
||||
for (DockingActionIf action : dockingActions) {
|
||||
MenuData popupMenuData = action.getPopupMenuData();
|
||||
if (popupMenuData != null && action.isValidContext(actionContext) &&
|
||||
|
|
|
@ -319,7 +319,7 @@ class RootNode extends WindowNode {
|
|||
invalid = false;
|
||||
}
|
||||
|
||||
winMgr.getActionManager().update();
|
||||
winMgr.getActionToGuiMapper().update();
|
||||
windowWrapper.validate();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@ package docking;
|
|||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.action.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
@ -33,8 +32,10 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
protected static final ImageIcon EMPTY_ICON =
|
||||
ResourceManager.loadImage("images/EmptyIcon16.gif");
|
||||
protected static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME;
|
||||
private ComponentPlaceholder info;
|
||||
|
||||
protected DockingWindowManager winMgr;
|
||||
private ComponentPlaceholder info;
|
||||
private String title;
|
||||
|
||||
private static String truncateTitleAsNeeded(String title) {
|
||||
if (title.length() <= MAX_LENGTH) {
|
||||
|
@ -48,36 +49,36 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
super(truncateTitleAsNeeded(name), DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ShowComponentAction object.
|
||||
* @param winMgr the DockingWindowManager that this action belongs to.
|
||||
* @param info the info of the component to be shown when this action is invoked.
|
||||
*/
|
||||
ShowComponentAction(DockingWindowManager winMgr, ComponentPlaceholder info, String subMenuName,
|
||||
boolean isTransient) {
|
||||
super(truncateTitleAsNeeded(info.getTitle()), DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
ShowComponentAction(DockingWindowManager winMgr, ComponentPlaceholder placeholder,
|
||||
String subMenuName, boolean isTransient) {
|
||||
super(placeholder.getProvider().getName(), DockingWindowManager.DOCKING_WINDOWS_OWNER,
|
||||
createKeyBindingType(isTransient, placeholder));
|
||||
|
||||
this.info = placeholder;
|
||||
this.winMgr = winMgr;
|
||||
this.title = truncateTitleAsNeeded(placeholder.getTitle());
|
||||
String group = isTransient ? "Transient" : "Permanent";
|
||||
|
||||
Icon icon = info.getIcon();
|
||||
Icon icon = placeholder.getIcon();
|
||||
if (icon == null) {
|
||||
icon = EMPTY_ICON;
|
||||
}
|
||||
|
||||
if (subMenuName != null) {
|
||||
setMenuBarData(new MenuData(
|
||||
new String[] { MENU_WINDOW, subMenuName, info.getFullTitle() }, icon, "Permanent"));
|
||||
setMenuBarData(
|
||||
new MenuData(new String[] { MENU_WINDOW, subMenuName, placeholder.getFullTitle() },
|
||||
icon, "Permanent"));
|
||||
winMgr.doSetMenuGroup(new String[] { MENU_WINDOW, subMenuName }, group);
|
||||
}
|
||||
else {
|
||||
setMenuBarData(
|
||||
new MenuData(new String[] { MENU_WINDOW, getName() }, icon, "Permanent"));
|
||||
setMenuBarData(new MenuData(new String[] { MENU_WINDOW, title }, icon, "Permanent"));
|
||||
}
|
||||
|
||||
this.info = info;
|
||||
this.winMgr = winMgr;
|
||||
// keybinding data used to show the binding in the menu
|
||||
ComponentProvider provider = placeholder.getProvider();
|
||||
synchronizeKeyBinding(provider);
|
||||
|
||||
// Use provider Help for this action
|
||||
ComponentProvider provider = info.getProvider();
|
||||
// Use provider Help for this action
|
||||
HelpLocation helpLocation = provider.getHelpLocation();
|
||||
if (helpLocation != null) {
|
||||
setHelpLocation(helpLocation);
|
||||
|
@ -89,6 +90,32 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given provider's key binding matches this class's key binding
|
||||
* @param provider the provider
|
||||
*/
|
||||
private void synchronizeKeyBinding(ComponentProvider provider) {
|
||||
DockingActionIf action = provider.getShowProviderAction();
|
||||
KeyBindingData defaultBinding = action.getDefaultKeyBindingData();
|
||||
setKeyBindingData(defaultBinding);
|
||||
|
||||
KeyBindingData kbData = action.getKeyBindingData();
|
||||
if (kbData != null) {
|
||||
setUnvalidatedKeyBindingData(kbData);
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyBindingType createKeyBindingType(boolean isTransient,
|
||||
ComponentPlaceholder placeholder) {
|
||||
|
||||
if (isTransient) {
|
||||
return KeyBindingType.UNSUPPORTED; // temporary window
|
||||
}
|
||||
|
||||
// 'info' is null when this action is used to 'show all' instances of a given provider
|
||||
return placeholder == null ? KeyBindingType.UNSUPPORTED : KeyBindingType.SHARED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
winMgr.showComponent(info, true, true);
|
||||
|
@ -121,15 +148,23 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
|
||||
@Override
|
||||
public String getHelpInfo() {
|
||||
if (info == null) {
|
||||
return super.getHelpInfo();
|
||||
}
|
||||
|
||||
StringBuilder buffy = new StringBuilder(super.getHelpInfo());
|
||||
|
||||
Class<? extends ComponentProvider> clazz = info.getProvider().getClass();
|
||||
ComponentProvider provider = info.getProvider();
|
||||
Class<? extends ComponentProvider> clazz = provider.getClass();
|
||||
String className = clazz.getName();
|
||||
String filename = className.substring(className.lastIndexOf('.') + 1);
|
||||
String javaName = filename + ".java";
|
||||
|
||||
buffy.append(" ").append("PROVIDER: ").append(info.getName()).append(' ');
|
||||
buffy.append('(').append(javaName).append(":1)");
|
||||
DockingActionIf showAction = provider.getShowProviderAction();
|
||||
String realInception = showAction.getInceptionInformation();
|
||||
buffy.append(" ").append(realInception).append("\n ");
|
||||
|
||||
buffy.append(" ").append("PROVIDER: ").append(filename).append(' ');
|
||||
buffy.append('(').append(provider.getOwner()).append(")");
|
||||
buffy.append("\n ");
|
||||
|
||||
return buffy.toString();
|
||||
|
|
|
@ -72,6 +72,10 @@ public class WindowActionManager {
|
|||
}
|
||||
}
|
||||
|
||||
public DockingActionIf getToolbarAction(String actionName) {
|
||||
return toolBarMgr.getAction(actionName);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
JMenuBar menuBar = menuBarMgr.getMenuBar();
|
||||
if (menuBar.getMenuCount() > 0) {
|
||||
|
@ -92,9 +96,6 @@ public class WindowActionManager {
|
|||
toolBarMgr.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the window manager that an action context update is needed.
|
||||
*/
|
||||
synchronized void contextChanged(ComponentPlaceholder placeHolder) {
|
||||
placeHolderForScheduledActionUpdate = placeHolder;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ package docking.action;
|
|||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.*;
|
||||
|
@ -63,8 +64,8 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
private String inceptionInformation;
|
||||
|
||||
private boolean isEnabled = true;
|
||||
private boolean isKeyBindingManaged = true;
|
||||
|
||||
private KeyBindingType keyBindingType = KeyBindingType.INDIVIDUAL;
|
||||
private KeyBindingData defaultKeyBindingData;
|
||||
private KeyBindingData keyBindingData;
|
||||
private MenuBarData menuBarData;
|
||||
|
@ -72,19 +73,29 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
private ToolBarData toolBarData;
|
||||
|
||||
public DockingAction(String name, String owner) {
|
||||
this(name, owner, true);
|
||||
}
|
||||
|
||||
public DockingAction(String name, String owner, boolean isKeyBindingManaged) {
|
||||
this.name = name;
|
||||
this.owner = owner;
|
||||
this.isKeyBindingManaged = isKeyBindingManaged;
|
||||
|
||||
recordInception();
|
||||
HelpLocation location = new HelpLocation(owner, name, inceptionInformation);
|
||||
setHelpLocation(location);
|
||||
}
|
||||
|
||||
public DockingAction(String name, String owner, KeyBindingType kbType) {
|
||||
this(name, owner);
|
||||
this.keyBindingType = Objects.requireNonNull(kbType);
|
||||
}
|
||||
|
||||
public DockingAction(String name, String owner, boolean supportsKeyBindings) {
|
||||
this(name, owner);
|
||||
this.keyBindingType =
|
||||
supportsKeyBindings ? KeyBindingType.INDIVIDUAL : KeyBindingType.UNSUPPORTED;
|
||||
}
|
||||
|
||||
protected KeyBindingType getPreferredKeyBindingType() {
|
||||
return KeyBindingType.INDIVIDUAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void actionPerformed(ActionContext context);
|
||||
|
||||
|
@ -98,11 +109,6 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
propertyListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeyBindingManaged() {
|
||||
return isKeyBindingManaged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
|
@ -258,6 +264,11 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
return menuItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyBindingType getKeyBindingType() {
|
||||
return keyBindingType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyStroke getKeyBinding() {
|
||||
return keyBindingData == null ? null : keyBindingData.getKeyBinding();
|
||||
|
@ -489,7 +500,7 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
inceptionInformation = getInceptionFromTheFirstClassThatIsNotUs();
|
||||
}
|
||||
|
||||
private String getInceptionFromTheFirstClassThatIsNotUs() {
|
||||
protected String getInceptionFromTheFirstClassThatIsNotUs() {
|
||||
Throwable t = ReflectionUtilities.createThrowableWithStackOlderThan(getClass());
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
String classInfo = trace[0].toString();
|
||||
|
|
|
@ -23,6 +23,17 @@ import javax.swing.*;
|
|||
import docking.ActionContext;
|
||||
import docking.help.HelpDescriptor;
|
||||
|
||||
/**
|
||||
* The base interface for clients that wish to create commands to be registered with a tool.
|
||||
*
|
||||
* <p>An action may appear in a primary menu, a popup menu or a toolbar. Further, an action
|
||||
* may have a key binding assigned.
|
||||
*
|
||||
* <p>The particular support for key bindings is defined by {@link KeyBindingType}. Almost all
|
||||
* client actions will use the default setting of {@link KeyBindingType#INDIVIDUAL}. To control
|
||||
* the level of key binding support, you can pass the desired {@link KeyBindingType} to the
|
||||
* base implementation of this interface.
|
||||
*/
|
||||
public interface DockingActionIf extends HelpDescriptor {
|
||||
public static final String ENABLEMENT_PROPERTY = "enabled";
|
||||
public static final String GLOBALCONTEXT_PROPERTY = "globalContext";
|
||||
|
@ -44,6 +55,16 @@ public interface DockingActionIf extends HelpDescriptor {
|
|||
*/
|
||||
public String getOwner();
|
||||
|
||||
/**
|
||||
* Returns a description of this actions owner. For most actions this will return the
|
||||
* same value as {@link #getOwner()}.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
public default String getOwnerDescription() {
|
||||
return getOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a short description of this action. Generally used for a tooltip
|
||||
* @return the description
|
||||
|
@ -248,10 +269,17 @@ public interface DockingActionIf extends HelpDescriptor {
|
|||
public boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes);
|
||||
|
||||
/**
|
||||
* Returns true if this action can have its keybinding information changed by the user.
|
||||
* @return true if this action can have its keybinding information changed by the user.
|
||||
* Returns this actions level of support for key binding accelerator keys
|
||||
*
|
||||
* <p>Actions support key bindings by default. Some reserved actions do not support
|
||||
* key bindings, while others wish to share the same key bindings with multiple, equivalent
|
||||
* actions (this allows the user to set one binding that works in many different contexts).
|
||||
*
|
||||
* @return the key binding support
|
||||
*/
|
||||
public boolean isKeyBindingManaged();
|
||||
public default KeyBindingType getKeyBindingType() {
|
||||
return KeyBindingType.INDIVIDUAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link KeyBindingData} on an action to either assign a keybinding or remove it
|
||||
|
@ -272,22 +300,4 @@ public interface DockingActionIf extends HelpDescriptor {
|
|||
* @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding
|
||||
*/
|
||||
public void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData);
|
||||
|
||||
/**
|
||||
* Returns true if this action shares a keybinding with other actions. If this returns true,
|
||||
* then this action, and any action that shares a name with this action, will be updated
|
||||
* to the same key binding value whenever the key binding options change.
|
||||
*
|
||||
* <p>This will be false for the vast majority of actions. If you are unsure if your action
|
||||
* should use a shared keybinding, then do not set this value to true.
|
||||
*
|
||||
* <p>This value is not meant to change over the life of the action. Thus, there is no
|
||||
* <code>set</code> method to change this value. Rather, you should override this method
|
||||
* to return <code>true</code> as desired.
|
||||
*
|
||||
* @return true to share a shared keybinding
|
||||
*/
|
||||
public default boolean usesSharedKeyBinding() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,8 +17,6 @@ package docking.action;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import docking.ActionContext;
|
||||
|
||||
/**
|
||||
* An interface for objects (really Components) to implement that signals they provide actions
|
||||
* for the Docking environment. This interface will be called when the implementor is the source
|
||||
|
@ -32,9 +29,9 @@ import docking.ActionContext;
|
|||
*/
|
||||
public interface DockingActionProviderIf {
|
||||
|
||||
/**
|
||||
* Returns actions that are compatible with the given context.
|
||||
* @param context the current context of the Docking system
|
||||
*/
|
||||
public List<DockingActionIf> getDockingActions( ActionContext context );
|
||||
/**
|
||||
* Returns actions that are compatible with the given context.
|
||||
* @return the actions
|
||||
*/
|
||||
public List<DockingActionIf> getDockingActions();
|
||||
}
|
||||
|
|
|
@ -15,12 +15,10 @@
|
|||
*/
|
||||
package docking.action;
|
||||
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.KeyBindingPrecedence;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
public class KeyBindingData {
|
||||
private KeyStroke keyStroke;
|
||||
|
@ -49,6 +47,7 @@ public class KeyBindingData {
|
|||
|
||||
/**
|
||||
* Returns an accelerator keystroke to be associated with this action.
|
||||
* @return the binding
|
||||
*/
|
||||
public KeyStroke getKeyBinding() {
|
||||
return keyStroke;
|
||||
|
@ -56,6 +55,7 @@ public class KeyBindingData {
|
|||
|
||||
/**
|
||||
* Returns the keyBindingPrecedence for this action
|
||||
* @return the precedence
|
||||
*/
|
||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||
return keyBindingPrecedence;
|
||||
|
@ -80,6 +80,10 @@ public class KeyBindingData {
|
|||
* @return the potentially changed data
|
||||
*/
|
||||
public static KeyBindingData validateKeyBindingData(KeyBindingData newKeyBindingData) {
|
||||
if (newKeyBindingData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyStroke keyBinding = newKeyBindingData.getKeyBinding();
|
||||
if (keyBinding == null) {
|
||||
// not sure when this can happen
|
||||
|
@ -88,63 +92,8 @@ public class KeyBindingData {
|
|||
|
||||
KeyBindingPrecedence precedence = newKeyBindingData.getKeyBindingPrecedence();
|
||||
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
|
||||
return createReservedKeyBindingData(validateKeyStroke(keyBinding));
|
||||
return createReservedKeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding));
|
||||
}
|
||||
return new KeyBindingData(validateKeyStroke(keyBinding), precedence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* the <tt>control</tt> key will be converted to the <tt>command</tt> key on the Mac.
|
||||
*
|
||||
* @param keyStroke the keystroke to validate
|
||||
* @return the potentially changed keystroke
|
||||
*/
|
||||
public static KeyStroke validateKeyStroke(KeyStroke keyStroke) {
|
||||
if (keyStroke == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove system-dependent control key mask and transform deprecated modifiers
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
if ((modifiers & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_DOWN_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ ActionEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.SHIFT_MASK;
|
||||
modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.ALT_MASK;
|
||||
modifiers = modifiers | InputEvent.ALT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.META_MASK) == InputEvent.META_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.META_MASK;
|
||||
modifiers = modifiers | InputEvent.META_DOWN_MASK;
|
||||
}
|
||||
|
||||
int eventType = keyStroke.getKeyEventType();
|
||||
if (eventType == KeyEvent.KEY_TYPED) {
|
||||
// we know that typed events have a key code of VK_UNDEFINED
|
||||
return KeyStroke.getKeyStroke(keyStroke.getKeyChar(), modifiers);
|
||||
}
|
||||
|
||||
// key pressed or released
|
||||
boolean isOnKeyRelease = keyStroke.isOnKeyRelease();
|
||||
return KeyStroke.getKeyStroke(keyStroke.getKeyCode(), modifiers, isOnKeyRelease);
|
||||
return new KeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding), precedence);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Allows clients to signal their support for the assigning of key binding shortcut keys. Most
|
||||
* action clients need not be concerned with this class. The default settings of
|
||||
* {@link DockingAction} work correctly for almost all cases, which is to have the action
|
||||
* support individual key bindings, which are managed by the system via the UI.
|
||||
*
|
||||
* @see DockingActionIf
|
||||
*/
|
||||
public enum KeyBindingType {
|
||||
|
||||
//@formatter:off
|
||||
/**
|
||||
* Indicates the setting of key bindings through the UI is not supported
|
||||
*/
|
||||
UNSUPPORTED,
|
||||
|
||||
/**
|
||||
* Supports the assignment of key bindings via the UI. Setting a key binding on an action
|
||||
* with this type will not affect any other action.
|
||||
*/
|
||||
INDIVIDUAL,
|
||||
|
||||
/**
|
||||
* When the key binding is set via the UI, this action, and any action that shares a
|
||||
* name with this action, will be updated to the same key binding value whenever the key
|
||||
* binding options change.
|
||||
*
|
||||
* <p>Most actions will not be shared. If you are unsure if your action
|
||||
* should use a shared keybinding, then do not do so.
|
||||
*/
|
||||
|
||||
SHARED;
|
||||
//@formatter:on
|
||||
|
||||
/**
|
||||
* Returns true if this type supports key bindings. This is a convenience method for
|
||||
* checking that this type is not {@link #UNSUPPORTED}.
|
||||
* @return true if key bindings are supported
|
||||
*/
|
||||
public boolean supportsKeyBindings() {
|
||||
return this != UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for checking if this type is the {@link #SHARED} type
|
||||
* @return true if shared
|
||||
*/
|
||||
public boolean isShared() {
|
||||
return this == SHARED;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for clients to check whether this key binding type should be
|
||||
* managed directly by the system.
|
||||
*
|
||||
* <p>Shared actions are not managed directly by the system, but are instead managed through
|
||||
* a proxy action.
|
||||
*
|
||||
* @return true if managed directly by the system; false if key binding are not supported
|
||||
* or are managed through a proxy
|
||||
*/
|
||||
public boolean isManaged() {
|
||||
return this == INDIVIDUAL;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,34 +13,33 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking;
|
||||
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
import ghidra.util.exception.AssertException;
|
||||
package docking.action;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.*;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class KeyBindingsManager implements PropertyChangeListener {
|
||||
|
||||
protected Map<KeyStroke, DockingKeyBindingAction> dockingKeyMap;
|
||||
protected Map<DockingActionIf, ComponentProvider> actionToProviderMap;
|
||||
private DockingTool tool;
|
||||
|
||||
private DockingWindowManager winMgr;
|
||||
|
||||
public KeyBindingsManager(DockingWindowManager winMgr) {
|
||||
this.winMgr = winMgr;
|
||||
dockingKeyMap = new HashMap<KeyStroke, DockingKeyBindingAction>();
|
||||
actionToProviderMap = new HashMap<DockingActionIf, ComponentProvider>();
|
||||
public KeyBindingsManager(DockingTool tool) {
|
||||
this.tool = tool;
|
||||
dockingKeyMap = new HashMap<>();
|
||||
actionToProviderMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void addAction(DockingActionIf action, ComponentProvider optionalProvider) {
|
||||
public void addAction(ComponentProvider optionalProvider, DockingActionIf action) {
|
||||
action.addPropertyChangeListener(this);
|
||||
if (optionalProvider != null) {
|
||||
actionToProviderMap.put(action, optionalProvider);
|
||||
|
@ -50,7 +48,7 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
|
||||
if (keyBinding != null) {
|
||||
addKeyBinding(action, optionalProvider, keyBinding);
|
||||
addKeyBinding(optionalProvider, action, keyBinding);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,13 +57,17 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
addReservedKeyBinding(action, keyBinding);
|
||||
}
|
||||
|
||||
public void addReservedAction(DockingActionIf action, KeyStroke ks) {
|
||||
addReservedKeyBinding(action, ks);
|
||||
}
|
||||
|
||||
public void removeAction(DockingActionIf action) {
|
||||
action.removePropertyChangeListener(this);
|
||||
actionToProviderMap.remove(action);
|
||||
removeKeyBinding(action.getKeyBinding(), action);
|
||||
}
|
||||
|
||||
private void addKeyBinding(DockingActionIf action, ComponentProvider provider,
|
||||
private void addKeyBinding(ComponentProvider provider, DockingActionIf action,
|
||||
KeyStroke keyStroke) {
|
||||
if (ReservedKeyBindings.isReservedKeystroke(keyStroke)) {
|
||||
throw new AssertException("Cannot assign action to a reserved keystroke. " +
|
||||
|
@ -74,7 +76,7 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
|
||||
DockingKeyBindingAction existingAction = dockingKeyMap.get(keyStroke);
|
||||
if (existingAction == null) {
|
||||
dockingKeyMap.put(keyStroke, new MultipleKeyAction(winMgr, provider, action, keyStroke));
|
||||
dockingKeyMap.put(keyStroke, new MultipleKeyAction(tool, provider, action, keyStroke));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -93,7 +95,9 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
"action to a given keystroke: " + keyStroke);
|
||||
}
|
||||
|
||||
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(winMgr, action, keyStroke));
|
||||
KeyBindingData binding = KeyBindingData.createReservedKeyBindingData(keyStroke);
|
||||
action.setKeyBindingData(binding);
|
||||
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(tool, action, keyStroke));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,23 +145,17 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
if (newKeyData != null) {
|
||||
KeyStroke ks = newKeyData.getKeyBinding();
|
||||
if (ks != null) {
|
||||
addKeyBinding(action, actionToProviderMap.get(action), ks);
|
||||
addKeyBinding(actionToProviderMap.get(action), action, ks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<DockingActionIf> getLocalActions() {
|
||||
return new ArrayList<DockingActionIf>(actionToProviderMap.keySet());
|
||||
}
|
||||
|
||||
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
||||
return dockingKeyMap.get(keyStroke);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
winMgr = null;
|
||||
dockingKeyMap.clear();
|
||||
actionToProviderMap.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ import java.util.*;
|
|||
import javax.swing.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
|
@ -31,35 +32,17 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
|
||||
private ActionDialog dialog;
|
||||
|
||||
class ActionData {
|
||||
DockingActionIf action;
|
||||
ComponentProvider provider;
|
||||
|
||||
ActionData(DockingActionIf action, ComponentProvider provider) {
|
||||
this.action = action;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
boolean isGlobalAction() {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
boolean isMyProvider(ComponentProvider otherProvider) {
|
||||
return provider == otherProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new MultipleKeyAction
|
||||
*
|
||||
* @param winMgr window manager used to determine context.
|
||||
* @param tool used to determine context
|
||||
* @param provider the provider, if any, associated with the action
|
||||
* @param action action that will be added to the list of actions bound to a keystroke
|
||||
* @param keyStroke the keystroke, if any, associated with the action
|
||||
*/
|
||||
public MultipleKeyAction(DockingWindowManager winMgr, ComponentProvider provider,
|
||||
DockingActionIf action, KeyStroke keyStroke) {
|
||||
super(winMgr, action, keyStroke);
|
||||
public MultipleKeyAction(DockingTool tool, ComponentProvider provider, DockingActionIf action,
|
||||
KeyStroke keyStroke) {
|
||||
super(tool, action, keyStroke);
|
||||
addAction(provider, action);
|
||||
}
|
||||
|
||||
|
@ -80,6 +63,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
throw new IllegalArgumentException(
|
||||
"KeyStrokes don't match - was: " + keyStroke + " new: " + keyBinding);
|
||||
}
|
||||
|
||||
actions.add(new ActionData(action, provider));
|
||||
}
|
||||
|
||||
|
@ -133,11 +117,11 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
@Override
|
||||
public void actionPerformed(final ActionEvent event) {
|
||||
// Build list of actions which are valid in current context
|
||||
ComponentProvider localProvider = winMgr.getActiveComponentProvider();
|
||||
ComponentProvider localProvider = tool.getActiveComponentProvider();
|
||||
ActionContext localContext = getLocalContext(localProvider);
|
||||
localContext.setSource(event.getSource());
|
||||
|
||||
ActionContext globalContext = winMgr.getGlobalContext();
|
||||
ActionContext globalContext = tool.getGlobalContext();
|
||||
List<ExecutableKeyActionAdapter> list = getValidContextActions(localContext, globalContext);
|
||||
|
||||
// If menu active, disable all key bindings
|
||||
|
@ -149,7 +133,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
if (list.size() > 1) {
|
||||
// popup dialog to show multiple actions
|
||||
if (dialog == null) {
|
||||
dialog = new ActionDialog(parseKeyStroke(keyStroke), list);
|
||||
dialog = new ActionDialog(KeyBindingUtils.parseKeyStroke(keyStroke), list);
|
||||
}
|
||||
else {
|
||||
dialog.setActionList(list);
|
||||
|
@ -162,12 +146,12 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
}
|
||||
else if (list.size() == 1) {
|
||||
final ExecutableKeyActionAdapter actionProxy = list.get(0);
|
||||
winMgr.setStatusText("");
|
||||
tool.setStatusInfo("");
|
||||
actionProxy.execute();
|
||||
}
|
||||
else {
|
||||
String name = (String) getValue(Action.NAME);
|
||||
winMgr.setStatusText("Action (" + name + ") not valid in this context!", true);
|
||||
tool.setStatusInfo("Action (" + name + ") not valid in this context!", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,9 +213,9 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
|
||||
@Override
|
||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||
ComponentProvider localProvider = winMgr.getActiveComponentProvider();
|
||||
ComponentProvider localProvider = tool.getActiveComponentProvider();
|
||||
ActionContext localContext = getLocalContext(localProvider);
|
||||
ActionContext globalContext = winMgr.getGlobalContext();
|
||||
ActionContext globalContext = tool.getGlobalContext();
|
||||
List<ExecutableKeyActionAdapter> validActions =
|
||||
getValidContextActions(localContext, globalContext);
|
||||
|
||||
|
@ -273,4 +257,27 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
|
||||
return buildy.toString();
|
||||
}
|
||||
|
||||
private class ActionData {
|
||||
DockingActionIf action;
|
||||
ComponentProvider provider;
|
||||
|
||||
ActionData(DockingActionIf action, ComponentProvider provider) {
|
||||
this.action = action;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
boolean isGlobalAction() {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
boolean isMyProvider(ComponentProvider otherProvider) {
|
||||
return provider == otherProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return provider.toString() + " - " + action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,21 +13,25 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking;
|
||||
package docking.action;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.*;
|
||||
|
||||
class ReservedKeyBindingAction extends DockingKeyBindingAction {
|
||||
|
||||
ReservedKeyBindingAction(DockingWindowManager winMgr, DockingActionIf action,
|
||||
KeyStroke keyStroke) {
|
||||
super(winMgr, action, keyStroke);
|
||||
ReservedKeyBindingAction(DockingTool tool, DockingActionIf action, KeyStroke keyStroke) {
|
||||
super(tool, action, keyStroke);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReservedKeybindingPrecedence() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||
return KeyBindingPrecedence.ReservedActionsLevel;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -29,14 +28,16 @@ public abstract class ToggleDockingAction extends DockingAction implements Toggl
|
|||
super(name, owner);
|
||||
}
|
||||
|
||||
public ToggleDockingAction(String name, String owner, boolean isKeybindingManaged) {
|
||||
super(name, owner, isKeybindingManaged);
|
||||
public ToggleDockingAction(String name, String owner, boolean supportsKeyBindings) {
|
||||
super(name, owner, supportsKeyBindings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelected() {
|
||||
return isSelected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(boolean newValue) {
|
||||
isSelected = newValue;
|
||||
firePropertyChanged(SELECTED_STATE_PROPERTY, !isSelected, isSelected);
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/* ###
|
||||
* 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.Set;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.DockingActionIf;
|
||||
|
||||
/**
|
||||
* Represents the collection of actions registered with the tool, along with method for adding
|
||||
* and removing actions.
|
||||
*/
|
||||
public interface DockingToolActions {
|
||||
|
||||
/**
|
||||
* Adds the given action that enabled when the given provider is active
|
||||
*
|
||||
* @param provider the provider
|
||||
* @param action the action
|
||||
*/
|
||||
public void addLocalAction(ComponentProvider provider, DockingActionIf action);
|
||||
|
||||
/**
|
||||
* Gets the provider action by the given name
|
||||
*
|
||||
* @param provider the provider
|
||||
* @param actionName the action name
|
||||
* @return the action
|
||||
*/
|
||||
public DockingActionIf getLocalAction(ComponentProvider provider, String actionName);
|
||||
|
||||
/**
|
||||
* Removes the given provider's local action
|
||||
*
|
||||
* @param provider the provider
|
||||
* @param action the action
|
||||
*/
|
||||
public void removeLocalAction(ComponentProvider provider, DockingActionIf action);
|
||||
|
||||
/**
|
||||
* Adds the given action that is enabled, regardless of the active provider
|
||||
*
|
||||
* @param action the action
|
||||
*/
|
||||
public void addGlobalAction(DockingActionIf action);
|
||||
|
||||
/**
|
||||
* Removes the given global action
|
||||
* @param action the action
|
||||
*/
|
||||
public void removeGlobalAction(DockingActionIf action);
|
||||
|
||||
/**
|
||||
* Removes all global actions for the given owner
|
||||
*
|
||||
* @param owner the owner
|
||||
*/
|
||||
public void removeActions(String owner);
|
||||
|
||||
/**
|
||||
* Removes all local actions for the given provider
|
||||
*
|
||||
* @param provider the provider
|
||||
*/
|
||||
public void removeActions(ComponentProvider provider);
|
||||
|
||||
/**
|
||||
* Returns all actions with the given owner
|
||||
*
|
||||
* @param owner the owner
|
||||
* @return the actions
|
||||
*/
|
||||
public Set<DockingActionIf> getActions(String owner);
|
||||
|
||||
/**
|
||||
* Returns all actions known to the tool
|
||||
* @return the actions
|
||||
*/
|
||||
public Set<DockingActionIf> getAllActions();
|
||||
|
||||
}
|
|
@ -13,24 +13,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.action;
|
||||
package docking.actions;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.action.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
|
||||
public class KeyBindingAction extends DockingAction {
|
||||
private final ActionToGuiMapper dockingActionManager;
|
||||
|
||||
public KeyBindingAction(ActionToGuiMapper dockingActionManager) {
|
||||
super("Set KeyBinding", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
this.dockingActionManager = dockingActionManager;
|
||||
createReservedKeyBinding(ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY);
|
||||
setEnabled(true);
|
||||
public static String NAME = "Set KeyBinding";
|
||||
private ToolActions toolActions;
|
||||
|
||||
public KeyBindingAction(ToolActions toolActions) {
|
||||
super(NAME, DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
this.toolActions = toolActions;
|
||||
|
||||
// Help actions don't have help
|
||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||
|
@ -50,15 +49,14 @@ public class KeyBindingAction extends DockingAction {
|
|||
|
||||
action = maybeGetToolLevelAction(action);
|
||||
|
||||
if (!action.isKeyBindingManaged()) {
|
||||
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
||||
Component parent = windowManager.getActiveComponent();
|
||||
Msg.showInfo(getClass(), parent, "Unable to Set Keybinding",
|
||||
"Action \"" + getActionName(action) + "\" is not keybinding managed and thus a " +
|
||||
"keybinding cannot be set.");
|
||||
"Action \"" + getActionName(action) + "\" does not support key bindings");
|
||||
return;
|
||||
}
|
||||
|
||||
KeyEntryDialog d = new KeyEntryDialog(action, dockingActionManager);
|
||||
KeyEntryDialog d = new KeyEntryDialog(action, toolActions);
|
||||
DockingWindowManager.showDialog(d);
|
||||
}
|
||||
|
||||
|
@ -69,17 +67,15 @@ public class KeyBindingAction extends DockingAction {
|
|||
* @return A tool-level action if one is found; otherwise, the original action
|
||||
*/
|
||||
private DockingActionIf maybeGetToolLevelAction(DockingActionIf dockingAction) {
|
||||
if (dockingAction.isKeyBindingManaged()) {
|
||||
return dockingAction;
|
||||
}
|
||||
|
||||
// It is not key binding managed, which means that it may be a shared key binding
|
||||
String actionName = dockingAction.getName();
|
||||
Set<DockingActionIf> allActions = dockingActionManager.getAllActions();
|
||||
DockingActionIf sharedAction =
|
||||
KeyBindingUtils.getSharedKeyBindingAction(allActions, actionName);
|
||||
if (sharedAction != null) {
|
||||
return sharedAction;
|
||||
if (dockingAction.getKeyBindingType().isShared()) {
|
||||
|
||||
// It is not key binding managed, which means that it may be a shared key binding
|
||||
String actionName = dockingAction.getName();
|
||||
DockingActionIf sharedAction = toolActions.getSharedStubKeyBindingAction(actionName);
|
||||
if (sharedAction != null) {
|
||||
return sharedAction;
|
||||
}
|
||||
}
|
||||
|
||||
return dockingAction;
|
|
@ -15,13 +15,17 @@
|
|||
*/
|
||||
package docking.actions;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.indexOfIgnoreCase;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jdom.*;
|
||||
|
@ -31,6 +35,7 @@ import org.jdom.output.XMLOutputter;
|
|||
import com.google.common.collect.Sets;
|
||||
|
||||
import docking.DockingTool;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.*;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
|
@ -53,10 +58,20 @@ import utilities.util.reflection.ReflectionUtilities;
|
|||
public class KeyBindingUtils {
|
||||
private static final String LAST_KEY_BINDING_EXPORT_DIRECTORY = "LastKeyBindingExportDirectory";
|
||||
|
||||
private static final String RELEASED = "released";
|
||||
private static final String TYPED = "typed";
|
||||
private static final String PRESSED = "pressed";
|
||||
|
||||
private static final String SHIFT = "Shift";
|
||||
private static final String CTRL = "Ctrl";
|
||||
private static final String CONTROL = "Control";
|
||||
private static final String ALT = "Alt";
|
||||
private static final String META = "Meta";
|
||||
private static final String MODIFIER_SEPARATOR = "-";
|
||||
|
||||
private static final Logger log = LogManager.getLogger(KeyBindingUtils.class);
|
||||
|
||||
public static final String PREFERENCES_FILE_EXTENSION = ".kbxml";
|
||||
|
||||
private static final GhidraFileFilter FILE_FILTER = new GhidraFileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname, GhidraFileChooserModel model) {
|
||||
|
@ -330,15 +345,21 @@ public class KeyBindingUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* A utility method to get all key binding actions. This method will remove duplicate
|
||||
* actions and will only return actions that are {@link DockingActionIf#isKeyBindingManaged()}
|
||||
* A utility method to get all key binding actions. This method will
|
||||
* only return actions that support {@link KeyBindingType key bindings}.
|
||||
*
|
||||
* <p>The mapping returned provides a list of items because it is possible for there to
|
||||
* exists multiple actions with the same name and owner. (This can happen when multiple copies
|
||||
* of a component provider are shown, each with their own set of actions that share the
|
||||
* same name.)
|
||||
*
|
||||
* @param tool the tool containing the actions
|
||||
* @return the actions mapped by their full name (e.g., 'Name (OwnerName)')
|
||||
*/
|
||||
public static Map<String, DockingActionIf> getAllActionsByFullName(DockingTool tool) {
|
||||
public static Map<String, List<DockingActionIf>> getAllActionsByFullName(DockingTool tool) {
|
||||
|
||||
Map<String, DockingActionIf> deduper = new HashMap<>();
|
||||
Map<String, List<DockingActionIf>> result =
|
||||
LazyMap.lazyMap(new HashMap<>(), s -> new LinkedList<>());
|
||||
Set<DockingActionIf> actions = tool.getAllActions();
|
||||
for (DockingActionIf action : actions) {
|
||||
if (isIgnored(action)) {
|
||||
|
@ -348,16 +369,16 @@ public class KeyBindingUtils {
|
|||
continue;
|
||||
}
|
||||
|
||||
deduper.put(action.getFullName(), action);
|
||||
result.get(action.getFullName()).add(action);
|
||||
}
|
||||
|
||||
return deduper;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to get all key binding actions that have the given owner.
|
||||
* This method will remove duplicate actions and will only return actions
|
||||
* that are {@link DockingActionIf#isKeyBindingManaged()}
|
||||
* that support {@link KeyBindingType key bindings}.
|
||||
*
|
||||
* @param tool the tool containing the actions
|
||||
* @param owner the action owner name
|
||||
|
@ -398,36 +419,6 @@ public class KeyBindingUtils {
|
|||
return Sets.filter(ownerMatch, action -> action.getName().equals(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to locate the {@link SharedStubKeyBindingAction} representative for the given
|
||||
* action name. This method is not useful to general clients.
|
||||
*
|
||||
* @param allActions all actions in the system
|
||||
* @param sharedName the name of the shared action
|
||||
* @return the shared action representative
|
||||
*/
|
||||
public static DockingActionIf getSharedKeyBindingAction(Set<DockingActionIf> allActions,
|
||||
String sharedName) {
|
||||
|
||||
String owner = "Tool";
|
||||
for (DockingActionIf action : allActions) {
|
||||
if (!(action instanceof SharedStubKeyBindingAction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.getOwner().equals(owner) && action.getName().equals(sharedName)) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isIgnored(DockingActionIf action) {
|
||||
// not keybinding managed; a shared keybinding implies that this action should not be in
|
||||
// the UI, as there will be a single proxy in place of all actions sharing that binding
|
||||
return !action.isKeyBindingManaged() || action.usesSharedKeyBinding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the existing docking action and allows it to be registered with
|
||||
* Swing components
|
||||
|
@ -455,9 +446,17 @@ public class KeyBindingUtils {
|
|||
public static void assertSameDefaultKeyBindings(DockingActionIf newAction,
|
||||
Collection<DockingActionIf> existingActions) {
|
||||
|
||||
if (!newAction.getKeyBindingType().supportsKeyBindings()) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyBindingData newDefaultBinding = newAction.getDefaultKeyBindingData();
|
||||
KeyStroke defaultKs = getKeyStroke(newDefaultBinding);
|
||||
for (DockingActionIf action : existingActions) {
|
||||
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
KeyBindingData existingDefaultBinding = action.getDefaultKeyBindingData();
|
||||
KeyStroke existingKs = getKeyStroke(existingDefaultBinding);
|
||||
if (!Objects.equals(defaultKs, existingKs)) {
|
||||
|
@ -493,6 +492,252 @@ public class KeyBindingUtils {
|
|||
Msg.warn(KeyBindingUtils.class, s, ReflectionUtilities.createJavaFilteredThrowable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* the <tt>control</tt> key will be converted to the <tt>command</tt> key on the Mac.
|
||||
*
|
||||
* @param keyStroke the keystroke to validate
|
||||
* @return the potentially changed keystroke
|
||||
*/
|
||||
// TODO ignore the deprecation, as this method is responsible for fixing deprecated usage.
|
||||
// When all actions no longer user the deprecated modifiers, the deprecated elements
|
||||
// of this method can be removed
|
||||
@SuppressWarnings("deprecation")
|
||||
public static KeyStroke validateKeyStroke(KeyStroke keyStroke) {
|
||||
if (keyStroke == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove system-dependent control key mask and transform deprecated modifiers
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
if ((modifiers & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_DOWN_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ ActionEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.SHIFT_MASK;
|
||||
modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.ALT_MASK;
|
||||
modifiers = modifiers | InputEvent.ALT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.META_MASK) == InputEvent.META_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.META_MASK;
|
||||
modifiers = modifiers | InputEvent.META_DOWN_MASK;
|
||||
}
|
||||
|
||||
int eventType = keyStroke.getKeyEventType();
|
||||
if (eventType == KeyEvent.KEY_TYPED) {
|
||||
// we know that typed events have a key code of VK_UNDEFINED
|
||||
return KeyStroke.getKeyStroke(Character.valueOf(keyStroke.getKeyChar()), modifiers);
|
||||
}
|
||||
|
||||
// key pressed or released
|
||||
boolean isOnKeyRelease = keyStroke.isOnKeyRelease();
|
||||
return KeyStroke.getKeyStroke(keyStroke.getKeyCode(), modifiers, isOnKeyRelease);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the toString() form of the keyStroke.
|
||||
* <br>In Java 1.4.2 & earlier, Ctrl-M is returned as "keyCode CtrlM-P"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
* <br>In Java 1.5.0, Ctrl-M is returned as "ctrl pressed M"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
*
|
||||
* @param keyStroke the key stroke
|
||||
* @return the string value; the empty string if the key stroke is null
|
||||
*/
|
||||
public static String parseKeyStroke(KeyStroke keyStroke) {
|
||||
|
||||
if (keyStroke == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final String keyPressSuffix = "-P";
|
||||
String keyString = keyStroke.toString();
|
||||
int type = keyStroke.getKeyEventType();
|
||||
if (type == KeyEvent.KEY_TYPED) {
|
||||
return String.valueOf(keyStroke.getKeyChar());
|
||||
}
|
||||
|
||||
// get the character used in the key stroke
|
||||
int firstIndex = keyString.lastIndexOf(' ') + 1;
|
||||
int ctrlIndex = keyString.indexOf(CTRL, firstIndex);
|
||||
if (ctrlIndex >= 0) {
|
||||
firstIndex = ctrlIndex + CTRL.length();
|
||||
}
|
||||
int altIndex = keyString.indexOf(ALT, firstIndex);
|
||||
if (altIndex >= 0) {
|
||||
firstIndex = altIndex + ALT.length();
|
||||
}
|
||||
int shiftIndex = keyString.indexOf(SHIFT, firstIndex);
|
||||
if (shiftIndex >= 0) {
|
||||
firstIndex = shiftIndex + SHIFT.length();
|
||||
}
|
||||
int metaIndex = keyString.indexOf(META, firstIndex);
|
||||
if (metaIndex >= 0) {
|
||||
firstIndex = metaIndex + META.length();
|
||||
}
|
||||
|
||||
int lastIndex = keyString.length();
|
||||
if (keyString.endsWith(keyPressSuffix)) {
|
||||
lastIndex -= keyPressSuffix.length();
|
||||
}
|
||||
if (lastIndex >= 0) {
|
||||
keyString = keyString.substring(firstIndex, lastIndex);
|
||||
}
|
||||
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
if (isShift(modifiers)) {
|
||||
buffy.insert(0, SHIFT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if (isAlt(modifiers)) {
|
||||
buffy.insert(0, ALT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if (isControl(modifiers)) {
|
||||
buffy.insert(0, CTRL + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if (isMeta(modifiers)) {
|
||||
buffy.insert(0, META + MODIFIER_SEPARATOR);
|
||||
}
|
||||
buffy.append(keyString);
|
||||
return buffy.toString();
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isShift(int mask) {
|
||||
return (mask & InputEvent.SHIFT_DOWN_MASK) != 0 || (mask & InputEvent.SHIFT_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isAlt(int mask) {
|
||||
return (mask & InputEvent.ALT_DOWN_MASK) != 0 || (mask & InputEvent.ALT_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isControl(int mask) {
|
||||
return (mask & InputEvent.CTRL_DOWN_MASK) != 0 || (mask & InputEvent.CTRL_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isMeta(int mask) {
|
||||
return (mask & InputEvent.META_DOWN_MASK) != 0 || (mask & InputEvent.META_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given text into a KeyStroke. This method relies upon
|
||||
* {@link KeyStroke#getKeyStroke(String)} for parsing. Before making that call, this method
|
||||
* will perform fixup on the given text for added flexibility. For example, the given
|
||||
* text may contain spaces or dashes as the separators between parts in the string. Also,
|
||||
* the text is converted such that it is not case-sensitive. So, the following example
|
||||
* formats are allowed:
|
||||
* <pre>
|
||||
* Alt-F
|
||||
* alt p
|
||||
* Ctrl-Alt-Z
|
||||
* ctrl Z
|
||||
* </pre>
|
||||
*
|
||||
* @param keyStroke
|
||||
* @return the new key stroke (as returned by {@link KeyStroke#getKeyStroke(String)}
|
||||
*/
|
||||
public static KeyStroke parseKeyStroke(String keyStroke) {
|
||||
List<String> pieces = new ArrayList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(keyStroke, "- ");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String token = tokenizer.nextToken();
|
||||
if (!pieces.contains(token)) {
|
||||
pieces.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
for (Iterator<String> iterator = pieces.iterator(); iterator.hasNext();) {
|
||||
String piece = iterator.next();
|
||||
if (indexOfIgnoreCase(piece, SHIFT) != -1) {
|
||||
buffy.append("shift ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, CTRL) != -1) {
|
||||
buffy.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, CONTROL) != -1) {
|
||||
buffy.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, ALT) != -1) {
|
||||
buffy.append("alt ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, META) != -1) {
|
||||
buffy.append("meta ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, PRESSED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, TYPED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, RELEASED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buffy.append(PRESSED).append(' ');
|
||||
|
||||
// at this point we should only have left one piece--the key ID
|
||||
int leftover = pieces.size();
|
||||
if (leftover > 1 || leftover == 0) {
|
||||
Msg.warn(KeyBindingUtils.class, "Invalid keystroke string found. Expected " +
|
||||
"format of '[modifier] ... key'. Found: '" + keyStroke + "'");
|
||||
|
||||
if (leftover == 0) {
|
||||
return null; // nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
String key = pieces.get(0);
|
||||
buffy.append(key.toUpperCase());
|
||||
|
||||
return KeyStroke.getKeyStroke(buffy.toString());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private static boolean isIgnored(DockingActionIf action) {
|
||||
// a shared keybinding implies that this action should not be in
|
||||
// the UI, as there will be a single proxy in place of all actions sharing that binding
|
||||
return !action.getKeyBindingType().isManaged();
|
||||
}
|
||||
|
||||
private static KeyStroke getKeyStroke(KeyBindingData data) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
|
@ -500,12 +745,7 @@ public class KeyBindingUtils {
|
|||
return data.getKeyBinding();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
// prompts the user for a file location from which to read key binding
|
||||
// data
|
||||
// prompts the user for a file location from which to read key binding data
|
||||
private static InputStream getInputStreamForFile(File startingDir) {
|
||||
File selectedFile = getFileFromUser(startingDir);
|
||||
|
||||
|
@ -587,4 +827,5 @@ public class KeyBindingUtils {
|
|||
|
||||
return selectedFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.action;
|
||||
package docking.actions;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
@ -22,8 +22,9 @@ import java.util.List;
|
|||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.KeyEntryTextField;
|
||||
import docking.action.*;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
|
@ -35,7 +36,7 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class KeyEntryDialog extends DialogComponentProvider {
|
||||
|
||||
private ActionToGuiMapper actionManager;
|
||||
private ToolActions toolActions;
|
||||
private DockingActionIf action;
|
||||
private JPanel defaultPanel;
|
||||
private KeyEntryTextField keyEntryField;
|
||||
|
@ -45,10 +46,10 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
private SimpleAttributeSet textAttrSet;
|
||||
private Color bgColor;
|
||||
|
||||
public KeyEntryDialog(DockingActionIf action, ActionToGuiMapper actionManager) {
|
||||
public KeyEntryDialog(DockingActionIf action, ToolActions actions) {
|
||||
super("Set Key Binding for " + action.getName(), true);
|
||||
this.actionManager = actionManager;
|
||||
this.action = action;
|
||||
this.toolActions = actions;
|
||||
setUpAttributes();
|
||||
createPanel();
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
|
@ -105,7 +106,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
p.add(keyEntryField);
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
if (keyBinding != null) {
|
||||
keyEntryField.setText(DockingKeyBindingAction.parseKeyStroke(keyBinding));
|
||||
keyEntryField.setText(KeyBindingUtils.parseKeyStroke(keyBinding));
|
||||
}
|
||||
setFocusComponent(keyEntryField);
|
||||
defaultPanel.add(p, BorderLayout.CENTER);
|
||||
|
@ -129,6 +130,14 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given keystroke value into the text field of this dialog
|
||||
* @param ks the keystroke to set
|
||||
*/
|
||||
public void setKeyStroke(KeyStroke ks) {
|
||||
keyEntryField.setKeyStroke(ks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
close();
|
||||
|
@ -136,23 +145,24 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
KeyStroke keyStroke = keyEntryField.getCurrentKeyStroke();
|
||||
if (keyStroke != null && ReservedKeyBindings.isReservedKeystroke(keyStroke)) {
|
||||
KeyStroke newKeyStroke = keyEntryField.getKeyStroke();
|
||||
if (newKeyStroke != null && ReservedKeyBindings.isReservedKeystroke(newKeyStroke)) {
|
||||
setStatusText(keyEntryField.getText() + " is a reserved keystroke");
|
||||
return;
|
||||
}
|
||||
|
||||
clearStatusText();
|
||||
|
||||
Set<DockingActionIf> allActions = actionManager.getAllActions();
|
||||
Set<DockingActionIf> actions =
|
||||
KeyBindingUtils.getActions(allActions, action.getOwner(), action.getName());
|
||||
for (DockingActionIf element : actions) {
|
||||
if (element.isKeyBindingManaged()) {
|
||||
element.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
|
||||
}
|
||||
KeyStroke existingKeyStroke = action.getKeyBinding();
|
||||
if (Objects.equals(existingKeyStroke, newKeyStroke)) {
|
||||
setStatusText("Key binding unchanged");
|
||||
return;
|
||||
}
|
||||
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKeyStroke));
|
||||
|
||||
toolActions.keyBindingsChanged();
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
|
@ -168,23 +178,30 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private void updateCollisionPane(KeyStroke ks) {
|
||||
clearStatusText();
|
||||
collisionPane.setText("");
|
||||
if (ks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyStroke existingKeyStroke = action.getKeyBinding();
|
||||
if (Objects.equals(existingKeyStroke, ks)) {
|
||||
setStatusText("Key binding unchanged");
|
||||
return;
|
||||
}
|
||||
|
||||
List<DockingActionIf> list = getManagedActionsForKeyStroke(ks);
|
||||
if (list.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String ksName = DockingKeyBindingAction.parseKeyStroke(ks);
|
||||
String ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
try {
|
||||
doc.insertString(0, "Actions mapped to " + ksName + "\n\n", textAttrSet);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DockingActionIf a = list.get(i);
|
||||
|
||||
String collisionStr = "\t" + a.getName() + " (" + a.getOwner() + ")\n";
|
||||
String collisionStr = "\t" + a.getName() + " (" + a.getOwnerDescription() + ")\n";
|
||||
int offset = doc.getLength();
|
||||
doc.insertString(offset, collisionStr, textAttrSet);
|
||||
doc.setParagraphAttributes(offset, 1, tabAttrSet, false);
|
||||
|
@ -202,6 +219,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
if (multiAction == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<DockingActionIf> list = multiAction.getActions();
|
||||
Map<String, DockingActionIf> nameMap = new HashMap<>(list.size());
|
||||
|
||||
|
@ -218,7 +236,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private MultipleKeyAction getMultipleKeyAction(KeyStroke ks) {
|
||||
Action keyAction = actionManager.getDockingKeyAction(ks);
|
||||
Action keyAction = toolActions.getAction(ks);
|
||||
if (keyAction instanceof MultipleKeyAction) {
|
||||
return (MultipleKeyAction) keyAction;
|
||||
}
|
||||
|
@ -226,11 +244,6 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private boolean shouldAddAction(DockingActionIf dockableAction) {
|
||||
if (dockableAction.isKeyBindingManaged()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// shared key bindings are handled specially
|
||||
return !dockableAction.usesSharedKeyBinding();
|
||||
return dockableAction.getKeyBindingType().isManaged();
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ import java.util.Map.Entry;
|
|||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.action.*;
|
||||
|
@ -83,6 +85,33 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
|||
updateActionKeyStrokeFromOptions(action, defaultKs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwnerDescription() {
|
||||
List<String> owners = getDistinctOwners();
|
||||
Collections.sort(owners);
|
||||
if (owners.size() == 1) {
|
||||
return owners.get(0);
|
||||
}
|
||||
return StringUtils.join(owners, ", ");
|
||||
}
|
||||
|
||||
private List<String> getDistinctOwners() {
|
||||
List<String> results = new ArrayList<>();
|
||||
Set<DockingActionIf> actions = clientActions.keySet();
|
||||
for (DockingActionIf action : actions) {
|
||||
String owner = action.getOwner();
|
||||
if (DockingWindowManager.DOCKING_WINDOWS_OWNER.equals(owner)) {
|
||||
// special case: this is the owner for special system-level actions
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!results.contains(owner)) {
|
||||
results.add(owner);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private KeyStroke validateActionsHaveTheSameDefaultKeyStroke(DockingActionIf newAction) {
|
||||
|
||||
// this value may be null
|
||||
|
@ -116,12 +145,16 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
|||
|
||||
private void updateActionKeyStrokeFromOptions(DockingActionIf action, KeyStroke defaultKs) {
|
||||
|
||||
KeyStroke stubKs = defaultKs;
|
||||
KeyStroke optionsKs = getKeyStrokeFromOptions(defaultKs);
|
||||
if (!Objects.equals(defaultKs, optionsKs)) {
|
||||
// we use the 'unvalidated' call since this value is provided by the user--we assume
|
||||
// that user input is correct; we only validate programmer input
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(optionsKs));
|
||||
stubKs = optionsKs;
|
||||
}
|
||||
|
||||
setUnvalidatedKeyBindingData(new KeyBindingData(stubKs));
|
||||
}
|
||||
|
||||
private KeyStroke getKeyStrokeFromOptions(KeyStroke validatedKeyStroke) {
|
||||
|
|
|
@ -19,23 +19,29 @@ import java.beans.PropertyChangeEvent;
|
|||
import java.beans.PropertyChangeListener;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
import org.apache.commons.collections4.Predicate;
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* An class to manage actions registered with the tool
|
||||
*/
|
||||
public class ToolActions implements PropertyChangeListener {
|
||||
public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||
|
||||
private DockingWindowManager winMgr;
|
||||
private ActionToGuiHelper actionGuiHelper;
|
||||
|
||||
/*
|
||||
|
@ -51,24 +57,44 @@ public class ToolActions implements PropertyChangeListener {
|
|||
|
||||
private ToolOptions keyBindingOptions;
|
||||
private DockingTool dockingTool;
|
||||
private KeyBindingsManager keyBindingsManager;
|
||||
|
||||
/**
|
||||
* Construct an ActionManager
|
||||
*
|
||||
* @param tool tool using this ActionManager
|
||||
* @param windowManager manager of the "Docking" arrangement of a set of components
|
||||
* and actions in the tool
|
||||
* @param actionToGuiHelper the class that takes actions and maps them to GUI widgets
|
||||
*/
|
||||
public ToolActions(DockingTool tool, DockingWindowManager windowManager) {
|
||||
public ToolActions(DockingTool tool, ActionToGuiHelper actionToGuiHelper) {
|
||||
this.dockingTool = tool;
|
||||
this.winMgr = windowManager;
|
||||
this.actionGuiHelper = new ActionToGuiHelper(winMgr);
|
||||
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
this.actionGuiHelper = actionToGuiHelper;
|
||||
this.keyBindingsManager = new KeyBindingsManager(tool);
|
||||
this.keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
createReservedKeyBindings();
|
||||
}
|
||||
|
||||
private void createReservedKeyBindings() {
|
||||
KeyBindingAction keyBindingAction = new KeyBindingAction(this);
|
||||
keyBindingsManager.addReservedAction(keyBindingAction,
|
||||
ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY);
|
||||
|
||||
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));
|
||||
|
||||
// these are diagnostic
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
keyBindingsManager.addReservedAction(new ShowFocusInfoAction());
|
||||
keyBindingsManager.addReservedAction(new ShowFocusCycleAction());
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
actionsByNameByOwner.clear();
|
||||
sharedActionMap.clear();
|
||||
keyBindingsManager.dispose();
|
||||
}
|
||||
|
||||
private void addActionToMap(DockingActionIf action) {
|
||||
|
@ -83,34 +109,35 @@ public class ToolActions implements PropertyChangeListener {
|
|||
* @param provider provider associated with the action
|
||||
* @param action local action to the provider
|
||||
*/
|
||||
@Override
|
||||
public synchronized void addLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||
checkForAlreadyAddedAction(provider, action);
|
||||
|
||||
action.addPropertyChangeListener(this);
|
||||
addActionToMap(action);
|
||||
setKeyBindingOption(action);
|
||||
initializeKeyBinding(provider, action);
|
||||
actionGuiHelper.addLocalAction(provider, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the action to the tool.
|
||||
* @param action the action to be added.
|
||||
*/
|
||||
public synchronized void addToolAction(DockingActionIf action) {
|
||||
@Override
|
||||
public synchronized void addGlobalAction(DockingActionIf action) {
|
||||
checkForAlreadyAddedAction(null, action);
|
||||
|
||||
action.addPropertyChangeListener(this);
|
||||
addActionToMap(action);
|
||||
setKeyBindingOption(action);
|
||||
initializeKeyBinding(null, action);
|
||||
actionGuiHelper.addToolAction(action);
|
||||
}
|
||||
|
||||
private void setKeyBindingOption(DockingActionIf action) {
|
||||
private void initializeKeyBinding(ComponentProvider provider, DockingActionIf action) {
|
||||
|
||||
if (action.usesSharedKeyBinding()) {
|
||||
installSharedKeyBinding(action);
|
||||
KeyBindingType type = action.getKeyBindingType();
|
||||
if (!type.supportsKeyBindings()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!action.isKeyBindingManaged()) {
|
||||
if (type.isShared()) {
|
||||
installSharedKeyBinding(provider, action);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -121,9 +148,11 @@ public class ToolActions implements PropertyChangeListener {
|
|||
if (!Objects.equals(ks, newKs)) {
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
|
||||
}
|
||||
|
||||
keyBindingsManager.addAction(provider, action);
|
||||
}
|
||||
|
||||
private void installSharedKeyBinding(DockingActionIf action) {
|
||||
private void installSharedKeyBinding(ComponentProvider provider, DockingActionIf action) {
|
||||
String name = action.getName();
|
||||
KeyStroke defaultKeyStroke = action.getKeyBinding();
|
||||
|
||||
|
@ -132,29 +161,31 @@ public class ToolActions implements PropertyChangeListener {
|
|||
|
||||
SharedStubKeyBindingAction newStub =
|
||||
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
||||
newStub.addPropertyChangeListener(this);
|
||||
keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
||||
defaultKeyStroke, null, null);
|
||||
return newStub;
|
||||
});
|
||||
|
||||
stub.addClientAction(action);
|
||||
|
||||
// note: only put the stub in the manager, not the actual action
|
||||
keyBindingsManager.addAction(provider, stub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given action from the tool
|
||||
* @param action the action to be removed.
|
||||
*/
|
||||
public synchronized void removeToolAction(DockingActionIf action) {
|
||||
@Override
|
||||
public synchronized void removeGlobalAction(DockingActionIf action) {
|
||||
action.removePropertyChangeListener(this);
|
||||
removeAction(action);
|
||||
actionGuiHelper.removeToolAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all actions that have the given owner.
|
||||
* @param owner owner of the actions to remove
|
||||
*/
|
||||
public synchronized void removeToolActions(String owner) {
|
||||
@Override
|
||||
public synchronized void removeActions(String owner) {
|
||||
|
||||
// remove from the outer map first, to prevent concurrent modification exceptions
|
||||
Map<String, Set<DockingActionIf>> toCleanup = actionsByNameByOwner.remove(owner);
|
||||
|
@ -166,15 +197,17 @@ public class ToolActions implements PropertyChangeListener {
|
|||
toCleanup.values()
|
||||
.stream()
|
||||
.flatMap(set -> set.stream())
|
||||
.forEach(action -> removeToolAction(action))
|
||||
.forEach(action -> removeGlobalAction(action))
|
||||
;
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) {
|
||||
if (getActionStorage(action).contains(action)) {
|
||||
throw new AssertException("Cannot add the same action more than once. Provider " +
|
||||
provider.getName() + " - action: " + action.getFullName());
|
||||
String providerString =
|
||||
provider == null ? "Action: " : "Provider " + provider.getName() + " - action: ";
|
||||
throw new AssertException("Cannot add the same action more than once. " +
|
||||
providerString + action.getFullName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,6 +217,7 @@ public class ToolActions implements PropertyChangeListener {
|
|||
* @return array of actions; zero length array is returned if no
|
||||
* action exists with the given name
|
||||
*/
|
||||
@Override
|
||||
public synchronized Set<DockingActionIf> getActions(String owner) {
|
||||
|
||||
Set<DockingActionIf> result = new HashSet<>();
|
||||
|
@ -201,8 +235,10 @@ public class ToolActions implements PropertyChangeListener {
|
|||
|
||||
/**
|
||||
* Get a set of all actions in the tool
|
||||
* @return the actions
|
||||
*
|
||||
* @return a new set of the existing actions
|
||||
*/
|
||||
@Override
|
||||
public synchronized Set<DockingActionIf> getAllActions() {
|
||||
|
||||
Set<DockingActionIf> result = new HashSet<>();
|
||||
|
@ -218,56 +254,83 @@ public class ToolActions implements PropertyChangeListener {
|
|||
return result;
|
||||
}
|
||||
|
||||
private Iterator<DockingActionIf> getAllActionsIterator() {
|
||||
|
||||
// chain all items together, rather than copy the data
|
||||
Iterator<DockingActionIf> iterator = IteratorUtils.emptyIterator();
|
||||
Collection<Map<String, Set<DockingActionIf>>> maps = actionsByNameByOwner.values();
|
||||
for (Map<String, Set<DockingActionIf>> actionsByName : maps) {
|
||||
for (Set<DockingActionIf> actions : actionsByName.values()) {
|
||||
Iterator<DockingActionIf> next = actions.iterator();
|
||||
|
||||
// Note: do not use apache commons here--the code below degrades exponentially
|
||||
//iterator = IteratorUtils.chainedIterator(iterator, next);
|
||||
iterator = Iterators.concat(iterator, next);
|
||||
}
|
||||
}
|
||||
|
||||
return Iterators.concat(iterator, sharedActionMap.values().iterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the keybindings for each action so that they are still registered as being used;
|
||||
* otherwise the options will be removed because they are noted as not being used.
|
||||
*/
|
||||
public synchronized void restoreKeyBindings() {
|
||||
keyBindingOptions = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
Set<DockingActionIf> actions = getAllActions();
|
||||
for (DockingActionIf action : actions) {
|
||||
if (!action.isKeyBindingManaged()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Iterator<DockingActionIf> it = getKeyBindingActionsIterator();
|
||||
for (DockingActionIf action : CollectionUtils.asIterable(it)) {
|
||||
KeyStroke ks = action.getKeyBinding();
|
||||
KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks);
|
||||
if (ks != newKs) {
|
||||
if (!Objects.equals(ks, newKs)) {
|
||||
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return only actions that allow key bindings
|
||||
private Iterator<DockingActionIf> getKeyBindingActionsIterator() {
|
||||
Predicate<DockingActionIf> filter = a -> a.getKeyBindingType() == KeyBindingType.INDIVIDUAL;
|
||||
return IteratorUtils.filteredIterator(getAllActionsIterator(), filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an action that works specifically with a component provider.
|
||||
* @param provider provider associated with the action
|
||||
* @param action local action to the provider
|
||||
*/
|
||||
public synchronized void removeProviderAction(ComponentProvider provider,
|
||||
DockingActionIf action) {
|
||||
@Override
|
||||
public synchronized void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||
action.removePropertyChangeListener(this);
|
||||
removeAction(action);
|
||||
keyBindingsManager.removeAction(action);
|
||||
actionGuiHelper.removeProviderAction(provider, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions for the given provider and remove them from the action map
|
||||
* @param provider provider whose actions are to be removed
|
||||
*/
|
||||
public synchronized void removeComponentActions(ComponentProvider provider) {
|
||||
@Override
|
||||
public synchronized void removeActions(ComponentProvider provider) {
|
||||
Iterator<DockingActionIf> it = actionGuiHelper.getComponentActions(provider);
|
||||
|
||||
// copy the data to avoid concurrent modification exceptions
|
||||
Set<DockingActionIf> set = CollectionUtils.asSet(it);
|
||||
for (DockingActionIf action : set) {
|
||||
removeProviderAction(provider, action);
|
||||
removeLocalAction(provider, action);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAction(DockingActionIf action) {
|
||||
|
||||
keyBindingsManager.removeAction(action);
|
||||
|
||||
getActionStorage(action).remove(action);
|
||||
if (action.usesSharedKeyBinding()) {
|
||||
SharedStubKeyBindingAction stub = sharedActionMap.get(action.getName());
|
||||
if (stub != null) {
|
||||
stub.removeClientAction(action);
|
||||
}
|
||||
if (!action.getKeyBindingType().isShared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SharedStubKeyBindingAction stub = sharedActionMap.get(action.getName());
|
||||
if (stub != null) {
|
||||
stub.removeClientAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,27 +342,59 @@ public class ToolActions implements PropertyChangeListener {
|
|||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
||||
DockingAction action = (DockingAction) evt.getSource();
|
||||
if (!action.isKeyBindingManaged()) {
|
||||
dockingTool.setConfigChanged(true);
|
||||
return;
|
||||
}
|
||||
KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue();
|
||||
KeyStroke newKeyStroke = keyBindingData.getKeyBinding();
|
||||
Options opt = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
KeyStroke optKeyStroke = opt.getKeyStroke(action.getFullName(), null);
|
||||
if (newKeyStroke == null) {
|
||||
opt.removeOption(action.getFullName());
|
||||
}
|
||||
else if (!newKeyStroke.equals(optKeyStroke)) {
|
||||
opt.setKeyStroke(action.getFullName(), newKeyStroke);
|
||||
dockingTool.setConfigChanged(true);
|
||||
if (!evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DockingAction action = (DockingAction) evt.getSource();
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
KeyBindingData newKeyBindingData = (KeyBindingData) evt.getNewValue();
|
||||
KeyStroke newKeyStroke = null;
|
||||
if (newKeyBindingData != null) {
|
||||
newKeyStroke = newKeyBindingData.getKeyBinding();
|
||||
}
|
||||
|
||||
Options opt = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
KeyStroke optKeyStroke = opt.getKeyStroke(action.getFullName(), null);
|
||||
if (newKeyStroke == null) {
|
||||
opt.removeOption(action.getFullName());
|
||||
}
|
||||
else if (!newKeyStroke.equals(optKeyStroke)) {
|
||||
opt.setKeyStroke(action.getFullName(), newKeyStroke);
|
||||
keyBindingsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockingActionIf getLocalAction(ComponentProvider provider, String actionName) {
|
||||
|
||||
Iterator<DockingActionIf> it = actionGuiHelper.getComponentActions(provider);
|
||||
while (it.hasNext()) {
|
||||
DockingActionIf action = it.next();
|
||||
if (action.getName().equals(actionName)) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Action getAction(KeyStroke ks) {
|
||||
return keyBindingsManager.getDockingKeyAction(ks);
|
||||
}
|
||||
|
||||
DockingActionIf getSharedStubKeyBindingAction(String name) {
|
||||
return sharedActionMap.get(name);
|
||||
}
|
||||
|
||||
// triggered by a user-initiated action
|
||||
void keyBindingsChanged() {
|
||||
dockingTool.setConfigChanged(true);
|
||||
actionGuiHelper.keyBindingsChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -24,7 +23,7 @@ import docking.action.DockingActionIf;
|
|||
import docking.action.MenuData;
|
||||
|
||||
/**
|
||||
* Manages the main menu bar on the main frame.
|
||||
* Manages the main menu bar on the main frame
|
||||
*/
|
||||
public class MenuBarManager implements MenuGroupListener {
|
||||
|
||||
|
@ -32,36 +31,41 @@ public class MenuBarManager implements MenuGroupListener {
|
|||
private Map<String, MenuManager> menuManagers;
|
||||
private final MenuGroupMap menuGroupMap;
|
||||
|
||||
/**
|
||||
* Constructs a new MenuBarManager
|
||||
*/
|
||||
public MenuBarManager(MenuHandler actionHandler, MenuGroupMap menuGroupMap) {
|
||||
this.menuGroupMap = menuGroupMap;
|
||||
menuManagers = new TreeMap<String, MenuManager>();
|
||||
menuManagers = new TreeMap<>();
|
||||
this.menuHandler = actionHandler;
|
||||
}
|
||||
|
||||
public void clearActions() {
|
||||
menuManagers = new TreeMap<String, MenuManager>();
|
||||
menuManagers = new TreeMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action to the menu.
|
||||
* @param action the action to be added.
|
||||
* @param groupMgr the MenuGroupMap
|
||||
* Adds an action to the menu
|
||||
* @param action the action to be added
|
||||
*/
|
||||
public void addAction(DockingActionIf action) {
|
||||
MenuManager menuManager = getMenuManager(action);
|
||||
if (menuManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
menuManager.addAction(action);
|
||||
}
|
||||
|
||||
private MenuManager getMenuManager(DockingActionIf action) {
|
||||
MenuData menuBarData = action.getMenuBarData();
|
||||
if (menuBarData == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] menuPath = menuBarData.getMenuPath();
|
||||
if (menuPath == null || menuPath.length <= 1) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
MenuManager menuMgr = getMenuManager(menuPath[0]);
|
||||
menuMgr.addAction(action);
|
||||
|
||||
return getMenuManager(menuPath[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,17 +108,13 @@ public class MenuBarManager implements MenuGroupListener {
|
|||
|
||||
MenuManager mgr = menuManagers.get(menuName);
|
||||
if (mgr == null) {
|
||||
mgr =
|
||||
new MenuManager(menuName, new String[] { menuName }, mk, 1, null, false,
|
||||
menuHandler, menuGroupMap);
|
||||
mgr = new MenuManager(menuName, new String[] { menuName }, mk, 1, null, false,
|
||||
menuHandler, menuGroupMap);
|
||||
menuManagers.put(menuName, mgr);
|
||||
}
|
||||
return mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JMenuBar for all the actions.
|
||||
*/
|
||||
public JMenuBar getMenuBar() {
|
||||
MenuManager fileMenu = menuManagers.get("File");
|
||||
MenuManager editMenu = menuManagers.get("Edit");
|
||||
|
@ -153,6 +153,7 @@ public class MenuBarManager implements MenuGroupListener {
|
|||
* @param menuPath the menu path whose group changed.
|
||||
* @param group the new group for the given menuPath.
|
||||
*/
|
||||
@Override
|
||||
public void menuGroupChanged(String[] menuPath, String group) {
|
||||
if (menuPath != null && menuPath.length > 1) {
|
||||
MenuManager mgr = getMenuManager(menuPath[0]);
|
||||
|
|
|
@ -43,13 +43,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
// listeners to handle help activation
|
||||
// -this listener covers activation by keyboard and by mouse *when enabled*
|
||||
private ChangeListener buttonModelChangeListener;
|
||||
|
||||
// -this listener covers activation by mouse *when the action is disabled*
|
||||
private MouseAdapter menuHoverListener;
|
||||
|
||||
/**
|
||||
* Constructs a new MenuItemManger
|
||||
* @param dockableAction the action whose menuItem is being managed.
|
||||
*/
|
||||
MenuItemManager(MenuHandler actionHandler, DockingActionIf dockingAction,
|
||||
boolean usePopupPath) {
|
||||
this.menuHandler = actionHandler;
|
||||
|
@ -104,9 +101,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#getWindowGroup()
|
||||
*/
|
||||
@Override
|
||||
public String getGroup() {
|
||||
MenuData menuData = isPopup ? action.getPopupMenuData() : action.getMenuBarData();
|
||||
|
@ -119,10 +113,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
return menuData == null ? null : menuData.getMenuSubGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#dispose()
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (action != null) {
|
||||
|
@ -138,9 +128,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
action = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#getMenuItem()
|
||||
*/
|
||||
@Override
|
||||
public JMenuItem getMenuItem() {
|
||||
if (menuItem != null) {
|
||||
|
@ -158,17 +145,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
return menuItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner associated with this items action.
|
||||
*/
|
||||
public String getOwner() {
|
||||
return action.getOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the menuItem to reflect changes in the actions properties.
|
||||
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
|
||||
*/
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent e) {
|
||||
if (menuItem == null) {
|
||||
|
@ -209,16 +189,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action associated with this menu item.
|
||||
*/
|
||||
public DockingActionIf getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (menuHandler != null) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -30,8 +29,8 @@ import docking.action.MenuData;
|
|||
public class MenuManager implements ManagedMenuItem {
|
||||
private static String NULL_GROUP_NAME = "<null group>";
|
||||
|
||||
private Set<ManagedMenuItem> managedMenuItems = new HashSet<ManagedMenuItem>();
|
||||
private Map<String, MenuManager> subMenus = new HashMap<String, MenuManager>();
|
||||
private Set<ManagedMenuItem> managedMenuItems = new HashSet<>();
|
||||
private Map<String, MenuManager> subMenus = new HashMap<>();
|
||||
|
||||
private String name;
|
||||
private final String[] menuPath;
|
||||
|
@ -50,9 +49,9 @@ public class MenuManager implements ManagedMenuItem {
|
|||
* @param name the name of the menu.
|
||||
* @param mnemonicKey the key to use for the menu mnemonic
|
||||
* @param group the group of the menu.
|
||||
* @param showKeyBindings if true, includes the keybinding text on the menu item.
|
||||
* @param usePopupPath if true, registers actions with popup paths as popup items.
|
||||
* @param menuHandler Listener to be notified of menu behavior.
|
||||
* @param menuGroupMap maps menu groups to menu paths
|
||||
*/
|
||||
public MenuManager(String name, char mnemonicKey, String group, boolean usePopupPath,
|
||||
MenuHandler menuHandler, MenuGroupMap menuGroupMap) {
|
||||
|
@ -68,9 +67,9 @@ public class MenuManager implements ManagedMenuItem {
|
|||
* @param mnemonicKey the key to use for the menu mnemonic
|
||||
* @param level the number of parent menus that this menu is in.
|
||||
* @param group the group of this menu.
|
||||
* @param showKeyBindings if true, includes the keybinding text on the menu item.
|
||||
* @param usePopupPath if true, registers actions with popup paths as popup items.
|
||||
* @param menuHandler Listener to be notified of menu behavior.
|
||||
* @param menuGroupMap maps menu groups to menu paths
|
||||
*/
|
||||
MenuManager(String name, String[] menuPath, char mnemonicKey, int level, String group,
|
||||
boolean usePopupPath, MenuHandler menuHandler, MenuGroupMap menuGroupMap) {
|
||||
|
@ -96,10 +95,8 @@ public class MenuManager implements ManagedMenuItem {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds an action to this menu. Can create subMenus depending on the menuPath of the
|
||||
* action.
|
||||
* @param action the action to be added.
|
||||
* @param menuGroupMap group map for menuItems
|
||||
* Adds an action to this menu. Can create subMenus depending on the menuPath of the action
|
||||
* @param action the action to be added
|
||||
*/
|
||||
public void addAction(DockingActionIf action) {
|
||||
checkForSwingThread();
|
||||
|
@ -122,9 +119,8 @@ public class MenuManager implements ManagedMenuItem {
|
|||
submenuGroup = subMenuName;
|
||||
}
|
||||
|
||||
mgr =
|
||||
new MenuManager(cleanSubMenuName, submenuPath, mnemonic, submenuLevel,
|
||||
submenuGroup, usePopupPath, menuHandler, menuGroupMap);
|
||||
mgr = new MenuManager(cleanSubMenuName, submenuPath, mnemonic, submenuLevel,
|
||||
submenuGroup, usePopupPath, menuHandler, menuGroupMap);
|
||||
subMenus.put(cleanSubMenuName, mgr);
|
||||
managedMenuItems.add(mgr);
|
||||
}
|
||||
|
@ -198,7 +194,7 @@ public class MenuManager implements ManagedMenuItem {
|
|||
menu.addMenuListener(menuHandler);
|
||||
}
|
||||
|
||||
List<ManagedMenuItem> list = new ArrayList<ManagedMenuItem>(managedMenuItems);
|
||||
List<ManagedMenuItem> list = new ArrayList<>(managedMenuItems);
|
||||
Collections.sort(list, comparator);
|
||||
String lastGroup = null;
|
||||
|
||||
|
@ -259,7 +255,7 @@ public class MenuManager implements ManagedMenuItem {
|
|||
if (popupMenu == null) {
|
||||
popupMenu = new JPopupMenu(name);
|
||||
|
||||
List<ManagedMenuItem> list = new ArrayList<ManagedMenuItem>(managedMenuItems);
|
||||
List<ManagedMenuItem> list = new ArrayList<>(managedMenuItems);
|
||||
Collections.sort(list, comparator);
|
||||
String lastGroup = NULL_GROUP_NAME;
|
||||
boolean hasMenuItems = false;
|
||||
|
|
|
@ -15,16 +15,18 @@
|
|||
*/
|
||||
package docking.menu;
|
||||
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
import java.awt.event.*;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import ghidra.docking.util.DockingWindowsLookAndFeelUtils;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
/**
|
||||
* Class to manager toolbar buttons.
|
||||
|
@ -78,7 +80,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
}
|
||||
|
||||
private void setToolTipText(JButton button, DockingActionIf action, String toolTipText) {
|
||||
String keyBindingText = getKeyBindingAcceleratorText(action.getKeyBinding());
|
||||
String keyBindingText = getKeyBindingAcceleratorText(button, action.getKeyBinding());
|
||||
if (keyBindingText != null) {
|
||||
button.setToolTipText(combingToolTipTextWithKeyBinding(toolTipText, keyBindingText));
|
||||
}
|
||||
|
@ -93,7 +95,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
StringBuilder buffy = new StringBuilder(toolTipText);
|
||||
if (StringUtilities.startsWithIgnoreCase(toolTipText, "<HTML>")) {
|
||||
String endHTMLTag = "</HTML>";
|
||||
int closeTagIndex = StringUtilities.indexOfIgnoreCase(toolTipText, endHTMLTag);
|
||||
int closeTagIndex = StringUtils.indexOfIgnoreCase(toolTipText, endHTMLTag);
|
||||
if (closeTagIndex < 0) {
|
||||
// no closing tag, which is acceptable
|
||||
buffy.append(START_KEYBINDING_TEXT).append(keyBindingText).append(
|
||||
|
@ -120,7 +122,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
return action.getName();
|
||||
}
|
||||
|
||||
private String getKeyBindingAcceleratorText(KeyStroke keyStroke) {
|
||||
private String getKeyBindingAcceleratorText(JButton button, KeyStroke keyStroke) {
|
||||
if (keyStroke == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -129,8 +131,12 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
StringBuilder builder = new StringBuilder();
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
if (modifiers > 0) {
|
||||
builder.append(KeyEvent.getKeyModifiersText(modifiers));
|
||||
builder.append('+');
|
||||
builder.append(InputEvent.getModifiersExText(modifiers));
|
||||
|
||||
// The Aqua LaF does not use the '+' symbol between modifiers
|
||||
if (!DockingWindowsLookAndFeelUtils.isUsingAquaUI(button.getUI())) {
|
||||
builder.append('+');
|
||||
}
|
||||
}
|
||||
int keyCode = keyStroke.getKeyCode();
|
||||
if (keyCode != 0) {
|
||||
|
@ -204,16 +210,13 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
final ActionContext finalContext = tempContext;
|
||||
|
||||
// this gives the UI some time to repaint before executing the action
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (toolBarAction.isEnabledForContext(finalContext)) {
|
||||
if (toolBarAction instanceof ToggleDockingActionIf) {
|
||||
ToggleDockingActionIf toggleAction = (ToggleDockingActionIf) toolBarAction;
|
||||
toggleAction.setSelected(!toggleAction.isSelected());
|
||||
}
|
||||
toolBarAction.actionPerformed(finalContext);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (toolBarAction.isEnabledForContext(finalContext)) {
|
||||
if (toolBarAction instanceof ToggleDockingActionIf) {
|
||||
ToggleDockingActionIf toggleAction = (ToggleDockingActionIf) toolBarAction;
|
||||
toggleAction.setSelected(!toggleAction.isSelected());
|
||||
}
|
||||
toolBarAction.actionPerformed(finalContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import docking.widgets.VariableHeightPanel;
|
|||
*/
|
||||
public class ToolBarManager {
|
||||
private Map<String, List<ToolBarItemManager>> groupToItemsMap =
|
||||
new TreeMap<String, List<ToolBarItemManager>>(new GroupComparator());
|
||||
new TreeMap<>(new GroupComparator());
|
||||
private Comparator<? super ToolBarItemManager> toolBarItemComparator =
|
||||
new ToolBarItemManagerComparator();
|
||||
|
||||
|
@ -47,9 +47,6 @@ public class ToolBarManager {
|
|||
toolBar = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the action to the toolbar.
|
||||
*/
|
||||
public void addAction(DockingActionIf action) {
|
||||
ToolBarData toolBarData = action.getToolBarData();
|
||||
if (toolBarData == null) {
|
||||
|
@ -61,7 +58,7 @@ public class ToolBarManager {
|
|||
String group = toolBarData.getToolBarGroup();
|
||||
List<ToolBarItemManager> items = groupToItemsMap.get(group);
|
||||
if (items == null) {
|
||||
items = new ArrayList<ToolBarItemManager>();
|
||||
items = new ArrayList<>();
|
||||
groupToItemsMap.put(group, items);
|
||||
}
|
||||
items.add(new ToolBarItemManager(action, windowManager));
|
||||
|
@ -96,9 +93,6 @@ public class ToolBarManager {
|
|||
groupToItemsMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the toolbar is empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return groupToItemsMap.isEmpty();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.google.common.collect.Sets;
|
|||
import docking.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingActionIf;
|
||||
import docking.actions.DockingToolActions;
|
||||
import docking.dnd.GClipboard;
|
||||
import docking.framework.DockingApplicationConfiguration;
|
||||
import docking.menu.DockingToolbarButton;
|
||||
|
@ -1114,7 +1115,8 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* A helper method to find all actions with the given owner's name
|
||||
* A helper method to find all actions with the given owner's name (this will not include
|
||||
* reserved system actions)
|
||||
*
|
||||
* @param tool the tool containing all system actions
|
||||
* @param name the owner's name to match
|
||||
|
@ -1125,7 +1127,8 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* A helper method to find all actions by name, with the given owner's name
|
||||
* A helper method to find all actions by name, with the given owner's name (this will not
|
||||
* include reserved system actions)
|
||||
*
|
||||
* @param tool the tool containing all system actions
|
||||
* @param owner the owner's name
|
||||
|
@ -1167,7 +1170,8 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
/**
|
||||
* Finds the action by the given owner name and action name.
|
||||
* If you do not know the owner name, then use
|
||||
* the call {@link #getActionsByName(DockingTool, String)} instead.
|
||||
* the call {@link #getActionsByName(DockingTool, String)} instead (this will not include
|
||||
* reserved system actions).
|
||||
*
|
||||
* <P>Note: more specific test case subclasses provide other methods for finding actions
|
||||
* when you have an owner name (which is usually the plugin name).
|
||||
|
@ -1192,8 +1196,18 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
return CollectionUtils.any(actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action by the given name that belongs to the given provider
|
||||
*
|
||||
* @param provider the provider
|
||||
* @param actionName the action name
|
||||
* @return the action
|
||||
*/
|
||||
public static DockingActionIf getLocalAction(ComponentProvider provider, String actionName) {
|
||||
return getAction(provider.getTool(), provider.getName(), actionName);
|
||||
DockingTool tool = provider.getTool();
|
||||
DockingToolActions toolActions = tool.getToolActions();
|
||||
DockingActionIf action = toolActions.getLocalAction(provider, actionName);
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1847,10 +1861,12 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
*
|
||||
* @param tool the tool in which the provider lives
|
||||
* @param name the name of the provider to show
|
||||
* @return the newly shown provider
|
||||
*/
|
||||
public void showProvider(DockingTool tool, String name) {
|
||||
public ComponentProvider showProvider(DockingTool tool, String name) {
|
||||
ComponentProvider provider = tool.getComponentProvider(name);
|
||||
tool.showComponentProvider(provider, true);
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
package docking.widgets.table;
|
||||
|
||||
import static docking.DockingUtils.*;
|
||||
import static docking.action.MenuData.*;
|
||||
import static java.awt.event.InputEvent.*;
|
||||
import static docking.DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
import static docking.action.MenuData.NO_MNEMONIC;
|
||||
import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
@ -509,23 +509,14 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
|
|||
return autoLookupKeyStrokeConsumer.isKeyConsumed(keyStroke);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<DockingActionIf> getDockingActions(ActionContext context) {
|
||||
Object sourceObject = context.getSourceObject();
|
||||
if (sourceObject != this) {
|
||||
// we are only interested in providing actions when we are the source of the event
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<DockingActionIf> getDockingActions() {
|
||||
return getDefaultDockingActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default actions of this table. Normally, the Docking Windows systems uses
|
||||
* {@link #getDockingActions(ActionContext)} to get the correct actions to show. However,
|
||||
* {@link #getDockingActions()} to get the correct actions to show. However,
|
||||
* there are some cases where clients override what appears when you click on a table (such
|
||||
* as in {@link DialogComponentProvider}s. For those clients that are creating their own
|
||||
* action building, they need a way to get the default actions, hence this method.
|
||||
|
@ -1178,7 +1169,8 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
|
|||
|
||||
int subGroupIndex = 1; // order by insertion
|
||||
String owner = getClass().getSimpleName();
|
||||
copyAction = new DockingAction("Table Data Copy", owner, false) {
|
||||
owner = "GTable";
|
||||
copyAction = new DockingAction("Table Data Copy", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
copying = true;
|
||||
|
@ -1209,7 +1201,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
|
|||
//@formatter:on
|
||||
|
||||
copyCurrentColumnAction =
|
||||
new DockingAction("Table Data Copy Current Column", owner, false) {
|
||||
new DockingAction("Table Data Copy Current Column", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
|
@ -1246,17 +1238,18 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
|
|||
copyCurrentColumnAction.setHelpLocation(new HelpLocation("Tables", "Copy_Current_Column"));
|
||||
//@formatter:on
|
||||
|
||||
copyColumnsAction = new DockingAction("Table Data Copy by Columns", owner, false) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
int[] userColumns = promptUserForColumns();
|
||||
if (userColumns == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
copyColumnsAction =
|
||||
new DockingAction("Table Data Copy by Columns", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
int[] userColumns = promptUserForColumns();
|
||||
if (userColumns == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
|
||||
copyColumns(userColumns);
|
||||
}
|
||||
};
|
||||
copyColumns(userColumns);
|
||||
}
|
||||
};
|
||||
//@formatter:off
|
||||
copyColumnsAction.setPopupMenuData(new MenuData(
|
||||
new String[] { "Copy", "Copy Columns..." },
|
||||
|
@ -1269,7 +1262,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
|
|||
copyColumnsAction.setHelpLocation(new HelpLocation("Tables", "Copy_Columns"));
|
||||
//@formatter:on
|
||||
|
||||
exportAction = new DockingAction("Table Data CSV Export", owner, false) {
|
||||
exportAction = new DockingAction("Table Data CSV Export", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
File file = chooseExportFile();
|
||||
|
@ -1291,7 +1284,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
|
|||
//@formatter:on
|
||||
|
||||
exportColumnsAction =
|
||||
new DockingAction("Table Data CSV Export (by Columns)", owner, false) {
|
||||
new DockingAction("Table Data CSV Export (by Columns)", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
int[] userColumns = promptUserForColumns();
|
||||
|
@ -1323,7 +1316,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr
|
|||
exportColumnsAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV_Columns"));
|
||||
//@formatter:on
|
||||
|
||||
selectAllAction = new DockingAction("Table Select All", owner, false) {
|
||||
selectAllAction = new DockingAction("Table Select All", owner, KeyBindingType.SHARED) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
selectAll();
|
||||
|
|
|
@ -35,9 +35,11 @@ import docking.tool.util.DockingToolConstants;
|
|||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SpyErrorLogger;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
||||
|
||||
private static final String NON_SHARED_NAME = "Non-Shared Action Name";
|
||||
private static final String SHARED_NAME = "Shared Action Name";
|
||||
private static final String SHARED_OWNER = SharedStubKeyBindingAction.SHARED_OWNER;
|
||||
|
||||
|
@ -167,7 +169,14 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1);
|
||||
|
||||
tool.addAction(action1);
|
||||
tool.addAction(action1);
|
||||
|
||||
try {
|
||||
tool.addAction(action1);
|
||||
fail("Did not get expected exception");
|
||||
}
|
||||
catch (AssertException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
assertOnlyOneVersionOfActionInTool(action1);
|
||||
|
||||
|
@ -294,7 +303,7 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testNonSharedKeyBinding_SameActionAddedTwice() {
|
||||
public void testSharedKeyBinding_SameActionAddedTwice() {
|
||||
//
|
||||
// We support adding the same action twice. (This can happen when a transient component
|
||||
// provider is repeatedly shown, such as a search results provider.) Make sure we get
|
||||
|
@ -323,7 +332,7 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testNonSharedKeyBinding_DifferentActionsWithSameFullName() {
|
||||
public void testSharedKeyBinding_DifferentActionsWithSameFullName() {
|
||||
//
|
||||
// We support adding the same action twice. (This can happen when a transient component
|
||||
// provider is repeatedly shown, such as a search results provider.) Make sure we get
|
||||
|
@ -351,17 +360,47 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
assertActionNotInTool(action1Copy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonSharedKeyBinding_DifferentActionsWithSameFullName() {
|
||||
//
|
||||
// We support adding the same action twice. (This can happen when a transient component
|
||||
// provider is repeatedly shown, such as a search results provider.) Make sure we get
|
||||
// a warning if the same action is added twice, but with different key bindings.
|
||||
//
|
||||
// Note: in this context, two actions are considered to be the same if they share the
|
||||
// same name and owner.
|
||||
//
|
||||
|
||||
TestNonSharedAction action1 = new TestNonSharedAction(OWNER_1, DEFAULT_KS_1);
|
||||
TestNonSharedAction action1Copy =
|
||||
new TestNonSharedAction(OWNER_1, DEFAULT_KS_DIFFERENT_THAN_1);
|
||||
|
||||
tool.addAction(action1);
|
||||
tool.addAction(action1Copy);
|
||||
assertActionInTool(action1);
|
||||
assertActionInTool(action1Copy);
|
||||
|
||||
assertImproperDefaultBindingMessage();
|
||||
|
||||
tool.removeAction(action1);
|
||||
assertActionNotInTool(action1);
|
||||
assertActionInTool(action1Copy);
|
||||
|
||||
tool.removeAction(action1Copy);
|
||||
assertActionNotInTool(action1Copy);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void assertSharedStubInTool() {
|
||||
ToolActions actionManager = (ToolActions) getInstanceField("actionMgr", tool);
|
||||
ToolActions actionManager = (ToolActions) getInstanceField("toolActions", tool);
|
||||
DockingActionIf action = actionManager.getSharedStubKeyBindingAction(SHARED_NAME);
|
||||
assertNotNull("Shared action stub is not in the tool", action);
|
||||
}
|
||||
|
||||
private void assertOnlyOneVersionOfActionInTool(TestAction action) {
|
||||
private void assertOnlyOneVersionOfActionInTool(DockingActionIf action) {
|
||||
|
||||
// this method will fail if more than one action is registered
|
||||
DockingActionIf registeredAction = getAction(tool, action.getOwner(), action.getName());
|
||||
|
@ -369,7 +408,7 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
registeredAction);
|
||||
}
|
||||
|
||||
private void assertActionInTool(TestAction action) {
|
||||
private void assertActionInTool(DockingActionIf action) {
|
||||
|
||||
Set<DockingActionIf> actions = getActionsByName(tool, action.getName());
|
||||
for (DockingActionIf toolAction : actions) {
|
||||
|
@ -381,7 +420,7 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
fail("Action is not in the tool: " + action);
|
||||
}
|
||||
|
||||
private void assertActionNotInTool(TestAction action) {
|
||||
private void assertActionNotInTool(DockingActionIf action) {
|
||||
Set<DockingActionIf> actions = getActionsByName(tool, action.getName());
|
||||
for (DockingActionIf toolAction : actions) {
|
||||
assertNotSame(toolAction, action);
|
||||
|
@ -426,16 +465,21 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
private class TestAction extends DockingAction {
|
||||
|
||||
public TestAction(String owner, KeyStroke ks) {
|
||||
super(SHARED_NAME, owner);
|
||||
|
||||
if (ks != null) {
|
||||
setKeyBindingData(new KeyBindingData(ks));
|
||||
}
|
||||
super(SHARED_NAME, owner, KeyBindingType.SHARED);
|
||||
setKeyBindingData(new KeyBindingData(ks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesSharedKeyBinding() {
|
||||
return true;
|
||||
public void actionPerformed(ActionContext context) {
|
||||
fail("Action performed should not have been called");
|
||||
}
|
||||
}
|
||||
|
||||
private class TestNonSharedAction extends DockingAction {
|
||||
|
||||
public TestNonSharedAction(String owner, KeyStroke ks) {
|
||||
super(NON_SHARED_NAME, owner, KeyBindingType.INDIVIDUAL);
|
||||
setKeyBindingData(new KeyBindingData(ks));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,9 +35,9 @@ public class FakeDockingTool extends AbstractDockingTool {
|
|||
|
||||
DockWinListener listener = new DummyListener();
|
||||
List<Image> windowIcons = ApplicationInformationDisplayFactory.getWindowIcons();
|
||||
winMgr = new DockingWindowManager("EMPTY", windowIcons, listener, false /*isModal*/,
|
||||
winMgr = new DockingWindowManager(this, windowIcons, listener, false /*isModal*/,
|
||||
true /*isDockable*/, true /*hasStatus*/, null /*DropTargetFactory*/);
|
||||
actionMgr = new ToolActions(this, winMgr);
|
||||
toolActions = new ToolActions(this, new ActionToGuiHelper(winMgr));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,7 +71,5 @@ public class FakeDockingTool extends AbstractDockingTool {
|
|||
public List<DockingActionIf> getPopupActions(ActionContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue