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 74ebc32519..e5a49ef85c 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 @@ -23,8 +23,7 @@ import java.util.List; import javax.swing.JComponent; -import docking.ActionContext; -import docking.WindowPosition; +import docking.*; import docking.action.DockingActionIf; import docking.actions.PopupActionProvider; @@ -55,7 +54,7 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter implemen } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { ListingPanel resultPanel = mergePanel.getResultPanel(); if (resultPanel != null) { return resultPanel.getHeaderActions(getName()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java index 76a67d950b..1509a330ab 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java @@ -22,6 +22,7 @@ import javax.swing.Icon; import javax.swing.SwingUtilities; import docking.ActionContext; +import docking.DockingTool; import docking.action.*; import docking.actions.PopupActionProvider; import docking.widgets.table.GTable; @@ -493,7 +494,7 @@ public class BookmarkPlugin extends ProgramPlugin } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { Object contextObject = context.getContextObject(); if (!(contextObject instanceof MarkerLocation)) { return null; 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 a5f7a7e067..009c592d12 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 @@ -952,7 +952,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { if (context.getComponentProvider() == this) { return listingPanel.getHeaderActions(getName()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java index a7be5ed583..3d6756c789 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java @@ -557,6 +557,7 @@ public abstract class CompositeEditorPanel extends JPanel setVisible(false); } model.removeCompositeEditorModelListener(this); + table.dispose(); } private void createTable() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java index a556b9f230..3d1c21d610 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java @@ -27,6 +27,7 @@ import javax.swing.SwingUtilities; import javax.swing.tree.TreePath; import docking.ActionContext; +import docking.DockingTool; import docking.action.*; import docking.actions.PopupActionProvider; import docking.widgets.tree.GTreeNode; @@ -48,7 +49,8 @@ import ghidra.framework.Application; import ghidra.framework.main.OpenVersionedFileDialog; import ghidra.framework.model.*; import ghidra.framework.options.SaveState; -import ghidra.framework.plugintool.*; +import ghidra.framework.plugintool.PluginInfo; +import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.database.DataTypeArchiveContentHandler; import ghidra.program.database.data.ProgramDataTypeManager; @@ -700,7 +702,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool dockingTool, ActionContext context) { if (!(context instanceof DataTypesActionContext)) { return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java index bbf677f241..53aafbc6eb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/FunctionComparisonProvider.java @@ -21,6 +21,7 @@ import java.util.*; import javax.swing.Icon; import docking.ActionContext; +import docking.DockingTool; import docking.action.DockingAction; import docking.action.DockingActionIf; import docking.actions.PopupActionProvider; @@ -221,7 +222,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { if (context.getComponentProvider() == this) { ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java index 9476016c80..89be3de7d3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java @@ -17,13 +17,11 @@ package ghidra.app.plugin.core.instructionsearch.ui; import java.awt.Color; import java.awt.event.*; -import java.util.ArrayList; import java.util.List; import javax.swing.*; -import docking.ActionContext; -import docking.DockingWindowManager; +import docking.*; import docking.action.DockingActionIf; import docking.widgets.EmptyBorderButton; import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin; @@ -102,8 +100,8 @@ public class InstructionTable extends AbstractInstructionTable { * (which is all of them). */ @Override - public List getPopupActions(ActionContext context) { - return new ArrayList<>(); + public List getPopupActions(DockingTool tool, ActionContext context) { + return null; } public InsertBytesWidget getInsertBytesWidget() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/PreviewTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/PreviewTable.java index 85cd1c61be..3908ff59d8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/PreviewTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/PreviewTable.java @@ -136,10 +136,10 @@ public class PreviewTable extends AbstractInstructionTable { * any existing menus; it simply adds to them. */ @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { // Invoke the base class method to add default menu options. - List list = super.getPopupActions(context); + List list = super.getPopupActions(tool, context); // And now add our own. addCustomMenuItems(list); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java index 72e41ee871..992b1976ee 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableServicePlugin.java @@ -202,6 +202,20 @@ public class TableServicePlugin extends ProgramPlugin } } + void removeDialog(MyTableChooserDialog dialog) { + Iterator iter = programToDialogMap.keySet().iterator(); + while (iter.hasNext()) { + Program p = iter.next(); + List list = programToDialogMap.get(p); + if (list.remove(dialog)) { + if (list.size() == 0) { + programToDialogMap.remove(p); + return; + } + } + } + } + @Override public void domainObjectChanged(DomainObjectChangedEvent ev) { updateMgr.update(); @@ -263,17 +277,4 @@ public class TableServicePlugin extends ProgramPlugin return dialog; } - public void removeDialog(MyTableChooserDialog dialog) { - Iterator iter = programToDialogMap.keySet().iterator(); - while (iter.hasNext()) { - Program p = iter.next(); - List list = programToDialogMap.get(p); - if (list.remove(dialog)) { - if (list.size() == 0) { - programToDialogMap.remove(p); - return; - } - } - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java index 2999d21a87..8df25aa740 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java @@ -191,6 +191,7 @@ public class TableChooserDialog extends DialogComponentProvider if (navigatable != null) { navigatable.removeNavigatableListener(this); } + dispose(); } @Override @@ -319,6 +320,11 @@ public class TableChooserDialog extends DialogComponentProvider return rowObjects; } + public void dispose() { + table.dispose(); + workers.forEach(w -> w.cancel(true)); + } + //================================================================================================== // Inner Classes //================================================================================================== diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/bookmark/BookmarkPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/bookmark/BookmarkPluginTest.java index 25724c56e2..3ca183b18e 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/bookmark/BookmarkPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/bookmark/BookmarkPluginTest.java @@ -544,7 +544,7 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest { applyCmd(program, cmd); List actions = runSwing(() -> plugin.getPopupActions( - new ActionContext(null, new MarkerLocation(null, addr("0100b6db"), 0, 0)))); + null, new ActionContext(null, new MarkerLocation(null, addr("0100b6db"), 0, 0)))); assertEquals(10, actions.size()); } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java b/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java index e76bfb7fed..fb1076ca11 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java @@ -359,6 +359,12 @@ public class DummyTool implements Tool { //do nothing } + @Override + public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) { + // TODO Auto-generated method stub + + } + @Override public void contextChanged(ComponentProvider provider) { //do nothing 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 81454e1bf1..62f1c3bf97 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 @@ -28,8 +28,7 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.JTableHeader; -import docking.ActionContext; -import docking.WindowPosition; +import docking.*; import docking.action.*; import docking.actions.PopupActionProvider; import docking.menu.ActionState; @@ -216,7 +215,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { if (context.getComponentProvider() == this) { ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel(); 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 fdc7939a91..62eae749bc 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 @@ -452,7 +452,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { ListingCodeComparisonPanel dualListingPanel = functionComparisonPanel.getDualListingPanel(); if (context.getComponentProvider() == this && dualListingPanel != null) { ListingPanel sourcePanel = dualListingPanel.getLeftPanel(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java index 0c181c0a42..b7e6738408 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java @@ -185,6 +185,25 @@ public abstract class AbstractDockingTool implements DockingTool { winMgr.updateTitle(provider); } + /** + * Set the menu group associated with a cascaded submenu. This allows + * a cascading menu item to be grouped with a specific set of actions. + * The default group for a cascaded submenu is the name of the submenu. + * + * @param menuPath menu name path where the last element corresponds + * to the specified group name. + * @param group group name + * @see #setMenuGroup(String[], String, String) + */ + public void setMenuGroup(String[] menuPath, String group) { + setMenuGroup(menuPath, group, null); + } + + @Override + public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) { + winMgr.setMenuGroup(menuPath, group, menuSubGroup); + } + @Override public void contextChanged(ComponentProvider provider) { winMgr.contextChanged(provider); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java index 3f84748118..c707095f3e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ActionToGuiMapper.java @@ -110,10 +110,6 @@ public class ActionToGuiMapper { globalActions.clear(); } - void setMenuGroup(String[] menuPath, String group) { - menuGroupMap.setMenuGroup(menuPath, group); - } - void setMenuGroup(String[] menuPath, String group, String menuSubGroup) { menuGroupMap.setMenuGroup(menuPath, group, menuSubGroup); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ComponentLoadedListener.java b/Ghidra/Framework/Docking/src/main/java/docking/ComponentLoadedListener.java index f652e8099a..f5d9828685 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentLoadedListener.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentLoadedListener.java @@ -26,8 +26,7 @@ public interface ComponentLoadedListener { /** * Called when the component is made displayable * - * @param windowManager the window manager associated with the loaded component; this can - * be null when dialogs are used without a tool or window manager + * @param windowManager the window manager associated with the loaded component */ public void componentLoaded(DockingWindowManager windowManager); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java index 21f388bc6a..7f2f2ad98c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java @@ -104,6 +104,20 @@ public interface DockingTool { */ public void clearStatusInfo(); + /** + * Set the menu group associated with a cascaded submenu. This allows + * a cascading menu item to be grouped with a specific set of actions. + *

+ * The default group for a cascaded submenu is the name of the submenu. + *

+ * + * @param menuPath menu name path where the last element corresponds to the specified group name. + * @param group group name + * @param menuSubGroup the name used to sort the cascaded menu within other menu items at + * its level + */ + public void setMenuGroup(String[] menuPath, String group, String menuSubGroup); + /** * Adds the action to the tool. * @param action the action to be added. diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index a00a789feb..917bdd37c3 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -41,6 +41,7 @@ import ghidra.util.datastruct.*; import ghidra.util.exception.AssertException; import ghidra.util.task.SwingUpdateManager; import util.CollectionUtils; +import utilities.util.reflection.ReflectionUtilities; /** * Manages the "Docking" arrangement of a set of components and actions. The components can be "docked" @@ -70,7 +71,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder */ private static HelpService helpService = new DefaultHelpService(); - private static List instanceList = new ArrayList<>(); + // we use a list to maintain order + private static List instances = new ArrayList<>(); private DockingTool tool; private RootNode root; @@ -172,11 +174,11 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder } private static synchronized void addInstance(DockingWindowManager winMgr) { - instanceList.add(winMgr); + instances.add(winMgr); } private static synchronized void removeInstance(DockingWindowManager winMgr) { - instanceList.remove(winMgr); + instances.remove(winMgr); } /** @@ -190,7 +192,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return null; } - Iterator iter = instanceList.iterator(); + Iterator iter = instances.iterator(); while (iter.hasNext()) { DockingWindowManager winMgr = iter.next(); if (winMgr.root.getFrame() == win) { @@ -242,8 +244,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder // most active. Any time we change the active manager, it will be placed // in the back of the list. // - for (int i = instanceList.size() - 1; i >= 0; i--) { - DockingWindowManager mgr = instanceList.get(i); + for (int i = instances.size() - 1; i >= 0; i--) { + DockingWindowManager mgr = instances.get(i); if (mgr.root.isVisible()) { return mgr; } @@ -256,7 +258,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * @return a new list of all DockingWindowManager instances know to exist. */ public static synchronized List getAllDockingWindowManagers() { - return new ArrayList<>(instanceList); + return new ArrayList<>(instances); } /** @@ -264,8 +266,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * @param mgr the window manager that became active. */ static synchronized void setActiveManager(DockingWindowManager mgr) { - if (instanceList.remove(mgr)) { - instanceList.add(mgr); + if (instances.remove(mgr)) { + instances.add(mgr); } } @@ -1937,26 +1939,13 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder Toolkit.getDefaultToolkit().beep(); } - /** - * Set the menu group associated with a cascaded submenu. This allows - * a cascading menu item to be grouped with a specific set of actions. - * The default group for a cascaded submenu is the name of the submenu. - * @param menuPath menu name path where the last element corresponds - * to the specified group name. - * @param group group name - */ - public void setMenuGroup(String[] menuPath, String group) { - doSetMenuGroup(menuPath, group); - scheduleUpdate(); - } - /* * A version of setMenuGroup() that does *not* trigger an update. When clients call the * public API, an update is needed. This method is used during the rebuilding process * when we know that an update is not need, as we are in the middle of an update. */ void doSetMenuGroup(String[] menuPath, String group) { - actionToGuiMapper.setMenuGroup(menuPath, group); + actionToGuiMapper.setMenuGroup(menuPath, group, null); } /** @@ -2122,7 +2111,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder List actionList = new ArrayList<>(); for (PopupActionProvider pl : popupActionProviders) { - List actions = pl.getPopupActions(context); + List actions = pl.getPopupActions(tool, context); if (actions != null) { actionList.addAll(actions); } @@ -2149,9 +2138,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder /** * Registers a callback to be notified when the given component has been parented to - * a docking window manager. - * @param component the component that will be parented in a docking window system. - * @param listener the listener to be notified the component was parented. + * a docking window manager + * + * @param component the component that will be parented in a docking window system + * @param listener the listener to be notified the component was parented */ public static void registerComponentLoadedListener(Component component, ComponentLoadedListener listener) { @@ -2160,16 +2150,32 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder @Override public void hierarchyChanged(HierarchyEvent e) { long changeFlags = e.getChangeFlags(); - if (HierarchyEvent.DISPLAYABILITY_CHANGED == (changeFlags & - HierarchyEvent.DISPLAYABILITY_CHANGED)) { - // check for the first time we are put together - boolean isDisplayable = component.isDisplayable(); - if (isDisplayable) { - component.removeHierarchyListener(this); - DockingWindowManager windowManager = getInstance(component); - listener.componentLoaded(windowManager); - } + if (HierarchyEvent.DISPLAYABILITY_CHANGED != (changeFlags & + HierarchyEvent.DISPLAYABILITY_CHANGED)) { + return; + } + + // check for the first time we are put together + boolean isDisplayable = component.isDisplayable(); + if (!isDisplayable) { + return; + } + + component.removeHierarchyListener(this); + DockingWindowManager dwm = getInstance(component); + if (dwm != null) { + listener.componentLoaded(dwm); + return; + } + + // Unable to find the manager. This can happen during testing; only report if + // it is unexpected + if (!instances.isEmpty()) { + Msg.debug(DockingWindowManager.class, + "Unable to find Docking Window Manager for " + + component.getClass().getSimpleName(), + ReflectionUtilities.createJavaFilteredThrowable()); } } }); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java index 12fd797548..7b241fa652 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/PopupActionManager.java @@ -24,10 +24,11 @@ import java.util.*; import javax.swing.JPopupMenu; import docking.action.*; +import docking.actions.PopupActionProvider; import docking.menu.*; public class PopupActionManager implements PropertyChangeListener { - private List popupActions = new ArrayList(); + private List popupActions = new ArrayList<>(); private DockingWindowManager windowManager; private MenuGroupMap menuGroupMap; @@ -94,23 +95,11 @@ public class PopupActionManager implements PropertyChangeListener { popupMenu.show(c, e.getX(), e.getY()); } - private void populatePopupMenuActions(ComponentPlaceholder info, - ActionContext actionContext, MenuManager menuMgr) { + private void populatePopupMenuActions(ComponentPlaceholder info, ActionContext actionContext, + MenuManager menuMgr) { - // Include unregistered actions - Object source = actionContext.getSourceObject(); - 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); - } - } - } + // Unregistered actions are those used by special-needs components, on-the-fly + addUnregisteredActions(actionContext, menuMgr); // Include temporary actions List tempActions = windowManager.getTemporaryPopupActions(actionContext); @@ -133,7 +122,7 @@ public class PopupActionManager implements PropertyChangeListener { MenuData popupMenuData = action.getPopupMenuData(); if (popupMenuData != null && action.isValidContext(actionContext) && action.isAddToPopup(actionContext)) { - + boolean isEnabled = action.isEnabledForContext(actionContext); action.setEnabled(isEnabled); menuMgr.addAction(action); @@ -152,6 +141,42 @@ public class PopupActionManager implements PropertyChangeListener { } } + private void addUnregisteredActions(ActionContext actionContext, MenuManager menuMgr) { + + Object source = actionContext.getSourceObject(); + + // this interface is deprecated in favor of the next block + 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); + } + } + } + + // note: this is temporary; there is only one client that needs this. This will be + // removed in a future ticket when that client uses the standard tool action system + if (source instanceof PopupActionProvider) { + PopupActionProvider actionProvider = (PopupActionProvider) source; + DockingTool tool = windowManager.getTool(); + List dockingActions = + actionProvider.getPopupActions(tool, actionContext); + 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/actions/PopupActionProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/PopupActionProvider.java index 3f1b6c9edc..7268e2f335 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/actions/PopupActionProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/PopupActionProvider.java @@ -30,7 +30,9 @@ import docking.action.DockingActionIf; * Most clients will register actions directly with the tool. However, clients that have numerous * actions that vary greatly with the context can use this method to only create those actions * on demand as the popup is about to be shown, and only if their context is active. This - * mechanism can reduce the tool's action management overhead. + * mechanism can reduce the tool's action management overhead. Once you have created an + * implementation of this class, you must register it with + * {@link DockingTool#addPopupActionProvider(PopupActionProvider)}. */ public interface PopupActionProvider { @@ -40,8 +42,9 @@ public interface PopupActionProvider { * included in the menu if they have a valid popup menu path and respond true to the * {@link DockingActionIf#isValidContext(ActionContext)} call. * + * @param tool the tool requesting the actions * @param context the ActionContext * @return list of temporary popup actions; return null if there are no popup actions */ - public List getPopupActions(ActionContext context); + public List getPopupActions(DockingTool tool, ActionContext context); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/menu/MenuGroupMap.java b/Ghidra/Framework/Docking/src/main/java/docking/menu/MenuGroupMap.java index d96adcea88..57d5d19712 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/menu/MenuGroupMap.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/menu/MenuGroupMap.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,19 +24,8 @@ import docking.action.MenuData; * Maps menuPaths to groups */ public class MenuGroupMap { - private Map preferredMenuGroups = new HashMap(); - private Map preferredMenuSubGroups = new HashMap(); - - /** - * Sets the group for the given menuPath - * @param menuPath the menuPath for which to assign a group - * @param group the name of the group for the action with the given menu path - * - * @see #setMenuGroup(String[], String, String) - */ - public void setMenuGroup(String[] menuPath, String group) { - setMenuGroup(menuPath, group, MenuData.NO_SUBGROUP); - } + private Map preferredMenuGroups = new HashMap<>(); + private Map preferredMenuSubGroups = new HashMap<>(); /** * Sets the group for the given menuPath @@ -66,6 +54,7 @@ public class MenuGroupMap { /** * Returns the group for the given menu path * @param menuPath the menu path for which to find its group + * @return the menu group */ public String getMenuGroup(String[] menuPath) { return preferredMenuGroups.get(getMenuPathKey(menuPath)); @@ -76,6 +65,7 @@ public class MenuGroupMap { * sorting of menu items that exist in the same group. * * @param menuPath the menu path for which to find its group + * @return the menu sub-group */ public String getMenuSubGroup(String[] menuPath) { return preferredMenuSubGroups.get(getMenuPathKey(menuPath)); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java index 2e98287f2f..15cae8e1dd 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GTable.java @@ -511,13 +511,12 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { // we want these top-level groups to all appear together, with no separator - DockingWindowManager dwm = DockingWindowManager.getInstance(this); - dwm.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1"); - dwm.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2"); - dwm.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3"); + tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1"); + tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2"); + tool.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3"); List list = new ArrayList<>(); list.add(copyAction); @@ -579,15 +578,6 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv createPopupActions(); initializeRowHeight(); - - DockingWindowManager.registerComponentLoadedListener(this, dwm -> { - - if (dwm == null) { - return; - } - - dwm.getTool().addPopupActionProvider(this); - }); } private void initializeHeader(JTableHeader header) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/ProjectDataTablePanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/ProjectDataTablePanel.java index bf265fbe23..f510994dad 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/ProjectDataTablePanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/ProjectDataTablePanel.java @@ -25,8 +25,7 @@ import java.util.List; import javax.swing.*; -import docking.ActionContext; -import docking.ComponentProvider; +import docking.*; import docking.action.DockingActionIf; import docking.help.Help; import docking.help.HelpService; @@ -490,7 +489,7 @@ public class ProjectDataTablePanel extends JPanel { } @Override - public List getPopupActions(ActionContext context) { + public List getPopupActions(DockingTool tool, ActionContext context) { // TODO we should at least add the 'copy' action diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java index 99af2d9841..6f23e542bc 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java @@ -1009,18 +1009,18 @@ public abstract class PluginTool extends AbstractDockingTool implements Tool, Se action.setEnabled(true); addAction(action); - DockingAction userAgreementAction = - new DockingAction("User Agreement", ToolConstants.TOOL_OWNER) { - @Override - public void actionPerformed(ActionContext context) { - DockingWindowManager.showDialog(new UserAgreementDialog(false, false)); - } + DockingAction userAgreementAction = new DockingAction("User Agreement", + ToolConstants.TOOL_OWNER, KeyBindingType.UNSUPPORTED) { + @Override + public void actionPerformed(ActionContext context) { + DockingWindowManager.showDialog(new UserAgreementDialog(false, false)); + } - @Override - public boolean shouldAddToWindow(boolean isMainWindow, Set> contextTypes) { - return true; - } - }; + @Override + public boolean shouldAddToWindow(boolean isMainWindow, Set> contextTypes) { + return true; + } + }; userAgreementAction.setMenuBarData( new MenuData(new String[] { ToolConstants.MENU_HELP, "&User Agreement" }, null, ToolConstants.HELP_CONTENTS_MENU_GROUP)); @@ -1355,36 +1355,6 @@ public abstract class PluginTool extends AbstractDockingTool implements Tool, Se winMgr.showEditWindow(defaultText, comp, rect, listener); } - /** - * Set the menu group associated with a cascaded submenu. This allows - * a cascading menu item to be grouped with a specific set of actions. - * The default group for a cascaded submenu is the name of the submenu. - * - * @param menuPath menu name path where the last element corresponds - * to the specified group name. - * @param group group name - * @see #setMenuGroup(String[], String, String) - */ - public void setMenuGroup(String[] menuPath, String group) { - winMgr.setMenuGroup(menuPath, group); - } - - /** - * Set the menu group associated with a cascaded submenu. This allows - * a cascading menu item to be grouped with a specific set of actions. - *

- * The default group for a cascaded submenu is the name of the submenu. - *

- * - * @param menuPath menu name path where the last element corresponds to the specified group name. - * @param group group name - * @param menuSubGroup the name used to sort the cascaded menu within other menu items at - * its level - */ - public void setMenuGroup(String[] menuPath, String group, String menuSubGroup) { - winMgr.setMenuGroup(menuPath, group, menuSubGroup); - } - /** * Cancel the current task in the tool. */ diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java index 56fe52455e..dde093a54b 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/KeyBindingsPanel.java @@ -59,7 +59,7 @@ public class KeyBindingsPanel extends JPanel { private static final int FONT_SIZE = 11; private JTextPane statusLabel; - private JTable actionTable; + private GTable actionTable; private JPanel infoPanel; private MultiLineLabel collisionLabel; private KeyBindingsTableModel tableModel; @@ -96,6 +96,7 @@ public class KeyBindingsPanel extends JPanel { public void dispose() { tableFilterPanel.dispose(); tableModel.dispose(); + actionTable.dispose(); } /** diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java index c8b46fd8d7..33048e6625 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/OptionsManager.java @@ -25,6 +25,7 @@ import org.jdom.Element; import docking.options.editor.OptionsDialog; import docking.tool.ToolConstants; +import docking.tool.util.DockingToolConstants; import ghidra.framework.options.*; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginTool; @@ -242,12 +243,16 @@ public class OptionsManager implements OptionsService, OptionsChangeListener { return null; } + Options keyBindingOptions = getOptions(DockingToolConstants.KEY_BINDINGS); TreePath path = null; if (optionsDialog != null) { path = optionsDialog.getSelectedPath(); + optionsDialog.dispose(); + + OptionsEditor oldEditor = keyBindingOptions.getOptionsEditor(); + oldEditor.dispose(); } - Options keyBindingOptions = getOptions(ToolConstants.KEY_BINDINGS); keyBindingOptions.registerOptionsEditor(new KeyBindingOptionsEditor()); dialog = new OptionsDialog("Options for " + tool.getName(), "Options", getEditableOptions(), null, true);