Fixed docking action key bindings in dialogs

This commit is contained in:
dragonmacher
2026-03-27 09:53:57 -04:00
parent 834fef4fd2
commit 3a31863dc0
47 changed files with 376 additions and 327 deletions

View File

@@ -538,20 +538,26 @@ public abstract class AbstractGhidraHeadedDebuggerTest
}
protected static void assertDisabled(ActionContextProvider provider, DockingActionIf action) {
ActionContext context = provider.getActionContext(null);
ActionContext context = createActionContext(provider);
assertFalse(action.isEnabledForContext(context));
}
protected static void assertEnabled(ActionContextProvider provider, DockingActionIf action) {
ActionContext context = provider.getActionContext(null);
ActionContext context = createActionContext(provider);
assertTrue(action.isEnabledForContext(context));
}
protected static void performEnabledAction(ActionContextProvider provider,
DockingActionIf action, boolean wait) {
ActionContext context = waitForValue(() -> {
ActionContext ctx =
provider == null ? new DefaultActionContext() : provider.getActionContext(null);
ActionContext ctx = null;
if (provider == null) {
ctx = new DefaultActionContext();
}
ctx.setContextProvider(provider);
if (!action.isEnabledForContext(ctx)) {
return null;
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -25,6 +25,7 @@ import javax.swing.JTextField;
import org.junit.*;
import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingAction;
import docking.menu.ActionState;
@@ -118,7 +119,8 @@ public class SampleGraphPluginTest extends AbstractGhidraHeadedIntegrationTest {
ToggleDockingAction showFilterAction =
(ToggleDockingAction) getAction(plugin, SampleGraphProvider.SHOW_FILTER_ACTION_NAME);
setToggleActionSelected(showFilterAction, provider.getActionContext(null), true);
ActionContext context = createActionContext(provider);
setToggleActionSelected(showFilterAction, context, true);
Component filterPanel =
findComponentByName(provider.getComponent(), "sample.graph.filter.panel");

View File

@@ -43,6 +43,7 @@ public class ProgramActionContext extends DefaultActionContext {
if (sourceComponent == null) {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
setSourceObject(kfm.getFocusOwner());
setContextProvider(provider);
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -21,8 +21,7 @@ import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.border.Border;
import docking.ActionContext;
import docking.WindowPosition;
import docking.*;
import docking.util.image.ToolIconURL;
import docking.widgets.OptionDialog;
import docking.widgets.label.*;
@@ -86,30 +85,24 @@ class MergeManagerProvider extends ComponentProviderAdapter {
MergeManager mergeManager = plugin.getMergeManager();
if (event != null && event.getSource() instanceof FieldHeaderComp) {
FieldHeaderComp comp = (FieldHeaderComp) event.getSource();
FieldHeaderLocation fieldHeaderLocation = comp.getFieldHeaderLocation(event.getPoint());
return createContext(fieldHeaderLocation);
FieldHeaderLocation fhLoc = comp.getFieldHeaderLocation(event.getPoint());
return new DefaultActionContext(this).setContextObject(fhLoc);
}
if (mergeManager instanceof ProgramMultiUserMergeManager) {
ProgramMultiUserMergeManager programMergeManager =
(ProgramMultiUserMergeManager) mergeManager;
Navigatable navigatable = programMergeManager.navigatable;
if (currentComponent instanceof ListingMergePanel) {
if (mergeManager instanceof ProgramMultiUserMergeManager programMerger) {
Navigatable navigatable = programMerger.navigatable;
if (currentComponent instanceof ListingMergePanel listingMergePanel) {
// Set the program location within the context so it is from the listing panel
// that is being clicked. Actions should use the location to know which of the
// 4 programs or listings is in the current context.
ListingMergePanel listingMergePanel = (ListingMergePanel) currentComponent;
Object actionContext = listingMergePanel.getActionContext(event);
if (actionContext instanceof ProgramLocation) {
ListingActionContext listingActionContext = new ListingActionContext(this,
navigatable, (ProgramLocation) actionContext);
return listingActionContext;
if (actionContext instanceof ProgramLocation loc) {
return new ListingActionContext(this, navigatable, loc);
}
}
ProgramLocation programLocation = navigatable.getLocation();
ListingActionContext listingActionContext =
new ListingActionContext(this, navigatable, programLocation);
return listingActionContext;
ProgramLocation location = navigatable.getLocation();
return new ListingActionContext(this, navigatable, location);
}
return null;
}

View File

@@ -47,7 +47,7 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter
@Override
public ActionContext getActionContext(MouseEvent event) {
Object obj = mergePanel.getActionContext(event);
return createContext(obj);
return new DefaultActionContext(this).setContextObject(obj);
}
void dispose() {

View File

@@ -312,13 +312,13 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
FieldHeader headerPanel = listingPanel.getFieldHeader();
if (headerPanel != null && source instanceof FieldHeaderComp) {
FieldHeaderLocation fhLoc = headerPanel.getFieldHeaderLocation(event.getPoint());
return createContext(fhLoc);
return new DefaultActionContext(this).setContextObject(fhLoc);
}
if (otherPanel != null && otherPanel.isAncestorOf((Component) source)) {
Object obj = getContextForMarginPanels(otherPanel, event);
if (obj != null) {
return createContext(obj);
return new DefaultActionContext(this).setContextObject(obj);
}
return new OtherPanelContext(this, program);
}
@@ -333,7 +333,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
}
}
return createContext(getContextForMarginPanels(listingPanel, event));
Object marginContextObject = getContextForMarginPanels(listingPanel, event);
return new DefaultActionContext(this).setContextObject(marginContextObject);
}
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {

View File

@@ -18,7 +18,6 @@ package ghidra.app.plugin.core.script;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.*;
import javax.swing.*;
@@ -632,11 +631,6 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
}
}
@Override
public ActionContext getActionContext(MouseEvent event) {
return createContext(this);
}
@Override
public JComponent getComponent() {
return scrollPane;
@@ -645,6 +639,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
//==================================================================================================
// Inner Classes
//==================================================================================================
/**
* Special JTextArea that knows how to properly handle it's key events.
* See {@link #processKeyBinding(KeyStroke, KeyEvent, int, boolean)}

View File

@@ -23,6 +23,7 @@ import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelListener;
import docking.ActionContext;
import docking.DefaultActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import generic.theme.GIcon;
@@ -217,7 +218,7 @@ public class PropertyManagerProvider extends ComponentProviderAdapter {
Rectangle rowBounds =
table.getCellRect(row, PropertyManagerTableModel.PROPERTY_NAME_COLUMN, true);
if (rowBounds.contains(event.getPoint())) {
return createContext(rowBounds);
return new DefaultActionContext(this).setContextObject(rowBounds);
}
}
}

View File

@@ -15,24 +15,14 @@
*/
package ghidra.features.base.memsearch.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.*;
import docking.ActionContext;
import docking.DockingContextListener;
@@ -46,9 +36,7 @@ import docking.widgets.OptionDialogBuilder;
import docking.widgets.table.actions.DeleteTableRowAction;
import generic.theme.GIcon;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.nav.Navigatable;
import ghidra.app.nav.NavigatableRegistry;
import ghidra.app.nav.NavigatableRemovalListener;
import ghidra.app.nav.*;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.script.AskDialog;
import ghidra.app.util.HelpTopics;
@@ -58,10 +46,7 @@ import ghidra.features.base.memsearch.combiner.Combiner;
import ghidra.features.base.memsearch.matcher.SearchData;
import ghidra.features.base.memsearch.matcher.UserInputByteMatcher;
import ghidra.features.base.memsearch.scan.Scanner;
import ghidra.features.base.memsearch.searcher.AlignmentFilter;
import ghidra.features.base.memsearch.searcher.CodeUnitFilter;
import ghidra.features.base.memsearch.searcher.MemoryMatch;
import ghidra.features.base.memsearch.searcher.MemorySearcher;
import ghidra.features.base.memsearch.searcher.*;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.DomainObjectClosedListener;
import ghidra.framework.plugintool.ComponentProviderAdapter;
@@ -69,9 +54,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
import ghidra.program.util.BytesFieldLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.program.util.*;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.layout.VerticalLayout;
@@ -127,7 +110,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
// used to show a temporary message over the table
private GGlassPaneMessage glassPaneMessage;
public MemorySearchProvider(MemorySearchPlugin plugin, Navigatable navigatable,
SearchSettings settings, MemorySearchOptions options, SearchHistory history) {
super(plugin.getTool(), "Memory Search", plugin.getName());
@@ -721,13 +704,8 @@ public class MemorySearchProvider extends ComponentProviderAdapter
}
@Override
protected ActionContext createContext(Component focusedComponent, Object contextObject) {
public ActionContext getActionContext(MouseEvent event) {
ActionContext context = new NavigatableActionContext(this, navigatable);
context.setContextObject(contextObject);
// the 'sourceComponent' will be the focused item if the focus owner is in our provider,
// otherwise it will be the main component
context.setSourceObject(focusedComponent);
// we make the source component be the table so that the 'activate filter' action works
// from anywhere in this provider
@@ -761,28 +739,31 @@ public class MemorySearchProvider extends ComponentProviderAdapter
}
}
ArrayList<String> choices = new ArrayList<String>(programMap.keySet());
AskDialog<String> dialog = new AskDialog<String>(null, "Compare to...", "Program", AskDialog.STRING, choices, null);
AskDialog<String> dialog = new AskDialog<String>(null, "Compare to...", "Program",
AskDialog.STRING, choices, null);
if (dialog.isCanceled()) {
return;
}
Navigatable next = programMap.get(dialog.getChoiceValue());
MemorySearchProvider nextProvider = new MemorySearchProvider(plugin, next, model.getSettings(), options, new SearchHistory(searchHistory));
MemorySearchProvider nextProvider = new MemorySearchProvider(plugin, next,
model.getSettings(), options, new SearchHistory(searchHistory));
AddressableByteSource nextByteSource = nextProvider.byteSource;
nextProvider.setSearchInput(this.getSearchInput());
nextProvider.showScanPanel(true);
List<MemoryMatch<SearchData>> searchResults = getSearchResults();
List<MemoryMatch<SearchData>> rebasedResults = new ArrayList<>();
for (MemoryMatch<SearchData> match : searchResults) {
ProgramLocation canonicalLocation = byteSource.getCanonicalLocation(match.getAddress());
Address rebase = nextByteSource.rebaseFromCanonical(canonicalLocation);
if (rebase != null) {
MemoryMatch<SearchData> nextMatch = new MemoryMatch<>(rebase, match.getBytes(), match.getPattern());
MemoryMatch<SearchData> nextMatch =
new MemoryMatch<>(rebase, match.getBytes(), match.getPattern());
rebasedResults.add(nextMatch);
}
}
MemorySearchResultsPanel nextResultsPanel = nextProvider.getResultsPanel();
nextProvider.setBusy(true);
nextResultsPanel.refreshAndMaybeScanForChanges(nextByteSource, scanner, rebasedResults);

View File

@@ -166,11 +166,7 @@ public abstract class AbstractDataTreeDialog extends DialogComponentProvider
return super.getActionContext(event);
}
ActionContext actionContext = treePanel.getActionContext(null, event);
if (actionContext instanceof DialogActionContext dac) {
dac.setDialogComponentProvider(this);
}
return actionContext;
return treePanel.getActionContext(null, event);
}
public void show() {

View File

@@ -352,7 +352,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
PlaceholderManager pm = dwm.getPlaceholderManager();
Set<ComponentProvider> allProviders = pm.getActiveProviders();
for (ComponentProvider cp : allProviders) {
ActionContext context = cp.getActionContext(null);
ActionContext context = createActionContext(cp);
if (context == null) {
continue; // a null context is allowed
}

View File

@@ -22,6 +22,7 @@ import java.util.*;
import org.junit.Before;
import org.junit.Test;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.DockingAction;
import docking.action.DockingActionIf;
@@ -831,7 +832,8 @@ public class CopyPasteCommentsTest extends AbstractProgramBasedTest {
private void assertEnabled(DockingActionIf action, ComponentProvider provider) {
boolean isEnabled = runSwing(() -> {
return action.isEnabledForContext(provider.getActionContext(null));
ActionContext context = createActionContext(provider);
return action.isEnabledForContext(context);
});
assertTrue("Action was not enabled when it should be", isEnabled);
}

View File

@@ -116,7 +116,7 @@ public class ParseDialogTest extends AbstractGhidraHeadedIntegrationTest {
JButton parseToFileButton = findButtonByText(dialog, "Parse to File...");
assertNotNull(parseToFileButton);
ActionContext context = dialog.getActionContext(null);
ActionContext context = createActionContext(dialog);
DockingActionIf saveAsAction = getAction(dialog, "Save Profile As");
assertTrue(saveAsAction.isEnabledForContext(context));
@@ -139,10 +139,12 @@ public class ParseDialogTest extends AbstractGhidraHeadedIntegrationTest {
addSourceFile("c:\\temp\\fred.h");
DockingActionIf saveAction = getAction(dialog, "Save Profile");
assertFalse(saveAction.isEnabledForContext(dialog.getActionContext(null)));
ActionContext context = createActionContext(dialog);
assertFalse(saveAction.isEnabledForContext(context));
DockingActionIf saveAsAction = getAction(dialog, "Save Profile As");
assertTrue(saveAsAction.isEnabledForContext(dialog.getActionContext(null)));
context = createActionContext(dialog);
assertTrue(saveAsAction.isEnabledForContext(context));
}
@Test

View File

@@ -4,9 +4,9 @@
* 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.
@@ -75,8 +75,8 @@ public class LocationReferencesPlugin2Test extends AbstractLocationReferencesTes
Address address = addr(0x01004350);
goTo(address, "Mnemonic", 1);
assertTrue(!showReferencesAction.isEnabledForContext(
getCodeViewerProvider().getActionContext(null)));
ActionContext context = createActionContext(getCodeViewerProvider());
assertTrue(!showReferencesAction.isEnabledForContext(context));
LocationReferencesProvider provider = getResultsProvider();
assertNull("Found a provider for showing references to an undefined mnemonic field.",
@@ -128,8 +128,8 @@ public class LocationReferencesPlugin2Test extends AbstractLocationReferencesTes
// test that the current provider contains the correct location descriptor for a
// given location
assertTrue(!showReferencesAction.isEnabledForContext(
getCodeViewerProvider().getActionContext(null)));
ActionContext context = createActionContext(getCodeViewerProvider());
assertTrue(!showReferencesAction.isEnabledForContext(context));
assertNoResults("Found a provider for showing references to an undefined mnemonic field.");
}
@@ -214,7 +214,7 @@ public class LocationReferencesPlugin2Test extends AbstractLocationReferencesTes
int parameterColumn = 5;
goTo(address, "Variable XRef Header", parameterColumn);
ActionContext context = getCodeViewerProvider().getActionContext(null);
ActionContext context = createActionContext(getCodeViewerProvider());
assertFalse(showReferencesAction.isEnabledForContext(context));
}

View File

@@ -36,6 +36,7 @@ import javax.swing.undo.UndoableEdit;
import org.junit.*;
import docking.ActionContext;
import docking.DefaultActionContext;
import docking.action.DockingActionIf;
import docking.widgets.OptionDialog;
@@ -1086,7 +1087,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
waitForSwing();
DockingActionIf saveAction = getAction(plugin, "Save Script");
boolean isEnabled = saveAction.isEnabledForContext(editor.getActionContext(null));
ActionContext context = createActionContext(editor);
boolean isEnabled = saveAction.isEnabledForContext(context);
if (!isEnabled) {
// the action is enabled when the provider detects changes; it is disabled for read-only
@@ -1135,7 +1137,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
protected void assertSaveButtonDisabled() {
waitForSwing();
DockingActionIf saveAction = getAction(plugin, "Save Script");
assertFalse(saveAction.isEnabledForContext(editor.getActionContext(null)));
ActionContext context = createActionContext(editor);
assertFalse(saveAction.isEnabledForContext(context));
assertEditorHasNoChanges();
}

View File

@@ -24,6 +24,7 @@ import javax.swing.table.TableModel;
import org.junit.*;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.DockingActionIf;
import docking.tool.ToolConstants;
@@ -98,10 +99,12 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest {
public void testActionEnablement() {
DockingAction saveAction = managePluginsDialog.getSaveAction();
performAction(saveAction, true);
assertFalse(saveAction.isEnabledForContext(managePluginsDialog.getActionContext(null)));
ActionContext context = createActionContext(managePluginsDialog);
assertFalse(saveAction.isEnabledForContext(context));
DockingAction saveAsAction = managePluginsDialog.getSaveAsAction();
assertTrue(saveAsAction.isEnabledForContext(managePluginsDialog.getActionContext(null)));
context = createActionContext(managePluginsDialog);
assertTrue(saveAsAction.isEnabledForContext(context));
}
@Test
@@ -148,7 +151,8 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(tool.hasConfigChanged());
DockingAction action = managePluginsDialog.getSaveAction();
assertTrue(action.isEnabledForContext(managePluginsDialog.getActionContext(null)));
ActionContext context = createActionContext(managePluginsDialog);
assertTrue(action.isEnabledForContext(context));
assertTrue(
pluginModel.isLoaded(PluginDescription.getPluginDescription(AboutProgramPlugin.class)));
}
@@ -160,7 +164,8 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest {
pluginModel.removePlugin(PluginDescription.getPluginDescription(EquateTablePlugin.class));
assertTrue(tool.hasConfigChanged());
DockingAction action = managePluginsDialog.getSaveAction();
assertTrue(action.isEnabledForContext(managePluginsDialog.getActionContext(null)));
ActionContext context = createActionContext(managePluginsDialog);
assertTrue(action.isEnabledForContext(context));
assertFalse(
pluginModel.isLoaded(PluginDescription.getPluginDescription(AboutProgramPlugin.class)));

View File

@@ -22,6 +22,7 @@ import java.util.List;
import org.junit.*;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.DockingActionIf;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.features.base.quickfix.*;
@@ -55,8 +56,9 @@ public class SearchAndReplaceDialogTest extends AbstractGhidraHeadedIntegrationT
env.open(program);
env.showTool();
searchAndReplaceAction = getAction(plugin, "Search And Replace");
ActionContext actionContext = tool.getActiveComponentProvider().getActionContext(null);
performAction(searchAndReplaceAction, actionContext, false);
ComponentProvider provider = tool.getActiveComponentProvider();
ActionContext context = createActionContext(provider);
performAction(searchAndReplaceAction, context, false);
dialog = waitForDialogComponent(SearchAndReplaceDialog.class);
assertNotNull(dialog);
}

View File

@@ -426,7 +426,8 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
//
DockingAction copyAction = getCopyAction();
ComponentProvider provider = getProvider();
assertTrue(copyAction.isEnabledForContext(provider.getActionContext(null)));
ActionContext context = createActionContext(provider);
assertTrue(copyAction.isEnabledForContext(context));
performAction(copyAction, provider, false);
@@ -478,11 +479,11 @@ public class FunctionGraphPlugin1Test extends AbstractFunctionGraphTest {
// Validate and execute the action
//
ComponentProvider provider = getProvider();
ActionContext actionContext = provider.getActionContext(null);
boolean isEnabled = copyAction.isEnabledForContext(actionContext);
debugAction(copyAction, actionContext);
ActionContext context = createActionContext(provider);
boolean isEnabled = copyAction.isEnabledForContext(context);
debugAction(copyAction, context);
assertTrue(isEnabled);
performAction(copyAction, actionContext, true);
performAction(copyAction, context, true);
Transferable contents = systemClipboard.getContents(systemClipboard);
assertNotNull(contents);

View File

@@ -109,7 +109,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
* Whether to ensure the focused vertex is visible, scrolling if necessary the visualization in
* order to center the selected vertex or the center of the set of selected vertices
*/
private boolean ensureVertexIsVisible = false;
private boolean ensureVertexIsVisible = true;
/**
* Allows selection of various {@link LayoutAlgorithm} ('arrangements')
@@ -314,21 +314,17 @@ public class DefaultGraphDisplay implements GraphDisplay {
new ToggleActionBuilder("Scroll To Selection", ACTION_OWNER)
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.description("Ensure that the 'focused' vertex is visible")
.selected(true)
.onAction(context -> ensureVertexIsVisible =
((AbstractButton) context.getSourceObject()).isSelected())
.selected(ensureVertexIsVisible)
.onAction(context -> ensureVertexIsVisible = !ensureVertexIsVisible)
.buildAndInstallLocal(componentProvider);
this.ensureVertexIsVisible = true; // since we initialized action to selected
// create a toggle for enabling 'free-form' selection: selection is inside of a traced
// shape instead of a rectangle
new ToggleActionBuilder("Free-Form Selection", ACTION_OWNER)
.toolBarIcon(DefaultDisplayGraphIcons.LASSO_ICON)
.description("Trace Free-Form Shape to select multiple vertices (CTRL-click-drag)")
.selected(false)
.onAction(context -> freeFormSelection =
((AbstractButton) context.getSourceObject()).isSelected())
.selected(freeFormSelection)
.onAction(context -> freeFormSelection = !freeFormSelection)
.buildAndInstallLocal(componentProvider);
// create an icon button to display the satellite view
@@ -349,8 +345,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", ACTION_OWNER)
.description("Show View Magnifier")
.toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON)
.onAction(context -> magnifyViewSupport
.activate(((AbstractButton) context.getSourceObject()).isSelected()))
.onAction(context -> {
boolean isActive = magnifyViewSupport.isActive();
magnifyViewSupport.activate(!isActive);
})
.build();
magnifyViewSupport.addItemListener(
itemEvent -> lensToggle.setSelected(itemEvent.getStateChange() == ItemEvent.SELECTED));
@@ -749,9 +747,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
}
private void toggleSatellite(ActionContext context) {
boolean selected = ((AbstractButton) context.getSourceObject()).isSelected();
graphDisplayProvider.setDefaultSatelliteState(selected);
if (selected) {
boolean wasSelected = graphDisplayProvider.getDefaultSatelliteState();
boolean isSelected = !wasSelected;
graphDisplayProvider.setDefaultSatelliteState(isSelected);
if (isSelected) {
viewer.getComponent().add(satelliteViewer.getComponent());
satelliteViewer.scaleToLayout();
}

View File

@@ -468,11 +468,13 @@ public class DiffTest extends DiffTestAdapter {
JTree tree = getProgramTree();
selectTreeNodeByText(tree, ".data");
runSwing(() -> setView.actionPerformed(programTreeProvider.getActionContext(null)));
ActionContext context1 = createActionContext(programTreeProvider);
runSwing(() -> setView.actionPerformed(context1));
selectTreeNodeByText(tree, ".rsrc");
runSwing(() -> goToView.actionPerformed(programTreeProvider.getActionContext(null)));
ActionContext context2 = createActionContext(programTreeProvider);
runSwing(() -> goToView.actionPerformed(context2));
topOfFile(fp1);
assertEquals(addr("1008000"), cb.getCurrentAddress());
@@ -536,7 +538,7 @@ public class DiffTest extends DiffTestAdapter {
openDiff(diffTestP1, diffTestP2);
JTree tree = getProgramTree();
selectTreeNodeByText(tree, "DiffTestPgm1");
ActionContext context = runSwing(() -> programTreeProvider.getActionContext(null));
ActionContext context = runSwing(() -> createActionContext(programTreeProvider));
performAction(removeView, context, true);
AddressSet viewSet = new AddressSet();
assertEquals(viewSet, cb.getView());

View File

@@ -511,7 +511,7 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
}
protected void setView() {
ActionContext context = runSwing(() -> programTreeProvider.getActionContext(null));
ActionContext context = createActionContext(programTreeProvider);
performAction(setView, context, true);
}

View File

@@ -399,7 +399,7 @@ public class DualProgramTest extends DiffTestAdapter {
setView();
selectTreeNodeByText(tree, ".rsrc");
ActionContext context = runSwing(() -> programTreeProvider.getActionContext(null));
ActionContext context = runSwing(() -> createActionContext(programTreeProvider));
performAction(goToView, context, true);
topOfFile(fp1);
@@ -413,7 +413,7 @@ public class DualProgramTest extends DiffTestAdapter {
openSecondProgram(diffTestP1, diffTestP2);
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
selectTreeNodeByText(tree, "DiffTestPgm1");
ActionContext context = runSwing(() -> programTreeProvider.getActionContext(null));
ActionContext context = runSwing(() -> createActionContext(programTreeProvider));
performAction(removeView, context, true);
topOfFile(fp1);
assertNull(cb.getCurrentAddress());

View File

@@ -279,6 +279,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
vtListingContext.setCodeComparisonPanel(dualListingProvider);
vtListingContext.setContextObject(dualListingProvider);
vtListingContext.setSourceObject(source);
vtListingContext.setContextProvider(this);
return vtListingContext;
}

View File

@@ -512,6 +512,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
vtListingContext.setCodeComparisonPanel(listingView);
vtListingContext.setContextObject(listingView);
vtListingContext.setSourceObject(source);
vtListingContext.setContextProvider(this);
return vtListingContext;
}
}

View File

@@ -18,6 +18,7 @@ package docking;
import java.awt.Component;
import java.awt.event.*;
import docking.action.ActionContextProvider;
import docking.action.DockingActionIf;
/**
@@ -49,9 +50,9 @@ import docking.action.DockingActionIf;
* {@link DockingActionIf}, again using a possible default context if the active context isn't valid
* for that action. Ultimately, context serves to manage actions and to
* allow plugins to share state with actions without them being directly coupled together.
*
* <P>
* {@link ComponentProvider}s are required to return ActionContext objects in their
* {@link ComponentProvider}s can choose to return ActionContext objects in their
* {@link ComponentProvider#getActionContext(MouseEvent)} methods. Generally, ComponentProviders
* have two ways to use this class. They can either create an {@link DefaultActionContext} instance
* and pass in a contextObject that will be useful to its actions or, subclass the ActionContext
@@ -64,7 +65,8 @@ import docking.action.DockingActionIf;
*
* <ul>
* <li><b>provider</b> - the component provider to which this context belongs; the provider that
* contains the component that is the source of the user action
* contains the component that is the source of the user action. This value
* is client-defined, typically at construction time.
* </li>
* <li><b>contextObject</b> - client-defined data object. This allows clients to save any
* information desired to be used when the action is performed.
@@ -82,7 +84,14 @@ import docking.action.DockingActionIf;
* will not change between
* {@link DockingActionIf#isEnabledForContext(ActionContext) enablement}
* and {@link DockingActionIf#actionPerformed(ActionContext) execution}.
* This value is set by the framework.
* </li>
* <li><b>contextProvider</b> - the {@link ActionContextProvider} that created the context. This
* will be null in the case that a default context was created. When
* not null this will typically be a {@link ComponentProvider} or a
* {@link DialogComponentProvider}. This value is set by the
* framework.
* </li>
* <li><b>mouseEvent</b> - the mouse event that triggered the action; null if the action was
* triggered by a key binding.
* </li>
@@ -110,12 +119,6 @@ public interface ActionContext {
*/
public ActionContext setContextObject(Object contextObject);
/**
* Returns the sourceObject from the actionEvent that triggered this context to be generated.
* @return the sourceObject from the actionEvent that triggered this context to be generated.
*/
public Object getSourceObject();
/**
* Sets the modifiers for this event that were present when the item was clicked on.
*
@@ -145,14 +148,35 @@ public interface ActionContext {
/**
* Sets the sourceObject for this ActionContext. This method is used internally by the
* DockingWindowManager. ComponentProvider and action developers should only use this
* method for testing.
* framework. ComponentProvider and action developers should only use this method for testing.
*
* @param sourceObject the source object
* @return this context
*/
public ActionContext setSourceObject(Object sourceObject);
/**
* Returns the sourceObject from the actionEvent that triggered this context to be generated.
* The value returned will typically be the clicked component for mouse events and the focused
* component for key binding events.
* @return the sourceObject; may be null.
*/
public Object getSourceObject();
/**
* Sets the context provider for this ActionContext. This method is used internally by the
* framework.
* @param provider the context provider
* @return this context
*/
public ActionContext setContextProvider(ActionContextProvider provider);
/**
* Returns the context provider used to create this context. May be null.
* @return the context provider
*/
public ActionContextProvider getContextProvider();
/**
* Updates the context's mouse event. Contexts that are based upon key events will have no
* mouse event. This method is really for the framework to use. Client calls to this

View File

@@ -544,13 +544,16 @@ public class ComponentPlaceholder {
return; // disposed
}
ActionContext actionContext = componentProvider.getActionContext(null);
if (actionContext == null) {
actionContext = new DefaultActionContext(componentProvider, null);
ActionContext context = componentProvider.getActionContext(null);
if (context == null) {
context = new DefaultActionContext(componentProvider, null);
}
for (DockingActionIf action : actions) {
action.setEnabled(
action.isValidContext(actionContext) && action.isEnabledForContext(actionContext));
context.setContextProvider(componentProvider);
for (DockingActionIf a : actions) {
boolean enabled = a.isValidContext(context) && a.isEnabledForContext(context);
a.setEnabled(enabled);
}
}

View File

@@ -487,42 +487,68 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
}
/**
* Returns the context object which corresponds to the
* area of focus within this provider's component. Null
* is returned when there is no context.
* @param event popup event which corresponds to this request.
* May be null for key-stroke or other non-mouse event.
* Returns the context object which corresponds to the area of focus within this provider's
* component. Null is returned when there is no context.
* <p>
* Subclasses should override this method to provider more specific context objects or
* information.
*
* @param event popup event which corresponds to this request. Will be null for key-stroke or
* other non-mouse uses.
*/
@Override
public ActionContext getActionContext(MouseEvent event) {
Component c = getContextSourceComponent();
// Note: this call is deprecated. It shall remain here to handle cases where the subclasses
// have overridden createContext(). Eventually we will remove this call and create the
// default context directly.
return createContext(c, null);
}
/**
* Returns a component to use as the {@code sourceComponent} when creating an action context.
* The focused component is preferred when it is inside of this provider's
* {@link #getComponent() component}. Otherwise, this provider's component is returned.
*
* @return the component
*/
protected Component getContextSourceComponent() {
Component c = getComponent();
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component focusedComponent = kfm.getFocusOwner();
if (focusedComponent != null && SwingUtilities.isDescendingFrom(focusedComponent, c)) {
c = focusedComponent;
}
return createContext(c, null);
return c;
}
/**
* A default method for creating an action context for this provider, using the given
* {@link ActionContext#getContextObject() context object}. If the given context object is a
* component, then it will be used to initialize the {@code sourceComponent} of the created
* context.
*
* @param contextObject the provider-specific context object
* @return the new context
* @deprecated instead use
* {@code new DefaultActionContext(ComponentProvider.this).setContextObject(contextObject)}
*/
@Deprecated(since = "12.2", forRemoval = true)
protected ActionContext createContext(Object contextObject) {
return new DefaultActionContext(this).setContextObject(contextObject);
}
/**
* A default method for creating an action context for this provider
* @return the new context
* @deprecated instead use {@code new DefaultActionContext(ComponentProvider.this)}
*/
@Deprecated(since = "12.2", forRemoval = true)
protected ActionContext createContext() {
return new DefaultActionContext(this);
}
/**
* A default method for creating an action context for this provider, using the given
* {@link ActionContext#getContextObject() context object}
*
* @param contextObject the provider-specific context object
* @return the new context
*/
protected ActionContext createContext(Object contextObject) {
return new DefaultActionContext(this).setContextObject(contextObject);
}
/**
* A default method for creating an action context for this provider, using the given
* {@link ActionContext#getContextObject() context object} and component
@@ -530,7 +556,11 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
* @param sourceComponent the component that is the target of the context being created
* @param contextObject the provider-specific context object
* @return the new context
* @deprecated this method is still called from {@link #getActionContext(MouseEvent)}, but this
* will change in a future release. Clients calling this method can replace that call with
* {@code new DefaultActionContext(this, sourceComponent).setContextObject(contextObject)}.
*/
@Deprecated(since = "12.2", forRemoval = true)
protected ActionContext createContext(Component sourceComponent, Object contextObject) {
return new DefaultActionContext(this, sourceComponent).setContextObject(contextObject);
}

View File

@@ -18,6 +18,8 @@ package docking;
import java.awt.Component;
import java.awt.event.MouseEvent;
import docking.action.ActionContextProvider;
/**
* The default implementation of ActionContext
*/
@@ -33,6 +35,10 @@ public class DefaultActionContext implements ActionContext {
// has not already been set.
private Component sourceComponent;
// Initialized by the framework. This will be null if clients create their own context outside
// of the framework and do not set the provider.
private ActionContextProvider contextProvider;
/**
* Default constructor with no provider, context object, or source component
*/
@@ -118,11 +124,6 @@ public class DefaultActionContext implements ActionContext {
return this;
}
@Override
public Object getSourceObject() {
return sourceObject;
}
@Override
public void setEventClickModifiers(int modifiers) {
this.eventClickModifiers = modifiers;
@@ -138,12 +139,28 @@ public class DefaultActionContext implements ActionContext {
return (eventClickModifiers & modifiersMask) != 0;
}
@Override
public Object getSourceObject() {
return sourceObject;
}
@Override
public DefaultActionContext setSourceObject(Object sourceObject) {
this.sourceObject = sourceObject;
return this;
}
@Override
public ActionContext setContextProvider(ActionContextProvider provider) {
this.contextProvider = provider;
return this;
}
@Override
public ActionContextProvider getContextProvider() {
return contextProvider;
}
@Override
public DefaultActionContext setMouseEvent(MouseEvent e) {
if (e != null) {

View File

@@ -16,30 +16,24 @@
package docking;
import java.awt.Component;
import java.util.Objects;
import docking.action.ActionContextProvider;
/**
* Action context for {@link DialogComponentProvider}s.
* <p>
* Note: due to context changes related to the addition of
* {@link #setContextProvider(ActionContextProvider)}, this class serves no real purpose at the time
* this comment was made.
*/
public class DialogActionContext extends DefaultActionContext {
private DialogComponentProvider dialogProvider;
public DialogActionContext(DialogComponentProvider dialogProvider, Component sourceComponent) {
super(null, dialogProvider, sourceComponent);
this.dialogProvider = Objects.requireNonNull(dialogProvider);
}
// this constructor allows clients to set the dialog later
// An unusual constructor for when clients don't yet have a dialog in hand
public DialogActionContext(Object contextObject, Component sourceComponent) {
super(null, contextObject, sourceComponent);
}
public void setDialogComponentProvider(DialogComponentProvider dialogProvider) {
this.dialogProvider = dialogProvider;
}
public DialogComponentProvider getDialogComponentProvider() {
return dialogProvider;
}
}

View File

@@ -198,10 +198,9 @@ public class DialogComponentProvider
DockingAction closeAction = new ActionBuilder(CLOSE_ACTION_NAME, owner)
.sharedKeyBinding()
.keyBinding(ESC_KEYSTROKE)
.withContext(DialogActionContext.class)
.enabledWhen(c -> c.getDialogComponentProvider() != null)
.enabledWhen(c -> c.getContextProvider() instanceof DialogComponentProvider)
.onAction(c -> {
DialogComponentProvider dcp = c.getDialogComponentProvider();
DialogComponentProvider dcp = (DialogComponentProvider) c.getContextProvider();
dcp.escapeCallback();
})
.build();
@@ -1253,7 +1252,7 @@ public class DialogComponentProvider
/**
* An optional extension point for subclasses to provider action context for the actions used by
* this provider.
*
*
* @param event The mouse event used (may be null) to generate a popup menu
*/
@Override
@@ -1274,7 +1273,10 @@ public class DialogComponentProvider
if (sourceComponent != null) {
c = sourceComponent;
}
return new DialogActionContext(this, c).setSourceObject(event.getSource());
DialogActionContext context = new DialogActionContext(this, c);
context.setSourceObject(event.getSource());
return context;
}
/**
@@ -1286,6 +1288,9 @@ public class DialogComponentProvider
if (context == null) {
context = new DefaultActionContext();
}
context.setContextProvider(this);
Set<DockingActionIf> keySet = toolbarButtonsByAction.keySet();
for (DockingActionIf action : keySet) {
action.setEnabled(action.isEnabledForContext(context));
@@ -1460,8 +1465,11 @@ public class DialogComponentProvider
@Override
public void popupTriggered(MouseEvent e) {
ActionContext actionContext = getActionContext(e);
popupManager.popupMenu(actionContext, e);
ActionContext context = getActionContext(e);
if (context != null) {
context.setContextProvider(DialogComponentProvider.this);
}
popupManager.popupMenu(context, e);
}
@Override
@@ -1500,15 +1508,8 @@ public class DialogComponentProvider
@Override
public boolean isEnabledForContext(ActionContext context) {
if (context instanceof DialogActionContext dialogContext) {
DialogComponentProvider contextProvider =
dialogContext.getDialogComponentProvider();
if (provider != contextProvider) {
return false;
}
return dockingAction.isEnabledForContext(context);
}
return false;
ActionContextProvider contextProvider = context.getContextProvider();
return provider == contextProvider;
}
}
}

View File

@@ -62,8 +62,7 @@ public class DialogComponentProviderPopupActionManager {
actionContext = new DefaultActionContext();
}
// If the source is null, must set it or we won't have
// any popups shown.
// If the source is null, must set it or we won't have any popups shown.
if (actionContext.getSourceObject() == null) {
actionContext.setSourceObject(e.getSource());
}

View File

@@ -57,12 +57,13 @@ public abstract class DockingKeyBindingAction extends AbstractAction {
return new DefaultActionContext();
}
ActionContext actionContext = localProvider.getActionContext(null);
if (actionContext != null) {
return actionContext;
ActionContext context = localProvider.getActionContext(null);
if (context == null) {
context = new DefaultActionContext(localProvider);
}
return new DefaultActionContext(localProvider, null);
context.setContextProvider(localProvider);
return context;
}
public List<DockingActionIf> getActions() {

View File

@@ -2516,10 +2516,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
*/
public ActionContext getDefaultActionContext(Class<? extends ActionContext> contextType) {
ActionContextProvider actionContextProvider = defaultContextProviderMap.get(contextType);
if (actionContextProvider != null) {
return actionContextProvider.getActionContext(null);
if (actionContextProvider == null) {
return null;
}
return null;
ActionContext context = actionContextProvider.getActionContext(null);
if (context != null) {
context.setContextProvider(actionContextProvider);
}
return context;
}
/**
@@ -2532,7 +2538,13 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
defaultContextProviderMap.entrySet();
for (Entry<Class<? extends ActionContext>, ActionContextProvider> entry : entrySet) {
contextMap.put(entry.getKey(), entry.getValue().getActionContext(null));
Class<? extends ActionContext> clazz = entry.getKey();
ActionContextProvider provider = entry.getValue();
ActionContext context = provider.getActionContext(null);
if (context != null) {
context.setContextProvider(provider);
}
contextMap.put(clazz, context);
}
return contextMap;
}
@@ -2549,8 +2561,11 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
public ActionContext createActionContext(DockingActionIf action) {
ComponentProvider provider = getActiveComponentProvider();
ActionContext context = provider == null ? null : provider.getActionContext(null);
if (context != null && action.isValidContext(context)) {
return context;
if (context != null) {
context.setContextProvider(provider);
if (action.isValidContext(context)) {
return context;
}
}
// Some actions work on a non-active, default component provider. See if this action
@@ -2574,10 +2589,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
private ActionContext getDefaultContext(Class<? extends ActionContext> contextType) {
ActionContextProvider contextProvider = defaultContextProviderMap.get(contextType);
if (contextProvider != null) {
return contextProvider.getActionContext(null);
if (contextProvider == null) {
return null;
}
return null;
ActionContext context = contextProvider.getActionContext(null);
if (context != null) {
context.setContextProvider(contextProvider);
}
return context;
}
/**

View File

@@ -218,6 +218,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
if (provider != null) {
ActionContext context = provider.getActionContext(null);
if (context != null) {
context.setContextProvider(provider);
return context;
}
}
@@ -231,17 +232,22 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener {
return new DefaultActionContext();
}
ComponentProvider provider = null;
ActionContext context = null;
ComponentPlaceholder placeholder = windowNode.getLastFocusedProviderInWindow();
if (placeholder != null) {
ComponentProvider provider = placeholder.getProvider();
provider = placeholder.getProvider();
if (provider != null) {
context = provider.getActionContext(null);
}
}
if (context == null) {
context = new DefaultActionContext();
}
context.setContextProvider(provider);
return context;
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -26,7 +26,8 @@ import javax.swing.JPopupMenu;
import org.apache.commons.collections4.IteratorUtils;
import docking.action.*;
import docking.action.DockingActionIf;
import docking.action.MenuData;
import docking.menu.*;
public class PopupActionManager implements PropertyChangeListener {
@@ -77,6 +78,7 @@ public class PopupActionManager implements PropertyChangeListener {
actionContext = new DefaultActionContext();
}
actionContext.setContextProvider(popupProvider);
actionContext.setSourceObject(popupContext.getSource());
actionContext.setMouseEvent(event);
@@ -115,9 +117,6 @@ public class PopupActionManager implements PropertyChangeListener {
void populatePopupMenuActions(Iterator<DockingActionIf> localActions,
ActionContext actionContext, MenuManager menuMgr) {
// Unregistered actions are those used by special-needs components, on-the-fly
addUnregisteredActions(actionContext, menuMgr);
// Include temporary actions
List<DockingActionIf> tempActions = windowManager.getTemporaryPopupActions(actionContext);
if (tempActions != null) {
@@ -131,11 +130,7 @@ public class PopupActionManager implements PropertyChangeListener {
}
}
// Include global actions
Iterator<DockingActionIf> iter = popupActions.iterator();
while (iter.hasNext()) {
DockingActionIf action = iter.next();
for (DockingActionIf action : popupActions) {
MenuData popupMenuData = action.getPopupMenuData();
if (popupMenuData != null && action.isValidContext(actionContext) &&
action.isAddToPopup(actionContext)) {
@@ -157,25 +152,6 @@ public class PopupActionManager implements PropertyChangeListener {
}
}
private void addUnregisteredActions(ActionContext actionContext, MenuManager menuMgr) {
Object source = actionContext.getSourceObject();
// this interface is deprecated in favor the code that calls this method; this will be deleted
if (source instanceof DockingActionProviderIf) {
DockingActionProviderIf actionProvider = (DockingActionProviderIf) source;
List<DockingActionIf> dockingActions = actionProvider.getDockingActions();
for (DockingActionIf action : dockingActions) {
MenuData popupMenuData = action.getPopupMenuData();
if (popupMenuData != null && action.isValidContext(actionContext) &&
action.isAddToPopup(actionContext)) {
action.setEnabled(action.isEnabledForContext(actionContext));
menuMgr.addAction(action);
}
}
}
}
private boolean isRemovingFromPopup(MenuData oldData, MenuData newData) {
return oldData != null && newData == null;
}

View File

@@ -1,43 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.action;
import java.util.List;
import docking.Tool;
/**
* An interface for objects (really Components) to implement that signals they provide actions
* for the Docking environment. This interface will be called when the implementor is the source
* of a Java event, like a MouseEvent.
* <p>
* As an example, a JTable that wishes to provide popup menu actions can implement this interface.
* When the user right-clicks on said table, then Docking system will ask this object for its
* actions. Further, in this example, the actions given will be inserted into the popup menu
* that is shown.
*
* @deprecated use {@link Tool}
*/
// Note: this API is not likely used by forward-facing clients and can be removed in the next release
@Deprecated(since = "9.1", forRemoval = true)
public interface DockingActionProviderIf {
/**
* Returns actions that are compatible with the given context.
* @return the actions
*/
public List<DockingActionIf> getDockingActions();
}

View File

@@ -352,6 +352,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return multiAction;
}
context.setContextProvider(provider);
/*
See the note in createNonDialogExecutableAction().
*/

View File

@@ -46,7 +46,9 @@ public class ShowActionChooserDialogAction extends DockingAction {
Tool tool = DockingWindowManager.getActiveInstance().getTool();
if (focusedWindow instanceof DockingDialog dialog) {
context = dialog.getDialogComponent().getActionContext(null);
DialogComponentProvider provider = dialog.getDialogComponent();
context = provider.getActionContext(null);
context.setContextProvider(provider);
showActionsDialog(tool, dialog, context);
}
else if (focusedWindow instanceof DockingFrame dockingFrame) {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -120,11 +120,15 @@ public class ActionAdapter implements Action, PropertyChangeListener {
ActionContext context = null;
if (contextProvider != null) {
context = contextProvider.getActionContext(null);
context.setContextProvider(contextProvider);
}
if (context == null) {
context = new DefaultActionContext();
context.setSourceObject(e.getSource());
}
context.setSourceObject(e.getSource());
if (dockingAction.isEnabledForContext(context)) {
dockingAction.actionPerformed(context);
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -18,8 +18,7 @@ package docking.menu;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import docking.DockingWindowManager;
import docking.EmptyBorderToggleButton;
import docking.*;
import docking.action.*;
import ghidra.util.Swing;
@@ -64,7 +63,17 @@ public class DialogToolbarButton extends EmptyBorderToggleButton {
}
// Give the Swing thread a chance to repaint
Swing.runLater(() -> dockingAction.actionPerformed(contextProvider.getActionContext(null)));
Swing.runLater(() -> {
ActionContext context = contextProvider.getActionContext(null);
if (context == null) {
context = new DefaultActionContext();
}
context.setSourceObject(e.getSource());
context.setContextProvider(contextProvider);
dockingAction.actionPerformed(context);
});
}
@Override

View File

@@ -27,7 +27,6 @@ import javax.swing.border.CompoundBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import docking.*;
import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.util.Swing;
@@ -198,21 +197,6 @@ public class MultiStateButton<T> extends JButton {
addMouseListener(popupListener);
}
protected ActionContext getActionContext() {
ComponentProvider provider = getComponentProvider();
ActionContext context = provider == null ? null : provider.getActionContext(null);
final ActionContext actionContext = context == null ? new DefaultActionContext() : context;
return actionContext;
}
private ComponentProvider getComponentProvider() {
DockingWindowManager manager = DockingWindowManager.getActiveInstance();
if (manager == null) {
return null;
}
return manager.getActiveComponentProvider();
}
/**
* Show a popup containing all the actions below the button
*

View File

@@ -4,9 +4,9 @@
* 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.
@@ -134,9 +134,17 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
protected ActionContext getActionContext() {
ComponentProvider provider = getComponentProvider();
ActionContext context = provider == null ? null : provider.getActionContext(null);
final ActionContext actionContext = context == null ? new DefaultActionContext() : context;
return actionContext;
ActionContext context = null;
if (provider != null) {
context = provider.getActionContext(null);
}
if (context == null) {
context = new DefaultActionContext();
}
context.setContextProvider(provider);
return context;
}
private ComponentProvider getComponentProvider() {
@@ -198,7 +206,7 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
// a custom Ghidra UI that handles alignment issues and allows for tabulating presentation
item.setUI(DockingMenuItemUI.createUI(item));
final DockingActionIf delegateAction = dockingAction;
DockingActionIf delegateAction = dockingAction;
item.addActionListener(e -> {
ActionContext context = getActionContext();
context.setSourceObject(e.getSource());

View File

@@ -36,6 +36,7 @@ import javax.swing.text.JTextComponent;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.junit.*;
import docking.*;
@@ -351,19 +352,19 @@ public abstract class AbstractDockingTest extends AbstractGuiTest {
}
String title = dialog.getTitle();
boolean isSavePrompt = StringUtils.containsAny(title, "Changed", "Saved");
boolean isSavePrompt = Strings.CS.containsAny(title, "Changed", "Saved");
if (!isSavePrompt) {
throw new AssertionError("Unexpected dialog with title '" + title + "'; " +
"Expected a dialog alerting to program changes");
}
if (StringUtils.contains(title, "Program Changed")) {
if (Strings.CS.contains(title, "Program Changed")) {
// the program is read-only or not in a writable project
pressButtonByText(dialog, "Continue");
return;
}
if (StringUtils.contains(title, "Save Program?")) {
if (Strings.CS.contains(title, "Save Program?")) {
pressButtonByText(dialog, "Cancel");
return;
}
@@ -1283,6 +1284,7 @@ public abstract class AbstractDockingTest extends AbstractGuiTest {
ActionContext providerContext = provider.getActionContext(null);
if (providerContext != null) {
providerContext.setContextProvider(provider);
return providerContext;
}
@@ -1338,11 +1340,13 @@ public abstract class AbstractDockingTest extends AbstractGuiTest {
ActionContext newContext = provider.getActionContext(null);
if (newContext == null) {
actionContext.setContextProvider(provider);
return actionContext;
}
actionContext = newContext;
actionContext.setSourceObject(provider.getComponent());
actionContext.setContextProvider(provider);
return actionContext;
});
@@ -1366,6 +1370,7 @@ public abstract class AbstractDockingTest extends AbstractGuiTest {
ActionContext actionContext = provider.getActionContext(null);
if (actionContext != null) {
actionContext.setSourceObject(provider.getComponent());
actionContext.setContextProvider(provider);
}
return actionContext;
});
@@ -2183,7 +2188,17 @@ public abstract class AbstractDockingTest extends AbstractGuiTest {
}
public static boolean isEnabled(DockingActionIf action, ActionContextProvider contextProvider) {
return runSwing(() -> action.isEnabledForContext(contextProvider.getActionContext(null)));
return runSwing(() -> action.isEnabledForContext(createActionContext(contextProvider)));
}
public static ActionContext createActionContext(ActionContextProvider provider) {
return runSwing(() -> {
ActionContext context = provider.getActionContext(null);
if (context != null) {
context.setContextProvider(provider);
}
return context;
});
}
public static boolean isEnabled(AbstractButton button) {

View File

@@ -98,9 +98,9 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
public void testDefaultSettings() {
positionListingTop(0x40d3a4);
ComponentProvider componentProvider = getProvider(CodeViewerProvider.class);
ActionContext actionContext = componentProvider.getActionContext(null);
DockingActionIf action = getAction("Default Settings", actionContext);
performAction(action, actionContext, false);
ActionContext context = createActionContext(componentProvider);
DockingActionIf action = getAction("Default Settings", context);
performAction(action, context, false);
captureDialog();
}

View File

@@ -1450,7 +1450,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
private ActionContext getActionContext(ComponentProviderWrapper wrapper) {
return runSwing(() -> {
ComponentProvider provider = wrapper.getComponentProvider();
ActionContext context = provider.getActionContext(null);
ActionContext context = createActionContext(provider);
return context;
});
}
@@ -1622,7 +1622,7 @@ public class ClipboardPluginTest extends AbstractGhidraHeadedIntegrationTest {
@Override
public ActionContext getContext() {
return provider.getActionContext(null);
return createActionContext(provider);
}
@Override

View File

@@ -320,7 +320,7 @@ public class FrontEndPluginOpenProgramActionsTest extends AbstractGhidraHeadedIn
private ActionContext getFrontEndContext() {
ComponentProvider provider = env.getFrontEndProvider();
return runSwing(() -> provider.getActionContext(null));
return createActionContext(provider);
}
private DomainFile openInDefaultTool(String fileName) throws Exception {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -302,7 +302,7 @@ public class FrontEndTestEnv {
DockingActionIf terminateCheckoutAction =
AbstractDockingTest.getAction(provider, "Terminate Checkout");
ActionContext context = provider.getActionContext(null);
ActionContext context = AbstractDockingTest.createActionContext(provider);
AbstractDockingTest.performAction(terminateCheckoutAction, context, false);
OptionDialog optDialog = AbstractDockingTest.waitForDialogComponent(OptionDialog.class);
AbstractGuiTest.pressButtonByText(optDialog.getComponent(), "Yes", true);
@@ -341,7 +341,7 @@ public class FrontEndTestEnv {
public void performFrontEndAction(DockingActionIf action) {
ComponentProvider provider = env.getFrontEndProvider();
runSwing(() -> {
ActionContext context = provider.getActionContext(null);
ActionContext context = AbstractDockingTest.createActionContext(provider);
action.actionPerformed(context);
}, false);
waitForSwing();