mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch 'origin/GP-5599-dragonmacher-shared-key-binding-fix--SQUASHED'
This commit is contained in:
commit
bf1659f11d
7 changed files with 146 additions and 80 deletions
|
@ -112,7 +112,7 @@ public class LocationReferencesPlugin extends Plugin
|
||||||
// only so the user can bind a key binding to it if they wish.
|
// only so the user can bind a key binding to it if they wish.
|
||||||
new ActionBuilder("Show Xrefs", getName())
|
new ActionBuilder("Show Xrefs", getName())
|
||||||
.description("Show the Xrefs to the code unit containing the cursor")
|
.description("Show the Xrefs to the code unit containing the cursor")
|
||||||
.validContextWhen(context -> context instanceof ListingActionContext)
|
.withContext(ListingActionContext.class)
|
||||||
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Show_Xrefs"))
|
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Show_Xrefs"))
|
||||||
.onAction(context -> showXrefs(context))
|
.onAction(context -> showXrefs(context))
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
|
@ -394,7 +394,7 @@ public class ListingCodeComparisonPanel
|
||||||
.toolBarIcon(NEXT_DIFF_ICON)
|
.toolBarIcon(NEXT_DIFF_ICON)
|
||||||
.toolBarGroup(DIFF_NAVIGATE_GROUP)
|
.toolBarGroup(DIFF_NAVIGATE_GROUP)
|
||||||
.keyBinding("ctrl alt N")
|
.keyBinding("ctrl alt N")
|
||||||
.validContextWhen(c -> isValidPanelContext(c))
|
.validWhen(c -> isValidPanelContext(c))
|
||||||
.enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
|
.enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
|
||||||
.onAction(c -> nextAreaDiff(true))
|
.onAction(c -> nextAreaDiff(true))
|
||||||
.build();
|
.build();
|
||||||
|
@ -409,7 +409,7 @@ public class ListingCodeComparisonPanel
|
||||||
.toolBarIcon(PREVIOUS_DIFF_ICON)
|
.toolBarIcon(PREVIOUS_DIFF_ICON)
|
||||||
.toolBarGroup(DIFF_NAVIGATE_GROUP)
|
.toolBarGroup(DIFF_NAVIGATE_GROUP)
|
||||||
.keyBinding("ctrl alt P")
|
.keyBinding("ctrl alt P")
|
||||||
.validContextWhen(c -> isValidPanelContext(c))
|
.validWhen(c -> isValidPanelContext(c))
|
||||||
.enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
|
.enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
|
||||||
.onAction(c -> nextAreaDiff(false))
|
.onAction(c -> nextAreaDiff(false))
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -584,5 +584,10 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||||
tool.setStatusInfo(message, true);
|
tool.setStatusInfo(message, true);
|
||||||
Toolkit.getDefaultToolkit().beep();
|
Toolkit.getDefaultToolkit().beep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": " + validActions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -570,6 +570,24 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param predicate the predicate
|
||||||
|
* @return this builder (for chaining)
|
||||||
|
* @deprecated use {@link #validWhen(Predicate)}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "11.4")
|
||||||
|
public B validContextWhen(Predicate<C> predicate) {
|
||||||
|
validContextPredicate = Objects.requireNonNull(predicate);
|
||||||
|
|
||||||
|
// automatic enablement management triggered, make sure there is a existing enablement
|
||||||
|
// predicate. The default behavior of manual management interferes with automatic management.
|
||||||
|
if (enabledPredicate == null) {
|
||||||
|
enabledPredicate = ALWAYS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a predicate for dynamically determining if this action is valid for the current
|
* Sets a predicate for dynamically determining if this action is valid for the current
|
||||||
* {@link ActionContext}. See {@link DockingActionIf#isValidContext(ActionContext)}.
|
* {@link ActionContext}. See {@link DockingActionIf#isValidContext(ActionContext)}.
|
||||||
|
@ -584,7 +602,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends
|
||||||
* validity for a given {@link ActionContext}
|
* validity for a given {@link ActionContext}
|
||||||
* @return this builder (for chaining)
|
* @return this builder (for chaining)
|
||||||
*/
|
*/
|
||||||
public B validContextWhen(Predicate<C> predicate) {
|
public B validWhen(Predicate<C> predicate) {
|
||||||
validContextPredicate = Objects.requireNonNull(predicate);
|
validContextPredicate = Objects.requireNonNull(predicate);
|
||||||
|
|
||||||
// automatic enablement management triggered, make sure there is a existing enablement
|
// automatic enablement management triggered, make sure there is a existing enablement
|
||||||
|
|
|
@ -211,14 +211,20 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setKeyBindingData(DockingActionIf action, ActionTrigger actionTrigger) {
|
private void setKeyBindingData(DockingActionIf action, ActionTrigger actionTrigger) {
|
||||||
KeyBindingData kbData = null;
|
|
||||||
|
KeyBindingData newKbData = null;
|
||||||
if (actionTrigger != null) {
|
if (actionTrigger != null) {
|
||||||
kbData = new KeyBindingData(actionTrigger);
|
newKbData = new KeyBindingData(actionTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyBindingData currentKbData = action.getKeyBindingData();
|
||||||
|
if (Objects.equals(currentKbData, newKbData)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we use the 'unvalidated' call since this value is provided by the user--we assume
|
// we use the 'unvalidated' call since this value is provided by the user--we assume
|
||||||
// that user input is correct; we only validate programmer input
|
// that user input is correct; we only validate programmer input
|
||||||
action.setUnvalidatedKeyBindingData(kbData);
|
action.setUnvalidatedKeyBindingData(newKbData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActionTrigger getActionTriggerFromOptions(ActionTrigger validatedTrigger) {
|
private ActionTrigger getActionTriggerFromOptions(ActionTrigger validatedTrigger) {
|
||||||
|
@ -232,6 +238,27 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
||||||
return data.getActionTrigger();
|
return data.getActionTrigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This can be called from ToolActions.optionsRebuilt(). In that case, the code only
|
||||||
|
* loops over KeyBindingType.INDIVIDAL actions types (which this class is), but not
|
||||||
|
* KeyBindingType.SHARED actions, which the contained actions are. In the case of options
|
||||||
|
* getting restored, 1) only this action gets updated, and 2), there is no options changed
|
||||||
|
* event fired. Thus, to handle options being restored, we have to override this method to make
|
||||||
|
* sure we update out internal client actions.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData) {
|
||||||
|
|
||||||
|
// update this shared key binding
|
||||||
|
super.setUnvalidatedKeyBindingData(newKeyBindingData);
|
||||||
|
|
||||||
|
// update the client keybindings
|
||||||
|
ActionTrigger newTrigger = getActionTrigger(newKeyBindingData);
|
||||||
|
for (DockingActionIf action : clientActions.keySet()) {
|
||||||
|
setKeyBindingData(action, newTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||||
Object newValue) {
|
Object newValue) {
|
||||||
|
@ -240,9 +267,11 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
||||||
return; // not my binding
|
return; // not my binding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update this shared key binding
|
||||||
ActionTrigger newTrigger = (ActionTrigger) newValue;
|
ActionTrigger newTrigger = (ActionTrigger) newValue;
|
||||||
setKeyBindingData(this, newTrigger);
|
setKeyBindingData(this, newTrigger);
|
||||||
|
|
||||||
|
// update the client keybindings
|
||||||
for (DockingActionIf action : clientActions.keySet()) {
|
for (DockingActionIf action : clientActions.keySet()) {
|
||||||
setKeyBindingData(action, newTrigger);
|
setKeyBindingData(action, newTrigger);
|
||||||
}
|
}
|
||||||
|
@ -275,6 +304,11 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
||||||
keyBindingOptions.removeOptionsChangeListener(this);
|
keyBindingOptions.removeOptionsChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Shared Stub Action: " + super.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private static void logDifferentKeyBindingsWarnigMessage(DockingActionIf newAction,
|
private static void logDifferentKeyBindingsWarnigMessage(DockingActionIf newAction,
|
||||||
DockingActionIf existingAction, ActionTrigger existingDefaultTrigger) {
|
DockingActionIf existingAction, ActionTrigger existingDefaultTrigger) {
|
||||||
|
|
||||||
|
|
|
@ -206,7 +206,7 @@ public class ActionBuilderTest {
|
||||||
@Test
|
@Test
|
||||||
public void testValidContextWhen() {
|
public void testValidContextWhen() {
|
||||||
DockingAction action = new ActionBuilder("Test", "Test")
|
DockingAction action = new ActionBuilder("Test", "Test")
|
||||||
.validContextWhen(c -> c.getContextObject() == this)
|
.validWhen(c -> c.getContextObject() == this)
|
||||||
.onAction(e -> actionCount++)
|
.onAction(e -> actionCount++)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.framework.main.datatree;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
import docking.action.KeyBindingData;
|
import docking.action.KeyBindingData;
|
||||||
import ghidra.framework.main.datatable.ProjectTreeAction;
|
import ghidra.framework.main.datatable.ProjectTreeAction;
|
||||||
|
|
||||||
|
@ -30,10 +31,18 @@ public class ClearCutAction extends ProjectTreeAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(FrontEndProjectTreeContext context) {
|
public boolean isValidContext(ActionContext context) {
|
||||||
return DataTreeClipboardUtils.isCuttablePresent();
|
return DataTreeClipboardUtils.isCuttablePresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(FrontEndProjectTreeContext context) {
|
||||||
|
// If we are valid, then we are enabled (see isValidContext()). Most actions are always
|
||||||
|
// valid, but only sometimes enabled. We use the valid check to remove ourselves completely
|
||||||
|
// from the workflow. But, if we are valid, then we are also enabled.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(FrontEndProjectTreeContext context) {
|
public void actionPerformed(FrontEndProjectTreeContext context) {
|
||||||
DataTreeClipboardUtils.clearCuttables();
|
DataTreeClipboardUtils.clearCuttables();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue