mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GT-2925 - Key Bindings - Support Window Menu Provider Key Bindings -
Step 7 - Untangled and removed the key binding management from the ActionToGuiMapper; fixed bugs and tests
This commit is contained in:
parent
d684ee3ce6
commit
3946a05ded
27 changed files with 515 additions and 345 deletions
|
@ -59,7 +59,7 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
public void addComponentProvider(ComponentProvider provider, boolean show) {
|
||||
Runnable r = () -> {
|
||||
winMgr.addComponent(provider, show);
|
||||
toolActions.addToolAction(provider.getShowProviderAction());
|
||||
toolActions.addGlobalAction(provider.getShowProviderAction());
|
||||
};
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
@Override
|
||||
public void removeComponentProvider(ComponentProvider provider) {
|
||||
Runnable r = () -> {
|
||||
toolActions.removeComponentActions(provider);
|
||||
toolActions.removeActions(provider);
|
||||
winMgr.removeComponent(provider);
|
||||
};
|
||||
Swing.runNow(r);
|
||||
|
@ -99,12 +99,12 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
|
||||
@Override
|
||||
public void addAction(DockingActionIf action) {
|
||||
toolActions.addToolAction(action);
|
||||
toolActions.addGlobalAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAction(DockingActionIf action) {
|
||||
toolActions.removeToolAction(action);
|
||||
toolActions.removeGlobalAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,15 +114,12 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
|
||||
@Override
|
||||
public void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||
toolActions.removeProviderAction(provider, action);
|
||||
toolActions.removeLocalAction(provider, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DockingActionIf> getAllActions() {
|
||||
Set<DockingActionIf> actions = toolActions.getAllActions();
|
||||
ActionToGuiMapper am = winMgr.getActionToGuiMapper();
|
||||
actions.addAll(am.getAllActions());
|
||||
return actions;
|
||||
return toolActions.getAllActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -130,6 +127,11 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
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);
|
||||
|
@ -176,6 +178,11 @@ 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);
|
||||
|
@ -200,4 +207,9 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
public boolean hasConfigChanged() {
|
||||
return configChangedFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToolActions getToolActions() {
|
||||
return toolActions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package docking;
|
|||
import java.util.Iterator;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.KeyBindingsManager;
|
||||
|
||||
/**
|
||||
* A class that exists primarily to provide access to action-related package-level methods of the
|
||||
|
@ -80,15 +79,6 @@ public class ActionToGuiHelper {
|
|||
windowManager.removeProviderAction(provider, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes key bindings manager of docking window manager
|
||||
*
|
||||
* @param keyBindingsManager key bindings manager
|
||||
*/
|
||||
public void setKeyBindingsManager(KeyBindingsManager keyBindingsManager) {
|
||||
windowManager.setKeyBindingsManager(keyBindingsManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to signal that key bindings for one or more actions have changed
|
||||
*/
|
||||
|
|
|
@ -16,63 +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 static boolean enableDiagnosticActions;
|
||||
|
||||
private Set<DockingActionIf> globalActions = new LinkedHashSet<>();
|
||||
|
||||
private MenuHandler menuBarMenuHandler;
|
||||
private MenuGroupMap menuGroupMap;
|
||||
|
||||
private KeyBindingsManager keyBindingsManager;
|
||||
private GlobalMenuAndToolBarManager menuAndToolBarManager;
|
||||
private PopupActionManager popupActionManager;
|
||||
|
||||
ActionToGuiMapper(DockingWindowManager winMgr, KeyBindingsManager keyBindingsManager) {
|
||||
this.keyBindingsManager = keyBindingsManager;
|
||||
ActionToGuiMapper(DockingWindowManager winMgr) {
|
||||
menuGroupMap = new MenuGroupMap();
|
||||
menuBarMenuHandler = new MenuBarMenuHandler(winMgr);
|
||||
menuAndToolBarManager =
|
||||
new GlobalMenuAndToolBarManager(winMgr, menuBarMenuHandler, menuGroupMap);
|
||||
popupActionManager = new PopupActionManager(winMgr, menuGroupMap);
|
||||
|
||||
initializeHelpActions();
|
||||
}
|
||||
|
||||
private void initializeHelpActions() {
|
||||
DockingWindowsContextSensitiveHelpListener.install();
|
||||
|
||||
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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,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);
|
||||
}
|
||||
|
@ -132,24 +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;
|
||||
}
|
||||
|
||||
Set<DockingActionIf> getGlobalActions() {
|
||||
return globalActions;
|
||||
}
|
||||
|
@ -162,28 +95,16 @@ public class ActionToGuiMapper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all menus (includes popup menus)
|
||||
*/
|
||||
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();
|
||||
|
@ -216,8 +137,4 @@ public class ActionToGuiMapper {
|
|||
public void showPopupMenu(ComponentPlaceholder componentInfo, MouseEvent e) {
|
||||
popupActionManager.popupMenu(componentInfo, e);
|
||||
}
|
||||
|
||||
Action getDockingKeyAction(KeyStroke keyStroke) {
|
||||
return keyBindingsManager.getDockingKeyAction(keyStroke);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -544,21 +544,6 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
}
|
||||
|
||||
if (isInTool()) {
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
4) Wire default 'close' action to keybinding
|
||||
5) Add global action for (show last provider)
|
||||
--Navigation menu?
|
||||
8) Update help locations
|
||||
|
||||
Questions:
|
||||
|
||||
C) How to wire universal close action (it is focus-dependent)
|
||||
|
||||
*/
|
||||
|
||||
dockingTool.getWindowManager().setIcon(this, icon);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,17 +27,16 @@ 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 {
|
||||
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) {
|
||||
public DockingKeyBindingAction(DockingTool tool, DockingActionIf action, KeyStroke keyStroke) {
|
||||
super(KeyBindingUtils.parseKeyStroke(keyStroke));
|
||||
this.winMgr = winMgr;
|
||||
this.tool = tool;
|
||||
this.docakbleAction = action;
|
||||
this.keyStroke = keyStroke;
|
||||
}
|
||||
|
@ -52,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);
|
||||
|
|
|
@ -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,15 @@ 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
|
||||
|
@ -246,4 +262,15 @@ public interface DockingTool {
|
|||
* @return true if the tool's configuration has changed
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ import javax.swing.*;
|
|||
import org.jdom.Element;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.KeyBindingsManager;
|
||||
import docking.actions.DockingToolActions;
|
||||
import docking.actions.ToolActions;
|
||||
import docking.help.HelpService;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
|
@ -73,6 +74,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
|
||||
private static List<DockingWindowManager> instanceList = new ArrayList<>();
|
||||
|
||||
private DockingTool tool;
|
||||
private RootNode root;
|
||||
|
||||
private PlaceholderManager placeholderManager;
|
||||
|
@ -105,18 +107,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 +127,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,7 +140,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
images = new ArrayList<>();
|
||||
}
|
||||
|
||||
root = new RootNode(this, toolName, images, modal, factory);
|
||||
root = new RootNode(this, tool.getName(), images, modal, factory);
|
||||
|
||||
KeyboardFocusManager km = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
km.addPropertyChangeListener("permanentFocusOwner", this);
|
||||
|
@ -145,6 +148,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
addInstance(this);
|
||||
|
||||
placeholderManager = new PlaceholderManager(this);
|
||||
actionToGuiMapper = new ActionToGuiMapper(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,15 +156,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.
|
||||
|
@ -310,17 +305,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 actionToGuiMapper.getDockingKeyAction(keyStroke);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this manager contains the given provider.
|
||||
*
|
||||
|
@ -362,6 +346,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);
|
||||
|
@ -640,24 +631,10 @@ 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) {
|
||||
actionToGuiMapper.removeAll(owner);
|
||||
placeholderManager.removeAll(owner);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Package-level Action Methods
|
||||
//==================================================================================================
|
||||
|
||||
void setKeyBindingsManager(KeyBindingsManager keyBindingsManager) {
|
||||
actionToGuiMapper = new ActionToGuiMapper(this, keyBindingsManager);
|
||||
}
|
||||
|
||||
Iterator<DockingActionIf> getComponentActions(ComponentProvider provider) {
|
||||
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
||||
if (placeholder != null) {
|
||||
|
@ -671,7 +648,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
||||
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
||||
if (placeholder != null) {
|
||||
actionToGuiMapper.removeLocalAction(action);
|
||||
placeholder.removeAction(action);
|
||||
}
|
||||
}
|
||||
|
@ -682,7 +658,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
throw new IllegalArgumentException("Unknown component provider: " + provider);
|
||||
}
|
||||
placeholder.addAction(action);
|
||||
actionToGuiMapper.addLocalAction(action, provider);
|
||||
}
|
||||
|
||||
void addToolAction(DockingActionIf action) {
|
||||
|
@ -695,9 +670,31 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
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.
|
||||
|
@ -1002,7 +999,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
Iterator<DockingActionIf> iter = placeholder.getActions();
|
||||
while (iter.hasNext()) {
|
||||
DockingActionIf action = iter.next();
|
||||
actionToGuiMapper.removeLocalAction(action);
|
||||
placeholder.removeAction(action);
|
||||
}
|
||||
|
||||
ComponentNode node = placeholder.getNode();
|
||||
|
@ -1093,7 +1090,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return;
|
||||
}
|
||||
|
||||
actionToGuiMapper.removeAll(DOCKING_WINDOWS_OWNER);
|
||||
tool.getToolActions().removeActions(DOCKING_WINDOWS_OWNER);
|
||||
|
||||
Map<String, List<ComponentPlaceholder>> permanentMap = new HashMap<>();
|
||||
Map<String, List<ComponentPlaceholder>> transientMap = new HashMap<>();
|
||||
|
@ -1160,9 +1157,11 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
actionList.add(new ShowAllComponentsAction(this, placeholders, subMenuName));
|
||||
}
|
||||
}
|
||||
|
||||
DockingToolActions toolActions = tool.getToolActions();
|
||||
Collections.sort(actionList);
|
||||
for (ShowComponentAction action : actionList) {
|
||||
actionToGuiMapper.addToolAction(action);
|
||||
toolActions.addGlobalAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1198,9 +1197,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
}
|
||||
}
|
||||
|
||||
DockingToolActions toolActions = tool.getToolActions();
|
||||
Collections.sort(actions);
|
||||
for (ShowWindowAction action : actions) {
|
||||
actionToGuiMapper.addToolAction(action);
|
||||
toolActions.addGlobalAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1208,6 +1208,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* Notifies the window manager that an update is needed
|
||||
*/
|
||||
void scheduleUpdate() {
|
||||
if (rebuildUpdater.isBusy()) {
|
||||
return;
|
||||
}
|
||||
rebuildUpdater.updateLater();
|
||||
}
|
||||
|
||||
|
@ -1225,7 +1228,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());
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
protected DockingWindowManager winMgr;
|
||||
private ComponentPlaceholder info;
|
||||
private String title;
|
||||
private boolean isTransient;
|
||||
|
||||
private static String truncateTitleAsNeeded(String title) {
|
||||
if (title.length() <= MAX_LENGTH) {
|
||||
|
@ -58,7 +57,6 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
this.info = placeholder;
|
||||
this.winMgr = winMgr;
|
||||
this.title = truncateTitleAsNeeded(placeholder.getTitle());
|
||||
this.isTransient = isTransient;
|
||||
String group = isTransient ? "Transient" : "Permanent";
|
||||
|
||||
Icon icon = placeholder.getIcon();
|
||||
|
@ -139,6 +137,10 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
|
||||
@Override
|
||||
public String getHelpInfo() {
|
||||
if (info == null) {
|
||||
return super.getHelpInfo();
|
||||
}
|
||||
|
||||
StringBuilder buffy = new StringBuilder(super.getHelpInfo());
|
||||
|
||||
ComponentProvider provider = info.getProvider();
|
||||
|
|
|
@ -17,7 +17,8 @@ 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;
|
||||
|
@ -30,11 +31,10 @@ 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;
|
||||
public KeyBindingsManager(DockingTool tool) {
|
||||
this.tool = tool;
|
||||
dockingKeyMap = new HashMap<>();
|
||||
actionToProviderMap = new HashMap<>();
|
||||
}
|
||||
|
@ -76,8 +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;
|
||||
}
|
||||
|
||||
|
@ -98,7 +97,7 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
|
||||
KeyBindingData binding = KeyBindingData.createReservedKeyBindingData(keyStroke);
|
||||
action.setKeyBindingData(binding);
|
||||
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(winMgr, action, keyStroke));
|
||||
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(tool, action, keyStroke));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,18 +150,12 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
public List<DockingActionIf> getLocalActions() {
|
||||
return new ArrayList<>(actionToProviderMap.keySet());
|
||||
}
|
||||
|
||||
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
||||
return dockingKeyMap.get(keyStroke);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
winMgr = null;
|
||||
dockingKeyMap.clear();
|
||||
actionToProviderMap.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,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);
|
||||
}
|
||||
|
||||
|
@ -134,11 +116,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
|
||||
|
@ -163,12 +145,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,9 +212,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);
|
||||
|
||||
|
@ -274,4 +256,22 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,18 +17,21 @@ package docking.action;
|
|||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.DockingKeyBindingAction;
|
||||
import docking.DockingWindowManager;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/* ###
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
|
@ -25,6 +25,7 @@ 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.*;
|
||||
|
@ -344,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 support {@link KeyBindingType key bindings}.
|
||||
* 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)) {
|
||||
|
@ -362,10 +369,10 @@ public class KeyBindingUtils {
|
|||
continue;
|
||||
}
|
||||
|
||||
deduper.put(action.getFullName(), action);
|
||||
result.get(action.getFullName()).add(action);
|
||||
}
|
||||
|
||||
return deduper;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -720,7 +727,7 @@ public class KeyBindingUtils {
|
|||
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().isShared();
|
||||
return !action.getKeyBindingType().isManaged();
|
||||
}
|
||||
|
||||
private static KeyStroke getKeyStroke(KeyBindingData data) {
|
||||
|
|
|
@ -33,13 +33,14 @@ 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 ActionToGuiHelper actionGuiHelper;
|
||||
|
||||
|
@ -62,25 +63,38 @@ public class ToolActions implements PropertyChangeListener {
|
|||
* 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.actionGuiHelper = new ActionToGuiHelper(windowManager);
|
||||
this.keyBindingsManager = new KeyBindingsManager(windowManager);
|
||||
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);
|
||||
|
||||
actionGuiHelper.setKeyBindingsManager(keyBindingsManager);
|
||||
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) {
|
||||
|
@ -95,12 +109,14 @@ 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);
|
||||
keyBindingsManager.addAction(action, provider);
|
||||
actionGuiHelper.addLocalAction(provider, action);
|
||||
}
|
||||
|
||||
|
@ -108,10 +124,14 @@ public class ToolActions implements PropertyChangeListener {
|
|||
* 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);
|
||||
keyBindingsManager.addAction(action, null);
|
||||
actionGuiHelper.addToolAction(action);
|
||||
}
|
||||
|
||||
|
@ -158,17 +178,15 @@ public class ToolActions implements PropertyChangeListener {
|
|||
* 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);
|
||||
|
@ -180,15 +198,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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +218,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<>();
|
||||
|
@ -215,8 +236,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<>();
|
||||
|
@ -278,23 +301,24 @@ public class ToolActions implements PropertyChangeListener {
|
|||
* @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) {
|
||||
|
@ -346,12 +370,12 @@ public class ToolActions implements PropertyChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
DockingActionIf getSharedStubKeyBindingAction(String name) {
|
||||
return sharedActionMap.get(name);
|
||||
public Action getAction(KeyStroke ks) {
|
||||
return keyBindingsManager.getDockingKeyAction(ks);
|
||||
}
|
||||
|
||||
Action getAction(KeyStroke ks) {
|
||||
return keyBindingsManager.getDockingKeyAction(ks);
|
||||
DockingActionIf getSharedStubKeyBindingAction(String name) {
|
||||
return sharedActionMap.get(name);
|
||||
}
|
||||
|
||||
// triggered by a user-initiated action
|
||||
|
|
|
@ -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,6 +360,36 @@ 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
|
||||
//==================================================================================================
|
||||
|
@ -361,7 +400,7 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
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);
|
||||
|
@ -436,6 +475,19 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
|
|||
}
|
||||
}
|
||||
|
||||
private class TestNonSharedAction extends DockingAction {
|
||||
|
||||
public TestNonSharedAction(String owner, KeyStroke ks) {
|
||||
super(NON_SHARED_NAME, owner, KeyBindingType.INDIVIDUAL);
|
||||
setKeyBindingData(new KeyBindingData(ks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
fail("Action performed should not have been called");
|
||||
}
|
||||
}
|
||||
|
||||
private class DummyComponentProvider extends ComponentProvider {
|
||||
public DummyComponentProvider() {
|
||||
super(tool, "Dummy", "Dummy Owner");
|
||||
|
|
|
@ -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*/);
|
||||
toolActions = 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