diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerTest.java index 9c8e31cbfa..18c2827ab3 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerTest.java @@ -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; } diff --git a/Ghidra/Extensions/sample/src/test.slow/java/ghidra/examples/graph/SampleGraphPluginTest.java b/Ghidra/Extensions/sample/src/test.slow/java/ghidra/examples/graph/SampleGraphPluginTest.java index 1e5687c269..de66863678 100644 --- a/Ghidra/Extensions/sample/src/test.slow/java/ghidra/examples/graph/SampleGraphPluginTest.java +++ b/Ghidra/Extensions/sample/src/test.slow/java/ghidra/examples/graph/SampleGraphPluginTest.java @@ -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"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramActionContext.java index 5fd9cb90e7..7587e5d2a3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramActionContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/context/ProgramActionContext.java @@ -43,6 +43,7 @@ public class ProgramActionContext extends DefaultActionContext { if (sourceComponent == null) { KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); setSourceObject(kfm.getFocusOwner()); + setContextProvider(provider); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManagerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManagerProvider.java index 0edd9768c7..a5cfd98f65 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManagerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManagerProvider.java @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/tool/ListingMergePanelProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/tool/ListingMergePanelProvider.java index 59f39bc4c5..64ad8c1354 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/tool/ListingMergePanelProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/tool/ListingMergePanelProvider.java @@ -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() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java index 579a543eef..8cdf4166c9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeViewerProvider.java @@ -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) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java index 04a45aabd7..a4389f04d0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java @@ -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)} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java index 9bd994279f..647509e354 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/propertymanager/PropertyManagerProvider.java @@ -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); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java index cb97ae40fd..40406c8636 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java @@ -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 choices = new ArrayList(programMap.keySet()); - AskDialog dialog = new AskDialog(null, "Compare to...", "Program", AskDialog.STRING, choices, null); + AskDialog dialog = new AskDialog(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> searchResults = getSearchResults(); List> rebasedResults = new ArrayList<>(); for (MemoryMatch match : searchResults) { ProgramLocation canonicalLocation = byteSource.getCanonicalLocation(match.getAddress()); Address rebase = nextByteSource.rebaseFromCanonical(canonicalLocation); if (rebase != null) { - MemoryMatch nextMatch = new MemoryMatch<>(rebase, match.getBytes(), match.getPattern()); + MemoryMatch nextMatch = + new MemoryMatch<>(rebase, match.getBytes(), match.getPattern()); rebasedResults.add(nextMatch); } } - + MemorySearchResultsPanel nextResultsPanel = nextProvider.getResultsPanel(); nextProvider.setBusy(true); nextResultsPanel.refreshAndMaybeScanForChanges(nextByteSource, scanner, rebasedResults); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/AbstractDataTreeDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/AbstractDataTreeDialog.java index 389f1bc862..f2f042ebf5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/AbstractDataTreeDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/AbstractDataTreeDialog.java @@ -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() { diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java index 2c24b53ada..08e040c5ac 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java @@ -352,7 +352,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio PlaceholderManager pm = dwm.getPlaceholderManager(); Set 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 } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/clipboard/CopyPasteCommentsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/clipboard/CopyPasteCommentsTest.java index fda4ea7025..8b84134ca2 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/clipboard/CopyPasteCommentsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/clipboard/CopyPasteCommentsTest.java @@ -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); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java index 498ec16617..14de6fa2e3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogTest.java @@ -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 diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin2Test.java index f3353cfc83..50ae13b2b4 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesPlugin2Test.java @@ -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)); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java index aa3ba83861..c264487d2f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java @@ -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(); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java index 45ec6eef73..78bd459345 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java @@ -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))); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/features/base/replace/SearchAndReplaceDialogTest.java b/Ghidra/Features/Base/src/test/java/ghidra/features/base/replace/SearchAndReplaceDialogTest.java index b34b9b0a4a..6fc5ab5648 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/features/base/replace/SearchAndReplaceDialogTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/features/base/replace/SearchAndReplaceDialogTest.java @@ -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); } diff --git a/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin1Test.java b/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin1Test.java index 35be292c71..8bac917bf3 100644 --- a/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin1Test.java +++ b/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/FunctionGraphPlugin1Test.java @@ -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); diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java index 662373d945..59954c39e0 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/DefaultGraphDisplay.java @@ -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(); } diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java index 9dd0681895..d0ea41be35 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTest.java @@ -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()); diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java index 7c482ca6c4..5f932bc93e 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DiffTestAdapter.java @@ -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); } diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DualProgramTest.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DualProgramTest.java index c1876e50cc..487417bc36 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DualProgramTest.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/DualProgramTest.java @@ -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()); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java index a9de9bdded..154378aeef 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/functionassociation/VTFunctionAssociationProvider.java @@ -279,6 +279,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter vtListingContext.setCodeComparisonPanel(dualListingProvider); vtListingContext.setContextObject(dualListingProvider); vtListingContext.setSourceObject(source); + vtListingContext.setContextProvider(this); return vtListingContext; } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java index d1af28507d..75dfd9a4b4 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/markuptable/VTMarkupItemsTableProvider.java @@ -512,6 +512,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter vtListingContext.setCodeComparisonPanel(listingView); vtListingContext.setContextObject(listingView); vtListingContext.setSourceObject(source); + vtListingContext.setContextProvider(this); return vtListingContext; } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ActionContext.java b/Ghidra/Framework/Docking/src/main/java/docking/ActionContext.java index 519a74f5f7..76234d1554 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ActionContext.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ActionContext.java @@ -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. - + * *

- * {@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; * *

    *
  • provider - 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. *
  • *
  • contextObject - 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. *
  • + *
  • contextProvider - 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. + *
  • *
  • mouseEvent - the mouse event that triggered the action; null if the action was * triggered by a key binding. *
  • @@ -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 diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ComponentPlaceholder.java b/Ghidra/Framework/Docking/src/main/java/docking/ComponentPlaceholder.java index 90fcfd74ba..e9329f8f68 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentPlaceholder.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentPlaceholder.java @@ -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); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java index b1b9adcbd4..84db78106c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java @@ -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. + *

    + * 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); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DefaultActionContext.java b/Ghidra/Framework/Docking/src/main/java/docking/DefaultActionContext.java index 1e5c7e6c66..e4868a508c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DefaultActionContext.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DefaultActionContext.java @@ -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) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogActionContext.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogActionContext.java index 7768f9aafb..5f5d800e31 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogActionContext.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogActionContext.java @@ -16,30 +16,24 @@ package docking; import java.awt.Component; -import java.util.Objects; + +import docking.action.ActionContextProvider; /** * Action context for {@link DialogComponentProvider}s. + *

    + * 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; - } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java index 9580d0a34f..ae45829b42 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java @@ -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 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; } } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java index 77759312be..85e570b773 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java @@ -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()); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingKeyBindingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingKeyBindingAction.java index 3b6f4edfc2..1394c15b67 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingKeyBindingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingKeyBindingAction.java @@ -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 getActions() { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 163a6b6130..77a70bfb17 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -2516,10 +2516,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder */ public ActionContext getDefaultActionContext(Class 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, ActionContextProvider> entry : entrySet) { - contextMap.put(entry.getKey(), entry.getValue().getActionContext(null)); + Class 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 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; } /** diff --git a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java index 4b64f2f064..4fe9a0c6f0 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java @@ -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; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java index 28baee2725..e42f6c4549 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java @@ -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 localActions, ActionContext actionContext, MenuManager menuMgr) { - // Unregistered actions are those used by special-needs components, on-the-fly - addUnregisteredActions(actionContext, menuMgr); - // Include temporary actions List tempActions = windowManager.getTemporaryPopupActions(actionContext); if (tempActions != null) { @@ -131,11 +130,7 @@ public class PopupActionManager implements PropertyChangeListener { } } - // Include global actions - Iterator 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 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; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionProviderIf.java b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionProviderIf.java deleted file mode 100644 index 5819cd9e6c..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionProviderIf.java +++ /dev/null @@ -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. - *

    - * 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 getDockingActions(); -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java index 84813716a5..6b4d99777f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/MultipleKeyAction.java @@ -352,6 +352,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction { return multiAction; } + context.setContextProvider(provider); + /* See the note in createNonDialogExecutableAction(). */ diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/ShowActionChooserDialogAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/ShowActionChooserDialogAction.java index 74f5bc0527..8626a5e192 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/ShowActionChooserDialogAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/ShowActionChooserDialogAction.java @@ -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) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionAdapter.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionAdapter.java index 9fc5f10067..63fb46e75a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionAdapter.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/ActionAdapter.java @@ -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); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java b/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java index fc508388e2..bbbefffa90 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/menu/DialogToolbarButton.java @@ -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 diff --git a/Ghidra/Framework/Docking/src/main/java/docking/menu/MultiStateButton.java b/Ghidra/Framework/Docking/src/main/java/docking/menu/MultiStateButton.java index a3c34907f2..a154c072de 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/menu/MultiStateButton.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/menu/MultiStateButton.java @@ -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 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 * diff --git a/Ghidra/Framework/Docking/src/main/java/docking/menu/MultipleActionDockingToolbarButton.java b/Ghidra/Framework/Docking/src/main/java/docking/menu/MultipleActionDockingToolbarButton.java index d7f64c9b44..07c39d18ee 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/menu/MultipleActionDockingToolbarButton.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/menu/MultipleActionDockingToolbarButton.java @@ -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()); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java b/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java index d4c7d15e52..5581efdc68 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java @@ -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) { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java index ff012ae0e0..716ffebd9d 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataPluginScreenShots.java @@ -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(); } diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/clipboard/ClipboardPluginTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/clipboard/ClipboardPluginTest.java index dfcadeee03..64bbdab59c 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/clipboard/ClipboardPluginTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/clipboard/ClipboardPluginTest.java @@ -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 diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginOpenProgramActionsTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginOpenProgramActionsTest.java index a6053e3a06..4c10beef49 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginOpenProgramActionsTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginOpenProgramActionsTest.java @@ -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 { diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java index 8fca5319fe..25aa91a073 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java @@ -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();