GT-2869 - Key Bindings - refactor of methods to provide all actions

This commit is contained in:
dragonmacher 2019-06-04 18:15:16 -04:00
parent c1de98304a
commit 43fa7e3f92
75 changed files with 1243 additions and 1049 deletions

View file

@ -20,6 +20,7 @@ import java.io.*;
import java.util.*;
import docking.action.DockingActionIf;
import docking.actions.KeyBindingUtils;
import ghidra.app.script.GhidraScript;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
@ -30,9 +31,9 @@ public class CreateHelpTemplateScript extends GhidraScript {
@Override
protected void run() throws Exception {
PluginTool tool = state.getTool();
Plugin[] plugins = getSortedPlugins(tool);
List<Plugin> plugins = getSortedPlugins(tool);
Plugin selectedPlugin =
askChoice("Select Plugin To Use To Generate Help", "Plugin", plugins, plugins[0]);
askChoice("Select Plugin To Use To Generate Help", "Plugin", plugins, plugins.get(0));
if (selectedPlugin == null) {
printerr("no plugin selected, no help template created.");
return;
@ -101,7 +102,8 @@ public class CreateHelpTemplateScript extends GhidraScript {
}
private List<DockingActionIf> getActions(PluginTool tool, Plugin plugin) {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = KeyBindingUtils.getKeyBindingActions(tool, plugin.getName());
List<DockingActionIf> list = new ArrayList<>(actions);
Comparator<DockingActionIf> comparator = (action1, action2) -> {
try {
return action1.getName().compareTo(action2.getName());
@ -110,14 +112,12 @@ public class CreateHelpTemplateScript extends GhidraScript {
return 0;
}
};
Collections.sort(actions, comparator);
return actions;
Collections.sort(list, comparator);
return list;
}
private Plugin[] getSortedPlugins(PluginTool tool) {
private List<Plugin> getSortedPlugins(PluginTool tool) {
List<Plugin> list = tool.getManagedPlugins();
Plugin[] plugins = new Plugin[list.size()];
list.toArray(plugins);
Comparator<Plugin> comparator = (plugin1, plugin2) -> {
try {
return plugin1.getName().compareTo(plugin2.getName());
@ -126,8 +126,9 @@ public class CreateHelpTemplateScript extends GhidraScript {
return 0;
}
};
Arrays.sort(plugins, comparator);
return plugins;
Collections.sort(list, comparator);
return list;
}
}

View file

@ -21,9 +21,7 @@ import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.event.*;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.EventObject;
import java.util.List;
import java.util.*;
import javax.swing.*;
import javax.swing.border.Border;
@ -33,10 +31,10 @@ import javax.swing.table.*;
import javax.swing.text.JTextComponent;
import docking.action.DockingActionIf;
import docking.actions.KeyBindingUtils;
import docking.dnd.*;
import docking.help.Help;
import docking.help.HelpService;
import docking.util.KeyBindingUtils;
import docking.widgets.DropDownSelectionTextField;
import docking.widgets.OptionDialog;
import docking.widgets.fieldpanel.support.FieldRange;
@ -1519,17 +1517,18 @@ public abstract class CompositeEditorPanel extends JPanel
@Override
public boolean isKeyConsumed(KeyStroke keyStroke) {
if (isEditing()) {
// don't let actions through when editing our table
return true;
}
// don't let actions through when editing our table
// TODO this should no longer be needed
return !hasLocalActionForKeyStroke(keyStroke);
}
private boolean hasLocalActionForKeyStroke(KeyStroke keyStroke) {
Plugin plugin = provider.getPlugin();
PluginTool tool = plugin.getTool();
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
for (DockingActionIf action : actions) {
if (!(action instanceof CompositeEditorTableAction)) {
continue;

View file

@ -32,8 +32,8 @@ import javax.swing.undo.UndoableEdit;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.*;
import docking.actions.KeyBindingUtils;
import docking.options.editor.FontPropertyEditor;
import docking.util.KeyBindingUtils;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import ghidra.framework.options.SaveState;

View file

@ -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.
@ -28,7 +27,7 @@ import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import docking.util.KeyBindingUtils;
import docking.actions.KeyBindingUtils;
class FunctionSignatureTextField extends JTextPane {
private static final String ENTER_ACTION_NAME = "ENTER";

View file

@ -24,7 +24,7 @@ import java.util.Map.Entry;
import javax.swing.*;
import javax.swing.border.*;
import docking.util.KeyBindingUtils;
import docking.actions.KeyBindingUtils;
import docking.widgets.label.GDLabel;
import docking.widgets.label.GIconLabel;
import generic.util.WindowUtilities;

View file

@ -26,8 +26,8 @@ import javax.swing.*;
import javax.swing.tree.*;
import docking.DockingUtils;
import docking.actions.KeyBindingUtils;
import docking.dnd.*;
import docking.util.KeyBindingUtils;
import docking.widgets.table.AutoscrollAdapter;
/**

View file

@ -30,8 +30,8 @@ import javax.swing.tree.*;
import docking.DockingUtils;
import docking.action.DockingAction;
import docking.actions.KeyBindingUtils;
import docking.dnd.DropTgtAdapter;
import docking.util.KeyBindingUtils;
import docking.widgets.JTreeMouseListenerDelegate;
import ghidra.app.util.SelectionTransferData;
import ghidra.app.util.SelectionTransferable;

View file

@ -28,9 +28,9 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import docking.action.DockingAction;
import docking.actions.KeyBindingUtils;
import docking.dnd.DropTgtAdapter;
import docking.dnd.Droppable;
import docking.util.KeyBindingUtils;
import docking.widgets.label.GDLabel;
import ghidra.app.util.*;
import ghidra.app.util.viewer.field.BrowserCodeUnitFormat;

View file

@ -43,7 +43,6 @@ import utilities.util.FileUtilities;
class GhidraScriptActionManager {
public static final String RERUN_LAST_SHARED_ACTION_NAME = "Rerun Last Script";
public static final String GLOBAL_RERUN_LAST_SHARED_ACTION_NAME = "Global Rerun Last Script";
private static final KeyStroke RERUN_LAST_SCRIPT_KEYSTROKE = KeyStroke.getKeyStroke(
KeyEvent.VK_R, DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.SHIFT_DOWN_MASK);
private static final String SCRIPT_ACTIONS_KEY = "Scripts_Actions_Key";

View file

@ -29,8 +29,8 @@ import javax.swing.undo.UndoableEdit;
import docking.*;
import docking.action.*;
import docking.actions.KeyBindingUtils;
import docking.options.editor.FontPropertyEditor;
import docking.util.KeyBindingUtils;
import docking.widgets.OptionDialog;
import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScriptUtil;

View file

@ -28,7 +28,7 @@ import docking.widgets.label.GDLabel;
* components (usually icon buttons)
*/
public class TitledPanel extends JPanel {
private JLabel title;
private JLabel title; // GDLabel or GHtmlLabel
private JPanel titlePanel;
private JPanel iconPanel;
private JComponent bottomComp;
@ -37,16 +37,27 @@ public class TitledPanel extends JPanel {
/**
* Creates a new TitlePanel
* @param name the name of the panel
* @param panel the component to wrap.
* @param margin the size of the margin to use.
* @param panel the component to wrap
* @param margin the size of the margin to use
*/
public TitledPanel(String name, JComponent panel, int margin) {
this(new GDLabel(name), panel, margin);
}
/**
* Creates a new TitlePanel
*
* @param titleLabel the title label for the panel; this allow clients to provide HTML-based
* title text. Note: it is up to the client to escape this text as needed for safety
* @param panel the component to wrap
* @param margin the size of the margin to use
*/
public TitledPanel(JLabel titleLabel, JComponent panel, int margin) {
super(new BorderLayout());
titlePanel = new JPanel(new BorderLayout());
iconPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 4, 1));
iconPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
title = new GDLabel(name);
title.setToolTipText(name);
title = titleLabel;
JLabel filler = new GDLabel();
filler.setPreferredSize(new Dimension(margin, filler.getPreferredSize().height));
titlePanel.add(filler, BorderLayout.WEST);

View file

@ -212,9 +212,8 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
public void performAction(String actionName, String owner, ComponentProvider contextProvider,
boolean wait) {
String fullActionName = actionName + " (" + owner + ")";
List<DockingActionIf> action = tool.getDockingActionsByFullActionName(fullActionName);
performAction(action.get(0), contextProvider, wait);
DockingActionIf action = getAction(tool, owner, actionName);
performAction(action, contextProvider, wait);
}
public void showOptions(final String optionsCategoryName) {

View file

@ -359,12 +359,11 @@ public abstract class GhidraScreenShotGenerator extends AbstractScreenShotGenera
}
public void performFrontEndAction(String actionName, String owner, boolean wait) {
String fullActionName = actionName + " (" + owner + ")";
FrontEndTool frontEnd = getFrontEndTool();
List<DockingActionIf> action = frontEnd.getDockingActionsByFullActionName(fullActionName);
DockingActionIf action = getAction(frontEnd, owner, actionName);
ComponentProvider compProvider =
(ComponentProvider) getInstanceField("compProvider", frontEnd);
performAction(action.get(0), compProvider, wait);
performAction(action, compProvider, wait);
}
}

View file

@ -18,7 +18,7 @@ package ghidra.app.plugin.core.algorithmtree;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Set;
import org.junit.*;
@ -33,6 +33,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramModule;
import ghidra.program.util.GroupPath;
import ghidra.test.*;
import util.CollectionUtils;
/**
* Test the module algorithm plugin gui elements.
@ -43,7 +44,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe
private PluginTool tool;
private Program program;
private ModuleAlgorithmPlugin plugin;
private List<DockingActionIf> actions;
private Set<DockingActionIf> actions;
private ProgramTreeService service;
private Object context;
@ -58,7 +59,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe
tool.addPlugin(ProgramTreePlugin.class.getName());
tool.addPlugin(ModuleAlgorithmPlugin.class.getName());
plugin = env.getPlugin(ModuleAlgorithmPlugin.class);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
service = tool.getService(ProgramTreeService.class);
}
@ -98,7 +99,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe
getContextObject(vps);
performAction(actions.get(0), new ActionContext(null, context), true);
performAction(CollectionUtils.any(actions), new ActionContext(null, context), true);
waitForTasks();
program.flushEvents();

View file

@ -1090,10 +1090,9 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
private DockingActionIf getAction(String actionName) {
// make sure there is a provider from which to get actions
getProvider();
String fullActionName = actionName + " (CallTreePlugin)";
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(fullActionName);
Assert.assertTrue("Could not find action: " + fullActionName, actions.size() > 0);
return actions.get(0);
DockingActionIf action = getAction(tool, "CallTreePlugin", actionName);
return action;
}
private void myWaitForTree(GTree gTree, CallTreeProvider treeProvider) {

View file

@ -165,7 +165,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
builtInDataTypesManager.setFavorite(root.getDataType("word"), true);
}
protected void checkActions(List<DockingActionIf> actions, boolean enabled, String caseStr) {
protected void checkActions(Set<DockingActionIf> actions, boolean enabled, String caseStr) {
checkAction(actions, CREATE_STRUCTURE, enabled, caseStr);
checkAction(actions, EDIT_DATA_TYPE, enabled, caseStr);
checkAction(actions, CREATE_ARRAY, enabled, caseStr);
@ -1063,7 +1063,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
ProgramSelection sel = getCurrentSelection();
boolean useSelection = (sel != null && !sel.isEmpty());
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
for (DockingActionIf element : actions) {
MenuData menuBarData = element.getMenuBarData();
@ -1113,31 +1113,6 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
}
}
// if (useSelection) {
//
// checkAction(actions, CREATE_STRUCTURE, true, caseName);
// checkAction(actions, EDIT_STRUCTURE, false, caseName);
// checkAction(actions, CREATE_ARRAY, true, caseName);
// checkAction(actions, DEFAULT_DATA_SETTINGS, false, caseName);
// checkAction(actions, DATA_SETTINGS, false, caseName);
// checkAction(actions, CYCLE_FLOAT_DOUBLE, true, caseName);
// checkAction(actions, CYCLE_BYTE_WORD_DWORD_QWORD, true, caseName);
// checkAction(actions, CYCLE_CHAR_STRING_UNICODE, true, caseName);
// checkAction(actions, DEFINE_BYTE, true, caseName);
// checkAction(actions, DEFINE_WORD, true, caseName);
// checkAction(actions, DEFINE_DWORD, true, caseName);
// checkAction(actions, DEFINE_QWORD, true, caseName);
// checkAction(actions, DEFINE_FLOAT, true, caseName);
// checkAction(actions, DEFINE_DOUBLE, true, caseName);
// checkAction(actions, DEFINE_TERM_CSTRING, true, caseName);
// checkAction(actions, DEFINE_POINTER, true, caseName);
//
// PluginAction recentlyUsedAction = getAction(RECENTLY_USED);
// if (recentlyUsedAction != null) {
// checkAction(recentlyUsedAction, false, caseName);
// }
// return;
// }
if (data != null) {
DataType dt = data.getDataType();
@ -1184,10 +1159,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
}
protected void checkOnUndefined(List<DockingActionIf> actions) {
protected void checkOnUndefined(Set<DockingActionIf> actions) {
if (actions == null) {
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
}
Data data = getContextData();
@ -1223,10 +1198,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
}
protected void checkOnDefined(List<DockingActionIf> actions, Class<?> expectedDataType) {
protected void checkOnDefined(Set<DockingActionIf> actions, Class<?> expectedDataType) {
if (actions == null) {
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
}
String dtName = expectedDataType.getName();
@ -1287,10 +1262,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
checkAction(actions, DEFINE_POINTER, true, caseName);
}
protected void checkOnArray(List<DockingActionIf> actions, DataType interiorDt, int arraySize) {
protected void checkOnArray(Set<DockingActionIf> actions, DataType interiorDt, int arraySize) {
if (actions == null) {
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
}
Data d = getContextData();
@ -1356,10 +1331,10 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
* @param actions
* @param structSize structure size or -1 to disable size check
*/
protected void checkOnStructure(List<DockingActionIf> actions, int structSize) {
protected void checkOnStructure(Set<DockingActionIf> actions, int structSize) {
if (actions == null) {
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
}
Data d = getContextData();
@ -1400,7 +1375,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
}
protected DockingActionIf getAction(String name) {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
for (DockingActionIf element : actions) {
String actionName = element.getName();
int pos = actionName.indexOf(" (");
@ -1439,7 +1414,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
}
protected void checkAction(List<DockingActionIf> actions, String name, boolean isEnabled,
protected void checkAction(Set<DockingActionIf> actions, String name, boolean isEnabled,
String caseName) {
for (DockingActionIf element : actions) {
String actionName = element.getName();

View file

@ -17,7 +17,7 @@ package ghidra.app.plugin.core.data;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@ -39,13 +39,13 @@ public class DataAction4Test extends AbstractDataActionTest {
@Test
public void testNotepadLocations() {
List<DockingActionIf> actions;
Set<DockingActionIf> actions;
program.addConsumer(this); // allow program to survive close
try {
closeProgram();
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkActions(actions, false, "Start");
@ -60,7 +60,7 @@ public class DataAction4Test extends AbstractDataActionTest {
closeProgram();
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkActions(actions, false, "Start");
}
@ -73,13 +73,13 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_BYTE, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, ByteDataType.class);
undo(program);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnUndefined(actions);
gotoLocation(0x010069f2);
@ -107,7 +107,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_WORD, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, WordDataType.class);
@ -138,7 +138,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_DWORD, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, DWordDataType.class);
@ -169,7 +169,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_QWORD, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, QWordDataType.class);
@ -200,7 +200,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_FLOAT, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, FloatDataType.class);
@ -231,7 +231,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_DOUBLE, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, DoubleDataType.class);
@ -264,7 +264,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(CYCLE_CHAR_STRING_UNICODE, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, CharDataType.class);
@ -283,15 +283,15 @@ public class DataAction4Test extends AbstractDataActionTest {
checkOnDefined(actions, CharDataType.class);
doAction(CYCLE_CHAR_STRING_UNICODE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, StringDataType.class);
doAction(CYCLE_CHAR_STRING_UNICODE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, UnicodeDataType.class);
doAction(CYCLE_CHAR_STRING_UNICODE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, CharDataType.class);
clearLocation(0x01006a00);
@ -326,7 +326,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, ByteDataType.class);
@ -345,19 +345,19 @@ public class DataAction4Test extends AbstractDataActionTest {
checkOnDefined(actions, ByteDataType.class);
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, WordDataType.class);
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, DWordDataType.class);
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, QWordDataType.class);
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, ByteDataType.class);
clearLocation(0x01006a00);
@ -387,19 +387,19 @@ public class DataAction4Test extends AbstractDataActionTest {
// Test cycle when it does not fit
gotoLocation(0x010069f0);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnUndefined(actions);
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, ByteDataType.class);
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, WordDataType.class);
doAction(CYCLE_BYTE_WORD_DWORD_QWORD, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnUndefined(actions);
}
@ -414,7 +414,7 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(CYCLE_FLOAT_DOUBLE, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(ACTION_COUNT, actions.size());
checkOnDefined(actions, FloatDataType.class);
@ -433,11 +433,11 @@ public class DataAction4Test extends AbstractDataActionTest {
checkOnDefined(actions, FloatDataType.class);
doAction(CYCLE_FLOAT_DOUBLE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, DoubleDataType.class);
doAction(CYCLE_FLOAT_DOUBLE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, FloatDataType.class);
clearLocation(0x01006a00);
@ -461,15 +461,15 @@ public class DataAction4Test extends AbstractDataActionTest {
// Test cycle when it does not fit
gotoLocation(0x010069ee);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnUndefined(actions);
doAction(CYCLE_FLOAT_DOUBLE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, FloatDataType.class);
doAction(CYCLE_FLOAT_DOUBLE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnUndefined(actions);
}
@ -495,7 +495,7 @@ public class DataAction4Test extends AbstractDataActionTest {
waitForPostedSwingRunnables();
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
checkOnArray(actions, null, 0x20);
// Test action disablement on array element location
@ -524,7 +524,7 @@ public class DataAction4Test extends AbstractDataActionTest {
waitForPostedSwingRunnables();
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnArray(actions, new ByteDataType(), 0x10);
}
@ -541,7 +541,7 @@ public class DataAction4Test extends AbstractDataActionTest {
clearSelection();// Remove selection to allow array check to work
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
checkOnArray(actions, null, 0x20);
// Create Byte[0x10] array
@ -555,7 +555,7 @@ public class DataAction4Test extends AbstractDataActionTest {
clearSelection();// Remove selection to allow array check to work
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnArray(actions, new ByteDataType(), 0x10);
}
@ -565,10 +565,7 @@ public class DataAction4Test extends AbstractDataActionTest {
gotoLocation(0x01006c00);
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(RECENTLY_USED + " (" + plugin.getName() + ")");
assertEquals(1, actions.size());
DockingActionIf recentlyUsedAction = actions.get(0);
DockingActionIf recentlyUsedAction = getAction(tool, plugin.getName(), RECENTLY_USED);
String caseName = "On Structure at: " + getCurrentLocation();
checkAction(recentlyUsedAction, false, caseName);
@ -591,7 +588,7 @@ public class DataAction4Test extends AbstractDataActionTest {
clearSelection();
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
checkOnStructure(actions, 0x20);
gotoLocation(0x01006c00);
@ -708,14 +705,14 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_BYTE, true);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, ByteDataType.class);
gotoLocation(0x01006a01, new int[] { 1 });
doAction(DEFINE_FLOAT, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, FloatDataType.class);
Data pdata = getContextData().getParent();
@ -757,7 +754,7 @@ public class DataAction4Test extends AbstractDataActionTest {
waitForPostedSwingRunnables();
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
checkOnArray(actions, structDt, 5);
// Expand structure
@ -772,14 +769,14 @@ public class DataAction4Test extends AbstractDataActionTest {
doAction(DEFINE_BYTE, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, ByteDataType.class);
gotoLocation(0x01006a01, new int[] { 0, 1 });
doAction(DEFINE_FLOAT, true);
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
checkOnDefined(actions, FloatDataType.class);
Data pdata = getContextData().getParent();

View file

@ -37,7 +37,7 @@ import org.junit.*;
import docking.DockingUtils;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingActionIf;
import docking.util.KeyBindingUtils;
import docking.actions.KeyBindingUtils;
import docking.widgets.OptionDialog;
import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.dialogs.InputWithChoicesDialog;

View file

@ -17,7 +17,7 @@ package ghidra.app.plugin.core.equate;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
import javax.swing.JTextField;
@ -115,11 +115,10 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest
// for selection containing a function stack address
builder.setBytes("0x01004bbd", "c2 08 00"); // return of previous function
builder.setBytes("0x01004bc0",
"53 8b 5c 24 08 56 8b 35 b8 10 00 01 57 ff 35 78 80 00 01 " +
"53 ff d6 8b 3d e0 10 00 01 53 ff d7 8d 5c 43 02 68 9c 13 00 01 53 ff d6 53 ff d7 " +
"ff 35 7c 80 00 01 8d 5c 43 02 53 ff d6 53 ff d7 8d 5c 43 02 68 e0 17 00 01 53 ff " +
"d6 53 ff d7 66 83 64 43 02 00 8d 44 43 02 5f 5e 5b c2 04 00");
builder.setBytes("0x01004bc0", "53 8b 5c 24 08 56 8b 35 b8 10 00 01 57 ff 35 78 80 00 01 " +
"53 ff d6 8b 3d e0 10 00 01 53 ff d7 8d 5c 43 02 68 9c 13 00 01 53 ff d6 53 ff d7 " +
"ff 35 7c 80 00 01 8d 5c 43 02 53 ff d6 53 ff d7 8d 5c 43 02 68 e0 17 00 01 53 ff " +
"d6 53 ff d7 66 83 64 43 02 00 8d 44 43 02 5f 5e 5b c2 04 00");
builder.disassemble(new AddressSet(builder.getProgram(), builder.addr("0x01004bc0"),
builder.addr("0x01004c1a")), true);
builder.createFunction("0x01004bc0");
@ -203,8 +202,6 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest
env.dispose();
}
//=================================================================================================
// Private Methods
//=================================================================================================
@ -218,8 +215,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest
ComponentProvider provider = tool.getComponentProvider(PluginConstants.CODE_BROWSER);
DockingActionIf action = getAction(equatePlugin, "Apply Enum");
performAction(action, provider, false);
ApplyEnumDialog d =
waitForDialogComponent(tool.getToolFrame(), ApplyEnumDialog.class, DEFAULT_WAIT_DELAY);
ApplyEnumDialog d = waitForDialogComponent(ApplyEnumDialog.class);
return d;
}
@ -231,7 +227,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest
}
protected void assertConvertActionsInPopup(boolean inPopup) {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("EquatePlugin");
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String actionName = action.getName();
if (actionName.startsWith("Convert")) {
@ -242,7 +238,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest
}
protected void assertNonFloatConvertActionsInPopup() {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("EquatePlugin");
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String actionName = action.getName();
if (actionName.startsWith("Convert")) {
@ -255,7 +251,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest
}
protected void assertConvertNonCharNonSignedActionsInPopup() {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("EquatePlugin");
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf element : actions) {
String name = element.getName();
if (name.startsWith("Convert") &&
@ -266,7 +262,7 @@ public abstract class AbstractEquatePluginTest extends AbstractProgramBasedTest
}
protected void assertConvertNonSignedActionsInPopup() {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("EquatePlugin");
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String name = action.getName();
if (name.startsWith("Convert") && name.indexOf("Signed") < 0) {

View file

@ -19,6 +19,7 @@ import static org.junit.Assert.*;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.table.TableModel;
@ -841,7 +842,7 @@ public class EquatePlugin1Test extends AbstractEquatePluginTest {
putCursorOnOperand(0x010064ae, 1);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("EquatePlugin");
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
int found = 0;
for (DockingActionIf action : actions) {
String name = action.getName();
@ -892,7 +893,7 @@ public class EquatePlugin1Test extends AbstractEquatePluginTest {
putCursorOnOperand(0x010064a3, 0);
int found = 0;
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("EquatePlugin");
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String name = action.getName();
if (!name.startsWith("Convert") || !action.isAddToPopup(getListingContext())) {
@ -944,7 +945,7 @@ public class EquatePlugin1Test extends AbstractEquatePluginTest {
putCursorOnOperand(0x01003a94, 0);
int found = 0;
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("EquatePlugin");
Set<DockingActionIf> actions = getActionsByOwner(tool, "EquatePlugin");
for (DockingActionIf action : actions) {
String name = action.getName();
if (!name.startsWith("Convert") || !action.isAddToPopup(getListingContext())) {

View file

@ -17,7 +17,7 @@ package ghidra.app.plugin.core.fallthrough;
import static org.junit.Assert.assertEquals;
import java.util.List;
import java.util.Set;
import org.junit.*;
@ -37,7 +37,8 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.test.*;
public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest implements LocationCallback {
public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest
implements LocationCallback {
private Program program;
private TestEnv env;
private PluginTool tool;
@ -70,7 +71,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i
@Test
public void testNotepadLocations() {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
checkAction(actions, AUTO_OVERRIDE, false, "Start");
checkAction(actions, CLEAR_FALLTHROUGH, false, "Start");
@ -96,7 +97,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i
new ProgramSelectionPluginEvent("Test", selection, program);
tool.firePluginEvent(ev);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
checkAction(actions, AUTO_OVERRIDE, true, "selection");
checkAction(actions, CLEAR_FALLTHROUGH, true, "selection");
@ -110,7 +111,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i
@Override
public void locationGenerated(ProgramLocation loc) {
tool.firePluginEvent(new ProgramLocationPluginEvent("test", loc, program));
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
ListingActionContext actionContext =
(ListingActionContext) cb.getProvider().getActionContext(null);
@ -126,7 +127,7 @@ public class FallThroughActionTest extends AbstractGhidraHeadedIntegrationTest i
}
private void checkAction(List<DockingActionIf> actions, String name, boolean isValidContext,
private void checkAction(Set<DockingActionIf> actions, String name, boolean isValidContext,
String caseName) {
for (DockingActionIf action : actions) {
String actionName = action.getName();

View file

@ -19,7 +19,7 @@ import static org.junit.Assert.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Set;
import javax.swing.JTable;
import javax.swing.JTextField;
@ -96,7 +96,7 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
env.close(program);
program = buildProgram("sdk");
env.open(program);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
for (DockingActionIf action : actions) {
if (action.getName().equals("Add Block") || action.getName().equals("Set Image Base") ||
action.getName().equals("View Memory Map")) {
@ -114,7 +114,7 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
env.close(program);
JTable table = provider.getTable();
assertEquals(0, table.getModel().getRowCount());
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
for (DockingActionIf action : actions) {
if (!action.getName().equals("View Memory Map")) {
assertTrue(!action.isEnabledForContext(provider.getActionContext(null)));

View file

@ -19,7 +19,6 @@ import static org.junit.Assert.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.table.JTableHeader;
@ -117,7 +116,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
// select first row
// all actions except "merge" should be enabled
table.addRowSelectionInterval(0, 0);
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
for (DockingActionIf action : actions) {
if (action.getName().equals("Merge Blocks")) {
assertTrue(!action.isEnabled());
@ -133,7 +132,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
table.addRowSelectionInterval(0, 1);
assertEquals(2, table.getSelectedRowCount());
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
for (DockingActionIf action : actions) {
String name = action.getName();
if (name.equals("Add Block") || name.equals("Merge Blocks") ||

View file

@ -18,8 +18,7 @@ package ghidra.app.plugin.core.module;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import org.junit.*;
@ -42,7 +41,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
private PluginTool tool;
private Program program;
private ModuleSortPlugin plugin;
private List<DockingActionIf> actions;
private Set<DockingActionIf> actions;
private ProgramTreeService service;
public ModuleSortPluginTest() {
@ -63,7 +62,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
break;
}
}
actions = tool.getDockingActionsByOwnerName(plugin.getName());
actions = getActionsByOwner(tool, plugin.getName());
service = tool.getService(ProgramTreeService.class);
ProgramBuilder builder = new ProgramBuilder("notepad", ProgramBuilder._TOY);

View file

@ -17,8 +17,7 @@ package ghidra.app.plugin.core.navigation;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import javax.swing.*;
@ -65,7 +64,7 @@ import ghidra.util.Msg;
import ghidra.util.table.GhidraProgramTableModel;
import ghidra.util.table.field.LabelTableColumn;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import util.CollectionUtils;
public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env;
@ -104,20 +103,20 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testActionEnablement() throws Exception {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(plugin.getName());
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
assertEquals(1, actions.size());
assertEquals("Go To Address/Label", actions.get(0).getName());
assertEquals("Go To Address/Label", CollectionUtils.any(actions).getName());
ActionContext actionContext = getActionContext();
assertTrue(!actions.get(0).isEnabledForContext(actionContext));
assertTrue(!CollectionUtils.any(actions).isEnabledForContext(actionContext));
loadProgram("x86");
actionContext = getActionContext();
assertTrue(actions.get(0).isEnabledForContext(actionContext));
assertTrue(CollectionUtils.any(actions).isEnabledForContext(actionContext));
final ProgramManager pm = tool.getService(ProgramManager.class);
SwingUtilities.invokeAndWait(() -> pm.closeProgram(program, true));
runSwing(() -> pm.closeProgram(program, true));
actionContext = getActionContext();
assertTrue(!actions.get(0).isEnabledForContext(actionContext));
assertTrue(!CollectionUtils.any(actions).isEnabledForContext(actionContext));
}
@Test
@ -476,7 +475,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
performOkCallback();
assertEquals("No results for xyzabc*", dialog.getStatusText());
SwingUtilities.invokeAndWait(() -> dialog.close());
runSwing(() -> dialog.close());
}
@Test
@ -561,7 +560,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
program.endTransaction(transactionID, true);
final JCheckBox cb = findComponent(dialog, JCheckBox.class);
SwingUtilities.invokeAndWait(() -> {
runSwing(() -> {
cb.setSelected(false);
dialog.setText("COm*");
@ -831,7 +830,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
Assert.assertNotNull(program);
final ProgramManager pm = tool.getService(ProgramManager.class);
SwingUtilities.invokeAndWait(() -> pm.openProgram(program.getDomainFile()));
runSwing(() -> pm.openProgram(program.getDomainFile()));
program.release(this);
addrFactory = program.getAddressFactory();
}
@ -965,7 +964,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
try {
Memory memory = program.getMemory();
return memory.createInitializedBlock(name, addr(address), size, (byte) 0,
TaskMonitorAdapter.DUMMY_MONITOR, true);
TaskMonitor.DUMMY, true);
}
finally {
program.endTransaction(transactionID, true);
@ -1023,7 +1022,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
}
private void setText(final String text) throws Exception {
SwingUtilities.invokeAndWait(() -> dialog.setText(text));
runSwing(() -> dialog.setText(text));
}
private void performOkCallback() throws Exception {
@ -1035,17 +1034,7 @@ public class GoToPluginTest extends AbstractGhidraHeadedIntegrationTest {
}
private void waitForOKCallback() {
int numWaits = 0;
while (++numWaits < 50 && !okButton.isEnabled()) {
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
// no biggie, will try again
}
}
Assert.assertNotEquals("Timed-out waiting for Go To dialog to finish", 50, numWaits);
waitForCondition(() -> runSwing(() -> okButton.isEnabled()));
}
private void assumeCurrentAddressSpace(boolean b) {

View file

@ -20,8 +20,7 @@ import static org.junit.Assert.*;
import java.awt.Window;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -60,6 +59,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.task.*;
import util.CollectionUtils;
import utilities.util.FileUtilities;
public abstract class AbstractGhidraScriptMgrPluginTest
@ -272,10 +272,18 @@ public abstract class AbstractGhidraScriptMgrPluginTest
assertTrue(message, fullText.contains(piece));
}
protected void assertRunLastActionEnabled(boolean enabled) {
final DockingActionIf runLastAction = getAction(plugin, "Rerun Last Script");
assertNotNull(runLastAction);
private DockingActionIf getRunLastScriptAction() {
// note: this provider adds 2 versions of the same action--pick either
Set<DockingActionIf> actions =
getActionsByOwnerAndName(plugin.getTool(), plugin.getName(), "Rerun Last Script");
assertFalse(actions.isEmpty());
DockingActionIf runLastAction = CollectionUtils.any(actions);
return runLastAction;
}
protected void assertRunLastActionEnabled(boolean enabled) {
DockingActionIf runLastAction = getRunLastScriptAction();
final AtomicReference<Boolean> ref = new AtomicReference<>();
runSwing(() -> ref.set(runLastAction.isEnabledForContext(new ActionContext())));
assertEquals("Run Last Action not enabled as expected", enabled, ref.get());
@ -557,17 +565,15 @@ public abstract class AbstractGhidraScriptMgrPluginTest
}
protected void pressRunLastScriptButton() {
DockingActionIf action =
getAction(plugin, GhidraScriptActionManager.RERUN_LAST_SHARED_ACTION_NAME);
performAction(action, false);
DockingActionIf runLastAction = getRunLastScriptAction();
performAction(runLastAction, false);
waitForSwing();
}
protected void performGlobalRunLastScriptAction() {
DockingActionIf action =
getAction(plugin, GhidraScriptActionManager.GLOBAL_RERUN_LAST_SHARED_ACTION_NAME);
performAction(action, false);
waitForSwing();
// note: this action used to be different from the 'run last script'; currently they are
// the same
pressRunLastScriptButton();
}
protected KeyBindingInputDialog pressKeyBindingAction() {
@ -1293,7 +1299,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
protected void assertToolKeyBinding(KeyStroke ks) {
String actionOwner = GhidraScriptMgrPlugin.class.getSimpleName();
PluginTool tool = env.getTool();
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName(actionOwner);
Set<DockingActionIf> actions = getActionsByOwner(tool, actionOwner);
for (DockingActionIf action : actions) {
KeyStroke keyBinding = action.getKeyBinding();
if (keyBinding == null) {

View file

@ -684,8 +684,8 @@ public class SearchTextPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
final GTable table = threadedTablePanel.getTable();
Random random = new Random();
final int randomRow = random.nextInt(model.getRowCount());
DockingActionIf deleteRowAction =
tool.getDockingActionsByFullActionName("Remove Items (TableServicePlugin)").get(0);
DockingActionIf deleteRowAction = getAction(tool, "TableServicePlugin", "Remove Items");
ProgramLocation toBeDeleted = model.getRowObject(randomRow);
runSwing(() -> table.setRowSelectionInterval(randomRow, randomRow));
performAction(deleteRowAction, true);

View file

@ -1306,11 +1306,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
private void setupSymbolTableFilterToShowParameters() throws Exception {
// get the filter action - "Set Filter"
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName("Set Filter (SymbolTablePlugin)");
assertNotNull(actions);
assertTrue(actions.size() > 0);
DockingActionIf filterAction = actions.get(0);
DockingActionIf filterAction = getAction(tool, "SymbolTablePlugin", "Set Filter");
// execute
performAction(filterAction, false);

View file

@ -30,6 +30,7 @@ import org.junit.*;
import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.test.AbstractDockingTest;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeRootNode;
@ -596,10 +597,9 @@ public class ActionManager1Test extends AbstractGhidraHeadedIntegrationTest {
}
private DockingActionIf getAction(String actionName) {
List<DockingActionIf> a =
frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)");
Assert.assertEquals(1, a.size());
return a.get(0);
DockingActionIf action =
AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName);
return action;
}
private void expandTreePath(TreePath path) {

View file

@ -30,6 +30,7 @@ import org.junit.*;
import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingAction;
import docking.test.AbstractDockingTest;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeRootNode;
@ -60,10 +61,6 @@ public class ActionManager2Test extends AbstractGhidraHeadedIntegrationTest {
private DomainFolder rootFolder;
private GTreeRootNode rootNode;
public ActionManager2Test() {
super();
}
@Before
public void setUp() throws Exception {
env = new TestEnv();
@ -363,10 +360,9 @@ public class ActionManager2Test extends AbstractGhidraHeadedIntegrationTest {
}
private DockingActionIf getAction(String actionName) {
List<DockingActionIf> a =
frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)");
assertEquals(1, a.size());
return a.get(0);
DockingActionIf action =
AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName);
return action;
}
private void setSelectionPath(final TreePath path) throws Exception {

View file

@ -30,10 +30,10 @@ import javax.swing.tree.TreePath;
import org.junit.*;
import docking.action.DockingActionIf;
import docking.actions.KeyBindingUtils;
import docking.options.editor.OptionsDialog;
import docking.options.editor.OptionsPanel;
import docking.tool.util.DockingToolConstants;
import docking.util.KeyBindingUtils;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.tree.GTree;
import generic.io.NullWriter;
@ -228,13 +228,13 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
// import the original values file through the tool
importOptionsWithGUI(saveFile, true);
// get the updated values that have not been applied
Map<?, ?> optionsMap = (Map<?, ?>) getInstanceField("actionMap", panel);
Map<String, KeyStroke> keyStrokeMap = panel.getKeyStrokeMap();
debug("f");
// verify the data is the same as it was before the changes
boolean same = compareOptionsWithKeyStrokeMap(originalOptions, optionsMap);
assertTrue("The Options object contains different data than was " + "imported.", same);
boolean same = compareOptionsWithKeyStrokeMap(originalOptions, keyStrokeMap);
assertTrue("The Options object contains different data than was imported.", same);
debug("g");
@ -405,26 +405,26 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
}
private void setKeyBinding(String keyText, int keyCode) throws Exception {
List<DockingActionIf> list = tool.getAllActions();
DockingActionIf action = null;
for (int i = 0; i < list.size(); i++) {
action = list.get(i);
Set<DockingActionIf> list = tool.getAllActions();
DockingActionIf arbitraryAction = null;
for (DockingActionIf action : list) {
if (action.isKeyBindingManaged() && action.getKeyBinding() == null) {
arbitraryAction = action;
break;
}
}
if (action == null) {
if (arbitraryAction == null) {
Assert.fail("Unable to find an action for which to set a key binding.");
}
selectRowForAction(action);
selectRowForAction(arbitraryAction);
triggerText(keyField, keyText);
assertEquals(keyText.toUpperCase(), keyField.getText());
runSwing(() -> panel.apply());
assertEquals(KeyStroke.getKeyStroke(keyCode, 0), action.getKeyBinding());
assertEquals(KeyStroke.getKeyStroke(keyCode, 0), arbitraryAction.getKeyBinding());
}
private void selectRowForAction(DockingActionIf action) throws Exception {
@ -533,20 +533,19 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
// compares the provided options with the mapping of property names to
// keystrokes (the map is obtained from the key bindings panel after an
// import is done).
private boolean compareOptionsWithKeyStrokeMap(Options options, Map<?, ?> optionsMap) {
private boolean compareOptionsWithKeyStrokeMap(Options options,
Map<String, KeyStroke> panelKeyStrokeMap) {
List<String> propertyNames = options.getOptionNames();
for (String element : propertyNames) {
boolean match = optionsMap.containsKey(element);
boolean match = panelKeyStrokeMap.containsKey(element);
Object value = invokeInstanceMethod("getKeyStroke", options,
new Class[] { String.class, KeyStroke.class }, new Object[] { element, null });
Object value2 = optionsMap.get(element);
KeyStroke optionsKs = options.getKeyStroke(element, null);
KeyStroke panelKs = panelKeyStrokeMap.get(element);
// if the value is null, then it would not have been placed into the
// options map in the key bindings panel, so we only care about
// non-null values
if (value != null) {
match &= (value.equals(value2));
// if the value is null, then it would not have been placed into the options map
// in the key bindings panel, so we only care about non-null values
if (optionsKs != null) {
match &= (optionsKs.equals(panelKs));
}
else {
match = true;

View file

@ -20,7 +20,7 @@ import static org.junit.Assert.*;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.table.*;
@ -29,10 +29,10 @@ import org.junit.*;
import docking.KeyEntryTextField;
import docking.action.DockingActionIf;
import docking.tool.util.DockingToolConstants;
import docking.widgets.MultiLineLabel;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.ToolConstants;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
import ghidra.util.Msg;
@ -102,9 +102,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testManagedKeyBindings() {
List<DockingActionIf> list = tool.getAllActions();
for (int i = 0; i < list.size(); i++) {
DockingActionIf action = list.get(i);
Set<DockingActionIf> list = tool.getAllActions();
for (DockingActionIf action : list) {
if (action.isKeyBindingManaged()) {
assertTrue(actionInTable(action));
}
@ -128,10 +127,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testActionNotSelected() throws Exception {
table.clearSelection();
List<DockingActionIf> list = tool.getAllActions();
DockingActionIf action = null;
for (int i = 0; i < list.size(); i++) {
action = list.get(i);
Set<DockingActionIf> list = tool.getAllActions();
for (DockingActionIf action : list) {
KeyStroke ks = getKeyStroke(action);
if (isKeyBindingManaged(action) && ks != KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0)) {
break;
@ -318,10 +315,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
}
private DockingActionIf getKeyBindingPluginAction() {
List<DockingActionIf> list = tool.getAllActions();
DockingActionIf action = null;
for (int i = 0; i < list.size(); i++) {
action = list.get(i);
Set<DockingActionIf> list = tool.getAllActions();
for (DockingActionIf action : list) {
KeyStroke ks = action.getKeyBinding();
if (action.isKeyBindingManaged() && ks != null &&
ks != KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0)) {
@ -372,7 +367,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
private void setUpDialog() throws Exception {
runSwing(() -> {
panel = new KeyBindingsPanel(tool, tool.getOptions(ToolConstants.KEY_BINDINGS));
panel = new KeyBindingsPanel(tool, tool.getOptions(DockingToolConstants.KEY_BINDINGS));
dialog = new JDialog(tool.getToolFrame(), "Test KeyBindings", false);
dialog.getContentPane().add(panel);
@ -391,10 +386,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
}
private void grabActionsWithoutKeybinding() {
List<DockingActionIf> list = tool.getAllActions();
DockingActionIf action = null;
for (int i = 0; i < list.size(); i++) {
action = list.get(i);
Set<DockingActionIf> list = tool.getAllActions();
for (DockingActionIf action : list) {
if (!action.isKeyBindingManaged()) {
continue;
}

View file

@ -20,8 +20,6 @@ import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
import javax.swing.SwingUtilities;
import org.junit.*;
import docking.action.DockingActionIf;
@ -74,7 +72,7 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest
@After
public void tearDown() throws Exception {
SwingUtilities.invokeAndWait(() -> {
runSwing(() -> {
tool.setConfigChanged(false);
provider.close();
});
@ -99,33 +97,27 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest
final Plugin p = getPlugin(tool, ArchivePlugin.class);
assertNotNull(p);
SwingUtilities.invokeAndWait(() -> {
runSwing(() -> {
provider.close();
tool.removePlugins(new Plugin[] { p });
});
showProvider();
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName("Save Project (" + plugin.getName() + ")");
assertEquals(1, actions.size());
performAction(actions.get(0), true);
DockingActionIf action = getAction(tool, plugin.getName(), "Save Project");
performAction(action, true);
actions =
tool.getDockingActionsByFullActionName("Close Project (" + plugin.getName() + ")");
assertEquals(1, actions.size());
performAction(actions.get(0), true);
action = getAction(tool, plugin.getName(), "Close Project");
performAction(action, true);
assertTrue(!provider.isVisible());
}
private void showProvider() throws Exception {
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName("Configure Tool (Project Window)");
assertEquals(1, actions.size());
performAction(actions.get(0), true);
DockingActionIf action = getAction(tool, "Project Window", "Configure Tool");
performAction(action, true);
waitForPostedSwingRunnables();
SwingUtilities.invokeAndWait(() -> tool.showConfig(false, false));
runSwing(() -> tool.showConfig(false, false));
provider = tool.getManagePluginsDialog();
pluginManagerComponent = (PluginManagerComponent) getInstanceField("comp", provider);

View file

@ -254,11 +254,9 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest {
}
private void showProvider() {
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName("Configure Tool (Tool)");
assertEquals(1, actions.size());
performAction(actions.get(0), true);
DockingActionIf action = getAction(tool, "Tool", "Configure Tool");
performAction(action, true);
waitForSwing();
provider = tool.getManagePluginsDialog();
pluginManagerComponent = (PluginManagerComponent) getInstanceField("comp", provider);

View file

@ -18,7 +18,6 @@ package ghidra.framework.project.tool;
import static org.junit.Assert.*;
import java.awt.Window;
import java.util.List;
import org.junit.*;
@ -56,7 +55,7 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest {
public void tearDown() throws Exception {
executeOnSwingWithoutBlocking(() -> env.dispose());
closeAllWindowsAndFrames();
closeAllWindows();
}
@ -138,7 +137,7 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest {
closeTool(tool);
// check for warning dialog
Window window = waitForWindow(tool.getToolFrame(), "Tool Busy", 2000);
Window window = waitForWindow("Tool Busy");
assertNotNull("Did not get tool busy dialog", window);
closeWindow(window);
@ -146,7 +145,7 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest {
closeProgram(tool, program);
// check for warning dialog
window = waitForWindow(tool.getToolFrame(), "Close notepad Failed", 2000);
window = waitForWindow("Close notepad Failed");
assertNotNull("Did not get \"close failed\" dialog", window);
closeWindow(window);
@ -169,11 +168,9 @@ public class CloseToolTest extends AbstractGhidraHeadedIntegrationTest {
}
private void closeProgram(final PluginTool tool, final ProgramDB program) {
List<DockingActionIf> actionList =
tool.getDockingActionsByFullActionName("Close File (ProgramManagerPlugin)");
assertTrue(!actionList.isEmpty());
performAction(actionList.get(0), new ProgramActionContext(null, program), false);
DockingActionIf action = getAction(tool, "ProgramManagerPlugin", "Close File");
performAction(action, new ProgramActionContext(null, program), false);
waitForPostedSwingRunnables();
}

View file

@ -201,10 +201,9 @@ public class DateEditorTest extends AbstractGhidraHeadedIntegrationTest {
}
private void showProgramOptions() {
List<DockingActionIf> list = tool.getAllActions();
for (int i = 0; i < list.size(); i++) {
DockingActionIf action = list.get(i);
// TODO change to getAction("Program Options")
Set<DockingActionIf> list = tool.getAllActions();
for (DockingActionIf action : list) {
if (action.getName().equals("Program Options")) {
performAction(action, plugin.getProvider(), false);
break;

View file

@ -988,10 +988,9 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
}
private void showOptionsDialog(PluginTool pluginTool) throws Exception {
List<DockingActionIf> list = pluginTool.getAllActions();
for (int i = 0; i < list.size(); i++) {
DockingActionIf action = list.get(i);
// TODO change to getAction("Edit Options")
Set<DockingActionIf> list = pluginTool.getAllActions();
for (DockingActionIf action : list) {
if (action.getName().equals("Edit Options")) {
performAction(action, false);
break;

View file

@ -19,7 +19,7 @@ import java.awt.Window;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.swing.ImageIcon;
import javax.swing.event.ChangeListener;
@ -438,18 +438,13 @@ public class DummyTool implements Tool {
}
@Override
public List<DockingActionIf> getAllActions() {
return Collections.emptyList();
public Set<DockingActionIf> getAllActions() {
return Collections.emptySet();
}
@Override
public List<DockingActionIf> getDockingActionsByOwnerName(String owner) {
return Collections.emptyList();
}
@Override
public List<DockingActionIf> getDockingActionsByFullActionName(String fullActionName) {
return Collections.emptyList();
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner) {
return Collections.emptySet();
}
@Override

View file

@ -21,7 +21,6 @@ import java.awt.event.ActionListener;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import javax.swing.*;
@ -97,9 +96,9 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
tc.remove("MyTestTool");
tc.remove("TestTool");
waitForPostedSwingRunnables();
waitForSwing();
tool.setConfigChanged(false);
SwingUtilities.invokeAndWait(() -> saveDialog.close());
runSwing(() -> saveDialog.close());
env.dispose();
}
@ -125,7 +124,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
pressButtonByText(saveDialog, "Save");
assertTrue(!tool.hasConfigChanged());
waitForPostedSwingRunnables();
waitForSwing();
assertTrue(!saveDialog.isVisible());
ToolChest tc = tool.getProject().getLocalToolChest();
ToolTemplate config = tc.getToolTemplate("MyTestTool");
@ -142,7 +141,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
while (saveDialog.isVisible()) {
Thread.sleep(5);
}
waitForPostedSwingRunnables();
waitForSwing();
assertEquals("Name cannot have spaces.", msg);
}
@ -156,7 +155,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
pressButtonByText(saveDialog, "Save");
assertTrue(!tool.hasConfigChanged());
waitForPostedSwingRunnables();
waitForSwing();
assertTrue(!saveDialog.isVisible());
ToolChest tc = tool.getProject().getLocalToolChest();
ToolTemplate template = tc.getToolTemplate("MyTestTool");
@ -180,7 +179,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
while (saveDialog.isVisible()) {
Thread.sleep(5);
}
waitForPostedSwingRunnables();
waitForSwing();
}
@Test
@ -208,8 +207,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
final JButton browseButton = (JButton) findComponentByName(saveDialog, "BrowseButton");
pressButton(browseButton, false);
final GhidraFileChooser chooser =
waitForDialogComponent(GhidraFileChooser.class);
final GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class);
assertNotNull(chooser);
runSwing(() -> chooser.setSelectedFile(destFile));
@ -240,7 +238,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
while (tc.getToolTemplate("MyTestTool") == null) {
Thread.sleep(10);
}
waitForPostedSwingRunnables();
waitForSwing();
setText(toolNameField, "MyTestTool", false);
@ -256,10 +254,9 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
JButton saveButton = findButtonByText(saveDialog, "Save");
saveButton.getActionListeners()[0].actionPerformed(null);
});
waitForPostedSwingRunnables();
waitForSwing();
final OptionDialog d =
waitForDialogComponent(tool.getToolFrame(), OptionDialog.class, 2000);
final OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
assertEquals("Overwrite Tool?", d.getTitle());
pressButtonByText(d.getComponent(), "Overwrite");
@ -267,7 +264,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
while (d.isVisible()) {
Thread.sleep(10);
}
waitForPostedSwingRunnables();
waitForSwing();
assertTrue(!tool.hasConfigChanged());
}
@ -282,11 +279,11 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
while (tc.getToolTemplate("MyTestTool") == null) {
Thread.sleep(10);
}
waitForPostedSwingRunnables();
waitForSwing();
setText(toolNameField, "MyTestTool", false);
SwingUtilities.invokeAndWait(() -> {
runSwing(() -> {
// force a change to the tool config
try {
tool.addPlugin(ByteViewerPlugin.class.getName());
@ -301,10 +298,9 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
JButton saveButton = findButtonByText(saveDialog, "Save");
saveButton.getActionListeners()[0].actionPerformed(null);
});
waitForPostedSwingRunnables();
waitForSwing();
final OptionDialog d =
waitForDialogComponent(OptionDialog.class);
final OptionDialog d = waitForDialogComponent(OptionDialog.class);
assertNotNull(d);
assertEquals("Overwrite Tool?", d.getTitle());
pressButtonByText(d.getComponent(), "Cancel");
@ -312,17 +308,16 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
while (d.isVisible()) {
Thread.sleep(10);
}
waitForPostedSwingRunnables();
waitForSwing();
assertTrue(tool.hasConfigChanged());
}
/////////////////////////////////////////////////////////////////////
private void showDialogs() throws Exception {
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName("Save Tool As (Tool)");
performAction(actions.get(0), false);
waitForPostedSwingRunnables();
DockingActionIf action = getAction(tool, "Tool", "Save Tool As");
performAction(action, false);
waitForSwing();
saveDialog = waitForDialogComponent(SaveToolConfigDialog.class);
@ -335,7 +330,7 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
private void setText(final JTextField field, final String text, final boolean doAction)
throws Exception {
SwingUtilities.invokeAndWait(() -> {
runSwing(() -> {
field.setText(text);
if (doAction) {
ActionListener[] listeners = field.getActionListeners();
@ -344,6 +339,6 @@ public class SaveToolConfigDialogTest extends AbstractGhidraHeadedIntegrationTes
}
}
});
waitForPostedSwingRunnables();
waitForSwing();
}
}

View file

@ -27,6 +27,7 @@ import docking.action.DockingAction;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.label.GDHtmlLabel;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.util.viewer.listingpanel.ProgramLocationListener;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
@ -255,8 +256,13 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
String leftTitle1 = FunctionUtility.getFunctionTitle(functions[LEFT]);
String rightTitle1 = FunctionUtility.getFunctionTitle(functions[RIGHT]);
titlePanels[LEFT] = new TitledPanel(leftTitle1, cPanels[LEFT], 5);
titlePanels[RIGHT] = new TitledPanel(rightTitle1, cPanels[RIGHT], 5);
// use mutable labels, as the titles update when functions are selected
GDHtmlLabel leftTitleLabel = new GDHtmlLabel(leftTitle1);
GDHtmlLabel rightTitleLabel = new GDHtmlLabel(rightTitle1);
titlePanels[LEFT] = new TitledPanel(leftTitleLabel, cPanels[LEFT], 5);
titlePanels[RIGHT] = new TitledPanel(rightTitleLabel, cPanels[RIGHT], 5);
// Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's
// divider from becoming locked (can't be moved) due to extra long title names.

View file

@ -35,6 +35,7 @@ import docking.*;
import docking.action.*;
import docking.menu.ActionState;
import docking.menu.MultiStateDockingAction;
import docking.test.AbstractDockingTest;
import docking.widgets.EventTrigger;
import docking.widgets.OptionDialog;
import docking.widgets.dialogs.MultiLineInputDialog;
@ -470,13 +471,11 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected void performReload() throws Exception {
String name = "Reset Graph";
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)");
assertEquals("Could not find action: " + name, 1, actions.size());
DockingActionIf action = getAction(tool, graphPlugin.getName(), name);
long start = System.currentTimeMillis();
performAction(actions.get(0), false);
performAction(action, false);
Window window = waitForWindow("Reset Graph?");
pressButtonByText(window, "Yes");
@ -917,20 +916,16 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
private void toggleSatellite() {
String name = "Display Satellite View";
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)");
assertEquals("Could not find action: " + name, 1, actions.size());
ToggleDockingAction dockAction = (ToggleDockingAction) actions.get(0);
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name);
ToggleDockingAction dockAction = (ToggleDockingAction) action;
performAction(dockAction, true);
}
protected void undockSatellite() {
String name = "Dock Satellite View";
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)");
assertEquals("Could not find action: " + name, 1, actions.size());
ToggleDockingAction dockAction = (ToggleDockingAction) actions.get(0);
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name);
ToggleDockingAction dockAction = (ToggleDockingAction) action;
assertTrue(name + " action is not selected as expected", dockAction.isSelected());
performAction(dockAction, true);
@ -938,11 +933,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected void redockSatellite() {
String name = "Dock Satellite View";
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)");
assertEquals("Could not find action: " + name, 1, actions.size());
ToggleDockingAction dockAction = (ToggleDockingAction) actions.get(0);
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name);
ToggleDockingAction dockAction = (ToggleDockingAction) action;
assertFalse(name + " action is not selected as expected", dockAction.isSelected());
performAction(dockAction, true);
@ -1210,10 +1203,8 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected FGController cloneGraph() {
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(
"Function Graph Clone (" + graphPlugin.getName() + ")");
assertEquals(1, actions.size());
DockingActionIf snapshotAction = actions.get(0);
DockingActionIf snapshotAction =
AbstractDockingTest.getAction(tool, graphPlugin.getName(), "Function Graph Clone");
performAction(snapshotAction, true);
@SuppressWarnings("unchecked")
@ -1429,6 +1420,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
assertSelected(ungroupedVertices);
}
// @formatter:on
protected void doTestGroupingProperlyTranslatesEdgesFromGroupedVerticesToRealVertices() {
//
@ -1437,7 +1429,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
// need to test this functionality, but we don't have a jComplicatedTest, so we will do
// it here.
//
//
// Desired Behavior: We want to be able to group vertices, group grouped vertices and then
// ungroup them in any order. For us to be able to do this, our group
@ -1457,7 +1449,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
//
// The fix is mentioned in the Desired Behavior section.
//
/*
0) Initial Graph
@ -1468,26 +1460,26 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
5
*/
create12345Graph();
//
// Our graph maps from number to address like so:
//
FGVertex v1 = vertex("100415a");
FGVertex v2 = vertex("1004178");
FGVertex v3 = vertex("1004192");
FGVertex v4 = vertex("1004196");
FGVertex v5 = vertex("100419c");
// verify initial graph
verifyEdge(v1, v2);
verifyEdge(v2, v3);
verifyEdge(v3, v4);
verifyEdge(v3, v5);
verifyEdgeCount(4);
/*
1) Create two separate group vertices (A and B), such that A has an edge to B.
@ -1497,14 +1489,14 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
5
*/
GroupedFunctionGraphVertex groupA = group("A", v1, v2);
GroupedFunctionGraphVertex groupB = group("B", v3, v4);
verifyEdge(groupA, groupB);
verifyEdge(groupB, v5);
verifyEdgeCount(2);// no other edges
/*
2) Create a third group vertex (Z) that contains a non-grouped vertex *and* one
of the other groups (B).
@ -1515,12 +1507,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
)
*/
GroupedFunctionGraphVertex groupZ = group("Z", groupB, v5);
verifyEdge(groupA, groupZ);
verifyEdgeCount(1);
/*
3) Now, ungroup the 1 remaining originally grouped vertex (A).
@ -1530,13 +1522,13 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
)
*/
ungroup(groupA);
verifyEdge(v1, v2);
verifyEdge(v2, groupZ);
verifyEdgeCount(2);
/*
4) Now, ungroup Z and go back to having one remaining group vertex (B)
@ -1547,14 +1539,14 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
5
*/
ungroup(groupZ);
verifyEdge(v1, v2);
verifyEdge(v2, groupB);
verifyEdge(groupB, v5);
verifyEdgeCount(3);
/*
5) Finally, ungroup the last group and make sure the graph is restored
@ -1564,15 +1556,15 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
5
*/
ungroup(groupB);
verifyEdge(v1, v2);
verifyEdge(v2, v3);
verifyEdge(v3, v4);
verifyEdge(v3, v5);
verifyEdgeCount(4);
}
private void doTestRestoringWhenCodeBlocksHaveChanged_DoesntRegroup() {
@ -1587,28 +1579,28 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
// be found by the regrouping algorithm. Furthermore, the regrouping will not take place
// if at least two vertices cannot be found.
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
FunctionGraph functionGraph = graphData.getFunctionGraph();
Set<FGVertex> ungroupedVertices =
selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */);
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// -01002cf5: 2 in, 1 out
int expectedGroupedEdgeCount = 5;
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
AddressSetView addresses = groupedVertex.getAddresses();
Address minAddress = addresses.getMinAddress();
//
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
@ -1618,12 +1610,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
//
graphFunction("0100415a");
clearCache();
//
// Add a label to trigger a code block change
//
createLabel("01002d18");// in the middle of the LAB_01002d11 code block
//
// Relaunch the graph, which will use the above persisted group settings...
//
@ -1646,18 +1638,18 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
// be found by the regrouping algorithm. Furthermore, the regrouping *will* still
// take place, as at least two vertices cannot be found.
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
FunctionGraph functionGraph = graphData.getFunctionGraph();
Set<FGVertex> ungroupedVertices = selectVertices(functionGraph,
"01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */, "01002d1f" /* MyLocal */);
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// -01002d11: 2 in, (1 out that was removed)
@ -1665,11 +1657,11 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
int expectedGroupedEdgeCount = 6;
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
AddressSetView addresses = groupedVertex.getAddresses();
Address minAddress = addresses.getMinAddress();
Address maxAddress = addresses.getMaxAddress();
//
// Ideally, we would like to save, close and re-open the program so that we can get
// a round-trip saving and reloading. However, in the test environment, we cannot save
@ -1679,12 +1671,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
//
graphFunction("0100415a");
clearCache();
//
// Add a label to trigger a code block change
//
Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block
//
// Relaunch the graph, which will use the above persisted group settings...
//
@ -1694,22 +1686,22 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
FGVertex expectedGroupVertex = functionGraph.getVertexForAddress(minAddress);
assertTrue(expectedGroupVertex instanceof GroupedFunctionGraphVertex);
assertEquals(maxAddress, expectedGroupVertex.getAddresses().getMaxAddress());
// ...we expect that the two original grouped vertices have again been grouped...
FGVertex splitVertex =
functionGraph.getVertexForAddress(getAddress("01002d11") /* LAB_01002d11 */);
assertTrue("The split vertex should not have been regrouped",
!(splitVertex instanceof GroupedFunctionGraphVertex));
FGVertex unchangedVertex =
functionGraph.getVertexForAddress(getAddress("01002cf5") /* ghidra */);
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
(unchangedVertex instanceof GroupedFunctionGraphVertex));
unchangedVertex = functionGraph.getVertexForAddress(getAddress("01002d1f") /* MyLocal */);
assertTrue("An unchanged vertex should have been regrouped: " + unchangedVertex,
(unchangedVertex instanceof GroupedFunctionGraphVertex));
// ...but the newly created code block has not
FGVertex newlyCreatedVertex = functionGraph.getVertexForAddress(labelAddress);
assertNotNull(newlyCreatedVertex);
@ -1722,30 +1714,30 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
// However, if the affected vertex is grouped, then the FG will not split the node, but
// should still show the 'stale' indicator.
//
//
// Pick a function and group some nodes.
//
FGData graphData = graphFunction("01002cf5");
FunctionGraph functionGraph = graphData.getFunctionGraph();
Set<FGVertex> ungroupedVertices =
selectVertices(functionGraph, "01002d11" /* LAB_01002d11 */, "01002cf5" /* ghidra */);
group(ungroupedVertices);
// 5 edges expected:
// -01002cf5: 2 out
// -01002cf5: 2 in, 1 out
int expectedGroupedEdgeCount = 5;
GroupedFunctionGraphVertex groupedVertex = validateNewGroupedVertexFromVertices(
functionGraph, ungroupedVertices, expectedGroupedEdgeCount);
//
// Add a label to trigger a code block change
//
Address labelAddress = createLabel("01002d18");// in the middle of the LAB_01002d11 code block
//
// Make sure the newly created code block does not have a corresponding vertex
//
@ -1792,7 +1784,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
Msg.debug(this, "\t" + v);
}
}
Assert.fail("Did not find group vertex at " + address + ". Instead found " + vertex);
}
return (GroupedFunctionGraphVertex) vertex;
@ -1819,7 +1811,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected FGData graphFunction(String functionAddress) {
// Find a good test function.
goToAddress(functionAddress);
FGData graphData = getFunctionGraphData();
assertNotNull(graphData);
assertTrue("Unexpectedly received an empty FunctionGraphData", graphData.hasResults());
@ -1838,22 +1830,22 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
DockingAction action = (DockingAction) TestUtils.getInstanceField("groupAction", component);
performAction(action, graphProvider, false);
waitForAnimation();
MultiLineInputDialog dialog = waitForDialogComponent(MultiLineInputDialog.class);
if (groupVertexText != null) {
final JTextArea inputTextArea = (JTextArea) getInstanceField("inputTextArea", dialog);
runSwing(() -> inputTextArea.setText(groupVertexText));
}
pressButtonByText(dialog.getComponent(), "OK");
if (groupVertexText != null) {
String value = dialog.getValue();
assertEquals("Group vertex text was not set in the dialog", groupVertexText, value);
}
waitForAnimation();
FGController controller = getFunctionGraphController();
FGData data = controller.getFunctionGraphData();
FunctionGraph fg = data.getFunctionGraph();
@ -1861,19 +1853,19 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
}
protected GroupedFunctionGraphVertex group(String groupName, FGVertex... vertices) {
HashSet<FGVertex> set = new HashSet<>();
for (FGVertex v : vertices) {
set.add(v);
}
pickVertices(set);
GroupedFunctionGraphVertex groupVertex = group(set, groupName);
// for debugging
Object componentPanel = getComponent(groupVertex);
setInstanceField("title", componentPanel, groupName);
return groupVertex;
}
@ -1887,23 +1879,22 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
runSwing(() -> {
PickedState<FGVertex> pickedState = getPickedState();
pickedState.clear();
for (FGVertex vertex : vertices) {
pickedState.pick(vertex, true);
}
});
}
protected GroupedFunctionGraphVertex regroup(FGVertex vertex) {
DockingActionIf regroupAction = getRegroupAction(vertex);
if (regroupAction == null) {
Assert.fail("Did not find the regroup action on vertex: " + vertex.getTitle());
}
performAction(regroupAction, false);
waitForBusyGraph();
FGController controller = getFunctionGraphController();
FGData data = controller.getFunctionGraphData();
FunctionGraph fg = data.getFunctionGraph();
@ -1912,12 +1903,12 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
private void removeEdge(FunctionGraph functionGraph, Address startAddress,
Address destinationAddress) {
Graph<FGVertex, FGEdge> graph = functionGraph;
FGVertex startVertex = functionGraph.getVertexForAddress(startAddress);
FGVertex destinationVertex = functionGraph.getVertexForAddress(destinationAddress);
FGEdge edge = graph.findEdge(startVertex, destinationVertex);
runSwing(() -> graph.removeEdge(edge));
FGController controller = getFunctionGraphController();
@ -1927,10 +1918,10 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected void removeFromUncollapsedGroup(FGVertex... vertices) {
FunctionGraph functionGraph = getFunctionGraph();
selectVertices(functionGraph, vertices);
DockingActionIf action = getAction(graphPlugin, "Remove From Group");
assertNotNull(action);
performAction(action, graphProvider, false);
waitForBusyGraph();
}
@ -1944,35 +1935,32 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
}
protected FGData reset() {
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName("Reset Graph (FunctionGraphPlugin)");
assertEquals(1, actions.size());
DockingActionIf action = actions.get(0);
DockingActionIf action = getAction(tool, graphPlugin.getName(), "Reset Graph");
performAction(action, graphProvider, false);
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
pressButtonByText(dialog, "Yes");
// wait for the threaded graph layout code
return getFunctionGraphData();
}
private Set<FGVertex> selectVertices(FunctionGraph functionGraph, FGVertex... vertices) {
Set<FGVertex> set = new HashSet<>();
for (FGVertex vertex : vertices) {
set.add(vertex);
}
pickVertices(set);
waitForSwing();
return set;
}
protected Set<FGVertex> selectVertices(FunctionGraph functionGraph, String... addressString) {
Set<FGVertex> vertices = new HashSet<>();
for (String string : addressString) {
Address vertexAddress = getAddress(string);
@ -1980,26 +1968,26 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
assertNotNull("No vertex for address: " + vertexAddress, vertex);
vertices.add(vertex);
}
pickVertices(vertices);
waitForSwing();
return vertices;
}
protected void setGroupText(GroupedFunctionGraphVertex group, final String newText) {
final GroupedFunctionGraphVertex updatedGroup = update(group);
runSwing(() -> updatedGroup.editLabel(null), false);
Window window = waitForWindow("Enter Group Vertex Text");
assertNotNull(window);
final JTextArea textArea = findComponent(window, JTextArea.class);
runSwing(() -> textArea.setText(newText));
pressButtonByText(window, "OK");
waitForSwing();
}
@ -2018,9 +2006,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
action.setCurrentActionState(state);
}
}
});
}
protected FGData triggerPersistence(String functionAddress) {
@ -2033,7 +2021,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
//
graphFunction("0100415a");
clearCache();
//
// Graph the original function and make sure that the previously grouped nodes is again
// grouped.
@ -2046,9 +2034,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
// Unusual Code: for some of the regroup actions, group vertices are created and restored,
// but are always equal. Thus, the Function Graph works correctly, but the
// test can get out-of-sync, so we update before we use it
groupedVertex = getGroupVertex(getFunctionGraph(), groupedVertex.getVertexAddress());
ungroup(groupedVertex);
}
@ -2063,45 +2051,42 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
}
protected void ungroupAll() {
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName("Ungroup All Vertices (FunctionGraphPlugin)");
assertEquals(1, actions.size());
DockingActionIf action = actions.get(0);
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", "Ungroup All Vertices");
performAction(action, graphProvider, false);
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
pressButtonByText(dialog, "Yes");
// wait for the threaded graph layout code
waitForBusyGraph();
}
//==================================================================================================
// Private Methods
//==================================================================================================
private GroupedFunctionGraphVertex update(GroupedFunctionGraphVertex group) {
// Unusual Code: for some of the regroup actions, group vertices are created and restored,
// but are always equal. Thus, the Function Graph works correctly, but the
// test can get out-of-sync, so we update before we use it
return getGroupVertex(getFunctionGraph(), group.getVertexAddress());
}
//==================================================================================================
// Private Methods
//==================================================================================================
private GroupedFunctionGraphVertex update(GroupedFunctionGraphVertex group) {
// Unusual Code: for some of the regroup actions, group vertices are created and restored,
// but are always equal. Thus, the Function Graph works correctly, but the
// test can get out-of-sync, so we update before we use it
return getGroupVertex(getFunctionGraph(), group.getVertexAddress());
}
protected GroupedFunctionGraphVertex validateNewGroupedVertexFromVertices(
FunctionGraph functionGraph, Set<FGVertex> vertices, int expectedGroupedEdgeCount) {
FGVertex aVertex = vertices.iterator().next();
FGVertex currentVertex = functionGraph.getVertexForAddress(aVertex.getVertexAddress());
assertTrue(currentVertex instanceof GroupedFunctionGraphVertex);
GroupedFunctionGraphVertex groupedVertex = (GroupedFunctionGraphVertex) currentVertex;
//
// make sure we have new edges
//
Graph<FGVertex, FGEdge> graph = functionGraph;
Collection<FGEdge> groupedEdges = graph.getIncidentEdges(groupedVertex);
assertEquals("Ungrouped edges not replaced with new edges for the grouped vertex",
expectedGroupedEdgeCount, groupedEdges.size());
assertSelected(groupedVertex);
@ -2127,7 +2112,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
FGData data = getFunctionGraphData();
FunctionGraph functionGraph = data.getFunctionGraph();
Graph<FGVertex, FGEdge> graph = functionGraph;
FGEdge edge = graph.findEdge(start, destination);
assertNotNull("No edge exists for vertices: " + start + " and " + destination, edge);
}
@ -2166,7 +2151,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
ComponentProvider provider = tool.getComponentProvider(FGSatelliteUndockedProvider.NAME);
assertUndockedProviderShowing(provider);
}
protected void assertUndockedProviderShowing(ComponentProvider satellite) {
assertNotNull("Undocked provider is not installed when it should be", satellite);
assertTrue("Undocked provider is not showing after being undocked", satellite.isVisible());
@ -2176,7 +2161,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
Double scale = getGraphScale(getPrimaryGraphViewer());
int result = Double.compare(scale, 1.0);
assertEquals("Graph not fully zoomed-in; scale: " + scale, 0, result);
FGVertex v = getFocusedVertex();
Rectangle cursorBounds = v.getCursorBounds();
Window graphWindow = windowForComponent(getPrimaryGraphViewer());
@ -2196,13 +2181,13 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
DockingAction action =
(DockingAction) TestUtils.getInstanceField("chooseColorAction", setColorAction);
performAction(action, graphProvider, false);
Window chooserWindow = waitForWindow("Please Select Background Color");
Object colorChooserDialog = chooserWindow;// the name is the real type
JColorChooser chooser =
(JColorChooser) TestUtils.getInstanceField("chooserPane", colorChooserDialog);
chooser.setColor(testColor);
JButton okButton = findButtonByText(chooserWindow, "OK");
runSwing(() -> okButton.doClick());
waitForSwing();
@ -2216,32 +2201,32 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
}
protected void debugAction(DockingAction copyAction, ActionContext context) {
Msg.debug(this, "Copy action not enabled at location " + codeBrowser.getCurrentLocation());
FGVertex focusedVertex = getFocusedVertex();
Msg.debug(this, "\tfocused vertex: " + focusedVertex);
Msg.debug(this, "\tcontext: " + context);
// Figure out which check in the action failed
Object clipboardService = getInstanceField("clipboardService", copyAction);
Msg.debug(this, "\tservice: " + clipboardService);
Boolean result = (Boolean) invokeInstanceMethod("isValidContext", clipboardService,
new Class[] { ActionContext.class }, new Object[] { context });
Msg.debug(this, "\tisValidContext()?: " + result);
result = (Boolean) invokeInstanceMethod("canCopy", clipboardService);
Msg.debug(this, "\tcanCopy: " + result);
Boolean copyFromSelectionEnabled =
(Boolean) getInstanceField("copyFromSelectionEnabled", clipboardService);
Msg.debug(this, "\tcopyFromSelectionEnabled: " + copyFromSelectionEnabled);
String stringContent = (String) getInstanceField("stringContent", clipboardService);
Msg.debug(this, "\tstringContent: " + stringContent);
Object location = getInstanceField("currentLocation", clipboardService);
Msg.debug(this, "\tservice location: " + location);
}
@ -2258,7 +2243,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
ProgramLocation location = getLocationForAddressString(startAddressString);
assertTrue(graphData.containsLocation(location));
FunctionGraph functionGraph = graphData.getFunctionGraph();
// locate vertex with cursor
FGVertex focusedVertex = getFocusVertex(functionGraph);
assertNotNull("We did not start with a focused vertex", focusedVertex);
@ -2274,7 +2259,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected SetVertexMostRecentColorAction getSetMostRecentColorAction(FGVertex vertex) {
// this action is odd in that it is not installed in the tool, but is owned by each
// vertex directly
JComponent internalGraphComponent = vertex.getComponent();
return (SetVertexMostRecentColorAction) getInstanceField("setVertexMostRecentAction",
internalGraphComponent);
@ -2284,7 +2269,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
Address addr = getAddress(address);
GoToService goToService = tool.getService(GoToService.class);
goToService.goTo(addr);
waitForBusyGraph();
}
@ -2299,11 +2284,9 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected void navigateBack() {
String name = "Previous in History Buffer";
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(name + " (NextPrevAddressPlugin)");
assertEquals("Could not find action: " + name, 1, actions.size());
performAction(actions.get(0), true);
DockingActionIf action = getAction(tool, "NextPrevAddressPlugin", name);
performAction(action, true);
waitForBusyGraph();
}
@ -2320,18 +2303,16 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
}
protected void setGraphWindowSize(final int width, final int height) {
final Window graphWindow = windowForComponent(getPrimaryGraphViewer());
runSwing(() -> graphWindow.setSize(width, height));
}
protected void toggleSatalliteVisible(boolean expectedVisible) {
String name = "Display Satellite View";
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(name + " (FunctionGraphPlugin)");
assertEquals("Could not find action: " + name, 1, actions.size());
ToggleDockingAction displayAction = (ToggleDockingAction) actions.get(0);
DockingActionIf action = getAction(tool, "FunctionGraphPlugin", name);
ToggleDockingAction displayAction = (ToggleDockingAction) action;
setToggleActionSelected(displayAction, new ActionContext(), expectedVisible);
//
// // make sure the action is not already in the state we expect

View file

@ -177,10 +177,8 @@ public class FunctionGraphPlugin2Test extends AbstractFunctionGraphTest {
GraphPerspectiveInfo<FGVertex, FGEdge> primaryPerspective =
primaryController.getGraphPerspective(location);
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(
"Function Graph Clone (" + graphPlugin.getName() + ")");
assertEquals(1, actions.size());
DockingActionIf snapshotAction = actions.get(0);
DockingActionIf snapshotAction =
getAction(tool, graphPlugin.getName(), "Function Graph Clone");
performAction(snapshotAction, true);
assertEquals(1, disconnectedProviders.size());

View file

@ -21,7 +21,7 @@ import java.awt.*;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.swing.*;
@ -589,7 +589,7 @@ public class DiffTestAdapter extends AbstractGhidraHeadedIntegrationTest {
}
public static DockingActionIf getToolAction(PluginTool tool, String name) {
List<DockingActionIf> actions = tool.getDockingActionsByOwnerName("Tool");
Set<DockingActionIf> actions = getActionsByOwner(tool, "Tool");
for (DockingActionIf action : actions) {
if (name.equals(action.getName())) {
return action;

View file

@ -17,9 +17,11 @@ package ghidra.feature.vt.gui.plugin;
import java.net.URL;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import docking.action.DockingActionIf;
import docking.help.Help;
import docking.help.HelpService;
import docking.wizard.WizardManager;
@ -121,14 +123,29 @@ public class VTPlugin extends Plugin {
createActions();
registerServiceProvided(VTController.class, controller);
tool.setUnconfigurable();
tool.removeAction(tool.getDockingActionsByFullActionName("Save Tool As (Tool)").get(0));
tool.removeAction(tool.getDockingActionsByFullActionName("Export Tool (Tool)").get(0));
DockingActionIf saveAs = getToolAction("Save Tool As");
tool.removeAction(saveAs);
DockingActionIf export = getToolAction("Export Tool");
tool.removeAction(export);
new MatchStatusUpdaterAssociationHook(controller);
new ImpliedMatchAssociationHook(controller);
initializeOptions();
}
private DockingActionIf getToolAction(String actionName) {
Set<DockingActionIf> actions = tool.getDockingActionsByOwnerName("Tool");
for (DockingActionIf action : actions) {
if (action.getName().equals(actionName)) {
return action;
}
}
throw new IllegalArgumentException("Unable to find Tool action '" + actionName + "'");
}
private void initializeOptions() {
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
options.registerOptionsEditor(new ListingDisplayOptionsEditor(this, options));

View file

@ -17,8 +17,7 @@ package ghidra.feature.vt.gui.plugin;
import java.awt.Component;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import javax.swing.KeyStroke;
@ -27,6 +26,7 @@ import org.jdom.output.XMLOutputter;
import docking.ActionContext;
import docking.action.*;
import docking.tool.util.DockingToolConstants;
import docking.widgets.OptionDialog;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.support.FieldSelection;
@ -153,17 +153,17 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
catch (PluginException e) {
Msg.error(this, "Failed to create subordinate tool: " + toolName);
}
newTool.setToolName(toolName);
newTool.removeAction(newTool.getDockingActionsByFullActionName("Save Tool (Tool)").get(0));
newTool.removeAction(
newTool.getDockingActionsByFullActionName("Save Tool As (Tool)").get(0));
// newTool.removeAction(newTool.getDockableActionsByFullActionName("Export
// Tool (Tool)").get(0));
DockingActionIf save = getToolAction(newTool, "Save Tool");
newTool.removeAction(save);
createMarkupActions(newTool);
newTool.setConfigChanged(false);
ToolOptions options = newTool.getOptions(ToolConstants.KEY_BINDINGS);
ToolOptions options = newTool.getOptions(DockingToolConstants.KEY_BINDINGS);
options.addOptionsChangeListener(this);
// custom VT actions
@ -172,6 +172,16 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
return newTool;
}
private DockingActionIf getToolAction(Tool tool, String actionName) {
Set<DockingActionIf> actions = tool.getDockingActionsByOwnerName("Tool");
for (DockingActionIf action : actions) {
if (action.getName().equals(actionName)) {
return action;
}
}
throw new IllegalArgumentException("Unable to find Tool action '" + actionName + "'");
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {

View file

@ -17,7 +17,6 @@ package docking;
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.JFrame;
@ -116,22 +115,16 @@ public abstract class AbstractDockingTool implements DockingTool {
}
@Override
public List<DockingActionIf> getAllActions() {
return actionMgr.getAllActions();
}
@Override
public List<DockingActionIf> getDockingActionsByOwnerName(String owner) {
List<DockingActionIf> actions = actionMgr.getActions(owner);
public Set<DockingActionIf> getAllActions() {
Set<DockingActionIf> actions = actionMgr.getAllActions();
DockingActionManager am = winMgr.getActionManager();
actions.addAll(am.getAllActions());
return actions;
}
@Override
public List<DockingActionIf> getDockingActionsByFullActionName(String fullActionName) {
Set<DockingActionIf> set = new HashSet<>();
set.addAll(actionMgr.getDockingActionsByFullActionName(fullActionName));
set.addAll(winMgr.getActions(fullActionName));
return new ArrayList<>(set);
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner) {
return actionMgr.getActions(owner);
}
@Override

View file

@ -28,6 +28,8 @@ import org.jdesktop.animation.timing.TimingTargetAdapter;
import docking.action.ActionContextProvider;
import docking.action.DockingActionIf;
import docking.actions.ActionAdapter;
import docking.actions.KeyBindingUtils;
import docking.event.mouse.GMouseListenerAdapter;
import docking.menu.DockingToolbarButton;
import docking.util.*;

View file

@ -44,11 +44,6 @@ public class DockingActionManager {
private PopupActionManager popupActionManager;
private DockingAction keyBindingsAction;
/**
* Constructs a new ActionManager
* @param frame the frame to contain the menu and toolbar.
* @param enableDiagnosticActions if true additional diagnostic actions will enabled
*/
DockingActionManager(DockingWindowManager winMgr) {
menuGroupMap = new MenuGroupMap();
@ -90,9 +85,10 @@ public class DockingActionManager {
/**
* Register a specific Help content location for a component.
* The DocWinListener will be notified with the help location if the specified
* component 'c' has focus and the help key is pressed.
* component 'c' has focus and the help key is pressed.
*
* @param c component
* @param helpURL help content URL
* @param helpLocation the help location
*/
static void setHelpLocation(JComponent c, HelpLocation helpLocation) {
DockingWindowManager.getHelpService().registerHelp(c, helpLocation);
@ -149,41 +145,18 @@ public class DockingActionManager {
globalActions.remove(action);
}
public List<DockingActionIf> getAllDockingActionsByFullActionName(String fullActionName) {
public Set<DockingActionIf> getAllActions() {
// Note: this method is called by non-Swing test code. Synchronize access to the
// data structures in this class in order to prevent concurrent mod exceptions.
List<DockingActionIf> actions = new ArrayList<>();
Set<DockingActionIf> actions = new HashSet<>();
SystemUtilities.runSwingNow(() -> {
actions.addAll(getGlobalDockingActionsByFullActionName(fullActionName));
actions.addAll(getLocalDockingActionsByFullActionName(fullActionName));
actions.addAll(globalActions);
actions.addAll(keyBindingsManager.getLocalActions());
});
return actions;
}
private List<DockingActionIf> getGlobalDockingActionsByFullActionName(String fullActionName) {
List<DockingActionIf> matchingActions = new ArrayList<>();
ArrayList<DockingActionIf> existingAction = new ArrayList<>(globalActions);
for (DockingActionIf action : existingAction) {
if (fullActionName.equals(action.getFullName())) {
matchingActions.add(action);
}
}
return matchingActions;
}
private List<DockingActionIf> getLocalDockingActionsByFullActionName(String fullActionName) {
List<DockingActionIf> matchingActions = new ArrayList<>();
ArrayList<DockingActionIf> existingAction =
new ArrayList<>(keyBindingsManager.getLocalActions());
for (DockingActionIf action : existingAction) {
if (fullActionName.equals(action.getFullName())) {
matchingActions.add(action);
}
}
return matchingActions;
}
public Action getDockingKeyAction(KeyStroke keyStroke) {
return keyBindingsManager.getDockingKeyAction(keyStroke);
}

View file

@ -16,13 +16,17 @@
package docking;
import java.awt.Window;
import java.util.List;
import java.util.Set;
import javax.swing.ImageIcon;
import docking.action.DockingActionIf;
import ghidra.framework.options.ToolOptions;
/**
* Generic tool interface for managing {@link ComponentProvider}s and
* {@link DockingActionIf actions}
*/
public interface DockingTool {
/**
@ -125,25 +129,27 @@ public interface DockingTool {
public void removeLocalAction(ComponentProvider componentProvider, DockingActionIf action);
/**
* Return a list of all actions in the tool.
* @return list of all actions
* Return a set of all actions in the tool.
*
* <p>Note: the result may contain conceptually duplicate actions, which is when multiple
* actions exist that share the same full name (the full name is the action name with the
* owner name, such as "My Action (MyPlugin)".
*
* @return set of all actions
*/
public List<DockingActionIf> getAllActions();
public Set<DockingActionIf> getAllActions();
/**
* Returns all actions for the given owner
*
* <p>Note: the result may contain conceptually duplicate actions, which is when multiple
* actions exist that share the same full name (the full name is the action name with the
* owner name, such as "My Action (MyPlugin)".
*
* @param owner the action owner's name
* @return the actions
*/
public List<DockingActionIf> getDockingActionsByOwnerName(String owner);
/**
* Return an list of actions with the given full name
* @param fullActionName action name that includes the owner's name in
* parentheses, e.g. "MyAction (MyPlugin)"
* @return the actions
*/
public List<DockingActionIf> getDockingActionsByFullActionName(String fullActionName);
public Set<DockingActionIf> getDockingActionsByOwnerName(String owner);
/**
* Shows or hides the component provider in the tool

View file

@ -666,22 +666,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
scheduleUpdate();
}
/**
* Adds an action that will be associated with the given provider. These actions will
* appear in the local header for the component as a toolbar button or a drop-down menu
* item if it has an icon and menu path respectively.
* @param provider the provider whose header on which the action is to be placed.
* @param action the action to add to the providers header bar.
*/
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
if (placeholder == null) {
throw new IllegalArgumentException("Unknown component provider: " + provider);
}
placeholder.addAction(action);
actionManager.addLocalAction(action, provider);
}
/**
* Removes the action from the given provider's header bar.
* @param provider the provider whose header bar from which the action should be removed.
@ -695,13 +679,33 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
}
}
//==================================================================================================
// Package-level Action Methods
//==================================================================================================
/**
* Adds an action that will be associated with the given provider. These actions will
* appear in the local header for the component as a toolbar button or a drop-down menu
* item if it has an icon and menu path respectively.
* @param provider the provider whose header on which the action is to be placed.
* @param action the action to add to the providers header bar.
*/
void addLocalAction(ComponentProvider provider, DockingActionIf action) {
ComponentPlaceholder placeholder = getActivePlaceholder(provider);
if (placeholder == null) {
throw new IllegalArgumentException("Unknown component provider: " + provider);
}
placeholder.addAction(action);
actionManager.addLocalAction(action, provider);
}
/**
* Adds an action to the global menu or toolbar which appear in the main frame. If
* the action has a menu path, it will be in the menu. If it has an icon, it will
* appear in the toolbar.
* @param action the action to be added.
*/
public void addToolAction(DockingActionIf action) {
void addToolAction(DockingActionIf action) {
actionManager.addToolAction(action);
scheduleUpdate();
}
@ -710,15 +714,23 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* Removes the given action from the global menu and toolbar.
* @param action the action to be removed.
*/
public void removeToolAction(DockingActionIf action) {
void removeToolAction(DockingActionIf action) {
actionManager.removeToolAction(action);
scheduleUpdate();
}
public Collection<DockingActionIf> getActions(String fullActionName) {
return actionManager.getAllDockingActionsByFullActionName(fullActionName);
/**
* Returns all actions registered with this manager
* @return the actions
*/
public Set<DockingActionIf> getAllActions() {
return actionManager.getAllActions();
}
//==================================================================================================
// End Package-level Methods
//==================================================================================================
/**
* Hides or shows the component associated with the given provider.
* <p><br>

View file

@ -0,0 +1,72 @@
/* ###
* 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;
import java.util.Set;
import docking.action.DockingActionIf;
/**
* A class that exists primarily to provide access to action-related package-level methods of the
* {@link DockingWindowManager}. This allows the manager's interface to hide methods that
* don't make sense for public consumption.
*/
public class DockingWindowManagerActionUpdater {
private DockingWindowManager windowManager;
public DockingWindowManagerActionUpdater(DockingWindowManager windowManager) {
this.windowManager = windowManager;
}
/**
* Adds an action to the global menu or toolbar which appear in the main frame. If the action
* has a menu path, it will be in the menu. If it has an icon, it will appear in the toolbar.
* @param action the action to be added
*/
public void addToolAction(DockingActionIf action) {
windowManager.addToolAction(action);
}
/**
* Removes the given action from the global menu and toolbar
* @param action the action to be removed
*/
public void removeToolAction(DockingActionIf action) {
windowManager.removeToolAction(action);
}
/**
* Returns all actions registered with this manager
* @return the actions
*/
public Set<DockingActionIf> getAllActions() {
return windowManager.getAllActions();
}
/**
* Adds an action that will be associated with the given provider. These actions will
* appear in the local header for the component as a toolbar button or a drop-down menu
* item if it has an icon and menu path respectively.
*
* @param provider the provider whose header on which the action is to be placed
* @param action the action to add to the providers header bar
*/
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
windowManager.addLocalAction(provider, action);
}
}

View file

@ -25,7 +25,7 @@ import java.awt.event.KeyListener;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import docking.util.KeyBindingUtils;
import docking.actions.KeyBindingUtils;
import ghidra.util.bean.GGlassPane;
import ghidra.util.exception.AssertException;

View file

@ -16,9 +16,10 @@
package docking.action;
import java.awt.Component;
import java.util.List;
import java.util.Set;
import docking.*;
import docking.actions.KeyBindingUtils;
import ghidra.util.Msg;
import ghidra.util.ReservedKeyBindings;
@ -51,20 +52,20 @@ public class KeyBindingAction extends DockingAction {
if (!action.isKeyBindingManaged()) {
Component parent = windowManager.getActiveComponent();
Msg.showInfo(getClass(), parent, "Unable to Set Keybinding", "Action \"" +
getActionName(action) + "\" is not keybinding managed and thus a " +
"keybinding cannot be set.");
Msg.showInfo(getClass(), parent, "Unable to Set Keybinding",
"Action \"" + getActionName(action) + "\" is not keybinding managed and thus a " +
"keybinding cannot be set.");
return;
}
KeyEntryDialog d = new KeyEntryDialog(action, dockingActionManager);
windowManager.showDialog(d);
DockingWindowManager.showDialog(d);
}
/**
* Checks to see if the given action is key binding-managed by another action at the
* tool-level and returns that tool-level action if found.
* @param dockableAction The action for which to check for tool-level actions
* @param dockingAction The action for which to check for tool-level actions
* @return A tool-level action if one is found; otherwise, the original action
*/
private DockingActionIf maybeGetToolLevelAction(DockingActionIf dockingAction) {
@ -72,15 +73,13 @@ public class KeyBindingAction extends DockingAction {
return dockingAction;
}
// It is not key binding managed, which means that it may have tool-level representation
// that allows for key binding editing (think DummyKeyBindingsOptionsAction).
// Bad form (code duplication)--code from DockingAction.getFullName()
String actionToolName = dockingAction.getName() + " (Tool)";
List<DockingActionIf> actions =
dockingActionManager.getAllDockingActionsByFullActionName(actionToolName);
for (DockingActionIf action : actions) {
return action;
// It is not key binding managed, which means that it may be a shared key binding
String actionName = dockingAction.getName();
Set<DockingActionIf> allActions = dockingActionManager.getAllActions();
DockingActionIf sharedAction =
KeyBindingUtils.getSharedKeyBindingAction(allActions, actionName);
if (sharedAction != null) {
return sharedAction;
}
return dockingAction;

View file

@ -23,6 +23,7 @@ import javax.swing.*;
import javax.swing.text.*;
import docking.*;
import docking.actions.KeyBindingUtils;
import docking.widgets.label.GIconLabel;
import ghidra.util.HelpLocation;
import ghidra.util.ReservedKeyBindings;
@ -92,12 +93,9 @@ public class KeyEntryDialog extends DialogComponentProvider {
labelPanel.add(pane);
labelPanel.add(Box.createHorizontalStrut(5));
keyEntryField = new KeyEntryTextField(20, new KeyEntryListener() {
@Override
public void processEntry(KeyStroke keyStroke) {
okButton.setEnabled(true);
updateCollisionPane(keyStroke);
}
keyEntryField = new KeyEntryTextField(20, keyStroke -> {
okButton.setEnabled(true);
updateCollisionPane(keyStroke);
});
defaultPanel.add(labelPanel, BorderLayout.NORTH);
@ -146,8 +144,9 @@ public class KeyEntryDialog extends DialogComponentProvider {
clearStatusText();
List<DockingActionIf> actions =
actionManager.getAllDockingActionsByFullActionName(action.getFullName());
Set<DockingActionIf> allActions = actionManager.getAllActions();
Set<DockingActionIf> actions =
KeyBindingUtils.getActions(allActions, action.getOwner(), action.getName());
for (DockingActionIf element : actions) {
if (element.isKeyBindingManaged()) {
element.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
@ -160,7 +159,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
private void setUpAttributes() {
textAttrSet = new SimpleAttributeSet();
textAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
textAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
textAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
textAttrSet.addAttribute(StyleConstants.Foreground, Color.BLUE);
tabAttrSet = new SimpleAttributeSet();
@ -207,7 +206,7 @@ public class KeyEntryDialog extends DialogComponentProvider {
Map<String, DockingActionIf> nameMap = new HashMap<>(list.size());
// the list may have multiple matches for a single owner, which we do not want (see
// DummyKeyBindingsOptionsAction)
// SharedStubKeyBindingAction)
for (DockingActionIf dockableAction : list) {
if (shouldAddAction(dockableAction)) {
// this overwrites same named actions
@ -218,9 +217,6 @@ public class KeyEntryDialog extends DialogComponentProvider {
return new ArrayList<>(nameMap.values());
}
/**
* Get the multiple key action for the given keystroke.
*/
private MultipleKeyAction getMultipleKeyAction(KeyStroke ks) {
Action keyAction = actionManager.getDockingKeyAction(ks);
if (keyAction instanceof MultipleKeyAction) {
@ -230,6 +226,11 @@ public class KeyEntryDialog extends DialogComponentProvider {
}
private boolean shouldAddAction(DockingActionIf dockableAction) {
return dockableAction.isKeyBindingManaged();
if (dockableAction.isKeyBindingManaged()) {
return true;
}
// shared key bindings are handled specially
return !dockableAction.usesSharedKeyBinding();
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.util;
package docking.actions;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;

View file

@ -18,9 +18,12 @@ package docking.actions;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.function.Predicate;
import javax.swing.KeyStroke;
import org.apache.commons.collections4.map.LazyMap;
import docking.*;
import docking.action.*;
import docking.tool.util.DockingToolConstants;
@ -33,13 +36,16 @@ import ghidra.util.exception.AssertException;
public class DockingToolActionManager implements PropertyChangeListener {
private DockingWindowManager winMgr;
private Map<String, List<DockingActionIf>> actionMap = new HashMap<>();
private DockingWindowManagerActionUpdater winMgrActionUpdater;
private Map<String, List<DockingActionIf>> actionMap =
LazyMap.lazyMap(new HashMap<>(), () -> new ArrayList<>());
private Map<String, SharedStubKeyBindingAction> sharedActionMap = new HashMap<>();
private ToolOptions keyBindingOptions;
private DockingTool dockingTool;
/**
* Construct an ActionManager.
* Construct an ActionManager
*
* @param tool tool using this ActionManager
* @param windowManager manager of the "Docking" arrangement of a set of components
@ -48,6 +54,7 @@ public class DockingToolActionManager implements PropertyChangeListener {
public DockingToolActionManager(DockingTool tool, DockingWindowManager windowManager) {
this.dockingTool = tool;
this.winMgr = windowManager;
this.winMgrActionUpdater = new DockingWindowManagerActionUpdater(winMgr);
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
}
@ -58,26 +65,32 @@ public class DockingToolActionManager implements PropertyChangeListener {
private void addActionToMap(DockingActionIf action) {
String name = action.getFullName();
List<DockingActionIf> actionList = actionMap.get(name);
if (actionList == null) {
List<DockingActionIf> newList = new ArrayList<>();
newList.add(action);
actionMap.put(name, newList);
}
else {
actionList.add(action);
List<DockingActionIf> list = actionMap.get(name);
if (!list.isEmpty()) {
KeyBindingUtils.assertSameDefaultKeyBindings(action, actionList);
}
actionList.add(action);
}
private void removeActionFromMap(DockingActionIf action) {
String name = action.getFullName();
List<DockingActionIf> actionList = actionMap.get(name);
if (actionList == null) {
return;
}
actionMap.get(name).remove(action);
}
if (actionList.remove(action) && actionList.isEmpty()) {
actionMap.remove(name);
}
/**
* Add an action that works specifically with a component provider.
* @param provider provider associated with the action
* @param action local action to the provider
*/
public synchronized void addLocalAction(ComponentProvider provider, DockingActionIf action) {
checkForAlreadyAddedAction(provider, action);
action.addPropertyChangeListener(this);
addActionToMap(action);
setKeyBindingOption(action);
winMgrActionUpdater.addLocalAction(provider, action);
}
/**
@ -88,7 +101,7 @@ public class DockingToolActionManager implements PropertyChangeListener {
action.addPropertyChangeListener(this);
addActionToMap(action);
setKeyBindingOption(action);
winMgr.addToolAction(action);
winMgrActionUpdater.addToolAction(action);
}
private void setKeyBindingOption(DockingActionIf action) {
@ -135,7 +148,7 @@ public class DockingToolActionManager implements PropertyChangeListener {
public synchronized void removeToolAction(DockingActionIf action) {
action.removePropertyChangeListener(this);
removeActionFromMap(action);
winMgr.removeToolAction(action);
winMgrActionUpdater.removeToolAction(action);
}
/**
@ -143,32 +156,16 @@ public class DockingToolActionManager implements PropertyChangeListener {
* @param owner owner of the actions to remove
*/
public synchronized void removeToolActions(String owner) {
List<DockingActionIf> actions = getActions(owner);
Predicate<String> ownerMatches = actionOwner -> actionOwner.equals(owner);
Set<DockingActionIf> actions = getActions(ownerMatches);
for (DockingActionIf action : actions) {
removeToolAction(action);
}
}
/**
* Add an action that works specifically with a component provider.
* @param provider provider associated with the action
* @param action local action to the provider
*/
public synchronized void addLocalAction(ComponentProvider provider, DockingActionIf action) {
checkForAlreadyAddedAction(provider, action);
action.addPropertyChangeListener(this);
addActionToMap(action);
setKeyBindingOption(action);
winMgr.addLocalAction(provider, action);
}
private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) {
String name = action.getFullName();
List<DockingActionIf> actionList = actionMap.get(name);
if (actionList == null) {
return;
}
if (actionList.contains(action)) {
throw new AssertException("Cannot add the same action more than once. Provider " +
provider.getName() + " - action: " + name);
@ -193,46 +190,38 @@ public class DockingToolActionManager implements PropertyChangeListener {
* @param fullName full name for the action, e.g., "My Action (My Plugin)"
* @return list of actions; empty if no action exists with the given name
*/
public List<DockingActionIf> getDockingActionsByFullActionName(String fullName) {
public Set<DockingActionIf> getDockingActionsByFullActionName(String fullName) {
List<DockingActionIf> list = actionMap.get(fullName);
if (list == null) {
list = new ArrayList<>();
}
return new ArrayList<>(list);
return new HashSet<>(list);
}
/**
* Returns a list of actions whose owner matches the given owner or all actions if the given
* owner is null.
* <p>
* This method will only return a single instance of any named action, even if multiple
* actions have been registered with the same name.
* <p>
* Returns a list of actions whose owner matches the given predicate.
*
* Note: Actions with the same name are assumed to be different instances of the same action.
*
* @param owner The of the action, or null to get all actions
* @param ownerFilter the predicate that is used to test if the owners are the same; to get
* all actions, return an 'always true' predicate
* @return a list of deduped actions.
*/
private List<DockingActionIf> getUniqueActionList(String owner) {
private Set<DockingActionIf> getActions(Predicate<String> ownerFilter) {
List<DockingActionIf> matchingActionList = new ArrayList<>();
for (List<DockingActionIf> actionList : actionMap.values()) {
// we only want *one* instance of duplicate actions
DockingActionIf action = actionList.get(0);
if (owner == null || action.getOwner().equals(owner)) {
matchingActionList.add(action);
Set<DockingActionIf> result = new HashSet<>();
for (List<DockingActionIf> list : actionMap.values()) {
for (DockingActionIf action : list) {
if (ownerFilter.test(action.getOwner())) {
result.addAll(list);
}
}
}
// these are the 'shared' actions that are needed in order to appear in the options UI
for (DockingActionIf action : sharedActionMap.values()) {
if (owner == null || action.getOwner().equals(owner)) {
matchingActionList.add(action);
if (ownerFilter.test(action.getOwner())) {
result.add(action);
}
}
return matchingActionList;
return result;
}
/**
@ -241,17 +230,18 @@ public class DockingToolActionManager implements PropertyChangeListener {
* @return array of actions; zero length array is returned if no
* action exists with the given name
*/
public synchronized List<DockingActionIf> getActions(String owner) {
List<DockingActionIf> list = getUniqueActionList(owner);
return list;
public synchronized Set<DockingActionIf> getActions(String owner) {
Predicate<String> ownerMatches = actionOwner -> actionOwner.equals(owner);
return getActions(ownerMatches);
}
/**
* Get a list of all actions in the tool.
* Get a list of all actions in the tool
* @return list of PluginAction objects
*/
public List<DockingActionIf> getAllActions() {
return getUniqueActionList(null);
public synchronized Set<DockingActionIf> getAllActions() {
Predicate<String> allOwnersMatch = name -> true;
return getActions(allOwnersMatch);
}
/**
@ -262,7 +252,7 @@ public class DockingToolActionManager implements PropertyChangeListener {
*/
public synchronized void restoreKeyBindings() {
keyBindingOptions = dockingTool.getOptions(DockingToolConstants.KEY_BINDINGS);
List<DockingActionIf> actions = getAllActions();
Set<DockingActionIf> actions = getAllActions();
for (DockingActionIf action : actions) {
if (!action.isKeyBindingManaged()) {
continue;
@ -284,10 +274,7 @@ public class DockingToolActionManager implements PropertyChangeListener {
while (iterator.hasNext()) {
DockingActionIf action = iterator.next();
String name = action.getFullName();
List<DockingActionIf> actionList = actionMap.get(name);
if (actionList != null && actionList.remove(action) && actionList.isEmpty()) {
actionMap.remove(name);
}
actionMap.get(name).remove(action);
}
}

View file

@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.util;
package docking.actions;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.io.*;
import java.util.*;
import javax.swing.*;
@ -27,8 +28,10 @@ import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import docking.action.ActionContextProvider;
import docking.action.DockingAction;
import com.google.common.collect.Sets;
import docking.DockingTool;
import docking.action.*;
import docking.widgets.filechooser.GhidraFileChooser;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.preferences.Preferences;
@ -37,6 +40,8 @@ import ghidra.util.filechooser.GhidraFileChooserModel;
import ghidra.util.filechooser.GhidraFileFilter;
import ghidra.util.xml.GenericXMLOutputter;
import ghidra.util.xml.XmlUtilities;
import util.CollectionUtils;
import utilities.util.reflection.ReflectionUtilities;
/**
* A class to provide utilities for system key bindings, such as importing and
@ -81,6 +86,7 @@ public class KeyBindingUtils {
* If there is a problem reading the data then the user will be shown an
* error dialog.
*
* @param inputStream the input stream from which to read options
* @return An options object that is composed of key binding names and their
* associated keystrokes.
*/
@ -232,9 +238,8 @@ public class KeyBindingUtils {
* @param keyStroke the keystroke for to which the action will be bound
* @param action the action to execute when the given keystroke is triggered
* @param focusCondition the focus condition under which to bind the action
* ({@link JComponent#getInputMap(int)})
* @param focusCondition see {@link JComponent} for more info; the default
* is usually {@link JComponent#WHEN_FOCUSED}
* ({@link JComponent#getInputMap(int)}). See {@link JComponent} for more info;
* the default is usually {@link JComponent#WHEN_FOCUSED}
*/
public static void registerAction(JComponent component, KeyStroke keyStroke, Action action,
int focusCondition) {
@ -324,6 +329,116 @@ public class KeyBindingUtils {
return (binding == null) ? null : actionMap.get(binding);
}
/**
* A utility method to get all key binding actions. This method will remove duplicate
* actions and will only return actions that are {@link DockingActionIf#isKeyBindingManaged()}
*
* @param tool the tool containing the actions
* @return the actions mapped by their full name (e.g., 'Name (OwnerName)')
*/
public static Map<String, DockingActionIf> getAllKeyBindingActions(DockingTool tool) {
Map<String, DockingActionIf> deduper = new HashMap<>();
Set<DockingActionIf> actions = tool.getAllActions();
for (DockingActionIf action : actions) {
if (isIgnored(action)) {
// don't bother tracking non-keybinding actions; this would be a mistake due
// to the potential for a shared key binding action overwriting its
// SharedStubKeyBindingAction
continue;
}
deduper.put(action.getFullName(), action);
}
return deduper;
}
/**
* A utility method to get all key binding actions that have the given owner.
* This method will remove duplicate actions and will only return actions
* that are {@link DockingActionIf#isKeyBindingManaged()}
*
* @param tool the tool containing the actions
* @param owner the action owner name
* @return the actions
*/
public static Set<DockingActionIf> getKeyBindingActions(DockingTool tool, String owner) {
Map<String, DockingActionIf> deduper = new HashMap<>();
Set<DockingActionIf> actions = tool.getDockingActionsByOwnerName(owner);
for (DockingActionIf action : actions) {
if (isIgnored(action)) {
// don't bother tracking non-keybinding actions; this would be a mistake due
// to the potential for a shared key binding action overwriting its
// SharedStubKeyBindingAction
continue;
}
deduper.put(action.getFullName(), action);
}
return CollectionUtils.asSet(deduper.values());
}
/**
* Returns all actions that match the given owner and name
*
* @param tool the tool containing the actions
* @param owner the owner
* @param name the name
* @return the actions
*/
public static Set<DockingActionIf> getActions(DockingTool tool, String owner, String name) {
Set<DockingActionIf> actions = tool.getDockingActionsByOwnerName(owner);
return getActions(actions, owner, name);
}
/**
* Returns all actions that match the given owner and name
*
* @param allActions the universe of actions
* @param owner the owner
* @param name the name
* @return the actions
*/
public static Set<DockingActionIf> getActions(Set<DockingActionIf> allActions, String owner,
String name) {
Set<DockingActionIf> ownerMatch =
Sets.filter(allActions, action -> action.getOwner().equals(owner));
return Sets.filter(ownerMatch, action -> action.getName().equals(name));
}
/**
* A method to locate the {@link SharedStubKeyBindingAction} representative for the given
* action name. This method is not useful to general clients.
*
* @param allActions all actions in the system
* @param sharedName the name of the shared action
* @return the shared action representative
*/
public static DockingActionIf getSharedKeyBindingAction(Set<DockingActionIf> allActions,
String sharedName) {
Set<DockingActionIf> toolActions = getActions(allActions, "Tool", sharedName);
//@formatter:off
return toolActions
.stream()
.filter(action -> action instanceof SharedStubKeyBindingAction)
.findAny()
.orElse(null)
;
//@formatter:on
}
private static boolean isIgnored(DockingActionIf action) {
// not keybinding managed; a shared keybinding implies that this action should not be in
// the UI, as there will be a single proxy in place of all actions sharing that binding
return !action.isKeyBindingManaged() || action.usesSharedKeyBinding();
}
/**
* Takes the existing docking action and allows it to be registered with
* Swing components
@ -341,6 +456,46 @@ public class KeyBindingUtils {
return new ActionAdapter(action);
}
public static void assertSameDefaultKeyBindings(DockingActionIf newAction,
List<DockingActionIf> existingActions) {
KeyBindingData newDefaultBinding = newAction.getDefaultKeyBindingData();
KeyStroke defaultKs = getKeyStroke(newDefaultBinding);
for (DockingActionIf action : existingActions) {
KeyBindingData existingDefaultBinding = action.getDefaultKeyBindingData();
KeyStroke existingKs = getKeyStroke(existingDefaultBinding);
if (!Objects.equals(defaultKs, existingKs)) {
logDifferentKeyBindingsWarnigMessage(newAction, action, existingKs);
break; // one warning seems like enough
}
}
}
public static void logDifferentKeyBindingsWarnigMessage(DockingActionIf newAction,
DockingActionIf existingAction, KeyStroke existingDefaultKs) {
//@formatter:off
String s = "Shared Key Binding Actions have different default values. These " +
"must be the same." +
"\n\tAction name: '"+existingAction.getName()+"'" +
"\n\tAction 1: " + existingAction.getInceptionInformation() +
"\n\t\tKey Binding: " + existingDefaultKs +
"\n\tAction 2: " + newAction.getInceptionInformation() +
"\n\t\tKey Binding: " + newAction.getKeyBinding() +
"\nUsing the " +
"first value set - " + existingDefaultKs;
//@formatter:on
Msg.warn(KeyBindingUtils.class, s, ReflectionUtilities.createJavaFilteredThrowable());
}
private static KeyStroke getKeyStroke(KeyBindingData data) {
if (data == null) {
return null;
}
return data.getKeyBinding();
}
//==================================================================================================
// Private Methods
//==================================================================================================

View file

@ -25,8 +25,6 @@ import docking.DockingWindowManager;
import docking.action.*;
import ghidra.framework.options.OptionsChangeListener;
import ghidra.framework.options.ToolOptions;
import ghidra.util.Msg;
import utilities.util.reflection.ReflectionUtilities;
/**
* A stub action that allows key bindings to be edited through the key bindings options. This
@ -95,7 +93,8 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
continue;
}
logDifferentKeyBindingsWarnigMessage(newAction, existingAction, existingDefaultKs);
KeyBindingUtils.logDifferentKeyBindingsWarnigMessage(newAction, existingAction,
existingDefaultKs);
//
// Not sure which keystroke to prefer here--keep the first one that was set
@ -111,24 +110,6 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
return newDefaultKs;
}
private void logDifferentKeyBindingsWarnigMessage(DockingActionIf newAction,
DockingActionIf existingAction, KeyStroke existingDefaultKs) {
//@formatter:off
String s = "Shared Key Binding Actions have different deafult values. These " +
"must be the same." +
"\n\tAction name: '"+existingAction.getName()+"'" +
"\n\tAction 1: " + existingAction.getInceptionInformation() +
"\n\t\tKey Binding: " + existingDefaultKs +
"\n\tAction 2: " + newAction.getInceptionInformation() +
"\n\t\tKey Binding: " + newAction.getKeyBinding() +
"\nUsing the " +
"first value set - " + existingDefaultKs;
//@formatter:on
Msg.warn(this, s, ReflectionUtilities.createJavaFilteredThrowable());
}
private void updateActionKeyStrokeFromOptions(DockingActionIf action, KeyStroke defaultKs) {
KeyStroke optionsKs = getKeyStrokeFromOptions(defaultKs);

View file

@ -32,7 +32,7 @@ import javax.swing.text.Document;
import docking.DockingUtils;
import docking.DockingWindowManager;
import docking.util.KeyBindingUtils;
import docking.actions.KeyBindingUtils;
import docking.widgets.*;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;

View file

@ -38,6 +38,8 @@ import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.*;
import com.google.common.collect.Sets;
import docking.*;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingActionIf;
@ -1098,23 +1100,48 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
* @param name the name to match
* @return the matching actions; empty list if no matches
*/
public static Set<DockingActionIf> getActions(DockingTool tool, String name) {
public static Set<DockingActionIf> getActionsByName(DockingTool tool, String name) {
Set<DockingActionIf> actions = new HashSet<>();
Set<DockingActionIf> result = new HashSet<>();
List<DockingActionIf> toolActions = tool.getAllActions();
Set<DockingActionIf> toolActions = tool.getAllActions();
for (DockingActionIf action : toolActions) {
if (action.getName().equals(name)) {
actions.add(action);
result.add(action);
}
}
return actions;
return result;
}
/**
* A helper method to find all actions with the given owner's name
*
* @param tool the tool containing all system actions
* @param name the owner's name to match
* @return the matching actions; empty list if no matches
*/
public static Set<DockingActionIf> getActionsByOwner(DockingTool tool, String name) {
return tool.getDockingActionsByOwnerName(name);
}
/**
* A helper method to find all actions by name, with the given owner's name
*
* @param tool the tool containing all system actions
* @param owner the owner's name
* @param name the owner's name to match
* @return the matching actions; empty list if no matches
*/
public static Set<DockingActionIf> getActionsByOwnerAndName(DockingTool tool, String owner,
String name) {
Set<DockingActionIf> ownerActions = tool.getDockingActionsByOwnerName(owner);
return Sets.filter(ownerActions, action -> action.getName().equals(name));
}
/**
* Finds the singular tool action by the given name. If more than one action exists with
* that name, then an exception is thrown. If you want more than one matching action,
* the call {@link #getActions(DockingTool, String)} instead.
* the call {@link #getActionsByName(DockingTool, String)} instead.
*
* <P>Note: more specific test case subclasses provide other methods for finding actions
* when you have an owner name (which is usually the plugin name).
@ -1125,7 +1152,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
*/
public static DockingActionIf getAction(DockingTool tool, String name) {
Set<DockingActionIf> actions = getActions(tool, name);
Set<DockingActionIf> actions = getActionsByName(tool, name);
if (actions.isEmpty()) {
return null;
}
@ -1140,7 +1167,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
/**
* Finds the action by the given owner name and action name.
* If you do not know the owner name, then use
* the call {@link #getActions(DockingTool, String)} instead.
* the call {@link #getActionsByName(DockingTool, String)} instead.
*
* <P>Note: more specific test case subclasses provide other methods for finding actions
* when you have an owner name (which is usually the plugin name).
@ -1151,8 +1178,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
* @return the matching action; null if no matching action can be found
*/
public static DockingActionIf getAction(DockingTool tool, String owner, String name) {
String fullName = name + " (" + owner + ")";
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(fullName);
Set<DockingActionIf> actions = getActionsByOwnerAndName(tool, owner, name);
if (actions.isEmpty()) {
return null;
}
@ -1160,7 +1186,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
if (actions.size() > 1) {
// This shouldn't happen
throw new AssertionFailedError(
"Found more than one action for name '" + fullName + "'");
"Found more than one action for name '" + name + " (" + owner + ")'");
}
return CollectionUtils.any(actions);

View file

@ -442,7 +442,9 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
public int compare(T t1, T t2) {
// at this point we compare the rows, since all of the sorting column values are equal
if (t1 instanceof Comparable) {
// (Warning: there is a chance that the two objects are comparable, but not on each
// other. In this case, a ClassCastException will be thrown)
if (t1 instanceof Comparable && t2 instanceof Comparable) {
return ((Comparable) t1).compareTo(t2);
}

View file

@ -31,7 +31,7 @@ import javax.swing.table.*;
import docking.*;
import docking.action.*;
import docking.util.KeyBindingUtils;
import docking.actions.KeyBindingUtils;
import docking.widgets.OptionDialog;
import docking.widgets.dialogs.SettingsDialog;
import docking.widgets.filechooser.GhidraFileChooser;

View file

@ -36,7 +36,7 @@ import ghidra.framework.options.ToolOptions;
import ghidra.util.Msg;
import ghidra.util.SpyErrorLogger;
public class SharedKeybindingDockingActionTest extends AbstractDockingTest {
public class SharedKeyBindingDockingActionTest extends AbstractDockingTest {
private static final String SHARED_NAME = "Shared Action Name";
private static final String SHARED_OWNER = SharedStubKeyBindingAction.SHARED_OWNER;
@ -158,8 +158,7 @@ public class SharedKeybindingDockingActionTest extends AbstractDockingTest {
tool.removeAction(action2);
assertActionNotInTool(action2);
String sharedName = action1.getFullName();
assertNoSharedKeyBindingStubInstalled(sharedName);
assertNoSharedKeyBindingStubInstalled(action1);
}
@Test
@ -271,8 +270,7 @@ public class SharedKeybindingDockingActionTest extends AbstractDockingTest {
tool.removeLocalAction(provider, action2);
assertActionNotInTool(action2);
String sharedName = action1.getFullName();
assertNoSharedKeyBindingStubInstalled(sharedName);
assertNoSharedKeyBindingStubInstalled(action1);
}
@Test
@ -293,7 +291,65 @@ public class SharedKeybindingDockingActionTest extends AbstractDockingTest {
assertActionNotInTool(action2);
String sharedName = action1.getFullName();
assertNoSharedKeyBindingStubInstalled(sharedName);
assertNoSharedKeyBindingStubInstalled(action1);
}
@Test
public void testNonSharedKeyBinding_SameActionAddedTwice() {
//
// We support adding the same action twice. (This can happen when a transient component
// provider is repeatedly shown, such as a search results provider.) Make sure we get
// a warning if the same action is added twice, but with different key bindings.
//
// Note: in this context, two actions are considered to be the same if they share the
// same name and owner.
//
TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1);
TestAction action1Copy = new TestAction(OWNER_1, DEFAULT_KS_1);
tool.addAction(action1);
tool.addAction(action1Copy);
assertActionInTool(action1);
assertActionInTool(action1Copy);
assertNoLoggedMessages();
tool.removeAction(action1);
assertActionNotInTool(action1);
assertActionInTool(action1Copy);
tool.removeAction(action1Copy);
assertActionNotInTool(action1Copy);
}
@Test
public void testNonSharedKeyBinding_DifferentActionsWithSameFullName() {
//
// We support adding the same action twice. (This can happen when a transient component
// provider is repeatedly shown, such as a search results provider.) Make sure we get
// a warning if the same action is added twice, but with different key bindings.
//
// Note: in this context, two actions are considered to be the same if they share the
// same name and owner.
//
TestAction action1 = new TestAction(OWNER_1, DEFAULT_KS_1);
TestAction action1Copy = new TestAction(OWNER_1, DEFAULT_KS_DIFFERENT_THAN_1);
tool.addAction(action1);
tool.addAction(action1Copy);
assertActionInTool(action1);
assertActionInTool(action1Copy);
assertImproperDefaultBindingMessage();
tool.removeAction(action1);
assertActionNotInTool(action1);
assertActionInTool(action1Copy);
tool.removeAction(action1Copy);
assertActionNotInTool(action1Copy);
}
//==================================================================================================
@ -317,7 +373,7 @@ public class SharedKeybindingDockingActionTest extends AbstractDockingTest {
private void assertActionInTool(TestAction action) {
Set<DockingActionIf> actions = getActions(tool, action.getName());
Set<DockingActionIf> actions = getActionsByName(tool, action.getName());
for (DockingActionIf toolAction : actions) {
if (toolAction == action) {
return;
@ -328,16 +384,19 @@ public class SharedKeybindingDockingActionTest extends AbstractDockingTest {
}
private void assertActionNotInTool(TestAction action) {
Set<DockingActionIf> actions = getActions(tool, action.getName());
Set<DockingActionIf> actions = getActionsByName(tool, action.getName());
for (DockingActionIf toolAction : actions) {
assertNotSame(toolAction, action);
}
}
private void assertNoSharedKeyBindingStubInstalled(String sharedName) {
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(sharedName);
assertTrue("There should be no actions registered for '" + sharedName + "'",
actions.isEmpty());
private void assertNoSharedKeyBindingStubInstalled(DockingActionIf action) {
String name = action.getName();
String owner = action.getOwner();
DockingActionIf sharedAction = getAction(tool, owner, name);
assertNull("There should be no actions registered for '" + name + " (" + owner + ")'",
sharedAction);
}
private void setSharedKeyBinding(KeyStroke newKs) {
@ -355,7 +414,7 @@ public class SharedKeybindingDockingActionTest extends AbstractDockingTest {
}
private void assertImproperDefaultBindingMessage() {
spyLogger.assertLogMessage("shared", "key", "binding", "action", "different", "default");
spyLogger.assertLogMessage("shared", "key", "binding", "actions", "different", "default");
}
private void assertKeyBinding(TestAction action, KeyStroke expectedKs) {

View file

@ -26,7 +26,7 @@ import org.junit.Test;
import generic.test.AbstractGenericTest;
public class DockingActionKeybindingTest extends AbstractGenericTest {
public class DockingActionKeyBindingTest extends AbstractGenericTest {
@Test
public void testKeybinding_Unmodified() {

View file

@ -846,23 +846,25 @@ public class StringUtilities {
}
/**
* Turn the given list into an attractive string, with the separator of you choosing.
* Turn the given data into an attractive string, with the separator of your choosing
*
* @param list the list from which a string will be generated
* @param collection the data from which a string will be generated
* @param separator the string used to separate elements
* @return a string representation of the given list
*/
public static String toString(List<?> list, String separator) {
if (list == null) {
public static String toString(Collection<?> collection, String separator) {
if (collection == null) {
return null;
}
int i = 0;
StringBuffer buffer = new StringBuffer("[ ");
for (int i = 0; i < list.size(); i++) {
buffer.append(list.get(i).toString());
if (i + 1 < list.size()) {
for (Object o : collection) {
buffer.append(o.toString());
if (i + 1 < collection.size()) {
buffer.append(separator);
}
i++;
}
buffer.append(" ]");

View file

@ -22,7 +22,7 @@ import javax.swing.*;
import javax.swing.tree.TreePath;
import docking.DockingUtils;
import docking.util.KeyBindingUtils;
import docking.actions.KeyBindingUtils;
import docking.widgets.tree.*;
import docking.widgets.tree.support.GTreeRenderer;
import ghidra.framework.main.FrontEndTool;

View file

@ -21,6 +21,7 @@ import javax.swing.Icon;
import javax.swing.event.ChangeListener;
import docking.action.DockingActionIf;
import docking.actions.KeyBindingUtils;
import ghidra.framework.plugintool.util.*;
import ghidra.util.Msg;
import resources.ResourceManager;
@ -212,12 +213,12 @@ public class PluginConfigurationModel {
* @param pluginDescription The description for which to find loaded actions.
* @return all of the actions loaded by the Plugin represented by the given PluginDescription.
*/
public List<DockingActionIf> getActionsForPlugin(PluginDescription pluginDescription) {
public Set<DockingActionIf> getActionsForPlugin(PluginDescription pluginDescription) {
if (!isLoaded(pluginDescription)) {
return Collections.emptyList();
return Collections.emptySet();
}
return tool.getDockingActionsByOwnerName(pluginDescription.getName());
return KeyBindingUtils.getKeyBindingActions(tool, pluginDescription.getName());
}
/**

View file

@ -20,6 +20,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
@ -30,8 +31,8 @@ import docking.DockingUtils;
import docking.KeyEntryTextField;
import docking.action.DockingActionIf;
import docking.action.KeyBindingData;
import docking.actions.KeyBindingUtils;
import docking.tool.util.DockingToolConstants;
import docking.util.KeyBindingUtils;
import docking.widgets.MultiLineLabel;
import docking.widgets.OptionDialog;
import docking.widgets.label.GIconLabel;
@ -39,8 +40,7 @@ import docking.widgets.table.*;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HTMLUtilities;
import ghidra.util.ReservedKeyBindings;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
import ghidra.util.layout.PairLayout;
import ghidra.util.layout.VerticalLayout;
@ -65,11 +65,13 @@ public class KeyBindingsPanel extends JPanel {
private KeyBindingsTableModel tableModel;
private ListSelectionModel selectionModel;
private Options options;
private Map<String, KeyStroke> actionMap; // map action name to keystroke
private Map<String, List<String>> keyMap; // map keystroke name to ArrayList of action names
private List<DockingActionIf> actionList;
private Map<String, KeyStroke> originalValues; // original mapping for action name to
// keystroke (to know what changed)
private Map<String, DockingActionIf> actionsByFullName;
private Map<String, List<String>> actionNamesByKeyStroke;
private Map<String, KeyStroke> keyStrokesByFullName;
private Map<String, KeyStroke> originalValues; // to know what has been changed
private List<DockingActionIf> tableActions;
private KeyEntryTextField ksField;
private boolean unappliedChanges;
@ -81,7 +83,7 @@ public class KeyBindingsPanel extends JPanel {
public KeyBindingsPanel(PluginTool tool, Options options) {
this.tool = tool;
this.options = options;
actionList = new ArrayList<>();
tableActions = new ArrayList<>();
create();
createActionMap();
addListeners();
@ -100,10 +102,10 @@ public class KeyBindingsPanel extends JPanel {
* Apply the changes to the actions.
*/
public void apply() {
Iterator<String> iter = actionMap.keySet().iterator();
Iterator<String> iter = keyStrokesByFullName.keySet().iterator();
while (iter.hasNext()) {
String actionName = iter.next();
KeyStroke currentKeyStroke = actionMap.get(actionName);
KeyStroke currentKeyStroke = keyStrokesByFullName.get(actionName);
KeyStroke originalKeyStroke = originalValues.get(actionName);
updateOptions(actionName, originalKeyStroke, currentKeyStroke);
}
@ -111,7 +113,7 @@ public class KeyBindingsPanel extends JPanel {
changesMade(false);
}
private boolean updateOptions(String actionName, KeyStroke currentKeyStroke,
private boolean updateOptions(String fullActionName, KeyStroke currentKeyStroke,
KeyStroke newKeyStroke) {
if ((currentKeyStroke != null && currentKeyStroke.equals(newKeyStroke)) ||
(currentKeyStroke == null && newKeyStroke == null)) {
@ -119,17 +121,17 @@ public class KeyBindingsPanel extends JPanel {
}
if (newKeyStroke != null) {
options.setKeyStroke(actionName, newKeyStroke);
options.setKeyStroke(fullActionName, newKeyStroke);
}
else {
options.removeOption(actionName);
options.removeOption(fullActionName);
}
originalValues.put(actionName, newKeyStroke);
actionMap.put(actionName, newKeyStroke);
originalValues.put(fullActionName, newKeyStroke);
keyStrokesByFullName.put(fullActionName, newKeyStroke);
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(actionName);
Set<DockingActionIf> actions = tool.getAllActions();
for (DockingActionIf action : actions) {
if (action.isKeyBindingManaged()) {
if (action.getFullName().equals(fullActionName)) {
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKeyStroke));
}
}
@ -145,46 +147,40 @@ public class KeyBindingsPanel extends JPanel {
while (iter.hasNext()) {
String actionName = iter.next();
KeyStroke originalKS = originalValues.get(actionName);
KeyStroke modifiedKS = actionMap.get(actionName);
KeyStroke modifiedKS = keyStrokesByFullName.get(actionName);
if (modifiedKS != null && !modifiedKS.equals(originalKS)) {
actionMap.put(actionName, originalKS);
keyStrokesByFullName.put(actionName, originalKS);
}
}
tableModel.fireTableDataChanged();
}
public void reload() {
// run this after the current pending events in the swing
// thread so that the screen will repaint itself
SwingUtilities.invokeLater(() -> {
// clear the current user key stroke so that it does not
// appear as though the user is editing while restoring
Swing.runLater(() -> {
// clear the current user key stroke so that it does not appear as though the
// user is editing while restoring
actionTable.clearSelection();
restoreDefaultKeybindings();
});
}
/**
* Create the maps for actions and names.
*/
private void createActionMap() {
actionMap = new HashMap<>();
keyMap = new HashMap<>();
keyStrokesByFullName = new HashMap<>();
actionNamesByKeyStroke = new HashMap<>();
originalValues = new HashMap<>();
String longestName = "";
List<DockingActionIf> actions = tool.getAllActions();
for (DockingActionIf action : actions) {
if (isIgnored(action)) {
continue;
}
actionsByFullName = KeyBindingUtils.getAllKeyBindingActions(tool);
Set<Entry<String, DockingActionIf>> entries = actionsByFullName.entrySet();
for (Entry<String, DockingActionIf> entry : entries) {
String actionName = action.getFullName();
actionList.add(action);
DockingActionIf action = entry.getValue();
tableActions.add(action);
String actionName = entry.getKey();
KeyStroke ks = options.getKeyStroke(actionName, null);
actionMap.put(actionName, ks);
keyStrokesByFullName.put(actionName, ks);
addToKeyMap(ks, actionName);
originalValues.put(actionName, ks);
@ -206,12 +202,6 @@ public class KeyBindingsPanel extends JPanel {
tableModel.fireTableDataChanged();
}
private boolean isIgnored(DockingActionIf action) {
// not keybinding managed; a shared keybinding implies that this action should not be in
// the UI, as there will be a single proxy in place of all actions sharing that binding
return !action.isKeyBindingManaged() || action.usesSharedKeyBinding();
}
/**
* Create the components in this panel.
*/
@ -328,11 +318,10 @@ public class KeyBindingsPanel extends JPanel {
return;
}
// run this after the current pending events in the swing
// thread so that the screen will repaint itself
SwingUtilities.invokeLater(() -> {
// clear the current user key stroke so that it does not
// appear as though the user is editing while importing
// give Swing a chance to repaint
Swing.runLater(() -> {
// clear the current user key stroke so that it does not appear as though the
// user is editing while importing
actionTable.clearSelection();
processKeyBindingsFromOptions(KeyBindingUtils.importKeyBindings());
});
@ -349,9 +338,8 @@ public class KeyBindingsPanel extends JPanel {
return;
}
// run this after the current pending events in the swing
// thread so that the screen will repaint itself
SwingUtilities.invokeLater(() -> {
// give Swing a chance to repaint
Swing.runLater(() -> {
ToolOptions keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
KeyBindingUtils.exportKeyBindings(keyBindingOptions);
});
@ -421,16 +409,16 @@ public class KeyBindingsPanel extends JPanel {
}
private void restoreDefaultKeybindings() {
Iterator<String> iter = actionMap.keySet().iterator();
Iterator<String> iter = keyStrokesByFullName.keySet().iterator();
while (iter.hasNext()) {
String actionName = iter.next();
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(actionName);
if (actions.size() == 0) {
DockingActionIf action = actionsByFullName.get(actionName);
if (action == null) {
throw new AssertException("No actions defined for " + actionName);
}
KeyStroke currentKeyStroke = actionMap.get(actionName);
KeyBindingData defaultBinding = actions.get(0).getDefaultKeyBindingData();
KeyStroke currentKeyStroke = keyStrokesByFullName.get(actionName);
KeyBindingData defaultBinding = action.getDefaultKeyBindingData();
KeyStroke newKeyStroke =
(defaultBinding == null) ? null : defaultBinding.getKeyBinding();
@ -453,7 +441,7 @@ public class KeyBindingsPanel extends JPanel {
String ksName = KeyEntryTextField.parseKeyStroke(keyStroke);
// remove old keystroke for action name
KeyStroke oldKs = actionMap.get(actionName);
KeyStroke oldKs = keyStrokesByFullName.get(actionName);
if (oldKs != null) {
String oldName = KeyEntryTextField.parseKeyStroke(oldKs);
if (oldName.equals(ksName)) {
@ -463,7 +451,7 @@ public class KeyBindingsPanel extends JPanel {
}
addToKeyMap(keyStroke, actionName);
actionMap.put(actionName, keyStroke);
keyStrokesByFullName.put(actionName, keyStroke);
changesMade(true);
return true;
}
@ -486,7 +474,7 @@ public class KeyBindingsPanel extends JPanel {
}
int selectedRow = actionTable.getSelectedRow();
int modelRow = tableFilterPanel.getModelRow(selectedRow);
return actionList.get(modelRow).getFullName();
return tableActions.get(modelRow).getFullName();
}
/**
@ -497,10 +485,10 @@ public class KeyBindingsPanel extends JPanel {
return;
}
String ksName = KeyEntryTextField.parseKeyStroke(ks);
List<String> list = keyMap.get(ksName);
List<String> list = actionNamesByKeyStroke.get(ksName);
if (list == null) {
list = new ArrayList<>();
keyMap.put(ksName, list);
actionNamesByKeyStroke.put(ksName, list);
}
if (!list.contains(actionName)) {
list.add(actionName);
@ -515,11 +503,11 @@ public class KeyBindingsPanel extends JPanel {
return;
}
String ksName = KeyEntryTextField.parseKeyStroke(ks);
List<String> list = keyMap.get(ksName);
List<String> list = actionNamesByKeyStroke.get(ksName);
if (list != null) {
list.remove(actionName);
if (list.isEmpty()) {
keyMap.remove(ksName);
actionNamesByKeyStroke.remove(ksName);
}
}
}
@ -529,7 +517,7 @@ public class KeyBindingsPanel extends JPanel {
* @param ksName name of Keystroke that has multiple actions mapped
*/
private void showActionMapped(String ksName) {
List<String> list = keyMap.get(ksName);
List<String> list = actionNamesByKeyStroke.get(ksName);
if (list == null) {
return;
}
@ -595,7 +583,7 @@ public class KeyBindingsPanel extends JPanel {
// prevent non-existing keybindings from being added to Ghidra (this can happen
// when actions exist in the imported bindings, but have been removed from
// Ghidra
if (!actionMap.containsKey(name)) {
if (!keyStrokesByFullName.containsKey(name)) {
continue;
}
@ -647,12 +635,10 @@ public class KeyBindingsPanel extends JPanel {
char keyChar = keyStroke.getKeyChar();
if (Character.isWhitespace(keyChar) ||
Character.getType(keyChar) == Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE) {
// remove keystroke
removeKeystroke(actionName);
}
else {
// check the action to see if is different than the current
// value
// check the action to see if is different than the current value
return checkAction(actionName, keyStroke);
}
}
@ -663,20 +649,24 @@ public class KeyBindingsPanel extends JPanel {
private void removeKeystroke(String selectedActionName) {
ksField.setText("");
if (actionMap.containsKey(selectedActionName)) {
KeyStroke stroke = actionMap.get(selectedActionName);
if (keyStrokesByFullName.containsKey(selectedActionName)) {
KeyStroke stroke = keyStrokesByFullName.get(selectedActionName);
if (stroke == null) {
// nothing to remove; nothing has changed
return;
}
removeFromKeyMap(stroke, selectedActionName);
actionMap.put(selectedActionName, null);
keyStrokesByFullName.put(selectedActionName, null);
tableModel.fireTableDataChanged();
changesMade(true);
}
}
Map<String, KeyStroke> getKeyStrokeMap() {
return keyStrokesByFullName;
}
//==================================================================================================
// Inner Classes
//==================================================================================================
@ -690,8 +680,12 @@ public class KeyBindingsPanel extends JPanel {
return;
}
String selectedAction = getSelectedAction();
KeyStroke ks = actionMap.get(selectedAction);
String fullActionName = getSelectedAction();
if (fullActionName == null) {
return;
}
KeyStroke ks = keyStrokesByFullName.get(fullActionName);
String ksName = "";
clearInfoPanel();
@ -705,18 +699,12 @@ public class KeyBindingsPanel extends JPanel {
statusLabel.setPreferredSize(
new Dimension(statusLabel.getPreferredSize().width, STATUS_LABEL_HEIGHT));
try {
List<DockingActionIf> actions =
tool.getDockingActionsByFullActionName(selectedAction);
String description = actions.get(0).getDescription();
if (description == null || description.trim().isEmpty()) {
description = actions.get(0).getName();
}
statusLabel.setText("<html>" + HTMLUtilities.escapeHTML(description));
}
catch (Exception ex) {
statusLabel.setText("");
DockingActionIf action = actionsByFullName.get(fullActionName);
String description = action.getDescription();
if (description == null || description.trim().isEmpty()) {
description = action.getName();
}
statusLabel.setText("<html>" + HTMLUtilities.escapeHTML(description));
}
}
@ -740,7 +728,7 @@ public class KeyBindingsPanel extends JPanel {
return action.getName();
case KEY_BINDING:
KeyStroke ks = actionMap.get(action.getFullName());
KeyStroke ks = keyStrokesByFullName.get(action.getFullName());
if (ks != null) {
return KeyEntryTextField.parseKeyStroke(ks);
}
@ -753,7 +741,7 @@ public class KeyBindingsPanel extends JPanel {
@Override
public List<DockingActionIf> getModelData() {
return actionList;
return tableActions;
}
@Override
@ -773,7 +761,7 @@ public class KeyBindingsPanel extends JPanel {
@Override
public int getRowCount() {
return actionList.size();
return tableActions.size();
}
}
}

View file

@ -17,8 +17,7 @@ package ghidra.framework.plugintool.dialog;
import java.awt.Color;
import java.awt.Point;
import java.util.Collections;
import java.util.List;
import java.util.*;
import javax.swing.KeyStroke;
import javax.swing.text.SimpleAttributeSet;
@ -35,9 +34,9 @@ import ghidra.framework.plugintool.util.PluginStatus;
* Panel that contains a JTextPane to show plugin description information.
*/
class PluginDetailsPanel extends AbstractDetailsPanel {
private static SimpleAttributeSet nameAttrSet;
private static SimpleAttributeSet depNameAttrSet;
private static SimpleAttributeSet depNameAttrSet;
private static SimpleAttributeSet descrAttrSet;
private static SimpleAttributeSet categoriesAttrSet;
private static SimpleAttributeSet classAttrSet;
@ -47,84 +46,62 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
private static SimpleAttributeSet noValueAttrSet;
private final PluginConfigurationModel model;
PluginDetailsPanel(PluginConfigurationModel model) {
super();
this.model = model;
createFieldAttributes();
createMainPanel();
}
void setPluginDescription(PluginDescription pluginDescription) {
textLabel.setText("");
if (pluginDescription == null) {
return;
}
textLabel.setText("");
if (pluginDescription == null) {
return;
}
List<PluginDescription> dependencies = model.getDependencies(pluginDescription);
Collections.sort(dependencies, (pd1, pd2) -> pd1.getName().compareTo(pd2.getName()));
StringBuilder buffer = new StringBuilder("<HTML>");
buffer.append( "<TABLE cellpadding=2>" );
insertRowTitle(buffer, "Name");
buffer.append("<TABLE cellpadding=2>");
insertRowTitle(buffer, "Name");
insertRowValue(buffer, pluginDescription.getName(),
!dependencies.isEmpty() ? depNameAttrSet : nameAttrSet);
insertRowTitle(buffer, "Description");
insertRowValue(buffer, formatDescription(pluginDescription.getDescription()), descrAttrSet);
insertRowTitle(buffer, "Description");
insertRowValue(buffer, formatDescription(pluginDescription.getDescription()), descrAttrSet);
insertRowTitle(buffer, "Status");
insertRowValue(buffer,
pluginDescription.getStatus().getDescription(),
(pluginDescription.getStatus() == PluginStatus.RELEASED) ?
titleAttrSet :developerAttrSet);
insertRowTitle(buffer, "Status");
insertRowValue(buffer, pluginDescription.getStatus().getDescription(),
(pluginDescription.getStatus() == PluginStatus.RELEASED) ? titleAttrSet
: developerAttrSet);
insertRowTitle(buffer, "Package");
insertRowValue(buffer, pluginDescription.getPluginPackage().getName(), categoriesAttrSet);
insertRowTitle(buffer, "Package");
insertRowValue(buffer, pluginDescription.getPluginPackage().getName(), categoriesAttrSet);
insertRowTitle(buffer, "Category");
insertRowValue(buffer, pluginDescription.getCategory(), categoriesAttrSet);
insertRowTitle(buffer, "Category");
insertRowValue(buffer, pluginDescription.getCategory(), categoriesAttrSet);
insertRowTitle(buffer, "Plugin Class");
insertRowValue(buffer, pluginDescription.getPluginClass().getName(), classAttrSet);
insertRowTitle(buffer, "Plugin Class");
insertRowValue(buffer, pluginDescription.getPluginClass().getName(), classAttrSet);
insertRowTitle(buffer, "Class Location");
insertRowValue(buffer, pluginDescription.getSourceLocation(), locAttrSet);
insertRowTitle(buffer, "Class Location");
insertRowValue(buffer, pluginDescription.getSourceLocation(), locAttrSet);
insertRowTitle(buffer, "Used By");
buffer.append( "<TD VALIGN=\"TOP\">" );
if (dependencies.isEmpty()) {
insertHTMLLine("None", titleAttrSet, buffer);
}
else {
for (int i = 0; i < dependencies.size(); i++) {
insertHTMLString(dependencies.get(i).getPluginClass().getName(), dependencyAttrSet,
buffer);
if (i < dependencies.size() - 1) {
insertHTMLString("<BR>", dependencyAttrSet, buffer);
}
}
insertHTMLLine("", titleAttrSet, buffer); // add a newline
}
buffer.append( "</TD>" );
buffer.append( "</TR>" );
insertRowTitle(buffer, "Services Required");
buffer.append("<TD VALIGN=\"TOP\">");
List<Class<?>> servicesRequired = pluginDescription.getServicesRequired();
if (servicesRequired.isEmpty()) {
if (dependencies.isEmpty()) {
insertHTMLLine("None", titleAttrSet, buffer);
}
else {
for (int i = 0; i < servicesRequired.size(); i++) {
insertHTMLString(servicesRequired.get(i).getName(), dependencyAttrSet,
for (int i = 0; i < dependencies.size(); i++) {
insertHTMLString(dependencies.get(i).getPluginClass().getName(), dependencyAttrSet,
buffer);
if (i < dependencies.size() - 1) {
insertHTMLString("<BR>", dependencyAttrSet, buffer);
@ -135,165 +112,185 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
buffer.append("</TD>");
buffer.append("</TR>");
//
// Developer
//
//
// Optional: Actions loaded by this plugin
//
addLoadedActionsContent( buffer, pluginDescription );
buffer.append( "</TABLE>" );
insertRowTitle(buffer, "Services Required");
textLabel.setText( buffer.toString() );
sp.getViewport().setViewPosition(new Point(0,0));
}
buffer.append("<TD VALIGN=\"TOP\">");
List<Class<?>> servicesRequired = pluginDescription.getServicesRequired();
if (servicesRequired.isEmpty()) {
insertHTMLLine("None", titleAttrSet, buffer);
}
else {
for (int i = 0; i < servicesRequired.size(); i++) {
insertHTMLString(servicesRequired.get(i).getName(), dependencyAttrSet, buffer);
if (i < dependencies.size() - 1) {
insertHTMLString("<BR>", dependencyAttrSet, buffer);
}
}
insertHTMLLine("", titleAttrSet, buffer); // add a newline
}
buffer.append("</TD>");
buffer.append("</TR>");
//
// Developer
//
//
// Optional: Actions loaded by this plugin
//
addLoadedActionsContent(buffer, pluginDescription);
buffer.append("</TABLE>");
textLabel.setText(buffer.toString());
sp.getViewport().setViewPosition(new Point(0, 0));
}
// creates an HTML table to display actions loaded by the plugin
private void addLoadedActionsContent(StringBuilder buffer,
PluginDescription pluginDescription) {
if ( !model.isLoaded( pluginDescription ) ) {
return;
}
buffer.append( "<TR>" );
buffer.append( "<TD VALIGN=\"TOP\">" );
insertHTMLLine("Loaded Actions:", titleAttrSet, buffer);
buffer.append( "</TD>" );
if (!model.isLoaded(pluginDescription)) {
return;
}
List<DockingActionIf> actionList = model.getActionsForPlugin( pluginDescription );
if ( actionList.size() == 0 ) {
buffer.append( "<TD VALIGN=\"TOP\">" );
insertHTMLLine("No actions for plugin", noValueAttrSet, buffer);
buffer.append( "</TD>" );
buffer.append( "</TR>" );
return;
}
buffer.append( "<TD VALIGN=\"TOP\">" );
buffer.append( "<TABLE BORDER=1><TR><TH>Action Name</TH><TH>Menu Path</TH><TH>Keybinding</TH></TR>" );
for ( DockingActionIf dockableAction : actionList ) {
buffer.append( "<TR><TD WIDTH=\"200\">" );
insertHTMLString( dockableAction.getName(), locAttrSet, buffer );
buffer.append( "</TD>" );
buffer.append( "<TD WIDTH=\"300\">" );
MenuData menuBarData = dockableAction.getMenuBarData();
String[] menuPath = menuBarData == null ? null : menuBarData.getMenuPath();
String menuPathString = createStringForMenuPath( menuPath );
if ( menuPathString != null ) {
insertHTMLString( menuPathString, locAttrSet, buffer );
}
else {
MenuData popupMenuData = dockableAction.getPopupMenuData();
String[] popupPath = popupMenuData == null ? null : popupMenuData.getMenuPath();
buffer.append("<TR>");
buffer.append("<TD VALIGN=\"TOP\">");
insertHTMLLine("Loaded Actions:", titleAttrSet, buffer);
buffer.append("</TD>");
if ( popupPath != null ) {
insertHTMLString( "(in a context popup menu)", noValueAttrSet, buffer );
}
else {
insertHTMLString( "Not in a menu", noValueAttrSet, buffer );
}
}
buffer.append( "</TD>" );
buffer.append( "<TD WIDTH=\"100\">" );
KeyStroke keyBinding = dockableAction.getKeyBinding();
if ( keyBinding != null ) {
String keyStrokeString = DockingKeyBindingAction.parseKeyStroke( keyBinding );
insertHTMLString( keyStrokeString, locAttrSet, buffer );
}
else {
insertHTMLString( "No keybinding", noValueAttrSet, buffer );
}
buffer.append( "</TD></TR>" );
}
buffer.append( "</TABLE>" );
buffer.append( "</TD>" );
buffer.append( "</TR>" );
Set<DockingActionIf> actions = model.getActionsForPlugin(pluginDescription);
if (actions.size() == 0) {
buffer.append("<TD VALIGN=\"TOP\">");
insertHTMLLine("No actions for plugin", noValueAttrSet, buffer);
buffer.append("</TD>");
buffer.append("</TR>");
return;
}
buffer.append("<TD VALIGN=\"TOP\">");
buffer.append(
"<TABLE BORDER=1><TR><TH>Action Name</TH><TH>Menu Path</TH><TH>Keybinding</TH></TR>");
for (DockingActionIf dockableAction : actions) {
buffer.append("<TR><TD WIDTH=\"200\">");
insertHTMLString(dockableAction.getName(), locAttrSet, buffer);
buffer.append("</TD>");
buffer.append("<TD WIDTH=\"300\">");
MenuData menuBarData = dockableAction.getMenuBarData();
String[] menuPath = menuBarData == null ? null : menuBarData.getMenuPath();
String menuPathString = createStringForMenuPath(menuPath);
if (menuPathString != null) {
insertHTMLString(menuPathString, locAttrSet, buffer);
}
else {
MenuData popupMenuData = dockableAction.getPopupMenuData();
String[] popupPath = popupMenuData == null ? null : popupMenuData.getMenuPath();
if (popupPath != null) {
insertHTMLString("(in a context popup menu)", noValueAttrSet, buffer);
}
else {
insertHTMLString("Not in a menu", noValueAttrSet, buffer);
}
}
buffer.append("</TD>");
buffer.append("<TD WIDTH=\"100\">");
KeyStroke keyBinding = dockableAction.getKeyBinding();
if (keyBinding != null) {
String keyStrokeString = DockingKeyBindingAction.parseKeyStroke(keyBinding);
insertHTMLString(keyStrokeString, locAttrSet, buffer);
}
else {
insertHTMLString("No keybinding", noValueAttrSet, buffer);
}
buffer.append("</TD></TR>");
}
buffer.append("</TABLE>");
buffer.append("</TD>");
buffer.append("</TR>");
}
private String createStringForMenuPath( String[] path ) {
if ( path == null ) {
return null;
}
StringBuffer buffy = new StringBuffer();
for ( int i = 0; i < path.length; i++ ) {
buffy.append( path[i].replaceAll( "\\&", "" ) ); // strip off the mnemonic identifier '&'
if ( i != path.length-1 ) {
buffy.append( "-&gt;" );
}
}
return buffy.toString();
private String createStringForMenuPath(String[] path) {
if (path == null) {
return null;
}
StringBuffer buffy = new StringBuffer();
for (int i = 0; i < path.length; i++) {
buffy.append(path[i].replaceAll("\\&", "")); // strip off the mnemonic identifier '&'
if (i != path.length - 1) {
buffy.append("-&gt;");
}
}
return buffy.toString();
}
@Override
protected void createFieldAttributes() {
titleAttrSet = new SimpleAttributeSet();
titleAttrSet = new SimpleAttributeSet();
titleAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
titleAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
titleAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
titleAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
titleAttrSet.addAttribute(StyleConstants.Foreground, new Color(140, 0, 0));
nameAttrSet = new SimpleAttributeSet();
nameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
nameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
nameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
nameAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
nameAttrSet.addAttribute(StyleConstants.Foreground, new Color(0, 204, 51));
depNameAttrSet = new SimpleAttributeSet();
depNameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
depNameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
depNameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
depNameAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
depNameAttrSet.addAttribute(StyleConstants.Foreground, Color.RED);
descrAttrSet = new SimpleAttributeSet();
descrAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
descrAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
descrAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
descrAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
descrAttrSet.addAttribute(StyleConstants.Foreground, Color.BLUE);
categoriesAttrSet = new SimpleAttributeSet();
categoriesAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
categoriesAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
categoriesAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
categoriesAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
categoriesAttrSet.addAttribute(StyleConstants.Foreground, new Color(204, 0, 204));
classAttrSet = new SimpleAttributeSet();
classAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced");
classAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
classAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
classAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
classAttrSet.addAttribute(StyleConstants.Foreground, Color.BLACK);
locAttrSet = new SimpleAttributeSet();
locAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
locAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
locAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
locAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
locAttrSet.addAttribute(StyleConstants.Foreground, Color.DARK_GRAY);
developerAttrSet = new SimpleAttributeSet();
developerAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
developerAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
developerAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
developerAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
developerAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
developerAttrSet.addAttribute(StyleConstants.Foreground, new Color(230, 15, 85));
dependencyAttrSet = new SimpleAttributeSet();
dependencyAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced");
dependencyAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
dependencyAttrSet.addAttribute(StyleConstants.FontFamily, "monospaced");
dependencyAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
dependencyAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
dependencyAttrSet.addAttribute(StyleConstants.Foreground, new Color(23, 100, 30));
noValueAttrSet = new SimpleAttributeSet();
noValueAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
noValueAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
noValueAttrSet.addAttribute(StyleConstants.Italic, Boolean.TRUE);
noValueAttrSet.addAttribute(StyleConstants.Foreground, new Color(192, 192, 192));
noValueAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
noValueAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11));
noValueAttrSet.addAttribute(StyleConstants.Italic, Boolean.TRUE);
noValueAttrSet.addAttribute(StyleConstants.Foreground, new Color(192, 192, 192));
}
}

View file

@ -21,26 +21,33 @@ import java.util.*;
import javax.swing.KeyStroke;
import docking.ComponentProvider;
import docking.DockingWindowManager;
import docking.*;
import docking.action.*;
import docking.tool.util.DockingToolConstants;
import ghidra.framework.options.OptionType;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.ToolConstants;
import ghidra.util.exception.AssertException;
/**
* Helper class to manage plugin actions for the tool.
*/
public class ProjectActionManager implements PropertyChangeListener {
private DockingWindowManager winMgr;
private DockingWindowManagerActionUpdater winMgrActionUpdater;
// TODO
// TODO
// TODO does this class need the shared action concept (probably not)
// TODO
// TODO
private Map<String, List<DockingActionIf>> actionMap;
private Options keyBindingOptions;
private PluginTool tool;
/**
* Construct an ActionManager.
* Construct an ActionManager
* @param tool plugin tool using this ActionManager
* @param winMgr manager of the "Docking" arrangement
* of a set of components and actions in the tool
@ -48,8 +55,9 @@ public class ProjectActionManager implements PropertyChangeListener {
public ProjectActionManager(PluginTool tool, DockingWindowManager winMgr) {
this.tool = tool;
this.winMgr = winMgr;
this.winMgrActionUpdater = new DockingWindowManagerActionUpdater(winMgr);
actionMap = new HashMap<>();
keyBindingOptions = tool.getOptions(ToolConstants.KEY_BINDINGS);
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
}
public void dispose() {
@ -97,7 +105,8 @@ public class ProjectActionManager implements PropertyChangeListener {
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
}
}
winMgr.addToolAction(action);
winMgrActionUpdater.addToolAction(action);
}
/**
@ -107,7 +116,7 @@ public class ProjectActionManager implements PropertyChangeListener {
public synchronized void removeToolAction(DockingActionIf action) {
action.removePropertyChangeListener(this);
removeActionFromMap(action);
winMgr.removeToolAction(action);
winMgrActionUpdater.removeToolAction(action);
}
/**
@ -140,7 +149,7 @@ public class ProjectActionManager implements PropertyChangeListener {
action.setUnvalidatedKeyBindingData(new KeyBindingData(newKs));
}
}
winMgr.addLocalAction(provider, action);
winMgrActionUpdater.addLocalAction(provider, action);
}
private void checkForAlreadyAddedAction(ComponentProvider provider, DockingActionIf action) {
@ -167,14 +176,15 @@ public class ProjectActionManager implements PropertyChangeListener {
winMgr.removeProviderAction(provider, action);
}
// TODO delete me
/**
* Get all actions that have the action name which includes the action owner's name.
*
* @param fullName full name for the action, e.g., "My Action (My Plugin)"
* @return list of actions; empty if no action exists with the given name
*/
public List<DockingActionIf> getDockingActionsByFullActionName(String fullActionName) {
List<DockingActionIf> list = actionMap.get(fullActionName);
public List<DockingActionIf> getDockingActionsByFullActionName(String fullName) {
List<DockingActionIf> list = actionMap.get(fullName);
if (list == null) {
return new ArrayList<>();
}
@ -233,7 +243,7 @@ public class ProjectActionManager implements PropertyChangeListener {
*
*/
public synchronized void restoreKeyBindings() {
keyBindingOptions = tool.getOptions(ToolConstants.KEY_BINDINGS);
keyBindingOptions = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
List<DockingActionIf> actions = getAllActions();
for (DockingActionIf action : actions) {
if (!action.isKeyBindingManaged()) {
@ -275,7 +285,7 @@ public class ProjectActionManager implements PropertyChangeListener {
}
KeyBindingData keyBindingData = (KeyBindingData) evt.getNewValue();
KeyStroke newKeyStroke = keyBindingData.getKeyBinding();
Options opt = tool.getOptions(ToolConstants.KEY_BINDINGS);
Options opt = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
KeyStroke optKeyStroke = opt.getKeyStroke(action.getFullName(), null);
if (newKeyStroke == null) {
opt.removeOption(action.getFullName());

View file

@ -18,12 +18,10 @@ package help.screenshot;
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.util.List;
import org.junit.Test;
import docking.action.DockingActionIf;
import ghidra.app.plugin.core.assembler.AssembleDockingAction;
import ghidra.app.plugin.core.assembler.AssemblerPlugin;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.framework.plugintool.util.PluginException;
@ -37,9 +35,7 @@ public class AssemblerPluginScreenShots extends GhidraScreenShotGenerator {
positionCursor(0x0040512e);
tool.addPlugin(AssemblerPlugin.class.getName());
String fullActionName = "Assemble (AssemblerPlugin)";
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(fullActionName);
AssembleDockingAction action = (AssembleDockingAction) actions.get(0);
DockingActionIf action = getAction(tool, "AssemblerPlugin", "Assemble");
performAction(action, true);

View file

@ -156,10 +156,10 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator
@Test
public void testPreviewWindow() {
String fullActionName = "Show Preview Window (DataTypeManagerPlugin)";
List<DockingActionIf> action = tool.getDockingActionsByFullActionName(fullActionName);
performAction(action.get(0));
// performAction("Show Preview Window", "DataTypeManagerPlugin", false);
DockingActionIf action = getAction(tool, "DataTypeManagerPlugin", "Show Preview Window");
performAction(action);
DataTypesProvider provider = getProvider(DataTypesProvider.class);
GTree tree = (GTree) getInstanceField("archiveGTree", provider);
GTreeRootNode rootNode = tree.getRootNode();

View file

@ -283,10 +283,9 @@ public class ToolScreenShots extends GhidraScreenShotGenerator {
DockingWindowManager windowManager = tool.getWindowManager();
DockingActionManager actionMgr =
(DockingActionManager) getInstanceField("actionManager", windowManager);
String fullActionName = "Delete Function" + " (" + "FunctionPlugin" + ")";
List<DockingActionIf> actions = tool.getDockingActionsByFullActionName(fullActionName);
final KeyEntryDialog keyEntryDialog = new KeyEntryDialog(actions.get(0), actionMgr);
DockingActionIf action = getAction(tool, "FunctionPlugin", "Delete Function");
final KeyEntryDialog keyEntryDialog = new KeyEntryDialog(action, actionMgr);
runSwing(() -> tool.showDialog(keyEntryDialog), false);
captureDialog();

View file

@ -18,14 +18,12 @@
*/
package ghidra.framework.main;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import javax.swing.*;
@ -239,10 +237,10 @@ public class SharedProjectUtil {
}
private static DockingActionIf getAction(FrontEndTool frontEndTool, String actionName) {
List<DockingActionIf> actions =
frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)");
assertEquals(1, actions.size());
return actions.get(0);
DockingActionIf action =
AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName);
return action;
}
private static class UtilProjectListener implements ProjectListener {

View file

@ -23,8 +23,6 @@ import java.util.*;
import javax.swing.*;
import javax.swing.tree.TreePath;
import org.junit.Assert;
import docking.*;
import docking.action.DockingActionIf;
import docking.test.AbstractDockingTest;
@ -341,15 +339,10 @@ public class FrontEndTestEnv {
return new ArrayList<>(Arrays.asList(tools));
}
public List<DockingActionIf> getFrontEndActions() {
return frontEndTool.getDockingActionsByOwnerName("FrontEndPlugin");
}
public DockingActionIf getAction(String actionName) {
List<DockingActionIf> a =
frontEndTool.getDockingActionsByFullActionName(actionName + " (FrontEndPlugin)");
Assert.assertEquals(1, a.size());
return a.get(0);
DockingActionIf action =
AbstractDockingTest.getAction(frontEndTool, "FrontEndPlugin", actionName);
return action;
}
public void performFrontEndAction(DockingActionIf action) {