mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +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 javax.swing.*;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
import docking.action.ToolBarData;
|
import docking.action.ToolBarData;
|
||||||
|
@ -34,7 +36,6 @@ import docking.widgets.filter.FilterTextField;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.StringUtilities;
|
|
||||||
import ghidra.util.task.SwingUpdateManager;
|
import ghidra.util.task.SwingUpdateManager;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
|
|
||||||
|
@ -351,7 +352,7 @@ class FilterAction extends ToggleDockingAction {
|
||||||
String curType = itr.next();
|
String curType = itr.next();
|
||||||
Boolean lEnabled = typeEnabledMap.get(curType);
|
Boolean lEnabled = typeEnabledMap.get(curType);
|
||||||
StringBuffer buildMetaCurTypeBuff = new StringBuffer(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();
|
int lastIndex = firstIndex + filteredText.length();
|
||||||
buildMetaCurTypeBuff.insert(lastIndex, "</b>");//THIS MUST ALWAYS COME BEFORE FIRST INDEX (FOR NO MATH on INDEX)
|
buildMetaCurTypeBuff.insert(lastIndex, "</b>");//THIS MUST ALWAYS COME BEFORE FIRST INDEX (FOR NO MATH on INDEX)
|
||||||
buildMetaCurTypeBuff.insert(firstIndex, "<b>");
|
buildMetaCurTypeBuff.insert(firstIndex, "<b>");
|
||||||
|
@ -409,7 +410,7 @@ class FilterAction extends ToggleDockingAction {
|
||||||
while (iteratorIndex.hasNext()) {
|
while (iteratorIndex.hasNext()) {
|
||||||
Entry<String, Boolean> entry = iteratorIndex.next();
|
Entry<String, Boolean> entry = iteratorIndex.next();
|
||||||
String checkboxName = entry.getKey();
|
String checkboxName = entry.getKey();
|
||||||
if (StringUtilities.containsIgnoreCase(checkboxName, filteredText)) {
|
if (StringUtils.containsIgnoreCase(checkboxName, filteredText)) {
|
||||||
checkboxNameList.add(checkboxName);
|
checkboxNameList.add(checkboxName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,14 @@ package ghidra.app.plugin.core.navigation.locationreferences;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.support.Highlight;
|
import docking.widgets.fieldpanel.support.Highlight;
|
||||||
import ghidra.app.util.viewer.field.*;
|
import ghidra.app.util.viewer.field.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.Composite;
|
import ghidra.program.model.data.Composite;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.StringUtilities;
|
|
||||||
import ghidra.util.datastruct.Accumulator;
|
import ghidra.util.datastruct.Accumulator;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -115,7 +116,7 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
|
||||||
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
||||||
|
|
||||||
// Not sure how to get the correct part of the text. This is a hack for now.
|
// 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) {
|
if (offset != -1) {
|
||||||
return new Highlight[] {
|
return new Highlight[] {
|
||||||
new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) };
|
new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) };
|
||||||
|
|
|
@ -28,6 +28,7 @@ import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
|
import docking.actions.KeyBindingUtils;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptUtil;
|
||||||
|
@ -92,7 +93,7 @@ class GhidraScriptActionManager {
|
||||||
action.setKeyBindingData(null);
|
action.setKeyBindingData(null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
KeyStroke stroke = DockingKeyBindingAction.parseKeyStroke(strokeStr);
|
KeyStroke stroke = KeyBindingUtils.parseKeyStroke(strokeStr);
|
||||||
if (stroke == null) {
|
if (stroke == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +138,7 @@ class GhidraScriptActionManager {
|
||||||
saveState.putString(scriptFile.getName(), "");
|
saveState.putString(scriptFile.getName(), "");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String strokeStr = DockingKeyBindingAction.parseKeyStroke(stroke);
|
String strokeStr = KeyBindingUtils.parseKeyStroke(stroke);
|
||||||
saveState.putString(scriptFile.getName(), strokeStr);
|
saveState.putString(scriptFile.getName(), strokeStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ package ghidra.app.plugin.core.script;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.DockingKeyBindingAction;
|
import docking.actions.KeyBindingUtils;
|
||||||
|
|
||||||
class KeyBindingsInfo implements Comparable<KeyBindingsInfo> {
|
class KeyBindingsInfo implements Comparable<KeyBindingsInfo> {
|
||||||
boolean hasAction;
|
boolean hasAction;
|
||||||
|
@ -31,7 +31,7 @@ class KeyBindingsInfo implements Comparable<KeyBindingsInfo> {
|
||||||
|
|
||||||
KeyBindingsInfo(boolean hasAction, KeyStroke stroke) {
|
KeyBindingsInfo(boolean hasAction, KeyStroke stroke) {
|
||||||
this.hasAction = hasAction;
|
this.hasAction = hasAction;
|
||||||
this.keystroke = stroke == null ? "" : DockingKeyBindingAction.parseKeyStroke(stroke);
|
this.keystroke = stroke == null ? "" : KeyBindingUtils.parseKeyStroke(stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,7 +28,7 @@ import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.DockingKeyBindingAction;
|
import docking.actions.KeyBindingUtils;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.util.HTMLUtilities;
|
import ghidra.util.HTMLUtilities;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
@ -318,7 +318,7 @@ public class ScriptInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keyBinding = DockingKeyBindingAction.parseKeyStroke(buildy.toString());
|
keyBinding = KeyBindingUtils.parseKeyStroke(buildy.toString());
|
||||||
if (keyBinding == null) {
|
if (keyBinding == null) {
|
||||||
// note: this message will be cleared by the parseHeader() method
|
// note: this message will be cleared by the parseHeader() method
|
||||||
keybindingErrorMessage = "Unable to parse keybinding: " + buildy;
|
keybindingErrorMessage = "Unable to parse keybinding: " + buildy;
|
||||||
|
@ -479,7 +479,7 @@ public class ScriptInfo {
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return DockingKeyBindingAction.parseKeyStroke(keyStroke);
|
return KeyBindingUtils.parseKeyStroke(keyStroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toToolTip(String string) {
|
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 org.junit.*;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
|
import docking.actions.KeyEntryDialog;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.data.DataPlugin;
|
import ghidra.app.plugin.core.data.DataPlugin;
|
||||||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||||
|
|
|
@ -33,8 +33,8 @@ import javax.swing.tree.TreePath;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.DockingKeyBindingAction;
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.actions.KeyBindingUtils;
|
||||||
import docking.options.editor.*;
|
import docking.options.editor.*;
|
||||||
import docking.widgets.MultiLineLabel;
|
import docking.widgets.MultiLineLabel;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
|
@ -712,7 +712,7 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
if (StringUtils.isBlank(keyBindingColumnValue)) {
|
if (StringUtils.isBlank(keyBindingColumnValue)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return DockingKeyBindingAction.parseKeyStroke(keyBindingColumnValue);
|
return KeyBindingUtils.parseKeyStroke(keyBindingColumnValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertOptionsKeyStroke(String actionName, String pluginName, KeyStroke value)
|
private void assertOptionsKeyStroke(String actionName, String pluginName, KeyStroke value)
|
||||||
|
|
|
@ -55,30 +55,16 @@ public class DummyTool implements Tool {
|
||||||
this.project = project;
|
this.project = project;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the type name of the tool.
|
|
||||||
* @exception PropertyVetoException thrown if a VetoableChangeListener
|
|
||||||
* rejects the change
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setToolName(String typeName) throws PropertyVetoException {
|
public void setToolName(String typeName) throws PropertyVetoException {
|
||||||
name = typeName;
|
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
|
@Override
|
||||||
public void exit() {
|
public void exit() {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.framework.model.Tool#close()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (project != null) {
|
if (project != null) {
|
||||||
|
@ -92,37 +78,21 @@ public class DummyTool implements Tool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Associates a unique(within a session) name to a tool instance.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void putInstanceName(String newInstanceName) {
|
public void putInstanceName(String newInstanceName) {
|
||||||
this.instanceName = newInstanceName;
|
this.instanceName = newInstanceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name associated with the tool's type.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getToolName() {
|
public String getToolName() {
|
||||||
return name;
|
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
|
@Override
|
||||||
public void setVisible(boolean visibility) {
|
public void setVisible(boolean visibility) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Tool#isVisible()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVisible() {
|
public boolean isVisible() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -133,238 +103,129 @@ public class DummyTool implements Tool {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* returns a combination of the type name and the instance name of the
|
|
||||||
* form typename(instancename)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name + instanceName;
|
return name + instanceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of eventNames that this Tool is interested in.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getConsumedToolEventNames() {
|
public String[] getConsumedToolEventNames() {
|
||||||
return new String[] { "DummyToolEvent" };
|
return new String[] { "DummyToolEvent" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the tool's unique name.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getInstanceName() {
|
public String getInstanceName() {
|
||||||
return instanceName;
|
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) {
|
public void addToolListener(ToolListener listener, String toolEvent) {
|
||||||
//do nothing
|
//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
|
@Override
|
||||||
public void addToolListener(ToolListener listener) {
|
public void addToolListener(ToolListener listener) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the names of all the possible ToolEvents that this
|
|
||||||
* tool might generate. Used by the ConnectionManager to connect
|
|
||||||
* tools together.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getToolEventNames() {
|
public String[] getToolEventNames() {
|
||||||
return new String[] { "DummyToolEvent" };
|
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) {
|
public void removeToolListener(ToolListener listener, String toolEvent) {
|
||||||
//do nothing
|
//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
|
@Override
|
||||||
public void removeToolListener(ToolListener listener) {
|
public void removeToolListener(ToolListener listener) {
|
||||||
//do nothing
|
//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
|
@Override
|
||||||
public boolean hasConfigChanged() {
|
public boolean hasConfigChanged() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a change listener that is notified when a tool changes its state.
|
|
||||||
*/
|
|
||||||
public void addChangeListener(ChangeListener l) {
|
public void addChangeListener(ChangeListener l) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add property change listener.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void addPropertyChangeListener(PropertyChangeListener l) {
|
public void addPropertyChangeListener(PropertyChangeListener l) {
|
||||||
//do nothing
|
//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
|
@Override
|
||||||
public Class<?>[] getSupportedDataTypes() {
|
public Class<?>[] getSupportedDataTypes() {
|
||||||
return new Class[] { Program.class };
|
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
|
@Override
|
||||||
public boolean acceptDomainFiles(DomainFile[] data) {
|
public boolean acceptDomainFiles(DomainFile[] data) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the domain files that this tool currently has open.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public DomainFile[] getDomainFiles() {
|
public DomainFile[] getDomainFiles() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the change listener.
|
|
||||||
*/
|
|
||||||
public void removeChangeListener(ChangeListener l) {
|
public void removeChangeListener(ChangeListener l) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells tool to write its config state from the given output stream.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setConfigChanged(boolean changed) {
|
public void setConfigChanged(boolean changed) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells tool to write its config state from the given output stream.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Element saveToXml(boolean includeConfigState) {
|
public Element saveToXml(boolean includeConfigState) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells tool to write its config state from the given output stream.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Element saveDataStateToXml(boolean isTransactionState) {
|
public Element saveDataStateToXml(boolean isTransactionState) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells tool to write its config state from the given output stream.
|
|
||||||
*/
|
|
||||||
public void restoreFromXml(Element root) {
|
public void restoreFromXml(Element root) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells tool to write its config state from the given output stream.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreDataStateFromXml(Element root) {
|
public void restoreDataStateFromXml(Element root) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove property change listener.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void removePropertyChangeListener(PropertyChangeListener l) {
|
public void removePropertyChangeListener(PropertyChangeListener l) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is invoked when the registered ToolEvent event occurs.
|
|
||||||
*
|
|
||||||
* @param toolEvent The ToolEvent.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void processToolEvent(PluginEvent toolEvent) {
|
public void processToolEvent(PluginEvent toolEvent) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fire the plugin event by notifying the event manager which
|
|
||||||
* calls the listeners.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void firePluginEvent(PluginEvent event) {
|
public void firePluginEvent(PluginEvent event) {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the description of the tool.
|
|
||||||
*/
|
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the description of the tool.
|
|
||||||
*/
|
|
||||||
public void setDescription(String description) {
|
public void setDescription(String description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the icon for this tool configuration.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setIconURL(ToolIconURL iconURL) {
|
public void setIconURL(ToolIconURL iconURL) {
|
||||||
this.iconURL = iconURL;
|
this.iconURL = iconURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the icon for this tool configuration.
|
|
||||||
*
|
|
||||||
* @return Icon
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public ToolIconURL getIconURL() {
|
public ToolIconURL getIconURL() {
|
||||||
return iconURL;
|
return iconURL;
|
||||||
|
@ -380,9 +241,6 @@ public class DummyTool implements Tool {
|
||||||
return getToolTemplate(true);
|
return getToolTemplate(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Tool#enableClose()
|
|
||||||
*/
|
|
||||||
public void enableClose() {
|
public void enableClose() {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ import java.util.regex.*;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.widgets.SearchLocation;
|
import docking.widgets.SearchLocation;
|
||||||
import docking.widgets.fieldpanel.Layout;
|
import docking.widgets.fieldpanel.Layout;
|
||||||
import docking.widgets.fieldpanel.LayoutModel;
|
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.listing.Program;
|
||||||
import ghidra.program.model.pcode.HighFunction;
|
import ghidra.program.model.pcode.HighFunction;
|
||||||
import ghidra.util.Msg;
|
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 -> {
|
java.util.function.Function<String, SearchMatch> function = textLine -> {
|
||||||
|
|
||||||
int index = StringUtilities.indexOfIgnoreCase(textLine, searchString);
|
int index = StringUtils.indexOfIgnoreCase(textLine, searchString);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
return SearchMatch.NO_MATCH;
|
return SearchMatch.NO_MATCH;
|
||||||
}
|
}
|
||||||
|
@ -534,7 +535,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||||
|
|
||||||
java.util.function.Function<String, SearchMatch> function = textLine -> {
|
java.util.function.Function<String, SearchMatch> function = textLine -> {
|
||||||
|
|
||||||
int index = StringUtilities.lastIndexOfIgnoreCase(textLine, searchString);
|
int index = StringUtils.lastIndexOfIgnoreCase(textLine, searchString);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
return SearchMatch.NO_MATCH;
|
return SearchMatch.NO_MATCH;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import javax.swing.JFrame;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.actions.ToolActions;
|
import docking.actions.ToolActions;
|
||||||
import ghidra.framework.options.ToolOptions;
|
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
|
* 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 {
|
public abstract class AbstractDockingTool implements DockingTool {
|
||||||
|
|
||||||
protected DockingWindowManager winMgr;
|
protected DockingWindowManager winMgr;
|
||||||
protected ToolActions actionMgr;
|
protected ToolActions toolActions;
|
||||||
protected Map<String, ToolOptions> optionsMap = new HashMap<>();
|
protected Map<String, ToolOptions> optionsMap = new HashMap<>();
|
||||||
protected boolean configChangedFlag;
|
protected boolean configChangedFlag;
|
||||||
|
|
||||||
|
@ -57,17 +57,20 @@ public abstract class AbstractDockingTool implements DockingTool {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addComponentProvider(ComponentProvider provider, boolean show) {
|
public void addComponentProvider(ComponentProvider provider, boolean show) {
|
||||||
Runnable r = () -> winMgr.addComponent(provider, show);
|
Runnable r = () -> {
|
||||||
SystemUtilities.runSwingNow(r);
|
winMgr.addComponent(provider, show);
|
||||||
|
toolActions.addToolAction(provider.getShowProviderAction());
|
||||||
|
};
|
||||||
|
Swing.runNow(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeComponentProvider(ComponentProvider provider) {
|
public void removeComponentProvider(ComponentProvider provider) {
|
||||||
Runnable r = () -> {
|
Runnable r = () -> {
|
||||||
actionMgr.removeComponentActions(provider);
|
toolActions.removeComponentActions(provider);
|
||||||
winMgr.removeComponent(provider);
|
winMgr.removeComponent(provider);
|
||||||
};
|
};
|
||||||
SystemUtilities.runSwingNow(r);
|
Swing.runNow(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,41 +99,41 @@ public abstract class AbstractDockingTool implements DockingTool {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAction(DockingActionIf action) {
|
public void addAction(DockingActionIf action) {
|
||||||
actionMgr.addToolAction(action);
|
toolActions.addToolAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAction(DockingActionIf action) {
|
public void removeAction(DockingActionIf action) {
|
||||||
actionMgr.removeToolAction(action);
|
toolActions.removeToolAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
|
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||||
actionMgr.addLocalAction(provider, action);
|
toolActions.addLocalAction(provider, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
|
public void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
|
||||||
actionMgr.removeProviderAction(provider, action);
|
toolActions.removeProviderAction(provider, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DockingActionIf> getAllActions() {
|
public Set<DockingActionIf> getAllActions() {
|
||||||
Set<DockingActionIf> actions = actionMgr.getAllActions();
|
Set<DockingActionIf> actions = toolActions.getAllActions();
|
||||||
ActionToGuiMapper am = winMgr.getActionManager();
|
ActionToGuiMapper am = winMgr.getActionToGuiMapper();
|
||||||
actions.addAll(am.getAllActions());
|
actions.addAll(am.getAllActions());
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner) {
|
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner) {
|
||||||
return actionMgr.getActions(owner);
|
return toolActions.getActions(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showComponentProvider(ComponentProvider provider, boolean visible) {
|
public void showComponentProvider(ComponentProvider provider, boolean visible) {
|
||||||
Runnable r = () -> winMgr.showComponent(provider, visible);
|
Runnable r = () -> winMgr.showComponent(provider, visible);
|
||||||
SystemUtilities.runSwingNow(r);
|
Swing.runNow(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -150,7 +153,7 @@ public abstract class AbstractDockingTool implements DockingTool {
|
||||||
@Override
|
@Override
|
||||||
public void toFront(ComponentProvider provider) {
|
public void toFront(ComponentProvider provider) {
|
||||||
Runnable r = () -> winMgr.toFront(provider);
|
Runnable r = () -> winMgr.toFront(provider);
|
||||||
SystemUtilities.runSwingNow(r);
|
Swing.runNow(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,6 +18,7 @@ package docking;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.action.KeyBindingsManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that exists primarily to provide access to action-related package-level methods of the
|
* 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) {
|
public void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
||||||
windowManager.removeProviderAction(provider, 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 {
|
public class ActionToGuiMapper {
|
||||||
|
|
||||||
private HashSet<DockingActionIf> globalActions = new LinkedHashSet<>();
|
private static boolean enableDiagnosticActions;
|
||||||
|
|
||||||
|
private Set<DockingActionIf> globalActions = new LinkedHashSet<>();
|
||||||
|
|
||||||
private MenuHandler menuBarMenuHandler;
|
private MenuHandler menuBarMenuHandler;
|
||||||
private MenuGroupMap menuGroupMap;
|
private MenuGroupMap menuGroupMap;
|
||||||
|
|
||||||
private static boolean enableDiagnosticActions;
|
|
||||||
|
|
||||||
private KeyBindingsManager keyBindingsManager;
|
private KeyBindingsManager keyBindingsManager;
|
||||||
|
|
||||||
private GlobalMenuAndToolBarManager menuAndToolBarManager;
|
private GlobalMenuAndToolBarManager menuAndToolBarManager;
|
||||||
|
|
||||||
private PopupActionManager popupActionManager;
|
private PopupActionManager popupActionManager;
|
||||||
private DockingAction keyBindingsAction;
|
|
||||||
|
|
||||||
ActionToGuiMapper(DockingWindowManager winMgr) {
|
ActionToGuiMapper(DockingWindowManager winMgr, KeyBindingsManager keyBindingsManager) {
|
||||||
|
this.keyBindingsManager = keyBindingsManager;
|
||||||
menuGroupMap = new MenuGroupMap();
|
menuGroupMap = new MenuGroupMap();
|
||||||
|
|
||||||
menuBarMenuHandler = new MenuBarMenuHandler(winMgr);
|
menuBarMenuHandler = new MenuBarMenuHandler(winMgr);
|
||||||
|
|
||||||
keyBindingsManager = new KeyBindingsManager(winMgr);
|
|
||||||
menuAndToolBarManager =
|
menuAndToolBarManager =
|
||||||
new GlobalMenuAndToolBarManager(winMgr, menuBarMenuHandler, menuGroupMap);
|
new GlobalMenuAndToolBarManager(winMgr, menuBarMenuHandler, menuGroupMap);
|
||||||
popupActionManager = new PopupActionManager(winMgr, menuGroupMap);
|
popupActionManager = new PopupActionManager(winMgr, menuGroupMap);
|
||||||
|
@ -60,12 +55,10 @@ public class ActionToGuiMapper {
|
||||||
private void initializeHelpActions() {
|
private void initializeHelpActions() {
|
||||||
DockingWindowsContextSensitiveHelpListener.install();
|
DockingWindowsContextSensitiveHelpListener.install();
|
||||||
|
|
||||||
keyBindingsAction = new KeyBindingAction(this);
|
|
||||||
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY1));
|
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY1));
|
||||||
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY2));
|
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY2));
|
||||||
keyBindingsManager.addReservedAction(
|
keyBindingsManager.addReservedAction(
|
||||||
new HelpAction(true, ReservedKeyBindings.HELP_INFO_KEY));
|
new HelpAction(true, ReservedKeyBindings.HELP_INFO_KEY));
|
||||||
keyBindingsManager.addReservedAction(keyBindingsAction);
|
|
||||||
|
|
||||||
if (enableDiagnosticActions) {
|
if (enableDiagnosticActions) {
|
||||||
keyBindingsManager.addReservedAction(new ShowFocusInfoAction());
|
keyBindingsManager.addReservedAction(new ShowFocusInfoAction());
|
||||||
|
@ -157,10 +150,6 @@ public class ActionToGuiMapper {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
|
||||||
return keyBindingsManager.getDockingKeyAction(keyStroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<DockingActionIf> getGlobalActions() {
|
Set<DockingActionIf> getGlobalActions() {
|
||||||
return globalActions;
|
return globalActions;
|
||||||
}
|
}
|
||||||
|
@ -227,4 +216,8 @@ public class ActionToGuiMapper {
|
||||||
public void showPopupMenu(ComponentPlaceholder componentInfo, MouseEvent e) {
|
public void showPopupMenu(ComponentPlaceholder componentInfo, MouseEvent e) {
|
||||||
popupActionManager.popupMenu(componentInfo, 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.action.*;
|
||||||
import docking.help.HelpDescriptor;
|
import docking.help.HelpDescriptor;
|
||||||
import docking.help.HelpService;
|
import docking.help.HelpService;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.*;
|
||||||
import ghidra.util.UniversalIdGenerator;
|
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
import utilities.util.reflection.ReflectionUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for creating dockable GUI components within a tool.
|
* 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 #componentActivated()} and {@link #componentDeactived()}
|
||||||
* <li>{@link #componentHidden()} and {@link #componentShown()}
|
* <li>{@link #componentHidden()} and {@link #componentShown()}
|
||||||
* </ul>
|
* </ul>
|
||||||
|
*
|
||||||
* <p>
|
* <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.
|
* without having to understand that they must first add themselves to the WindowManager.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class ComponentProvider implements HelpDescriptor, ActionContextProvider {
|
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";
|
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
|
// 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> oldOwnerMap = new HashMap<>();
|
||||||
private static Map<String, String> oldNameMap = 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 title;
|
||||||
private String subTitle;
|
private String subTitle;
|
||||||
private String tabText;
|
private String tabText;
|
||||||
private Icon icon;
|
|
||||||
private Set<DockingActionIf> actionSet = new LinkedHashSet<>();
|
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 boolean isTransient;
|
||||||
private HelpLocation helpLocation;
|
private HelpLocation helpLocation;
|
||||||
|
|
||||||
|
private Icon icon;
|
||||||
|
private String windowMenuGroup;
|
||||||
private String group = DEFAULT_WINDOW_GROUP;
|
private String group = DEFAULT_WINDOW_GROUP;
|
||||||
private WindowPosition defaultWindowPosition = WindowPosition.WINDOW;
|
private WindowPosition defaultWindowPosition = WindowPosition.WINDOW;
|
||||||
private WindowPosition defaultIntraGroupPosition = WindowPosition.STACK;
|
private WindowPosition defaultIntraGroupPosition = WindowPosition.STACK;
|
||||||
|
private DockingAction showProviderAction;
|
||||||
|
|
||||||
private final Class<?> contextType;
|
private final Class<?> contextType;
|
||||||
|
|
||||||
private long instanceID = UniversalIdGenerator.nextID().getValue();
|
private long instanceID = UniversalIdGenerator.nextID().getValue();
|
||||||
|
@ -118,6 +137,32 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||||
this.contextType = contextType;
|
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
|
* Returns the component to be displayed
|
||||||
* @return 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.
|
* @param icon the icon to use for this provider.
|
||||||
*/
|
*/
|
||||||
public void setIcon(Icon icon) {
|
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.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()) {
|
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);
|
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
|
* @return the menu group for this provider or null if this provider should appear in the
|
||||||
* top-level menu.
|
* top-level menu.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public String getWindowSubMenuName() {
|
public String getWindowSubMenuName() {
|
||||||
return windowMenuGroup;
|
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
|
* Returns true if this component goes away during a user session (most providers remain in
|
||||||
* the tool all session long, visible or not)
|
* the tool all session long, visible or not)
|
||||||
* @return true if transitent
|
* @return true if transient
|
||||||
*/
|
*/
|
||||||
public boolean isTransient() {
|
public boolean isTransient() {
|
||||||
return isTransient;
|
return isTransient;
|
||||||
|
@ -485,6 +576,15 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||||
*/
|
*/
|
||||||
protected void setTransient() {
|
protected void setTransient() {
|
||||||
isTransient = true;
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionToGuiMapper actionManager = dwm.getActionManager();
|
ActionToGuiMapper actionManager = dwm.getActionToGuiMapper();
|
||||||
MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap();
|
MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap();
|
||||||
MenuManager menuMgr =
|
MenuManager menuMgr =
|
||||||
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
|
new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap);
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
|
||||||
this.componentInfo = placeholder;
|
this.componentInfo = placeholder;
|
||||||
|
|
||||||
winMgr = placeholder.getNode().winMgr;
|
winMgr = placeholder.getNode().winMgr;
|
||||||
actionMgr = winMgr.getActionManager();
|
actionMgr = winMgr.getActionToGuiMapper();
|
||||||
|
|
||||||
popupListener = new MouseAdapter() {
|
popupListener = new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -60,7 +60,7 @@ class DockableToolBarManager {
|
||||||
ComponentPlaceholder placeholder = dockableComp.getComponentWindowingPlaceholder();
|
ComponentPlaceholder placeholder = dockableComp.getComponentWindowingPlaceholder();
|
||||||
DockingWindowManager winMgr =
|
DockingWindowManager winMgr =
|
||||||
dockableComp.getComponentWindowingPlaceholder().getNode().winMgr;
|
dockableComp.getComponentWindowingPlaceholder().getNode().winMgr;
|
||||||
ActionToGuiMapper actionManager = winMgr.getActionManager();
|
ActionToGuiMapper actionManager = winMgr.getActionToGuiMapper();
|
||||||
menuGroupMap = actionManager.getMenuGroupMap();
|
menuGroupMap = actionManager.getMenuGroupMap();
|
||||||
|
|
||||||
MenuHandler menuHandler = actionManager.getMenuHandler();
|
MenuHandler menuHandler = actionManager.getMenuHandler();
|
||||||
|
|
|
@ -15,15 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package docking;
|
package docking;
|
||||||
|
|
||||||
import java.awt.event.*;
|
import java.awt.event.ActionEvent;
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import ghidra.util.Msg;
|
import docking.actions.KeyBindingUtils;
|
||||||
import ghidra.util.StringUtilities;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that can be used as an interface for using actions associated with keybindings. This
|
* 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 {
|
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;
|
private DockingActionIf docakbleAction;
|
||||||
|
|
||||||
protected KeyStroke keyStroke;
|
protected KeyStroke keyStroke;
|
||||||
|
@ -49,7 +36,7 @@ public class DockingKeyBindingAction extends AbstractAction {
|
||||||
|
|
||||||
public DockingKeyBindingAction(DockingWindowManager winMgr, DockingActionIf action,
|
public DockingKeyBindingAction(DockingWindowManager winMgr, DockingActionIf action,
|
||||||
KeyStroke keyStroke) {
|
KeyStroke keyStroke) {
|
||||||
super(parseKeyStroke(keyStroke));
|
super(KeyBindingUtils.parseKeyStroke(keyStroke));
|
||||||
this.winMgr = winMgr;
|
this.winMgr = winMgr;
|
||||||
this.docakbleAction = action;
|
this.docakbleAction = action;
|
||||||
this.keyStroke = keyStroke;
|
this.keyStroke = keyStroke;
|
||||||
|
@ -94,147 +81,4 @@ public class DockingKeyBindingAction extends AbstractAction {
|
||||||
|
|
||||||
return new ActionContext(localProvider, null);
|
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
|
* @return true if the tool's configuration has changed
|
||||||
*/
|
*/
|
||||||
public boolean hasConfigChanged();
|
public boolean hasConfigChanged();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,13 @@ import javax.swing.*;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.action.KeyBindingsManager;
|
||||||
import docking.help.HelpService;
|
import docking.help.HelpService;
|
||||||
import generic.util.WindowUtilities;
|
import generic.util.WindowUtilities;
|
||||||
import ghidra.framework.OperatingSystem;
|
import ghidra.framework.OperatingSystem;
|
||||||
import ghidra.framework.Platform;
|
import ghidra.framework.Platform;
|
||||||
import ghidra.framework.options.PreferenceState;
|
import ghidra.framework.options.PreferenceState;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.*;
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
import ghidra.util.datastruct.*;
|
import ghidra.util.datastruct.*;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.task.SwingUpdateManager;
|
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, ComponentProvider> providerNameCache = new HashMap<>();
|
||||||
private Map<String, PreferenceState> preferenceStateMap = new HashMap<>();
|
private Map<String, PreferenceState> preferenceStateMap = new HashMap<>();
|
||||||
private DockWinListener docListener;
|
private DockWinListener docListener;
|
||||||
private ActionToGuiMapper actionManager;
|
private ActionToGuiMapper actionToGuiMapper;
|
||||||
|
|
||||||
private WeakSet<DockingContextListener> contextListeners =
|
private WeakSet<DockingContextListener> contextListeners =
|
||||||
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
|
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
|
||||||
|
@ -138,7 +138,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
root = new RootNode(this, toolName, images, modal, factory);
|
root = new RootNode(this, toolName, images, modal, factory);
|
||||||
actionManager = new ActionToGuiMapper(this);
|
|
||||||
|
|
||||||
KeyboardFocusManager km = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
KeyboardFocusManager km = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
km.addPropertyChangeListener("permanentFocusOwner", this);
|
km.addPropertyChangeListener("permanentFocusOwner", this);
|
||||||
|
@ -319,7 +318,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
* given keystroke.
|
* given keystroke.
|
||||||
*/
|
*/
|
||||||
Action getActionForKeyStroke(KeyStroke 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;
|
return placeholderManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionToGuiMapper getActionManager() {
|
ActionToGuiMapper getActionToGuiMapper() {
|
||||||
return actionManager;
|
return actionToGuiMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
RootNode getRootNode() {
|
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.
|
* @param owner the name of the owner whose associated component and actions should be removed.
|
||||||
*/
|
*/
|
||||||
public void removeAll(String owner) {
|
public void removeAll(String owner) {
|
||||||
actionManager.removeAll(owner);
|
actionToGuiMapper.removeAll(owner);
|
||||||
placeholderManager.removeAll(owner);
|
placeholderManager.removeAll(owner);
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
@ -655,6 +654,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
// Package-level Action Methods
|
// Package-level Action Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
|
void setKeyBindingsManager(KeyBindingsManager keyBindingsManager) {
|
||||||
|
actionToGuiMapper = new ActionToGuiMapper(this, keyBindingsManager);
|
||||||
|
}
|
||||||
|
|
||||||
Iterator<DockingActionIf> getComponentActions(ComponentProvider provider) {
|
Iterator<DockingActionIf> getComponentActions(ComponentProvider provider) {
|
||||||
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
||||||
if (placeholder != null) {
|
if (placeholder != null) {
|
||||||
|
@ -668,7 +671,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
void removeProviderAction(ComponentProvider provider, DockingActionIf action) {
|
||||||
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
|
||||||
if (placeholder != null) {
|
if (placeholder != null) {
|
||||||
actionManager.removeLocalAction(action);
|
actionToGuiMapper.removeLocalAction(action);
|
||||||
placeholder.removeAction(action);
|
placeholder.removeAction(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -679,16 +682,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
throw new IllegalArgumentException("Unknown component provider: " + provider);
|
throw new IllegalArgumentException("Unknown component provider: " + provider);
|
||||||
}
|
}
|
||||||
placeholder.addAction(action);
|
placeholder.addAction(action);
|
||||||
actionManager.addLocalAction(action, provider);
|
actionToGuiMapper.addLocalAction(action, provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addToolAction(DockingActionIf action) {
|
void addToolAction(DockingActionIf action) {
|
||||||
actionManager.addToolAction(action);
|
actionToGuiMapper.addToolAction(action);
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeToolAction(DockingActionIf action) {
|
void removeToolAction(DockingActionIf action) {
|
||||||
actionManager.removeToolAction(action);
|
actionToGuiMapper.removeToolAction(action);
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,6 +715,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
if (placeholder != null) {
|
if (placeholder != null) {
|
||||||
showComponent(placeholder, visibleState, true);
|
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) {
|
public void toFront(ComponentProvider provider) {
|
||||||
|
@ -791,7 +800,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
mgr.removePropertyChangeListener("permanentFocusOwner", this);
|
mgr.removePropertyChangeListener("permanentFocusOwner", this);
|
||||||
|
|
||||||
actionManager.dispose();
|
actionToGuiMapper.dispose();
|
||||||
root.dispose();
|
root.dispose();
|
||||||
|
|
||||||
placeholderManager.disposePlaceholders();
|
placeholderManager.disposePlaceholders();
|
||||||
|
@ -993,7 +1002,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
Iterator<DockingActionIf> iter = placeholder.getActions();
|
Iterator<DockingActionIf> iter = placeholder.getActions();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
DockingActionIf action = iter.next();
|
DockingActionIf action = iter.next();
|
||||||
actionManager.removeLocalAction(action);
|
actionToGuiMapper.removeLocalAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentNode node = placeholder.getNode();
|
ComponentNode node = placeholder.getNode();
|
||||||
|
@ -1084,7 +1093,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
actionManager.removeAll(DOCKING_WINDOWS_OWNER);
|
actionToGuiMapper.removeAll(DOCKING_WINDOWS_OWNER);
|
||||||
|
|
||||||
Map<String, List<ComponentPlaceholder>> permanentMap = new HashMap<>();
|
Map<String, List<ComponentPlaceholder>> permanentMap = new HashMap<>();
|
||||||
Map<String, List<ComponentPlaceholder>> transientMap = new HashMap<>();
|
Map<String, List<ComponentPlaceholder>> transientMap = new HashMap<>();
|
||||||
|
@ -1111,7 +1120,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
createActions(permanentMap, false);
|
createActions(permanentMap, false);
|
||||||
createWindowActions();
|
createWindowActions();
|
||||||
|
|
||||||
actionManager.update();
|
actionToGuiMapper.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWindowMenuShowing() {
|
private boolean isWindowMenuShowing() {
|
||||||
|
@ -1153,7 +1162,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
}
|
}
|
||||||
Collections.sort(actionList);
|
Collections.sort(actionList);
|
||||||
for (ShowComponentAction action : actionList) {
|
for (ShowComponentAction action : actionList) {
|
||||||
actionManager.addToolAction(action);
|
actionToGuiMapper.addToolAction(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1191,7 +1200,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
|
|
||||||
Collections.sort(actions);
|
Collections.sort(actions);
|
||||||
for (ShowWindowAction action : actions) {
|
for (ShowWindowAction action : actions) {
|
||||||
actionManager.addToolAction(action);
|
actionToGuiMapper.addToolAction(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1387,7 +1396,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
actionManager.setActive(active);
|
actionToGuiMapper.setActive(active);
|
||||||
if (active) {
|
if (active) {
|
||||||
setActiveManager(this);
|
setActiveManager(this);
|
||||||
if (focusedPlaceholder != null && root.getWindow(focusedPlaceholder) == window) {
|
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.
|
* when we know that an update is not need, as we are in the middle of an update.
|
||||||
*/
|
*/
|
||||||
void doSetMenuGroup(String[] menuPath, String group) {
|
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
|
* its level
|
||||||
*/
|
*/
|
||||||
public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) {
|
public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) {
|
||||||
actionManager.setMenuGroup(menuPath, group, menuSubGroup);
|
actionToGuiMapper.setMenuGroup(menuPath, group, menuSubGroup);
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2060,7 +2069,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
|
|
||||||
public void contextChanged(ComponentProvider provider) {
|
public void contextChanged(ComponentProvider provider) {
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
actionManager.contextChangedAll(); // update all windows;
|
actionToGuiMapper.contextChangedAll(); // update all windows;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2069,7 +2078,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
placeHolder.contextChanged();
|
placeHolder.contextChanged();
|
||||||
actionManager.contextChanged(placeHolder);
|
actionToGuiMapper.contextChanged(placeHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addContextListener(DockingContextListener listener) {
|
public void addContextListener(DockingContextListener listener) {
|
||||||
|
|
|
@ -17,11 +17,10 @@ package docking;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.menu.MenuGroupMap;
|
import docking.menu.MenuGroupMap;
|
||||||
import docking.menu.MenuHandler;
|
import docking.menu.MenuHandler;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
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() {
|
public void dispose() {
|
||||||
for (WindowActionManager actionManager : windowToActionManagerMap.values()) {
|
for (WindowActionManager actionManager : windowToActionManagerMap.values()) {
|
||||||
actionManager.dispose();
|
actionManager.dispose();
|
||||||
|
@ -130,7 +140,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DockingActionIf> getActionsForWindow(WindowNode windowNode) {
|
private List<DockingActionIf> getActionsForWindow(WindowNode windowNode) {
|
||||||
ActionToGuiMapper actionManager = windowManager.getActionManager();
|
ActionToGuiMapper actionManager = windowManager.getActionToGuiMapper();
|
||||||
Collection<DockingActionIf> globalActions = actionManager.getGlobalActions();
|
Collection<DockingActionIf> globalActions = actionManager.getGlobalActions();
|
||||||
List<DockingActionIf> actionsForWindow = new ArrayList<>(globalActions.size());
|
List<DockingActionIf> actionsForWindow = new ArrayList<>(globalActions.size());
|
||||||
Set<Class<?>> contextTypes = windowNode.getContextTypes();
|
Set<Class<?>> contextTypes = windowNode.getContextTypes();
|
||||||
|
@ -143,12 +153,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void contextChangedAll() {
|
public void contextChangedAll() {
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
Swing.runIfSwingOrRunLater(this::updateAllWindowActions);
|
||||||
updateAllWindowActions();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SwingUtilities.invokeLater(() -> updateAllWindowActions());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAllWindowActions() {
|
private void updateAllWindowActions() {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.JTextField;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import docking.actions.KeyBindingUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text field captures key strokes and notifies a listener to process the key entry.
|
* 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;
|
return currentKeyStroke;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the toString() form of the keyStroke, e.g., Ctrl-M
|
* Sets the current key stroke
|
||||||
* is returned as "keyCode CtrlM-P" and we want it to look like:
|
* @param ks the new key stroke
|
||||||
* "Ctrl-M"
|
*/
|
||||||
* @param ks the keystroke to parse.
|
public void setKeyStroke(KeyStroke ks) {
|
||||||
* @return the parse string for the keystroke.
|
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) {
|
public static String parseKeyStroke(KeyStroke ks) {
|
||||||
return DockingKeyBindingAction.parseKeyStroke(ks);
|
return KeyBindingUtils.parseKeyStroke(ks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearField() {
|
public void clearField() {
|
||||||
|
@ -66,8 +77,6 @@ public class KeyEntryTextField extends JTextField {
|
||||||
currentKeyStroke = null;
|
currentKeyStroke = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private void processEntry(KeyStroke ks) {
|
private void processEntry(KeyStroke ks) {
|
||||||
ksName = null;
|
ksName = null;
|
||||||
|
|
||||||
|
@ -78,7 +87,7 @@ public class KeyEntryTextField extends JTextField {
|
||||||
char keyChar = ks.getKeyChar();
|
char keyChar = ks.getKeyChar();
|
||||||
if (!Character.isWhitespace(keyChar) &&
|
if (!Character.isWhitespace(keyChar) &&
|
||||||
Character.getType(keyChar) != Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE) {
|
Character.getType(keyChar) != Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE) {
|
||||||
ksName = DockingKeyBindingAction.parseKeyStroke(ks);
|
ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.processEntry(ks);
|
listener.processEntry(ks);
|
||||||
|
@ -111,7 +120,7 @@ public class KeyEntryTextField extends JTextField {
|
||||||
|
|
||||||
KeyStroke keyStroke = null;
|
KeyStroke keyStroke = null;
|
||||||
if (!isClearKey(keyCode) && !isModifiersOnly(e)) {
|
if (!isClearKey(keyCode) && !isModifiersOnly(e)) {
|
||||||
keyStroke = KeyStroke.getKeyStroke(keyCode, e.getModifiersEx() | e.getModifiers());
|
keyStroke = KeyStroke.getKeyStroke(keyCode, e.getModifiersEx());
|
||||||
}
|
}
|
||||||
|
|
||||||
processEntry(keyStroke);
|
processEntry(keyStroke);
|
||||||
|
|
|
@ -319,7 +319,7 @@ class RootNode extends WindowNode {
|
||||||
invalid = false;
|
invalid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
winMgr.getActionManager().update();
|
winMgr.getActionToGuiMapper().update();
|
||||||
windowWrapper.validate();
|
windowWrapper.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,7 @@ package docking;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import docking.action.DockingAction;
|
import docking.action.*;
|
||||||
import docking.action.MenuData;
|
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
|
@ -33,8 +32,11 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
||||||
protected static final ImageIcon EMPTY_ICON =
|
protected static final ImageIcon EMPTY_ICON =
|
||||||
ResourceManager.loadImage("images/EmptyIcon16.gif");
|
ResourceManager.loadImage("images/EmptyIcon16.gif");
|
||||||
protected static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME;
|
protected static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME;
|
||||||
private ComponentPlaceholder info;
|
|
||||||
protected DockingWindowManager winMgr;
|
protected DockingWindowManager winMgr;
|
||||||
|
private ComponentPlaceholder info;
|
||||||
|
private String title;
|
||||||
|
private boolean isTransient;
|
||||||
|
|
||||||
private static String truncateTitleAsNeeded(String title) {
|
private static String truncateTitleAsNeeded(String title) {
|
||||||
if (title.length() <= MAX_LENGTH) {
|
if (title.length() <= MAX_LENGTH) {
|
||||||
|
@ -48,14 +50,14 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
||||||
super(truncateTitleAsNeeded(name), DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
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,
|
ShowComponentAction(DockingWindowManager winMgr, ComponentPlaceholder info, String subMenuName,
|
||||||
boolean isTransient) {
|
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";
|
String group = isTransient ? "Transient" : "Permanent";
|
||||||
|
|
||||||
Icon icon = info.getIcon();
|
Icon icon = info.getIcon();
|
||||||
|
@ -69,15 +71,18 @@ class ShowComponentAction extends DockingAction implements Comparable<ShowCompon
|
||||||
winMgr.doSetMenuGroup(new String[] { MENU_WINDOW, subMenuName }, group);
|
winMgr.doSetMenuGroup(new String[] { MENU_WINDOW, subMenuName }, group);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setMenuBarData(
|
setMenuBarData(new MenuData(new String[] { MENU_WINDOW, title }, icon, "Permanent"));
|
||||||
new MenuData(new String[] { MENU_WINDOW, getName() }, icon, "Permanent"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.info = info;
|
// keybinding data used to show the binding in the menu
|
||||||
this.winMgr = winMgr;
|
ComponentProvider provider = info.getProvider();
|
||||||
|
DockingActionIf action = provider.getShowProviderAction();
|
||||||
|
KeyBindingData kbData = action.getKeyBindingData();
|
||||||
|
if (kbData != null) {
|
||||||
|
setKeyBindingData(kbData);
|
||||||
|
}
|
||||||
|
|
||||||
// Use provider Help for this action
|
// Use provider Help for this action
|
||||||
ComponentProvider provider = info.getProvider();
|
|
||||||
HelpLocation helpLocation = provider.getHelpLocation();
|
HelpLocation helpLocation = provider.getHelpLocation();
|
||||||
if (helpLocation != null) {
|
if (helpLocation != null) {
|
||||||
setHelpLocation(helpLocation);
|
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
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
winMgr.showComponent(info, true, true);
|
winMgr.showComponent(info, true, true);
|
||||||
|
|
|
@ -72,6 +72,10 @@ public class WindowActionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DockingActionIf getToolbarAction(String actionName) {
|
||||||
|
return toolBarMgr.getAction(actionName);
|
||||||
|
}
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
JMenuBar menuBar = menuBarMgr.getMenuBar();
|
JMenuBar menuBar = menuBarMgr.getMenuBar();
|
||||||
if (menuBar.getMenuCount() > 0) {
|
if (menuBar.getMenuCount() > 0) {
|
||||||
|
|
|
@ -15,12 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package docking.action;
|
package docking.action;
|
||||||
|
|
||||||
import java.awt.event.*;
|
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.DockingUtils;
|
|
||||||
import docking.KeyBindingPrecedence;
|
import docking.KeyBindingPrecedence;
|
||||||
|
import docking.actions.KeyBindingUtils;
|
||||||
|
|
||||||
public class KeyBindingData {
|
public class KeyBindingData {
|
||||||
private KeyStroke keyStroke;
|
private KeyStroke keyStroke;
|
||||||
|
@ -49,6 +47,7 @@ public class KeyBindingData {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an accelerator keystroke to be associated with this action.
|
* Returns an accelerator keystroke to be associated with this action.
|
||||||
|
* @return the binding
|
||||||
*/
|
*/
|
||||||
public KeyStroke getKeyBinding() {
|
public KeyStroke getKeyBinding() {
|
||||||
return keyStroke;
|
return keyStroke;
|
||||||
|
@ -56,6 +55,7 @@ public class KeyBindingData {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the keyBindingPrecedence for this action
|
* Returns the keyBindingPrecedence for this action
|
||||||
|
* @return the precedence
|
||||||
*/
|
*/
|
||||||
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
public KeyBindingPrecedence getKeyBindingPrecedence() {
|
||||||
return keyBindingPrecedence;
|
return keyBindingPrecedence;
|
||||||
|
@ -88,63 +88,8 @@ public class KeyBindingData {
|
||||||
|
|
||||||
KeyBindingPrecedence precedence = newKeyBindingData.getKeyBindingPrecedence();
|
KeyBindingPrecedence precedence = newKeyBindingData.getKeyBindingPrecedence();
|
||||||
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
|
if (precedence == KeyBindingPrecedence.ReservedActionsLevel) {
|
||||||
return createReservedKeyBindingData(validateKeyStroke(keyBinding));
|
return createReservedKeyBindingData(KeyBindingUtils.validateKeyStroke(keyBinding));
|
||||||
}
|
}
|
||||||
return new KeyBindingData(validateKeyStroke(keyBinding), precedence);
|
return new KeyBindingData(KeyBindingUtils.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package docking;
|
package docking.action;
|
||||||
|
|
||||||
import ghidra.util.ReservedKeyBindings;
|
|
||||||
import ghidra.util.exception.AssertException;
|
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
@ -26,7 +22,9 @@ import java.util.*;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.action.*;
|
import docking.*;
|
||||||
|
import ghidra.util.ReservedKeyBindings;
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
public class KeyBindingsManager implements PropertyChangeListener {
|
public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
|
|
||||||
|
@ -37,8 +35,8 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
|
|
||||||
public KeyBindingsManager(DockingWindowManager winMgr) {
|
public KeyBindingsManager(DockingWindowManager winMgr) {
|
||||||
this.winMgr = winMgr;
|
this.winMgr = winMgr;
|
||||||
dockingKeyMap = new HashMap<KeyStroke, DockingKeyBindingAction>();
|
dockingKeyMap = new HashMap<>();
|
||||||
actionToProviderMap = new HashMap<DockingActionIf, ComponentProvider>();
|
actionToProviderMap = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAction(DockingActionIf action, ComponentProvider optionalProvider) {
|
public void addAction(DockingActionIf action, ComponentProvider optionalProvider) {
|
||||||
|
@ -59,6 +57,10 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
addReservedKeyBinding(action, keyBinding);
|
addReservedKeyBinding(action, keyBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addReservedAction(DockingActionIf action, KeyStroke ks) {
|
||||||
|
addReservedKeyBinding(action, ks);
|
||||||
|
}
|
||||||
|
|
||||||
public void removeAction(DockingActionIf action) {
|
public void removeAction(DockingActionIf action) {
|
||||||
action.removePropertyChangeListener(this);
|
action.removePropertyChangeListener(this);
|
||||||
actionToProviderMap.remove(action);
|
actionToProviderMap.remove(action);
|
||||||
|
@ -74,7 +76,8 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
|
|
||||||
DockingKeyBindingAction existingAction = dockingKeyMap.get(keyStroke);
|
DockingKeyBindingAction existingAction = dockingKeyMap.get(keyStroke);
|
||||||
if (existingAction == null) {
|
if (existingAction == null) {
|
||||||
dockingKeyMap.put(keyStroke, new MultipleKeyAction(winMgr, provider, action, keyStroke));
|
dockingKeyMap.put(keyStroke,
|
||||||
|
new MultipleKeyAction(winMgr, provider, action, keyStroke));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +96,8 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
"action to a given keystroke: " + keyStroke);
|
"action to a given keystroke: " + keyStroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyBindingData binding = KeyBindingData.createReservedKeyBindingData(keyStroke);
|
||||||
|
action.setKeyBindingData(binding);
|
||||||
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(winMgr, action, keyStroke));
|
dockingKeyMap.put(keyStroke, new ReservedKeyBindingAction(winMgr, action, keyStroke));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +152,7 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DockingActionIf> getLocalActions() {
|
public List<DockingActionIf> getLocalActions() {
|
||||||
return new ArrayList<DockingActionIf>(actionToProviderMap.keySet());
|
return new ArrayList<>(actionToProviderMap.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
public Action getDockingKeyAction(KeyStroke keyStroke) {
|
|
@ -21,6 +21,7 @@ import java.util.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
|
import docking.actions.KeyBindingUtils;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,7 +150,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||||
if (list.size() > 1) {
|
if (list.size() > 1) {
|
||||||
// popup dialog to show multiple actions
|
// popup dialog to show multiple actions
|
||||||
if (dialog == null) {
|
if (dialog == null) {
|
||||||
dialog = new ActionDialog(parseKeyStroke(keyStroke), list);
|
dialog = new ActionDialog(KeyBindingUtils.parseKeyStroke(keyStroke), list);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dialog.setActionList(list);
|
dialog.setActionList(list);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package docking;
|
package docking.action;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.DockingKeyBindingAction;
|
||||||
|
import docking.DockingWindowManager;
|
||||||
|
|
||||||
class ReservedKeyBindingAction extends DockingKeyBindingAction {
|
class ReservedKeyBindingAction extends DockingKeyBindingAction {
|
||||||
|
|
|
@ -13,24 +13,23 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package docking.action;
|
package docking.actions;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import docking.*;
|
import docking.ActionContext;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.DockingWindowManager;
|
||||||
|
import docking.action.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.ReservedKeyBindings;
|
|
||||||
|
|
||||||
public class KeyBindingAction extends DockingAction {
|
public class KeyBindingAction extends DockingAction {
|
||||||
private final ActionToGuiMapper dockingActionManager;
|
|
||||||
|
|
||||||
public KeyBindingAction(ActionToGuiMapper dockingActionManager) {
|
public static String NAME = "Set KeyBinding";
|
||||||
super("Set KeyBinding", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
private ToolActions toolActions;
|
||||||
this.dockingActionManager = dockingActionManager;
|
|
||||||
createReservedKeyBinding(ReservedKeyBindings.UPDATE_KEY_BINDINGS_KEY);
|
public KeyBindingAction(ToolActions toolActions) {
|
||||||
setEnabled(true);
|
super(NAME, DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||||
|
this.toolActions = toolActions;
|
||||||
|
|
||||||
// Help actions don't have help
|
// Help actions don't have help
|
||||||
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
DockingWindowManager.getHelpService().excludeFromHelp(this);
|
||||||
|
@ -58,7 +57,7 @@ public class KeyBindingAction extends DockingAction {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEntryDialog d = new KeyEntryDialog(action, dockingActionManager);
|
KeyEntryDialog d = new KeyEntryDialog(action, toolActions);
|
||||||
DockingWindowManager.showDialog(d);
|
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
|
// It is not key binding managed, which means that it may be a shared key binding
|
||||||
String actionName = dockingAction.getName();
|
String actionName = dockingAction.getName();
|
||||||
Set<DockingActionIf> allActions = dockingActionManager.getAllActions();
|
DockingActionIf sharedAction = toolActions.getSharedStubKeyBindingAction(actionName);
|
||||||
DockingActionIf sharedAction =
|
|
||||||
KeyBindingUtils.getSharedKeyBindingAction(allActions, actionName);
|
|
||||||
if (sharedAction != null) {
|
if (sharedAction != null) {
|
||||||
return sharedAction;
|
return sharedAction;
|
||||||
}
|
}
|
|
@ -15,8 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package docking.actions;
|
package docking.actions;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.indexOfIgnoreCase;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.KeyboardFocusManager;
|
import java.awt.KeyboardFocusManager;
|
||||||
|
import java.awt.event.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -31,6 +34,7 @@ import org.jdom.output.XMLOutputter;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import docking.DockingTool;
|
import docking.DockingTool;
|
||||||
|
import docking.DockingUtils;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
|
@ -53,10 +57,20 @@ import utilities.util.reflection.ReflectionUtilities;
|
||||||
public class KeyBindingUtils {
|
public class KeyBindingUtils {
|
||||||
private static final String LAST_KEY_BINDING_EXPORT_DIRECTORY = "LastKeyBindingExportDirectory";
|
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);
|
private static final Logger log = LogManager.getLogger(KeyBindingUtils.class);
|
||||||
|
|
||||||
public static final String PREFERENCES_FILE_EXTENSION = ".kbxml";
|
public static final String PREFERENCES_FILE_EXTENSION = ".kbxml";
|
||||||
|
|
||||||
private static final GhidraFileFilter FILE_FILTER = new GhidraFileFilter() {
|
private static final GhidraFileFilter FILE_FILTER = new GhidraFileFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File pathname, GhidraFileChooserModel model) {
|
public boolean accept(File pathname, GhidraFileChooserModel model) {
|
||||||
|
@ -398,36 +412,6 @@ public class KeyBindingUtils {
|
||||||
return Sets.filter(ownerMatch, action -> action.getName().equals(name));
|
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
|
* Takes the existing docking action and allows it to be registered with
|
||||||
* Swing components
|
* Swing components
|
||||||
|
@ -493,6 +477,252 @@ public class KeyBindingUtils {
|
||||||
Msg.warn(KeyBindingUtils.class, s, ReflectionUtilities.createJavaFilteredThrowable());
|
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) {
|
private static KeyStroke getKeyStroke(KeyBindingData data) {
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -500,12 +730,7 @@ public class KeyBindingUtils {
|
||||||
return data.getKeyBinding();
|
return data.getKeyBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
// prompts the user for a file location from which to read key binding data
|
||||||
// Private Methods
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
// prompts the user for a file location from which to read key binding
|
|
||||||
// data
|
|
||||||
private static InputStream getInputStreamForFile(File startingDir) {
|
private static InputStream getInputStreamForFile(File startingDir) {
|
||||||
File selectedFile = getFileFromUser(startingDir);
|
File selectedFile = getFileFromUser(startingDir);
|
||||||
|
|
||||||
|
@ -587,4 +812,5 @@ public class KeyBindingUtils {
|
||||||
|
|
||||||
return selectedFile;
|
return selectedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package docking.action;
|
package docking.actions;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -23,7 +23,7 @@ import javax.swing.*;
|
||||||
import javax.swing.text.*;
|
import javax.swing.text.*;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.action.*;
|
||||||
import docking.widgets.label.GIconLabel;
|
import docking.widgets.label.GIconLabel;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.ReservedKeyBindings;
|
import ghidra.util.ReservedKeyBindings;
|
||||||
|
@ -35,7 +35,7 @@ import resources.ResourceManager;
|
||||||
*/
|
*/
|
||||||
public class KeyEntryDialog extends DialogComponentProvider {
|
public class KeyEntryDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
private ActionToGuiMapper actionManager;
|
private ToolActions toolActions;
|
||||||
private DockingActionIf action;
|
private DockingActionIf action;
|
||||||
private JPanel defaultPanel;
|
private JPanel defaultPanel;
|
||||||
private KeyEntryTextField keyEntryField;
|
private KeyEntryTextField keyEntryField;
|
||||||
|
@ -45,10 +45,10 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
||||||
private SimpleAttributeSet textAttrSet;
|
private SimpleAttributeSet textAttrSet;
|
||||||
private Color bgColor;
|
private Color bgColor;
|
||||||
|
|
||||||
public KeyEntryDialog(DockingActionIf action, ActionToGuiMapper actionManager) {
|
public KeyEntryDialog(DockingActionIf action, ToolActions actions) {
|
||||||
super("Set Key Binding for " + action.getName(), true);
|
super("Set Key Binding for " + action.getName(), true);
|
||||||
this.actionManager = actionManager;
|
|
||||||
this.action = action;
|
this.action = action;
|
||||||
|
this.toolActions = actions;
|
||||||
setUpAttributes();
|
setUpAttributes();
|
||||||
createPanel();
|
createPanel();
|
||||||
KeyStroke keyBinding = action.getKeyBinding();
|
KeyStroke keyBinding = action.getKeyBinding();
|
||||||
|
@ -105,7 +105,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
||||||
p.add(keyEntryField);
|
p.add(keyEntryField);
|
||||||
KeyStroke keyBinding = action.getKeyBinding();
|
KeyStroke keyBinding = action.getKeyBinding();
|
||||||
if (keyBinding != null) {
|
if (keyBinding != null) {
|
||||||
keyEntryField.setText(DockingKeyBindingAction.parseKeyStroke(keyBinding));
|
keyEntryField.setText(KeyBindingUtils.parseKeyStroke(keyBinding));
|
||||||
}
|
}
|
||||||
setFocusComponent(keyEntryField);
|
setFocusComponent(keyEntryField);
|
||||||
defaultPanel.add(p, BorderLayout.CENTER);
|
defaultPanel.add(p, BorderLayout.CENTER);
|
||||||
|
@ -129,6 +129,14 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
||||||
return p;
|
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
|
@Override
|
||||||
protected void cancelCallback() {
|
protected void cancelCallback() {
|
||||||
close();
|
close();
|
||||||
|
@ -136,23 +144,37 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void okCallback() {
|
protected void okCallback() {
|
||||||
KeyStroke keyStroke = keyEntryField.getCurrentKeyStroke();
|
KeyStroke newKeyStroke = keyEntryField.getKeyStroke();
|
||||||
if (keyStroke != null && ReservedKeyBindings.isReservedKeystroke(keyStroke)) {
|
if (newKeyStroke != null && ReservedKeyBindings.isReservedKeystroke(newKeyStroke)) {
|
||||||
setStatusText(keyEntryField.getText() + " is a reserved keystroke");
|
setStatusText(keyEntryField.getText() + " is a reserved keystroke");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearStatusText();
|
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 =
|
Set<DockingActionIf> actions =
|
||||||
KeyBindingUtils.getActions(allActions, action.getOwner(), action.getName());
|
KeyBindingUtils.getActions(allActions, action.getOwner(), action.getName());
|
||||||
for (DockingActionIf element : actions) {
|
for (DockingActionIf element : actions) {
|
||||||
if (element.isKeyBindingManaged()) {
|
if (element.isKeyBindingManaged()) {
|
||||||
element.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
|
element.setUnvalidatedKeyBindingData(kbData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
toolActions.keyBindingsChanged();
|
||||||
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +200,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String ksName = DockingKeyBindingAction.parseKeyStroke(ks);
|
String ksName = KeyBindingUtils.parseKeyStroke(ks);
|
||||||
try {
|
try {
|
||||||
doc.insertString(0, "Actions mapped to " + ksName + "\n\n", textAttrSet);
|
doc.insertString(0, "Actions mapped to " + ksName + "\n\n", textAttrSet);
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
@ -218,7 +240,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MultipleKeyAction getMultipleKeyAction(KeyStroke ks) {
|
private MultipleKeyAction getMultipleKeyAction(KeyStroke ks) {
|
||||||
Action keyAction = actionManager.getDockingKeyAction(ks);
|
Action keyAction = toolActions.getAction(ks);
|
||||||
if (keyAction instanceof MultipleKeyAction) {
|
if (keyAction instanceof MultipleKeyAction) {
|
||||||
return (MultipleKeyAction) keyAction;
|
return (MultipleKeyAction) keyAction;
|
||||||
}
|
}
|
|
@ -20,6 +20,8 @@ import java.util.Map.Entry;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
|
@ -83,6 +85,27 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
||||||
updateActionKeyStrokeFromOptions(action, defaultKs);
|
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) {
|
private KeyStroke validateActionsHaveTheSameDefaultKeyStroke(DockingActionIf newAction) {
|
||||||
|
|
||||||
// this value may be null
|
// this value may be null
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.swing.Action;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import org.apache.commons.collections4.map.LazyMap;
|
import org.apache.commons.collections4.map.LazyMap;
|
||||||
|
@ -27,6 +28,7 @@ import docking.*;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.tool.util.DockingToolConstants;
|
import docking.tool.util.DockingToolConstants;
|
||||||
import ghidra.framework.options.*;
|
import ghidra.framework.options.*;
|
||||||
|
import ghidra.util.ReservedKeyBindings;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import util.CollectionUtils;
|
import util.CollectionUtils;
|
||||||
|
|
||||||
|
@ -35,7 +37,6 @@ import util.CollectionUtils;
|
||||||
*/
|
*/
|
||||||
public class ToolActions implements PropertyChangeListener {
|
public class ToolActions implements PropertyChangeListener {
|
||||||
|
|
||||||
private DockingWindowManager winMgr;
|
|
||||||
private ActionToGuiHelper actionGuiHelper;
|
private ActionToGuiHelper actionGuiHelper;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -51,6 +52,7 @@ public class ToolActions implements PropertyChangeListener {
|
||||||
|
|
||||||
private ToolOptions keyBindingOptions;
|
private ToolOptions keyBindingOptions;
|
||||||
private DockingTool dockingTool;
|
private DockingTool dockingTool;
|
||||||
|
private KeyBindingsManager keyBindingsManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an ActionManager
|
* Construct an ActionManager
|
||||||
|
@ -61,9 +63,15 @@ public class ToolActions implements PropertyChangeListener {
|
||||||
*/
|
*/
|
||||||
public ToolActions(DockingTool tool, DockingWindowManager windowManager) {
|
public ToolActions(DockingTool tool, DockingWindowManager windowManager) {
|
||||||
this.dockingTool = tool;
|
this.dockingTool = tool;
|
||||||
this.winMgr = windowManager;
|
this.actionGuiHelper = new ActionToGuiHelper(windowManager);
|
||||||
this.actionGuiHelper = new ActionToGuiHelper(winMgr);
|
this.keyBindingsManager = new KeyBindingsManager(windowManager);
|
||||||
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
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() {
|
public void dispose() {
|
||||||
|
@ -132,6 +140,7 @@ public class ToolActions implements PropertyChangeListener {
|
||||||
|
|
||||||
SharedStubKeyBindingAction newStub =
|
SharedStubKeyBindingAction newStub =
|
||||||
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
new SharedStubKeyBindingAction(name, keyBindingOptions);
|
||||||
|
newStub.addPropertyChangeListener(this);
|
||||||
keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
keyBindingOptions.registerOption(newStub.getFullName(), OptionType.KEYSTROKE_TYPE,
|
||||||
defaultKeyStroke, null, null);
|
defaultKeyStroke, null, null);
|
||||||
return newStub;
|
return newStub;
|
||||||
|
@ -279,12 +288,18 @@ public class ToolActions implements PropertyChangeListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
if (evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
if (!evt.getPropertyName().equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
||||||
DockingAction action = (DockingAction) evt.getSource();
|
|
||||||
if (!action.isKeyBindingManaged()) {
|
|
||||||
dockingTool.setConfigChanged(true);
|
|
||||||
return;
|
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();
|
KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue();
|
||||||
KeyStroke newKeyStroke = keyBindingData.getKeyBinding();
|
KeyStroke newKeyStroke = keyBindingData.getKeyBinding();
|
||||||
Options opt = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
Options opt = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||||
|
@ -294,12 +309,21 @@ public class ToolActions implements PropertyChangeListener {
|
||||||
}
|
}
|
||||||
else if (!newKeyStroke.equals(optKeyStroke)) {
|
else if (!newKeyStroke.equals(optKeyStroke)) {
|
||||||
opt.setKeyStroke(action.getFullName(), newKeyStroke);
|
opt.setKeyStroke(action.getFullName(), newKeyStroke);
|
||||||
dockingTool.setConfigChanged(true);
|
keyBindingsChanged();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DockingActionIf getSharedStubKeyBindingAction(String name) {
|
DockingActionIf getSharedStubKeyBindingAction(String name) {
|
||||||
return sharedActionMap.get(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
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
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 {
|
public class MenuBarManager implements MenuGroupListener {
|
||||||
|
|
||||||
|
@ -32,36 +31,41 @@ public class MenuBarManager implements MenuGroupListener {
|
||||||
private Map<String, MenuManager> menuManagers;
|
private Map<String, MenuManager> menuManagers;
|
||||||
private final MenuGroupMap menuGroupMap;
|
private final MenuGroupMap menuGroupMap;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new MenuBarManager
|
|
||||||
*/
|
|
||||||
public MenuBarManager(MenuHandler actionHandler, MenuGroupMap menuGroupMap) {
|
public MenuBarManager(MenuHandler actionHandler, MenuGroupMap menuGroupMap) {
|
||||||
this.menuGroupMap = menuGroupMap;
|
this.menuGroupMap = menuGroupMap;
|
||||||
menuManagers = new TreeMap<String, MenuManager>();
|
menuManagers = new TreeMap<>();
|
||||||
this.menuHandler = actionHandler;
|
this.menuHandler = actionHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearActions() {
|
public void clearActions() {
|
||||||
menuManagers = new TreeMap<String, MenuManager>();
|
menuManagers = new TreeMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an action to the menu.
|
* Adds an action to the menu
|
||||||
* @param action the action to be added.
|
* @param action the action to be added
|
||||||
* @param groupMgr the MenuGroupMap
|
|
||||||
*/
|
*/
|
||||||
public void addAction(DockingActionIf action) {
|
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();
|
MenuData menuBarData = action.getMenuBarData();
|
||||||
if (menuBarData == null) {
|
if (menuBarData == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] menuPath = menuBarData.getMenuPath();
|
String[] menuPath = menuBarData.getMenuPath();
|
||||||
if (menuPath == null || menuPath.length <= 1) {
|
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);
|
MenuManager mgr = menuManagers.get(menuName);
|
||||||
if (mgr == null) {
|
if (mgr == null) {
|
||||||
mgr =
|
mgr = new MenuManager(menuName, new String[] { menuName }, mk, 1, null, false,
|
||||||
new MenuManager(menuName, new String[] { menuName }, mk, 1, null, false,
|
|
||||||
menuHandler, menuGroupMap);
|
menuHandler, menuGroupMap);
|
||||||
menuManagers.put(menuName, mgr);
|
menuManagers.put(menuName, mgr);
|
||||||
}
|
}
|
||||||
return mgr;
|
return mgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JMenuBar for all the actions.
|
|
||||||
*/
|
|
||||||
public JMenuBar getMenuBar() {
|
public JMenuBar getMenuBar() {
|
||||||
MenuManager fileMenu = menuManagers.get("File");
|
MenuManager fileMenu = menuManagers.get("File");
|
||||||
MenuManager editMenu = menuManagers.get("Edit");
|
MenuManager editMenu = menuManagers.get("Edit");
|
||||||
|
@ -153,6 +153,7 @@ public class MenuBarManager implements MenuGroupListener {
|
||||||
* @param menuPath the menu path whose group changed.
|
* @param menuPath the menu path whose group changed.
|
||||||
* @param group the new group for the given menuPath.
|
* @param group the new group for the given menuPath.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void menuGroupChanged(String[] menuPath, String group) {
|
public void menuGroupChanged(String[] menuPath, String group) {
|
||||||
if (menuPath != null && menuPath.length > 1) {
|
if (menuPath != null && menuPath.length > 1) {
|
||||||
MenuManager mgr = getMenuManager(menuPath[0]);
|
MenuManager mgr = getMenuManager(menuPath[0]);
|
||||||
|
|
|
@ -43,13 +43,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
||||||
// listeners to handle help activation
|
// listeners to handle help activation
|
||||||
// -this listener covers activation by keyboard and by mouse *when enabled*
|
// -this listener covers activation by keyboard and by mouse *when enabled*
|
||||||
private ChangeListener buttonModelChangeListener;
|
private ChangeListener buttonModelChangeListener;
|
||||||
|
|
||||||
// -this listener covers activation by mouse *when the action is disabled*
|
// -this listener covers activation by mouse *when the action is disabled*
|
||||||
private MouseAdapter menuHoverListener;
|
private MouseAdapter menuHoverListener;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new MenuItemManger
|
|
||||||
* @param dockableAction the action whose menuItem is being managed.
|
|
||||||
*/
|
|
||||||
MenuItemManager(MenuHandler actionHandler, DockingActionIf dockingAction,
|
MenuItemManager(MenuHandler actionHandler, DockingActionIf dockingAction,
|
||||||
boolean usePopupPath) {
|
boolean usePopupPath) {
|
||||||
this.menuHandler = actionHandler;
|
this.menuHandler = actionHandler;
|
||||||
|
@ -104,9 +101,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#getWindowGroup()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getGroup() {
|
public String getGroup() {
|
||||||
MenuData menuData = isPopup ? action.getPopupMenuData() : action.getMenuBarData();
|
MenuData menuData = isPopup ? action.getPopupMenuData() : action.getMenuBarData();
|
||||||
|
@ -119,10 +113,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
||||||
return menuData == null ? null : menuData.getMenuSubGroup();
|
return menuData == null ? null : menuData.getMenuSubGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#dispose()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
|
@ -138,9 +128,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
||||||
action = null;
|
action = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.docking.menu.ManagedMenuItem#getMenuItem()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public JMenuItem getMenuItem() {
|
public JMenuItem getMenuItem() {
|
||||||
if (menuItem != null) {
|
if (menuItem != null) {
|
||||||
|
@ -158,17 +145,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
||||||
return menuItem;
|
return menuItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the owner associated with this items action.
|
|
||||||
*/
|
|
||||||
public String getOwner() {
|
public String getOwner() {
|
||||||
return action.getOwner();
|
return action.getOwner();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the menuItem to reflect changes in the actions properties.
|
|
||||||
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent e) {
|
public void propertyChange(PropertyChangeEvent e) {
|
||||||
if (menuItem == null) {
|
if (menuItem == null) {
|
||||||
|
@ -209,16 +189,10 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the action associated with this menu item.
|
|
||||||
*/
|
|
||||||
public DockingActionIf getAction() {
|
public DockingActionIf getAction() {
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
if (menuHandler != null) {
|
if (menuHandler != null) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 {
|
public class MenuManager implements ManagedMenuItem {
|
||||||
private static String NULL_GROUP_NAME = "<null group>";
|
private static String NULL_GROUP_NAME = "<null group>";
|
||||||
|
|
||||||
private Set<ManagedMenuItem> managedMenuItems = new HashSet<ManagedMenuItem>();
|
private Set<ManagedMenuItem> managedMenuItems = new HashSet<>();
|
||||||
private Map<String, MenuManager> subMenus = new HashMap<String, MenuManager>();
|
private Map<String, MenuManager> subMenus = new HashMap<>();
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private final String[] menuPath;
|
private final String[] menuPath;
|
||||||
|
@ -50,9 +49,9 @@ public class MenuManager implements ManagedMenuItem {
|
||||||
* @param name the name of the menu.
|
* @param name the name of the menu.
|
||||||
* @param mnemonicKey the key to use for the menu mnemonic
|
* @param mnemonicKey the key to use for the menu mnemonic
|
||||||
* @param group the group of the menu.
|
* @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 usePopupPath if true, registers actions with popup paths as popup items.
|
||||||
* @param menuHandler Listener to be notified of menu behavior.
|
* @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,
|
public MenuManager(String name, char mnemonicKey, String group, boolean usePopupPath,
|
||||||
MenuHandler menuHandler, MenuGroupMap menuGroupMap) {
|
MenuHandler menuHandler, MenuGroupMap menuGroupMap) {
|
||||||
|
@ -68,9 +67,9 @@ public class MenuManager implements ManagedMenuItem {
|
||||||
* @param mnemonicKey the key to use for the menu mnemonic
|
* @param mnemonicKey the key to use for the menu mnemonic
|
||||||
* @param level the number of parent menus that this menu is in.
|
* @param level the number of parent menus that this menu is in.
|
||||||
* @param group the group of this menu.
|
* @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 usePopupPath if true, registers actions with popup paths as popup items.
|
||||||
* @param menuHandler Listener to be notified of menu behavior.
|
* @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,
|
MenuManager(String name, String[] menuPath, char mnemonicKey, int level, String group,
|
||||||
boolean usePopupPath, MenuHandler menuHandler, MenuGroupMap menuGroupMap) {
|
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
|
* Adds an action to this menu. Can create subMenus depending on the menuPath of the action
|
||||||
* action.
|
* @param action the action to be added
|
||||||
* @param action the action to be added.
|
|
||||||
* @param menuGroupMap group map for menuItems
|
|
||||||
*/
|
*/
|
||||||
public void addAction(DockingActionIf action) {
|
public void addAction(DockingActionIf action) {
|
||||||
checkForSwingThread();
|
checkForSwingThread();
|
||||||
|
@ -122,8 +119,7 @@ public class MenuManager implements ManagedMenuItem {
|
||||||
submenuGroup = subMenuName;
|
submenuGroup = subMenuName;
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr =
|
mgr = new MenuManager(cleanSubMenuName, submenuPath, mnemonic, submenuLevel,
|
||||||
new MenuManager(cleanSubMenuName, submenuPath, mnemonic, submenuLevel,
|
|
||||||
submenuGroup, usePopupPath, menuHandler, menuGroupMap);
|
submenuGroup, usePopupPath, menuHandler, menuGroupMap);
|
||||||
subMenus.put(cleanSubMenuName, mgr);
|
subMenus.put(cleanSubMenuName, mgr);
|
||||||
managedMenuItems.add(mgr);
|
managedMenuItems.add(mgr);
|
||||||
|
@ -198,7 +194,7 @@ public class MenuManager implements ManagedMenuItem {
|
||||||
menu.addMenuListener(menuHandler);
|
menu.addMenuListener(menuHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ManagedMenuItem> list = new ArrayList<ManagedMenuItem>(managedMenuItems);
|
List<ManagedMenuItem> list = new ArrayList<>(managedMenuItems);
|
||||||
Collections.sort(list, comparator);
|
Collections.sort(list, comparator);
|
||||||
String lastGroup = null;
|
String lastGroup = null;
|
||||||
|
|
||||||
|
@ -259,7 +255,7 @@ public class MenuManager implements ManagedMenuItem {
|
||||||
if (popupMenu == null) {
|
if (popupMenu == null) {
|
||||||
popupMenu = new JPopupMenu(name);
|
popupMenu = new JPopupMenu(name);
|
||||||
|
|
||||||
List<ManagedMenuItem> list = new ArrayList<ManagedMenuItem>(managedMenuItems);
|
List<ManagedMenuItem> list = new ArrayList<>(managedMenuItems);
|
||||||
Collections.sort(list, comparator);
|
Collections.sort(list, comparator);
|
||||||
String lastGroup = NULL_GROUP_NAME;
|
String lastGroup = NULL_GROUP_NAME;
|
||||||
boolean hasMenuItems = false;
|
boolean hasMenuItems = false;
|
||||||
|
|
|
@ -15,16 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package docking.menu;
|
package docking.menu;
|
||||||
|
|
||||||
import ghidra.util.StringUtilities;
|
|
||||||
|
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
|
import ghidra.util.StringUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to manager toolbar buttons.
|
* Class to manager toolbar buttons.
|
||||||
|
@ -93,7 +94,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
||||||
StringBuilder buffy = new StringBuilder(toolTipText);
|
StringBuilder buffy = new StringBuilder(toolTipText);
|
||||||
if (StringUtilities.startsWithIgnoreCase(toolTipText, "<HTML>")) {
|
if (StringUtilities.startsWithIgnoreCase(toolTipText, "<HTML>")) {
|
||||||
String endHTMLTag = "</HTML>";
|
String endHTMLTag = "</HTML>";
|
||||||
int closeTagIndex = StringUtilities.indexOfIgnoreCase(toolTipText, endHTMLTag);
|
int closeTagIndex = StringUtils.indexOfIgnoreCase(toolTipText, endHTMLTag);
|
||||||
if (closeTagIndex < 0) {
|
if (closeTagIndex < 0) {
|
||||||
// no closing tag, which is acceptable
|
// no closing tag, which is acceptable
|
||||||
buffy.append(START_KEYBINDING_TEXT).append(keyBindingText).append(
|
buffy.append(START_KEYBINDING_TEXT).append(keyBindingText).append(
|
||||||
|
@ -204,9 +205,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
||||||
final ActionContext finalContext = tempContext;
|
final ActionContext finalContext = tempContext;
|
||||||
|
|
||||||
// this gives the UI some time to repaint before executing the action
|
// this gives the UI some time to repaint before executing the action
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (toolBarAction.isEnabledForContext(finalContext)) {
|
if (toolBarAction.isEnabledForContext(finalContext)) {
|
||||||
if (toolBarAction instanceof ToggleDockingActionIf) {
|
if (toolBarAction instanceof ToggleDockingActionIf) {
|
||||||
ToggleDockingActionIf toggleAction = (ToggleDockingActionIf) toolBarAction;
|
ToggleDockingActionIf toggleAction = (ToggleDockingActionIf) toolBarAction;
|
||||||
|
@ -214,7 +213,6 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
|
||||||
}
|
}
|
||||||
toolBarAction.actionPerformed(finalContext);
|
toolBarAction.actionPerformed(finalContext);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ import docking.widgets.VariableHeightPanel;
|
||||||
*/
|
*/
|
||||||
public class ToolBarManager {
|
public class ToolBarManager {
|
||||||
private Map<String, List<ToolBarItemManager>> groupToItemsMap =
|
private Map<String, List<ToolBarItemManager>> groupToItemsMap =
|
||||||
new TreeMap<String, List<ToolBarItemManager>>(new GroupComparator());
|
new TreeMap<>(new GroupComparator());
|
||||||
private Comparator<? super ToolBarItemManager> toolBarItemComparator =
|
private Comparator<? super ToolBarItemManager> toolBarItemComparator =
|
||||||
new ToolBarItemManagerComparator();
|
new ToolBarItemManagerComparator();
|
||||||
|
|
||||||
|
@ -47,9 +47,6 @@ public class ToolBarManager {
|
||||||
toolBar = null;
|
toolBar = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the action to the toolbar.
|
|
||||||
*/
|
|
||||||
public void addAction(DockingActionIf action) {
|
public void addAction(DockingActionIf action) {
|
||||||
ToolBarData toolBarData = action.getToolBarData();
|
ToolBarData toolBarData = action.getToolBarData();
|
||||||
if (toolBarData == null) {
|
if (toolBarData == null) {
|
||||||
|
@ -61,7 +58,7 @@ public class ToolBarManager {
|
||||||
String group = toolBarData.getToolBarGroup();
|
String group = toolBarData.getToolBarGroup();
|
||||||
List<ToolBarItemManager> items = groupToItemsMap.get(group);
|
List<ToolBarItemManager> items = groupToItemsMap.get(group);
|
||||||
if (items == null) {
|
if (items == null) {
|
||||||
items = new ArrayList<ToolBarItemManager>();
|
items = new ArrayList<>();
|
||||||
groupToItemsMap.put(group, items);
|
groupToItemsMap.put(group, items);
|
||||||
}
|
}
|
||||||
items.add(new ToolBarItemManager(action, windowManager));
|
items.add(new ToolBarItemManager(action, windowManager));
|
||||||
|
@ -96,9 +93,6 @@ public class ToolBarManager {
|
||||||
groupToItemsMap.clear();
|
groupToItemsMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the toolbar is empty.
|
|
||||||
*/
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return groupToItemsMap.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 tool the tool containing all system actions
|
||||||
* @param name the owner's name to match
|
* @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 tool the tool containing all system actions
|
||||||
* @param owner the owner's name
|
* @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.
|
* Finds the action by the given owner name and action name.
|
||||||
* If you do not know the owner name, then use
|
* 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
|
* <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).
|
* 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();
|
List<Image> windowIcons = ApplicationInformationDisplayFactory.getWindowIcons();
|
||||||
winMgr = new DockingWindowManager("EMPTY", windowIcons, listener, false /*isModal*/,
|
winMgr = new DockingWindowManager("EMPTY", windowIcons, listener, false /*isModal*/,
|
||||||
true /*isDockable*/, true /*hasStatus*/, null /*DropTargetFactory*/);
|
true /*isDockable*/, true /*hasStatus*/, null /*DropTargetFactory*/);
|
||||||
actionMgr = new ToolActions(this, winMgr);
|
toolActions = new ToolActions(this, winMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -303,7 +303,7 @@ public class JavaSourceFile {
|
||||||
if (nameAndMaybeDeclaraction.length == 2) {
|
if (nameAndMaybeDeclaraction.length == 2) {
|
||||||
return nameAndMaybeDeclaraction[0].endsWith("Action");
|
return nameAndMaybeDeclaraction[0].endsWith("Action");
|
||||||
}
|
}
|
||||||
return StringUtilities.containsIgnoreCase(nameAndMaybeDeclaraction[0], "action");
|
return StringUtils.containsIgnoreCase(nameAndMaybeDeclaraction[0], "action");
|
||||||
}
|
}
|
||||||
|
|
||||||
private JavaSourceLine findEndOfUnknownLine(int lineNumber) {
|
private JavaSourceLine findEndOfUnknownLine(int lineNumber) {
|
||||||
|
|
|
@ -348,22 +348,6 @@ public class StringUtilities {
|
||||||
return string.regionMatches(true, startIndex, postfix, 0, postfix.length());
|
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.
|
* Returns true if all the given <tt>searches</tt> are contained in the given string.
|
||||||
*
|
*
|
||||||
|
@ -455,61 +439,6 @@ public class StringUtilities {
|
||||||
return true;
|
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.
|
* Convert tabs in the given string to spaces.
|
||||||
*
|
*
|
||||||
|
@ -532,10 +461,8 @@ public class StringUtilities {
|
||||||
char c = str.charAt(i);
|
char c = str.charAt(i);
|
||||||
if (c == '\t') {
|
if (c == '\t') {
|
||||||
int nSpaces = tabSize - (linepos % tabSize);
|
int nSpaces = tabSize - (linepos % tabSize);
|
||||||
String pad = padString("", ' ', nSpaces);
|
String pad = pad("", ' ', nSpaces);
|
||||||
|
|
||||||
buffer.append(pad);
|
buffer.append(pad);
|
||||||
|
|
||||||
linepos += nSpaces;
|
linepos += nSpaces;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -606,23 +533,6 @@ public class StringUtilities {
|
||||||
return padded;
|
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
|
* Pads the source string to the specified length, using the filler string
|
||||||
* as the pad. If length is negative, left justifies the string, appending
|
* as the pad. If length is negative, left justifies the string, appending
|
||||||
|
|
|
@ -33,6 +33,8 @@ import resources.icons.TranslateIcon;
|
||||||
*/
|
*/
|
||||||
public class Icons {
|
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 ADD_ICON = ResourceManager.loadImage("images/Plus2.png");
|
||||||
|
|
||||||
public static final ImageIcon COLLAPSE_ALL_ICON =
|
public static final ImageIcon COLLAPSE_ALL_ICON =
|
||||||
|
|
|
@ -17,10 +17,8 @@ package ghidra.util;
|
||||||
|
|
||||||
import static ghidra.util.HTMLUtilities.HTML;
|
import static ghidra.util.HTMLUtilities.HTML;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -53,7 +51,7 @@ public class HTMLUtilitiesTest {
|
||||||
String s = "This text has<BR>an existing BR tag";
|
String s = "This text has<BR>an existing BR tag";
|
||||||
String html = HTMLUtilities.toHTML(s);
|
String html = HTMLUtilities.toHTML(s);
|
||||||
assertEquals(HTML + s, html);
|
assertEquals(HTML + s, html);
|
||||||
assertLogMessage("cannot", "wrap");
|
spyLogger.assertLogMessage("cannot", "wrap");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -61,7 +59,7 @@ public class HTMLUtilitiesTest {
|
||||||
String s = "This text has<BR>\nan existing BR tag and a newline";
|
String s = "This text has<BR>\nan existing BR tag and a newline";
|
||||||
String html = HTMLUtilities.toHTML(s);
|
String html = HTMLUtilities.toHTML(s);
|
||||||
assertEquals(HTML + s, html);
|
assertEquals(HTML + s, html);
|
||||||
assertLogMessage("cannot", "wrap");
|
spyLogger.assertLogMessage("cannot", "wrap");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -140,16 +138,6 @@ public class HTMLUtilitiesTest {
|
||||||
assertEquals("#FF0000", rgb);
|
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
|
@Test
|
||||||
public void testLinkPlaceholder() {
|
public void testLinkPlaceholder() {
|
||||||
String placeholderStr =
|
String placeholderStr =
|
||||||
|
|
|
@ -94,26 +94,6 @@ public class StringUtilitiesTest {
|
||||||
assertEquals(-1, StringUtilities.indexOfWord(sentenceWithTestNotAsAWord, word));
|
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
|
@Test
|
||||||
public void testIsAllBlank() {
|
public void testIsAllBlank() {
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.plugintool;
|
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.*;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
@ -158,7 +159,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
||||||
eventMgr = new EventManager(this);
|
eventMgr = new EventManager(this);
|
||||||
serviceMgr = new ServiceManager();
|
serviceMgr = new ServiceManager();
|
||||||
installServices();
|
installServices();
|
||||||
actionMgr = new ToolActions(this, winMgr);
|
toolActions = new ToolActions(this, winMgr);
|
||||||
pluginMgr = new PluginManager(this, serviceMgr);
|
pluginMgr = new PluginManager(this, serviceMgr);
|
||||||
dialogMgr = new DialogManager(this);
|
dialogMgr = new DialogManager(this);
|
||||||
initActions();
|
initActions();
|
||||||
|
@ -460,7 +461,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
||||||
winMgr.setVisible(false);
|
winMgr.setVisible(false);
|
||||||
eventMgr.clearLastEvents();
|
eventMgr.clearLastEvents();
|
||||||
pluginMgr.dispose();
|
pluginMgr.dispose();
|
||||||
actionMgr.dispose();
|
toolActions.dispose();
|
||||||
|
|
||||||
if (project != null) {
|
if (project != null) {
|
||||||
project.releaseFiles(this);
|
project.releaseFiles(this);
|
||||||
|
@ -1300,7 +1301,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
||||||
|
|
||||||
protected void restoreOptionsFromXml(Element root) {
|
protected void restoreOptionsFromXml(Element root) {
|
||||||
optionsMgr.setConfigState(root.getChild("OPTIONS"));
|
optionsMgr.setConfigState(root.getChild("OPTIONS"));
|
||||||
actionMgr.restoreKeyBindings();
|
toolActions.restoreKeyBindings();
|
||||||
setToolOptionsHelpLocation();
|
setToolOptionsHelpLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1321,7 +1322,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeAll(String owner) {
|
void removeAll(String owner) {
|
||||||
actionMgr.removeToolActions(owner);
|
toolActions.removeToolActions(owner);
|
||||||
winMgr.removeAll(owner);
|
winMgr.removeAll(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1496,7 +1497,7 @@ public abstract class PluginTool extends AbstractDockingTool
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshKeybindings() {
|
public void refreshKeybindings() {
|
||||||
actionMgr.restoreKeyBindings();
|
toolActions.restoreKeyBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnconfigurable() {
|
public void setUnconfigurable() {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import docking.KeyEntryTextField;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.action.KeyBindingData;
|
import docking.action.KeyBindingData;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
|
import docking.actions.SharedStubKeyBindingAction;
|
||||||
import docking.tool.util.DockingToolConstants;
|
import docking.tool.util.DockingToolConstants;
|
||||||
import docking.widgets.MultiLineLabel;
|
import docking.widgets.MultiLineLabel;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
|
@ -429,9 +430,6 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
tableModel.fireTableDataChanged();
|
tableModel.fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add listeners. Valid modifiers are CTRL and ALT and SHIFT.
|
|
||||||
*/
|
|
||||||
private void addListeners() {
|
private void addListeners() {
|
||||||
selectionModel = actionTable.getSelectionModel();
|
selectionModel = actionTable.getSelectionModel();
|
||||||
selectionModel.addListSelectionListener(new TableSelectionListener());
|
selectionModel.addListSelectionListener(new TableSelectionListener());
|
||||||
|
@ -463,11 +461,6 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
unappliedChanges = changes;
|
unappliedChanges = changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the action that is selected in the table.
|
|
||||||
*
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
private String getSelectedAction() {
|
private String getSelectedAction() {
|
||||||
if (selectionModel.isSelectionEmpty()) {
|
if (selectionModel.isSelectionEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -477,9 +470,6 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
return tableActions.get(modelRow).getFullName();
|
return tableActions.get(modelRow).getFullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the action name to the list for the given keystroke.
|
|
||||||
*/
|
|
||||||
private void addToKeyMap(KeyStroke ks, String actionName) {
|
private void addToKeyMap(KeyStroke ks, String actionName) {
|
||||||
if (ks == null) {
|
if (ks == null) {
|
||||||
return;
|
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) {
|
private void removeFromKeyMap(KeyStroke ks, String actionName) {
|
||||||
if (ks == null) {
|
if (ks == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -512,11 +499,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void showActionsMappedToKeyStroke(String ksName) {
|
||||||
* Display actions mapped to the given keystroke name.
|
|
||||||
* @param ksName name of Keystroke that has multiple actions mapped
|
|
||||||
*/
|
|
||||||
private void showActionMapped(String ksName) {
|
|
||||||
List<String> list = actionNamesByKeyStroke.get(ksName);
|
List<String> list = actionNamesByKeyStroke.get(ksName);
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -538,17 +521,10 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the info panel.
|
|
||||||
*/
|
|
||||||
private void clearInfoPanel() {
|
private void clearInfoPanel() {
|
||||||
updateInfoPanel(" ");
|
updateInfoPanel(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace multiline label in the info panel.
|
|
||||||
* @param text new text to show
|
|
||||||
*/
|
|
||||||
private void updateInfoPanel(String text) {
|
private void updateInfoPanel(String text) {
|
||||||
infoPanel.removeAll();
|
infoPanel.removeAll();
|
||||||
infoPanel.repaint();
|
infoPanel.repaint();
|
||||||
|
@ -559,8 +535,6 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
validate();
|
validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private void processKeyBindingsFromOptions(Options keyBindingOptions) {
|
private void processKeyBindingsFromOptions(Options keyBindingOptions) {
|
||||||
if (keyBindingOptions == null) {
|
if (keyBindingOptions == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -578,7 +552,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
String name = iterator.next();
|
String name = iterator.next();
|
||||||
KeyStroke keyStroke = keyBindingsMap.get(name);
|
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
|
// prevent non-existing keybindings from being added to Ghidra (this can happen
|
||||||
// when actions exist in the imported bindings, but have been removed from
|
// 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 (selectedActionName != null) {
|
||||||
if (processKeyStroke(selectedActionName, ks)) {
|
if (processKeyStroke(selectedActionName, ks)) {
|
||||||
String keyStrokeText = KeyEntryTextField.parseKeyStroke(ks);
|
String keyStrokeText = KeyEntryTextField.parseKeyStroke(ks);
|
||||||
showActionMapped(keyStrokeText);
|
showActionsMappedToKeyStroke(keyStrokeText);
|
||||||
tableModel.fireTableDataChanged();
|
tableModel.fireTableDataChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -691,7 +665,7 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
|
|
||||||
if (ks != null) {
|
if (ks != null) {
|
||||||
ksName = KeyEntryTextField.parseKeyStroke(ks);
|
ksName = KeyEntryTextField.parseKeyStroke(ks);
|
||||||
showActionMapped(ksName);
|
showActionsMappedToKeyStroke(ksName);
|
||||||
}
|
}
|
||||||
|
|
||||||
ksField.setText(ksName);
|
ksField.setText(ksName);
|
||||||
|
@ -734,11 +708,18 @@ public class KeyBindingsPanel extends JPanel {
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
case PLUGIN_NAME:
|
case PLUGIN_NAME:
|
||||||
return action.getOwner();
|
return getOwner(action);
|
||||||
}
|
}
|
||||||
return "Unknown Column!";
|
return "Unknown Column!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getOwner(DockingActionIf action) {
|
||||||
|
if (action instanceof SharedStubKeyBindingAction) {
|
||||||
|
return ((SharedStubKeyBindingAction) action).getOwnersDescription();
|
||||||
|
}
|
||||||
|
return action.getOwner();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DockingActionIf> getModelData() {
|
public List<DockingActionIf> getModelData() {
|
||||||
return tableActions;
|
return tableActions;
|
||||||
|
|
|
@ -23,9 +23,9 @@ import javax.swing.KeyStroke;
|
||||||
import javax.swing.text.SimpleAttributeSet;
|
import javax.swing.text.SimpleAttributeSet;
|
||||||
import javax.swing.text.StyleConstants;
|
import javax.swing.text.StyleConstants;
|
||||||
|
|
||||||
import docking.DockingKeyBindingAction;
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
|
import docking.actions.KeyBindingUtils;
|
||||||
import ghidra.framework.plugintool.PluginConfigurationModel;
|
import ghidra.framework.plugintool.PluginConfigurationModel;
|
||||||
import ghidra.framework.plugintool.util.PluginDescription;
|
import ghidra.framework.plugintool.util.PluginDescription;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
@ -201,7 +201,7 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
|
||||||
buffer.append("<TD WIDTH=\"100\">");
|
buffer.append("<TD WIDTH=\"100\">");
|
||||||
KeyStroke keyBinding = dockableAction.getKeyBinding();
|
KeyStroke keyBinding = dockableAction.getKeyBinding();
|
||||||
if (keyBinding != null) {
|
if (keyBinding != null) {
|
||||||
String keyStrokeString = DockingKeyBindingAction.parseKeyStroke(keyBinding);
|
String keyStrokeString = KeyBindingUtils.parseKeyStroke(keyBinding);
|
||||||
insertHTMLString(keyStrokeString, locAttrSet, buffer);
|
insertHTMLString(keyStrokeString, locAttrSet, buffer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -27,9 +27,11 @@ import javax.swing.border.Border;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import docking.*;
|
import docking.DialogComponentProvider;
|
||||||
|
import docking.StatusBar;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.action.KeyEntryDialog;
|
import docking.actions.KeyEntryDialog;
|
||||||
|
import docking.actions.ToolActions;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
|
@ -280,12 +282,10 @@ public class ToolScreenShots extends GhidraScreenShotGenerator {
|
||||||
public void testSetKeyBindings() {
|
public void testSetKeyBindings() {
|
||||||
|
|
||||||
tool = env.launchDefaultTool();
|
tool = env.launchDefaultTool();
|
||||||
DockingWindowManager windowManager = tool.getWindowManager();
|
ToolActions toolActions = (ToolActions) getInstanceField("toolActions", tool);
|
||||||
ActionToGuiMapper actionMgr =
|
|
||||||
(ActionToGuiMapper) getInstanceField("actionManager", windowManager);
|
|
||||||
|
|
||||||
DockingActionIf action = getAction(tool, "FunctionPlugin", "Delete Function");
|
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);
|
runSwing(() -> tool.showDialog(keyEntryDialog), false);
|
||||||
captureDialog();
|
captureDialog();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue