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 b6c5d9a710..66409d518a 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 @@ -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. @@ -16,19 +15,19 @@ */ package ghidra.app.merge.tool; -import ghidra.app.util.viewer.listingpanel.ListingPanel; -import ghidra.framework.plugintool.*; - import java.awt.event.MouseEvent; import java.util.List; import javax.swing.JComponent; -import docking.ActionContext; -import docking.WindowPosition; +import docking.*; import docking.action.DockingActionIf; +import docking.actions.PopupActionProvider; +import ghidra.app.util.viewer.listingpanel.ListingPanel; +import ghidra.framework.plugintool.*; -public class ListingMergePanelProvider extends ComponentProviderAdapter implements PopupListener { +public class ListingMergePanelProvider extends ComponentProviderAdapter + implements PopupActionProvider { private ListingMergePanel mergePanel; public ListingMergePanelProvider(PluginTool tool, Plugin plugin, String owner, @@ -37,7 +36,7 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter implemen setTitle("Listing Merge Tool"); setDefaultWindowPosition(WindowPosition.TOP); this.mergePanel = mergePanel; - tool.addPopupListener(this); + tool.addPopupActionProvider(this); } @Override @@ -52,10 +51,11 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter implemen } void dispose() { + tool.removePopupActionProvider(this); } @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 cf8ae6c1db..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,7 +22,9 @@ 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; import ghidra.app.CorePluginPackage; import ghidra.app.events.ProgramSelectionPluginEvent; @@ -60,7 +62,7 @@ import resources.*; ) //@formatter:on public class BookmarkPlugin extends ProgramPlugin - implements DomainObjectListener, PopupListener, BookmarkService { + implements DomainObjectListener, PopupActionProvider, BookmarkService { private final static int MAX_DELETE_ACTIONS = 10; @@ -190,7 +192,7 @@ public class BookmarkPlugin extends ProgramPlugin public synchronized void dispose() { navUpdater.dispose(); - tool.removePopupListener(this); + tool.removePopupActionProvider(this); if (repaintMgr != null) { repaintMgr.dispose(); } @@ -227,7 +229,7 @@ public class BookmarkPlugin extends ProgramPlugin provider.setGoToService(goToService); markerService = tool.getService(MarkerService.class); - tool.addPopupListener(this); + tool.addPopupActionProvider(this); navUpdater = new NavUpdater(); repaintMgr = new SwingUpdateManager(500, () -> provider.repaint()); @@ -492,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 a5f2651fa7..35a28963e2 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 @@ -29,6 +29,7 @@ import javax.swing.event.ChangeListener; import docking.*; import docking.action.*; +import docking.actions.PopupActionProvider; import docking.dnd.*; import docking.widgets.EventTrigger; import docking.widgets.fieldpanel.FieldPanel; @@ -47,7 +48,8 @@ import ghidra.app.util.viewer.listingpanel.*; import ghidra.app.util.viewer.multilisting.MultiListingLayoutModel; import ghidra.app.util.viewer.util.FieldNavigator; import ghidra.framework.options.SaveState; -import ghidra.framework.plugintool.*; +import ghidra.framework.plugintool.NavigatableComponentProviderAdapter; +import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.util.*; @@ -57,7 +59,7 @@ import resources.ResourceManager; public class CodeViewerProvider extends NavigatableComponentProviderAdapter implements ProgramLocationListener, ProgramSelectionListener, Draggable, Droppable, - ChangeListener, StringSelectionListener, PopupListener { + ChangeListener, StringSelectionListener, PopupActionProvider { private static final String OLD_NAME = "CodeBrowserPlugin"; private static final String NAME = "Listing"; @@ -156,7 +158,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter listingPanel.addIndexMapChangeListener(this); codeViewerClipboardProvider = new CodeBrowserClipboardProvider(tool, this); - tool.addPopupListener(this); + tool.addPopupActionProvider(this); } @Override @@ -226,6 +228,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter public void dispose() { super.dispose(); + tool.removePopupActionProvider(this); + if (clipboardService != null) { clipboardService.deRegisterClipboardContentProvider(codeViewerClipboardProvider); } @@ -951,7 +955,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/compositeeditor/CompositeEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java index 090824993d..ffc3896aec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorProvider.java @@ -64,6 +64,7 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter super(plugin.getTool(), "Composite Editor", plugin.getName()); this.plugin = plugin; setIcon(EDITOR_ICON); + setTransient(); listeners = WeakDataStructureFactory.createSingleThreadAccessWeakSet(); initializeServices(); } 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 2932a3e30b..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,7 +27,9 @@ 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; import generic.jar.ResourceFile; import generic.util.Path; @@ -47,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; @@ -78,7 +81,7 @@ import ghidra.util.task.TaskLauncher; ) //@formatter:on public class DataTypeManagerPlugin extends ProgramPlugin - implements DomainObjectListener, DataTypeManagerService, PopupListener { + implements DomainObjectListener, DataTypeManagerService, PopupActionProvider { private static final String SEACH_PROVIDER_NAME = "Search DataTypes Provider"; private static final int RECENTLY_USED_CACHE_SIZE = 10; @@ -158,7 +161,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin if (codeViewerService != null) { codeViewerService.addProgramDropProvider(new DataDropOnBrowserHandler(this)); } - tool.addPopupListener(this); + tool.addPopupActionProvider(this); tool.setMenuGroup(new String[] { SyncRefreshAction.MENU_NAME }, "SYNC"); tool.setMenuGroup(new String[] { UpdateAction.MENU_NAME }, "SYNC"); tool.setMenuGroup(new String[] { CommitAction.MENU_NAME }, "SYNC"); @@ -263,7 +266,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override public void dispose() { - tool.removePopupListener(this); + tool.removePopupActionProvider(this); provider.dispose(); close(); dataTypeManagerHandler.dispose(); @@ -699,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 883f334ae8..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,8 +21,10 @@ 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; import docking.widgets.fieldpanel.internal.FieldPanelCoordinator; import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel; import ghidra.app.util.viewer.listingpanel.ListingPanel; @@ -37,7 +39,7 @@ import resources.ResourceManager; /** * This is the dockable provider that displays a FunctionComparisonPanel. */ -public class FunctionComparisonProvider extends ComponentProviderAdapter implements PopupListener { +public class FunctionComparisonProvider extends ComponentProviderAdapter implements PopupActionProvider { private static final String HELP_TOPIC = "FunctionComparison"; private static final Icon ICON = ResourceManager.loadImage("images/page_white_c.png"); @@ -120,7 +122,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme setTransient(); setTabText(functionComparisonPanel); addSpecificCodeComparisonActions(); - tool.addPopupListener(this); + tool.addPopupActionProvider(this); setHelpLocation(new HelpLocation(HELP_TOPIC, "Function Comparison")); } @@ -178,7 +180,7 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme @Override public void removeFromTool() { - tool.removePopupListener(this); + tool.removePopupActionProvider(this); super.removeFromTool(); } @@ -220,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 17adcbe1d3..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,12 +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.DockingWindowManager; +import docking.*; import docking.action.DockingActionIf; import docking.widgets.EmptyBorderButton; import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin; @@ -101,8 +100,8 @@ public class InstructionTable extends AbstractInstructionTable { * (which is all of them). */ @Override - public List getDockingActions() { - 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 85226e77ed..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 getDockingActions() { + public List getPopupActions(DockingTool tool, ActionContext context) { // Invoke the base class method to add default menu options. - List list = super.getDockingActions(); + 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/main/java/ghidra/framework/main/OpenVersionedFileDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java index 5c87c12811..7b7befa869 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java @@ -17,10 +17,15 @@ package ghidra.framework.main; import java.awt.BorderLayout; import java.awt.Dimension; +import java.awt.event.MouseEvent; import java.io.IOException; +import java.util.Collections; +import java.util.List; import javax.swing.*; +import docking.ActionContext; +import docking.action.DockingActionIf; import ghidra.framework.main.datatree.VersionHistoryPanel; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; @@ -47,11 +52,13 @@ public class OpenVersionedFileDialog extends DataTreeDialog { private JSplitPane splitPane; private JButton historyButton; - private VersionHistoryPanel historyPanel; private JPanel mainPanel; private boolean historyIsShowing; private PluginTool tool; + private VersionHistoryPanel historyPanel; + private List popupActions = Collections.emptyList(); + /** * Constructor * @param tool tool where the file is being opened. @@ -178,13 +185,32 @@ public class OpenVersionedFileDialog extends DataTreeDialog { super.close(); } + @Override + protected void dialogShown() { + super.dialogShown(); + + for (DockingActionIf action : popupActions) { + addAction(action); + } + } + + @Override + protected void dialogClosed() { + super.dialogClosed(); + + for (DockingActionIf action : popupActions) { + removeAction(action); + } + } + private boolean createHistoryPanel() { try { historyPanel = new VersionHistoryPanel(tool, null); + popupActions = historyPanel.createPopupActions(); } catch (IOException ioe) { Msg.debug(getClass(), - "Error creating history panel for versioned file: " + ioe.getMessage()); + "Error creating history panel for versioned file: " + ioe.getMessage(), ioe); return false; } @@ -225,4 +251,17 @@ public class OpenVersionedFileDialog extends DataTreeDialog { } }); } + + @Override + public ActionContext getActionContext(MouseEvent event) { + ActionContext context = super.getActionContext(event); + if (context != null) { + return context; + } + + ActionContext actionContext = new ActionContext(null, event.getSource(), this); + actionContext.setMouseEvent(event); + + return actionContext; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/TestTool.java b/Ghidra/Features/Base/src/main/java/ghidra/test/TestTool.java index ceaf167763..0b5bdbc056 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/TestTool.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/TestTool.java @@ -32,7 +32,7 @@ public class TestTool extends GhidraTool { @Override protected DockingWindowManager createDockingWindowManager(boolean isDockable, boolean hasStatus, boolean isModal) { - return new DockingWindowManager(this, null, this, isModal, isDockable, hasStatus, null); + return new DockingWindowManager(this, null, isModal, isDockable, hasStatus, null); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java index 1b5941ab88..830b4e4b1a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/DeleteTableRowAction.java @@ -200,7 +200,6 @@ public class DeleteTableRowAction extends DockingAction { // prevent this action from appearing in the toolbar, menus, etc setToolBarData(null); setPopupMenuData(null); - setKeyBindingData(null); tool.addAction(this); } 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 f9a804408f..5dc2c58e3e 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java @@ -44,7 +44,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio private final Icon ICON = ResourceManager.loadImage("images/refresh.png"); private static final String PROVIDER_NAME = "Test Action Provider"; private static final KeyStroke CONTROL_T = - KeyStroke.getKeyStroke(Character.valueOf('T'), DockingUtils.CONTROL_KEY_MODIFIER_MASK); + KeyStroke.getKeyStroke(KeyEvent.VK_T, DockingUtils.CONTROL_KEY_MODIFIER_MASK); private TestEnv env; private PluginTool tool; @@ -453,8 +453,12 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio } private void assertProviderIsActive() { - assertTrue("The test provider is not showing and focused", - runSwing(() -> tool.isActive(provider))); + + assertTrue("Component provider is not showing", runSwing(() -> tool.isVisible(provider))); + + // note: we can't call 'isActive()' due to focus issues in parallel testing + //assertTrue("The test provider is not showing and focused", + // runSwing(() -> tool.isActive(provider))); } private void assertProviderIsHidden() { diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/DockingWindowManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/DockingWindowManagerTest.java index e4b6ec6a77..d6899593ef 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/DockingWindowManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/DockingWindowManagerTest.java @@ -45,7 +45,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // default window position. // - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerA = addProvider(dwm, "A", "a", RIGHT); ComponentProvider providerB = addProvider(dwm, "B", "b", BOTTOM); @@ -65,7 +65,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // intragroup window position. Note: 'Stacked' is the default. // - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerA1 = addProvider(dwm, "A1", "a", RIGHT, STACK); ComponentProvider providerA2 = addProvider(dwm, "A2", "a", BOTTOM, STACK); @@ -83,7 +83,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // intragroup window position. // - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerA1 = addProvider(dwm, "A1", "a", RIGHT, STACK); ComponentProvider providerA2 = addProvider(dwm, "A2", "a", LEFT, BOTTOM); @@ -100,7 +100,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // intragroup window position. // - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerA1 = addProvider(dwm, "A1", "a", RIGHT, WINDOW); ComponentProvider providerA2 = addProvider(dwm, "A2", "a", LEFT, WINDOW); @@ -121,7 +121,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // // note: the positions specified here are for default positions, not intragroup positions - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerA = addProvider(dwm, "A", "a", RIGHT, STACK); ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", BOTTOM, STACK); @@ -142,7 +142,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // // note: the positions specified here are for default positions, not intragroup positions - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerA = addProvider(dwm, "A", "a", RIGHT, BOTTOM); ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", BOTTOM, TOP); @@ -180,7 +180,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // Test that, for unrelated groups, the layout info stored in XML is re-used when providers // are shown after that XML is restored--even if the window positioning changes // - final DockingWindowManager dwm1 = new DockingWindowManager(tool, (List) null, null); + final DockingWindowManager dwm1 = new DockingWindowManager(tool, (List) null); ComponentProvider providerA = addProvider(dwm1, "A", "a", RIGHT); ComponentProvider providerB = addProvider(dwm1, "B", "b", BOTTOM); @@ -215,7 +215,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // Test that, for related groups, the layout info stored in XML is re-used when providers // are shown after that XML is restored--even if the window positioning changes // - DockingWindowManager dwm1 = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm1 = new DockingWindowManager(tool, (List) null); ComponentProvider providerA = addProvider(dwm1, "A", "a", RIGHT); ComponentProvider providerAB = addProvider(dwm1, "AB", "a.b", BOTTOM); @@ -251,7 +251,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // and that a subgroup 'a.b' will open relative to the parent. **Make sure that this // works when the parent is the first provider open. // - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerA = addProvider(dwm, "A", "a", TOP, RIGHT); ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", RIGHT, BOTTOM); @@ -268,7 +268,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // and that a subgroup 'a.b' will open relative to the parent. **Make sure that this // works when the subgroup is the first provider open. // - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider providerAB = addProvider(dwm, "AB", "a.b", RIGHT, BOTTOM); ComponentProvider providerA = addProvider(dwm, "A", "a", TOP, RIGHT); @@ -286,7 +286,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { // Test that two providers that don't share an owner (the plugin) can share a group and // open relative to each other. // - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider p1 = addProvider(dwm, "Owner_1", "Name_1", "group", TOP, TOP); ComponentProvider p2 = addProvider(dwm, "Owner_2", "Name_2", "group", BOTTOM, RIGHT); @@ -335,7 +335,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { */ - DockingWindowManager dwm = new DockingWindowManager(tool, (List) null, null); + DockingWindowManager dwm = new DockingWindowManager(tool, (List) null); ComponentProvider pA = addProvider(dwm, "Owner_1", "A", "a", LEFT, LEFT); ComponentProvider pB = addProvider(dwm, "Owner_2", "B", "b", RIGHT, RIGHT); @@ -408,7 +408,7 @@ public class DockingWindowManagerTest extends AbstractDockingTest { private DockingWindowManager createNewDockingWindowManagerFromXML(final Element element) { final DockingWindowManager dwm2 = - new DockingWindowManager(new DummyTool("Tool2"), (List) null, null); + new DockingWindowManager(new DummyTool("Tool2"), (List) null); runSwing(() -> { dwm2.setVisible(true); diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/dialogs/NumberInputDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/dialogs/NumberInputDialogTest.java index 2a4d54d64e..bb5b2cbc7d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/dialogs/NumberInputDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/dialogs/NumberInputDialogTest.java @@ -35,7 +35,7 @@ import ghidra.test.DummyTool; public class NumberInputDialogTest extends AbstractDockingTest { private DockingWindowManager dwm = - new DockingWindowManager(new DummyTool(), (List) null, null); + new DockingWindowManager(new DummyTool(), (List) null); private NumberInputDialog dialog; private JButton okButton; private JTextField textField; diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/table/GhidraTableFilterTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/table/GhidraTableFilterTest.java index 0d5d589179..0e11988bbc 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/table/GhidraTableFilterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/table/GhidraTableFilterTest.java @@ -51,7 +51,7 @@ public class GhidraTableFilterTest extends AbstractGhidraHeadedIntegrationTest { filteredModel = filterPanel.getTableFilterModel(); table.setAutoLookupColumn(4); - winMgr = new DockingWindowManager(new DummyTool(), null, null); + winMgr = new DockingWindowManager(new DummyTool(), null); winMgr.addComponent(new TestTableComponentProvider()); winMgr.setVisible(true); } 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.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java index cfb9912bc8..0cdccd0b8e 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingsTest.java @@ -97,7 +97,12 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest { // verify that the description is displayed for the selected action selectRowForAction(action1); - assertTrue(statusPane.getText().indexOf(action1.getDescription()) != -1); + + String actualText = statusPane.getText(); + assertTrue( + "Description is not updated for action '" + action1.getName() + "'; instead the " + + "description is '" + actualText + "'", + actualText.indexOf(action1.getDescription()) != -1); } @Test 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 3f01e155f2..fb1076ca11 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/test/DummyTool.java @@ -29,6 +29,7 @@ import org.jdom.Element; import docking.*; import docking.action.DockingActionIf; import docking.actions.DockingToolActions; +import docking.actions.PopupActionProvider; import ghidra.framework.model.*; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.PluginEvent; @@ -298,6 +299,16 @@ public class DummyTool implements Tool { //do nothing } + @Override + public void addPopupActionProvider(PopupActionProvider listener) { + // do nothing + } + + @Override + public void removePopupActionProvider(PopupActionProvider listener) { + // do nothing + } + @Override public Set getAllActions() { return Collections.emptySet(); @@ -348,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 2e9c934347..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,9 +28,9 @@ 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; import docking.menu.MultiStateDockingAction; import docking.widgets.EventTrigger; @@ -52,7 +52,6 @@ import ghidra.framework.model.*; import ghidra.framework.options.Options; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.ComponentProviderAdapter; -import ghidra.framework.plugintool.PopupListener; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; @@ -67,7 +66,7 @@ import resources.ResourceManager; * Provider for the version tracking function association table. */ public class VTFunctionAssociationProvider extends ComponentProviderAdapter - implements VTControllerListener, PopupListener { + implements VTControllerListener, PopupActionProvider { private static final String FILTER_SETTINGS_KEY = "FUNCTION_FILTER_SETTINGS"; private static final String BASE_TITLE = "Version Tracking Functions"; @@ -125,7 +124,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter createActions(); addGeneralCodeComparisonActions(); controller.addListener(this); - tool.addPopupListener(this); + tool.addPopupActionProvider(this); } private void createActions() { @@ -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(); @@ -334,7 +333,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter destinationFunctionsTable.dispose(); destinationTableFilterPanel.dispose(); - tool.removePopupListener(this); + tool.removePopupActionProvider(this); } public void reload() { 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 78f7d6a7d5..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 @@ -30,6 +30,7 @@ import javax.swing.table.*; import docking.*; import docking.action.*; +import docking.actions.PopupActionProvider; import docking.help.HelpService; import docking.widgets.EventTrigger; import docking.widgets.fieldpanel.FieldPanel; @@ -56,7 +57,6 @@ import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.framework.options.Options; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.ComponentProviderAdapter; -import ghidra.framework.plugintool.PopupListener; import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.util.ProgramLocation; @@ -69,7 +69,7 @@ import resources.ResourceManager; * This provides the GUI for displaying and working with version tracking markup items. */ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter - implements FilterDialogModel, VTControllerListener, PopupListener { + implements FilterDialogModel, VTControllerListener, PopupActionProvider { private static final String SHOW_COMPARISON_PANEL = "SHOW_COMPARISON_PANEL"; @@ -133,7 +133,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter iconTimer = new FilterIconFlashTimer<>(UNFILTERED_ICON, FILTERED_ICON, ancillaryFilterDialog, ancillaryFilterButton); - tool.addPopupListener(this); + tool.addPopupActionProvider(this); HelpLocation helpLocation = new HelpLocation("VersionTrackingPlugin", "Markup Items Table"); setHelpLocation(helpLocation); @@ -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(); @@ -534,7 +534,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter filter.dispose(); } - tool.removePopupListener(this); + tool.removePopupActionProvider(this); } VTController getController() { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java index 893ad0be06..b7e6738408 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/AbstractDockingTool.java @@ -21,6 +21,7 @@ import java.util.*; import javax.swing.JFrame; import docking.action.DockingActionIf; +import docking.actions.PopupActionProvider; import docking.actions.ToolActions; import ghidra.framework.options.ToolOptions; import ghidra.util.Swing; @@ -123,6 +124,16 @@ public abstract class AbstractDockingTool implements DockingTool { return toolActions.getAllActions(); } + @Override + public void addPopupActionProvider(PopupActionProvider provider) { + winMgr.addPopupActionProvider(provider); + } + + @Override + public void removePopupActionProvider(PopupActionProvider provider) { + winMgr.removePopupActionProvider(provider); + } + @Override public Set getDockingActionsByOwnerName(String owner) { return toolActions.getActions(owner); @@ -174,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 503c2c1ade..f5d9828685 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentLoadedListener.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentLoadedListener.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. @@ -16,7 +15,18 @@ */ package docking; +import java.awt.Component; +/** + * A listener interface to know when a component has been + * made {@link Component#isDisplayable() displayable} + */ public interface ComponentLoadedListener { + + /** + * Called when the component is made displayable + * + * @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/ComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java index dc6534605d..7fd85b6036 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java @@ -807,7 +807,7 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext DockingWindowManager myDwm = DockingWindowManager.getInstance(getComponent()); if (myDwm == null) { - // don't think this can happen + // this can happen when the tool loses focus dockingTool.showComponentProvider(ComponentProvider.this, true); return; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java index b9f35aa436..e8629339b4 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java @@ -32,7 +32,7 @@ import docking.actions.ActionAdapter; import docking.actions.KeyBindingUtils; import docking.event.mouse.GMouseListenerAdapter; import docking.menu.DockingToolbarButton; -import docking.util.*; +import docking.util.AnimationUtils; import docking.widgets.label.GDHtmlLabel; import ghidra.util.*; import ghidra.util.exception.AssertException; @@ -43,9 +43,8 @@ import utility.function.Callback; * Base class used for creating dialogs in Ghidra. Subclass this to create a dialog provider that has * all the gui elements to appear in the dialog, then use tool.showDialog() to display your dialog. */ - public class DialogComponentProvider - implements TaskListener, StatusListener, ActionContextProvider { + implements ActionContextProvider, StatusListener, TaskListener { private static final Color WARNING_COLOR = new Color(0xff9900); @@ -1369,5 +1368,4 @@ public class DialogComponentProvider } } - } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java index 060f05c814..1fe5f87119 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProviderPopupActionManager.java @@ -73,7 +73,7 @@ public class DialogComponentProviderPopupActionManager { MenuGroupMap menuGroupMap = actionManager.getMenuGroupMap(); MenuManager menuMgr = new MenuManager("Popup", '\0', null, true, popupMenuHandler, menuGroupMap); - populatePopupMenuActions(actionContext, menuMgr); + populatePopupMenuActions(dwm, menuMgr, actionContext); if (menuMgr.isEmpty()) { return; } @@ -85,7 +85,8 @@ public class DialogComponentProviderPopupActionManager { popupMenu.show(c, e.getX(), e.getY()); } - private void populatePopupMenuActions(ActionContext actionContext, MenuManager menuMgr) { + private void populatePopupMenuActions(DockingWindowManager dwm, MenuManager menuMgr, + ActionContext actionContext) { Iterator iter = popupActions.iterator(); while (iter.hasNext()) { @@ -112,6 +113,18 @@ public class DialogComponentProviderPopupActionManager { } } } + + List tempActions = dwm.getTemporaryPopupActions(actionContext); + if (tempActions != null) { + for (DockingActionIf action : tempActions) { + MenuData popupMenuData = action.getPopupMenuData(); + if (popupMenuData != null && action.isValidContext(actionContext) && + action.isAddToPopup(actionContext)) { + action.setEnabled(action.isEnabledForContext(actionContext)); + menuMgr.addAction(action); + } + } + } } //================================================================================================== diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockWinListener.java b/Ghidra/Framework/Docking/src/main/java/docking/DockWinListener.java deleted file mode 100644 index 0d83231395..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockWinListener.java +++ /dev/null @@ -1,44 +0,0 @@ -/* ### - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package docking; - -import java.util.List; - -import docking.action.DockingActionIf; - -/** - * Listener interface for the object to be notified when the user closes the - * docking windows manager or initiates a popup menu. - */ -public interface DockWinListener { - /** - * Notification triggered when the user presses the "x" button in the main tool frame. - * Typical reaction is to dispose the dockingWindowManger and/or exit. - */ - void close(); - - /** - * Provides notification when a popup menu is about to be displayed - * and permits a list of temporary actions to be returned. Only - * those actions which have a suitable popup menu path will be - * considered. - * @param context the ActionContext - * @return list of temporary actions. - */ - List getPopupActions(ActionContext context); - -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java index 2116bd64b7..7f2f2ad98c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingTool.java @@ -22,6 +22,7 @@ import javax.swing.ImageIcon; import docking.action.DockingActionIf; import docking.actions.DockingToolActions; +import docking.actions.PopupActionProvider; import ghidra.framework.options.ToolOptions; /** @@ -103,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. @@ -129,6 +144,19 @@ public interface DockingTool { */ public void removeLocalAction(ComponentProvider componentProvider, DockingActionIf action); + /** + * Adds the given popup action provider to this tool. This provider will be called each + * time the popup menu is about to be shown. + * @param provider the provider + */ + public void addPopupActionProvider(PopupActionProvider provider); + + /** + * Removes the given popup action provider + * @param provider the provider + */ + public void removePopupActionProvider(PopupActionProvider provider); + /** * Return a set of all actions in the tool. * @@ -273,4 +301,12 @@ public interface DockingTool { * @return the action manager */ public DockingToolActions getToolActions(); + + /** + * Suggests the tool to attempt to close(). This will be as though the user + * selected the close menu option on the tool or hit the closeWindow x button in + * the upper corner (Windows systems). + */ + public void close(); + } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java index 6beb26bf9d..add2e33395 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingWindowManager.java @@ -30,8 +30,7 @@ import org.apache.commons.collections4.map.LazyMap; import org.jdom.Element; import docking.action.DockingActionIf; -import docking.actions.DockingToolActions; -import docking.actions.ToolActions; +import docking.actions.*; import docking.help.HelpService; import generic.util.WindowUtilities; import ghidra.framework.OperatingSystem; @@ -42,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" @@ -71,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; @@ -87,9 +88,10 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder private Map providerNameCache = new HashMap<>(); private Map preferenceStateMap = new HashMap<>(); - private DockWinListener docListener; private ActionToGuiMapper actionToGuiMapper; + private WeakSet popupActionProviders = + WeakDataStructureFactory.createSingleThreadAccessWeakSet(); private WeakSet contextListeners = WeakDataStructureFactory.createSingleThreadAccessWeakSet(); @@ -108,10 +110,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * Constructs a new DockingWindowManager * @param tool the tool * @param images the images to use for windows in this window manager - * @param docListener the listener to be notified when the user closes the manager */ - public DockingWindowManager(DockingTool tool, List images, DockWinListener docListener) { - this(tool, images, docListener, false, true, true, null); + public DockingWindowManager(DockingTool tool, List images) { + this(tool, images, false, true, true, null); } /** @@ -119,20 +120,18 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * * @param tool the tool * @param images the list of icons to set on the window - * @param docListener the listener to be notified when the user closes the manager * @param modal if true then the root window will be a modal dialog instead of a frame * @param isDocking true for normal operation, false to suppress docking support(removes * component headers and window menu) * @param hasStatusBar if true a status bar will be created for the main window * @param factory the drop target factory */ - public DockingWindowManager(DockingTool tool, List images, DockWinListener docListener, - boolean modal, boolean isDocking, boolean hasStatusBar, DropTargetFactory factory) { + public DockingWindowManager(DockingTool tool, List images, boolean modal, + boolean isDocking, boolean hasStatusBar, DropTargetFactory factory) { KeyBindingOverrideKeyEventDispatcher.install(); this.tool = tool; - this.docListener = docListener; this.isDocking = isDocking; this.hasStatusBar = hasStatusBar; if (images == null) { @@ -174,16 +173,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder return helpService; } - List getTemporaryPopupActions(ActionContext context) { - return docListener.getPopupActions(context); - } - 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); } /** @@ -197,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) { @@ -249,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; } @@ -263,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); } /** @@ -271,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); } } @@ -850,9 +845,9 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder } placeholder.show(visibleState); - movePlaceholderToFront(placeholder, false); if (visibleState) { + movePlaceholderToFront(placeholder, false); if (placeholder.getNode() == null) { root.add(placeholder); } @@ -1084,7 +1079,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder * the main window frame. */ void close() { - docListener.close(); + tool.close(); } boolean isDocking() { @@ -1944,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); } /** @@ -2099,6 +2081,44 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder actionToGuiMapper.contextChanged(placeholder); } + /** + * Adds the given popup action provider to this tool. This provider will be called each + * time the popup menu is about to be shown. + * @param provider the provider + */ + public void addPopupActionProvider(PopupActionProvider provider) { + popupActionProviders.add(provider); + } + + /** + * Removes the given popup action provider + * @param provider the provider + */ + public void removePopupActionProvider(PopupActionProvider provider) { + popupActionProviders.remove(provider); + } + + /** + * Returns a list of temporary popup actions to be returned. Only those actions which have + * a suitable popup menu path will be considered. This mechanism allows clients to + * add transient actions to be added to the tool without the accompanying management overhead. + * + * @param context the ActionContext + * @return list of temporary actions + * @see #addPopupActionProvider(PopupActionProvider) + */ + List getTemporaryPopupActions(ActionContext context) { + + List actionList = new ArrayList<>(); + for (PopupActionProvider pl : popupActionProviders) { + List actions = pl.getPopupActions(tool, context); + if (actions != null) { + actionList.addAll(actions); + } + } + return actionList; + } + public void addContextListener(DockingContextListener listener) { contextListeners.add(listener); } @@ -2118,30 +2138,44 @@ 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(final Component component, - final ComponentLoadedListener listener) { + public static void registerComponentLoadedListener(Component component, + ComponentLoadedListener listener) { - // We want to load our state after the column model is loaded. We are using this - // listener to know when the table has been added to the component hierarchy, as its - // model has been loaded by then. component.addHierarchyListener(new HierarchyListener() { @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/action/DockingActionProviderIf.java b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionProviderIf.java index 176987d272..2aff5ffbcc 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionProviderIf.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingActionProviderIf.java @@ -17,6 +17,8 @@ package docking.action; import java.util.List; +import docking.DockingTool; + /** * 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 @@ -25,8 +27,12 @@ import java.util.List; * 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. + * that is shown. + * + * @deprecated use {@link DockingTool} */ +// 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 { /** diff --git a/Ghidra/Framework/Docking/src/main/java/docking/actions/PopupActionProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/actions/PopupActionProvider.java new file mode 100644 index 0000000000..7268e2f335 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/actions/PopupActionProvider.java @@ -0,0 +1,50 @@ +/* ### + * 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.actions; + +import java.util.List; + +import docking.ActionContext; +import docking.DockingTool; +import docking.action.DockingActionIf; + +/** + * Provides notification when the popup action menu is displayed. This interface allows + * temporary/transient actions (those not registered with the tool via + * {@link DockingTool#addAction(DockingActionIf)}) to be used in the popup context menu. + * + *

+ * 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. Once you have created an + * implementation of this class, you must register it with + * {@link DockingTool#addPopupActionProvider(PopupActionProvider)}. + */ +public interface PopupActionProvider { + + /** + * Provides notification that the popup menu is about to be displayed and allows a set of + * temporary actions to be included in the popup menu. Actions returned will be + * 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(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 58e884d7a8..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 @@ -32,6 +32,7 @@ import javax.swing.table.*; import docking.*; import docking.action.*; import docking.actions.KeyBindingUtils; +import docking.actions.PopupActionProvider; import docking.widgets.OptionDialog; import docking.widgets.dialogs.SettingsDialog; import docking.widgets.filechooser.GhidraFileChooser; @@ -69,7 +70,7 @@ import resources.ResourceManager; * * @see GTableFilterPanel */ -public class GTable extends JTable implements KeyStrokeConsumer, DockingActionProviderIf { +public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProvider { private static final String LAST_EXPORT_FILE = "LAST_EXPORT_DIR"; @@ -510,25 +511,12 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr } @Override - public List getDockingActions() { - return getDefaultDockingActions(); - } + public List getPopupActions(DockingTool tool, ActionContext context) { - /** - * Returns the default actions of this table. Normally, the Docking Windows systems uses - * {@link #getDockingActions()} to get the correct actions to show. However, - * there are some cases where clients override what appears when you click on a table (such - * as in {@link DialogComponentProvider}s. For those clients that are creating their own - * action building, they need a way to get the default actions, hence this method. - * - * @return the default actions - */ - public List getDefaultDockingActions() { // 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); @@ -1490,5 +1478,4 @@ public class GTable extends JTable implements KeyStrokeConsumer, DockingActionPr // ignored } } - } diff --git a/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java b/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java index bbbba7ab68..2696dbccaa 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/FakeDockingTool.java @@ -20,7 +20,6 @@ import java.util.List; import javax.swing.ImageIcon; -import docking.action.DockingActionIf; import docking.actions.ToolActions; import docking.framework.ApplicationInformationDisplayFactory; import ghidra.framework.options.ToolOptions; @@ -33,10 +32,9 @@ public class FakeDockingTool extends AbstractDockingTool { public FakeDockingTool() { - DockWinListener listener = new DummyListener(); List windowIcons = ApplicationInformationDisplayFactory.getWindowIcons(); - winMgr = new DockingWindowManager(this, windowIcons, listener, false /*isModal*/, - true /*isDockable*/, true /*hasStatus*/, null /*DropTargetFactory*/); + winMgr = new DockingWindowManager(this, windowIcons, false /*isModal*/, true /*isDockable*/, + true /*hasStatus*/, null /*DropTargetFactory*/); toolActions = new ToolActions(this, new ActionToGuiHelper(winMgr)); } @@ -60,16 +58,8 @@ public class FakeDockingTool extends AbstractDockingTool { return opt; } - private class DummyListener implements DockWinListener { - - @Override - public void close() { - // stub - } - - @Override - public List getPopupActions(ActionContext context) { - return null; - } + @Override + public void close() { + // stub } } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/WrappedKeyStroke.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/WrappedKeyStroke.java index 645be64115..dc3414089a 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/WrappedKeyStroke.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/WrappedKeyStroke.java @@ -15,6 +15,8 @@ */ package ghidra.framework.options; +import java.util.Objects; + import javax.swing.KeyStroke; /** @@ -84,4 +86,9 @@ class WrappedKeyStroke implements WrappedOption { public OptionType getOptionType() { return OptionType.KEYSTROKE_TYPE; } + + @Override + public String toString() { + return Objects.toString(keyStroke); + } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/app/util/GenericHelpTopics.java b/Ghidra/Framework/Project/src/main/java/ghidra/app/util/GenericHelpTopics.java index 579fc9a760..2286853585 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/app/util/GenericHelpTopics.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/app/util/GenericHelpTopics.java @@ -44,6 +44,11 @@ public class GenericHelpTopics { */ public final static String REPOSITORY = "Repository"; + /** + * Help Topic for the version control. + */ + public final static String VERSION_CONTROL = "VersionControl"; + /** * Help Topic for tools. */ 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 d3fe4da3e8..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 getDockingActions() { + 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/main/datatree/VersionHistoryDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryDialog.java index 6272482da1..a9c1cd9f87 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryDialog.java @@ -16,6 +16,7 @@ package ghidra.framework.main.datatree; import java.awt.event.MouseEvent; +import java.util.Collections; import java.util.List; import javax.swing.SwingUtilities; @@ -23,7 +24,6 @@ import javax.swing.SwingUtilities; import docking.ActionContext; import docking.DialogComponentProvider; import docking.action.DockingActionIf; -import docking.action.DockingActionProviderIf; import ghidra.app.util.GenericHelpTopics; import ghidra.framework.main.AppInfo; import ghidra.framework.main.FrontEndTool; @@ -31,24 +31,26 @@ import ghidra.framework.model.*; import ghidra.framework.store.FileSystem; import ghidra.util.HelpLocation; -public class VersionHistoryDialog extends DialogComponentProvider - implements ProjectListener, DockingActionProviderIf { +public class VersionHistoryDialog extends DialogComponentProvider implements ProjectListener { private VersionHistoryPanel versionPanel; private MyFolderListener listener = new MyFolderListener(); + private List popupActions = Collections.emptyList(); - public VersionHistoryDialog() { + public VersionHistoryDialog(DomainFile domainFile) { super("Version History", false); FrontEndTool frontEndTool = AppInfo.getFrontEndTool(); - setHelpLocation(new HelpLocation(GenericHelpTopics.REPOSITORY, "Show_History")); - versionPanel = new VersionHistoryPanel(frontEndTool, null, true); + setHelpLocation(new HelpLocation(GenericHelpTopics.VERSION_CONTROL, "Show_History")); + versionPanel = new VersionHistoryPanel(frontEndTool, domainFile, true); addWorkPanel(versionPanel); addDismissButton(); - versionPanel.addPopupActions(this); + + setDomainFile(domainFile); + popupActions = versionPanel.createPopupActions(); } - public void setDomainFile(DomainFile df) { + private void setDomainFile(DomainFile df) { versionPanel.setDomainFile(df); @@ -59,18 +61,24 @@ public class VersionHistoryDialog extends DialogComponentProvider setTitle("Version History for " + df.getName()); project.getProjectData().addDomainFolderChangeListener(listener); } - else { - setTitle("Version History"); - project.getProjectData().removeDomainFolderChangeListener(listener); + } + + @Override + protected void dialogShown() { + super.dialogShown(); + + for (DockingActionIf action : popupActions) { + addAction(action); } } @Override - protected void dismissCallback() { + protected void dialogClosed() { + super.dialogClosed(); - close(); - - setDomainFile(null); + for (DockingActionIf action : popupActions) { + removeAction(action); + } } @Override @@ -127,13 +135,8 @@ public class VersionHistoryDialog extends DialogComponentProvider @Override public ActionContext getActionContext(MouseEvent event) { - ActionContext actionContext = new ActionContext(null, this, this); + ActionContext actionContext = new ActionContext(null, versionPanel.getTable(), this); actionContext.setMouseEvent(event); return actionContext; } - - @Override - public List getDockingActions() { - return versionPanel.getDockingActions(); - } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java index ffb621583b..91328086e4 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java @@ -31,26 +31,29 @@ import javax.swing.event.ListSelectionListener; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; -import docking.*; +import docking.ActionContext; import docking.action.*; import docking.dnd.*; import docking.widgets.OptionDialog; import docking.widgets.table.*; +import ghidra.app.util.GenericHelpTopics; import ghidra.framework.client.ClientUtil; import ghidra.framework.main.GetVersionedObjectTask; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.store.ItemCheckoutStatus; import ghidra.framework.store.Version; -import ghidra.util.HTMLUtilities; -import ghidra.util.Msg; +import ghidra.util.*; import ghidra.util.task.*; /** - * Panel that shows version history in a JTable. + * Panel that shows version history in a JTable */ public class VersionHistoryPanel extends JPanel implements Draggable { + private static final HelpLocation HELP = + new HelpLocation(GenericHelpTopics.VERSION_CONTROL, "Show History"); + private PluginTool tool; private DomainFile domainFile; private String domainFilePath; @@ -79,7 +82,6 @@ public class VersionHistoryPanel extends JPanel implements Draggable { * @param tool tool * @param domainFile domain file * @param enableUserInteraction if true Draggable support will be enabled - * @throws IOException */ VersionHistoryPanel(PluginTool tool, DomainFile domainFile, boolean enableUserInteraction) { super(new BorderLayout()); @@ -94,11 +96,11 @@ public class VersionHistoryPanel extends JPanel implements Draggable { } /** - * Set the domain file to show its history. + * Set the domain file to show its history + * @param domainFile the file */ public void setDomainFile(DomainFile domainFile) { this.domainFile = domainFile; - domainFilePath = null; if (domainFile != null) { this.domainFilePath = domainFile.getPathname(); } @@ -122,21 +124,25 @@ public class VersionHistoryPanel extends JPanel implements Draggable { } /** - * Add the list selection listener to the history table. + * Add the list selection listener to the history table + * @param selectionListener the listener */ - public void addListSelectionListener(ListSelectionListener listener1) { - table.getSelectionModel().addListSelectionListener(listener1); + public void addListSelectionListener(ListSelectionListener selectionListener) { + table.getSelectionModel().addListSelectionListener(selectionListener); } /** * Remove the list selection listener from history table. + * @param selectionListener the listener */ - public void removeListSelectionListener(ListSelectionListener listener1) { - table.getSelectionModel().removeListSelectionListener(listener1); + public void removeListSelectionListener(ListSelectionListener selectionListener) { + table.getSelectionModel().removeListSelectionListener(selectionListener); } /** * Get the domain object for the selected version. + * @param consumer the consumer + * @param readOnly true if read only * @return null if there is no selection */ public DomainObject getSelectedVersion(Object consumer, boolean readOnly) { @@ -148,16 +154,10 @@ public class VersionHistoryPanel extends JPanel implements Draggable { return null; } - /** - * Return whether a version is selected. - */ public boolean isVersionSelected() { return !table.getSelectionModel().isSelectionEmpty(); } - /** - * Get the version number that was selected. - */ public int getSelectedVersionNumber() { int row = table.getSelectedRow(); if (row >= 0) { @@ -167,33 +167,21 @@ public class VersionHistoryPanel extends JPanel implements Draggable { return -1; } - /* (non-Javadoc) - * @see ghidra.util.bean.dnd.Draggable#dragCanceled() - */ @Override public void dragCanceled(DragSourceDropEvent event) { // no-op } - /* (non-Javadoc) - * @see ghidra.util.bean.dnd.Draggable#getDragAction() - */ @Override public int getDragAction() { return dragAction; } - /* (non-Javadoc) - * @see ghidra.util.bean.dnd.Draggable#getDragSourceListener() - */ @Override public DragSourceListener getDragSourceListener() { return dragSourceAdapter; } - /* (non-Javadoc) - * @see ghidra.util.bean.dnd.Draggable#getTransferable(java.awt.Point) - */ @Override public Transferable getTransferable(Point p) { int row = table.rowAtPoint(p); @@ -204,9 +192,6 @@ public class VersionHistoryPanel extends JPanel implements Draggable { return null; } - /* (non-Javadoc) - * @see ghidra.util.bean.dnd.Draggable#isStartDragOk(java.awt.dnd.DragGestureEvent) - */ @Override public boolean isStartDragOk(DragGestureEvent e) { int row = table.rowAtPoint(e.getDragOrigin()); @@ -216,9 +201,6 @@ public class VersionHistoryPanel extends JPanel implements Draggable { return false; } - /* (non-Javadoc) - * @see ghidra.util.bean.dnd.Draggable#move() - */ @Override public void move() { // no-op @@ -363,6 +345,38 @@ public class VersionHistoryPanel extends JPanel implements Draggable { openWith(null); } + public List createPopupActions() { + + List list = new ArrayList<>(); + list.add(new DeleteAction()); + + Project project = tool.getProject(); + ToolChest toolChest = project.getLocalToolChest(); + if (toolChest == null) { + return list; + } + + ToolTemplate[] templates = toolChest.getToolTemplates(); + if (templates.length == 0) { + return list; + } + + list.add(new OpenDefaultAction()); + for (ToolTemplate toolTemplate : templates) { + list.add(new OpenWithAction(toolTemplate.getName())); + } + + return list; + } + + GTable getTable() { + return table; + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + private class MyCellRenderer extends GTableCellRenderer { @Override @@ -398,36 +412,11 @@ public class VersionHistoryPanel extends JPanel implements Draggable { } - public void addPopupActions(DialogComponentProvider provider) { - provider.addAction(new DeleteAction()); - } - - public List getDockingActions() { - List list = new ArrayList<>(table.getDefaultDockingActions()); - Project project = tool.getProject(); - ToolChest toolChest = project.getLocalToolChest(); - if (toolChest == null) { - return list; - } - - ToolTemplate[] templates = toolChest.getToolTemplates(); - if (templates.length == 0) { - return list; - } - - ToolTemplate defaultConfig = tool.getToolServices().getDefaultToolTemplate(domainFile); - if (defaultConfig != null) { - list.add(new OpenDefaultAction()); - } - for (final ToolTemplate toolTemplate : templates) { - list.add(new OpenWithAction(toolTemplate.getName())); - } - return list; - } - private abstract class HistoryTableAction extends DockingAction { + HistoryTableAction(String name) { super(name, "Version History Panel", false); + setHelpLocation(HELP); } @Override @@ -436,6 +425,15 @@ public class VersionHistoryPanel extends JPanel implements Draggable { if (mouseEvent == null) { return false; } + + if (context.getContextObject() != table) { + return false; + } + + if (domainFile == null) { + return false; + } + int rowAtPoint = table.rowAtPoint(mouseEvent.getPoint()); return rowAtPoint >= 0; } @@ -479,8 +477,6 @@ public class VersionHistoryPanel extends JPanel implements Draggable { setDescription("Opens the version using the " + toolName + " tool."); MenuData data = new MenuData(new String[] { "Open With", toolName }, "AAB"); setPopupMenuData(data); - DockingWindowManager dwm = DockingWindowManager.getInstance(table); - dwm.setMenuGroup(new String[] { "Open With" }, "AAB", "2"); } @Override @@ -526,9 +522,4 @@ public class VersionHistoryPanel extends JPanel implements Draggable { } } - - GTable getTable() { - return table; - } - } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/VersionControlShowHistoryAction.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/VersionControlShowHistoryAction.java index 7421dc745e..9acffa01fd 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/VersionControlShowHistoryAction.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/VersionControlShowHistoryAction.java @@ -28,8 +28,6 @@ import ghidra.framework.plugintool.Plugin; */ public class VersionControlShowHistoryAction extends VersionControlAction { - private VersionHistoryDialog dialog; - /** * Creates an action to show the version history for a single version controlled * domain file in the repository. @@ -71,13 +69,12 @@ public class VersionControlShowHistoryAction extends VersionControlAction { if (!checkRepositoryConnected()) { return; } + if (domainFiles.size() != 1) { return; } - if (dialog == null) { - dialog = new VersionHistoryDialog(); - } - dialog.setDomainFile(domainFiles.get(0)); + + VersionHistoryDialog dialog = new VersionHistoryDialog(domainFiles.get(0)); tool.showDialog(dialog, tool.getToolFrame()); } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/Tool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/Tool.java index 9f72cbf106..823ab8141d 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/Tool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/Tool.java @@ -56,13 +56,6 @@ public interface Tool extends DockingTool, ToolListener { */ public void exit(); - /** - * Suggests the tool to attempt to close(). This will be as though the user - * selected the close menu option on the tool or hit the closeWindow x button in - * the upper corner (Windows systems). - */ - public void close(); - /** * Can this tool be closed? * @param isExiting true if all of Ghidra is closing, false if just this tool is closing. 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 de97d4660d..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 @@ -22,7 +22,6 @@ import java.awt.*; import java.awt.event.KeyEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -34,6 +33,7 @@ import org.jdom.Element; import docking.*; import docking.action.*; +import docking.actions.PopupActionProvider; import docking.actions.ToolActions; import docking.framework.AboutDialog; import docking.framework.ApplicationInformationDisplayFactory; @@ -58,24 +58,24 @@ import ghidra.framework.plugintool.util.*; import ghidra.framework.project.ProjectDataService; import ghidra.framework.project.tool.ToolIconURL; import ghidra.util.*; -import ghidra.util.datastruct.WeakDataStructureFactory; -import ghidra.util.datastruct.WeakSet; import ghidra.util.task.Task; import ghidra.util.task.TaskLauncher; /** - * Base class that is a container to manage plugins and their actions, and - * to coordinate the firing of plugin events and tool events. A - * PluginTool may have visible components supplied by - *

ComponentProviders 
. These components may be docked within the - * tool, or moved out into their own windows. - *

The PluginTool also manages tasks that run in the background, and - * options used by the plugins. - *

+ * Base class that is a container to manage plugins and their actions, and to coordinate the + * firing of plugin events and tool events. A PluginTool may have visible components supplied by + *
ComponentProviders 
. These components may be docked within the tool, or moved + * out into their own windows. + * + *

Plugins normally add actions via {@link #addAction(DockingActionIf)}. There is also + * an alternate method for getting actions to appear in the popup context menu (see + * {@link #addPopupActionProvider(PopupActionProvider)}). The popup listener mechanism is generally not + * needed and should only be used in special circumstances (see {@link PopupActionProvider}). + * + *

The PluginTool also manages tasks that run in the background, and options used by the plugins. * */ -public abstract class PluginTool extends AbstractDockingTool - implements Tool, DockWinListener, ServiceProvider { +public abstract class PluginTool extends AbstractDockingTool implements Tool, ServiceProvider { private static final String DOCKING_WINDOWS_ON_TOP = "Docking Windows On Top"; @@ -96,8 +96,6 @@ public abstract class PluginTool extends AbstractDockingTool private DialogManager dialogMgr; private PropertyChangeSupport propertyChangeMgr; - private WeakSet popupListeners = - WeakDataStructureFactory.createSingleThreadAccessWeakSet(); private OptionsChangeListener optionsListener = new ToolOptionsListener(); protected ManagePluginsDialog manageDialog; protected ExtensionTableProvider extensionTableProvider; @@ -192,7 +190,7 @@ public abstract class PluginTool extends AbstractDockingTool List windowIcons = ApplicationInformationDisplayFactory.getWindowIcons(); DockingWindowManager newManager = - new DockingWindowManager(this, windowIcons, this, isModal, isDockable, hasStatus, null); + new DockingWindowManager(this, windowIcons, isModal, isDockable, hasStatus, null); return newManager; } @@ -264,17 +262,6 @@ public abstract class PluginTool extends AbstractDockingTool return winMgr.isWindowsOnTop(); } - /** - * Add popup listener that is notified when the popup menu is about to be - * displayed. - * - * @param listener listener that is notified when the popup menu is to - * be displayed - */ - public void addPopupListener(PopupListener listener) { - popupListeners.add(listener); - } - /** * Returns the manage plugins dialog that is currently * being used. @@ -308,15 +295,6 @@ public abstract class PluginTool extends AbstractDockingTool showDialog(extensionTableProvider); } - /** - * Remove popup listener - * @param listener listener that is notified when the popup menu is to - * be displayed - */ - public void removePopupListener(PopupListener listener) { - popupListeners.remove(listener); - } - /** * Set whether a component's header should be shown; the header is the component that * is dragged in order to move the component within the tool, or out of the tool @@ -1031,18 +1009,18 @@ public abstract class PluginTool extends AbstractDockingTool 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)); @@ -1102,19 +1080,6 @@ public abstract class PluginTool extends AbstractDockingTool eventMgr.clearLastEvents(); } - @Override - public List getPopupActions(ActionContext context) { - - List actionList = new ArrayList<>(); - for (PopupListener pl : popupListeners) { - List actions = pl.getPopupActions(context); - if (actions != null) { - actionList.addAll(actions); - } - } - return actionList; - } - /** * Close this tool: *

    @@ -1390,36 +1355,6 @@ public abstract class PluginTool extends AbstractDockingTool 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/PopupListener.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PopupListener.java deleted file mode 100644 index 5cca8158d7..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PopupListener.java +++ /dev/null @@ -1,40 +0,0 @@ -/* ### - * 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. - * 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 ghidra.framework.plugintool; - -import java.util.List; - -import docking.ActionContext; -import docking.action.DockingActionIf; - -/** - * PopupListener provides notification when the popup action - * menu is displayed. - */ -public interface PopupListener { - - /** - * Provides notification that the popup menu is about to be displayed - * and allows a set of temporary actions to be included in the popup menu. - * Actions returned will be included in the menu if they have a valid popup - * menu path and respond true to the isValidContext method. - * @param context the ActionContext - * @return list of temporary popup actions (null may be returned) - */ - List getPopupActions(ActionContext context); - -} 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); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java index c9804a1f47..cadcb8c8d4 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java @@ -84,7 +84,7 @@ public class GhidraTool extends PluginTool { @Override protected DockingWindowManager createDockingWindowManager(boolean isDockable, boolean hasStatus, boolean isModal) { - return new DockingWindowManager(this, null, this, isModal, isDockable, hasStatus, + return new DockingWindowManager(this, null, isModal, isDockable, hasStatus, new OpenFileDropHandlerFactory(this)); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java index 95eccb2390..2ea3e10283 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java @@ -136,9 +136,8 @@ public class VersionControlScreenShots extends GhidraScreenShotGenerator { @Test public void testVersionHistory() throws Exception { - VersionHistoryDialog dialog = new VersionHistoryDialog(); DomainFile df = createDomainFile(); - dialog.setDomainFile(df); + VersionHistoryDialog dialog = new VersionHistoryDialog(df); runSwing(() -> tool.showDialog(dialog)); VersionHistoryDialog d = waitForDialogComponent(dialog.getClass());