GT-3165 - Tables - Fixed Ctrl-A conflict between tables and the Listing

This commit is contained in:
dragonmacher 2019-09-19 14:52:00 -04:00
parent ab5e715607
commit a87b65b758
5 changed files with 443 additions and 336 deletions

View file

@ -24,7 +24,6 @@ import javax.swing.*;
import docking.action.DockingActionIf;
import docking.widgets.label.GIconLabel;
import docking.widgets.label.GLabel;
import resources.ResourceManager;
/**
* Dialog to show multiple actions that are mapped to the same keystroke;
@ -39,10 +38,8 @@ public class ActionDialog extends DialogComponentProvider {
/**
* Constructor
* @param parent parent to this dialog
* @param keystrokeName keystroke name
* @param list list of PluginActions
* @param event event to pass the selected action
* @param list list of actions
*/
public ActionDialog(String keystrokeName, List<ExecutableKeyActionAdapter> list) {
super("Select Action", true);
@ -68,10 +65,8 @@ public class ActionDialog extends DialogComponentProvider {
}
/**
* Set the list of actions that are enabled.
* @param list list of actions
* @param event event to pass to the action that is
* selected.
* Set the list of actions that are enabled
* @param list list of actions selected
*/
public void setActionList(List<ExecutableKeyActionAdapter> list) {
okButton.setEnabled(false);
@ -85,8 +80,6 @@ public class ActionDialog extends DialogComponentProvider {
actionList.setSelectedIndex(0);
}
/////////////////////////////////////////////////////////////////////////
private void init() {
this.addWorkPanel(buildMainPanel());
addOKButton();
@ -107,7 +100,9 @@ public class ActionDialog extends DialogComponentProvider {
innerPanel.setBorder(BorderFactory.createTitledBorder("Actions"));
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
panel.add(new GIconLabel(ResourceManager.loadImage("images/warning.png")));
Icon icon = UIManager.getIcon("OptionPane.warningIcon");
panel.add(new GIconLabel(icon));
panel.add(labelPanel);
listModel = new DefaultListModel<>();
@ -139,7 +134,7 @@ public class ActionDialog extends DialogComponentProvider {
return mainPanel;
}
void addListeners() {
private void addListeners() {
actionList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {

View file

@ -0,0 +1,37 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.action;
import java.awt.Component;
import docking.ActionContext;
import docking.ComponentProvider;
/**
* An interface to signal that the implementing action works with an individual Java
* {@link Component}. Standard Docking Actions are either global tool-based actions or local
* {@link ComponentProvider} actions. This interface allows us to have the concept of an
* action that is effectively local to a specific Java component.
*/
public interface ComponentBasedDockingAction extends DockingActionIf {
/**
* Returns true if the given context contains this action's component
* @param context the context
* @return true if the given context contains this action's component
*/
public boolean isValidComponentContext(ActionContext context);
}

View file

@ -169,9 +169,11 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
List<ExecutableKeyActionAdapter> list = new ArrayList<>();
boolean hasLocalActionsForKeyBinding = false;
// search for local actions first...
//
// 1) Prefer local actions for the active provider
//
for (ActionData actionData : actions) {
if (actionData.isMyProvider(localContext.getComponentProvider())) {
if (actionData.isMyProvider(localContext)) {
hasLocalActionsForKeyBinding = true;
if (actionData.action.isEnabledForContext(localContext)) {
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext));
@ -185,7 +187,33 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return list;
}
// ...no locals, see if we have any global actions
//
// 2) Check for actions local to the source component
//
for (ActionData actionData : actions) {
if (!(actionData.action instanceof ComponentBasedDockingAction)) {
continue;
}
ComponentBasedDockingAction componentAction =
(ComponentBasedDockingAction) actionData.action;
if (componentAction.isValidComponentContext(localContext)) {
hasLocalActionsForKeyBinding = true;
if (actionData.action.isEnabledForContext(localContext)) {
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext));
}
}
}
if (hasLocalActionsForKeyBinding) {
// We have locals, ignore the globals. This prevents global actions from processing
// the given keybinding when a local action exits, regardless of enablement.
return list;
}
//
// 3) Check for global actions
//
for (ActionData actionData : actions) {
if (actionData.isGlobalAction()) {
// When looking for context matches, we prefer local context, even though this
@ -271,7 +299,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
return provider == null;
}
boolean isMyProvider(ComponentProvider otherProvider) {
boolean isMyProvider(ActionContext localContext) {
ComponentProvider otherProvider = localContext.getComponentProvider();
return provider == otherProvider;
}

View file

@ -15,9 +15,9 @@
*/
package docking.widgets.table;
import static docking.DockingUtils.CONTROL_KEY_MODIFIER_MASK;
import static docking.action.MenuData.NO_MNEMONIC;
import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
import static docking.DockingUtils.*;
import static docking.action.MenuData.*;
import static java.awt.event.InputEvent.*;
import java.awt.*;
import java.awt.event.*;
@ -448,8 +448,15 @@ public class GTable extends JTable implements KeyStrokeConsumer {
}
/**
* Enables the keyboard actions to pass through this table
* and up the component hierarchy.
* Enables the keyboard actions to pass through this table and up the component hierarchy.
* Specifically, passing true to this method allows unmodified keystrokes to work
* in the tool when this table is focused. Modified keystrokes, like <code>
* Ctrl-C</code>, will work at all times. Finally, if true is passed to this
* method, then the {@link #setAutoLookupColumn(int) auto lookup} feature is
* disabled.
*
* <p>The default state is for actions to be disabled.
*
* @param b true allows keyboard actions to pass up the component hierarchy.
*/
public void setActionsEnabled(boolean b) {
@ -1496,7 +1503,8 @@ public class GTable extends JTable implements KeyStrokeConsumer {
}
}
private abstract static class GTableAction extends DockingAction {
private abstract static class GTableAction extends DockingAction
implements ComponentBasedDockingAction {
GTableAction(String name, String owner) {
super(name, owner);
@ -1516,5 +1524,11 @@ public class GTable extends JTable implements KeyStrokeConsumer {
Component sourceComponent = context.getSourceComponent();
return sourceComponent instanceof GTable;
}
@Override
public boolean isValidComponentContext(ActionContext context) {
Component sourceComponent = context.getSourceComponent();
return sourceComponent instanceof GTable;
}
}
}

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.
@ -16,6 +15,8 @@
*/
package ghidra.program.model.block;
import java.util.*;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
@ -24,8 +25,6 @@ import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.util.*;
/**
* <CODE>MultEntSubModel</CODE> (M-model) defines subroutines which do not share code with
* any other subroutine and may have one or more entry points. Each entry-
@ -78,10 +77,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
* @return null if there is no subroutine with an entry at addr.
* @throws CancelledException if the monitor cancels the operation.
*/
@Override
public CodeBlock getCodeBlockAt(Address addr, TaskMonitor monitor) throws CancelledException {
if (addr == null)
if (addr == null) {
return null;
}
CodeBlock block = getSubFromCache(addr);
if (block == null) {
@ -89,11 +90,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
}
if (block != null) {
Address[] entPts = block.getStartAddresses();
for (int i = 0; i < entPts.length; i++) {
if (entPts[i].equals(addr))
for (Address entPt : entPts) {
if (entPt.equals(addr)) {
return block;
}
}
}
return null;
}
@ -108,7 +110,8 @@ public class MultEntSubModel implements SubroutineBlockModel {
* Null is returned if there is no instruction at addr.
* @throws CancelledException if the monitor cancels the operation.
**/
protected CodeBlock getAddressSetContaining(Address addr, TaskMonitor monitor) throws CancelledException {
protected CodeBlock getAddressSetContaining(Address addr, TaskMonitor monitor)
throws CancelledException {
if (addr.isExternalAddress()) {
if (includeExternals) {
@ -135,8 +138,9 @@ public class MultEntSubModel implements SubroutineBlockModel {
// Build model-M subroutine from basic blocks
while (!todoList.isEmpty() || !srcList.isEmpty()) {
if (monitor != null && monitor.isCancelled())
if (monitor != null && monitor.isCancelled()) {
throw new CancelledException();
}
// if todoList is empty
// process any blocks for sources that we put off to analyze later
@ -155,16 +159,19 @@ public class MultEntSubModel implements SubroutineBlockModel {
a = todoList.removeFirst();
// Get basic block at the specified address
if (addrSet.contains(a))
if (addrSet.contains(a)) {
continue; // already processed this block
}
CodeBlock bblock = bbModel.getFirstCodeBlockContaining(a, monitor);
if (bblock == null)
if (bblock == null) {
continue;
}
// Verify that the block contains instructions
if (listing.getInstructionAt(bblock.getMinAddress()) == null)
if (listing.getInstructionAt(bblock.getMinAddress()) == null) {
continue;
}
// Add basic block to subroutine address set
addrSet.add(bblock);
@ -176,12 +183,13 @@ public class MultEntSubModel implements SubroutineBlockModel {
srcList.addLast(bblock);
}
if (addrSet.isEmpty())
if (addrSet.isEmpty()) {
return null;
}
// Check for failure to find entry point
if (entryPtList.size() == 0) {
Msg.warn(this, "WARNING: failed to find entry point for subroutine containing " + addr);
Msg.warn(this, "Failed to find entry point for subroutine containing " + addr);
entryPtList.add(addrSet.getMinAddress());
}
Address[] entryPts = new Address[entryPtList.size()];
@ -193,8 +201,8 @@ public class MultEntSubModel implements SubroutineBlockModel {
return block;
}
private void addDestinations(TaskMonitor monitor, LinkedList<Address> todoList, CodeBlock bblock)
throws CancelledException {
private void addDestinations(TaskMonitor monitor, LinkedList<Address> todoList,
CodeBlock bblock) throws CancelledException {
CodeBlockReferenceIterator destIter = bblock.getDestinations(monitor);
while (destIter.hasNext()) {
CodeBlockReference destRef = destIter.next();
@ -212,9 +220,8 @@ public class MultEntSubModel implements SubroutineBlockModel {
}
}
private void addSources(TaskMonitor monitor,
List<Address> entryPtList, LinkedList<Address> todoList, CodeBlock bblock)
throws CancelledException {
private void addSources(TaskMonitor monitor, List<Address> entryPtList,
LinkedList<Address> todoList, CodeBlock bblock) throws CancelledException {
CodeBlockReferenceIterator srcIter = bblock.getSources(monitor);
boolean isSource = true;
boolean isEntry = false;
@ -225,7 +232,8 @@ public class MultEntSubModel implements SubroutineBlockModel {
if (refFlowType.isJump() || refFlowType.isFallthrough()) {
// Add Jump and Fall-through sources to the todoList
todoList.addLast(srcRef.getSourceAddress());
} else if (refFlowType.isCall()) {
}
else if (refFlowType.isCall()) {
// Basic block is a subroutine entry point
isEntry = true;
}
@ -244,7 +252,9 @@ public class MultEntSubModel implements SubroutineBlockModel {
* null otherwise.
* @throws CancelledException if the monitor cancels the operation.
*/
public CodeBlock getFirstCodeBlockContaining(Address addr, TaskMonitor monitor) throws CancelledException {
@Override
public CodeBlock getFirstCodeBlockContaining(Address addr, TaskMonitor monitor)
throws CancelledException {
CodeBlock block = getSubFromCache(addr);
if (block == null) {
@ -253,7 +263,6 @@ public class MultEntSubModel implements SubroutineBlockModel {
return block;
}
/**
* Returns the one code block contained by addr (only for
* a model that has shared subroutines would this method
@ -265,7 +274,9 @@ public class MultEntSubModel implements SubroutineBlockModel {
* empty array otherwise.
* @throws CancelledException if the monitor cancels the operation.
*/
public CodeBlock[] getCodeBlocksContaining(Address addr, TaskMonitor monitor) throws CancelledException {
@Override
public CodeBlock[] getCodeBlocksContaining(Address addr, TaskMonitor monitor)
throws CancelledException {
CodeBlock sub = getFirstCodeBlockContaining(addr, monitor);
if (sub == null) {
return emptyBlockArray;
@ -275,12 +286,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
return blocks;
}
/**
* Get an iterator over the code blocks in the entire program.
* @param monitor task monitor which allows user to cancel operation.
* @throws CancelledException if the monitor cancels the operation.
*/
@Override
public CodeBlockIterator getCodeBlocks(TaskMonitor monitor) throws CancelledException {
return new MultEntSubIterator(this, monitor);
}
@ -292,13 +303,16 @@ public class MultEntSubModel implements SubroutineBlockModel {
* @param monitor task monitor which allows user to cancel operation.
* @throws CancelledException if the monitor cancels the operation.
*/
public CodeBlockIterator getCodeBlocksContaining(AddressSetView addrSet, TaskMonitor monitor) throws CancelledException {
@Override
public CodeBlockIterator getCodeBlocksContaining(AddressSetView addrSet, TaskMonitor monitor)
throws CancelledException {
return new MultEntSubIterator(this, addrSet, monitor);
}
/**
* @see ghidra.program.model.block.CodeBlockModel#getProgram()
*/
@Override
public Program getProgram() {
return program;
}
@ -314,10 +328,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.CodeBlockModel#getName(ghidra.program.model.block.CodeBlock)
*/
@Override
public String getName(CodeBlock block) {
if (!(block.getModel() instanceof MultEntSubModel))
if (!(block.getModel() instanceof MultEntSubModel)) {
throw new IllegalArgumentException();
}
// get the start address for the block
// look up the symbol in the symbol table.
@ -334,7 +350,6 @@ public class MultEntSubModel implements SubroutineBlockModel {
return "SOURCE_SUB" + start.toString();
}
/**
* Return in general how things flow out of this node.
* This method exists for the SIMPLEBLOCK model.
@ -350,10 +365,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
*
* @return flow type of this node
*/
@Override
public FlowType getFlowType(CodeBlock block) {
if (!(block.getModel() instanceof MultEntSubModel))
if (!(block.getModel() instanceof MultEntSubModel)) {
throw new IllegalArgumentException();
}
/* If there are multiple unique ways out of the node, then we
should return FlowType.UNKNOWN (or FlowType.MULTIFLOW ?).
@ -369,9 +386,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.CodeBlockModel#getSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
*/
public CodeBlockReferenceIterator getSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
if (!(block.getModel() instanceof MultEntSubModel))
@Override
public CodeBlockReferenceIterator getSources(CodeBlock block, TaskMonitor monitor)
throws CancelledException {
if (!(block.getModel() instanceof MultEntSubModel)) {
throw new IllegalArgumentException();
}
return new SubroutineSourceReferenceIterator(block, monitor);
}
@ -379,10 +399,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.CodeBlockModel#getNumSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
*/
@Override
public int getNumSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
if (!(block.getModel() instanceof MultEntSubModel))
if (!(block.getModel() instanceof MultEntSubModel)) {
throw new IllegalArgumentException();
}
return SubroutineSourceReferenceIterator.getNumSources(block, monitor);
}
@ -390,10 +412,13 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.CodeBlockModel#getDestinations(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
*/
public CodeBlockReferenceIterator getDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
@Override
public CodeBlockReferenceIterator getDestinations(CodeBlock block, TaskMonitor monitor)
throws CancelledException {
if (!(block.getModel() instanceof MultEntSubModel))
if (!(block.getModel() instanceof MultEntSubModel)) {
throw new IllegalArgumentException();
}
return new SubroutineDestReferenceIterator(block, monitor);
}
@ -407,10 +432,12 @@ public class MultEntSubModel implements SubroutineBlockModel {
* @param monitor task monitor which allows user to cancel operation.
* @throws CancelledException if the monitor cancels the operation.
*/
@Override
public int getNumDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
if (!(block.getModel() instanceof MultEntSubModel))
if (!(block.getModel() instanceof MultEntSubModel)) {
throw new IllegalArgumentException();
}
return SubroutineDestReferenceIterator.getNumDestinations(block, monitor);
}
@ -423,8 +450,9 @@ public class MultEntSubModel implements SubroutineBlockModel {
*/
public AddressSetView getAddressSet(CodeBlock block) {
if (!(block.getModel() instanceof MultEntSubModel))
if (!(block.getModel() instanceof MultEntSubModel)) {
throw new IllegalArgumentException();
}
return new AddressSet(block);
}
@ -438,10 +466,10 @@ public class MultEntSubModel implements SubroutineBlockModel {
return mapObjs.length == 0 ? null : (CodeBlock) mapObjs[0];
}
/**
* @see ghidra.program.model.block.CodeBlockModel#getBasicBlockModel()
*/
@Override
public CodeBlockModel getBasicBlockModel() {
if (bbModel == null) {
bbModel = new SimpleBlockModel(program);
@ -452,6 +480,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.CodeBlockModel#getName()
*/
@Override
public String getName() {
return NAME;
}
@ -459,6 +488,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.SubroutineBlockModel#getBaseSubroutineModel()
*/
@Override
public SubroutineBlockModel getBaseSubroutineModel() {
return this;
}
@ -466,6 +496,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.CodeBlockModel#allowsBlockOverlap()
*/
@Override
public boolean allowsBlockOverlap() {
return false;
}
@ -473,6 +504,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
/**
* @see ghidra.program.model.block.CodeBlockModel#externalsIncluded()
*/
@Override
public boolean externalsIncluded() {
return includeExternals;
}