diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java index 906ceeee28..214066b9a9 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateHelpTemplateScript.java @@ -102,7 +102,7 @@ public class CreateHelpTemplateScript extends GhidraScript { } private List getActions(PluginTool tool, Plugin plugin) { - Set actions = KeyBindingUtils.getKeyBindingActions(tool, plugin.getName()); + Set actions = KeyBindingUtils.getKeyBindingActionsForOwner(tool, plugin.getName()); List list = new ArrayList<>(actions); Comparator comparator = (action1, action2) -> { try { diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java index ca7346a2a4..61038d2b83 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/action/KeyEntryDialogTest.java @@ -186,8 +186,8 @@ public class KeyEntryDialogTest extends AbstractGhidraHeadedIntegrationTest { public DockingAction getKeyBindingAction() { DockingWindowManager dwm = DockingWindowManager.getInstance(tool.getToolFrame()); - DockingActionManager dockingActionManager = - (DockingActionManager) getInstanceField("actionManager", dwm); + ActionToGuiMapper dockingActionManager = + (ActionToGuiMapper) getInstanceField("actionManager", dwm); return (DockingAction) getInstanceField("keyBindingsAction", dockingActionManager); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java index 9bef07cdae..5902ab81b9 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java @@ -21,7 +21,7 @@ import java.util.*; import javax.swing.JFrame; import docking.action.DockingActionIf; -import docking.actions.DockingToolActionManager; +import docking.actions.ToolActions; import ghidra.framework.options.ToolOptions; import ghidra.util.SystemUtilities; @@ -32,7 +32,7 @@ import ghidra.util.SystemUtilities; public abstract class AbstractDockingTool implements DockingTool { protected DockingWindowManager winMgr; - protected DockingToolActionManager actionMgr; + protected ToolActions actionMgr; protected Map optionsMap = new HashMap<>(); protected boolean configChangedFlag; @@ -117,7 +117,7 @@ public abstract class AbstractDockingTool implements DockingTool { @Override public Set getAllActions() { Set actions = actionMgr.getAllActions(); - DockingActionManager am = winMgr.getActionManager(); + ActionToGuiMapper am = winMgr.getActionManager(); actions.addAll(am.getAllActions()); return actions; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java similarity index 98% rename from Ghidra/Framework/Docking/src/main/java/docking/DockingActionManager.java rename to Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java index 3450320cb2..d70ac94189 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java @@ -28,7 +28,7 @@ import ghidra.util.*; /** * Manages the global actions for the menu and toolbar. */ -public class DockingActionManager { +public class ActionToGuiMapper { private HashSet globalActions = new LinkedHashSet<>(); @@ -44,7 +44,7 @@ public class DockingActionManager { private PopupActionManager popupActionManager; private DockingAction keyBindingsAction; - DockingActionManager(DockingWindowManager winMgr) { + ActionToGuiMapper(DockingWindowManager winMgr) { menuGroupMap = new MenuGroupMap(); menuBarMenuHandler = new MenuBarMenuHandler(winMgr); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java index 2e26661030..cc7270ca87 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java @@ -69,7 +69,7 @@ public class DialogComponentProviderPopupActionManager { return; } - DockingActionManager actionManager = dwm.getActionManager(); + ActionToGuiMapper actionManager = dwm.getActionManager(); MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap(); MenuManager menuMgr = new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java index 758d175f9d..ad7a2b5feb 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java @@ -52,7 +52,7 @@ public class DockableComponent extends JPanel implements ContainerListener { private JComponent providerComp; private Component focusedComponent; private DockingWindowManager winMgr; - private DockingActionManager actionMgr; + private ActionToGuiMapper actionMgr; private DropTarget dockableDropTarget; /** diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java index b37a79d643..b74d9deff5 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableToolBarManager.java @@ -60,7 +60,7 @@ class DockableToolBarManager { ComponentPlaceholder placeholder = dockableComp.getComponentWindowingPlaceholder(); DockingWindowManager winMgr = dockableComp.getComponentWindowingPlaceholder().getNode().winMgr; - DockingActionManager actionManager = winMgr.getActionManager(); + ActionToGuiMapper actionManager = winMgr.getActionManager(); menuGroupMap = actionManager.getMenuGroupMap(); MenuHandler menuHandler = actionManager.getMenuHandler(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManagerActionUpdater.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingActionPackageHelper.java similarity index 94% rename from Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManagerActionUpdater.java rename to Ghidra/Framework/Docking/src/main/java/docking/DockingActionPackageHelper.java index 917d4469b6..acf18d07b8 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManagerActionUpdater.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingActionPackageHelper.java @@ -24,11 +24,11 @@ import docking.action.DockingActionIf; * {@link DockingWindowManager}. This allows the manager's interface to hide methods that * don't make sense for public consumption. */ -public class DockingWindowManagerActionUpdater { +public class DockingActionPackageHelper { private DockingWindowManager windowManager; - public DockingWindowManagerActionUpdater(DockingWindowManager windowManager) { + public DockingActionPackageHelper(DockingWindowManager windowManager) { this.windowManager = windowManager; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 9db5066bfa..2ec9097e08 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -89,7 +89,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder private Map providerNameCache = new HashMap<>(); private Map preferenceStateMap = new HashMap<>(); private DockWinListener docListener; - private DockingActionManager actionManager; + private ActionToGuiMapper actionManager; private WeakSet contextListeners = WeakDataStructureFactory.createSingleThreadAccessWeakSet(); @@ -140,7 +140,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder } root = new RootNode(this, toolName, images, modal, factory); - actionManager = new DockingActionManager(this); + actionManager = new ActionToGuiMapper(this); KeyboardFocusManager km = KeyboardFocusManager.getCurrentKeyboardFocusManager(); km.addPropertyChangeListener("permanentFocusOwner", this); @@ -161,7 +161,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * @param enable */ public static void enableDiagnosticActions(boolean enable) { - DockingActionManager.enableDiagnosticActions(enable); + ActionToGuiMapper.enableDiagnosticActions(enable); } /** @@ -293,7 +293,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * @param helpLocation help content location */ public static void setHelpLocation(JComponent c, HelpLocation helpLocation) { - DockingActionManager.setHelpLocation(c, helpLocation); + ActionToGuiMapper.setHelpLocation(c, helpLocation); } /** @@ -338,7 +338,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return placeholderManager; } - DockingActionManager getActionManager() { + ActionToGuiMapper getActionManager() { return actionManager; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java index 823dec5538..9be430b4dc 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java @@ -130,7 +130,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener { } private List getActionsForWindow(WindowNode windowNode) { - DockingActionManager actionManager = windowManager.getActionManager(); + ActionToGuiMapper actionManager = windowManager.getActionManager(); Collection globalActions = actionManager.getGlobalActions(); List actionsForWindow = new ArrayList<>(globalActions.size()); Set> contextTypes = windowNode.getContextTypes(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java index 66a1518c79..fb8c979f7b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyBindingAction.java @@ -24,9 +24,9 @@ import ghidra.util.Msg; import ghidra.util.ReservedKeyBindings; public class KeyBindingAction extends DockingAction { - private final DockingActionManager dockingActionManager; + private final ActionToGuiMapper dockingActionManager; - public KeyBindingAction(DockingActionManager dockingActionManager) { + public KeyBindingAction(ActionToGuiMapper dockingActionManager) { super("Set KeyBinding", DockingWindowManager.DOCKING_WINDOWS_OWNER); this.dockingActionManager = dockingActionManager; createReservedKeyBinding(ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java index 535f6a99f2..f415c740f9 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/KeyEntryDialog.java @@ -35,7 +35,7 @@ import resources.ResourceManager; */ public class KeyEntryDialog extends DialogComponentProvider { - private DockingActionManager actionManager; + private ActionToGuiMapper actionManager; private DockingActionIf action; private JPanel defaultPanel; private KeyEntryTextField keyEntryField; @@ -45,7 +45,7 @@ public class KeyEntryDialog extends DialogComponentProvider { private SimpleAttributeSet textAttrSet; private Color bgColor; - public KeyEntryDialog(DockingActionIf action, DockingActionManager actionManager) { + public KeyEntryDialog(DockingActionIf action, ActionToGuiMapper actionManager) { super("Set Key Binding for " + action.getName(), true); this.actionManager = actionManager; this.action = action; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingUtils.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingUtils.java index 6939d2375a..a809f889ae 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingUtils.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/KeyBindingUtils.java @@ -336,7 +336,7 @@ public class KeyBindingUtils { * @param tool the tool containing the actions * @return the actions mapped by their full name (e.g., 'Name (OwnerName)') */ - public static Map getAllKeyBindingActions(DockingTool tool) { + public static Map getAllKeyBindingActionsByFullName(DockingTool tool) { Map deduper = new HashMap<>(); Set actions = tool.getAllActions(); @@ -363,7 +363,8 @@ public class KeyBindingUtils { * @param owner the action owner name * @return the actions */ - public static Set getKeyBindingActions(DockingTool tool, String owner) { + public static Set getKeyBindingActionsForOwner(DockingTool tool, + String owner) { Map deduper = new HashMap<>(); Set actions = tool.getDockingActionsByOwnerName(owner); @@ -381,19 +382,6 @@ public class KeyBindingUtils { return CollectionUtils.asSet(deduper.values()); } - /** - * Returns all actions that match the given owner and name - * - * @param tool the tool containing the actions - * @param owner the owner - * @param name the name - * @return the actions - */ - public static Set getActions(DockingTool tool, String owner, String name) { - Set actions = tool.getDockingActionsByOwnerName(owner); - return getActions(actions, owner, name); - } - /** * Returns all actions that match the given owner and name * @@ -421,16 +409,17 @@ public class KeyBindingUtils { public static DockingActionIf getSharedKeyBindingAction(Set allActions, String sharedName) { - Set toolActions = getActions(allActions, "Tool", sharedName); + String owner = "Tool"; + for (DockingActionIf action : allActions) { + if (!(action instanceof SharedStubKeyBindingAction)) { + continue; + } - //@formatter:off - return toolActions - .stream() - .filter(action -> action instanceof SharedStubKeyBindingAction) - .findAny() - .orElse(null) - ; - //@formatter:on + if (action.getOwner().equals(owner) && action.getName().equals(sharedName)) { + return action; + } + } + return null; } private static boolean isIgnored(DockingActionIf action) { @@ -456,8 +445,15 @@ public class KeyBindingUtils { return new ActionAdapter(action); } + /** + * Checks each action in the given collection against the given new action to make sure that + * they share the same default key binding. + * + * @param newAction the action to check + * @param existingActions the actions that have already been checked + */ public static void assertSameDefaultKeyBindings(DockingActionIf newAction, - List existingActions) { + Collection existingActions) { KeyBindingData newDefaultBinding = newAction.getDefaultKeyBindingData(); KeyStroke defaultKs = getKeyStroke(newDefaultBinding); @@ -471,6 +467,14 @@ public class KeyBindingUtils { } } + /** + * Logs a warning message for the two given actions to signal that they do not share the + * same default key binding + * + * @param newAction the new action + * @param existingAction the action that has already been validated + * @param existingDefaultKs the current validated key stroke + */ public static void logDifferentKeyBindingsWarnigMessage(DockingActionIf newAction, DockingActionIf existingAction, KeyStroke existingDefaultKs) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/DockingToolActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java similarity index 69% rename from Ghidra/Framework/Docking/src/main/java/docking/actions/DockingToolActionManager.java rename to Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java index e2093027c7..da354228ee 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/DockingToolActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/ToolActions.java @@ -18,7 +18,7 @@ package docking.actions; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; -import java.util.function.Predicate; +import java.util.stream.Collectors; import javax.swing.KeyStroke; @@ -33,14 +33,22 @@ import ghidra.util.exception.AssertException; /** * An class to manage actions registered with the tool */ -public class DockingToolActionManager implements PropertyChangeListener { +public class ToolActions implements PropertyChangeListener { private DockingWindowManager winMgr; - private DockingWindowManagerActionUpdater winMgrActionUpdater; + private DockingActionPackageHelper actionGuiHelper; + + /* + Map of Maps of Sets + + Owner Name -> + Action Name -> Set of Actions + */ + private Map>> actionsByNameByOwner = LazyMap.lazyMap( + new HashMap<>(), () -> LazyMap.lazyMap(new HashMap<>(), () -> new HashSet<>())); - private Map> actionMap = - LazyMap.lazyMap(new HashMap<>(), () -> new ArrayList<>()); private Map sharedActionMap = new HashMap<>(); + private ToolOptions keyBindingOptions; private DockingTool dockingTool; @@ -51,32 +59,27 @@ public class DockingToolActionManager implements PropertyChangeListener { * @param windowManager manager of the "Docking" arrangement of a set of components * and actions in the tool */ - public DockingToolActionManager(DockingTool tool, DockingWindowManager windowManager) { + public ToolActions(DockingTool tool, DockingWindowManager windowManager) { this.dockingTool = tool; this.winMgr = windowManager; - this.winMgrActionUpdater = new DockingWindowManagerActionUpdater(winMgr); + this.actionGuiHelper = new DockingActionPackageHelper(winMgr); keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS); } public void dispose() { - actionMap.clear(); + actionsByNameByOwner.clear(); + sharedActionMap.clear(); } private void addActionToMap(DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - List list = actionMap.get(name); - if (!list.isEmpty()) { - KeyBindingUtils.assertSameDefaultKeyBindings(action, actionList); - } - - actionList.add(action); + Set actions = getActionStorage(action); + KeyBindingUtils.assertSameDefaultKeyBindings(action, actions); + actions.add(action); } private void removeActionFromMap(DockingActionIf action) { - String name = action.getFullName(); - actionMap.get(name).remove(action); + getActionStorage(action).remove(action); } /** @@ -90,7 +93,7 @@ public class DockingToolActionManager implements PropertyChangeListener { action.addPropertyChangeListener(this); addActionToMap(action); setKeyBindingOption(action); - winMgrActionUpdater.addLocalAction(provider, action); + actionGuiHelper.addLocalAction(provider, action); } /** @@ -101,7 +104,7 @@ public class DockingToolActionManager implements PropertyChangeListener { action.addPropertyChangeListener(this); addActionToMap(action); setKeyBindingOption(action); - winMgrActionUpdater.addToolAction(action); + actionGuiHelper.addToolAction(action); } private void setKeyBindingOption(DockingActionIf action) { @@ -148,7 +151,7 @@ public class DockingToolActionManager implements PropertyChangeListener { public synchronized void removeToolAction(DockingActionIf action) { action.removePropertyChangeListener(this); removeActionFromMap(action); - winMgrActionUpdater.removeToolAction(action); + actionGuiHelper.removeToolAction(action); } /** @@ -156,19 +159,23 @@ public class DockingToolActionManager implements PropertyChangeListener { * @param owner owner of the actions to remove */ public synchronized void removeToolActions(String owner) { - Predicate ownerMatches = actionOwner -> actionOwner.equals(owner); - Set actions = getActions(ownerMatches); - for (DockingActionIf action : actions) { - removeToolAction(action); - } + + //@formatter:off + Set toRemove = actionsByNameByOwner.get(owner).values() + .stream() + .flatMap(set -> set.stream()) + .collect(Collectors.toSet()) + ; + //@formatter:on + + // must do this later to avoid concurrent modification exceptions + toRemove.forEach(action -> removeToolAction(action)); } private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList.contains(action)) { + if (getActionStorage(action).contains(action)) { throw new AssertException("Cannot add the same action more than once. Provider " + - provider.getName() + " - action: " + name); + provider.getName() + " - action: " + action.getFullName()); } } @@ -184,46 +191,6 @@ public class DockingToolActionManager implements PropertyChangeListener { winMgr.removeProviderAction(provider, action); } - /** - * Get all actions that have the action name which includes the action owner's name. - * - * @param fullName full name for the action, e.g., "My Action (My Plugin)" - * @return list of actions; empty if no action exists with the given name - */ - public Set getDockingActionsByFullActionName(String fullName) { - List list = actionMap.get(fullName); - return new HashSet<>(list); - } - - /** - * Returns a list of actions whose owner matches the given predicate. - * - * Note: Actions with the same name are assumed to be different instances of the same action. - * - * @param ownerFilter the predicate that is used to test if the owners are the same; to get - * all actions, return an 'always true' predicate - * @return a list of deduped actions. - */ - private Set getActions(Predicate ownerFilter) { - - Set result = new HashSet<>(); - for (List list : actionMap.values()) { - for (DockingActionIf action : list) { - if (ownerFilter.test(action.getOwner())) { - result.addAll(list); - } - } - } - - for (DockingActionIf action : sharedActionMap.values()) { - if (ownerFilter.test(action.getOwner())) { - result.add(action); - } - } - - return result; - } - /** * Get all actions for the given owner. * @param owner owner of the actions @@ -231,24 +198,42 @@ public class DockingToolActionManager implements PropertyChangeListener { * action exists with the given name */ public synchronized Set getActions(String owner) { - Predicate ownerMatches = actionOwner -> actionOwner.equals(owner); - return getActions(ownerMatches); + + Set result = new HashSet<>(); + Map> actionsByName = actionsByNameByOwner.get(owner); + for (Set actions : actionsByName.values()) { + result.addAll(actions); + } + + if (SharedStubKeyBindingAction.SHARED_OWNER.equals(owner)) { + result.addAll(sharedActionMap.values()); + } + + return result; } /** - * Get a list of all actions in the tool - * @return list of PluginAction objects + * Get a set of all actions in the tool + * @return the actions */ public synchronized Set getAllActions() { - Predicate allOwnersMatch = name -> true; - return getActions(allOwnersMatch); + + Set result = new HashSet<>(); + Collection>> maps = actionsByNameByOwner.values(); + for (Map> actionsByName : maps) { + for (Set actions : actionsByName.values()) { + result.addAll(actions); + } + } + + result.addAll(sharedActionMap.values()); + + return result; } /** - * Get the keybindings for each action so that they are still registered - * as being used; otherwise the options will be removed because they - * are noted as not being used. - * + * Get the keybindings for each action so that they are still registered as being used; + * otherwise the options will be removed because they are noted as not being used. */ public synchronized void restoreKeyBindings() { keyBindingOptions = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS); @@ -272,12 +257,16 @@ public class DockingToolActionManager implements PropertyChangeListener { public void removeComponentActions(ComponentProvider provider) { Iterator iterator = winMgr.getComponentActions(provider); while (iterator.hasNext()) { - DockingActionIf action = iterator.next(); - String name = action.getFullName(); - actionMap.get(name).remove(action); + removeActionFromMap(iterator.next()); } } + private Set getActionStorage(DockingActionIf action) { + String owner = action.getOwner(); + String name = action.getName(); + return actionsByNameByOwner.get(owner).get(name); + } + @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java index 2421f5d1d8..13f0a82ad7 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/AbstractSortedTableModel.java @@ -442,9 +442,10 @@ public abstract class AbstractSortedTableModel extends AbstractGTableModel public int compare(T t1, T t2) { // at this point we compare the rows, since all of the sorting column values are equal - // (Warning: there is a chance that the two objects are comparable, but not on each - // other. In this case, a ClassCastException will be thrown) - if (t1 instanceof Comparable && t2 instanceof Comparable) { + // (Warning: due to comparable being specific to the class upon which it is defined, + // we have to make sure the class is the same to prevent class cast + // exceptions when the table has mixed implementations of 'T') + if (t1 instanceof Comparable && t1.getClass().equals(t2.getClass())) { return ((Comparable) t1).compareTo(t2); } diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/actions/SharedKeyBindingDockingActionTest.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/actions/SharedKeyBindingDockingActionTest.java index 7e7385c514..2792ba6d33 100644 --- a/Ghidra/Framework/Docking/src/test.slow/java/docking/actions/SharedKeyBindingDockingActionTest.java +++ b/Ghidra/Framework/Docking/src/test.slow/java/docking/actions/SharedKeyBindingDockingActionTest.java @@ -357,8 +357,8 @@ public class SharedKeyBindingDockingActionTest extends AbstractDockingTest { //================================================================================================== private void assertSharedStubInTool() { - DockingToolActionManager actionManager = - (DockingToolActionManager) getInstanceField("actionMgr", tool); + ToolActions actionManager = + (ToolActions) getInstanceField("actionMgr", tool); DockingActionIf action = actionManager.getSharedStubKeyBindingAction(SHARED_NAME); assertNotNull("Shared action stub is not in the tool", action); } diff --git a/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java b/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java index bcbb45e91b..0398267581 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java @@ -21,7 +21,7 @@ import java.util.List; import javax.swing.ImageIcon; import docking.action.DockingActionIf; -import docking.actions.DockingToolActionManager; +import docking.actions.ToolActions; import docking.framework.ApplicationInformationDisplayFactory; import ghidra.framework.options.ToolOptions; @@ -37,7 +37,7 @@ public class FakeDockingTool extends AbstractDockingTool { List windowIcons = ApplicationInformationDisplayFactory.getWindowIcons(); winMgr = new DockingWindowManager("EMPTY", windowIcons, listener, false /*isModal*/, true /*isDockable*/, true /*hasStatus*/, null /*DropTargetFactory*/); - actionMgr = new DockingToolActionManager(this, winMgr); + actionMgr = new ToolActions(this, winMgr); } @Override diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java index 84236b795a..0d98af4381 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/StringUtilities.java @@ -18,6 +18,7 @@ package ghidra.util; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -857,18 +858,9 @@ public class StringUtilities { return null; } - int i = 0; - StringBuffer buffer = new StringBuffer("[ "); - for (Object o : collection) { - buffer.append(o.toString()); - if (i + 1 < collection.size()) { - buffer.append(separator); - } - i++; - } - - buffer.append(" ]"); - return buffer.toString(); + String asString = + collection.stream().map(o -> o.toString()).collect(Collectors.joining(separator)); + return "[ " + asString + " ]"; } public static String toStringWithIndent(Object o) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java index f0a36ded7f..df39ec4738 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java @@ -218,7 +218,7 @@ public class PluginConfigurationModel { return Collections.emptySet(); } - return KeyBindingUtils.getKeyBindingActions(tool, pluginDescription.getName()); + return KeyBindingUtils.getKeyBindingActionsForOwner(tool, pluginDescription.getName()); } /** diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java index 1e45ab40c7..91152002bd 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java @@ -33,7 +33,7 @@ import org.jdom.Element; import docking.*; import docking.action.*; -import docking.actions.DockingToolActionManager; +import docking.actions.ToolActions; import docking.framework.AboutDialog; import docking.framework.ApplicationInformationDisplayFactory; import docking.framework.SplashScreen; @@ -158,7 +158,7 @@ public abstract class PluginTool extends AbstractDockingTool eventMgr = new EventManager(this); serviceMgr = new ServiceManager(); installServices(); - actionMgr = new DockingToolActionManager(this, winMgr); + actionMgr = new ToolActions(this, winMgr); pluginMgr = new PluginManager(this, serviceMgr); dialogMgr = new DialogManager(this); initActions(); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java index ab0077ae67..4d0eb63bba 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java @@ -171,7 +171,7 @@ public class KeyBindingsPanel extends JPanel { originalValues = new HashMap<>(); String longestName = ""; - actionsByFullName = KeyBindingUtils.getAllKeyBindingActions(tool); + actionsByFullName = KeyBindingUtils.getAllKeyBindingActionsByFullName(tool); Set> entries = actionsByFullName.entrySet(); for (Entry entry : entries) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ProjectActionManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ProjectActionManager.java deleted file mode 100644 index ee11475c4b..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ProjectActionManager.java +++ /dev/null @@ -1,299 +0,0 @@ -/* ### - * 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.framework.plugintool.mgr; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.*; - -import javax.swing.KeyStroke; - -import docking.*; -import docking.action.*; -import docking.tool.util.DockingToolConstants; -import ghidra.framework.options.OptionType; -import ghidra.framework.options.Options; -import ghidra.framework.plugintool.PluginTool; -import ghidra.util.exception.AssertException; - -/** - * Helper class to manage plugin actions for the tool. - */ -public class ProjectActionManager implements PropertyChangeListener { - - private DockingWindowManager winMgr; - private DockingWindowManagerActionUpdater winMgrActionUpdater; - - // TODO - // TODO - // TODO does this class need the shared action concept (probably not) - // TODO - // TODO - private Map> actionMap; - private Options keyBindingOptions; - private PluginTool tool; - - /** - * Construct an ActionManager - * @param tool plugin tool using this ActionManager - * @param winMgr manager of the "Docking" arrangement - * of a set of components and actions in the tool - */ - public ProjectActionManager(PluginTool tool, DockingWindowManager winMgr) { - this.tool = tool; - this.winMgr = winMgr; - this.winMgrActionUpdater = new DockingWindowManagerActionUpdater(winMgr); - actionMap = new HashMap<>(); - keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - } - - public void dispose() { - actionMap.clear(); - } - - private void addActionToMap(DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - List newList = new ArrayList<>(); - newList.add(action); - actionMap.put(name, newList); - } - else { - actionList.add(action); - } - } - - private void removeActionFromMap(DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - return; - } - - if (actionList.remove(action) && actionList.isEmpty()) { - actionMap.remove(name); - } - } - - /** - * Adds the action to the tool. - * @param action the action to be added. - */ - public synchronized void addToolAction(DockingActionIf action) { - action.addPropertyChangeListener(this); - addActionToMap(action); - if (action.isKeyBindingManaged()) { - KeyStroke ks = action.getKeyBinding(); - keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, - null, null); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - - winMgrActionUpdater.addToolAction(action); - } - - /** - * Removes the given action from the tool - * @param action the action to be removed. - */ - public synchronized void removeToolAction(DockingActionIf action) { - action.removePropertyChangeListener(this); - removeActionFromMap(action); - winMgrActionUpdater.removeToolAction(action); - } - - /** - * Remove all actions that have the given owner. - * @param owner owner of the actions to remove - */ - public synchronized void removeToolActions(String owner) { - List actions = getActions(owner); - for (DockingActionIf action : actions) { - removeToolAction(action); - } - } - - /** - * Add an action that works specifically with a component provider. - * @param provider provider associated with the action - * @param action local action to the provider - */ - public synchronized void addLocalAction(ComponentProvider provider, DockingActionIf action) { - checkForAlreadyAddedAction(provider, action); - - action.addPropertyChangeListener(this); - addActionToMap(action); - if (action.isKeyBindingManaged()) { - KeyStroke ks = action.getKeyBinding(); - keyBindingOptions.registerOption(action.getFullName(), OptionType.KEYSTROKE_TYPE, ks, - null, null); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - winMgrActionUpdater.addLocalAction(provider, action); - } - - private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) { - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList == null) { - return; - } - if (actionList.contains(action)) { - throw new AssertException("Cannot add the same action more than once. Provider " + - provider.getName() + " - action: " + name); - } - } - - /** - * Remove an action that works specifically with a component provider. - * @param provider provider associated with the action - * @param action local action to the provider - */ - public synchronized void removeProviderAction(ComponentProvider provider, - DockingActionIf action) { - action.removePropertyChangeListener(this); - removeActionFromMap(action); - winMgr.removeProviderAction(provider, action); - } - - // TODO delete me - /** - * Get all actions that have the action name which includes the action owner's name. - * - * @param fullName full name for the action, e.g., "My Action (My Plugin)" - * @return list of actions; empty if no action exists with the given name - */ - public List getDockingActionsByFullActionName(String fullName) { - List list = actionMap.get(fullName); - if (list == null) { - return new ArrayList<>(); - } - return new ArrayList<>(list); - } - - /** - * Returns a list of actions whose owner matches the given owner or all actions if the given - * owner is null. - *

- * This method will only return a single instance of any named action, even if multiple - * actions have been registered with the same name. - *

- * Note: Actions with the same name are assumed to be different instances of the same action. - * - * @param owner The of the action, or null to get all actions - * @return a list of deduped actions. - */ - private List getUniqueActionList(String owner) { - List matchingActionList = new ArrayList<>(); - - for (List actionList : actionMap.values()) { - // we only want *one* instance of duplicate actions - DockingActionIf action = actionList.get(0); - if (owner == null || action.getOwner().equals(owner)) { - matchingActionList.add(action); - } - } - - return matchingActionList; - } - - /** - * Get all actions for the given owner. - * @param owner owner of the actions - * @return array of actions; zero length array is returned if no - * action exists with the given name - */ - public synchronized List getActions(String owner) { - List list = getUniqueActionList(owner); - return list; - } - - /** - * Get a list of all actions in the tool. - * @return list of PluginAction objects - */ - public List getAllActions() { - return getUniqueActionList(null); - } - - /** - * Get the keybindings for each action so that they are still registered - * as being used; otherwise the options will be removed because they - * are noted as not being used. - * - */ - public synchronized void restoreKeyBindings() { - keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - List actions = getAllActions(); - for (DockingActionIf action : actions) { - if (!action.isKeyBindingManaged()) { - continue; - } - KeyStroke ks = action.getKeyBinding(); - KeyStroke newKs = keyBindingOptions.getKeyStroke(action.getFullName(), ks); - if (ks != newKs) { - action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs)); - } - } - } - - /** - * Get the actions for the given provider and remove them from the - * actionMap; call the window manager to remove the provider. - * @param provider provider to be removed - */ - public void removeComponent(ComponentProvider provider) { - Iterator iterator = winMgr.getComponentActions(provider); - while (iterator.hasNext()) { - DockingActionIf action = iterator.next(); - String name = action.getFullName(); - List actionList = actionMap.get(name); - if (actionList != null && actionList.remove(action) && actionList.isEmpty()) { - actionMap.remove(name); - } - } - winMgr.removeComponent(provider); - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) { - DockingAction action = (DockingAction) evt.getSource(); - if (!action.isKeyBindingManaged()) { - tool.setConfigChanged(true); - return; - } - KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue(); - KeyStroke newKeyStroke = keyBindingData.getKeyBinding(); - Options opt = tool.getOptions(DockingToolConstants.KEY_BINDINGS); - KeyStroke optKeyStroke = opt.getKeyStroke(action.getFullName(), null); - if (newKeyStroke == null) { - opt.removeOption(action.getFullName()); - } - else if (!newKeyStroke.equals(optKeyStroke)) { - opt.setKeyStroke(action.getFullName(), newKeyStroke); - tool.setConfigChanged(true); - } - } - } -} diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java index e464465f77..c239c5904e 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ToolScreenShots.java @@ -281,8 +281,8 @@ public class ToolScreenShots extends GhidraScreenShotGenerator { tool = env.launchDefaultTool(); DockingWindowManager windowManager = tool.getWindowManager(); - DockingActionManager actionMgr = - (DockingActionManager) getInstanceField("actionManager", windowManager); + ActionToGuiMapper actionMgr = + (ActionToGuiMapper) getInstanceField("actionManager", windowManager); DockingActionIf action = getAction(tool, "FunctionPlugin", "Delete Function"); final KeyEntryDialog keyEntryDialog = new KeyEntryDialog(action, actionMgr);