mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GT-2925 - Key Bindings - Support Window Menu Provider Key Bindings -
Step 1 - basic plumbing and tests
This commit is contained in:
parent
ed0a441862
commit
8c11c1eebd
50 changed files with 1180 additions and 843 deletions
|
@ -23,6 +23,8 @@ import java.util.Map.Entry;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
|
@ -34,7 +36,6 @@ import docking.widgets.filter.FilterTextField;
|
|||
import docking.widgets.label.GLabel;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.StringUtilities;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import resources.Icons;
|
||||
|
||||
|
@ -351,7 +352,7 @@ class FilterAction extends ToggleDockingAction {
|
|||
String curType = itr.next();
|
||||
Boolean lEnabled = typeEnabledMap.get(curType);
|
||||
StringBuffer buildMetaCurTypeBuff = new StringBuffer(curType);
|
||||
int firstIndex = StringUtilities.indexOfIgnoreCase(curType, filteredText, 0);
|
||||
int firstIndex = StringUtils.indexOfIgnoreCase(curType, filteredText, 0);
|
||||
int lastIndex = firstIndex + filteredText.length();
|
||||
buildMetaCurTypeBuff.insert(lastIndex, "</b>");//THIS MUST ALWAYS COME BEFORE FIRST INDEX (FOR NO MATH on INDEX)
|
||||
buildMetaCurTypeBuff.insert(firstIndex, "<b>");
|
||||
|
@ -409,7 +410,7 @@ class FilterAction extends ToggleDockingAction {
|
|||
while (iteratorIndex.hasNext()) {
|
||||
Entry<String, Boolean> entry = iteratorIndex.next();
|
||||
String checkboxName = entry.getKey();
|
||||
if (StringUtilities.containsIgnoreCase(checkboxName, filteredText)) {
|
||||
if (StringUtils.containsIgnoreCase(checkboxName, filteredText)) {
|
||||
checkboxNameList.add(checkboxName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,14 @@ package ghidra.app.plugin.core.navigation.locationreferences;
|
|||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.fieldpanel.support.Highlight;
|
||||
import ghidra.app.util.viewer.field.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.StringUtilities;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -115,7 +116,7 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
|
|||
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
||||
|
||||
// Not sure how to get the correct part of the text. This is a hack for now.
|
||||
int offset = StringUtilities.indexOfIgnoreCase(text, typeAndFieldName, 0);
|
||||
int offset = StringUtils.indexOfIgnoreCase(text, typeAndFieldName, 0);
|
||||
if (offset != -1) {
|
||||
return new Highlight[] {
|
||||
new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) };
|
||||
|
|
|
@ -28,6 +28,7 @@ import javax.swing.KeyStroke;
|
|||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
|
@ -92,7 +93,7 @@ class GhidraScriptActionManager {
|
|||
action.setKeyBindingData(null);
|
||||
}
|
||||
else {
|
||||
KeyStroke stroke = DockingKeyBindingAction.parseKeyStroke(strokeStr);
|
||||
KeyStroke stroke = KeyBindingUtils.parseKeyStroke(strokeStr);
|
||||
if (stroke == null) {
|
||||
break;
|
||||
}
|
||||
|
@ -137,7 +138,7 @@ class GhidraScriptActionManager {
|
|||
saveState.putString(scriptFile.getName(), "");
|
||||
}
|
||||
else {
|
||||
String strokeStr = DockingKeyBindingAction.parseKeyStroke(stroke);
|
||||
String strokeStr = KeyBindingUtils.parseKeyStroke(stroke);
|
||||
saveState.putString(scriptFile.getName(), strokeStr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ package ghidra.app.plugin.core.script;
|
|||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.DockingKeyBindingAction;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
class KeyBindingsInfo implements Comparable<KeyBindingsInfo> {
|
||||
boolean hasAction;
|
||||
|
@ -31,7 +31,7 @@ class KeyBindingsInfo implements Comparable<KeyBindingsInfo> {
|
|||
|
||||
KeyBindingsInfo(boolean hasAction, KeyStroke stroke) {
|
||||
this.hasAction = hasAction;
|
||||
this.keystroke = stroke == null ? "" : DockingKeyBindingAction.parseKeyStroke(stroke);
|
||||
this.keystroke = stroke == null ? "" : KeyBindingUtils.parseKeyStroke(stroke);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,7 +28,7 @@ import javax.swing.KeyStroke;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.DockingKeyBindingAction;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.Msg;
|
||||
|
@ -318,7 +318,7 @@ public class ScriptInfo {
|
|||
}
|
||||
}
|
||||
|
||||
keyBinding = DockingKeyBindingAction.parseKeyStroke(buildy.toString());
|
||||
keyBinding = KeyBindingUtils.parseKeyStroke(buildy.toString());
|
||||
if (keyBinding == null) {
|
||||
// note: this message will be cleared by the parseHeader() method
|
||||
keybindingErrorMessage = "Unable to parse keybinding: " + buildy;
|
||||
|
@ -479,7 +479,7 @@ public class ScriptInfo {
|
|||
}
|
||||
return "";
|
||||
}
|
||||
return DockingKeyBindingAction.parseKeyStroke(keyStroke);
|
||||
return KeyBindingUtils.parseKeyStroke(keyStroke);
|
||||
}
|
||||
|
||||
private String toToolTip(String string) {
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.KeyEntryDialog;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SpyErrorLogger;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
// note: this has to happen after the test framework is initialized, so it cannot be static
|
||||
private final Icon ICON = ResourceManager.loadImage("images/refresh.png");
|
||||
private static final String PROVIDER_NAME = "Test Action Provider";
|
||||
private static final KeyStroke CONTROL_T =
|
||||
KeyStroke.getKeyStroke(Character.valueOf('t'), DockingUtils.CONTROL_KEY_MODIFIER_MASK);
|
||||
|
||||
private TestEnv env;
|
||||
private PluginTool tool;
|
||||
private TestActionsComponentProvider provider;
|
||||
private SpyErrorLogger spyLogger = new SpyErrorLogger();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
env = new TestEnv();
|
||||
tool = env.launchDefaultTool();
|
||||
provider = new TestActionsComponentProvider(tool);
|
||||
|
||||
Msg.setErrorLogger(spyLogger);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIcon_WithIcon_BeforeAddedToTool() {
|
||||
|
||||
setIcon(ICON);
|
||||
|
||||
showProvider();
|
||||
|
||||
assertWindowMenuActionHasIcon(ICON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIcon_WithIcon_AfterAddedToTool() {
|
||||
|
||||
setIcon(null);
|
||||
|
||||
showProvider();
|
||||
assertWindowMenuActionHasIcon(Icons.EMPTY_ICON);
|
||||
|
||||
setIcon(ICON);
|
||||
assertWindowMenuActionHasIcon(ICON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIcon_WithoutIcon() {
|
||||
showProvider();
|
||||
assertWindowMenuActionHasIcon(Icons.EMPTY_ICON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetKeyBinding_ViaDialog_FromWindowMenu() {
|
||||
|
||||
showProvider();
|
||||
|
||||
KeyStroke newKs = CONTROL_T;
|
||||
setKeyBindingViaF4Dialog_FromWindowsMenu(newKs);
|
||||
|
||||
assertProviderKeyStroke(newKs);
|
||||
assertOptionsKeyStroke(newKs);
|
||||
assertMenuItemHasKeyStroke(newKs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetKeyBinding_ViaOptions_WithoutToolbarAction() {
|
||||
|
||||
showProvider();
|
||||
|
||||
KeyStroke newKs = CONTROL_T;
|
||||
setOptionsKeyStroke(newKs);
|
||||
|
||||
assertProviderKeyStroke(newKs);
|
||||
assertOptionsKeyStroke(newKs);
|
||||
assertMenuItemHasKeyStroke(newKs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetKeyBinding_ViaOptions_WithToolbarAction() {
|
||||
|
||||
showProvider();
|
||||
|
||||
KeyStroke newKs = CONTROL_T;
|
||||
setOptionsKeyStroke(newKs);
|
||||
|
||||
assertProviderKeyStroke(newKs);
|
||||
assertOptionsKeyStroke(newKs);
|
||||
assertMenuItemHasKeyStroke(newKs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetKeyBinding_ViaDialog_FromToolBar() {
|
||||
|
||||
setToolbarIcon(ICON);
|
||||
showProvider();
|
||||
|
||||
KeyStroke newKs = CONTROL_T;
|
||||
setKeyBindingViaF4Dialog_FromToolsToolbar(newKs);
|
||||
|
||||
assertProviderKeyStroke(newKs);
|
||||
assertOptionsKeyStroke(newKs);
|
||||
assertMenuItemHasKeyStroke(newKs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetKeyBinding_TransientProvider_CannotBeSetFromWindowMenu() {
|
||||
|
||||
switchToTransientProvider();
|
||||
showProvider();
|
||||
|
||||
assertCannotShowKeyBindingDialog_FromWindowsMenu();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetKeyBinding_TransientProvider_CannotBeSetFromToolbar() {
|
||||
|
||||
switchToTransientProvider();
|
||||
|
||||
setErrorsExpected(true);
|
||||
setToolbarIcon(ICON);
|
||||
setErrorsExpected(false);
|
||||
|
||||
spyLogger.assertLogMessage("Transient", "not", "toolbar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetIcon_NullIconWithToolbarAction() {
|
||||
|
||||
try {
|
||||
setToolbarIcon(null);
|
||||
fail("Expected an exception passing a null icon when specifying a toolbar action");
|
||||
}
|
||||
catch (Exception e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeActionRelatedStateAfterConstruction_setTransient() {
|
||||
|
||||
setToolbarIcon(ICON);
|
||||
showProvider(); // this creates the 'Show Provider' action
|
||||
assertShowProviderActionIsInToolbar();
|
||||
|
||||
setErrorsExpected(true);
|
||||
switchToTransientProvider();
|
||||
setErrorsExpected(false);
|
||||
|
||||
assertShowProviderActionNotInToolbar();
|
||||
spyLogger.assertLogMessage("Transient", "not", "toolbar");
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// 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();
|
||||
}
|
||||
|
||||
private void showProvider() {
|
||||
provider.addToTool();
|
||||
tool.showComponentProvider(provider, true);
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void setIcon(Icon icon) {
|
||||
runSwing(() -> provider.setIcon(icon));
|
||||
}
|
||||
|
||||
private void setToolbarIcon(Icon icon) {
|
||||
runSwing(() -> provider.setIcon(icon, true));
|
||||
}
|
||||
|
||||
private DockingActionIf getShowProviderAction() {
|
||||
|
||||
DockingActionIf showProviderAction =
|
||||
getAction(tool, provider.getOwner(), provider.getName());
|
||||
assertNotNull("Could not find action to show ", showProviderAction);
|
||||
return showProviderAction;
|
||||
}
|
||||
|
||||
private DockingActionIf getWindowMenuShowProviderAction() {
|
||||
waitForSwing();
|
||||
DockingActionIf action = waitFor(() -> {
|
||||
Set<DockingActionIf> actions = getActionsByName(tool, PROVIDER_NAME);
|
||||
|
||||
//@formatter:off
|
||||
return actions
|
||||
.stream()
|
||||
.filter(a -> a.getOwner().equals(DockingWindowManager.DOCKING_WINDOWS_OWNER))
|
||||
.findFirst()
|
||||
.get()
|
||||
;
|
||||
//@formatter:on
|
||||
});
|
||||
|
||||
assertNotNull("Window menu action not installed for provider", action);
|
||||
assertTrue(action.getClass().getSimpleName().contains("ShowComponentAction"));
|
||||
return action;
|
||||
}
|
||||
|
||||
private DockingActionIf getToolbarShowProviderAction() {
|
||||
|
||||
DockingWindowManager dwm = tool.getWindowManager();
|
||||
ActionToGuiMapper guiActions =
|
||||
(ActionToGuiMapper) getInstanceField("actionToGuiMapper", dwm);
|
||||
GlobalMenuAndToolBarManager toolbarManager =
|
||||
(GlobalMenuAndToolBarManager) getInstanceField("menuAndToolBarManager", guiActions);
|
||||
DockingActionIf action = provider.getShowProviderAction();
|
||||
DockingActionIf toolbarAction = toolbarManager.getToolbarAction(action.getName());
|
||||
return toolbarAction;
|
||||
}
|
||||
|
||||
private void setOptionsKeyStroke(KeyStroke newKs) {
|
||||
ToolOptions keyOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
// shared option name/format: "Provider Name (Tool)" - the shared action's owner is the Tool
|
||||
runSwing(() -> keyOptions.setKeyStroke(provider.getName() + " (Tool)", newKs));
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void assertProviderKeyStroke(KeyStroke expectedKs) {
|
||||
|
||||
DockingActionIf action = getShowProviderAction();
|
||||
KeyStroke actionKs = action.getKeyBinding();
|
||||
assertEquals(expectedKs, actionKs);
|
||||
}
|
||||
|
||||
private void assertOptionsKeyStroke(KeyStroke expectedKs) {
|
||||
|
||||
ToolOptions options = getKeyBindingOptions();
|
||||
|
||||
// Option name: the action name with the 'Tool' as the owner
|
||||
String fullName = provider.getName() + " (Tool)";
|
||||
KeyStroke optionsKs = runSwing(() -> options.getKeyStroke(fullName, null));
|
||||
assertEquals("Key stroke in options does not match expected key stroke", expectedKs,
|
||||
optionsKs);
|
||||
}
|
||||
|
||||
private void assertWindowMenuActionHasIcon(Icon expected) {
|
||||
DockingActionIf action = getWindowMenuShowProviderAction();
|
||||
assertEquals("Windows menu icons for provider does not match the value set on the provider",
|
||||
expected, action.getMenuBarData().getMenuIcon());
|
||||
}
|
||||
|
||||
private void assertCannotShowKeyBindingDialog_FromWindowsMenu() {
|
||||
// simulate the user mousing over the 'Window' menu's action
|
||||
DockingActionIf windowMenuAction = getWindowMenuShowProviderAction();
|
||||
DockingWindowManager.setMouseOverAction(windowMenuAction);
|
||||
|
||||
performLaunchKeyStrokeDialogAction();
|
||||
DialogComponentProvider warningDialog = waitForDialogComponent("Unable to Set Keybinding");
|
||||
close(warningDialog);
|
||||
}
|
||||
|
||||
private void setKeyBindingViaF4Dialog_FromWindowsMenu(KeyStroke ks) {
|
||||
|
||||
// simulate the user mousing over the 'Window' menu's action
|
||||
DockingActionIf windowMenuAction = getWindowMenuShowProviderAction();
|
||||
DockingWindowManager.setMouseOverAction(windowMenuAction);
|
||||
|
||||
performLaunchKeyStrokeDialogAction();
|
||||
KeyEntryDialog dialog = waitForDialogComponent(KeyEntryDialog.class);
|
||||
|
||||
runSwing(() -> dialog.setKeyStroke(ks));
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
assertFalse("Invalid key stroke: " + ks, runSwing(() -> dialog.isVisible()));
|
||||
}
|
||||
|
||||
private void assertMenuItemHasKeyStroke(KeyStroke expected) {
|
||||
|
||||
DockingActionIf action = getWindowMenuShowProviderAction();
|
||||
assertEquals(
|
||||
"Windows menu key binding for provider does not match the value of the provider",
|
||||
expected, action.getKeyBinding());
|
||||
}
|
||||
|
||||
private void setKeyBindingViaF4Dialog_FromToolsToolbar(KeyStroke ks) {
|
||||
|
||||
// simulate the user mousing over the 'Window' menu's action
|
||||
DockingActionIf toolbarAction = getToolbarShowProviderAction();
|
||||
assertNotNull("Provider action not installed in toolbar", toolbarAction);
|
||||
DockingWindowManager.setMouseOverAction(toolbarAction);
|
||||
|
||||
performLaunchKeyStrokeDialogAction();
|
||||
KeyEntryDialog dialog = waitForDialogComponent(KeyEntryDialog.class);
|
||||
|
||||
runSwing(() -> dialog.setKeyStroke(ks));
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
assertFalse("Invalid key stroke: " + ks, runSwing(() -> dialog.isVisible()));
|
||||
}
|
||||
|
||||
private void performLaunchKeyStrokeDialogAction() {
|
||||
DockingWindowManager dwm = tool.getWindowManager();
|
||||
ActionToGuiMapper actionMapper = dwm.getActionToGuiMapper();
|
||||
Action action = actionMapper.getDockingKeyAction(KeyStroke.getKeyStroke("F4"));
|
||||
assertNotNull(action);
|
||||
runSwing(() -> action.actionPerformed(new ActionEvent(this, 0, "")), false);
|
||||
}
|
||||
|
||||
private ToolOptions getKeyBindingOptions() {
|
||||
return tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
}
|
||||
|
||||
private class TestActionsComponentProvider extends ComponentProvider {
|
||||
|
||||
private JComponent component = new JTextField("Hey!");
|
||||
|
||||
TestActionsComponentProvider(DockingTool tool) {
|
||||
super(tool, PROVIDER_NAME, "Fooberry Plugin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import javax.swing.*;
|
|||
import org.junit.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.actions.KeyEntryDialog;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.data.DataPlugin;
|
||||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||
|
|
|
@ -33,8 +33,8 @@ import javax.swing.tree.TreePath;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.*;
|
||||
|
||||
import docking.DockingKeyBindingAction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.options.editor.*;
|
||||
import docking.widgets.MultiLineLabel;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
|
@ -712,7 +712,7 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
if (StringUtils.isBlank(keyBindingColumnValue)) {
|
||||
return null;
|
||||
}
|
||||
return DockingKeyBindingAction.parseKeyStroke(keyBindingColumnValue);
|
||||
return KeyBindingUtils.parseKeyStroke(keyBindingColumnValue);
|
||||
}
|
||||
|
||||
private void assertOptionsKeyStroke(String actionName, String pluginName, KeyStroke value)
|
||||
|
|
|
@ -55,30 +55,16 @@ public class DummyTool implements Tool {
|
|||
this.project = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type name of the tool.
|
||||
* @exception PropertyVetoException thrown if a VetoableChangeListener
|
||||
* rejects the change
|
||||
*/
|
||||
@Override
|
||||
public void setToolName(String typeName) throws PropertyVetoException {
|
||||
name = typeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the tool to stop functioning and release its resources.
|
||||
* Called by Session when it wants to dispose of a tool. The tool
|
||||
* MUST NOT call System.exit() in response to this method call. Instead
|
||||
* the tool should dispose of all its windows and other resources.
|
||||
*/
|
||||
@Override
|
||||
public void exit() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.framework.model.Tool#close()
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (project != null) {
|
||||
|
@ -92,37 +78,21 @@ public class DummyTool implements Tool {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a unique(within a session) name to a tool instance.
|
||||
*/
|
||||
@Override
|
||||
public void putInstanceName(String newInstanceName) {
|
||||
this.instanceName = newInstanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name associated with the tool's type.
|
||||
*/
|
||||
@Override
|
||||
public String getToolName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tool visible or invisible. This method is used by
|
||||
* the Session to make it's tools visible or invisible depending
|
||||
* on whether or not the session this tool is in is the current Session.
|
||||
*
|
||||
* @param visibility true specifies that the tool should be visible.
|
||||
*/
|
||||
@Override
|
||||
public void setVisible(boolean visibility) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Tool#isVisible()
|
||||
*/
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return false;
|
||||
|
@ -133,238 +103,129 @@ public class DummyTool implements Tool {
|
|||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a combination of the type name and the instance name of the
|
||||
* form typename(instancename)
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name + instanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of eventNames that this Tool is interested in.
|
||||
*/
|
||||
@Override
|
||||
public String[] getConsumedToolEventNames() {
|
||||
return new String[] { "DummyToolEvent" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tool's unique name.
|
||||
*/
|
||||
@Override
|
||||
public String getInstanceName() {
|
||||
return instanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a ToolListener to be notified only for a specific ToolEvent.
|
||||
*
|
||||
* @param listener The ToolListener to be added.
|
||||
* @param toolEvent The name of the desired event.
|
||||
*/
|
||||
public void addToolListener(ToolListener listener, String toolEvent) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a ToolListener to be notified only for a specific ToolEvent.
|
||||
*
|
||||
* @param listener The ToolListener to be added.
|
||||
* @param toolEvent The name of the desired event.
|
||||
*/
|
||||
@Override
|
||||
public void addToolListener(ToolListener listener) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all the possible ToolEvents that this
|
||||
* tool might generate. Used by the ConnectionManager to connect
|
||||
* tools together.
|
||||
*/
|
||||
@Override
|
||||
public String[] getToolEventNames() {
|
||||
return new String[] { "DummyToolEvent" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a ToolListener from receiving the specific event.
|
||||
*
|
||||
* @param listener The ToolListener to be removed.
|
||||
* @param toolEvent The name of the event that no longer is of interest.
|
||||
*/
|
||||
public void removeToolListener(ToolListener listener, String toolEvent) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a ToolListener from receiving the specific event.
|
||||
*
|
||||
* @param listener The ToolListener to be removed.
|
||||
* @param toolEvent The name of the event that no longer is of interest.
|
||||
*/
|
||||
@Override
|
||||
public void removeToolListener(ToolListener listener) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this tool has changed its configuration.
|
||||
* This is called to check if a tool needs to save its state, when the
|
||||
* tool exits or the session the tool is in closes.
|
||||
*
|
||||
* @return true if the tool's configuration has changed, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean hasConfigChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a change listener that is notified when a tool changes its state.
|
||||
*/
|
||||
public void addChangeListener(ChangeListener l) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Add property change listener.
|
||||
*/
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener l) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the classes of the data types that this tool supports,
|
||||
* i.e., what data types can be dropped onto this tool.
|
||||
*/
|
||||
@Override
|
||||
public Class<?>[] getSupportedDataTypes() {
|
||||
return new Class[] { Program.class };
|
||||
}
|
||||
|
||||
/**
|
||||
* When the user drags a data file onto a tool, an event will be fired
|
||||
* that the tool will respond to by accepting the data.
|
||||
*
|
||||
* @param data the data to be used by the running tool
|
||||
*/
|
||||
@Override
|
||||
public boolean acceptDomainFiles(DomainFile[] data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain files that this tool currently has open.
|
||||
*/
|
||||
@Override
|
||||
public DomainFile[] getDomainFiles() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the change listener.
|
||||
*/
|
||||
public void removeChangeListener(ChangeListener l) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells tool to write its config state from the given output stream.
|
||||
*/
|
||||
@Override
|
||||
public void setConfigChanged(boolean changed) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells tool to write its config state from the given output stream.
|
||||
*/
|
||||
@Override
|
||||
public Element saveToXml(boolean includeConfigState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells tool to write its config state from the given output stream.
|
||||
*/
|
||||
@Override
|
||||
public Element saveDataStateToXml(boolean isTransactionState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells tool to write its config state from the given output stream.
|
||||
*/
|
||||
public void restoreFromXml(Element root) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells tool to write its config state from the given output stream.
|
||||
*/
|
||||
@Override
|
||||
public void restoreDataStateFromXml(Element root) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove property change listener.
|
||||
*/
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener l) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked when the registered ToolEvent event occurs.
|
||||
*
|
||||
* @param toolEvent The ToolEvent.
|
||||
*/
|
||||
@Override
|
||||
public void processToolEvent(PluginEvent toolEvent) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the plugin event by notifying the event manager which
|
||||
* calls the listeners.
|
||||
*/
|
||||
@Override
|
||||
public void firePluginEvent(PluginEvent event) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description of the tool.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the description of the tool.
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the icon for this tool configuration.
|
||||
*/
|
||||
@Override
|
||||
public void setIconURL(ToolIconURL iconURL) {
|
||||
this.iconURL = iconURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon for this tool configuration.
|
||||
*
|
||||
* @return Icon
|
||||
*/
|
||||
@Override
|
||||
public ToolIconURL getIconURL() {
|
||||
return iconURL;
|
||||
|
@ -380,9 +241,6 @@ public class DummyTool implements Tool {
|
|||
return getToolTemplate(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Tool#enableClose()
|
||||
*/
|
||||
public void enableClose() {
|
||||
//do nothing
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.util.regex.*;
|
|||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.SearchLocation;
|
||||
import docking.widgets.fieldpanel.Layout;
|
||||
import docking.widgets.fieldpanel.LayoutModel;
|
||||
|
@ -37,7 +39,6 @@ import ghidra.program.model.listing.Function;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -522,7 +523,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
|
||||
java.util.function.Function<String, SearchMatch> function = textLine -> {
|
||||
|
||||
int index = StringUtilities.indexOfIgnoreCase(textLine, searchString);
|
||||
int index = StringUtils.indexOfIgnoreCase(textLine, searchString);
|
||||
if (index == -1) {
|
||||
return SearchMatch.NO_MATCH;
|
||||
}
|
||||
|
@ -534,7 +535,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
|
||||
java.util.function.Function<String, SearchMatch> function = textLine -> {
|
||||
|
||||
int index = StringUtilities.lastIndexOfIgnoreCase(textLine, searchString);
|
||||
int index = StringUtils.lastIndexOfIgnoreCase(textLine, searchString);
|
||||
if (index == -1) {
|
||||
return SearchMatch.NO_MATCH;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import javax.swing.JFrame;
|
|||
import docking.action.DockingActionIf;
|
||||
import docking.actions.ToolActions;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
* A partial implementation of {@link DockingTool} that serves as a place to share common
|
||||
|
@ -32,7 +32,7 @@ import ghidra.util.SystemUtilities;
|
|||
public abstract class AbstractDockingTool implements DockingTool {
|
||||
|
||||
protected DockingWindowManager winMgr;
|
||||
protected ToolActions actionMgr;
|
||||
protected ToolActions toolActions;
|
||||
protected Map<String, ToolOptions> optionsMap = new HashMap<>();
|
||||
protected boolean configChangedFlag;
|
||||
|
||||
|
@ -57,17 +57,20 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
|
||||
@Override
|
||||
public void addComponentProvider(ComponentProvider provider, boolean show) {
|
||||
Runnable r = () -> winMgr.addComponent(provider, show);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Runnable r = () -> {
|
||||
winMgr.addComponent(provider, show);
|
||||
toolActions.addToolAction(provider.getShowProviderAction());
|
||||
};
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeComponentProvider(ComponentProvider provider) {
|
||||
Runnable r = () -> {
|
||||
actionMgr.removeComponentActions(provider);
|
||||
toolActions.removeComponentActions(provider);
|
||||
winMgr.removeComponent(provider);
|
||||
};
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,41 +99,41 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
|
||||
@Override
|
||||
public void addAction(DockingActionIf action) {
|
||||
actionMgr.addToolAction(action);
|
||||
toolActions.addToolAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAction(DockingActionIf action) {
|
||||
actionMgr.removeToolAction(action);
|
||||
toolActions.removeToolAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||
actionMgr.addLocalAction(provider, action);
|
||||
toolActions.addLocalAction(provider, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||
actionMgr.removeProviderAction(provider, action);
|
||||
toolActions.removeProviderAction(provider, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DockingActionIf> getAllActions() {
|
||||
Set<DockingActionIf> actions = actionMgr.getAllActions();
|
||||
ActionToGuiMapper am = winMgr.getActionManager();
|
||||
Set<DockingActionIf> actions = toolActions.getAllActions();
|
||||
ActionToGuiMapper am = winMgr.getActionToGuiMapper();
|
||||
actions.addAll(am.getAllActions());
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner) {
|
||||
return actionMgr.getActions(owner);
|
||||
return toolActions.getActions(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showComponentProvider(ComponentProvider provider, boolean visible) {
|
||||
Runnable r = () -> winMgr.showComponent(provider, visible);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,7 +153,7 @@ public abstract class AbstractDockingTool implements DockingTool {
|
|||
@Override
|
||||
public void toFront(ComponentProvider provider) {
|
||||
Runnable r = () -> winMgr.toFront(provider);
|
||||
SystemUtilities.runSwingNow(r);
|
||||
Swing.runNow(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@ 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
|
||||
|
@ -78,4 +79,20 @@ public class ActionToGuiHelper {
|
|||
public void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
||||
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
|
||||
*/
|
||||
public void keyBindingsChanged() {
|
||||
windowManager.scheduleUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,26 +30,21 @@ import ghidra.util.*;
|
|||
*/
|
||||
public class ActionToGuiMapper {
|
||||
|
||||
private HashSet<DockingActionIf> globalActions = new LinkedHashSet<>();
|
||||
private static boolean enableDiagnosticActions;
|
||||
|
||||
private Set<DockingActionIf> globalActions = new LinkedHashSet<>();
|
||||
|
||||
private MenuHandler menuBarMenuHandler;
|
||||
private MenuGroupMap menuGroupMap;
|
||||
|
||||
private static boolean enableDiagnosticActions;
|
||||
|
||||
private KeyBindingsManager keyBindingsManager;
|
||||
|
||||
private GlobalMenuAndToolBarManager menuAndToolBarManager;
|
||||
|
||||
private PopupActionManager popupActionManager;
|
||||
private DockingAction keyBindingsAction;
|
||||
|
||||
ActionToGuiMapper(DockingWindowManager winMgr) {
|
||||
ActionToGuiMapper(DockingWindowManager winMgr, KeyBindingsManager keyBindingsManager) {
|
||||
this.keyBindingsManager = keyBindingsManager;
|
||||
menuGroupMap = new MenuGroupMap();
|
||||
|
||||
menuBarMenuHandler = new MenuBarMenuHandler(winMgr);
|
||||
|
||||
keyBindingsManager = new KeyBindingsManager(winMgr);
|
||||
menuAndToolBarManager =
|
||||
new GlobalMenuAndToolBarManager(winMgr, menuBarMenuHandler, menuGroupMap);
|
||||
popupActionManager = new PopupActionManager(winMgr, menuGroupMap);
|
||||
|
@ -60,12 +55,10 @@ public class ActionToGuiMapper {
|
|||
private void initializeHelpActions() {
|
||||
DockingWindowsContextSensitiveHelpListener.install();
|
||||
|
||||
keyBindingsAction = new KeyBindingAction(this);
|
||||
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY1));
|
||||
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY2));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new HelpAction(true, ReservedKeyBindings.HELP_INFO_KEY));
|
||||
keyBindingsManager.addReservedAction(keyBindingsAction);
|
||||
|
||||
if (enableDiagnosticActions) {
|
||||
keyBindingsManager.addReservedAction(new ShowFocusInfoAction());
|
||||
|
@ -157,10 +150,6 @@ public class ActionToGuiMapper {
|
|||
return actions;
|
||||
}
|
||||
|
||||
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
||||
return keyBindingsManager.getDockingKeyAction(keyStroke);
|
||||
}
|
||||
|
||||
Set<DockingActionIf> getGlobalActions() {
|
||||
return globalActions;
|
||||
}
|
||||
|
@ -227,4 +216,8 @@ public class ActionToGuiMapper {
|
|||
public void showPopupMenu(ComponentPlaceholder componentInfo, MouseEvent e) {
|
||||
popupActionManager.popupMenu(componentInfo, e);
|
||||
}
|
||||
|
||||
Action getDockingKeyAction(KeyStroke keyStroke) {
|
||||
return keyBindingsManager.getDockingKeyAction(keyStroke);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ import javax.swing.*;
|
|||
import docking.action.*;
|
||||
import docking.help.HelpDescriptor;
|
||||
import docking.help.HelpService;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.UniversalIdGenerator;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
/**
|
||||
* Abstract base class for creating dockable GUI components within a tool.
|
||||
|
@ -61,13 +61,25 @@ import ghidra.util.exception.AssertException;
|
|||
* <li>{@link #componentActivated()} and {@link #componentDeactived()}
|
||||
* <li>{@link #componentHidden()} and {@link #componentShown()}
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Note: This class was created so that implementors could add local actions within the constructor
|
||||
* <b><u>Show Provider Action</u></b> - Each provider has an action to show the provider. For
|
||||
* typical, non-transient providers (see {@link #setTransient()}) the action will appear in
|
||||
* the tool's <b>Window</b> menu. You can have your provider also appear in the tool's toolbar
|
||||
* by calling {@link #setIcon(Icon, boolean)}, passing <code>true</code> for
|
||||
* <code>isToolbarAction</code>.
|
||||
* <p>
|
||||
* Historical Note: This class was created so that implementors could add local actions within the constructor
|
||||
* without having to understand that they must first add themselves to the WindowManager.
|
||||
*/
|
||||
|
||||
public abstract class ComponentProvider implements HelpDescriptor, ActionContextProvider {
|
||||
private static final String TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE =
|
||||
"Transient providers are not added to the toolbar";
|
||||
|
||||
public static final String DEFAULT_WINDOW_GROUP = "Default";
|
||||
|
||||
private static final String TOOLBAR_GROUP = "View";
|
||||
|
||||
// maps for mapping old provider names and owner to new names and/or owner
|
||||
private static Map<String, String> oldOwnerMap = new HashMap<>();
|
||||
private static Map<String, String> oldNameMap = new HashMap<>();
|
||||
|
@ -77,14 +89,21 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
private String title;
|
||||
private String subTitle;
|
||||
private String tabText;
|
||||
private Icon icon;
|
||||
|
||||
private Set<DockingActionIf> actionSet = new LinkedHashSet<>();
|
||||
private String windowMenuGroup;
|
||||
|
||||
/** True if this provider's action should appear in the toolbar */
|
||||
private boolean isToolbarAction;
|
||||
private boolean isTransient;
|
||||
private HelpLocation helpLocation;
|
||||
|
||||
private Icon icon;
|
||||
private String windowMenuGroup;
|
||||
private String group = DEFAULT_WINDOW_GROUP;
|
||||
private WindowPosition defaultWindowPosition = WindowPosition.WINDOW;
|
||||
private WindowPosition defaultIntraGroupPosition = WindowPosition.STACK;
|
||||
private DockingAction showProviderAction;
|
||||
|
||||
private final Class<?> contextType;
|
||||
|
||||
private long instanceID = UniversalIdGenerator.nextID().getValue();
|
||||
|
@ -118,6 +137,32 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
this.contextType = contextType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action used to show this provider
|
||||
* @return the action
|
||||
*/
|
||||
DockingActionIf getShowProviderAction() {
|
||||
createShowProviderAction();
|
||||
return showProviderAction;
|
||||
}
|
||||
|
||||
private void createShowProviderAction() {
|
||||
if (showProviderAction != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
showProviderAction = new ShowProviderAction();
|
||||
}
|
||||
|
||||
private void removeShowProviderAction() {
|
||||
if (showProviderAction == null) {
|
||||
return; // not installed
|
||||
}
|
||||
|
||||
dockingTool.removeAction(showProviderAction);
|
||||
showProviderAction = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component to be displayed
|
||||
* @return the component to be displayed
|
||||
|
@ -453,8 +498,55 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
* @param icon the icon to use for this provider.
|
||||
*/
|
||||
public void setIcon(Icon icon) {
|
||||
setIcon(icon, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for setting the provider's icon
|
||||
*
|
||||
* @param icon the icon to use for this provider
|
||||
* @param isToolbarAction true will cause this action to get added to the toolbar; if this
|
||||
* value is true, then the icon cannot be null
|
||||
*/
|
||||
public void setIcon(Icon icon, boolean isToolbarAction) {
|
||||
this.icon = icon;
|
||||
this.isToolbarAction = isToolbarAction;
|
||||
|
||||
if (isToolbarAction) {
|
||||
Objects.requireNonNull(icon,
|
||||
"Icon cannot be null when requesting the provider's action appear in the toolbar");
|
||||
|
||||
if (isTransient) {
|
||||
Msg.error(this, TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE,
|
||||
ReflectionUtilities.createJavaFilteredThrowable());
|
||||
isToolbarAction = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInTool()) {
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
4) Wire default 'close' action to keybinding
|
||||
5) Add global action for (show last provider)
|
||||
6) Remove plugin code that creates the 'show' actions
|
||||
|
||||
Questions:
|
||||
|
||||
C) How to wire universal close action (it is focus-dependent)
|
||||
|
||||
|
||||
Fix:
|
||||
|
||||
-Toolbar description for key doesn't match menu (goes away)
|
||||
-dummy actions getting added to toolbar
|
||||
-cleanup odd relationship with keybindings action and UI (move to tool??
|
||||
or ToolActions...<= this)
|
||||
|
||||
|
||||
*/
|
||||
|
||||
dockingTool.getWindowManager().setIcon(this, icon);
|
||||
}
|
||||
}
|
||||
|
@ -465,7 +557,6 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
* @return the menu group for this provider or null if this provider should appear in the
|
||||
* top-level menu.
|
||||
*/
|
||||
|
||||
public String getWindowSubMenuName() {
|
||||
return windowMenuGroup;
|
||||
}
|
||||
|
@ -473,7 +564,7 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
/**
|
||||
* Returns true if this component goes away during a user session (most providers remain in
|
||||
* the tool all session long, visible or not)
|
||||
* @return true if transitent
|
||||
* @return true if transient
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
return isTransient;
|
||||
|
@ -485,6 +576,15 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
*/
|
||||
protected void setTransient() {
|
||||
isTransient = true;
|
||||
|
||||
// avoid visually disturbing the user by adding/removing toolbar actions for temp providers
|
||||
if (isToolbarAction) {
|
||||
isToolbarAction = false;
|
||||
Msg.error(this, TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE,
|
||||
ReflectionUtilities.createJavaFilteredThrowable());
|
||||
}
|
||||
|
||||
removeShowProviderAction();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -633,4 +733,29 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
|||
return "owner=" + oldOwner + "name=" + oldName;
|
||||
}
|
||||
|
||||
private class ShowProviderAction extends DockingAction {
|
||||
|
||||
ShowProviderAction() {
|
||||
super(name, owner);
|
||||
|
||||
if (isToolbarAction) {
|
||||
setToolBarData(new ToolBarData(icon, TOOLBAR_GROUP));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
dockingTool.showComponentProvider(ComponentProvider.this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeyBindingManaged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesSharedKeyBinding() {
|
||||
return !isTransient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class DialogComponentProviderPopupActionManager {
|
|||
return;
|
||||
}
|
||||
|
||||
ActionToGuiMapper actionManager = dwm.getActionManager();
|
||||
ActionToGuiMapper actionManager = dwm.getActionToGuiMapper();
|
||||
MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap();
|
||||
MenuManager menuMgr =
|
||||
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
|
||||
|
|
|
@ -65,7 +65,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
|
|||
this.componentInfo = placeholder;
|
||||
|
||||
winMgr = placeholder.getNode().winMgr;
|
||||
actionMgr = winMgr.getActionManager();
|
||||
actionMgr = winMgr.getActionToGuiMapper();
|
||||
|
||||
popupListener = new MouseAdapter() {
|
||||
@Override
|
||||
|
|
|
@ -60,7 +60,7 @@ class DockableToolBarManager {
|
|||
ComponentPlaceholder placeholder = dockableComp.getComponentWindowingPlaceholder();
|
||||
DockingWindowManager winMgr =
|
||||
dockableComp.getComponentWindowingPlaceholder().getNode().winMgr;
|
||||
ActionToGuiMapper actionManager = winMgr.getActionManager();
|
||||
ActionToGuiMapper actionManager = winMgr.getActionToGuiMapper();
|
||||
menuGroupMap = actionManager.getMenuGroupMap();
|
||||
|
||||
MenuHandler menuHandler = actionManager.getMenuHandler();
|
||||
|
|
|
@ -15,15 +15,13 @@
|
|||
*/
|
||||
package docking;
|
||||
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.StringUtilities;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
/**
|
||||
* A class that can be used as an interface for using actions associated with keybindings. This
|
||||
|
@ -31,17 +29,6 @@ import ghidra.util.StringUtilities;
|
|||
*/
|
||||
public class DockingKeyBindingAction extends AbstractAction {
|
||||
|
||||
private static final String RELEASED = "released";
|
||||
private static final String TYPED = "typed";
|
||||
private static final String PRESSED = "pressed";
|
||||
|
||||
private static final String SHIFT = "Shift";
|
||||
private static final String CTRL = "Ctrl";
|
||||
private static final String CONTROL = "Control";
|
||||
private static final String ALT = "Alt";
|
||||
private static final String META = "Meta";
|
||||
private static final String MODIFIER_SEPARATOR = "-";
|
||||
|
||||
private DockingActionIf docakbleAction;
|
||||
|
||||
protected KeyStroke keyStroke;
|
||||
|
@ -49,7 +36,7 @@ public class DockingKeyBindingAction extends AbstractAction {
|
|||
|
||||
public DockingKeyBindingAction(DockingWindowManager winMgr, DockingActionIf action,
|
||||
KeyStroke keyStroke) {
|
||||
super(parseKeyStroke(keyStroke));
|
||||
super(KeyBindingUtils.parseKeyStroke(keyStroke));
|
||||
this.winMgr = winMgr;
|
||||
this.docakbleAction = action;
|
||||
this.keyStroke = keyStroke;
|
||||
|
@ -94,147 +81,4 @@ public class DockingKeyBindingAction extends AbstractAction {
|
|||
|
||||
return new ActionContext(localProvider, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the toString() form of the keyStroke.
|
||||
* <br>In Java 1.4.2 & earlier, Ctrl-M is returned as "keyCode CtrlM-P"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
* <br>In Java 1.5.0, Ctrl-M is returned as "ctrl pressed M"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
*/
|
||||
public static String parseKeyStroke(KeyStroke keyStroke) {
|
||||
final String keyPressSuffix = "-P";
|
||||
|
||||
String keyString = keyStroke.toString();
|
||||
int type = keyStroke.getKeyEventType();
|
||||
if (type == KeyEvent.KEY_TYPED) {
|
||||
return String.valueOf(keyStroke.getKeyChar());
|
||||
}
|
||||
|
||||
// get the character used in the key stroke
|
||||
int firstIndex = keyString.lastIndexOf(' ') + 1;
|
||||
int ctrlIndex = keyString.indexOf(CTRL, firstIndex);
|
||||
if (ctrlIndex >= 0) {
|
||||
firstIndex = ctrlIndex + CTRL.length();
|
||||
}
|
||||
int altIndex = keyString.indexOf(ALT, firstIndex);
|
||||
if (altIndex >= 0) {
|
||||
firstIndex = altIndex + ALT.length();
|
||||
}
|
||||
int shiftIndex = keyString.indexOf(SHIFT, firstIndex);
|
||||
if (shiftIndex >= 0) {
|
||||
firstIndex = shiftIndex + SHIFT.length();
|
||||
}
|
||||
int metaIndex = keyString.indexOf(META, firstIndex);
|
||||
if (metaIndex >= 0) {
|
||||
firstIndex = metaIndex + META.length();
|
||||
}
|
||||
|
||||
int lastIndex = keyString.length();
|
||||
if (keyString.endsWith(keyPressSuffix)) {
|
||||
lastIndex -= keyPressSuffix.length();
|
||||
}
|
||||
if (lastIndex >= 0) {
|
||||
keyString = keyString.substring(firstIndex, lastIndex);
|
||||
}
|
||||
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
|
||||
buffer.insert(0, SHIFT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if ((modifiers & InputEvent.ALT_MASK) != 0) {
|
||||
buffer.insert(0, ALT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if ((modifiers & InputEvent.CTRL_MASK) != 0) {
|
||||
buffer.insert(0, CTRL + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if ((modifiers & InputEvent.META_MASK) != 0) {
|
||||
buffer.insert(0, META + MODIFIER_SEPARATOR);
|
||||
}
|
||||
buffer.append(keyString);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given text into a KeyStroke. This method relies upon
|
||||
* {@link KeyStroke#getKeyStroke(String)} for parsing. Before making that call, this method
|
||||
* will perform fixup on the given text for added flexibility. For example, the given
|
||||
* text may contain spaces or dashes as the separators between parts in the string. Also,
|
||||
* the text is converted such that it is not case-sensitive. So, the following example
|
||||
* formats are allowed:
|
||||
* <pre>
|
||||
* Alt-F
|
||||
* alt p
|
||||
* Ctrl-Alt-Z
|
||||
* ctrl Z
|
||||
* </pre>
|
||||
*
|
||||
* @param keyStroke
|
||||
* @return
|
||||
*/
|
||||
public static KeyStroke parseKeyStroke(String keyStroke) {
|
||||
List<String> pieces = new ArrayList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(keyStroke, "- ");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String token = tokenizer.nextToken();
|
||||
if (!pieces.contains(token)) {
|
||||
pieces.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuffer keyStrokeBuff = new StringBuffer();
|
||||
for (Iterator<String> iterator = pieces.iterator(); iterator.hasNext();) {
|
||||
String piece = iterator.next();
|
||||
if (StringUtilities.indexOfIgnoreCase(piece, SHIFT) != -1) {
|
||||
keyStrokeBuff.append("shift ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, CTRL) != -1) {
|
||||
keyStrokeBuff.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, CONTROL) != -1) {
|
||||
keyStrokeBuff.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, ALT) != -1) {
|
||||
keyStrokeBuff.append("alt ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, META) != -1) {
|
||||
keyStrokeBuff.append("meta ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, PRESSED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, TYPED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (StringUtilities.indexOfIgnoreCase(piece, RELEASED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
keyStrokeBuff.append(PRESSED).append(' ');
|
||||
|
||||
// at this point we should only have left one piece--the key ID
|
||||
int leftover = pieces.size();
|
||||
if (leftover > 1 || leftover == 0) {
|
||||
Msg.warn(DockingKeyBindingAction.class, "Invalid keystroke string found. Expected " +
|
||||
"format of '[modifier] ... key'. Found: '" + keyStroke + "'");
|
||||
|
||||
if (leftover == 0) {
|
||||
return null; // nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
String key = pieces.get(0);
|
||||
keyStrokeBuff.append(key.toUpperCase());
|
||||
|
||||
return KeyStroke.getKeyStroke(keyStrokeBuff.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,5 +234,4 @@ public interface DockingTool {
|
|||
* @return true if the tool's configuration has changed
|
||||
*/
|
||||
public boolean hasConfigChanged();
|
||||
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ import javax.swing.*;
|
|||
import org.jdom.Element;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.KeyBindingsManager;
|
||||
import docking.help.HelpService;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.Platform;
|
||||
import ghidra.framework.options.PreferenceState;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
|
@ -87,7 +87,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
private Map<String, ComponentProvider> providerNameCache = new HashMap<>();
|
||||
private Map<String, PreferenceState> preferenceStateMap = new HashMap<>();
|
||||
private DockWinListener docListener;
|
||||
private ActionToGuiMapper actionManager;
|
||||
private ActionToGuiMapper actionToGuiMapper;
|
||||
|
||||
private WeakSet<DockingContextListener> contextListeners =
|
||||
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
|
||||
|
@ -138,7 +138,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
}
|
||||
|
||||
root = new RootNode(this, toolName, images, modal, factory);
|
||||
actionManager = new ActionToGuiMapper(this);
|
||||
|
||||
KeyboardFocusManager km = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
km.addPropertyChangeListener("permanentFocusOwner", this);
|
||||
|
@ -319,7 +318,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* given keystroke.
|
||||
*/
|
||||
Action getActionForKeyStroke(KeyStroke keyStroke) {
|
||||
return actionManager.getDockingKeyAction(keyStroke);
|
||||
return actionToGuiMapper.getDockingKeyAction(keyStroke);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -336,8 +335,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return placeholderManager;
|
||||
}
|
||||
|
||||
ActionToGuiMapper getActionManager() {
|
||||
return actionManager;
|
||||
ActionToGuiMapper getActionToGuiMapper() {
|
||||
return actionToGuiMapper;
|
||||
}
|
||||
|
||||
RootNode getRootNode() {
|
||||
|
@ -646,7 +645,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* @param owner the name of the owner whose associated component and actions should be removed.
|
||||
*/
|
||||
public void removeAll(String owner) {
|
||||
actionManager.removeAll(owner);
|
||||
actionToGuiMapper.removeAll(owner);
|
||||
placeholderManager.removeAll(owner);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
@ -655,6 +654,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
// 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) {
|
||||
|
@ -668,7 +671,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
||||
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
||||
if (placeholder != null) {
|
||||
actionManager.removeLocalAction(action);
|
||||
actionToGuiMapper.removeLocalAction(action);
|
||||
placeholder.removeAction(action);
|
||||
}
|
||||
}
|
||||
|
@ -679,16 +682,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
throw new IllegalArgumentException("Unknown component provider: " + provider);
|
||||
}
|
||||
placeholder.addAction(action);
|
||||
actionManager.addLocalAction(action, provider);
|
||||
actionToGuiMapper.addLocalAction(action, provider);
|
||||
}
|
||||
|
||||
void addToolAction(DockingActionIf action) {
|
||||
actionManager.addToolAction(action);
|
||||
actionToGuiMapper.addToolAction(action);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
void removeToolAction(DockingActionIf action) {
|
||||
actionManager.removeToolAction(action);
|
||||
actionToGuiMapper.removeToolAction(action);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
|
@ -712,6 +715,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
if (placeholder != null) {
|
||||
showComponent(placeholder, visibleState, true);
|
||||
}
|
||||
else {
|
||||
// a null placeholder implies the client is trying to show a provider that has not
|
||||
// been added to the tool
|
||||
Msg.warn(this, "Attempting to show an unknown Component Provider '" +
|
||||
provider.getName() + "' - " + "check that the provider has been added to the tool");
|
||||
}
|
||||
}
|
||||
|
||||
public void toFront(ComponentProvider provider) {
|
||||
|
@ -791,7 +800,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
mgr.removePropertyChangeListener("permanentFocusOwner", this);
|
||||
|
||||
actionManager.dispose();
|
||||
actionToGuiMapper.dispose();
|
||||
root.dispose();
|
||||
|
||||
placeholderManager.disposePlaceholders();
|
||||
|
@ -993,7 +1002,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
Iterator<DockingActionIf> iter = placeholder.getActions();
|
||||
while (iter.hasNext()) {
|
||||
DockingActionIf action = iter.next();
|
||||
actionManager.removeLocalAction(action);
|
||||
actionToGuiMapper.removeLocalAction(action);
|
||||
}
|
||||
|
||||
ComponentNode node = placeholder.getNode();
|
||||
|
@ -1084,7 +1093,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return;
|
||||
}
|
||||
|
||||
actionManager.removeAll(DOCKING_WINDOWS_OWNER);
|
||||
actionToGuiMapper.removeAll(DOCKING_WINDOWS_OWNER);
|
||||
|
||||
Map<String, List<ComponentPlaceholder>> permanentMap = new HashMap<>();
|
||||
Map<String, List<ComponentPlaceholder>> transientMap = new HashMap<>();
|
||||
|
@ -1111,7 +1120,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
createActions(permanentMap, false);
|
||||
createWindowActions();
|
||||
|
||||
actionManager.update();
|
||||
actionToGuiMapper.update();
|
||||
}
|
||||
|
||||
private boolean isWindowMenuShowing() {
|
||||
|
@ -1153,7 +1162,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
}
|
||||
Collections.sort(actionList);
|
||||
for (ShowComponentAction action : actionList) {
|
||||
actionManager.addToolAction(action);
|
||||
actionToGuiMapper.addToolAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1191,7 +1200,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
|
||||
Collections.sort(actions);
|
||||
for (ShowWindowAction action : actions) {
|
||||
actionManager.addToolAction(action);
|
||||
actionToGuiMapper.addToolAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1387,7 +1396,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
actionManager.setActive(active);
|
||||
actionToGuiMapper.setActive(active);
|
||||
if (active) {
|
||||
setActiveManager(this);
|
||||
if (focusedPlaceholder != null && root.getWindow(focusedPlaceholder) == window) {
|
||||
|
@ -1938,7 +1947,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* when we know that an update is not need, as we are in the middle of an update.
|
||||
*/
|
||||
void doSetMenuGroup(String[] menuPath, String group) {
|
||||
actionManager.setMenuGroup(menuPath, group);
|
||||
actionToGuiMapper.setMenuGroup(menuPath, group);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1954,7 +1963,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
* its level
|
||||
*/
|
||||
public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) {
|
||||
actionManager.setMenuGroup(menuPath, group, menuSubGroup);
|
||||
actionToGuiMapper.setMenuGroup(menuPath, group, menuSubGroup);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
|
@ -2060,7 +2069,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
|
||||
public void contextChanged(ComponentProvider provider) {
|
||||
if (provider == null) {
|
||||
actionManager.contextChangedAll(); // update all windows;
|
||||
actionToGuiMapper.contextChangedAll(); // update all windows;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2069,7 +2078,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
|||
return;
|
||||
}
|
||||
placeHolder.contextChanged();
|
||||
actionManager.contextChanged(placeHolder);
|
||||
actionToGuiMapper.contextChanged(placeHolder);
|
||||
}
|
||||
|
||||
public void addContextListener(DockingContextListener listener) {
|
||||
|
|
|
@ -17,11 +17,10 @@ package docking;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.menu.MenuGroupMap;
|
||||
import docking.menu.MenuHandler;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
||||
|
||||
|
@ -66,6 +65,17 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
|||
}
|
||||
}
|
||||
|
||||
public DockingActionIf getToolbarAction(String actionName) {
|
||||
|
||||
for (WindowActionManager actionManager : windowToActionManagerMap.values()) {
|
||||
DockingActionIf action = actionManager.getToolbarAction(actionName);
|
||||
if (action != null) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
for (WindowActionManager actionManager : windowToActionManagerMap.values()) {
|
||||
actionManager.dispose();
|
||||
|
@ -130,7 +140,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
|||
}
|
||||
|
||||
private List<DockingActionIf> getActionsForWindow(WindowNode windowNode) {
|
||||
ActionToGuiMapper actionManager = windowManager.getActionManager();
|
||||
ActionToGuiMapper actionManager = windowManager.getActionToGuiMapper();
|
||||
Collection<DockingActionIf> globalActions = actionManager.getGlobalActions();
|
||||
List<DockingActionIf> actionsForWindow = new ArrayList<>(globalActions.size());
|
||||
Set<Class<?>> contextTypes = windowNode.getContextTypes();
|
||||
|
@ -143,12 +153,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
|||
}
|
||||
|
||||
public void contextChangedAll() {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
updateAllWindowActions();
|
||||
}
|
||||
else {
|
||||
SwingUtilities.invokeLater(() -> updateAllWindowActions());
|
||||
}
|
||||
Swing.runIfSwingOrRunLater(this::updateAllWindowActions);
|
||||
}
|
||||
|
||||
private void updateAllWindowActions() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -22,6 +21,8 @@ import java.awt.event.KeyListener;
|
|||
import javax.swing.JTextField;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
/**
|
||||
* Text field captures key strokes and notifies a listener to process the key entry.
|
||||
*/
|
||||
|
@ -43,21 +44,31 @@ public class KeyEntryTextField extends JTextField {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the current key stroke.
|
||||
* Get the current key stroke
|
||||
* @return the key stroke
|
||||
*/
|
||||
public KeyStroke getCurrentKeyStroke() {
|
||||
public KeyStroke getKeyStroke() {
|
||||
return currentKeyStroke;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the toString() form of the keyStroke, e.g., Ctrl-M
|
||||
* is returned as "keyCode CtrlM-P" and we want it to look like:
|
||||
* "Ctrl-M"
|
||||
* @param ks the keystroke to parse.
|
||||
* @return the parse string for the keystroke.
|
||||
* Sets the current key stroke
|
||||
* @param ks the new key stroke
|
||||
*/
|
||||
public void setKeyStroke(KeyStroke ks) {
|
||||
processEntry(ks);
|
||||
setText(parseKeyStroke(ks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the toString() form of the keyStroke, e.g., Ctrl-M is returned as
|
||||
* "keyCode CtrlM-P" and we want it to look like: "Ctrl-M"
|
||||
*
|
||||
* @param ks the keystroke to parse
|
||||
* @return the parse string for the keystroke
|
||||
*/
|
||||
public static String parseKeyStroke(KeyStroke ks) {
|
||||
return DockingKeyBindingAction.parseKeyStroke(ks);
|
||||
return KeyBindingUtils.parseKeyStroke(ks);
|
||||
}
|
||||
|
||||
public void clearField() {
|
||||
|
@ -66,8 +77,6 @@ public class KeyEntryTextField extends JTextField {
|
|||
currentKeyStroke = null;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void processEntry(KeyStroke ks) {
|
||||
ksName = null;
|
||||
|
||||
|
@ -78,7 +87,7 @@ public class KeyEntryTextField extends JTextField {
|
|||
char keyChar = ks.getKeyChar();
|
||||
if (!Character.isWhitespace(keyChar) &&
|
||||
Character.getType(keyChar) != Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE) {
|
||||
ksName = DockingKeyBindingAction.parseKeyStroke(ks);
|
||||
ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
}
|
||||
}
|
||||
listener.processEntry(ks);
|
||||
|
@ -111,7 +120,7 @@ public class KeyEntryTextField extends JTextField {
|
|||
|
||||
KeyStroke keyStroke = null;
|
||||
if (!isClearKey(keyCode) && !isModifiersOnly(e)) {
|
||||
keyStroke = KeyStroke.getKeyStroke(keyCode, e.getModifiersEx() | e.getModifiers());
|
||||
keyStroke = KeyStroke.getKeyStroke(keyCode, e.getModifiersEx());
|
||||
}
|
||||
|
||||
processEntry(keyStroke);
|
||||
|
|
|
@ -319,7 +319,7 @@ class RootNode extends WindowNode {
|
|||
invalid = false;
|
||||
}
|
||||
|
||||
winMgr.getActionManager().update();
|
||||
winMgr.getActionToGuiMapper().update();
|
||||
windowWrapper.validate();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@ package docking;
|
|||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.action.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
@ -33,8 +32,11 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
protected static final ImageIcon EMPTY_ICON =
|
||||
ResourceManager.loadImage("images/EmptyIcon16.gif");
|
||||
protected static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME;
|
||||
private ComponentPlaceholder info;
|
||||
|
||||
protected DockingWindowManager winMgr;
|
||||
private ComponentPlaceholder info;
|
||||
private String title;
|
||||
private boolean isTransient;
|
||||
|
||||
private static String truncateTitleAsNeeded(String title) {
|
||||
if (title.length() <= MAX_LENGTH) {
|
||||
|
@ -48,14 +50,14 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
super(truncateTitleAsNeeded(name), DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ShowComponentAction object.
|
||||
* @param winMgr the DockingWindowManager that this action belongs to.
|
||||
* @param info the info of the component to be shown when this action is invoked.
|
||||
*/
|
||||
ShowComponentAction(DockingWindowManager winMgr, ComponentPlaceholder info, String subMenuName,
|
||||
boolean isTransient) {
|
||||
super(truncateTitleAsNeeded(info.getTitle()), DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
super(info.getProvider().getName(), DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
|
||||
this.info = info;
|
||||
this.winMgr = winMgr;
|
||||
this.title = truncateTitleAsNeeded(info.getTitle());
|
||||
this.isTransient = isTransient;
|
||||
String group = isTransient ? "Transient" : "Permanent";
|
||||
|
||||
Icon icon = info.getIcon();
|
||||
|
@ -69,15 +71,18 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
winMgr.doSetMenuGroup(new String[] { MENU_WINDOW, subMenuName }, group);
|
||||
}
|
||||
else {
|
||||
setMenuBarData(
|
||||
new MenuData(new String[] { MENU_WINDOW, getName() }, icon, "Permanent"));
|
||||
setMenuBarData(new MenuData(new String[] { MENU_WINDOW, title }, icon, "Permanent"));
|
||||
}
|
||||
|
||||
this.info = info;
|
||||
this.winMgr = winMgr;
|
||||
// keybinding data used to show the binding in the menu
|
||||
ComponentProvider provider = info.getProvider();
|
||||
DockingActionIf action = provider.getShowProviderAction();
|
||||
KeyBindingData kbData = action.getKeyBindingData();
|
||||
if (kbData != null) {
|
||||
setKeyBindingData(kbData);
|
||||
}
|
||||
|
||||
// Use provider Help for this action
|
||||
ComponentProvider provider = info.getProvider();
|
||||
HelpLocation helpLocation = provider.getHelpLocation();
|
||||
if (helpLocation != null) {
|
||||
setHelpLocation(helpLocation);
|
||||
|
@ -89,6 +94,21 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeyBindingManaged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesSharedKeyBinding() {
|
||||
if (isTransient) {
|
||||
return false; // temporary window
|
||||
}
|
||||
|
||||
// 'info' is null when this action is used to 'show all' instances of a given provider
|
||||
return info != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
winMgr.showComponent(info, true, true);
|
||||
|
|
|
@ -72,6 +72,10 @@ public class WindowActionManager {
|
|||
}
|
||||
}
|
||||
|
||||
public DockingActionIf getToolbarAction(String actionName) {
|
||||
return toolBarMgr.getAction(actionName);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
JMenuBar menuBar = menuBarMgr.getMenuBar();
|
||||
if (menuBar.getMenuCount() > 0) {
|
||||
|
|
|
@ -15,12 +15,10 @@
|
|||
*/
|
||||
package docking.action;
|
||||
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.KeyBindingPrecedence;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
|
||||
public class KeyBindingData {
|
||||
private KeyStroke keyStroke;
|
||||
|
@ -49,6 +47,7 @@ public class KeyBindingData {
|
|||
|
||||
/**
|
||||
* Returns an accelerator keystroke to be associated with this action.
|
||||
* @return the binding
|
||||
*/
|
||||
public KeyStroke getKeyBinding() {
|
||||
return keyStroke;
|
||||
|
@ -56,6 +55,7 @@ public class KeyBindingData {
|
|||
|
||||
/**
|
||||
* Returns the keyBindingPrecedence for this action
|
||||
* @return the precedence
|
||||
*/
|
||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||
return keyBindingPrecedence;
|
||||
|
@ -88,63 +88,8 @@ public class KeyBindingData {
|
|||
|
||||
KeyBindingPrecedence precedence = newKeyBindingData.getKeyBindingPrecedence();
|
||||
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
|
||||
return createReservedKeyBindingData(validateKeyStroke(keyBinding));
|
||||
return createReservedKeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding));
|
||||
}
|
||||
return new KeyBindingData(validateKeyStroke(keyBinding), precedence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* the <tt>control</tt> key will be converted to the <tt>command</tt> key on the Mac.
|
||||
*
|
||||
* @param keyStroke the keystroke to validate
|
||||
* @return the potentially changed keystroke
|
||||
*/
|
||||
public static KeyStroke validateKeyStroke(KeyStroke keyStroke) {
|
||||
if (keyStroke == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove system-dependent control key mask and transform deprecated modifiers
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
if ((modifiers & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_DOWN_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ ActionEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.SHIFT_MASK;
|
||||
modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.ALT_MASK;
|
||||
modifiers = modifiers | InputEvent.ALT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.META_MASK) == InputEvent.META_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.META_MASK;
|
||||
modifiers = modifiers | InputEvent.META_DOWN_MASK;
|
||||
}
|
||||
|
||||
int eventType = keyStroke.getKeyEventType();
|
||||
if (eventType == KeyEvent.KEY_TYPED) {
|
||||
// we know that typed events have a key code of VK_UNDEFINED
|
||||
return KeyStroke.getKeyStroke(keyStroke.getKeyChar(), modifiers);
|
||||
}
|
||||
|
||||
// key pressed or released
|
||||
boolean isOnKeyRelease = keyStroke.isOnKeyRelease();
|
||||
return KeyStroke.getKeyStroke(keyStroke.getKeyCode(), modifiers, isOnKeyRelease);
|
||||
return new KeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding), precedence);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,10 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking;
|
||||
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
import ghidra.util.exception.AssertException;
|
||||
package docking.action;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
@ -26,7 +22,9 @@ import java.util.*;
|
|||
import javax.swing.Action;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.*;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class KeyBindingsManager implements PropertyChangeListener {
|
||||
|
||||
|
@ -37,8 +35,8 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
|
||||
public KeyBindingsManager(DockingWindowManager winMgr) {
|
||||
this.winMgr = winMgr;
|
||||
dockingKeyMap = new HashMap<KeyStroke, DockingKeyBindingAction>();
|
||||
actionToProviderMap = new HashMap<DockingActionIf, ComponentProvider>();
|
||||
dockingKeyMap = new HashMap<>();
|
||||
actionToProviderMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void addAction(DockingActionIf action, ComponentProvider optionalProvider) {
|
||||
|
@ -59,6 +57,10 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
addReservedKeyBinding(action, keyBinding);
|
||||
}
|
||||
|
||||
public void addReservedAction(DockingActionIf action, KeyStroke ks) {
|
||||
addReservedKeyBinding(action, ks);
|
||||
}
|
||||
|
||||
public void removeAction(DockingActionIf action) {
|
||||
action.removePropertyChangeListener(this);
|
||||
actionToProviderMap.remove(action);
|
||||
|
@ -74,7 +76,8 @@ 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(winMgr, provider, action, keyStroke));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -93,6 +96,8 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
"action to a given keystroke: " + keyStroke);
|
||||
}
|
||||
|
||||
KeyBindingData binding = KeyBindingData.createReservedKeyBindingData(keyStroke);
|
||||
action.setKeyBindingData(binding);
|
||||
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(winMgr, action, keyStroke));
|
||||
}
|
||||
|
||||
|
@ -147,7 +152,7 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
|||
}
|
||||
|
||||
public List<DockingActionIf> getLocalActions() {
|
||||
return new ArrayList<DockingActionIf>(actionToProviderMap.keySet());
|
||||
return new ArrayList<>(actionToProviderMap.keySet());
|
||||
}
|
||||
|
||||
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
|
@ -21,6 +21,7 @@ import java.util.*;
|
|||
import javax.swing.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
|
@ -149,7 +150,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
|||
if (list.size() > 1) {
|
||||
// popup dialog to show multiple actions
|
||||
if (dialog == null) {
|
||||
dialog = new ActionDialog(parseKeyStroke(keyStroke), list);
|
||||
dialog = new ActionDialog(KeyBindingUtils.parseKeyStroke(keyStroke), list);
|
||||
}
|
||||
else {
|
||||
dialog.setActionList(list);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,11 +13,12 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking;
|
||||
package docking.action;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.DockingKeyBindingAction;
|
||||
import docking.DockingWindowManager;
|
||||
|
||||
class ReservedKeyBindingAction extends DockingKeyBindingAction {
|
||||
|
|
@ -13,24 +13,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.action;
|
||||
package docking.actions;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.action.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
|
||||
public class KeyBindingAction extends DockingAction {
|
||||
private final ActionToGuiMapper dockingActionManager;
|
||||
|
||||
public KeyBindingAction(ActionToGuiMapper dockingActionManager) {
|
||||
super("Set KeyBinding", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
this.dockingActionManager = dockingActionManager;
|
||||
createReservedKeyBinding(ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY);
|
||||
setEnabled(true);
|
||||
public static String NAME = "Set KeyBinding";
|
||||
private ToolActions toolActions;
|
||||
|
||||
public KeyBindingAction(ToolActions toolActions) {
|
||||
super(NAME, DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
this.toolActions = toolActions;
|
||||
|
||||
// Help actions don't have help
|
||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||
|
@ -58,7 +57,7 @@ public class KeyBindingAction extends DockingAction {
|
|||
return;
|
||||
}
|
||||
|
||||
KeyEntryDialog d = new KeyEntryDialog(action, dockingActionManager);
|
||||
KeyEntryDialog d = new KeyEntryDialog(action, toolActions);
|
||||
DockingWindowManager.showDialog(d);
|
||||
}
|
||||
|
||||
|
@ -75,9 +74,7 @@ public class KeyBindingAction extends DockingAction {
|
|||
|
||||
// It is not key binding managed, which means that it may be a shared key binding
|
||||
String actionName = dockingAction.getName();
|
||||
Set<DockingActionIf> allActions = dockingActionManager.getAllActions();
|
||||
DockingActionIf sharedAction =
|
||||
KeyBindingUtils.getSharedKeyBindingAction(allActions, actionName);
|
||||
DockingActionIf sharedAction = toolActions.getSharedStubKeyBindingAction(actionName);
|
||||
if (sharedAction != null) {
|
||||
return sharedAction;
|
||||
}
|
|
@ -15,8 +15,11 @@
|
|||
*/
|
||||
package docking.actions;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.indexOfIgnoreCase;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -31,6 +34,7 @@ import org.jdom.output.XMLOutputter;
|
|||
import com.google.common.collect.Sets;
|
||||
|
||||
import docking.DockingTool;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.*;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
|
@ -53,10 +57,20 @@ import utilities.util.reflection.ReflectionUtilities;
|
|||
public class KeyBindingUtils {
|
||||
private static final String LAST_KEY_BINDING_EXPORT_DIRECTORY = "LastKeyBindingExportDirectory";
|
||||
|
||||
private static final String RELEASED = "released";
|
||||
private static final String TYPED = "typed";
|
||||
private static final String PRESSED = "pressed";
|
||||
|
||||
private static final String SHIFT = "Shift";
|
||||
private static final String CTRL = "Ctrl";
|
||||
private static final String CONTROL = "Control";
|
||||
private static final String ALT = "Alt";
|
||||
private static final String META = "Meta";
|
||||
private static final String MODIFIER_SEPARATOR = "-";
|
||||
|
||||
private static final Logger log = LogManager.getLogger(KeyBindingUtils.class);
|
||||
|
||||
public static final String PREFERENCES_FILE_EXTENSION = ".kbxml";
|
||||
|
||||
private static final GhidraFileFilter FILE_FILTER = new GhidraFileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname, GhidraFileChooserModel model) {
|
||||
|
@ -398,36 +412,6 @@ public class KeyBindingUtils {
|
|||
return Sets.filter(ownerMatch, action -> action.getName().equals(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to locate the {@link SharedStubKeyBindingAction} representative for the given
|
||||
* action name. This method is not useful to general clients.
|
||||
*
|
||||
* @param allActions all actions in the system
|
||||
* @param sharedName the name of the shared action
|
||||
* @return the shared action representative
|
||||
*/
|
||||
public static DockingActionIf getSharedKeyBindingAction(Set<DockingActionIf> allActions,
|
||||
String sharedName) {
|
||||
|
||||
String owner = "Tool";
|
||||
for (DockingActionIf action : allActions) {
|
||||
if (!(action instanceof SharedStubKeyBindingAction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.getOwner().equals(owner) && action.getName().equals(sharedName)) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isIgnored(DockingActionIf action) {
|
||||
// not keybinding managed; a shared keybinding implies that this action should not be in
|
||||
// the UI, as there will be a single proxy in place of all actions sharing that binding
|
||||
return !action.isKeyBindingManaged() || action.usesSharedKeyBinding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the existing docking action and allows it to be registered with
|
||||
* Swing components
|
||||
|
@ -493,6 +477,252 @@ public class KeyBindingUtils {
|
|||
Msg.warn(KeyBindingUtils.class, s, ReflectionUtilities.createJavaFilteredThrowable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given data with system-independent versions of key modifiers. For example,
|
||||
* the <tt>control</tt> key will be converted to the <tt>command</tt> key on the Mac.
|
||||
*
|
||||
* @param keyStroke the keystroke to validate
|
||||
* @return the potentially changed keystroke
|
||||
*/
|
||||
// TODO ignore the deprecation, as this method is responsible for fixing deprecated usage.
|
||||
// When all actions no longer user the deprecated modifiers, the deprecated elements
|
||||
// of this method can be removed
|
||||
@SuppressWarnings("deprecation")
|
||||
public static KeyStroke validateKeyStroke(KeyStroke keyStroke) {
|
||||
if (keyStroke == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove system-dependent control key mask and transform deprecated modifiers
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
if ((modifiers & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_DOWN_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
|
||||
modifiers = modifiers ^ ActionEvent.CTRL_MASK;
|
||||
modifiers = modifiers | DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.SHIFT_MASK;
|
||||
modifiers = modifiers | InputEvent.SHIFT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.ALT_MASK;
|
||||
modifiers = modifiers | InputEvent.ALT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((modifiers & InputEvent.META_MASK) == InputEvent.META_MASK) {
|
||||
modifiers = modifiers ^ InputEvent.META_MASK;
|
||||
modifiers = modifiers | InputEvent.META_DOWN_MASK;
|
||||
}
|
||||
|
||||
int eventType = keyStroke.getKeyEventType();
|
||||
if (eventType == KeyEvent.KEY_TYPED) {
|
||||
// we know that typed events have a key code of VK_UNDEFINED
|
||||
return KeyStroke.getKeyStroke(Character.valueOf(keyStroke.getKeyChar()), modifiers);
|
||||
}
|
||||
|
||||
// key pressed or released
|
||||
boolean isOnKeyRelease = keyStroke.isOnKeyRelease();
|
||||
return KeyStroke.getKeyStroke(keyStroke.getKeyCode(), modifiers, isOnKeyRelease);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the toString() form of the keyStroke.
|
||||
* <br>In Java 1.4.2 & earlier, Ctrl-M is returned as "keyCode CtrlM-P"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
* <br>In Java 1.5.0, Ctrl-M is returned as "ctrl pressed M"
|
||||
* and we want it to look like: "Ctrl-M".
|
||||
*
|
||||
* @param keyStroke the key stroke
|
||||
* @return the string value; the empty string if the key stroke is null
|
||||
*/
|
||||
public static String parseKeyStroke(KeyStroke keyStroke) {
|
||||
|
||||
if (keyStroke == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final String keyPressSuffix = "-P";
|
||||
String keyString = keyStroke.toString();
|
||||
int type = keyStroke.getKeyEventType();
|
||||
if (type == KeyEvent.KEY_TYPED) {
|
||||
return String.valueOf(keyStroke.getKeyChar());
|
||||
}
|
||||
|
||||
// get the character used in the key stroke
|
||||
int firstIndex = keyString.lastIndexOf(' ') + 1;
|
||||
int ctrlIndex = keyString.indexOf(CTRL, firstIndex);
|
||||
if (ctrlIndex >= 0) {
|
||||
firstIndex = ctrlIndex + CTRL.length();
|
||||
}
|
||||
int altIndex = keyString.indexOf(ALT, firstIndex);
|
||||
if (altIndex >= 0) {
|
||||
firstIndex = altIndex + ALT.length();
|
||||
}
|
||||
int shiftIndex = keyString.indexOf(SHIFT, firstIndex);
|
||||
if (shiftIndex >= 0) {
|
||||
firstIndex = shiftIndex + SHIFT.length();
|
||||
}
|
||||
int metaIndex = keyString.indexOf(META, firstIndex);
|
||||
if (metaIndex >= 0) {
|
||||
firstIndex = metaIndex + META.length();
|
||||
}
|
||||
|
||||
int lastIndex = keyString.length();
|
||||
if (keyString.endsWith(keyPressSuffix)) {
|
||||
lastIndex -= keyPressSuffix.length();
|
||||
}
|
||||
if (lastIndex >= 0) {
|
||||
keyString = keyString.substring(firstIndex, lastIndex);
|
||||
}
|
||||
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
if (isShift(modifiers)) {
|
||||
buffy.insert(0, SHIFT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if (isAlt(modifiers)) {
|
||||
buffy.insert(0, ALT + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if (isControl(modifiers)) {
|
||||
buffy.insert(0, CTRL + MODIFIER_SEPARATOR);
|
||||
}
|
||||
if (isMeta(modifiers)) {
|
||||
buffy.insert(0, META + MODIFIER_SEPARATOR);
|
||||
}
|
||||
buffy.append(keyString);
|
||||
return buffy.toString();
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isShift(int mask) {
|
||||
return (mask & InputEvent.SHIFT_DOWN_MASK) != 0 || (mask & InputEvent.SHIFT_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isAlt(int mask) {
|
||||
return (mask & InputEvent.ALT_DOWN_MASK) != 0 || (mask & InputEvent.ALT_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isControl(int mask) {
|
||||
return (mask & InputEvent.CTRL_DOWN_MASK) != 0 || (mask & InputEvent.CTRL_MASK) != 0;
|
||||
}
|
||||
|
||||
// ignore the deprecated; remove when we are confident that all tool actions no longer use the
|
||||
// deprecated InputEvent mask types
|
||||
@SuppressWarnings("deprecation")
|
||||
private static boolean isMeta(int mask) {
|
||||
return (mask & InputEvent.META_DOWN_MASK) != 0 || (mask & InputEvent.META_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given text into a KeyStroke. This method relies upon
|
||||
* {@link KeyStroke#getKeyStroke(String)} for parsing. Before making that call, this method
|
||||
* will perform fixup on the given text for added flexibility. For example, the given
|
||||
* text may contain spaces or dashes as the separators between parts in the string. Also,
|
||||
* the text is converted such that it is not case-sensitive. So, the following example
|
||||
* formats are allowed:
|
||||
* <pre>
|
||||
* Alt-F
|
||||
* alt p
|
||||
* Ctrl-Alt-Z
|
||||
* ctrl Z
|
||||
* </pre>
|
||||
*
|
||||
* @param keyStroke
|
||||
* @return the new key stroke (as returned by {@link KeyStroke#getKeyStroke(String)}
|
||||
*/
|
||||
public static KeyStroke parseKeyStroke(String keyStroke) {
|
||||
List<String> pieces = new ArrayList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(keyStroke, "- ");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String token = tokenizer.nextToken();
|
||||
if (!pieces.contains(token)) {
|
||||
pieces.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
for (Iterator<String> iterator = pieces.iterator(); iterator.hasNext();) {
|
||||
String piece = iterator.next();
|
||||
if (indexOfIgnoreCase(piece, SHIFT) != -1) {
|
||||
buffy.append("shift ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, CTRL) != -1) {
|
||||
buffy.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, CONTROL) != -1) {
|
||||
buffy.append("ctrl ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, ALT) != -1) {
|
||||
buffy.append("alt ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, META) != -1) {
|
||||
buffy.append("meta ");
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, PRESSED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, TYPED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
else if (indexOfIgnoreCase(piece, RELEASED) != -1) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buffy.append(PRESSED).append(' ');
|
||||
|
||||
// at this point we should only have left one piece--the key ID
|
||||
int leftover = pieces.size();
|
||||
if (leftover > 1 || leftover == 0) {
|
||||
Msg.warn(KeyBindingUtils.class, "Invalid keystroke string found. Expected " +
|
||||
"format of '[modifier] ... key'. Found: '" + keyStroke + "'");
|
||||
|
||||
if (leftover == 0) {
|
||||
return null; // nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
String key = pieces.get(0);
|
||||
buffy.append(key.toUpperCase());
|
||||
|
||||
return KeyStroke.getKeyStroke(buffy.toString());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private static boolean isIgnored(DockingActionIf action) {
|
||||
// not keybinding managed; a shared keybinding implies that this action should not be in
|
||||
// the UI, as there will be a single proxy in place of all actions sharing that binding
|
||||
return !action.isKeyBindingManaged() || action.usesSharedKeyBinding();
|
||||
}
|
||||
|
||||
private static KeyStroke getKeyStroke(KeyBindingData data) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
|
@ -500,12 +730,7 @@ public class KeyBindingUtils {
|
|||
return data.getKeyBinding();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
// prompts the user for a file location from which to read key binding
|
||||
// data
|
||||
// prompts the user for a file location from which to read key binding data
|
||||
private static InputStream getInputStreamForFile(File startingDir) {
|
||||
File selectedFile = getFileFromUser(startingDir);
|
||||
|
||||
|
@ -587,4 +812,5 @@ public class KeyBindingUtils {
|
|||
|
||||
return selectedFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.action;
|
||||
package docking.actions;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
@ -23,7 +23,7 @@ import javax.swing.*;
|
|||
import javax.swing.text.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.action.*;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
|
@ -35,7 +35,7 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class KeyEntryDialog extends DialogComponentProvider {
|
||||
|
||||
private ActionToGuiMapper actionManager;
|
||||
private ToolActions toolActions;
|
||||
private DockingActionIf action;
|
||||
private JPanel defaultPanel;
|
||||
private KeyEntryTextField keyEntryField;
|
||||
|
@ -45,10 +45,10 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
private SimpleAttributeSet textAttrSet;
|
||||
private Color bgColor;
|
||||
|
||||
public KeyEntryDialog(DockingActionIf action, ActionToGuiMapper actionManager) {
|
||||
public KeyEntryDialog(DockingActionIf action, ToolActions actions) {
|
||||
super("Set Key Binding for " + action.getName(), true);
|
||||
this.actionManager = actionManager;
|
||||
this.action = action;
|
||||
this.toolActions = actions;
|
||||
setUpAttributes();
|
||||
createPanel();
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
|
@ -105,7 +105,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
p.add(keyEntryField);
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
if (keyBinding != null) {
|
||||
keyEntryField.setText(DockingKeyBindingAction.parseKeyStroke(keyBinding));
|
||||
keyEntryField.setText(KeyBindingUtils.parseKeyStroke(keyBinding));
|
||||
}
|
||||
setFocusComponent(keyEntryField);
|
||||
defaultPanel.add(p, BorderLayout.CENTER);
|
||||
|
@ -129,6 +129,14 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given keystroke value into the text field of this dialog
|
||||
* @param ks the keystroke to set
|
||||
*/
|
||||
public void setKeyStroke(KeyStroke ks) {
|
||||
keyEntryField.setKeyStroke(ks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
close();
|
||||
|
@ -136,23 +144,37 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
KeyStroke keyStroke = keyEntryField.getCurrentKeyStroke();
|
||||
if (keyStroke != null && ReservedKeyBindings.isReservedKeystroke(keyStroke)) {
|
||||
KeyStroke newKeyStroke = keyEntryField.getKeyStroke();
|
||||
if (newKeyStroke != null && ReservedKeyBindings.isReservedKeystroke(newKeyStroke)) {
|
||||
setStatusText(keyEntryField.getText() + " is a reserved keystroke");
|
||||
return;
|
||||
}
|
||||
|
||||
clearStatusText();
|
||||
|
||||
Set<DockingActionIf> allActions = actionManager.getAllActions();
|
||||
KeyStroke existingKeyStroke = action.getKeyBinding();
|
||||
if (Objects.equals(existingKeyStroke, newKeyStroke)) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyBindingData kbData = new KeyBindingData(newKeyStroke);
|
||||
if (action instanceof SharedStubKeyBindingAction) {
|
||||
action.setUnvalidatedKeyBindingData(kbData);
|
||||
}
|
||||
else {
|
||||
Set<DockingActionIf> allActions = toolActions.getAllActions();
|
||||
Set<DockingActionIf> actions =
|
||||
KeyBindingUtils.getActions(allActions, action.getOwner(), action.getName());
|
||||
for (DockingActionIf element : actions) {
|
||||
if (element.isKeyBindingManaged()) {
|
||||
element.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
|
||||
element.setUnvalidatedKeyBindingData(kbData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toolActions.keyBindingsChanged();
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
|
@ -178,7 +200,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
String ksName = DockingKeyBindingAction.parseKeyStroke(ks);
|
||||
String ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||
try {
|
||||
doc.insertString(0, "Actions mapped to " + ksName + "\n\n", textAttrSet);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
|
@ -218,7 +240,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private MultipleKeyAction getMultipleKeyAction(KeyStroke ks) {
|
||||
Action keyAction = actionManager.getDockingKeyAction(ks);
|
||||
Action keyAction = toolActions.getAction(ks);
|
||||
if (keyAction instanceof MultipleKeyAction) {
|
||||
return (MultipleKeyAction) keyAction;
|
||||
}
|
|
@ -20,6 +20,8 @@ import java.util.Map.Entry;
|
|||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.action.*;
|
||||
|
@ -83,6 +85,27 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
|||
updateActionKeyStrokeFromOptions(action, defaultKs);
|
||||
}
|
||||
|
||||
public String getOwnersDescription() {
|
||||
List<String> owners = getDistinctOwners();
|
||||
Collections.sort(owners);
|
||||
if (owners.size() == 1) {
|
||||
return owners.get(0);
|
||||
}
|
||||
return '(' + StringUtils.join(owners, ", ") + ')';
|
||||
}
|
||||
|
||||
private List<String> getDistinctOwners() {
|
||||
List<String> results = new ArrayList<>();
|
||||
Set<DockingActionIf> actions = clientActions.keySet();
|
||||
for (DockingActionIf action : actions) {
|
||||
String owner = action.getOwner();
|
||||
if (!results.contains(owner)) {
|
||||
results.add(owner);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private KeyStroke validateActionsHaveTheSameDefaultKeyStroke(DockingActionIf newAction) {
|
||||
|
||||
// this value may be null
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.beans.PropertyChangeEvent;
|
|||
import java.beans.PropertyChangeListener;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
@ -27,6 +28,7 @@ import docking.*;
|
|||
import docking.action.*;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.util.ReservedKeyBindings;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import util.CollectionUtils;
|
||||
|
||||
|
@ -35,7 +37,6 @@ import util.CollectionUtils;
|
|||
*/
|
||||
public class ToolActions implements PropertyChangeListener {
|
||||
|
||||
private DockingWindowManager winMgr;
|
||||
private ActionToGuiHelper actionGuiHelper;
|
||||
|
||||
/*
|
||||
|
@ -51,6 +52,7 @@ public class ToolActions implements PropertyChangeListener {
|
|||
|
||||
private ToolOptions keyBindingOptions;
|
||||
private DockingTool dockingTool;
|
||||
private KeyBindingsManager keyBindingsManager;
|
||||
|
||||
/**
|
||||
* Construct an ActionManager
|
||||
|
@ -61,9 +63,15 @@ public class ToolActions implements PropertyChangeListener {
|
|||
*/
|
||||
public ToolActions(DockingTool tool, DockingWindowManager windowManager) {
|
||||
this.dockingTool = tool;
|
||||
this.winMgr = windowManager;
|
||||
this.actionGuiHelper = new ActionToGuiHelper(winMgr);
|
||||
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
this.actionGuiHelper = new ActionToGuiHelper(windowManager);
|
||||
this.keyBindingsManager = new KeyBindingsManager(windowManager);
|
||||
this.keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
||||
KeyBindingAction keyBindingAction = new KeyBindingAction(this);
|
||||
keyBindingsManager.addReservedAction(keyBindingAction,
|
||||
ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY);
|
||||
|
||||
actionGuiHelper.setKeyBindingsManager(keyBindingsManager);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
|
@ -132,6 +140,7 @@ public class ToolActions implements PropertyChangeListener {
|
|||
|
||||
SharedStubKeyBindingAction newStub =
|
||||
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
||||
newStub.addPropertyChangeListener(this);
|
||||
keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
||||
defaultKeyStroke, null, null);
|
||||
return newStub;
|
||||
|
@ -279,12 +288,18 @@ public class ToolActions implements PropertyChangeListener {
|
|||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
||||
DockingAction action = (DockingAction) evt.getSource();
|
||||
if (!action.isKeyBindingManaged()) {
|
||||
dockingTool.setConfigChanged(true);
|
||||
if (!evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DockingAction action = (DockingAction) evt.getSource();
|
||||
if (!action.isKeyBindingManaged()) {
|
||||
// this reads unusually, but we need to notify the tool to rebuild its 'Window' menu
|
||||
// in the case that this action is one of the tool's special actions
|
||||
keyBindingsChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue();
|
||||
KeyStroke newKeyStroke = keyBindingData.getKeyBinding();
|
||||
Options opt = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
|
@ -294,12 +309,21 @@ public class ToolActions implements PropertyChangeListener {
|
|||
}
|
||||
else if (!newKeyStroke.equals(optKeyStroke)) {
|
||||
opt.setKeyStroke(action.getFullName(), newKeyStroke);
|
||||
dockingTool.setConfigChanged(true);
|
||||
}
|
||||
keyBindingsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
DockingActionIf getSharedStubKeyBindingAction(String name) {
|
||||
return sharedActionMap.get(name);
|
||||
}
|
||||
|
||||
Action getAction(KeyStroke ks) {
|
||||
return keyBindingsManager.getDockingKeyAction(ks);
|
||||
}
|
||||
|
||||
// triggered by a user-initiated action
|
||||
void keyBindingsChanged() {
|
||||
dockingTool.setConfigChanged(true);
|
||||
actionGuiHelper.keyBindingsChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -24,7 +23,7 @@ import docking.action.DockingActionIf;
|
|||
import docking.action.MenuData;
|
||||
|
||||
/**
|
||||
* Manages the main menu bar on the main frame.
|
||||
* Manages the main menu bar on the main frame
|
||||
*/
|
||||
public class MenuBarManager implements MenuGroupListener {
|
||||
|
||||
|
@ -32,36 +31,41 @@ public class MenuBarManager implements MenuGroupListener {
|
|||
private Map<String, MenuManager> menuManagers;
|
||||
private final MenuGroupMap menuGroupMap;
|
||||
|
||||
/**
|
||||
* Constructs a new MenuBarManager
|
||||
*/
|
||||
public MenuBarManager(MenuHandler actionHandler, MenuGroupMap menuGroupMap) {
|
||||
this.menuGroupMap = menuGroupMap;
|
||||
menuManagers = new TreeMap<String, MenuManager>();
|
||||
menuManagers = new TreeMap<>();
|
||||
this.menuHandler = actionHandler;
|
||||
}
|
||||
|
||||
public void clearActions() {
|
||||
menuManagers = new TreeMap<String, MenuManager>();
|
||||
menuManagers = new TreeMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action to the menu.
|
||||
* @param action the action to be added.
|
||||
* @param groupMgr the MenuGroupMap
|
||||
* Adds an action to the menu
|
||||
* @param action the action to be added
|
||||
*/
|
||||
public void addAction(DockingActionIf action) {
|
||||
MenuManager menuManager = getMenuManager(action);
|
||||
if (menuManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
menuManager.addAction(action);
|
||||
}
|
||||
|
||||
private MenuManager getMenuManager(DockingActionIf action) {
|
||||
MenuData menuBarData = action.getMenuBarData();
|
||||
if (menuBarData == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] menuPath = menuBarData.getMenuPath();
|
||||
if (menuPath == null || menuPath.length <= 1) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
MenuManager menuMgr = getMenuManager(menuPath[0]);
|
||||
menuMgr.addAction(action);
|
||||
|
||||
return getMenuManager(menuPath[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,17 +108,13 @@ public class MenuBarManager implements MenuGroupListener {
|
|||
|
||||
MenuManager mgr = menuManagers.get(menuName);
|
||||
if (mgr == null) {
|
||||
mgr =
|
||||
new MenuManager(menuName, new String[] { menuName }, mk, 1, null, false,
|
||||
mgr = new MenuManager(menuName, new String[] { menuName }, mk, 1, null, false,
|
||||
menuHandler, menuGroupMap);
|
||||
menuManagers.put(menuName, mgr);
|
||||
}
|
||||
return mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JMenuBar for all the actions.
|
||||
*/
|
||||
public JMenuBar getMenuBar() {
|
||||
MenuManager fileMenu = menuManagers.get("File");
|
||||
MenuManager editMenu = menuManagers.get("Edit");
|
||||
|
@ -153,6 +153,7 @@ public class MenuBarManager implements MenuGroupListener {
|
|||
* @param menuPath the menu path whose group changed.
|
||||
* @param group the new group for the given menuPath.
|
||||
*/
|
||||
@Override
|
||||
public void menuGroupChanged(String[] menuPath, String group) {
|
||||
if (menuPath != null && menuPath.length > 1) {
|
||||
MenuManager mgr = getMenuManager(menuPath[0]);
|
||||
|
|
|
@ -43,13 +43,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
// listeners to handle help activation
|
||||
// -this listener covers activation by keyboard and by mouse *when enabled*
|
||||
private ChangeListener buttonModelChangeListener;
|
||||
|
||||
// -this listener covers activation by mouse *when the action is disabled*
|
||||
private MouseAdapter menuHoverListener;
|
||||
|
||||
/**
|
||||
* Constructs a new MenuItemManger
|
||||
* @param dockableAction the action whose menuItem is being managed.
|
||||
*/
|
||||
MenuItemManager(MenuHandler actionHandler, DockingActionIf dockingAction,
|
||||
boolean usePopupPath) {
|
||||
this.menuHandler = actionHandler;
|
||||
|
@ -104,9 +101,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#getWindowGroup()
|
||||
*/
|
||||
@Override
|
||||
public String getGroup() {
|
||||
MenuData menuData = isPopup ? action.getPopupMenuData() : action.getMenuBarData();
|
||||
|
@ -119,10 +113,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
return menuData == null ? null : menuData.getMenuSubGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#dispose()
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (action != null) {
|
||||
|
@ -138,9 +128,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
action = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#getMenuItem()
|
||||
*/
|
||||
@Override
|
||||
public JMenuItem getMenuItem() {
|
||||
if (menuItem != null) {
|
||||
|
@ -158,17 +145,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
return menuItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner associated with this items action.
|
||||
*/
|
||||
public String getOwner() {
|
||||
return action.getOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the menuItem to reflect changes in the actions properties.
|
||||
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
|
||||
*/
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent e) {
|
||||
if (menuItem == null) {
|
||||
|
@ -209,16 +189,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action associated with this menu item.
|
||||
*/
|
||||
public DockingActionIf getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (menuHandler != null) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -30,8 +29,8 @@ import docking.action.MenuData;
|
|||
public class MenuManager implements ManagedMenuItem {
|
||||
private static String NULL_GROUP_NAME = "<null group>";
|
||||
|
||||
private Set<ManagedMenuItem> managedMenuItems = new HashSet<ManagedMenuItem>();
|
||||
private Map<String, MenuManager> subMenus = new HashMap<String, MenuManager>();
|
||||
private Set<ManagedMenuItem> managedMenuItems = new HashSet<>();
|
||||
private Map<String, MenuManager> subMenus = new HashMap<>();
|
||||
|
||||
private String name;
|
||||
private final String[] menuPath;
|
||||
|
@ -50,9 +49,9 @@ public class MenuManager implements ManagedMenuItem {
|
|||
* @param name the name of the menu.
|
||||
* @param mnemonicKey the key to use for the menu mnemonic
|
||||
* @param group the group of the menu.
|
||||
* @param showKeyBindings if true, includes the keybinding text on the menu item.
|
||||
* @param usePopupPath if true, registers actions with popup paths as popup items.
|
||||
* @param menuHandler Listener to be notified of menu behavior.
|
||||
* @param menuGroupMap maps menu groups to menu paths
|
||||
*/
|
||||
public MenuManager(String name, char mnemonicKey, String group, boolean usePopupPath,
|
||||
MenuHandler menuHandler, MenuGroupMap menuGroupMap) {
|
||||
|
@ -68,9 +67,9 @@ public class MenuManager implements ManagedMenuItem {
|
|||
* @param mnemonicKey the key to use for the menu mnemonic
|
||||
* @param level the number of parent menus that this menu is in.
|
||||
* @param group the group of this menu.
|
||||
* @param showKeyBindings if true, includes the keybinding text on the menu item.
|
||||
* @param usePopupPath if true, registers actions with popup paths as popup items.
|
||||
* @param menuHandler Listener to be notified of menu behavior.
|
||||
* @param menuGroupMap maps menu groups to menu paths
|
||||
*/
|
||||
MenuManager(String name, String[] menuPath, char mnemonicKey, int level, String group,
|
||||
boolean usePopupPath, MenuHandler menuHandler, MenuGroupMap menuGroupMap) {
|
||||
|
@ -96,10 +95,8 @@ public class MenuManager implements ManagedMenuItem {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds an action to this menu. Can create subMenus depending on the menuPath of the
|
||||
* action.
|
||||
* @param action the action to be added.
|
||||
* @param menuGroupMap group map for menuItems
|
||||
* Adds an action to this menu. Can create subMenus depending on the menuPath of the action
|
||||
* @param action the action to be added
|
||||
*/
|
||||
public void addAction(DockingActionIf action) {
|
||||
checkForSwingThread();
|
||||
|
@ -122,8 +119,7 @@ public class MenuManager implements ManagedMenuItem {
|
|||
submenuGroup = subMenuName;
|
||||
}
|
||||
|
||||
mgr =
|
||||
new MenuManager(cleanSubMenuName, submenuPath, mnemonic, submenuLevel,
|
||||
mgr = new MenuManager(cleanSubMenuName, submenuPath, mnemonic, submenuLevel,
|
||||
submenuGroup, usePopupPath, menuHandler, menuGroupMap);
|
||||
subMenus.put(cleanSubMenuName, mgr);
|
||||
managedMenuItems.add(mgr);
|
||||
|
@ -198,7 +194,7 @@ public class MenuManager implements ManagedMenuItem {
|
|||
menu.addMenuListener(menuHandler);
|
||||
}
|
||||
|
||||
List<ManagedMenuItem> list = new ArrayList<ManagedMenuItem>(managedMenuItems);
|
||||
List<ManagedMenuItem> list = new ArrayList<>(managedMenuItems);
|
||||
Collections.sort(list, comparator);
|
||||
String lastGroup = null;
|
||||
|
||||
|
@ -259,7 +255,7 @@ public class MenuManager implements ManagedMenuItem {
|
|||
if (popupMenu == null) {
|
||||
popupMenu = new JPopupMenu(name);
|
||||
|
||||
List<ManagedMenuItem> list = new ArrayList<ManagedMenuItem>(managedMenuItems);
|
||||
List<ManagedMenuItem> list = new ArrayList<>(managedMenuItems);
|
||||
Collections.sort(list, comparator);
|
||||
String lastGroup = NULL_GROUP_NAME;
|
||||
boolean hasMenuItems = false;
|
||||
|
|
|
@ -15,16 +15,17 @@
|
|||
*/
|
||||
package docking.menu;
|
||||
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
import java.awt.event.*;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
/**
|
||||
* Class to manager toolbar buttons.
|
||||
|
@ -93,7 +94,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
StringBuilder buffy = new StringBuilder(toolTipText);
|
||||
if (StringUtilities.startsWithIgnoreCase(toolTipText, "<HTML>")) {
|
||||
String endHTMLTag = "</HTML>";
|
||||
int closeTagIndex = StringUtilities.indexOfIgnoreCase(toolTipText, endHTMLTag);
|
||||
int closeTagIndex = StringUtils.indexOfIgnoreCase(toolTipText, endHTMLTag);
|
||||
if (closeTagIndex < 0) {
|
||||
// no closing tag, which is acceptable
|
||||
buffy.append(START_KEYBINDING_TEXT).append(keyBindingText).append(
|
||||
|
@ -204,9 +205,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
final ActionContext finalContext = tempContext;
|
||||
|
||||
// this gives the UI some time to repaint before executing the action
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (toolBarAction.isEnabledForContext(finalContext)) {
|
||||
if (toolBarAction instanceof ToggleDockingActionIf) {
|
||||
ToggleDockingActionIf toggleAction = (ToggleDockingActionIf) toolBarAction;
|
||||
|
@ -214,7 +213,6 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
|||
}
|
||||
toolBarAction.actionPerformed(finalContext);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import docking.widgets.VariableHeightPanel;
|
|||
*/
|
||||
public class ToolBarManager {
|
||||
private Map<String, List<ToolBarItemManager>> groupToItemsMap =
|
||||
new TreeMap<String, List<ToolBarItemManager>>(new GroupComparator());
|
||||
new TreeMap<>(new GroupComparator());
|
||||
private Comparator<? super ToolBarItemManager> toolBarItemComparator =
|
||||
new ToolBarItemManagerComparator();
|
||||
|
||||
|
@ -47,9 +47,6 @@ public class ToolBarManager {
|
|||
toolBar = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the action to the toolbar.
|
||||
*/
|
||||
public void addAction(DockingActionIf action) {
|
||||
ToolBarData toolBarData = action.getToolBarData();
|
||||
if (toolBarData == null) {
|
||||
|
@ -61,7 +58,7 @@ public class ToolBarManager {
|
|||
String group = toolBarData.getToolBarGroup();
|
||||
List<ToolBarItemManager> items = groupToItemsMap.get(group);
|
||||
if (items == null) {
|
||||
items = new ArrayList<ToolBarItemManager>();
|
||||
items = new ArrayList<>();
|
||||
groupToItemsMap.put(group, items);
|
||||
}
|
||||
items.add(new ToolBarItemManager(action, windowManager));
|
||||
|
@ -96,9 +93,6 @@ public class ToolBarManager {
|
|||
groupToItemsMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the toolbar is empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return groupToItemsMap.isEmpty();
|
||||
}
|
||||
|
|
|
@ -1114,7 +1114,8 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* A helper method to find all actions with the given owner's name
|
||||
* A helper method to find all actions with the given owner's name (this will not include
|
||||
* reserved system actions)
|
||||
*
|
||||
* @param tool the tool containing all system actions
|
||||
* @param name the owner's name to match
|
||||
|
@ -1125,7 +1126,8 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* A helper method to find all actions by name, with the given owner's name
|
||||
* A helper method to find all actions by name, with the given owner's name (this will not
|
||||
* include reserved system actions)
|
||||
*
|
||||
* @param tool the tool containing all system actions
|
||||
* @param owner the owner's name
|
||||
|
@ -1167,7 +1169,8 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
/**
|
||||
* Finds the action by the given owner name and action name.
|
||||
* If you do not know the owner name, then use
|
||||
* the call {@link #getActionsByName(DockingTool, String)} instead.
|
||||
* the call {@link #getActionsByName(DockingTool, String)} instead (this will not include
|
||||
* reserved system actions).
|
||||
*
|
||||
* <P>Note: more specific test case subclasses provide other methods for finding actions
|
||||
* when you have an owner name (which is usually the plugin name).
|
||||
|
|
|
@ -37,7 +37,7 @@ public class FakeDockingTool extends AbstractDockingTool {
|
|||
List<Image> windowIcons = ApplicationInformationDisplayFactory.getWindowIcons();
|
||||
winMgr = new DockingWindowManager("EMPTY", windowIcons, listener, false /*isModal*/,
|
||||
true /*isDockable*/, true /*hasStatus*/, null /*DropTargetFactory*/);
|
||||
actionMgr = new ToolActions(this, winMgr);
|
||||
toolActions = new ToolActions(this, winMgr);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -303,7 +303,7 @@ public class JavaSourceFile {
|
|||
if (nameAndMaybeDeclaraction.length == 2) {
|
||||
return nameAndMaybeDeclaraction[0].endsWith("Action");
|
||||
}
|
||||
return StringUtilities.containsIgnoreCase(nameAndMaybeDeclaraction[0], "action");
|
||||
return StringUtils.containsIgnoreCase(nameAndMaybeDeclaraction[0], "action");
|
||||
}
|
||||
|
||||
private JavaSourceLine findEndOfUnknownLine(int lineNumber) {
|
||||
|
|
|
@ -348,22 +348,6 @@ public class StringUtilities {
|
|||
return string.regionMatches(true, startIndex, postfix, 0, postfix.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given <tt>containingString</tt> contains the given
|
||||
* <tt>substring</tt>, ignoring case.
|
||||
*
|
||||
* @param containingString the string which may contain the prefix
|
||||
* @param substring the string for which to search within the containing string
|
||||
* @return true if the given <tt>containingString</tt> contains the given
|
||||
* <tt>substring</tt>, ignoring case.
|
||||
*/
|
||||
public static boolean containsIgnoreCase(String containingString, String substring) {
|
||||
if ((containingString == null) || (substring == null)) {
|
||||
return false;
|
||||
}
|
||||
return (indexOfIgnoreCase(containingString, substring, 0) >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all the given <tt>searches</tt> are contained in the given string.
|
||||
*
|
||||
|
@ -455,61 +439,6 @@ public class StringUtilities {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the first occurrence the given <tt>substring</tt> in the given
|
||||
* <tt>containingString</tt>, ignoring case to look for the substring.
|
||||
* <p>
|
||||
* This method is a convenience method for calling:
|
||||
* <pre>
|
||||
* <tt>indexOfIgnoreCase( containingString, substring, 0 );</tt>
|
||||
* </pre>
|
||||
* @param containingString the string which may contain the substring
|
||||
* @param substring the string for which to search within the containing string
|
||||
* @return index of substring within the given containing string
|
||||
*/
|
||||
public static int indexOfIgnoreCase(String containingString, String substring) {
|
||||
if ((containingString == null) || (substring == null)) {
|
||||
return -1;
|
||||
}
|
||||
return indexOfIgnoreCase(containingString, substring, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the first occurrence the given <tt>substring</tt> in the given
|
||||
* <tt>containingString</tt>, starting at the given <tt>index</tt>,
|
||||
* ignoring case to look for the substring.
|
||||
* <p>
|
||||
* @param containingString the string which may contain the substring
|
||||
* @param substring the string for which to search within the containing string
|
||||
* @param index the index from which to start the comparison
|
||||
* @return index of substring within the given containing string
|
||||
*/
|
||||
public static int indexOfIgnoreCase(String containingString, String substring, int index) {
|
||||
if ((containingString == null) || (substring == null)) {
|
||||
return -1;
|
||||
}
|
||||
return (containingString.toLowerCase().indexOf(substring.toLowerCase(), index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the last occurrence the given <tt>substring</tt> in the given
|
||||
* <tt>containingString</tt>, ignoring case to look for the substring.
|
||||
* <p>
|
||||
* This method is a convenience method for calling:
|
||||
* <pre>
|
||||
* <tt>lastIndexOfIgnoreCase( containingString, substring, 0 );</tt>
|
||||
* </pre>
|
||||
* @param containingString the string which may contain the substring
|
||||
* @param substring the string for which to search within the containing string
|
||||
* @return index of substring within the given containing string
|
||||
*/
|
||||
public static int lastIndexOfIgnoreCase(String containingString, String substring) {
|
||||
if ((containingString == null) || (substring == null)) {
|
||||
return -1;
|
||||
}
|
||||
return (containingString.toLowerCase().lastIndexOf(substring.toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert tabs in the given string to spaces.
|
||||
*
|
||||
|
@ -532,10 +461,8 @@ public class StringUtilities {
|
|||
char c = str.charAt(i);
|
||||
if (c == '\t') {
|
||||
int nSpaces = tabSize - (linepos % tabSize);
|
||||
String pad = padString("", ' ', nSpaces);
|
||||
|
||||
String pad = pad("", ' ', nSpaces);
|
||||
buffer.append(pad);
|
||||
|
||||
linepos += nSpaces;
|
||||
}
|
||||
else {
|
||||
|
@ -606,23 +533,6 @@ public class StringUtilities {
|
|||
return padded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads the source string to the specified length, using the filler string
|
||||
* as the pad. If length is negative, left justifies the string, appending
|
||||
* the filler; if length is positive, right justifies the source string.
|
||||
*
|
||||
* @param source the original string to pad.
|
||||
* @param filler the type of characters with which to pad
|
||||
* @param length the length of padding to add (0 results in no changes)
|
||||
* @return the padded string
|
||||
* @deprecated use {@link #pad(String, char, int)}; functionally the same, but smaller
|
||||
* and more consistent name
|
||||
*/
|
||||
@Deprecated
|
||||
public static String padString(String source, char filler, int length) {
|
||||
return pad(source, filler, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads the source string to the specified length, using the filler string
|
||||
* as the pad. If length is negative, left justifies the string, appending
|
||||
|
|
|
@ -33,6 +33,8 @@ import resources.icons.TranslateIcon;
|
|||
*/
|
||||
public class Icons {
|
||||
|
||||
public static final ImageIcon EMPTY_ICON = ResourceManager.loadImage("images/EmptyIcon16.gif");
|
||||
|
||||
public static final ImageIcon ADD_ICON = ResourceManager.loadImage("images/Plus2.png");
|
||||
|
||||
public static final ImageIcon COLLAPSE_ALL_ICON =
|
||||
|
|
|
@ -17,10 +17,8 @@ package ghidra.util;
|
|||
|
||||
import static ghidra.util.HTMLUtilities.HTML;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -53,7 +51,7 @@ public class HTMLUtilitiesTest {
|
|||
String s = "This text has<BR>an existing BR tag";
|
||||
String html = HTMLUtilities.toHTML(s);
|
||||
assertEquals(HTML + s, html);
|
||||
assertLogMessage("cannot", "wrap");
|
||||
spyLogger.assertLogMessage("cannot", "wrap");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -61,7 +59,7 @@ public class HTMLUtilitiesTest {
|
|||
String s = "This text has<BR>\nan existing BR tag and a newline";
|
||||
String html = HTMLUtilities.toHTML(s);
|
||||
assertEquals(HTML + s, html);
|
||||
assertLogMessage("cannot", "wrap");
|
||||
spyLogger.assertLogMessage("cannot", "wrap");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -140,16 +138,6 @@ public class HTMLUtilitiesTest {
|
|||
assertEquals("#FF0000", rgb);
|
||||
}
|
||||
|
||||
private void assertLogMessage(String... words) {
|
||||
for (String message : spyLogger) {
|
||||
if (StringUtilities.containsAllIgnoreCase(message, words)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Did not find log message containing all these words: " + Arrays.toString(words));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLinkPlaceholder() {
|
||||
String placeholderStr =
|
||||
|
|
|
@ -94,26 +94,6 @@ public class StringUtilitiesTest {
|
|||
assertEquals(-1, StringUtilities.indexOfWord(sentenceWithTestNotAsAWord, word));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastIndexOfIgnoresCase() {
|
||||
String bob = "bob";
|
||||
String endsWithBob = "endsWithBob";
|
||||
|
||||
assertEquals(8, StringUtilities.lastIndexOfIgnoreCase(endsWithBob, bob));
|
||||
|
||||
String endsWithBobUpperCase = "endsWithBOB";
|
||||
assertEquals(8, StringUtilities.lastIndexOfIgnoreCase(endsWithBobUpperCase, bob));
|
||||
|
||||
String startsWithBob = "bobWithTrailingText";
|
||||
assertEquals(0, StringUtilities.lastIndexOfIgnoreCase(startsWithBob, bob));
|
||||
|
||||
String justBob = "bOb";
|
||||
assertEquals(0, StringUtilities.lastIndexOfIgnoreCase(justBob, bob));
|
||||
|
||||
String manyBobs = "This is a string, bob, that has bob, many bobs...and then some text";
|
||||
assertEquals(42, StringUtilities.lastIndexOfIgnoreCase(manyBobs, bob));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAllBlank() {
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
package ghidra.framework.plugintool;
|
||||
|
||||
import static ghidra.framework.model.ToolTemplate.*;
|
||||
import static ghidra.framework.model.ToolTemplate.TOOL_INSTANCE_NAME_XML_NAME;
|
||||
import static ghidra.framework.model.ToolTemplate.TOOL_NAME_XML_NAME;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
@ -158,7 +159,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
|||
eventMgr = new EventManager(this);
|
||||
serviceMgr = new ServiceManager();
|
||||
installServices();
|
||||
actionMgr = new ToolActions(this, winMgr);
|
||||
toolActions = new ToolActions(this, winMgr);
|
||||
pluginMgr = new PluginManager(this, serviceMgr);
|
||||
dialogMgr = new DialogManager(this);
|
||||
initActions();
|
||||
|
@ -460,7 +461,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
|||
winMgr.setVisible(false);
|
||||
eventMgr.clearLastEvents();
|
||||
pluginMgr.dispose();
|
||||
actionMgr.dispose();
|
||||
toolActions.dispose();
|
||||
|
||||
if (project != null) {
|
||||
project.releaseFiles(this);
|
||||
|
@ -1300,7 +1301,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
|||
|
||||
protected void restoreOptionsFromXml(Element root) {
|
||||
optionsMgr.setConfigState(root.getChild("OPTIONS"));
|
||||
actionMgr.restoreKeyBindings();
|
||||
toolActions.restoreKeyBindings();
|
||||
setToolOptionsHelpLocation();
|
||||
}
|
||||
|
||||
|
@ -1321,7 +1322,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
|||
}
|
||||
|
||||
void removeAll(String owner) {
|
||||
actionMgr.removeToolActions(owner);
|
||||
toolActions.removeToolActions(owner);
|
||||
winMgr.removeAll(owner);
|
||||
}
|
||||
|
||||
|
@ -1496,7 +1497,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
|||
}
|
||||
|
||||
public void refreshKeybindings() {
|
||||
actionMgr.restoreKeyBindings();
|
||||
toolActions.restoreKeyBindings();
|
||||
}
|
||||
|
||||
public void setUnconfigurable() {
|
||||
|
|
|
@ -32,6 +32,7 @@ import docking.KeyEntryTextField;
|
|||
import docking.action.DockingActionIf;
|
||||
import docking.action.KeyBindingData;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.actions.SharedStubKeyBindingAction;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import docking.widgets.MultiLineLabel;
|
||||
import docking.widgets.OptionDialog;
|
||||
|
@ -429,9 +430,6 @@ public class KeyBindingsPanel extends JPanel {
|
|||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add listeners. Valid modifiers are CTRL and ALT and SHIFT.
|
||||
*/
|
||||
private void addListeners() {
|
||||
selectionModel = actionTable.getSelectionModel();
|
||||
selectionModel.addListSelectionListener(new TableSelectionListener());
|
||||
|
@ -463,11 +461,6 @@ public class KeyBindingsPanel extends JPanel {
|
|||
unappliedChanges = changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action that is selected in the table.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
private String getSelectedAction() {
|
||||
if (selectionModel.isSelectionEmpty()) {
|
||||
return null;
|
||||
|
@ -477,9 +470,6 @@ public class KeyBindingsPanel extends JPanel {
|
|||
return tableActions.get(modelRow).getFullName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the action name to the list for the given keystroke.
|
||||
*/
|
||||
private void addToKeyMap(KeyStroke ks, String actionName) {
|
||||
if (ks == null) {
|
||||
return;
|
||||
|
@ -495,9 +485,6 @@ public class KeyBindingsPanel extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given actionName from from the list for the keystroke.
|
||||
*/
|
||||
private void removeFromKeyMap(KeyStroke ks, String actionName) {
|
||||
if (ks == null) {
|
||||
return;
|
||||
|
@ -512,11 +499,7 @@ public class KeyBindingsPanel extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display actions mapped to the given keystroke name.
|
||||
* @param ksName name of Keystroke that has multiple actions mapped
|
||||
*/
|
||||
private void showActionMapped(String ksName) {
|
||||
private void showActionsMappedToKeyStroke(String ksName) {
|
||||
List<String> list = actionNamesByKeyStroke.get(ksName);
|
||||
if (list == null) {
|
||||
return;
|
||||
|
@ -538,17 +521,10 @@ public class KeyBindingsPanel extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the info panel.
|
||||
*/
|
||||
private void clearInfoPanel() {
|
||||
updateInfoPanel(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace multiline label in the info panel.
|
||||
* @param text new text to show
|
||||
*/
|
||||
private void updateInfoPanel(String text) {
|
||||
infoPanel.removeAll();
|
||||
infoPanel.repaint();
|
||||
|
@ -559,8 +535,6 @@ public class KeyBindingsPanel extends JPanel {
|
|||
validate();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void processKeyBindingsFromOptions(Options keyBindingOptions) {
|
||||
if (keyBindingOptions == null) {
|
||||
return;
|
||||
|
@ -578,7 +552,7 @@ public class KeyBindingsPanel extends JPanel {
|
|||
while (iterator.hasNext()) {
|
||||
String name = iterator.next();
|
||||
KeyStroke keyStroke = keyBindingsMap.get(name);
|
||||
keyStroke = KeyBindingData.validateKeyStroke(keyStroke);
|
||||
keyStroke = KeyBindingUtils.validateKeyStroke(keyStroke);
|
||||
|
||||
// prevent non-existing keybindings from being added to Ghidra (this can happen
|
||||
// when actions exist in the imported bindings, but have been removed from
|
||||
|
@ -619,7 +593,7 @@ public class KeyBindingsPanel extends JPanel {
|
|||
if (selectedActionName != null) {
|
||||
if (processKeyStroke(selectedActionName, ks)) {
|
||||
String keyStrokeText = KeyEntryTextField.parseKeyStroke(ks);
|
||||
showActionMapped(keyStrokeText);
|
||||
showActionsMappedToKeyStroke(keyStrokeText);
|
||||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
@ -691,7 +665,7 @@ public class KeyBindingsPanel extends JPanel {
|
|||
|
||||
if (ks != null) {
|
||||
ksName = KeyEntryTextField.parseKeyStroke(ks);
|
||||
showActionMapped(ksName);
|
||||
showActionsMappedToKeyStroke(ksName);
|
||||
}
|
||||
|
||||
ksField.setText(ksName);
|
||||
|
@ -734,11 +708,18 @@ public class KeyBindingsPanel extends JPanel {
|
|||
}
|
||||
return "";
|
||||
case PLUGIN_NAME:
|
||||
return action.getOwner();
|
||||
return getOwner(action);
|
||||
}
|
||||
return "Unknown Column!";
|
||||
}
|
||||
|
||||
private String getOwner(DockingActionIf action) {
|
||||
if (action instanceof SharedStubKeyBindingAction) {
|
||||
return ((SharedStubKeyBindingAction) action).getOwnersDescription();
|
||||
}
|
||||
return action.getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getModelData() {
|
||||
return tableActions;
|
||||
|
|
|
@ -23,9 +23,9 @@ import javax.swing.KeyStroke;
|
|||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
|
||||
import docking.DockingKeyBindingAction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.MenuData;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import ghidra.framework.plugintool.PluginConfigurationModel;
|
||||
import ghidra.framework.plugintool.util.PluginDescription;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
|
@ -201,7 +201,7 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
|||
buffer.append("<TD WIDTH=\"100\">");
|
||||
KeyStroke keyBinding = dockableAction.getKeyBinding();
|
||||
if (keyBinding != null) {
|
||||
String keyStrokeString = DockingKeyBindingAction.parseKeyStroke(keyBinding);
|
||||
String keyStrokeString = KeyBindingUtils.parseKeyStroke(keyBinding);
|
||||
insertHTMLString(keyStrokeString, locAttrSet, buffer);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -27,9 +27,11 @@ import javax.swing.border.Border;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.*;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.StatusBar;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.KeyEntryDialog;
|
||||
import docking.actions.KeyEntryDialog;
|
||||
import docking.actions.ToolActions;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.jar.ResourceFile;
|
||||
|
@ -280,12 +282,10 @@ public class ToolScreenShots extends GhidraScreenShotGenerator {
|
|||
public void testSetKeyBindings() {
|
||||
|
||||
tool = env.launchDefaultTool();
|
||||
DockingWindowManager windowManager = tool.getWindowManager();
|
||||
ActionToGuiMapper actionMgr =
|
||||
(ActionToGuiMapper) getInstanceField("actionManager", windowManager);
|
||||
ToolActions toolActions = (ToolActions) getInstanceField("toolActions", tool);
|
||||
|
||||
DockingActionIf action = getAction(tool, "FunctionPlugin", "Delete Function");
|
||||
final KeyEntryDialog keyEntryDialog = new KeyEntryDialog(action, actionMgr);
|
||||
final KeyEntryDialog keyEntryDialog = new KeyEntryDialog(action, toolActions);
|
||||
|
||||
runSwing(() -> tool.showDialog(keyEntryDialog), false);
|
||||
captureDialog();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue