diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/SampleProgramTreePlugin.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/SampleProgramTreePlugin.java index 26657268a9..a8cc03eed6 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/SampleProgramTreePlugin.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/SampleProgramTreePlugin.java @@ -73,14 +73,15 @@ public class SampleProgramTreePlugin extends ProgramPlugin { public void actionPerformed(ActionContext context) { modularize(); } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return currentProgram != null; + } }; action.setMenuBarData( new MenuData(new String[] { "Misc", "Create Sample Tree" }, null, null)); - - action.setEnabled(false); - action.setDescription("Plugin to create a program tree and modularize accordingly"); - enableOnProgram(action); tool.addAction(action); }// end of createActions() diff --git a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/TemplateProgramPlugin.java b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/TemplateProgramPlugin.java index 4291c0e46a..7eb6e75d99 100644 --- a/Ghidra/Extensions/sample/src/main/java/ghidra/examples/TemplateProgramPlugin.java +++ b/Ghidra/Extensions/sample/src/main/java/ghidra/examples/TemplateProgramPlugin.java @@ -32,7 +32,6 @@ import ghidra.program.util.ProgramChangeRecord; import ghidra.util.HelpLocation; import ghidra.util.Msg; - /** * Sample plugin for dealing with Programs. The base class handles * the event processing and enabling/disabling of actions. This @@ -51,107 +50,98 @@ import ghidra.util.Msg; //@formatter:on public class TemplateProgramPlugin extends ProgramPlugin implements DomainObjectListener { - private DockingAction action; + private DockingAction action; - /******************************************************************* - * - * Standard Plugin Constructor - * - *******************************************************************/ - public TemplateProgramPlugin(PluginTool tool) { + /******************************************************************* + * + * Standard Plugin Constructor + * + *******************************************************************/ + public TemplateProgramPlugin(PluginTool tool) { - super(tool, - true, // true means this plugin consumes ProgramLocation events - false); // false means this plugin does not consume - // ProgramSelection events - // the base class ProgramPlugin handles all the event registering + super(tool, true, // true means this plugin consumes ProgramLocation events + false); // false means this plugin does not consume + // ProgramSelection events + // the base class ProgramPlugin handles all the event registering - // set up list of actions. - setupActions(); - } - - /** - * This is the callback method for DomainObjectChangedEvents. - */ - public void domainObjectChanged(DomainObjectChangedEvent ev) { - for (int i=0; i< ev.numRecords(); i++) { - DomainObjectChangeRecord record = ev.getChangeRecord(i); - if (record instanceof ProgramChangeRecord) { - @SuppressWarnings("unused") - ProgramChangeRecord r = (ProgramChangeRecord)record; - // code for processing the record... - // ... - } - } - } + // set up list of actions. + setupActions(); + } - /** - * Called when the program is opened. - */ - @Override - protected void programActivated(Program program) { - program.addListener(this); - } - /** - * Called when the program is closed. - */ - @Override - protected void programDeactivated(Program program) { - program.removeListener(this); - } + /** + * This is the callback method for DomainObjectChangedEvents. + */ + @Override + public void domainObjectChanged(DomainObjectChangedEvent ev) { + for (int i = 0; i < ev.numRecords(); i++) { + DomainObjectChangeRecord record = ev.getChangeRecord(i); + if (record instanceof ProgramChangeRecord) { + @SuppressWarnings("unused") + ProgramChangeRecord r = (ProgramChangeRecord) record; + // code for processing the record... + // ... + } + } + } - /******************************************************************* - ******************************************************************** - ** - ** - ** Function 1 - ** - ** - ******************************************************************** - *******************************************************************/ - private void Function_1 () { - // do something with a program location - Msg.info(this, getPluginDescription().getName() - + ": Program Location==> " + currentLocation.getAddress()); - } + /** + * Called when the program is opened. + */ + @Override + protected void programActivated(Program program) { + program.addListener(this); + } + /** + * Called when the program is closed. + */ + @Override + protected void programDeactivated(Program program) { + program.removeListener(this); + } - /** - * Set up Actions - */ - private void setupActions() { + /******************************************************************* + ******************************************************************** + ** + ** + ** Function 1 + ** + ** + ******************************************************************** + *******************************************************************/ + private void Function_1() { + // do something with a program location + Msg.info(this, getPluginDescription().getName() + ": Program Location==> " + + currentLocation.getAddress()); + } - // - // Function 1 - // - action = new DockingAction("Function 1 Code", getName() ) { - @Override - public void actionPerformed(ActionContext e) { - Function_1(); - } - - @Override - public boolean isValidContext( ActionContext context ) { - return context instanceof ListingActionContext; - } - }; - action.setEnabled( false ); + /** + * Set up Actions + */ + private void setupActions() { + // + // Function 1 + // + action = new DockingAction("Function 1 Code", getName()) { + @Override + public void actionPerformed(ActionContext e) { + Function_1(); + } - action.setMenuBarData( new MenuData( - new String[]{"Misc", "Menu","Menu item 1"}, null, null ) ); - - - // call method in base class to enable this action when a - // program location event comes in; disable it when focus is - // lost; it will be disable when the program is closed - enableOnLocation(action); - - action.setHelpLocation(new HelpLocation("SampleHelpTopic", - "TemplateProgramPlugin_Anchor_Name")); - tool.addAction(action); - } + @Override + public boolean isValidContext(ActionContext context) { + return context instanceof ListingActionContext; + } + }; + action.setEnabled(false); + action.setMenuBarData( + new MenuData(new String[] { "Misc", "Menu", "Menu item 1" }, null, null)); + action.setHelpLocation( + new HelpLocation("SampleHelpTopic", "TemplateProgramPlugin_Anchor_Name")); + tool.addAction(action); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/ProgramPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/ProgramPlugin.java index aba3f1b5c3..6ac696bde7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/ProgramPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/ProgramPlugin.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,6 +15,10 @@ */ package ghidra.app.plugin; +import java.util.ArrayList; + +import docking.ActionContext; +import docking.action.DockingAction; import ghidra.app.events.*; import ghidra.app.services.GoToService; import ghidra.framework.plugintool.*; @@ -25,10 +28,6 @@ import ghidra.program.model.listing.*; import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramSelection; -import java.util.ArrayList; - -import docking.action.DockingAction; - /** * Base class to handle common program events: Program Open/Close, * Program Location, Program Selection, and Program Highlight. @@ -87,10 +86,10 @@ public abstract class ProgramPlugin extends Plugin { } registerEventConsumed(ProgramOpenedPluginEvent.class); registerEventConsumed(ProgramClosedPluginEvent.class); - programActionList = new ArrayList(3); - locationActionList = new ArrayList(3); - selectionActionList = new ArrayList(3); - highlightActionList = new ArrayList(3); + programActionList = new ArrayList<>(3); + locationActionList = new ArrayList<>(3); + selectionActionList = new ArrayList<>(3); + highlightActionList = new ArrayList<>(3); } public ProgramPlugin(PluginTool tool, boolean consumeLocationChange, @@ -201,7 +200,10 @@ public abstract class ProgramPlugin extends Plugin { * the program is closed. * @throws IllegalArgumentException if this action was called for * another enableOnXXX(PlugAction) method. + * @deprecated {@link ActionContext} is now used for action enablement. Deprecated in 9.1; to + * be removed no sooner than two versions after that. */ + @Deprecated protected void enableOnProgram(DockingAction action) { if (locationActionList.contains(action)) { throw new IllegalArgumentException("Action already added to location action list"); @@ -222,7 +224,10 @@ public abstract class ProgramPlugin extends Plugin { * is null. * @throws IllegalArgumentException if this action was called for * another enableOnXXX(PlugAction) method. + * @deprecated {@link ActionContext} is now used for action enablement. Deprecated in 9.1; to + * be removed no sooner than two versions after that. */ + @Deprecated protected void enableOnLocation(DockingAction action) { if (programActionList.contains(action)) { throw new IllegalArgumentException("Action already added to program action list"); @@ -243,7 +248,10 @@ public abstract class ProgramPlugin extends Plugin { * the selection is null or empty. * @throws IllegalArgumentException if this action was called for * another enableOnXXX(PlugAction) method. + * @deprecated {@link ActionContext} is now used for action enablement. Deprecated in 9.1; to + * be removed no sooner than two versions after that. */ + @Deprecated protected void enableOnSelection(DockingAction action) { if (programActionList.contains(action)) { throw new IllegalArgumentException("Action already added to program action list"); @@ -263,7 +271,10 @@ public abstract class ProgramPlugin extends Plugin { * the highlight is null or empty. * @throws IllegalArgumentException if this action was called for * another enableOnXXX(PlugAction) method. + * @deprecated {@link ActionContext} is now used for action enablement. Deprecated in 9.1; to + * be removed no sooner than two versions after that. */ + @Deprecated protected void enableOnHighlight(DockingAction action) { if (programActionList.contains(action)) { throw new IllegalArgumentException("Action already added to program action list"); @@ -378,8 +389,8 @@ public abstract class ProgramPlugin extends Plugin { if (currentProgram == null) { return; } - firePluginEvent(new ProgramSelectionPluginEvent(getName(), new ProgramSelection(set), - currentProgram)); + firePluginEvent( + new ProgramSelectionPluginEvent(getName(), new ProgramSelection(set), currentProgram)); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkProvider.java index df694141a4..9ed8ec667d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkProvider.java @@ -58,9 +58,9 @@ public class BookmarkProvider extends ComponentProviderAdapter { BookmarkProvider(PluginTool tool, BookmarkPlugin plugin) { super(tool, "Bookmarks", plugin.getName(), ProgramActionContext.class); - setIcon(BookmarkNavigator.NOTE_ICON, true); - setDefaultKeyBinding( - new KeyBindingData(KeyEvent.VK_B, DockingUtils.CONTROL_KEY_MODIFIER_MASK)); + setIcon(BookmarkNavigator.NOTE_ICON); + addToToolbar(); + setKeyBinding(new KeyBindingData(KeyEvent.VK_B, DockingUtils.CONTROL_KEY_MODIFIER_MASK)); model = new BookmarkTableModel(tool, null); threadedTablePanel = new GhidraThreadedTablePanel<>(model); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java index f66e9e4a8c..8fa58d5836 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java @@ -107,7 +107,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain setWindowMenuGroup(TITLE); setDefaultWindowPosition(WindowPosition.BOTTOM); - setIcon(CallTreePlugin.PROVIDER_ICON, true); + setIcon(CallTreePlugin.PROVIDER_ICON); + addToToolbar(); setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Plugin")); addToTool(); 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 430302b29a..fb777ec418 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 @@ -117,13 +117,18 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter this.plugin = plugin; this.formatMgr = formatMgr; + setConnected(isConnected); + setIcon(ResourceManager.loadImage("images/Browser.gif")); if (!isConnected) { setTransient(); } + else { + addToToolbar(); + } setHelpLocation(new HelpLocation("CodeBrowserPlugin", "Code_Browser")); setDefaultWindowPosition(WindowPosition.RIGHT); - setIcon(ResourceManager.loadImage("images/Browser.gif"), isConnected); + listingPanel = new ListingPanel(formatMgr); listingPanel.enablePropertyBasedColorModel(true); decorationPanel = new ListingPanelContainer(listingPanel, isConnected); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java index 78660773b9..85df4d7d90 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/commentwindow/CommentWindowPlugin.java @@ -15,7 +15,6 @@ */ package ghidra.app.plugin.core.commentwindow; -import docking.ActionContext; import docking.action.DockingAction; import ghidra.app.CorePluginPackage; import ghidra.app.events.ProgramSelectionPluginEvent; @@ -186,13 +185,7 @@ public class CommentWindowPlugin extends ProgramPlugin implements DomainObjectLi private void createActions() { - selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { - @Override - protected void makeSelection(ActionContext context) { - selectComment(provider.selectComment()); - } - }; - + selectAction = new MakeProgramSelectionAction(this, provider.getTable()); tool.addLocalAction(provider, selectAction); DockingAction selectionAction = new SelectionNavigationAction(this, provider.getTable()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java index c0fe79acb8..0d03a6e098 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java @@ -100,7 +100,8 @@ public class DataTypesProvider extends ComponentProviderAdapter { this.plugin = plugin; setTitle(TITLE); - setIcon(ResourceManager.loadImage(DATA_TYPES_ICON), true); + setIcon(ResourceManager.loadImage(DATA_TYPES_ICON)); + addToToolbar(); navigationHistory.setAllowDuplicates(true); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java index e51e9a8082..e483081b6e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datawindow/DataWindowPlugin.java @@ -18,7 +18,6 @@ package ghidra.app.plugin.core.datawindow; import java.util.ArrayList; import java.util.Iterator; -import docking.ActionContext; import docking.action.DockingAction; import ghidra.app.CorePluginPackage; import ghidra.app.events.ProgramSelectionPluginEvent; @@ -180,13 +179,7 @@ public class DataWindowPlugin extends ProgramPlugin implements DomainObjectListe */ private void createActions() { - selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { - @Override - protected void makeSelection(ActionContext context) { - selectData(provider.selectData()); - } - }; - + selectAction = new MakeProgramSelectionAction(this, provider.getTable()); tool.addLocalAction(provider, selectAction); filterAction = new FilterAction(this); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableDialog.java index b4e16b8bd5..a6bfb6ef9e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableDialog.java @@ -524,10 +524,28 @@ public class AddressTableDialog extends DialogComponentProvider { private void createAction() { - DockingAction selectAction = new MakeProgramSelectionAction(DIALOG_NAME, resultsTable) { + DockingAction selectAction = new MakeProgramSelectionAction(plugin, resultsTable) { @Override - protected void makeSelection(ActionContext context) { - doMakeSelection(); + protected ProgramSelection makeSelection(ActionContext context) { + Program program = plugin.getProgram(); + AddressSet set = new AddressSet(); + AutoTableDisassemblerModel model = plugin.getModel(); + int[] selectedRows = resultsTable.getSelectedRows(); + for (int selectedRow : selectedRows) { + Address selectedAddress = model.getAddress(selectedRow); + AddressTable addrTab = model.get(selectedAddress); + if (addrTab != null) { + set.addRange(selectedAddress, + selectedAddress.add(addrTab.getByteLength() - 1)); + } + } + ProgramSelection selection = new ProgramSelection(set); + if (!set.isEmpty()) { + plugin.firePluginEvent( + new ProgramSelectionPluginEvent(plugin.getName(), selection, program)); + } + + return selection; } }; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AutoTableDisassemblerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AutoTableDisassemblerPlugin.java index 5529704d10..bc9a610df5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AutoTableDisassemblerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AutoTableDisassemblerPlugin.java @@ -140,15 +140,19 @@ public class AutoTableDisassemblerPlugin extends ProgramPlugin implements Domain public void actionPerformed(ActionContext context) { startDialog(); } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return currentProgram != null; + } }; findTableAction.setHelpLocation( new HelpLocation(HelpTopics.SEARCH, findTableAction.getName())); findTableAction.setMenuBarData( new MenuData(new String[] { ToolConstants.MENU_SEARCH, "For Address Tables..." }, null, "search for")); - findTableAction.setDescription(getPluginDescription().getDescription()); - enableOnLocation(findTableAction); + tool.addAction(findTableAction); } // end of createActions() diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java index d42eee363e..8ed11b2128 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functionwindow/FunctionWindowPlugin.java @@ -190,13 +190,7 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL private void addSelectAction() { - selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { - @Override - protected void makeSelection(ActionContext context) { - selectFunctions(provider.selectFunctions()); - } - }; - + selectAction = new MakeProgramSelectionAction(this, provider.getTable()); tool.addLocalAction(provider, selectAction); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java index 0f80af9006..058f64a356 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java @@ -84,7 +84,8 @@ class MemoryMapProvider extends ComponentProviderAdapter { setHelpLocation(new HelpLocation(plugin.getName(), getName())); memManager = plugin.getMemoryMapManager(); - setIcon(ResourceManager.loadImage(MEMORY_IMAGE), true); + setIcon(ResourceManager.loadImage(MEMORY_IMAGE)); + addToToolbar(); mainPanel = buildMainPanel(); addToTool(); addLocalActions(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java index c92c30c7a6..d2a13de27b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/locationreferences/LocationReferencesProvider.java @@ -249,12 +249,8 @@ public class LocationReferencesProvider extends ComponentProviderAdapter homeAction.setToolBarData(new ToolBarData(HOME_ICON)); updateHomeActionState(); - selectionAction = new MakeProgramSelectionAction(getName(), referencesPanel.getTable()) { - @Override - protected void makeSelection(ActionContext context) { - doMakeSelection(); - } - }; + selectionAction = + new MakeProgramSelectionAction(locationReferencesPlugin, referencesPanel.getTable()); highlightAction = new ToggleDockingAction("Highlight Matches", getName()) { @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterManagerProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterManagerProvider.java index 7b2d414f05..ef19bb0c9d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterManagerProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterManagerProvider.java @@ -66,7 +66,8 @@ public class RegisterManagerProvider extends ComponentProviderAdapter { buildComponent(); setHelpLocation(new HelpLocation("RegisterPlugin", "Register_Manager")); - setIcon(REGISTER_ICON, true); + setIcon(REGISTER_ICON); + addToToolbar(); setDefaultWindowPosition(WindowPosition.WINDOW); updateMgr = new SwingUpdateManager(500, () -> update()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/reloc/RelocationTablePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/reloc/RelocationTablePlugin.java index a675c59369..0fbc7d89ef 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/reloc/RelocationTablePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/reloc/RelocationTablePlugin.java @@ -15,7 +15,6 @@ */ package ghidra.app.plugin.core.reloc; -import docking.ActionContext; import docking.action.DockingAction; import ghidra.app.CorePluginPackage; import ghidra.app.events.*; @@ -63,13 +62,7 @@ public class RelocationTablePlugin extends Plugin implements DomainObjectListene private void createActions() { - DockingAction selectAction = - new MakeProgramSelectionAction(getName(), provider.getTable()) { - @Override - protected void makeSelection(ActionContext context) { - doMakeSelection(); - } - }; + DockingAction selectAction = new MakeProgramSelectionAction(this, provider.getTable()); tool.addLocalAction(provider, selectAction); DockingAction navigationAction = new SelectionNavigationAction(this, provider.getTable()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java index ba6809d469..2c3ce5a431 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/scalartable/ScalarSearchProvider.java @@ -23,7 +23,6 @@ import javax.swing.*; import javax.swing.border.Border; import docking.*; -import docking.action.DockingAction; import docking.help.HelpService; import docking.widgets.label.GLabel; import docking.widgets.table.GTableFilterPanel; @@ -62,8 +61,6 @@ public class ScalarSearchProvider extends ComponentProviderAdapter { private GhidraTable scalarTable; private ScalarSearchModel scalarModel; - private DockingAction selectAction; - private ProgramSelection currentSelection; private Program program; private String primarySubTitle; @@ -258,21 +255,11 @@ public class ScalarSearchProvider extends ComponentProviderAdapter { private void createActions() { - selectAction = new MakeProgramSelectionAction(getName(), getTable()) { - @Override - protected void makeSelection(ActionContext context) { - selectDataInProgramFromTable(getSelection()); - } - }; - - tool.addLocalAction(this, selectAction); - - DockingAction selectionAction = new SelectionNavigationAction(plugin, getTable()); - tool.addLocalAction(this, selectionAction); + tool.addLocalAction(this, new MakeProgramSelectionAction(plugin, scalarTable)); + tool.addLocalAction(this, new SelectionNavigationAction(plugin, getTable())); GhidraTable table = threadedTablePanel.getTable(); - DockingAction removeItemsAction = new DeleteTableRowAction(table, plugin.getName()); - tool.addLocalAction(this, removeItemsAction); + tool.addLocalAction(this, new DeleteTableRowAction(table, plugin.getName())); } //================================================================================================== diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java index 5613dd0d22..ce998da852 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java @@ -102,7 +102,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { this.plugin = plugin; setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName())); - setIcon(ResourceManager.loadImage("images/play.png"), true); + setIcon(ResourceManager.loadImage("images/play.png")); + addToToolbar(); setWindowGroup(WINDOW_GROUP); build(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableModel.java index 144947d550..583f7aa42d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableModel.java @@ -21,7 +21,9 @@ import ghidra.docking.settings.Settings; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.ServiceProvider; import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSet; import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramSelection; import ghidra.program.util.string.FoundString; import ghidra.util.datastruct.Accumulator; import ghidra.util.exception.CancelledException; @@ -46,6 +48,20 @@ public class StringTableModel extends AddressBasedTableModel { } } + @Override + public ProgramSelection getProgramSelection(int[] rows) { + + AddressSet addressSet = new AddressSet(); + for (int row : rows) { + FoundString foundString = getRowObject(row); + Address addr = foundString.getAddress(); + if (addr != null) { + addressSet.addRange(addr, addr.add(foundString.getLength() - 1)); + } + } + return new ProgramSelection(addressSet); + } + @Override public Address getAddress(int row) { FoundString stringData = getRowObject(row); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTablePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTablePlugin.java index 4c7953d000..ce087c6f34 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTablePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTablePlugin.java @@ -28,7 +28,8 @@ import ghidra.app.services.GoToService; import ghidra.app.util.HelpTopics; import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.*; +import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.framework.plugintool.util.ToolConstants; import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramSelection; @@ -99,8 +100,7 @@ public class StringTablePlugin extends ProgramPlugin { */ @Override public void dispose() { - ArrayList list = - new ArrayList<>(transientProviders); + ArrayList list = new ArrayList<>(transientProviders); for (StringTableProvider stringTableProvider : list) { stringTableProvider.closeComponent(); @@ -114,8 +114,7 @@ public class StringTablePlugin extends ProgramPlugin { if (transientProviders.isEmpty()) { return; } - ArrayList list = - new ArrayList<>(transientProviders); + ArrayList list = new ArrayList<>(transientProviders); for (StringTableProvider stringTableProvider : list) { stringTableProvider.programClosed(program); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java index 98e385456d..4dbda3a731 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java @@ -30,15 +30,12 @@ import docking.widgets.label.GLabel; import docking.widgets.table.*; import docking.widgets.table.threaded.ThreadedTableModel; import docking.widgets.textfield.IntegerTextField; -import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.services.GoToService; import ghidra.app.util.HelpTopics; import ghidra.docking.settings.SettingsImpl; import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.framework.model.DomainObjectListener; import ghidra.framework.plugintool.ComponentProviderAdapter; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSet; import ghidra.program.model.data.StringDataInstance; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.DumbMemBufferImpl; @@ -305,10 +302,16 @@ public class StringTableProvider extends ComponentProviderAdapter implements Dom makeCharArrayAction.setHelpLocation(makeStringHelp); addLocalAction(makeCharArrayAction); - DockingAction selectAction = new MakeProgramSelectionAction(plugin.getName(), table) { + DockingAction selectAction = new MakeProgramSelectionAction(plugin, table) { @Override - protected void makeSelection(ActionContext context) { - doMakeSelection(); + protected ProgramSelection makeSelection(ActionContext context) { + ProgramSelection selection = super.makeSelection(context); + + // Also make sure this plugin keeps track of the new selection, since it will + // not receive this new event. + // TODO this should not be necessary; old code perhaps? + plugin.setSelection(selection); + return selection; } }; @@ -321,37 +324,6 @@ public class StringTableProvider extends ComponentProviderAdapter implements Dom } - private void doMakeSelection() { - AddressSet set = new AddressSet(); - - addToAddressSet(set, table.getSelectedRows()); - - if (!set.isEmpty()) { - - ProgramSelection ps = new ProgramSelection(set); - - // This event is given this specific source name because AsciiFinderPlugin - // is looking for it, so it can circumvent - // some unwanted behavior. See AsciiFinderPlugin.firePluginEvent for details. - plugin.firePluginEvent(new ProgramSelectionPluginEvent( - "AsciiFinderDialogFiredSelection", ps, currentProgram)); - - // Also make sure this plugin keeps track of the new selection, since it will - // not receive this new event. - plugin.setSelection(ps); - } - } - - void addToAddressSet(AddressSet modifiableSet, int[] rows) { - for (int rowValue : rows) { - FoundString foundString = stringModel.getRowObject(rowValue); - Address addr = foundString.getAddress(); - if (addr != null) { - modifiableSet.addRange(addr, addr.add(foundString.getLength() - 1)); - } - } - } - private JPanel createMainPanel() { JPanel panel = new JPanel(new BorderLayout()); panel.add(buildTablePanel(), BorderLayout.CENTER); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java index c52435c61c..9ee8a34e80 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/ViewStringsPlugin.java @@ -100,15 +100,7 @@ public class ViewStringsPlugin extends ProgramPlugin implements DomainObjectList refreshAction.setHelpLocation(new HelpLocation("ViewStringsPlugin", "Refresh")); tool.addLocalAction(provider, refreshAction); - selectAction = new MakeProgramSelectionAction(getName(), provider.getTable()) { - - @Override - protected void makeSelection(ActionContext context) { - selectData(provider.selectData()); - } - }; - - tool.addLocalAction(provider, selectAction); + tool.addLocalAction(provider, new MakeProgramSelectionAction(this, provider.getTable())); linkNavigationAction = new SelectionNavigationAction(this, provider.getTable()); tool.addLocalAction(provider, linkNavigationAction); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java index d55419243d..c6eee98a88 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java @@ -103,7 +103,8 @@ public class SymbolTreeProvider extends ComponentProviderAdapter { super(tool, NAME, plugin.getName()); this.plugin = plugin; - setIcon(ICON, true); + setIcon(ICON); + addToToolbar(); domainObjectListener = new SymbolTreeProviderDomainObjectListener(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferenceProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferenceProvider.java index 0d7f861661..6107fe3432 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferenceProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferenceProvider.java @@ -44,7 +44,8 @@ class ReferenceProvider extends ComponentProviderAdapter { super(plugin.getTool(), "Symbol References", plugin.getName(), ProgramActionContext.class); this.plugin = plugin; - setIcon(ICON, true); + setIcon(ICON); + addToToolbar(); setHelpLocation(new HelpLocation(plugin.getName(), "Symbol_References")); setWindowGroup("symbolTable"); setIntraGroupPosition(WindowPosition.RIGHT); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java index 3f170249a6..3338669448 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolProvider.java @@ -27,14 +27,11 @@ import docking.DockingUtils; import docking.action.KeyBindingData; import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramSymbolActionContext; -import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.util.SymbolInspector; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.ComponentProviderAdapter; -import ghidra.framework.plugintool.PluginEvent; import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.Symbol; -import ghidra.program.util.ProgramSelection; import ghidra.util.HelpLocation; import ghidra.util.table.GhidraTable; import resources.ResourceManager; @@ -52,9 +49,10 @@ class SymbolProvider extends ComponentProviderAdapter { super(plugin.getTool(), "Symbol Table", plugin.getName(), ProgramActionContext.class); this.plugin = plugin; - setIcon(ICON, true); - setDefaultKeyBinding( - new KeyBindingData(KeyEvent.VK_T, DockingUtils.CONTROL_KEY_MODIFIER_MASK)); + setIcon(ICON); + addToToolbar(); + setKeyBinding(new KeyBindingData(KeyEvent.VK_T, DockingUtils.CONTROL_KEY_MODIFIER_MASK)); + setHelpLocation(new HelpLocation(plugin.getName(), "Symbol_Table")); setWindowGroup("symbolTable"); renderer = new SymbolRenderer(); @@ -90,13 +88,6 @@ class SymbolProvider extends ComponentProviderAdapter { symbolKeyModel.delete(rowObjects); } - void makeSelection() { - ProgramSelection selection = symbolPanel.getProgramSelection(); - PluginEvent event = - new ProgramSelectionPluginEvent(plugin.getName(), selection, plugin.getProgram()); - plugin.firePluginEvent(event); - } - void setFilter() { symbolPanel.setFilter(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java index e6f57bbfd8..fbfa385b4b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java @@ -393,13 +393,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener { DockingAction editExternalLocationAction = new EditExternalLocationAction(this); tool.addLocalAction(symProvider, editExternalLocationAction); - makeSelectionAction = new MakeProgramSelectionAction(getName(), symProvider.getTable()) { - @Override - protected void makeSelection(ActionContext context) { - symProvider.makeSelection(); - } - }; - + makeSelectionAction = new MakeProgramSelectionAction(this, symProvider.getTable()); makeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup); tool.addLocalAction(symProvider, makeSelectionAction); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java index 2eb2592d8e..df42a812ee 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/table/TableComponentProvider.java @@ -159,10 +159,15 @@ public class TableComponentProvider extends ComponentProviderAdapter private void createActions(final Plugin plugin) { GhidraTable table = threadedPanel.getTable(); - selectAction = new MakeProgramSelectionAction(tableServicePlugin.getName(), table) { + selectAction = new MakeProgramSelectionAction(tableServicePlugin, table) { @Override - protected void makeSelection(ActionContext context) { - doMakeSelection(plugin); + protected ProgramSelection makeSelection(ActionContext context) { + + ProgramSelection selection = table.getProgramSelection(); + navigatable.goTo(program, new ProgramLocation(program, selection.getMinAddress())); + navigatable.setSelection(selection); + navigatable.requestFocus(); + return selection; } }; selectAction.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Make_Selection")); @@ -265,19 +270,6 @@ public class TableComponentProvider extends ComponentProviderAdapter } } - private void doMakeSelection(Plugin plugin) { - ProgramSelection selection = threadedPanel.getTable().getProgramSelection(); - Program modelProgram = model.getProgram(); - if (modelProgram == null || selection.getNumAddresses() == 0) { - return; - } - - navigatable.goTo(model.getProgram(), - new ProgramLocation(modelProgram, selection.getMinAddress())); - navigatable.setSelection(selection); - navigatable.requestFocus(); - } - @Override public void closeComponent() { if (navigatable != null) { 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 2190510f69..2999d21a87 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 @@ -160,8 +160,15 @@ public class TableChooserDialog extends DialogComponentProvider DockingAction selectAction = new MakeProgramSelectionAction(owner, table) { @Override - protected void makeSelection(ActionContext context) { - doMakeSelection(); + protected ProgramSelection makeSelection(ActionContext context) { + ProgramSelection selection = table.getProgramSelection(); + if (navigatable != null) { + navigatable.goTo(program, + new ProgramLocation(program, selection.getMinAddress())); + navigatable.setSelection(selection); + navigatable.requestFocus(); + } + return selection; } }; @@ -173,18 +180,6 @@ public class TableChooserDialog extends DialogComponentProvider addAction(selectionNavigationAction); } - private void doMakeSelection() { - ProgramSelection selection = table.getProgramSelection(); - if (program == null || program.isClosed() || selection.getNumAddresses() == 0) { - return; - } - if (navigatable != null) { - navigatable.goTo(program, new ProgramLocation(program, selection.getMinAddress())); - navigatable.setSelection(selection); - navigatable.requestFocus(); - } - } - public void show() { DockingWindowManager manager = DockingWindowManager.getActiveInstance(); tool.showDialog(this, manager.getMainWindow()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/GhidraTable.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/GhidraTable.java index 08bc4d7047..4d16646936 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/GhidraTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/GhidraTable.java @@ -191,10 +191,10 @@ public class GhidraTable extends GTable { } /** - * Returns the program selection equivalent - * to the rows currently selected in the table. This method - * is only valid when the underlying table model implements - * ProgramTableModel. + * Returns the program selection equivalent to the rows currently selected in the table. + * This method is only valid when the underlying table model implements + * {@link ProgramTableModel}. + *

