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:
dragonmacher 2019-06-28 15:20:13 -04:00
parent d684ee3ce6
commit 3946a05ded
27 changed files with 515 additions and 345 deletions

View file

@ -22,7 +22,6 @@ import javax.swing.ToolTipManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import docking.DockingWindowManager;
import docking.framework.SplashScreen;
import ghidra.base.help.GhidraHelpService;
import ghidra.framework.Application;
@ -86,7 +85,6 @@ public class GhidraRun implements GhidraLaunchable {
updateSplashScreenStatusMessage("Populating Ghidra help...");
GhidraHelpService.install();
DockingWindowManager.enableDiagnosticActions(SystemUtilities.isInDevelopmentMode());
ExtensionUtils.cleanupUninstalledExtensions();
// Allows handling of old content which did not have a content type property

View file

@ -32,7 +32,7 @@ public class TestTool extends GhidraTool {
@Override
protected DockingWindowManager createDockingWindowManager(boolean isDockable, boolean hasStatus,
boolean isModal) {
return new DockingWindowManager("EMPTY", null, this, isModal, isDockable, hasStatus, null);
return new DockingWindowManager(this, null, this, isModal, isDockable, hasStatus, null);
}
@Override

View file

@ -27,6 +27,7 @@ import org.junit.*;
import docking.action.DockingActionIf;
import docking.action.KeyBindingData;
import docking.actions.KeyEntryDialog;
import docking.actions.ToolActions;
import docking.tool.util.DockingToolConstants;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
@ -235,15 +236,6 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
// Private Methods
//==================================================================================================
private void assertShowProviderActionIsInToolbar() {
assertNotNull("The 'Show Provider' action is not in the toolbar",
getToolbarShowProviderAction());
}
private void assertShowProviderActionNotInToolbar() {
assertNull("The 'Show Provider' action is in the toolbar", getToolbarShowProviderAction());
}
private void switchToTransientProvider() {
provider.setTransient();
}
@ -390,9 +382,8 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
}
private void performLaunchKeyStrokeDialogAction() {
DockingWindowManager dwm = tool.getWindowManager();
ActionToGuiMapper actionMapper = dwm.getActionToGuiMapper();
Action action = actionMapper.getDockingKeyAction(KeyStroke.getKeyStroke("F4"));
ToolActions toolActions = ((AbstractDockingTool) tool).getToolActions();
Action action = toolActions.getAction(KeyStroke.getKeyStroke("F4"));
assertNotNull(action);
runSwing(() -> action.actionPerformed(new ActionEvent(this, 0, "")), false);
}

View file

@ -32,12 +32,11 @@ import org.junit.Test;
import docking.test.AbstractDockingTest;
import docking.widgets.label.GDLabel;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.test.DummyTool;
public class DockingWindowManagerTest extends AbstractDockingTest {
public DockingWindowManagerTest() {
super();
}
private DockingTool tool = new DummyTool();
@Test
public void testDefaultGroupWindowPosition() {
@ -46,7 +45,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// default window position.
//
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA = addProvider(dwm, "A", "a", RIGHT);
ComponentProvider providerB = addProvider(dwm, "B", "b", BOTTOM);
@ -66,7 +65,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// intragroup window position. Note: 'Stacked' is the default.
//
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA1 = addProvider(dwm, "A1", "a", RIGHT, STACK);
ComponentProvider providerA2 = addProvider(dwm, "A2", "a", BOTTOM, STACK);
@ -84,7 +83,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// intragroup window position.
//
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA1 = addProvider(dwm, "A1", "a", RIGHT, STACK);
ComponentProvider providerA2 = addProvider(dwm, "A2", "a", LEFT, BOTTOM);
@ -101,7 +100,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// intragroup window position.
//
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA1 = addProvider(dwm, "A1", "a", RIGHT, WINDOW);
ComponentProvider providerA2 = addProvider(dwm, "A2", "a", LEFT, WINDOW);
@ -122,7 +121,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
//
// note: the positions specified here are for default positions, not intragroup positions
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA = addProvider(dwm, "A", "a", RIGHT, STACK);
ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", BOTTOM, STACK);
@ -143,7 +142,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
//
// note: the positions specified here are for default positions, not intragroup positions
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA = addProvider(dwm, "A", "a", RIGHT, BOTTOM);
ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", BOTTOM, TOP);
@ -181,8 +180,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// Test that, for unrelated groups, the layout info stored in XML is re-used when providers
// are shown after that XML is restored--even if the window positioning changes
//
final DockingWindowManager dwm1 =
new DockingWindowManager("test", (List<Image>) null, null);
final DockingWindowManager dwm1 = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA = addProvider(dwm1, "A", "a", RIGHT);
ComponentProvider providerB = addProvider(dwm1, "B", "b", BOTTOM);
@ -217,7 +215,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// Test that, for related groups, the layout info stored in XML is re-used when providers
// are shown after that XML is restored--even if the window positioning changes
//
DockingWindowManager dwm1 = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm1 = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA = addProvider(dwm1, "A", "a", RIGHT);
ComponentProvider providerAB = addProvider(dwm1, "AB", "a.b", BOTTOM);
@ -253,7 +251,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// and that a subgroup 'a.b' will open relative to the parent. **Make sure that this
// works when the parent is the first provider open.
//
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerA = addProvider(dwm, "A", "a", TOP, RIGHT);
ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", RIGHT, BOTTOM);
@ -270,7 +268,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// and that a subgroup 'a.b' will open relative to the parent. **Make sure that this
// works when the subgroup is the first provider open.
//
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", RIGHT, BOTTOM);
ComponentProvider providerA = addProvider(dwm, "A", "a", TOP, RIGHT);
@ -288,7 +286,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
// Test that two providers that don't share an owner (the plugin) can share a group and
// open relative to each other.
//
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider p1 = addProvider(dwm, "Owner_1", "Name_1", "group", TOP, TOP);
ComponentProvider p2 = addProvider(dwm, "Owner_2", "Name_2", "group", BOTTOM, RIGHT);
@ -337,7 +335,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
*/
DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
DockingWindowManager dwm = new DockingWindowManager(tool, (List<Image>) null, null);
ComponentProvider pA = addProvider(dwm, "Owner_1", "A", "a", LEFT, LEFT);
ComponentProvider pB = addProvider(dwm, "Owner_2", "B", "b", RIGHT, RIGHT);
@ -410,7 +408,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest {
private DockingWindowManager createNewDockingWindowManagerFromXML(final Element element) {
final DockingWindowManager dwm2 =
new DockingWindowManager("text2", (List<Image>) null, null);
new DockingWindowManager(new DummyTool("Tool2"), (List<Image>) null, null);
runSwing(() -> {
dwm2.setVisible(true);

View file

@ -30,10 +30,12 @@ import org.junit.Test;
import docking.DockingWindowManager;
import docking.test.AbstractDockingTest;
import docking.widgets.textfield.IntegerTextField;
import ghidra.test.DummyTool;
public class NumberInputDialogTest extends AbstractDockingTest {
private DockingWindowManager dwm = new DockingWindowManager("test", (List<Image>) null, null);
private DockingWindowManager dwm =
new DockingWindowManager(new DummyTool(), (List<Image>) null, null);
private NumberInputDialog dialog;
private JButton okButton;
private JTextField textField;

View file

@ -28,6 +28,7 @@ import docking.widgets.filter.*;
import docking.widgets.table.model.DirData;
import docking.widgets.table.model.TestDataModel;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.DummyTool;
import ghidra.util.table.GhidraTable;
public class GhidraTableFilterTest extends AbstractGhidraHeadedIntegrationTest {
@ -50,7 +51,7 @@ public class GhidraTableFilterTest extends AbstractGhidraHeadedIntegrationTest {
filteredModel = filterPanel.getTableFilterModel();
table.setAutoLookupColumn(4);
winMgr = new DockingWindowManager("Tests", null, null);
winMgr = new DockingWindowManager(new DummyTool(), null, null);
winMgr.addComponent(new TestTableComponentProvider());
winMgr.setVisible(true);
}

View file

@ -28,6 +28,7 @@ import org.jdom.Element;
import docking.*;
import docking.action.DockingActionIf;
import docking.actions.DockingToolActions;
import ghidra.framework.model.*;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginEvent;
@ -42,6 +43,8 @@ public class DummyTool implements Tool {
private ToolIconURL iconURL;
private Project project;
private DockingToolActions toolActions = new DummyToolActions();
public DummyTool() {
this(DEFAULT_NAME);
}
@ -305,6 +308,11 @@ public class DummyTool implements Tool {
return Collections.emptySet();
}
@Override
public ComponentProvider getActiveComponentProvider() {
return null;
}
@Override
public void showComponentProvider(ComponentProvider componentProvider, boolean visible) {
//do nothing
@ -345,6 +353,11 @@ public class DummyTool implements Tool {
//do nothing
}
@Override
public ActionContext getGlobalContext() {
return null;
}
@Override
public void setStatusInfo(String text) {
//do nothing
@ -389,4 +402,9 @@ public class DummyTool implements Tool {
public void removeContextListener(DockingContextListener listener) {
//do nothing
}
@Override
public DockingToolActions getToolActions() {
return toolActions;
}
}

View file

@ -0,0 +1,66 @@
/* ###
* 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 ghidra.test;
import java.util.Set;
import docking.ComponentProvider;
import docking.action.DockingActionIf;
import docking.actions.DockingToolActions;
public class DummyToolActions implements DockingToolActions {
@Override
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
// stub
}
@Override
public void addGlobalAction(DockingActionIf action) {
// stub
}
@Override
public void removeGlobalAction(DockingActionIf action) {
// stub
}
@Override
public void removeActions(String owner) {
// stub
}
@Override
public Set<DockingActionIf> getActions(String owner) {
return null;
}
@Override
public Set<DockingActionIf> getAllActions() {
return null;
}
@Override
public void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
// stub
}
@Override
public void removeActions(ComponentProvider provider) {
// stub
}
}

View file

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

View file

@ -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
*/

View file

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

View file

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

View file

@ -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);

View file

@ -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();
}

View file

@ -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());
}

View file

@ -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();

View file

@ -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();
}
}

View file

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

View file

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

View file

@ -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();
}

View file

@ -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) {

View file

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

View file

@ -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");

View file

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

View file

@ -150,16 +150,16 @@ public abstract class PluginTool extends AbstractDockingTool
this.projectManager = projectManager;
this.toolServices = toolServices;
propertyChangeMgr = new PropertyChangeSupport(this);
winMgr = createDockingWindowManager(isDockable, hasStatus, isModal);
taskMgr = new ToolTaskManager(this);
optionsMgr = new OptionsManager(this);
winMgr = createDockingWindowManager(isDockable, hasStatus, isModal);
toolActions = new ToolActions(this, new ActionToGuiHelper(winMgr));
taskMgr = new ToolTaskManager(this);
setToolOptionsHelpLocation();
winMgr.addStatusItem(taskMgr.getMonitorComponent(), false, true);
winMgr.removeStatusItem(taskMgr.getMonitorComponent());
eventMgr = new EventManager(this);
serviceMgr = new ServiceManager();
installServices();
toolActions = new ToolActions(this, winMgr);
pluginMgr = new PluginManager(this, serviceMgr);
dialogMgr = new DialogManager(this);
initActions();
@ -190,8 +190,8 @@ public abstract class PluginTool extends AbstractDockingTool
boolean isModal) {
List<Image> windowIcons = ApplicationInformationDisplayFactory.getWindowIcons();
DockingWindowManager newManager = new DockingWindowManager("EMPTY", windowIcons, this,
isModal, isDockable, hasStatus, null);
DockingWindowManager newManager =
new DockingWindowManager(this, windowIcons, this, isModal, isDockable, hasStatus, null);
return newManager;
}
@ -1322,8 +1322,8 @@ public abstract class PluginTool extends AbstractDockingTool
}
void removeAll(String owner) {
toolActions.removeToolActions(owner);
winMgr.removeAll(owner);
toolActions.removeActions(owner);
winMgr.ownerRemoved(owner);
}
void registerEventProduced(Class<? extends PluginEvent> eventClass) {
@ -1468,6 +1468,7 @@ public abstract class PluginTool extends AbstractDockingTool
return winMgr.getActiveWindow();
}
@Override
public ComponentProvider getActiveComponentProvider() {
return winMgr.getActiveComponentProvider();
}

View file

@ -67,7 +67,7 @@ public class KeyBindingsPanel extends JPanel {
private ListSelectionModel selectionModel;
private Options options;
private Map<String, DockingActionIf> actionsByFullName;
private Map<String, List<DockingActionIf>> actionsByFullName;
private Map<String, List<String>> actionNamesByKeyStroke;
private Map<String, KeyStroke> keyStrokesByFullName;
private Map<String, KeyStroke> originalValues; // to know what has been changed
@ -114,11 +114,11 @@ public class KeyBindingsPanel extends JPanel {
changesMade(false);
}
private boolean updateOptions(String fullActionName, KeyStroke currentKeyStroke,
private void updateOptions(String fullActionName, KeyStroke currentKeyStroke,
KeyStroke newKeyStroke) {
if ((currentKeyStroke != null && currentKeyStroke.equals(newKeyStroke)) ||
(currentKeyStroke == null && newKeyStroke == null)) {
return false;
if (Objects.equals(currentKeyStroke, newKeyStroke)) {
return;
}
if (newKeyStroke != null) {
@ -130,19 +130,12 @@ public class KeyBindingsPanel extends JPanel {
originalValues.put(fullActionName, newKeyStroke);
keyStrokesByFullName.put(fullActionName, newKeyStroke);
Set<DockingActionIf> actions = tool.getAllActions();
List<DockingActionIf> actions = actionsByFullName.get(fullActionName);
for (DockingActionIf action : actions) {
if (action.getFullName().equals(fullActionName)) {
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKeyStroke));
}
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKeyStroke));
}
return true;
}
/**
* Cancel the changes to the actions.
*/
public void cancel() {
Iterator<String> iter = originalValues.keySet().iterator();
while (iter.hasNext()) {
@ -173,10 +166,12 @@ public class KeyBindingsPanel extends JPanel {
String longestName = "";
actionsByFullName = KeyBindingUtils.getAllActionsByFullName(tool);
Set<Entry<String, DockingActionIf>> entries = actionsByFullName.entrySet();
for (Entry<String, DockingActionIf> entry : entries) {
Set<Entry<String, List<DockingActionIf>>> entries = actionsByFullName.entrySet();
for (Entry<String, List<DockingActionIf>> entry : entries) {
DockingActionIf action = entry.getValue();
// pick one action, they are all conceptually the same
List<DockingActionIf> actions = entry.getValue();
DockingActionIf action = actions.get(0);
tableActions.add(action);
String actionName = entry.getKey();
@ -413,11 +408,13 @@ public class KeyBindingsPanel extends JPanel {
Iterator<String> iter = keyStrokesByFullName.keySet().iterator();
while (iter.hasNext()) {
String actionName = iter.next();
DockingActionIf action = actionsByFullName.get(actionName);
if (action == null) {
List<DockingActionIf> actions = actionsByFullName.get(actionName);
if (actions.isEmpty()) {
throw new AssertException("No actions defined for " + actionName);
}
// pick one action, they are all conceptually the same
DockingActionIf action = actions.get(0);
KeyStroke currentKeyStroke = keyStrokesByFullName.get(actionName);
KeyBindingData defaultBinding = action.getDefaultKeyBindingData();
KeyStroke newKeyStroke =
@ -669,11 +666,14 @@ public class KeyBindingsPanel extends JPanel {
}
ksField.setText(ksName);
// make sure the label gets enough space
statusLabel.setPreferredSize(
new Dimension(statusLabel.getPreferredSize().width, STATUS_LABEL_HEIGHT));
DockingActionIf action = actionsByFullName.get(fullActionName);
// pick one action, they are all conceptually the same
List<DockingActionIf> actions = actionsByFullName.get(fullActionName);
DockingActionIf action = actions.get(0);
String description = action.getDescription();
if (description == null || description.trim().isEmpty()) {
description = action.getName();

View file

@ -83,7 +83,7 @@ public class GhidraTool extends PluginTool {
@Override
protected DockingWindowManager createDockingWindowManager(boolean isDockable, boolean hasStatus,
boolean isModal) {
return new DockingWindowManager("EMPTY", null, this, isModal, isDockable, hasStatus,
return new DockingWindowManager(this, null, this, isModal, isDockable, hasStatus,
new OpenFileDropHandlerFactory(this));
}