* Returns null if no rows are selected or * the underlying model does not implement ProgramTableModel. * @return the program selection or null. @@ -207,6 +207,20 @@ public class GhidraTable extends GTable { return programTableModel.getProgramSelection(getSelectedRows()); } + /** + * Returns the program being used by this table; null if the underlying model does not + * implement {@link ProgramTableModel} + * + * @return the table's program + */ + public Program getProgram() { + ProgramTableModel programTableModel = getProgramTableModel(dataModel); + if (programTableModel == null) { + return null; + } + return programTableModel.getProgram(); + } + private ProgramTableModel getProgramTableModel(TableModel model) { if (model instanceof ProgramTableModel) { return (ProgramTableModel) model; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/MakeProgramSelectionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/MakeProgramSelectionAction.java index 3ebf7ec25c..da4a2e138e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/MakeProgramSelectionAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/actions/MakeProgramSelectionAction.java @@ -15,26 +15,53 @@ */ package ghidra.util.table.actions; -import javax.swing.JTable; import javax.swing.KeyStroke; import docking.ActionContext; import docking.action.*; +import ghidra.app.events.ProgramSelectionPluginEvent; +import ghidra.framework.plugintool.Plugin; +import ghidra.framework.plugintool.PluginEvent; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramSelection; import ghidra.util.HelpLocation; +import ghidra.util.table.GhidraTable; import resources.Icons; /** - * An action to make a program selection based on the given table's selection. The clients - * must implement the make selection code, as they know their own data. Also, for the context to + * An action to make a program selection based on the given table's selection. For the context to * work, the provider using this action must create an {@link ActionContext} that returns a - * context object that is the table passed to this action's constructor. + * context object that is the table passed to this action's constructor; otherwise, this action + * will not be enabled correctly. */ -public abstract class MakeProgramSelectionAction extends DockingAction { +public class MakeProgramSelectionAction extends DockingAction { - private JTable table; + private GhidraTable table; - public MakeProgramSelectionAction(String owner, JTable table) { + // we will have one of these fields be non-null after construction + private Plugin plugin; + + /** + * Special constructor for clients that do not have a plugin. Clients using this + * constructor must override {@link #makeSelection(ActionContext)}. + * + * @param owner the action's owner + * @param table the table needed for this action + */ + public MakeProgramSelectionAction(String owner, GhidraTable table) { super("Make Selection", owner, KeyBindingType.SHARED); + } + + /** + * This normal constructor for this action. The given plugin will be used along with the + * given table to fire program selection events as the action is executed. + * + * @param plugin the plugin + * @param table the table + */ + public MakeProgramSelectionAction(Plugin plugin, GhidraTable table) { + super("Make Selection", plugin.getName(), KeyBindingType.SHARED); + this.plugin = plugin; this.table = table; setPopupMenuData( @@ -45,7 +72,7 @@ public abstract class MakeProgramSelectionAction extends DockingAction { // this help location provides generic help; clients can override to point to their help setHelpLocation(new HelpLocation("Search", "Make_Selection")); - // null for now, but we may want a default binding in the future + // null for now, but we may want a default binding in the future initKeyStroke(null); } @@ -70,6 +97,15 @@ public abstract class MakeProgramSelectionAction extends DockingAction { return false; } + Program program = table.getProgram(); + if (program == null) { + return false; + } + + if (program.isClosed()) { + return false; + } + int n = table.getSelectedRowCount(); return n > 0; } @@ -79,5 +115,17 @@ public abstract class MakeProgramSelectionAction extends DockingAction { makeSelection(context); } - protected abstract void makeSelection(ActionContext context); + protected ProgramSelection makeSelection(ActionContext context) { + ProgramSelection selection = table.getProgramSelection(); + + if (plugin == null) { + throw new IllegalStateException("The Make Program Selection action cannot be used " + + "without a plugin unless the client overrides this method"); + } + + PluginEvent event = + new ProgramSelectionPluginEvent(plugin.getName(), selection, table.getProgram()); + plugin.firePluginEvent(event); + return selection; + } } 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 e2a716518c..eef801df44 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/ComponentProviderActionsTest.java @@ -24,8 +24,7 @@ import javax.swing.*; import org.junit.*; -import docking.action.DockingActionIf; -import docking.action.KeyBindingData; +import docking.action.*; import docking.actions.KeyEntryDialog; import docking.actions.ToolActions; import docking.tool.util.DockingToolConstants; @@ -54,7 +53,9 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio @Before public void setUp() throws Exception { env = new TestEnv(); - tool = env.launchDefaultTool(); + tool = env.showTool(); + + //tool = env.launchDefaultTool(); provider = new TestActionsComponentProvider(tool); Msg.setErrorLogger(spyLogger); @@ -243,23 +244,72 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio } @Test - public void testSetIcon_NullIconWithToolbarAction() { + public void testDefaultKeyBindingAppearsInWindowMenu() { + + setDefaultKeyBinding(CONTROL_T); + showProvider(); + assertWindowMenuActionHasKeyBinding(CONTROL_T); + } + + @Test + public void testAddToToolbar_WithoutIcon() { + + runSwing(() -> provider.addToToolbar()); try { - setToolbarIcon(null); + setErrorsExpected(true); + runSwingWithExceptions(this::showProvider, true); + setErrorsExpected(false); + fail(); + } + catch (Throwable t) { + // good + } + } + + @Test + public void testSetIcon_NullIconWithToolbarAction() { + + setIcon(ICON); + runSwing(() -> provider.addToToolbar()); + showProvider(); + + try { + setErrorsExpected(true); + runSwingWithExceptions(() -> provider.setIcon(null), true); + setErrorsExpected(false); fail("Expected an exception passing a null icon when specifying a toolbar action"); } - catch (Exception e) { + catch (Throwable t) { // expected } } @Test - public void testDefaultKeyBindingAppearsInWindowMenu() { - - provider.setDefaultKeyBinding(new KeyBindingData(CONTROL_T)); + public void testSetIcon_WithToolbarAction() { + setToolbarIcon(ICON); showProvider(); - assertWindowMenuActionHasKeyBinding(CONTROL_T); + + assertWindowMenuActionHasIcon(ICON); + assertToolbarActionHasIcon(ICON); + } + + @Test + public void testSetIcon_WithToolbarAction_AfterActionHasBeenAddedToToolbar() { + + // + // We currently do not prevent providers from changing their icons. Make sure we respond + // to changes correctly. + // + + setToolbarIcon(ICON); + showProvider(); + + Icon newIcon = Icons.COLLAPSE_ALL_ICON; + setIcon(newIcon); + + assertWindowMenuActionHasIcon(newIcon); + assertToolbarActionHasIcon(newIcon); } //================================================================================================== @@ -277,7 +327,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio } private void setDefaultKeyBinding(KeyStroke defaultKs) { - runSwing(() -> provider.setDefaultKeyBinding(new KeyBindingData(defaultKs))); + runSwing(() -> provider.setKeyBinding(new KeyBindingData(defaultKs))); } private void setIcon(Icon icon) { @@ -285,7 +335,10 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio } private void setToolbarIcon(Icon icon) { - runSwing(() -> provider.setIcon(icon, true)); + runSwing(() -> { + provider.setIcon(icon); + provider.addToToolbar(); + }); } private DockingActionIf getShowProviderAction() { @@ -368,6 +421,13 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio expected, action.getMenuBarData().getMenuIcon()); } + private void assertToolbarActionHasIcon(Icon expected) { + DockingActionIf action = getToolbarShowProviderAction(); + assertNotNull("No toolbar action found; it should be there", action); + ToolBarData tbData = action.getToolBarData(); + assertEquals(expected, tbData.getIcon()); + } + private void assertWindowMenuActionHasKeyBinding(KeyStroke ks) { DockingActionIf action = getWindowMenuShowProviderAction(); assertEquals( @@ -476,7 +536,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio super(tool, HasDefaultKeyBindingComponentProvider.class.getSimpleName(), "Fooberry Plugin"); - setDefaultKeyBinding(new KeyBindingData(CONTROL_T)); + setKeyBinding(new KeyBindingData(CONTROL_T)); } @Override diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/progmgr/MultiTabPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/progmgr/MultiTabPluginTest.java index 21cd84c437..2d9198bd25 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/progmgr/MultiTabPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/progmgr/MultiTabPluginTest.java @@ -256,35 +256,6 @@ public class MultiTabPluginTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(0, panel.getHiddenCount()); } - private void printResizeDebug() { - // - // To show the '>>' label, the number of tabs must exceed the room visible to show them - // - - // frame size - - // available width - int panelWidth = panel.getWidth(); - System.out.println("available width: " + panelWidth); - - // size label - int totalWidth = 0; - JComponent listLabel = (JComponent) getInstanceField("showHiddenListLabel", panel); - System.out.println("label width: " + listLabel.getWidth()); - totalWidth = listLabel.getWidth(); - - // size of each tab's panel - Map map = (Map) getInstanceField("linkedProgramMap", panel); - Collection values = map.values(); - for (Object object : values) { - JComponent c = (JComponent) object; - totalWidth += c.getWidth(); - System.out.println("\t" + c.getWidth()); - } - - System.out.println("Total width: " + totalWidth + " out of " + panelWidth); - } - @Test public void testTabUpdatesOnProgramChange() throws Exception { ProgramBuilder builder = new ProgramBuilder("notepad", ProgramBuilder._TOY); @@ -315,11 +286,11 @@ public class MultiTabPluginTest extends AbstractGhidraHeadedIntegrationTest { setFrameSize(400, 500); programNames = new String[] { "notepad", "login", "tms", "taskman", "TestGhidraSearches" }; openPrograms(programNames); - assertTrue(panel.isHidden(programs[1])); + assertHidden(programs[1]); runSwing(() -> panel.setSelectedProgram(programs[1])); assertEquals(programs[1], panel.getSelectedProgram()); - assertTrue(!panel.isHidden(programs[1])); + assertShowing(programs[1]); } @Test @@ -384,10 +355,6 @@ public class MultiTabPluginTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(BigInteger.valueOf(4), fp.getCursorLocation().getIndex()); } - private Color getFieldPanelBackgroundColor(FieldPanel fp, BigInteger index) { - return runSwing(() -> fp.getBackgroundColor(index)); - } - @Test public void testTabUpdate() throws Exception { Program p = openDummyProgram("login", true); @@ -449,8 +416,8 @@ public class MultiTabPluginTest extends AbstractGhidraHeadedIntegrationTest { // by trial-and-error, we know that 'tms' is the last visible program tab // after resizing setFrameSize(500, 500); - assertTrue(!panel.isHidden(programs[2])); - assertTrue(panel.isHidden(programs[3])); + assertShowing(programs[2]); + assertHidden(programs[3]); // select the last visible tab selectTab(programs[2]); @@ -485,8 +452,8 @@ public class MultiTabPluginTest extends AbstractGhidraHeadedIntegrationTest { // by trial-and-error, we know that 'tms' is the last visible program tab // after resizing setFrameSize(500, 500); - assertTrue(!panel.isHidden(programs[2])); - assertTrue(panel.isHidden(programs[3])); + assertShowing(programs[2]); + assertHidden(programs[3]); // select 'tms', which is the last tab before the list is shown selectTab(programs[2]); @@ -521,6 +488,43 @@ public class MultiTabPluginTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(selectedProgram, p); } + private void printResizeDebug() { + // + // To show the '>>' label, the number of tabs must exceed the room visible to show them + // + + // frame size + + // available width + int panelWidth = panel.getWidth(); + System.out.println("available width: " + panelWidth); + + // size label + int totalWidth = 0; + JComponent listLabel = (JComponent) getInstanceField("showHiddenListLabel", panel); + System.out.println("label width: " + listLabel.getWidth()); + totalWidth = listLabel.getWidth(); + + // size of each tab's panel + Map map = (Map) getInstanceField("linkedProgramMap", panel); + Collection values = map.values(); + for (Object object : values) { + JComponent c = (JComponent) object; + totalWidth += c.getWidth(); + System.out.println("\t" + c.getWidth()); + } + + System.out.println("Total width: " + totalWidth + " out of " + panelWidth); + } + + private void assertShowing(Program p) { + assertFalse(runSwing(() -> panel.isHidden(p))); + } + + private void assertHidden(Program p) { + assertTrue(runSwing(() -> panel.isHidden(p))); + } + private void assertListWindowHidden() { Window listWindow = getListWindow(); assertFalse(listWindow.isShowing()); @@ -544,6 +548,10 @@ public class MultiTabPluginTest extends AbstractGhidraHeadedIntegrationTest { return windowForComponent(listPanel); } + private Color getFieldPanelBackgroundColor(FieldPanel fp, BigInteger index) { + return runSwing(() -> fp.getBackgroundColor(index)); + } + private void performPreviousAction() throws Exception { MultiTabPlugin plugin = env.getPlugin(MultiTabPlugin.class); DockingAction goToPreviousProgramAction = diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java index e03bd215ea..d6035251ef 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteViewerComponentProvider.java @@ -72,11 +72,13 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi super(tool, plugin, "Bytes", ByteViewerActionContext.class); this.isConnected = isConnected; + setIcon(ResourceManager.loadImage("images/binaryData.gif")); if (!isConnected) { setTransient(); } - - setIcon(ResourceManager.loadImage("images/binaryData.gif"), isConnected); + else { + addToToolbar(); + } decorationComponent = new DecoratorPanel(panel, isConnected); clipboardProvider = new ByteViewerClipboardProvider(this, tool); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java index 4d9e29b00c..81b3d15fb9 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/DecompilerProvider.java @@ -142,11 +142,12 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter setTransient(); } else { - setDefaultKeyBinding( + addToToolbar(); + setKeyBinding( new KeyBindingData(KeyEvent.VK_E, DockingUtils.CONTROL_KEY_MODIFIER_MASK)); } - setIcon(C_SOURCE_ICON, isConnected); + setIcon(C_SOURCE_ICON); setTitle("Decompile"); setWindowMenuGroup("Decompile"); diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java index d8287c49c9..fe477d581e 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/FGProvider.java @@ -103,16 +103,19 @@ public class FGProvider extends VisualGraphComponentProvider oringalGroupedEdges = new HashSet<>(graph.getEdges());// copy so they don't get cleared @@ -99,7 +99,9 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { originalEdgeStrings.add(edge.toString()); } - graphData = triggerPersistence("01002cf5"); + // debug + capture(getPrimaryGraphViewer(), "graph.grouping.before.reload"); + graphData = triggerPersistenceAndReload("01002cf5"); waitForAnimation();// the re-grouping may be using animation, which runs after the graph is loaded functionGraph = graphData.getFunctionGraph(); @@ -113,6 +115,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { // TODO debug - this has failed; suspected timing issue waitForCondition(() -> pointsAreSimilar(location, newLocation)); + capture(getPrimaryGraphViewer(), "graph.grouping.after.reload"); assertTrue( "Vertex location not restored to default after performing a relayout " + "original point: " + location + " - reloaded point: " + newLocation, @@ -292,7 +295,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { Address secondMinAddress = outerAddresses.getMinAddress(); Address secondMaxAddress = outerAddresses.getMaxAddress(); - graphData = triggerPersistence("01002cf5"); + graphData = triggerPersistenceAndReload("01002cf5"); waitForAnimation();// the re-grouping may be using animation, which runs after the graph is loaded functionGraph = graphData.getFunctionGraph(); @@ -410,7 +413,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { Address minAddress = addresses.getMinAddress(); Address maxAddress = addresses.getMaxAddress(); - graphData = triggerPersistence("01002cf5"); + graphData = triggerPersistenceAndReload("01002cf5"); waitForAnimation();// the re-grouping may be using animation, which runs after the graph is loaded functionGraph = graphData.getFunctionGraph(); @@ -665,7 +668,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { // Trigger persistence // Address groupAddress = group.getVertexAddress(); - FGData graphData = triggerPersistence("01002cf5"); + FGData graphData = triggerPersistenceAndReload("01002cf5"); // // Retrieve the group and make sure its color is restored @@ -702,7 +705,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { // Trigger persistence // Address groupAddress = group.getVertexAddress(); - FGData graphData = triggerPersistence("01002cf5"); + FGData graphData = triggerPersistenceAndReload("01002cf5"); // // Retrieve the group and make sure its color is restored @@ -745,7 +748,7 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { // Trigger persistence // Address groupAddress = group.getVertexAddress(); - FGData graphData = triggerPersistence("01002cf5"); + FGData graphData = triggerPersistenceAndReload("01002cf5"); // // Retrieve the group and make sure its color is restored diff --git a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices3Test.java b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices3Test.java index 2b37d1693e..c2c6ee8cd2 100644 --- a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices3Test.java +++ b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices3Test.java @@ -122,7 +122,7 @@ public class FunctionGraphGroupVertices3Test extends AbstractFunctionGraphTest { uncollapse(groupA); assertUncollapsed(v1, v2); - triggerPersistence(functionAddress); + triggerPersistenceAndReload(functionAddress); waitForBusyGraph();// the re-grouping may be using animation, which runs after the graph is loaded v1 = vertex(a1); @@ -161,7 +161,7 @@ public class FunctionGraphGroupVertices3Test extends AbstractFunctionGraphTest { assertUncollapsed(v1, v2);// sanity check--still uncollapsed - triggerPersistence(functionAddress); + triggerPersistenceAndReload(functionAddress); waitForBusyGraph();// the re-grouping may be using animation, which runs after the graph is loaded v1 = vertex(a1); @@ -195,7 +195,7 @@ public class FunctionGraphGroupVertices3Test extends AbstractFunctionGraphTest { uncollapse(outerGroup); assertUncollapsed(innerGroup, v3, v4); - triggerPersistence(functionAddress); + triggerPersistenceAndReload(functionAddress); waitForBusyGraph();// the re-grouping may be using animation, which runs after the graph is loaded v1 = vertex(a1); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java index 01e824eb4a..e9d98fa0da 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ComponentProvider.java @@ -96,7 +96,7 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext private Set actionSet = new LinkedHashSet<>(); /** True if this provider's action should appear in the toolbar */ - private boolean isToolbarAction; + private boolean addToolbarAction; private boolean isTransient; private KeyBindingData defaultKeyBindingData; private Icon icon; @@ -159,6 +159,12 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext return; } + if (addToolbarAction) { + Objects.requireNonNull(icon, + "The provider's icon cannot be null when requesting the provider's action " + + "appear in the toolbar"); + } + boolean supportsKeyBindings = !isTransient; showProviderAction = new ShowProviderAction(supportsKeyBindings); } @@ -498,10 +504,12 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext } /** - * Sets the default key binding that will show this provider when pressed + * Sets the default key binding that will show this provider when pressed. This value can + * be changed by the user and saved as part of the Tool options. + * * @param kbData the key binding */ - public void setDefaultKeyBinding(KeyBindingData kbData) { + protected void setKeyBinding(KeyBindingData kbData) { if (isInTool()) { throw new IllegalStateException( @@ -510,7 +518,7 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext this.defaultKeyBindingData = kbData; - if (isTransient) { + if (isTransient && kbData != null) { Msg.error(this, TRANSIENT_PROVIDER_KEY_BINDING_WARNING_MESSAGE, ReflectionUtilities.createJavaFilteredThrowable()); this.defaultKeyBindingData = null; @@ -518,37 +526,34 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext } /** - * Convenience method for setting the provider's icon. - * @param icon the icon to use for this provider. + * Convenience method for setting the provider's icon + * @param icon the icon to use for this provider */ - public void setIcon(Icon icon) { - setIcon(icon, false); + protected void setIcon(Icon icon) { + this.icon = icon; + if (!isInTool()) { + return; + } + + if (addToolbarAction && showProviderAction != null) { + Objects.requireNonNull(icon, "Icon cannot be set to null when using a toolbar action"); + showProviderAction.setToolBarData(new ToolBarData(icon)); + } + + dockingTool.getWindowManager().setIcon(this, icon); } /** - * Convenience method for setting the provider's icon - * - * @param icon the icon to use for this provider - * @param isToolbarAction true will cause this action to get added to the toolbar; if this - * value is true, then the icon cannot be null + * Signals that this provider's action for showing the provider should appear in the main + * toolbar */ - public void setIcon(Icon icon, boolean isToolbarAction) { - this.icon = icon; - this.isToolbarAction = isToolbarAction; + protected void addToToolbar() { + this.addToolbarAction = true; - if (isToolbarAction) { - Objects.requireNonNull(icon, - "Icon cannot be null when requesting the provider's action appear in the toolbar"); - - if (isTransient) { - Msg.error(this, TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE, - ReflectionUtilities.createJavaFilteredThrowable()); - isToolbarAction = false; - } - } - - if (isInTool()) { - dockingTool.getWindowManager().setIcon(this, icon); + if (isTransient) { + Msg.error(this, TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE, + ReflectionUtilities.createJavaFilteredThrowable()); + addToolbarAction = false; } } @@ -595,8 +600,8 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext } // avoid visually disturbing the user by adding/removing toolbar actions for temp providers - if (isToolbarAction) { - isToolbarAction = false; + if (addToolbarAction) { + addToolbarAction = false; Msg.error(this, TRANSIENT_PROVIDER_TOOLBAR_WARNING_MESSAGE, ReflectionUtilities.createJavaFilteredThrowable()); } @@ -776,7 +781,7 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext super(name, owner, supportsKeyBindings ? KeyBindingType.SHARED : KeyBindingType.UNSUPPORTED); - if (isToolbarAction) { + if (addToolbarAction) { setToolBarData(new ToolBarData(icon, TOOLBAR_GROUP)); } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGenericTest.java b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGenericTest.java index 6cf64d94a0..b209b92556 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGenericTest.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGenericTest.java @@ -1092,6 +1092,26 @@ public abstract class AbstractGenericTest extends AbstractGTest { runSwing(runnable, true); } + /** + * Call this version of {@link #runSwing(Runnable)} when you expect your runnable to throw + * an exception + * @param runnable the runnable + * @param wait true signals to wait for the Swing operation to finish + * @throws Throwable any excption that is thrown on the Swing thread + */ + public static void runSwingWithExceptions(Runnable runnable, boolean wait) throws Throwable { + + if (Swing.isSwingThread()) { + throw new AssertException("Unexpectedly called from the Swing thread"); + } + + ExceptionHandlingRunner exceptionHandlingRunner = new ExceptionHandlingRunner(runnable); + Throwable throwable = exceptionHandlingRunner.getException(); + if (throwable != null) { + throw throwable; + } + } + public static void runSwing(Runnable runnable, boolean wait) { if (SwingUtilities.isEventDispatchThread()) { runnable.run(); @@ -1115,7 +1135,6 @@ public abstract class AbstractGenericTest extends AbstractGTest { }; SwingUtilities.invokeLater(swingExceptionCatcher); - } protected static class ExceptionHandlingRunner {