GT-3044 - Table Actions - updated table actions to use the modern action

system
This commit is contained in:
dragonmacher 2019-08-02 19:41:30 -04:00
parent 392c0846d1
commit 28f6500039
70 changed files with 642 additions and 493 deletions

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,26 +15,37 @@
*/ */
package ghidra.app.context; package ghidra.app.context;
import ghidra.program.model.listing.Program; import java.awt.Component;
import java.awt.KeyboardFocusManager;
import docking.ActionContext; import docking.ActionContext;
import docking.ComponentProvider; import docking.ComponentProvider;
import ghidra.program.model.listing.Program;
public class ProgramActionContext extends ActionContext { public class ProgramActionContext extends ActionContext {
protected final Program program; protected final Program program;
public ProgramActionContext(ComponentProvider provider, Program program) { public ProgramActionContext(ComponentProvider provider, Program program) {
this(provider, program, null); this(provider, program, null);
} }
public ProgramActionContext(ComponentProvider provider, Program program, Object contextObject) { public ProgramActionContext(ComponentProvider provider, Program program,
super(provider, contextObject); Component sourceComponent) {
this.program = program; this(provider, program, sourceComponent, sourceComponent);
}
public ProgramActionContext(ComponentProvider provider, Program program,
Component sourceComponent, Object contextObject) {
super(provider, contextObject, sourceComponent);
this.program = program;
// the case where the first constructor is called, which does not specify the component
if (sourceComponent == null) {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
setSourceObject(kfm.getFocusOwner());
}
} }
/**
* @return Returns the program.
*/
public Program getProgram() { public Program getProgram() {
return program; return program;
} }

View file

@ -15,6 +15,7 @@
*/ */
package ghidra.app.context; package ghidra.app.context;
import java.awt.Component;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@ -27,14 +28,9 @@ public class ProgramSymbolActionContext extends ProgramActionContext {
private final long[] symbolIDs; private final long[] symbolIDs;
public ProgramSymbolActionContext(ComponentProvider provider, Program program,
long[] symbolIDs) {
this(provider, program, symbolIDs, null);
}
public ProgramSymbolActionContext(ComponentProvider provider, Program program, long[] symbolIDs, public ProgramSymbolActionContext(ComponentProvider provider, Program program, long[] symbolIDs,
Object contextObj) { Component sourceComponent) {
super(provider, program, contextObj); super(provider, program, sourceComponent);
this.symbolIDs = symbolIDs; this.symbolIDs = symbolIDs;
} }

View file

@ -87,7 +87,7 @@ class MergeManagerProvider extends ComponentProviderAdapter {
if (event != null && event.getSource() instanceof FieldHeaderComp) { if (event != null && event.getSource() instanceof FieldHeaderComp) {
FieldHeaderComp comp = (FieldHeaderComp) event.getSource(); FieldHeaderComp comp = (FieldHeaderComp) event.getSource();
FieldHeaderLocation fieldHeaderLocation = comp.getFieldHeaderLocation(event.getPoint()); FieldHeaderLocation fieldHeaderLocation = comp.getFieldHeaderLocation(event.getPoint());
return new ActionContext(this, fieldHeaderLocation); return createContext(fieldHeaderLocation);
} }
if (mergeManager instanceof ProgramMultiUserMergeManager) { if (mergeManager instanceof ProgramMultiUserMergeManager) {

View file

@ -47,7 +47,7 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
Object obj = mergePanel.getActionContext(event); Object obj = mergePanel.getActionContext(event);
return new ActionContext(this, obj); return createContext(obj);
} }
void dispose() { void dispose() {
@ -55,7 +55,7 @@ public class ListingMergePanelProvider extends ComponentProviderAdapter
} }
@Override @Override
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) { public List<DockingActionIf> getPopupActions(DockingTool dt, ActionContext context) {
ListingPanel resultPanel = mergePanel.getResultPanel(); ListingPanel resultPanel = mergePanel.getResultPanel();
if (resultPanel != null) { if (resultPanel != null) {
return resultPanel.getHeaderActions(getName()); return resultPanel.getHeaderActions(getName());

View file

@ -105,7 +105,7 @@ public class BookmarkProvider extends ComponentProviderAdapter {
if (program == null) { if (program == null) {
return null; return null;
} }
return new ProgramActionContext(this, program); return new ProgramActionContext(this, program, bookmarkTable);
} }
void setGoToService(GoToService goToService) { void setGoToService(GoToService goToService) {

View file

@ -269,17 +269,17 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
FieldHeader headerPanel = listingPanel.getFieldHeader(); FieldHeader headerPanel = listingPanel.getFieldHeader();
if (headerPanel != null && source instanceof FieldHeaderComp) { if (headerPanel != null && source instanceof FieldHeaderComp) {
FieldHeaderLocation fhLoc = headerPanel.getFieldHeaderLocation(event.getPoint()); FieldHeaderLocation fhLoc = headerPanel.getFieldHeaderLocation(event.getPoint());
return new ActionContext(this, fhLoc); return createContext(fhLoc);
} }
if (otherPanel != null && otherPanel.isAncestorOf((Component) source)) { if (otherPanel != null && otherPanel.isAncestorOf((Component) source)) {
Object obj = getContextForMarginPanels(otherPanel, event); Object obj = getContextForMarginPanels(otherPanel, event);
if (obj != null) { if (obj != null) {
return new ActionContext(this, obj); return createContext(obj);
} }
return new OtherPanelContext(this, program); return new OtherPanelContext(this, program);
} }
return new ActionContext(this, getContextForMarginPanels(listingPanel, event)); return createContext(getContextForMarginPanels(listingPanel, event));
} }
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) { private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
@ -955,11 +955,11 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
} }
@Override @Override
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) { public List<DockingActionIf> getPopupActions(DockingTool dt, ActionContext context) {
if (context.getComponentProvider() == this) { if (context.getComponentProvider() == this) {
return listingPanel.getHeaderActions(getName()); return listingPanel.getHeaderActions(getName());
} }
return new ArrayList<>(); return null;
} }
//================================================================================================== //==================================================================================================

View file

@ -15,6 +15,7 @@
*/ */
package ghidra.app.plugin.core.datamgr; package ghidra.app.plugin.core.datamgr;
import java.awt.Component;
import java.awt.Point; import java.awt.Point;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
@ -333,7 +334,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
Object source = event.getSource(); Object source = event.getSource();
if (source instanceof JTextField || source instanceof JTextPane) { if (source instanceof JTextField || source instanceof JTextPane) {
return new ActionContext(this, source, source); Component component = (Component) source;
return new ActionContext(this, source, component);
} }
Point point = event.getPoint(); Point point = event.getPoint();

View file

@ -18,7 +18,6 @@ package ghidra.app.plugin.core.editor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.*; import java.io.*;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -91,10 +90,10 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
return textarea.getText(); return textarea.getText();
} }
private void initialize(TextEditorManagerPlugin plugin, String textContents) { private void initialize(TextEditorManagerPlugin p, String textContents) {
this.plugin = plugin; this.plugin = p;
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName())); setHelpLocation(new HelpLocation(p.getName(), p.getName()));
title = textFileName + (isReadOnly() ? " (Read-Only) " : ""); title = textFileName + (isReadOnly() ? " (Read-Only) " : "");
setTitle(title); setTitle(title);
@ -106,7 +105,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
addToTool(); addToTool();
setVisible(true); setVisible(true);
plugin.getTool().setStatusInfo("Press F1 for help."); p.getTool().setStatusInfo("Press F1 for help.");
createActions(); createActions();
} }
@ -278,8 +277,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
ActionContextProvider acp = e -> { ActionContextProvider acp = e -> {
ComponentProvider p = TextEditorComponentProvider.this; ComponentProvider p = TextEditorComponentProvider.this;
Object context = TextEditorComponentProvider.this; return new ActionContext(p);
return new ActionContext(p, context);
}; };
KeyBindingUtils.registerAction(textarea, saveAction, acp); KeyBindingUtils.registerAction(textarea, saveAction, acp);
@ -374,11 +372,6 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
} }
} }
@Override
public ActionContext getActionContext(MouseEvent event) {
return new ActionContext(this, this);
}
@Override @Override
public JComponent getComponent() { public JComponent getComponent() {
return scrollpane; return scrollpane;
@ -387,9 +380,10 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
/** /**
* Special JTextArea that knows how to properly handle it's key events. * Special JTextArea that knows how to properly handle it's key events.
* @see {@link #processKeyBinding(KeyStroke, KeyEvent, int, boolean)} * @see #processKeyBinding(KeyStroke, KeyEvent, int, boolean)
*/ */
private class KeyMasterTextArea extends JTextArea { private class KeyMasterTextArea extends JTextArea {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -455,7 +449,7 @@ public class TextEditorComponentProvider extends ComponentProviderAdapter {
return true; return true;
} }
return SwingUtilities.notifyAction(action, ks, e, this, e.getModifiers()); return SwingUtilities.notifyAction(action, ks, e, this, e.getModifiersEx());
} }
} }
return false; return false;

View file

@ -71,13 +71,9 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
if (event != null && event.getSource() == functionTable) {
return new ActionContext(this, functionTable); return new ActionContext(this, functionTable);
} }
return null;
}
@Override @Override
public JComponent getComponent() { public JComponent getComponent() {
return mainPanel; return mainPanel;

View file

@ -21,8 +21,7 @@ import java.util.List;
import javax.swing.*; import javax.swing.*;
import docking.*; import docking.DockingWindowManager;
import docking.action.DockingActionIf;
import docking.widgets.EmptyBorderButton; import docking.widgets.EmptyBorderButton;
import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin; import ghidra.app.plugin.core.instructionsearch.InstructionSearchPlugin;
import ghidra.app.plugin.core.instructionsearch.model.*; import ghidra.app.plugin.core.instructionsearch.model.*;
@ -95,13 +94,9 @@ public class InstructionTable extends AbstractInstructionTable {
dialog.getSearchData().registerForGuiUpdates(this); dialog.getSearchData().registerForGuiUpdates(this);
} }
/**
* Override from abstract class so we can remove the select and copy options
* (which is all of them).
*/
@Override @Override
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) { protected boolean supportsPopupActions() {
return null; return false;
} }
public InsertBytesWidget getInsertBytesWidget() { public InsertBytesWidget getInsertBytesWidget() {

View file

@ -135,17 +135,25 @@ public class PreviewTable extends AbstractInstructionTable {
* Adds custom context-sensitive menus to the table. This does NOT modify * Adds custom context-sensitive menus to the table. This does NOT modify
* any existing menus; it simply adds to them. * any existing menus; it simply adds to them.
*/ */
@Override
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
// Invoke the base class method to add default menu options. // TODO
List<DockingActionIf> list = super.getPopupActions(tool, context); // TODO
// TODO
// TODO Change the custom menu items to be real Docking Actions
// TODO
// TODO
// And now add our own. // @Override
addCustomMenuItems(list); // public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
//
return list; // // Invoke the base class method to add default menu options.
} // List<DockingActionIf> list = super.getPopupActions(tool, context);
//
// // And now add our own.
// addCustomMenuItems(list);
//
// return list;
// }
/** /**
* Replaces the contents of the preview table at the given row with the * Replaces the contents of the preview table at the given row with the

View file

@ -15,6 +15,15 @@
*/ */
package ghidra.app.plugin.core.programtree; package ghidra.app.plugin.core.programtree;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.swing.JComponent;
import javax.swing.event.ChangeEvent;
import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.app.events.ViewChangedPluginEvent; import ghidra.app.events.ViewChangedPluginEvent;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.app.services.ViewManagerService; import ghidra.app.services.ViewManagerService;
@ -25,15 +34,6 @@ import ghidra.program.util.*;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.task.*; import ghidra.util.task.*;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.swing.JComponent;
import javax.swing.event.ChangeEvent;
import docking.action.DockingAction;
/** /**
* Provides a view of the program tree. * Provides a view of the program tree.
*/ */
@ -51,9 +51,6 @@ class TreeViewProvider implements ViewProviderService {
private final static int DELAY = 500; private final static int DELAY = 500;
/**
* Constructor for TreeViewProvider.
*/
public TreeViewProvider(String treeName, final ProgramTreePlugin plugin) { public TreeViewProvider(String treeName, final ProgramTreePlugin plugin) {
treePanel = new ProgramTreePanel(treeName, plugin); treePanel = new ProgramTreePanel(treeName, plugin);
@ -72,25 +69,16 @@ class TreeViewProvider implements ViewProviderService {
}); });
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getViewComponent()
*/
@Override @Override
public JComponent getViewComponent() { public JComponent getViewComponent() {
return treePanel; return treePanel;
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getViewName()
*/
@Override @Override
public String getViewName() { public String getViewName() {
return treePanel.getTreeName(); return treePanel.getTreeName();
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#setHasFocus(JComponent, boolean)
*/
@Override @Override
public void setHasFocus(boolean hasFocus) { public void setHasFocus(boolean hasFocus) {
treePanel.setHasFocus(hasFocus); treePanel.setHasFocus(hasFocus);
@ -107,33 +95,26 @@ class TreeViewProvider implements ViewProviderService {
} }
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getActivePopupObject(MouseEvent)
*/
@Override @Override
public Object getActivePopupObject(MouseEvent event) { public Object getActivePopupObject(MouseEvent event) {
return treePanel.prepareSelectionForPopup(event); return treePanel.prepareSelectionForPopup(event);
} }
/** @Override
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getActiveObject() public ActionContext getActionContext(MouseEvent event) {
*/ return new ActionContext().setContextObject(getActivePopupObject(event));
}
@Override @Override
public Object getActiveObject() { public Object getActiveObject() {
return treePanel.getSelectedNode(); return treePanel.getSelectedNode();
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getToolBarActions()
*/
@Override @Override
public DockingAction[] getToolBarActions() { public DockingAction[] getToolBarActions() {
return plugin.getToolBarActions(); return plugin.getToolBarActions();
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#viewClosed()
*/
@Override @Override
public boolean viewClosed() { public boolean viewClosed() {
if (program == null) { if (program == null) {
@ -146,9 +127,6 @@ class TreeViewProvider implements ViewProviderService {
return false; return false;
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#viewDeleted()
*/
@Override @Override
public boolean viewDeleted() { public boolean viewDeleted() {
if (program == null) { if (program == null) {
@ -161,9 +139,6 @@ class TreeViewProvider implements ViewProviderService {
return false; return false;
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#viewRenamed(String)
*/
@Override @Override
public boolean viewRenamed(String newName) { public boolean viewRenamed(String newName) {
if (program == null) { if (program == null) {
@ -177,9 +152,6 @@ class TreeViewProvider implements ViewProviderService {
return false; return false;
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#addToView(ProgramLocation)
*/
@Override @Override
public AddressSetView addToView(ProgramLocation loc) { public AddressSetView addToView(ProgramLocation loc) {
if (program != null && loc != null && loc.getAddress() != null) { if (program != null && loc != null && loc.getAddress() != null) {
@ -192,9 +164,6 @@ class TreeViewProvider implements ViewProviderService {
} }
/**
* @see ghidra.app.plugin.core.programtree.ViewProviderService#getCurrentView()
*/
@Override @Override
public AddressSetView getCurrentView() { public AddressSetView getCurrentView() {
return view; return view;
@ -212,14 +181,14 @@ class TreeViewProvider implements ViewProviderService {
ProgramModule root = program.getListing().getRootModule(group.getTreeName()); ProgramModule root = program.getListing().getRootModule(group.getTreeName());
ProgramModule[] parents = group.getParents(); ProgramModule[] parents = group.getParents();
if (parents != null && parents.length > 0) { if (parents != null && parents.length > 0) {
for (int i = 0; i < parents.length; i++) { for (ProgramModule parent : parents) {
LinkedList<String> myList = new LinkedList<String>(list); LinkedList<String> myList = new LinkedList<>(list);
myList.addFirst(parents[i].getName()); myList.addFirst(parent.getName());
if (parents[i] == root) { if (parent == root) {
pathNameList.add(myList); pathNameList.add(myList);
} }
else { else {
setAncestorList(parents[i], myList, pathNameList); setAncestorList(parent, myList, pathNameList);
} }
} }
} }
@ -278,15 +247,12 @@ class TreeViewProvider implements ViewProviderService {
/** /**
* Set the tree selection. * Set the tree selection.
* @param groupPaths * @param paths the paths to select
*/ */
void setGroupSelection(GroupPath[] paths) { void setGroupSelection(GroupPath[] paths) {
treePanel.setGroupSelection(paths); treePanel.setGroupSelection(paths);
} }
/**
* Write group paths in the view.
*/
void writeDataState(SaveState saveState) { void writeDataState(SaveState saveState) {
GroupView currentView = treePanel.getGroupView(); GroupView currentView = treePanel.getGroupView();
String treeName = treePanel.getTreeName(); String treeName = treePanel.getTreeName();
@ -299,9 +265,6 @@ class TreeViewProvider implements ViewProviderService {
} }
} }
/**
* Read the state from save state object.
*/
void readDataState(SaveState saveState) { void readDataState(SaveState saveState) {
String treeName = treePanel.getTreeName(); String treeName = treePanel.getTreeName();
int numGroups = saveState.getInt(NUMBER_OF_GROUPS + treeName, 0); int numGroups = saveState.getInt(NUMBER_OF_GROUPS + treeName, 0);
@ -329,9 +292,6 @@ class TreeViewProvider implements ViewProviderService {
return treePanel.getDnDTree(); return treePanel.getDnDTree();
} }
/**
* Get the address set currently being viewed.
*/
AddressSet getView() { AddressSet getView() {
if (program == null) { if (program == null) {
return new AddressSet(); return new AddressSet();
@ -342,8 +302,8 @@ class TreeViewProvider implements ViewProviderService {
return set; return set;
} }
String treeName = treePanel.getTreeName(); String treeName = treePanel.getTreeName();
for (int i = 0; i < gp.length; i++) { for (GroupPath element : gp) {
Group group = gp[i].getGroup(program, treeName); Group group = element.getGroup(program, treeName);
if (group == null) { if (group == null) {
continue; continue;
} }
@ -365,8 +325,8 @@ class TreeViewProvider implements ViewProviderService {
return; return;
} }
view = getView(); view = getView();
plugin.firePluginEvent(new ViewChangedPluginEvent(plugin.getName(), plugin.firePluginEvent(
treePanel.getTreeName(), view)); new ViewChangedPluginEvent(plugin.getName(), treePanel.getTreeName(), view));
} }
/** /**
@ -379,7 +339,7 @@ class TreeViewProvider implements ViewProviderService {
if (fragment == null) { if (fragment == null) {
return; return;
} }
LinkedList<String> list = new LinkedList<String>(); LinkedList<String> list = new LinkedList<>();
list.add(fragment.getName()); list.add(fragment.getName());
Group group = fragment; Group group = fragment;
while (group != null) { while (group != null) {
@ -408,8 +368,8 @@ class TreeViewProvider implements ViewProviderService {
} }
else { else {
Group[] groups = ((ProgramModule) group).getChildren(); Group[] groups = ((ProgramModule) group).getChildren();
for (int i = 0; i < groups.length; i++) { for (Group group2 : groups) {
getAddressSet(groups[i], set); getAddressSet(group2, set);
} }
} }
} }
@ -439,11 +399,11 @@ class TreeViewProvider implements ViewProviderService {
if (fragment == null) { if (fragment == null) {
return; return;
} }
LinkedList<String> list = new LinkedList<String>(); LinkedList<String> list = new LinkedList<>();
list.add(fragment.getName()); list.add(fragment.getName());
Group group = fragment; Group group = fragment;
ArrayList<LinkedList<String>> pathNameList = new ArrayList<LinkedList<String>>(); ArrayList<LinkedList<String>> pathNameList = new ArrayList<>();
// need GroupPath for all occurrences of fragment // need GroupPath for all occurrences of fragment
setAncestorList(group, list, pathNameList); setAncestorList(group, list, pathNameList);

View file

@ -196,11 +196,13 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
if (currentProgram == null) { if (currentProgram == null) {
return null; return null;
} }
if (event != null) { if (event != null) {
return new ProgramActionContext(this, currentProgram, getActivePopupObject(event)); return new ProgramActionContext(this, currentProgram, viewPanel,
getActivePopupObject(event));
} }
return new ProgramActionContext(this, currentProgram, getFocusedContext()); return new ProgramActionContext(this, currentProgram, viewPanel, getFocusedContext());
} }
private Object getFocusedContext() { private Object getFocusedContext() {

View file

@ -15,14 +15,14 @@
*/ */
package ghidra.app.plugin.core.programtree; package ghidra.app.plugin.core.programtree;
import ghidra.app.services.ViewService;
import ghidra.framework.plugintool.ServiceInfo;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import javax.swing.JComponent; import javax.swing.JComponent;
import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
import ghidra.app.services.ViewService;
import ghidra.framework.plugintool.ServiceInfo;
/** /**
* Define methods for notification of which service becomes active; * Define methods for notification of which service becomes active;
@ -53,6 +53,13 @@ public interface ViewProviderService extends ViewService {
*/ */
public Object getActivePopupObject(MouseEvent event); public Object getActivePopupObject(MouseEvent event);
/**
* Returns the current action context for this view service
* @param event the mouse event
* @return the context
*/
public ActionContext getActionContext(MouseEvent event);
/** /**
* Get the actions that would go on a tool bar. * Get the actions that would go on a tool bar.
*/ */

View file

@ -651,7 +651,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
return new ActionContext(this, this); return createContext(this);
} }
@Override @Override

View file

@ -15,12 +15,9 @@
*/ */
package ghidra.app.plugin.debug; package ghidra.app.plugin.debug;
import java.awt.event.MouseEvent;
import javax.swing.JComponent; import javax.swing.JComponent;
import db.DBHandle; import db.DBHandle;
import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.Plugin;
@ -77,11 +74,6 @@ public class DbViewerProvider extends ComponentProviderAdapter {
} }
} }
@Override
public ActionContext getActionContext(MouseEvent event) {
return new ActionContext(this, this);
}
@Override @Override
public JComponent getComponent() { public JComponent getComponent() {
if (comp == null) { if (comp == null) {

View file

@ -219,7 +219,7 @@ public class PropertyManagerProvider extends ComponentProviderAdapter {
Rectangle rowBounds = Rectangle rowBounds =
table.getCellRect(row, PropertyManagerTableModel.PROPERTY_NAME_COLUMN, true); table.getCellRect(row, PropertyManagerTableModel.PROPERTY_NAME_COLUMN, true);
if (rowBounds.contains(event.getPoint())) { if (rowBounds.contains(event.getPoint())) {
return new ActionContext(this, rowBounds); return createContext(rowBounds);
} }
} }
} }

View file

@ -24,8 +24,7 @@ import javax.swing.*;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import docking.ActionContext; import docking.*;
import docking.ComponentProvider;
import docking.action.*; import docking.action.*;
import docking.help.Help; import docking.help.Help;
import docking.help.HelpService; import docking.help.HelpService;
@ -700,7 +699,8 @@ public class ListingCodeComparisonPanel
NextDiffAction() { NextDiffAction() {
super("Dual Listing Go To Next Area Marker", owner); super("Dual Listing Go To Next Area Marker", owner);
setEnabled(true); setEnabled(true);
setKeyBindingData(new KeyBindingData('N', InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); setKeyBindingData(new KeyBindingData('N',
DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.ALT_DOWN_MASK));
setDescription("Go to the next highlighted area."); setDescription("Go to the next highlighted area.");
setPopupMenuData(new MenuData(new String[] { "Go To Next Highlighted Area" }, setPopupMenuData(new MenuData(new String[] { "Go To Next Highlighted Area" },
NEXT_DIFF_ICON, DIFF_NAVIGATE_GROUP)); NEXT_DIFF_ICON, DIFF_NAVIGATE_GROUP));
@ -738,7 +738,8 @@ public class ListingCodeComparisonPanel
PreviousDiffAction() { PreviousDiffAction() {
super("Dual Listing Go To Previous Area Marker", owner); super("Dual Listing Go To Previous Area Marker", owner);
setEnabled(true); setEnabled(true);
setKeyBindingData(new KeyBindingData('P', InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); setKeyBindingData(new KeyBindingData('P',
DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.ALT_DOWN_MASK));
setDescription("Go to the previous highlighted area."); setDescription("Go to the previous highlighted area.");
setPopupMenuData(new MenuData(new String[] { "Go To Previous Highlighted Area" }, setPopupMenuData(new MenuData(new String[] { "Go To Previous Highlighted Area" },
PREVIOUS_DIFF_ICON, DIFF_NAVIGATE_GROUP)); PREVIOUS_DIFF_ICON, DIFF_NAVIGATE_GROUP));
@ -796,9 +797,6 @@ public class ListingCodeComparisonPanel
} }
} }
/**
* Returns true if the listings are showing the entire program.
*/
public boolean isEntireListingShowing() { public boolean isEntireListingShowing() {
return isShowingEntireListing; return isShowingEntireListing;
} }
@ -857,9 +855,6 @@ public class ListingCodeComparisonPanel
toggleHeaderAction.setSelected(show); toggleHeaderAction.setSelected(show);
} }
/**
* Returns true if the listings are being displayed side by side.
*/
public boolean isSideBySide() { public boolean isSideBySide() {
return isSideBySide; return isSideBySide;
} }
@ -2024,18 +2019,18 @@ public class ListingCodeComparisonPanel
Object leftMarginContext = getContextForMarginPanels(leftPanel, event); Object leftMarginContext = getContextForMarginPanels(leftPanel, event);
if (leftMarginContext != null) { if (leftMarginContext != null) {
return new ActionContext(provider, leftMarginContext); return new ActionContext(provider).setContextObject(leftMarginContext);
} }
Object rightMarginContext = getContextForMarginPanels(rightPanel, event); Object rightMarginContext = getContextForMarginPanels(rightPanel, event);
if (rightMarginContext != null) { if (rightMarginContext != null) {
return new ActionContext(provider, rightMarginContext); return new ActionContext(provider).setContextObject(rightMarginContext);
} }
Object source = event.getSource(); Object source = event.getSource();
if (source instanceof FieldHeaderComp) { if (source instanceof FieldHeaderComp) {
FieldHeaderLocation fieldHeaderLocation = FieldHeaderLocation fieldHeaderLocation =
leftPanel.getFieldHeader().getFieldHeaderLocation(event.getPoint()); leftPanel.getFieldHeader().getFieldHeaderLocation(event.getPoint());
return new ActionContext(provider, fieldHeaderLocation); return new ActionContext(provider).setContextObject(fieldHeaderLocation);
} }
Navigatable focusedNavigatable = dualListingPanel.getFocusedNavigatable(); Navigatable focusedNavigatable = dualListingPanel.getFocusedNavigatable();
@ -2043,7 +2038,7 @@ public class ListingCodeComparisonPanel
new DualListingActionContext(provider, focusedNavigatable); new DualListingActionContext(provider, focusedNavigatable);
myActionContext.setContextObject(this); myActionContext.setContextObject(this);
myActionContext.setCodeComparisonPanel(this); myActionContext.setCodeComparisonPanel(this);
myActionContext.setSource(source); myActionContext.setSourceObject(source);
return myActionContext; return myActionContext;
} }
@ -2607,13 +2602,13 @@ public class ListingCodeComparisonPanel
// Are we on a marker margin of the left listing? Return that margin's context. // Are we on a marker margin of the left listing? Return that margin's context.
Object sourceMarginContextObject = getContextObjectForMarginPanels(sourcePanel, event); Object sourceMarginContextObject = getContextObjectForMarginPanels(sourcePanel, event);
if (sourceMarginContextObject != null) { if (sourceMarginContextObject != null) {
return new ActionContext(provider, sourceMarginContextObject); return new ActionContext(provider).setContextObject(sourceMarginContextObject);
} }
// Are we on a marker margin of the right listing? Return that margin's context. // Are we on a marker margin of the right listing? Return that margin's context.
Object destinationMarginContextObject = Object destinationMarginContextObject =
getContextObjectForMarginPanels(destinationPanel, event); getContextObjectForMarginPanels(destinationPanel, event);
if (destinationMarginContextObject != null) { if (destinationMarginContextObject != null) {
return new ActionContext(provider, destinationMarginContextObject); return new ActionContext(provider).setContextObject(destinationMarginContextObject);
} }
// If the action is on the Field Header of the left listing panel return an // If the action is on the Field Header of the left listing panel return an
@ -2621,7 +2616,7 @@ public class ListingCodeComparisonPanel
if (sourceComponent instanceof FieldHeaderComp) { if (sourceComponent instanceof FieldHeaderComp) {
FieldHeaderLocation fieldHeaderLocation = FieldHeaderLocation fieldHeaderLocation =
sourcePanel.getFieldHeader().getFieldHeaderLocation(event.getPoint()); sourcePanel.getFieldHeader().getFieldHeaderLocation(event.getPoint());
return new ActionContext(provider, fieldHeaderLocation); return new ActionContext(provider).setContextObject(fieldHeaderLocation);
} }
} }
return null; return null;

View file

@ -259,7 +259,7 @@ public class OpenVersionedFileDialog extends DataTreeDialog {
return context; return context;
} }
ActionContext actionContext = new ActionContext(null, event.getSource(), this); ActionContext actionContext = new ActionContext(null, this, event.getComponent());
actionContext.setMouseEvent(event); actionContext.setMouseEvent(event);
return actionContext; return actionContext;

View file

@ -35,7 +35,7 @@ public class FSBActionContext extends ActionContext {
* @param gTree {@link FileSystemBrowserPlugin} provider tree. * @param gTree {@link FileSystemBrowserPlugin} provider tree.
*/ */
public FSBActionContext(ComponentProvider provider, Object contextObject, GTree gTree) { public FSBActionContext(ComponentProvider provider, Object contextObject, GTree gTree) {
super(provider, contextObject); super(provider, contextObject, gTree);
this.gTree = gTree; this.gTree = gTree;
} }

View file

@ -22,7 +22,6 @@ import java.util.Set;
import org.junit.*; import org.junit.*;
import docking.ActionContext;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import ghidra.app.plugin.core.programtree.ProgramTreePlugin; import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
import ghidra.app.plugin.core.programtree.ViewProviderService; import ghidra.app.plugin.core.programtree.ViewProviderService;
@ -99,7 +98,7 @@ public class ModuleAlgorithmPluginTest extends AbstractGhidraHeadedIntegrationTe
getContextObject(vps); getContextObject(vps);
performAction(CollectionUtils.any(actions), new ActionContext(null, context), true); performAction(CollectionUtils.any(actions), createContext(context), true);
waitForTasks(); waitForTasks();
program.flushEvents(); program.flushEvents();

View file

@ -433,7 +433,7 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest {
Address address = bookmark.getAddress(); Address address = bookmark.getAddress();
DeleteBookmarkAction action = new DeleteBookmarkAction(plugin, bookmark, true); DeleteBookmarkAction action = new DeleteBookmarkAction(plugin, bookmark, true);
MarkerLocation markerLocation = new MarkerLocation(null, address, 0, 0); MarkerLocation markerLocation = new MarkerLocation(null, address, 0, 0);
performAction(action, new ActionContext(null, markerLocation), true); performAction(action, createContext(markerLocation), true);
list = getBookmarks(program.getBookmarkManager()); list = getBookmarks(program.getBookmarkManager());
assertFalse(list.contains(bookmark)); assertFalse(list.contains(bookmark));
@ -441,7 +441,7 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest {
address = bookmark.getAddress(); address = bookmark.getAddress();
action = new DeleteBookmarkAction(plugin, bookmark, false); action = new DeleteBookmarkAction(plugin, bookmark, false);
markerLocation = new MarkerLocation(null, address, 0, 0); markerLocation = new MarkerLocation(null, address, 0, 0);
performAction(action, new ActionContext(null, markerLocation), true); performAction(action, createContext(markerLocation), true);
list = getBookmarks(program.getBookmarkManager()); list = getBookmarks(program.getBookmarkManager());
assertFalse(list.contains(bookmark)); assertFalse(list.contains(bookmark));
} }
@ -543,8 +543,8 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest {
CreateStructureCmd cmd = new CreateStructureCmd(addr("0100b6db"), 20); CreateStructureCmd cmd = new CreateStructureCmd(addr("0100b6db"), 20);
applyCmd(program, cmd); applyCmd(program, cmd);
List<DockingActionIf> actions = runSwing(() -> plugin.getPopupActions( List<DockingActionIf> actions = runSwing(() -> plugin.getPopupActions(null,
null, new ActionContext(null, new MarkerLocation(null, addr("0100b6db"), 0, 0)))); createContext(new MarkerLocation(null, addr("0100b6db"), 0, 0))));
assertEquals(10, actions.size()); assertEquals(10, actions.size());
} }

View file

@ -131,7 +131,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
DockingAction headerAction = getHeaderAction("Remove All Fields"); DockingAction headerAction = getHeaderAction("Remove All Fields");
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factorys[0], 0, 0); FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factorys[0], 0, 0);
ActionContext context = new ActionContext(provider, loc); ActionContext context = createContext(provider, loc);
performAction(headerAction, context, false); performAction(headerAction, context, false);
pressContinueOnResetFormatDialog("Remove All Fields?"); pressContinueOnResetFormatDialog("Remove All Fields?");
@ -151,7 +151,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
selectHeaderField(factories[0]); selectHeaderField(factories[0]);
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[0], 0, 0); FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[0], 0, 0);
ActionContext context = new ActionContext(provider, loc); ActionContext context = createContext(provider, loc);
DockingAction headerAction = getHeaderAction("Add Spacer Field"); DockingAction headerAction = getHeaderAction("Add Spacer Field");
performAction(headerAction, context, true); performAction(headerAction, context, true);
@ -173,7 +173,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
selectHeaderField(factories[0]); selectHeaderField(factories[0]);
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[0], 0, 0); FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[0], 0, 0);
ActionContext context = new ActionContext(provider, loc); ActionContext context = createContext(provider, loc);
DockingAction headerAction = getHeaderAction("SetTextAction"); DockingAction headerAction = getHeaderAction("SetTextAction");
performAction(headerAction, context, false); performAction(headerAction, context, false);
@ -200,7 +200,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(factories[1].isEnabled()); assertTrue(factories[1].isEnabled());
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 0); FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 0);
ActionContext context = new ActionContext(provider, loc); ActionContext context = createContext(provider, loc);
DockingAction headerAction = getHeaderAction("Disable Field"); DockingAction headerAction = getHeaderAction("Disable Field");
performAction(headerAction, context, true); performAction(headerAction, context, true);
@ -222,7 +222,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(factories[1] instanceof FunctionSignatureFieldFactory); assertTrue(factories[1] instanceof FunctionSignatureFieldFactory);
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 1); FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 1);
ActionContext context = new ActionContext(provider, loc); ActionContext context = createContext(provider, loc);
DockingAction headerAction = getHeaderAction("Remove Field"); DockingAction headerAction = getHeaderAction("Remove Field");
performAction(headerAction, context, true); performAction(headerAction, context, true);
@ -242,7 +242,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
functionFormat.removeAllFactories(); functionFormat.removeAllFactories();
FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 1); FieldHeaderLocation loc = new FieldHeaderLocation(functionFormat, factories[1], 0, 1);
ActionContext context = new ActionContext(provider, loc); ActionContext context = createContext(provider, loc);
DockingAction headerAction = getHeaderAction("Add All Field"); DockingAction headerAction = getHeaderAction("Add All Field");
performAction(headerAction, context, true); performAction(headerAction, context, true);
@ -272,8 +272,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
} }
private void enterTextIntoDialog(String input) { private void enterTextIntoDialog(String input) {
InputDialog dialog = InputDialog dialog = waitForDialogComponent(InputDialog.class);
waitForDialogComponent(null, InputDialog.class, DEFAULT_WINDOW_TIMEOUT);
assertNotNull("Never found the spacer text input dialog", dialog); assertNotNull("Never found the spacer text input dialog", dialog);
JTextComponent textField = JTextComponent textField =
(JTextComponent) findComponentByName(dialog, "input.dialog.text.field.0"); (JTextComponent) findComponentByName(dialog, "input.dialog.text.field.0");
@ -291,7 +290,7 @@ public class HeaderActionsTest extends AbstractGhidraHeadedIntegrationTest {
} }
private void pressContinueOnResetFormatDialog(String title) { private void pressContinueOnResetFormatDialog(String title) {
Window window = waitForWindow(title, DEFAULT_WINDOW_TIMEOUT); Window window = waitForWindow(title);
assertNotNull("Never found the dialog: " + title, window); assertNotNull("Never found the dialog: " + title, window);
pressButtonByText(window, "Continue"); pressButtonByText(window, "Continue");
waitForSwing(); waitForSwing();

View file

@ -275,14 +275,14 @@ public class HeaderTest extends AbstractGhidraHeadedIntegrationTest {
public void testInsertDeleteRow() { public void testInsertDeleteRow() {
FieldFormatModel model = header.getHeaderTab().getModel(); FieldFormatModel model = header.getHeaderTab().getModel();
InsertRowAction act = new InsertRowAction("Test", header); InsertRowAction act = new InsertRowAction("Test", header);
act.isEnabledForContext( act.isEnabledForContext(new ActionContext(cb.getProvider()).setContextObject(
new ActionContext(null, new FieldHeaderLocation(model, null, 0, 0))); new FieldHeaderLocation(model, null, 0, 0)));
performAction(act, true); performAction(act, true);
assertEquals(8, model.getNumRows()); assertEquals(8, model.getNumRows());
assertEquals(0, model.getNumFactorys(0)); assertEquals(0, model.getNumFactorys(0));
RemoveRowAction act2 = new RemoveRowAction("Test", header); RemoveRowAction act2 = new RemoveRowAction("Test", header);
act2.isEnabledForContext( act2.isEnabledForContext(new ActionContext(cb.getProvider()).setContextObject(
new ActionContext(null, new FieldHeaderLocation(model, null, 0, 0))); new FieldHeaderLocation(model, null, 0, 0)));
performAction(act2, true); performAction(act2, true);
assertEquals(7, model.getNumRows()); assertEquals(7, model.getNumRows());
assertEquals(2, model.getNumFactorys(0)); assertEquals(2, model.getNumFactorys(0));
@ -294,8 +294,8 @@ public class HeaderTest extends AbstractGhidraHeadedIntegrationTest {
ListingField bf = cb.getCurrentField(); ListingField bf = cb.getCurrentField();
int startX = bf.getStartX(); int startX = bf.getStartX();
InsertRowAction act = new InsertRowAction("Test", header); InsertRowAction act = new InsertRowAction("Test", header);
act.isEnabledForContext( act.isEnabledForContext(new ActionContext(cb.getProvider()).setContextObject(
new ActionContext(null, new FieldHeaderLocation(model, null, 0, 0))); new FieldHeaderLocation(model, null, 0, 0)));
performAction(act, true); performAction(act, true);
int width = bf.getWidth(); int width = bf.getWidth();
int dragX = startX + width / 2; int dragX = startX + width / 2;

View file

@ -109,21 +109,21 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
ViewProviderService vps = vmService.getCurrentViewProvider(); ViewProviderService vps = vmService.getCurrentViewProvider();
Object context = vps.getActivePopupObject(null); Object context = vps.getActivePopupObject(null);
assertTrue(!renameAction.isEnabledForContext(new ActionContext(null, context)));
assertTrue(!labelAction.isEnabledForContext(new ActionContext(null, context))); assertTrue(!renameAction.isEnabledForContext(createContext(context)));
assertTrue(!labelAction.isEnabledForContext(createContext(context)));
gps = new GroupPath[1]; gps = new GroupPath[1];
gps[0] = new GroupPath(new String[] { root.getName(), "DLLs", "USER32.DLL" }); gps[0] = new GroupPath(new String[] { root.getName(), "DLLs", "USER32.DLL" });
setSelection(gps); setSelection(gps);
context = vps.getActivePopupObject(null); context = vps.getActivePopupObject(null);
assertTrue(renameAction.isEnabledForContext(new ActionContext(null, context))); assertTrue(renameAction.isEnabledForContext(createContext(context)));
assertTrue(!labelAction.isEnabledForContext(new ActionContext(null, context))); assertTrue(!labelAction.isEnabledForContext(createContext(context)));
// fire Label program location // fire Label program location
Address addr = getAddr(0x10033f6); Address addr = getAddr(0x10033f6);
LabelFieldLocation loc = LabelFieldLocation loc = new LabelFieldLocation(program, addr, "SUB_010033f6");
new LabelFieldLocation(program, addr, "SUB_010033f6");
tool.firePluginEvent(new ProgramLocationPluginEvent("test", loc, program)); tool.firePluginEvent(new ProgramLocationPluginEvent("test", loc, program));
ActionContext actionContext = cb.getProvider().getActionContext(null); ActionContext actionContext = cb.getProvider().getActionContext(null);
@ -147,7 +147,7 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
ViewManagerService vmService = tool.getService(ViewManagerService.class); ViewManagerService vmService = tool.getService(ViewManagerService.class);
ViewProviderService vps = vmService.getCurrentViewProvider(); ViewProviderService vps = vmService.getCurrentViewProvider();
Object context = vps.getActivePopupObject(null); Object context = vps.getActivePopupObject(null);
performAction(renameAction, new ActionContext(null, context), true); performAction(renameAction, createContext(context), true);
program.flushEvents(); program.flushEvents();
assertNotNull(program.getListing().getFragment("Main Tree", s.getName())); assertNotNull(program.getListing().getFragment("Main Tree", s.getName()));
@ -166,12 +166,8 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
new LabelFieldLocation(program, addr, null, "SUB_010033f6", null, 0, 0); new LabelFieldLocation(program, addr, null, "SUB_010033f6", null, 0, 0);
cb.goTo(loc); cb.goTo(loc);
SwingUtilities.invokeAndWait(new Runnable() { SwingUtilities.invokeAndWait(
@Override () -> labelAction.actionPerformed(cb.getProvider().getActionContext(null)));
public void run() {
labelAction.actionPerformed(cb.getProvider().getActionContext(null));
}
});
program.flushEvents(); program.flushEvents();
assertNull(program.getListing().getFragment("SUB_010033f6", origName)); assertNull(program.getListing().getFragment("SUB_010033f6", origName));
assertEquals("SUB_010033f6", frag.getName()); assertEquals("SUB_010033f6", frag.getName());
@ -182,22 +178,12 @@ public class AutoRenamePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
private void setViewToMainTree() { private void setViewToMainTree() {
runSwing(new Runnable() { runSwing(() -> service.setViewedTree("Main Tree"));
@Override
public void run() {
service.setViewedTree("Main Tree");
}
});
} }
private void setSelection(final GroupPath[] gps) { private void setSelection(final GroupPath[] gps) {
runSwing(new Runnable() { runSwing(() -> service.setGroupSelection(gps));
@Override
public void run() {
service.setGroupSelection(gps);
}
});
} }
} }

View file

@ -22,7 +22,6 @@ import java.util.*;
import org.junit.*; import org.junit.*;
import docking.ActionContext;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import ghidra.app.plugin.core.programtree.ProgramTreePlugin; import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
import ghidra.app.plugin.core.programtree.ViewProviderService; import ghidra.app.plugin.core.programtree.ViewProviderService;
@ -113,7 +112,8 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
Object context = vps.getActivePopupObject(null); Object context = vps.getActivePopupObject(null);
for (DockingActionIf action : actions) { for (DockingActionIf action : actions) {
assertTrue(action.isAddToPopup(new ActionContext(null, context)));
assertTrue(action.isAddToPopup(vps.getActionContext(null)));
} }
gps = new GroupPath[1]; gps = new GroupPath[1];
@ -122,7 +122,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
context = vps.getActivePopupObject(null); context = vps.getActivePopupObject(null);
for (DockingActionIf action : actions) { for (DockingActionIf action : actions) {
assertTrue(!action.isAddToPopup(new ActionContext(null, context))); assertTrue(!action.isAddToPopup(vps.getActionContext(null)));
} }
} }
@ -159,11 +159,10 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
ViewManagerService vmService = tool.getService(ViewManagerService.class); ViewManagerService vmService = tool.getService(ViewManagerService.class);
ViewProviderService vps = vmService.getCurrentViewProvider(); ViewProviderService vps = vmService.getCurrentViewProvider();
Object context = vps.getActivePopupObject(null);
for (DockingActionIf action : actions) { for (DockingActionIf action : actions) {
if (action.getName().indexOf("Name") > 0) { if (action.getName().indexOf("Name") > 0) {
action.actionPerformed(new ActionContext(null, context));
action.actionPerformed(vps.getActionContext(null));
break; break;
} }
} }
@ -205,7 +204,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
for (DockingActionIf action : actions) { for (DockingActionIf action : actions) {
if (action.getName().indexOf("Address") > 0) { if (action.getName().indexOf("Address") > 0) {
action.actionPerformed(new ActionContext(null, context)); action.actionPerformed(vps.getActionContext(null));
break; break;
} }
} }
@ -232,7 +231,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
Object context = vps.getActivePopupObject(null); Object context = vps.getActivePopupObject(null);
for (DockingActionIf action : actions) { for (DockingActionIf action : actions) {
assertTrue(!action.isAddToPopup(new ActionContext(null, context))); assertTrue(!action.isAddToPopup(vps.getActionContext(null)));
} }
} }

View file

@ -152,7 +152,7 @@ public class ProviderNavigationPluginTest extends AbstractProgramBasedTest {
} }
private void forceActivate(ComponentProvider provider) { private void forceActivate(ComponentProvider provider) {
ActionContext context = new ActionContext(provider, provider); ActionContext context = new ActionContext(provider);
for (DockingContextListener l : testContextListeners) { for (DockingContextListener l : testContextListeners) {
l.contextChanged(context); l.contextChanged(context);
} }

View file

@ -67,4 +67,9 @@ public class DummyToolActions implements DockingToolActions {
public void removeActions(ComponentProvider provider) { public void removeActions(ComponentProvider provider) {
// stub // stub
} }
@Override
public boolean containsAction(DockingActionIf action) {
return false;
}
} }

View file

@ -528,13 +528,15 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
@Override @Override
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) { public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
Object sourceObject = null;
Component component = null;
if (event != null) { if (event != null) {
sourceObject = event.getSource(); component = event.getComponent();
} }
CDisplayPanel focusedDecompilerPanel = getFocusedDecompilerPanel(); CDisplayPanel focusedDecompilerPanel = getFocusedDecompilerPanel();
DualDecompilerActionContext dualDecompContext = DualDecompilerActionContext dualDecompContext =
new DualDecompilerActionContext(provider, focusedDecompilerPanel, sourceObject); new DualDecompilerActionContext(provider, focusedDecompilerPanel, component);
dualDecompContext.setCodeComparisonPanel(this); dualDecompContext.setCodeComparisonPanel(this);
return dualDecompContext; return dualDecompContext;
} }

View file

@ -15,6 +15,8 @@
*/ */
package ghidra.app.decompiler.component; package ghidra.app.decompiler.component;
import java.awt.Component;
import docking.ActionContext; import docking.ActionContext;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator; import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
@ -34,11 +36,11 @@ public class DualDecompilerActionContext extends ActionContext
* Creates an action context for a dual decompiler panel. * Creates an action context for a dual decompiler panel.
* @param provider the provider for this context * @param provider the provider for this context
* @param cPanel the decompiler panel associated with this context * @param cPanel the decompiler panel associated with this context
* @param sourceObject the source of the action * @param source the source of the action
*/ */
public DualDecompilerActionContext(ComponentProvider provider, CDisplayPanel cPanel, public DualDecompilerActionContext(ComponentProvider provider, CDisplayPanel cPanel,
Object sourceObject) { Component source) {
super(provider, cPanel, sourceObject); super(provider, cPanel, source);
} }
/** /**

View file

@ -122,7 +122,7 @@ public class SetFormatDialogComponentProvider extends DialogComponentProvider {
FieldHeader headerPanel = listingPanel.getFieldHeader(); FieldHeader headerPanel = listingPanel.getFieldHeader();
if (headerPanel != null && headerPanel.isAncestorOf(event.getComponent())) { if (headerPanel != null && headerPanel.isAncestorOf(event.getComponent())) {
FieldHeaderLocation fhLoc = headerPanel.getFieldHeaderLocation(event.getPoint()); FieldHeaderLocation fhLoc = headerPanel.getFieldHeaderLocation(event.getPoint());
return new ActionContext(null, fhLoc); return new ActionContext().setContextObject(fhLoc);
} }
return null; return null;
} }

View file

@ -624,7 +624,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
assertNotNull(action); assertNotNull(action);
FieldHeaderLocation fhLoc = createFieldHeaderLocation(provider); FieldHeaderLocation fhLoc = createFieldHeaderLocation(provider);
ActionContext context = new ActionContext(null, fhLoc); ActionContext context = createContext(fhLoc);
performAction(action, context, true); performAction(action, context, true);
waitForConditionWithoutFailing(() -> fieldIsVisible(provider, actionName)); waitForConditionWithoutFailing(() -> fieldIsVisible(provider, actionName));
@ -728,7 +728,7 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
assertNotNull(action); assertNotNull(action);
FieldHeaderLocation fhLoc = createFieldHeaderLocation(provider); FieldHeaderLocation fhLoc = createFieldHeaderLocation(provider);
ActionContext context = new ActionContext(null, fhLoc); ActionContext context = createContext(fhLoc);
performAction(action, context, false); performAction(action, context, false);
Window dialog = waitForWindow("Reset All Formats?"); Window dialog = waitForWindow("Reset All Formats?");

View file

@ -29,6 +29,6 @@ public class FcgActionContext extends ActionContext {
} }
public FcgActionContext(FcgProvider provider, Object contextObject) { public FcgActionContext(FcgProvider provider, Object contextObject) {
super(provider, contextObject); super(provider, contextObject, provider.getComponent());
} }
} }

View file

@ -277,7 +277,7 @@ public class VTFunctionAssociationProvider extends ComponentProviderAdapter
getExistingMatch(sourceFunction, destinationFunction)); getExistingMatch(sourceFunction, destinationFunction));
vtListingContext.setCodeComparisonPanel(dualListingPanel); vtListingContext.setCodeComparisonPanel(dualListingPanel);
vtListingContext.setContextObject(dualListingPanel); vtListingContext.setContextObject(dualListingPanel);
vtListingContext.setSource(source); vtListingContext.setSourceObject(source);
return vtListingContext; return vtListingContext;
} }

View file

@ -493,7 +493,7 @@ public class VTMarkupItemsTableProvider extends ComponentProviderAdapter
new VTListingContext(this, vtListingNavigator); new VTListingContext(this, vtListingNavigator);
vtListingContext.setCodeComparisonPanel(dualListingPanel); vtListingContext.setCodeComparisonPanel(dualListingPanel);
vtListingContext.setContextObject(dualListingPanel); vtListingContext.setContextObject(dualListingPanel);
vtListingContext.setSource(source); vtListingContext.setSourceObject(source);
return vtListingContext; return vtListingContext;
} }
} }

View file

@ -15,48 +15,128 @@
*/ */
package docking; package docking;
import java.awt.Component;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import docking.action.DockingActionIf;
/** /**
* ComponentProviders are required to return Objects of this type in their getActionContext() * Action context is a class that contains state information that is given to
* {@link DockingActionIf}s for them to decide if they are enabled for a given user action. User
* actions are toolbar button presses, menu bar item presses and popup menu item presses. As
* the user changes focus in the system, all actions are queried with the current context. Thus,
* toolbar buttons and menu items will enable and disable as the user interacts with the system.
* When the user executes an action, the current context will be passed to the backing
* {@link DockingActionIf}. Ultimately, context serves to control action enablement and to
* allow plugins to share state with actions without having to store that state information
* in class fields of the plugin.
*
* <p>ComponentProviders are required to return Objects of this type in their getActionContext()
* methods. Generally, ComponentProviders have two ways to use this class. They can either create * methods. Generally, ComponentProviders have two ways to use this class. They can either create
* an ActionContext instance and pass in a contextObject that will be useful to its actions or, * an ActionContext instance and pass in a contextObject that will be useful to its actions or,
* subclass the ActionContext object to include specific methods to provide the information that * subclass the ActionContext object to include specific methods to provide the information that
* actions will require. * actions will require.
*
* <p>The data contained by this class has meaning that can change relative to the code that
* created it. The intended purpose for the fields of this class is as follows:
* <ul>
* <li><b>provider</b> - the component provider to which this context belongs; the provider that
* contains the component that is the source of the user action
* </li>
* <li><b>contextObject</b> - client-defined data object. This allows clients to save any
* information desired to be used when the action is performed.
* </li>
* <li><b>sourceObject</b> - when checking enablement, this is the item that was clicked or
* activated; when performing an action this is either the active
* object or the component that was clicked. This value may change
* between the check for
* {@link DockingActionIf#isEnabledForContext(ActionContext) enablement}
* and {@link DockingActionIf#actionPerformed(ActionContext) execution}.
* </li>
* <li><b>sourceComponent</b> - this value is the component that is the source of the current
* context. Whereas the <code>sourceObject</code> is the actual
* clicked item, this value is the focused/active component and
* will not change between
* {@link DockingActionIf#isEnabledForContext(ActionContext) enablement}
* and {@link DockingActionIf#actionPerformed(ActionContext) execution}.
* </li>
* <li><b>mouseEvent</b> - the mouse event that triggered the action; null if the action was
* triggered by a key binding.
* </li>
* </ul>
*
* <p>Ultimately, clients can pass any values they wish for the fields of this class, even if
* that changes the meaning of the fields outlined above.
*/ */
public class ActionContext { public class ActionContext {
private ComponentProvider provider; private ComponentProvider provider;
private MouseEvent mouseEvent;
private Object contextObject; private Object contextObject;
private Object sourceObject; private Object sourceObject;
private MouseEvent mouseEvent;
// Note: the setting of this object is delayed. This allows clients to build-up the state
// of this context. This object will be set when getSourceComponent() is called if it
// has not already been set.
private Component sourceComponent;
public ActionContext() { public ActionContext() {
this(null, null); this(null, null);
} }
public ActionContext(ComponentProvider cp) {
this(cp, null);
}
/** /**
* Basic constructor for ActionContext * Basic constructor for ActionContext
* @param provider the ComponentProvider that generated this context. * @param provider the ComponentProvider that generated this context.
* @param contextObject an optional contextObject that the ComponentProvider can provide * @param sourceComponent an optional source object; this is intended to be the component that
* to the action. * is the source of the context, usually the focused component
*/ */
public ActionContext(ComponentProvider provider, Object contextObject) { public ActionContext(ComponentProvider provider, Component sourceComponent) {
this.provider = provider; this(provider, sourceComponent, sourceComponent);
this.contextObject = contextObject;
} }
/** /**
* Constructor * Constructor
* *
* @param provider the ComponentProvider that generated this context. * @param provider the ComponentProvider that generated this context.
* @param contextObject an optional contextObject that the ComponentProvider can provide * @param contextObject an optional contextObject that the ComponentProvider can provide; this
* @param sourceObject an optional source object; this can be anything that actions wish to * can be anything that actions wish to later retrieve
* later retrieve * @param sourceComponent an optional source object; this is intended to be the component that
* is the source of the context, usually the focused component
*/ */
public ActionContext(ComponentProvider provider, Object contextObject, Object sourceObject) { public ActionContext(ComponentProvider provider, Object contextObject,
this(provider, contextObject); Component sourceComponent) {
this.sourceObject = sourceObject; this.provider = provider;
this.contextObject = contextObject;
this.sourceObject = sourceComponent;
this.sourceComponent = sourceComponent;
}
private void lazyDeriveSourceComponent() {
if (sourceComponent != null) {
// Do not allow this to change once set. This prevents the value from getting changed
// when the user clicks a menu item.
return;
}
// check this in order of preference
if (sourceObject instanceof Component) {
sourceComponent = (Component) sourceObject;
return;
}
if (mouseEvent != null) {
sourceComponent = mouseEvent.getComponent();
return;
}
if (contextObject instanceof Component) {
sourceComponent = (Component) contextObject;
}
} }
/** /**
@ -83,9 +163,11 @@ public class ActionContext {
* choosing that can be provided for later retrieval. * choosing that can be provided for later retrieval.
* *
* @param contextObject Sets the context object for this context. * @param contextObject Sets the context object for this context.
* @return this context
*/ */
public void setContextObject(Object contextObject) { public ActionContext setContextObject(Object contextObject) {
this.contextObject = contextObject; this.contextObject = contextObject;
return this;
} }
/** /**
@ -98,22 +180,28 @@ public class ActionContext {
/** /**
* Sets the sourceObject for this ActionContext. This method is used internally by the * Sets the sourceObject for this ActionContext. This method is used internally by the
* DockingWindowManager. ComponentProvider and action developers should * DockingWindowManager. ComponentProvider and action developers should only use this
* only use this method for testing. * method for testing.
*
* @param sourceObject the source object * @param sourceObject the source object
* @return this context
*/ */
public void setSource(Object sourceObject) { public ActionContext setSourceObject(Object sourceObject) {
this.sourceObject = sourceObject; this.sourceObject = sourceObject;
return this;
} }
/** /**
* Updates the context's mouse event. Contexts that are based upon key events will have no * Updates the context's mouse event. Contexts that are based upon key events will have no
* mouse event. * mouse event. This method is really for the framework to use. Client calls to this
* method will be overridden by the framework when menu items are clicked.
* *
* @param e the event that triggered this context. * @param e the event that triggered this context.
* @return this context
*/ */
public void setMouseEvent(MouseEvent e) { public ActionContext setMouseEvent(MouseEvent e) {
this.mouseEvent = e; this.mouseEvent = e;
return this;
} }
/** /**
@ -126,6 +214,17 @@ public class ActionContext {
return mouseEvent; return mouseEvent;
} }
/**
* Returns the component that is the target of this context. This value should not change
* whether the context is triggered by a key binding or mouse event.
*
* @return the component; may be null
*/
public Component getSourceComponent() {
lazyDeriveSourceComponent();
return sourceComponent;
}
@Override @Override
public String toString() { public String toString() {
@ -134,6 +233,7 @@ public class ActionContext {
"\tprovider: " + provider + ",\n" + "\tprovider: " + provider + ",\n" +
"\tcontextObject: " + contextObject + ",\n" + "\tcontextObject: " + contextObject + ",\n" +
"\tsourceObject: " + sourceObject + ",\n" + "\tsourceObject: " + sourceObject + ",\n" +
"\tsourceComponent: " + sourceComponent + ",\n" +
"\tmouseEvent: " + mouseEvent + "\n" + "\tmouseEvent: " + mouseEvent + "\n" +
"}"; "}";
//@formatter:on //@formatter:on

View file

@ -27,6 +27,8 @@ public interface ComponentLoadedListener {
* Called when the component is made displayable * Called when the component is made displayable
* *
* @param windowManager the window manager associated with the loaded component * @param windowManager the window manager associated with the loaded component
* @param provider the provider that is the parent of the given component; null if this
* component is not the child of a component provider
*/ */
public void componentLoaded(DockingWindowManager windowManager); public void componentLoaded(DockingWindowManager windowManager, ComponentProvider provider);
} }

View file

@ -387,7 +387,28 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
*/ */
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
return new ActionContext(this, getComponent()); Component c = getComponent();
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component focusedComponent = kfm.getFocusOwner();
if (focusedComponent != null && SwingUtilities.isDescendingFrom(focusedComponent, c)) {
c = focusedComponent;
}
return createContext(c, null);
}
// TODO
protected ActionContext createContext() {
return new ActionContext(this);
}
// TODO
protected ActionContext createContext(Object payload) {
return new ActionContext(this).setContextObject(payload);
}
// TODO
protected ActionContext createContext(Component source, Object payload) {
return new ActionContext(this, source).setContextObject(payload);
} }
/** /**

View file

@ -1127,7 +1127,19 @@ public class DialogComponentProvider
*/ */
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
return new ActionContext(null, null);
Component c = getComponent();
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component focusedComponent = kfm.getFocusOwner();
if (focusedComponent != null && SwingUtilities.isDescendingFrom(focusedComponent, c)) {
c = focusedComponent;
}
if (event == null) {
return new ActionContext(null, c);
}
return new ActionContext(null, c).setSourceObject(event.getSource());
} }
/** /**

View file

@ -56,7 +56,7 @@ public class DialogComponentProviderPopupActionManager {
// If the source is null, must set it or we won't have // If the source is null, must set it or we won't have
// any popups shown. // any popups shown.
if (actionContext.getSourceObject() == null) { if (actionContext.getSourceObject() == null) {
actionContext.setSource(e.getSource()); actionContext.setSourceObject(e.getSource());
} }
MenuHandler popupMenuHandler = new PopupMenuHandler(actionContext); MenuHandler popupMenuHandler = new PopupMenuHandler(actionContext);
@ -152,7 +152,7 @@ public class DialogComponentProviderPopupActionManager {
public void processMenuAction(final DockingActionIf action, final ActionEvent event) { public void processMenuAction(final DockingActionIf action, final ActionEvent event) {
DockingWindowManager.clearMouseOverHelp(); DockingWindowManager.clearMouseOverHelp();
actionContext.setSource(event.getSource()); actionContext.setSourceObject(event.getSource());
// this gives the UI some time to repaint before executing the action // this gives the UI some time to repaint before executing the action
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {

View file

@ -214,6 +214,11 @@ public class DockingActionProxy
dockingAction.setUnvalidatedKeyBindingData(newKeyBindingData); dockingAction.setUnvalidatedKeyBindingData(newKeyBindingData);
} }
@Override
public void dispose() {
dockingAction.dispose();
}
@Override @Override
public String getHelpInfo() { public String getHelpInfo() {
return dockingAction.getHelpInfo(); return dockingAction.getHelpInfo();

View file

@ -62,7 +62,7 @@ public abstract class DockingKeyBindingAction extends AbstractAction {
tool.setStatusInfo(""); tool.setStatusInfo("");
ComponentProvider provider = tool.getActiveComponentProvider(); ComponentProvider provider = tool.getActiveComponentProvider();
ActionContext context = getLocalContext(provider); ActionContext context = getLocalContext(provider);
context.setSource(e.getSource()); context.setSourceObject(e.getSource());
docakbleAction.actionPerformed(context); docakbleAction.actionPerformed(context);
} }

View file

@ -73,7 +73,9 @@ public interface DockingTool {
public void addComponentProvider(ComponentProvider componentProvider, boolean show); public void addComponentProvider(ComponentProvider componentProvider, boolean show);
/** /**
* Removes the given ComponentProvider from the tool * Removes the given ComponentProvider from the tool. When a provider has been removed
* from the tool it is considered disposed and should not be reused.
*
* @param componentProvider the provider to remove from the tool * @param componentProvider the provider to remove from the tool
*/ */
public void removeComponentProvider(ComponentProvider componentProvider); public void removeComponentProvider(ComponentProvider componentProvider);
@ -125,7 +127,8 @@ public interface DockingTool {
public void addAction(DockingActionIf action); public void addAction(DockingActionIf action);
/** /**
* Removes the given action from the tool * Removes the given action from the tool. When an action is removed from the tool it will
* be disposed and should not be reused.
* @param action the action to be removed. * @param action the action to be removed.
*/ */
public void removeAction(DockingActionIf action); public void removeAction(DockingActionIf action);

View file

@ -63,8 +63,13 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
private static DockingActionIf actionUnderMouse; private static DockingActionIf actionUnderMouse;
private static Object objectUnderMouse; private static Object objectUnderMouse;
public static final String TOOL_PREFERENCES_XML_NAME = "PREFERENCES"; /**
* The owner name for docking windows actions.
* <p>Warning: Any action with this owner will get removed every time the 'Window' menu is
* rebuilt, with the exception if reserved key bindings.
*/
public static final String DOCKING_WINDOWS_OWNER = "DockingWindows"; public static final String DOCKING_WINDOWS_OWNER = "DockingWindows";
public static final String TOOL_PREFERENCES_XML_NAME = "PREFERENCES";
/** /**
* The helpService field should be set to the appropriate help service provider. * The helpService field should be set to the appropriate help service provider.
@ -560,6 +565,25 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
return list; return list;
} }
/**
* Returns the component provider that is the conceptual parent of the given component. More
* precisely, this will return the component provider whose
* {@link ComponentProvider#getComponent() component} is the parent of the given component.
*
* @param component the component for which to find a provider
* @return the provider; null if the component is not the child of a provider
*/
private ComponentProvider getComponentProvider(Component component) {
Set<ComponentProvider> providers = placeholderManager.getActiveProviders();
for (ComponentProvider provider : providers) {
JComponent providerComponent = provider.getComponent();
if (SwingUtilities.isDescendingFrom(component, providerComponent)) {
return provider;
}
}
return null;
}
DockableComponent getDockableComponent(ComponentProvider provider) { DockableComponent getDockableComponent(ComponentProvider provider) {
ComponentPlaceholder placeholder = placeholderManager.getPlaceholder(provider); ComponentPlaceholder placeholder = placeholderManager.getPlaceholder(provider);
return placeholder.getComponent(); return placeholder.getComponent();
@ -2165,7 +2189,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
component.removeHierarchyListener(this); component.removeHierarchyListener(this);
DockingWindowManager dwm = getInstance(component); DockingWindowManager dwm = getInstance(component);
if (dwm != null) { if (dwm != null) {
listener.componentLoaded(dwm); ComponentProvider provider = dwm.getComponentProvider(component);
listener.componentLoaded(dwm, provider);
return; return;
} }

View file

@ -408,8 +408,9 @@ class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher {
// ...next see if there is a key binding for when the component is the child of the focus // ...next see if there is a key binding for when the component is the child of the focus
// owner // owner
return KeyBindingUtils.getAction(jComponent, keyStroke, action = KeyBindingUtils.getAction(jComponent, keyStroke,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
return action;
} }
/** /**

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -63,7 +62,7 @@ public class MenuBarMenuHandler extends MenuHandler {
return; // context is not valid, nothing to do return; // context is not valid, nothing to do
} }
tempContext.setSource(event.getSource()); tempContext.setSourceObject(event.getSource());
final ActionContext finalContext = tempContext; final ActionContext finalContext = tempContext;
// this gives the UI some time to repaint before executing the action // this gives the UI some time to repaint before executing the action

View file

@ -24,7 +24,6 @@ import java.util.*;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import docking.action.*; import docking.action.*;
import docking.actions.PopupActionProvider;
import docking.menu.*; import docking.menu.*;
public class PopupActionManager implements PropertyChangeListener { public class PopupActionManager implements PropertyChangeListener {
@ -76,7 +75,7 @@ public class PopupActionManager implements PropertyChangeListener {
actionContext = new ActionContext(); actionContext = new ActionContext();
} }
actionContext.setSource(e.getSource()); actionContext.setSourceObject(e.getSource());
actionContext.setMouseEvent(e); actionContext.setMouseEvent(e);
MenuHandler popupMenuHandler = new PopupMenuHandler(windowManager, actionContext); MenuHandler popupMenuHandler = new PopupMenuHandler(windowManager, actionContext);
@ -145,7 +144,7 @@ public class PopupActionManager implements PropertyChangeListener {
Object source = actionContext.getSourceObject(); Object source = actionContext.getSourceObject();
// this interface is deprecated in favor of the next block // this interface is deprecated in favor the code that calls this method; this will be deleted
if (source instanceof DockingActionProviderIf) { if (source instanceof DockingActionProviderIf) {
DockingActionProviderIf actionProvider = (DockingActionProviderIf) source; DockingActionProviderIf actionProvider = (DockingActionProviderIf) source;
List<DockingActionIf> dockingActions = actionProvider.getDockingActions(); List<DockingActionIf> dockingActions = actionProvider.getDockingActions();
@ -158,23 +157,6 @@ public class PopupActionManager implements PropertyChangeListener {
} }
} }
} }
// note: this is temporary; there is only one client that needs this. This will be
// removed in a future ticket when that client uses the standard tool action system
if (source instanceof PopupActionProvider) {
PopupActionProvider actionProvider = (PopupActionProvider) source;
DockingTool tool = windowManager.getTool();
List<DockingActionIf> dockingActions =
actionProvider.getPopupActions(tool, actionContext);
for (DockingActionIf action : dockingActions) {
MenuData popupMenuData = action.getPopupMenuData();
if (popupMenuData != null && action.isValidContext(actionContext) &&
action.isAddToPopup(actionContext)) {
action.setEnabled(action.isEnabledForContext(actionContext));
menuMgr.addAction(action);
}
}
}
} }
private boolean isRemovingFromPopup(MenuData oldData, MenuData newData) { private boolean isRemovingFromPopup(MenuData oldData, MenuData newData) {

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -48,7 +47,7 @@ public class PopupMenuHandler extends MenuHandler {
public void processMenuAction(final DockingActionIf action, final ActionEvent event) { public void processMenuAction(final DockingActionIf action, final ActionEvent event) {
DockingWindowManager.clearMouseOverHelp(); DockingWindowManager.clearMouseOverHelp();
actionContext.setSource(event.getSource()); actionContext.setSourceObject(event.getSource());
// this gives the UI some time to repaint before executing the action // this gives the UI some time to repaint before executing the action
SwingUtilities.invokeLater( new Runnable() { SwingUtilities.invokeLater( new Runnable() {

View file

@ -372,9 +372,8 @@ public abstract class DockingAction implements DockingActionIf {
/** /**
* Cleans up any resources used by the action. * Cleans up any resources used by the action.
*/ */
@Override
public void dispose() { public void dispose() {
// TODO this doesn't seem to be called by the framework. Should't we call this when
// an action is removed from the tool??
propertyListeners.clear(); propertyListeners.clear();
} }

View file

@ -300,4 +300,9 @@ public interface DockingActionIf extends HelpDescriptor {
* @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding * @param newKeyBindingData the KeyBindingData to be used to assign this action to a keybinding
*/ */
public void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData); public void setUnvalidatedKeyBindingData(KeyBindingData newKeyBindingData);
/**
* Called when the action's owner is removed from the tool
*/
public void dispose();
} }

View file

@ -119,7 +119,7 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
// Build list of actions which are valid in current context // Build list of actions which are valid in current context
ComponentProvider localProvider = tool.getActiveComponentProvider(); ComponentProvider localProvider = tool.getActiveComponentProvider();
ActionContext localContext = getLocalContext(localProvider); ActionContext localContext = getLocalContext(localProvider);
localContext.setSource(event.getSource()); localContext.setSourceObject(event.getSource());
ActionContext globalContext = tool.getGlobalContext(); ActionContext globalContext = tool.getGlobalContext();
List<ExecutableKeyActionAdapter> list = getValidContextActions(localContext, globalContext); List<ExecutableKeyActionAdapter> list = getValidContextActions(localContext, globalContext);

View file

@ -122,7 +122,7 @@ public class ActionAdapter implements Action, PropertyChangeListener {
} }
if (context == null) { if (context == null) {
context = new ActionContext(); context = new ActionContext();
context.setSource(e.getSource()); context.setSourceObject(e.getSource());
} }
if (dockingAction.isEnabledForContext(context)) { if (dockingAction.isEnabledForContext(context)) {
dockingAction.actionPerformed(context); dockingAction.actionPerformed(context);

View file

@ -92,4 +92,6 @@ public interface DockingToolActions {
*/ */
public Set<DockingActionIf> getAllActions(); public Set<DockingActionIf> getAllActions();
// TODO
public boolean containsAction(DockingActionIf action);
} }

View file

@ -284,13 +284,32 @@ public class KeyBindingUtils {
am.put(keyText, action); am.put(keyText, action);
} }
/**
* Allows the client to clear Java key bindings when the client is creating a docking
* action. Without this call, any actions bound to the given component will prevent an
* action with the same key binding from firing. This is useful when your
* application is using tool-level key bindings that share the same
* keystroke as a built-in Java action, such as Ctrl-C for the copy action.
*
* @param component the component for which to clear the key binding
* @param action the action from which to get the key binding
*/
public static void clearKeyBinding(JComponent component, DockingActionIf action) {
KeyStroke keyBinding = action.getKeyBinding();
if (keyBinding == null) {
return;
}
clearKeyBinding(component, keyBinding);
}
/** /**
* Allows clients to clear Java key bindings. This is useful when your * Allows clients to clear Java key bindings. This is useful when your
* application is using tool-level key bindings that share the same * application is using tool-level key bindings that share the same
* keystroke as a built-in Java action, such as Ctrl-C for the copy action. * keystroke as a built-in Java action, such as Ctrl-C for the copy action.
* <p> * <p>
* Note: this method clears focus for the default * Note: this method clears the key binding for the
* ({@link JComponent#WHEN_FOCUSED}) focus condition. * {@link JComponent#WHEN_FOCUSED} and
* {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT} focus conditions.
* *
* @param component the component for which to clear the key binding * @param component the component for which to clear the key binding
* @param keyStroke the keystroke of the binding to be cleared * @param keyStroke the keystroke of the binding to be cleared
@ -298,6 +317,7 @@ public class KeyBindingUtils {
*/ */
public static void clearKeyBinding(JComponent component, KeyStroke keyStroke) { public static void clearKeyBinding(JComponent component, KeyStroke keyStroke) {
clearKeyBinding(component, keyStroke, JComponent.WHEN_FOCUSED); clearKeyBinding(component, keyStroke, JComponent.WHEN_FOCUSED);
clearKeyBinding(component, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
} }
/** /**
@ -314,13 +334,10 @@ public class KeyBindingUtils {
public static void clearKeyBinding(JComponent component, KeyStroke keyStroke, public static void clearKeyBinding(JComponent component, KeyStroke keyStroke,
int focusCondition) { int focusCondition) {
InputMap inputMap = component.getInputMap(focusCondition); InputMap inputMap = component.getInputMap(focusCondition);
ActionMap actionMap = component.getActionMap(); if (inputMap != null) {
if (inputMap == null || actionMap == null) {
return;
}
inputMap.put(keyStroke, "none"); inputMap.put(keyStroke, "none");
} }
}
/** /**
* Returns the registered action for the given keystroke, or null of no * Returns the registered action for the given keystroke, or null of no

View file

@ -32,8 +32,7 @@ import docking.*;
import docking.action.*; import docking.action.*;
import docking.tool.util.DockingToolConstants; import docking.tool.util.DockingToolConstants;
import ghidra.framework.options.*; import ghidra.framework.options.*;
import ghidra.util.ReservedKeyBindings; import ghidra.util.*;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import util.CollectionUtils; import util.CollectionUtils;
@ -187,6 +186,16 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
action.removePropertyChangeListener(this); action.removePropertyChangeListener(this);
removeAction(action); removeAction(action);
actionGuiHelper.removeToolAction(action); actionGuiHelper.removeToolAction(action);
dispose(action);
}
private void dispose(DockingActionIf action) {
try {
action.dispose();
}
catch (Throwable t) {
Msg.error(this, "Exception disposing action '" + action.getFullName() + "'", t);
}
} }
@Override @Override
@ -311,6 +320,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
removeAction(action); removeAction(action);
keyBindingsManager.removeAction(action); keyBindingsManager.removeAction(action);
actionGuiHelper.removeProviderAction(provider, action); actionGuiHelper.removeProviderAction(provider, action);
dispose(action);
} }
@Override @Override
@ -395,6 +405,11 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
return null; return null;
} }
@Override
public boolean containsAction(DockingActionIf action) {
return getActionStorage(action).contains(action);
}
public Action getAction(KeyStroke ks) { public Action getAction(KeyStroke ks) {
return keyBindingsManager.getDockingKeyAction(ks); return keyBindingsManager.getDockingKeyAction(ks);
} }

View file

@ -197,10 +197,12 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (menuHandler != null) { if (menuHandler != null) {
menuHandler.processMenuAction(action, e); menuHandler.processMenuAction(action, e);
return;
} }
else {
try { try {
ActionContext context = new ActionContext(null, null, e.getSource()); ActionContext context = new ActionContext();
context.setSourceObject(e.getSource());
if (action.isEnabledForContext(context)) { if (action.isEnabledForContext(context)) {
if (action instanceof ToggleDockingActionIf) { if (action instanceof ToggleDockingActionIf) {
ToggleDockingActionIf toggleAction = ((ToggleDockingActionIf) action); ToggleDockingActionIf toggleAction = ((ToggleDockingActionIf) action);
@ -213,7 +215,6 @@ class MenuItemManager implements ManagedMenuItem, PropertyChangeListener, Action
Msg.error(this, "Unexpected Exception: " + t.getMessage(), t); Msg.error(this, "Unexpected Exception: " + t.getMessage(), t);
} }
} }
}
@Override @Override
public String toString() { public String toString() {

View file

@ -184,7 +184,7 @@ public class MultipleActionDockingToolbarButton extends EmptyBorderButton {
final DockingActionIf delegateAction = dockingAction; final DockingActionIf delegateAction = dockingAction;
item.addActionListener(e -> { item.addActionListener(e -> {
ActionContext context = getActionContext(); ActionContext context = getActionContext();
context.setSource(e.getSource()); context.setSourceObject(e.getSource());
if (delegateAction instanceof ToggleDockingAction) { if (delegateAction instanceof ToggleDockingAction) {
ToggleDockingAction toggleAction = (ToggleDockingAction) delegateAction; ToggleDockingAction toggleAction = (ToggleDockingAction) delegateAction;
toggleAction.setSelected(!toggleAction.isSelected()); toggleAction.setSelected(!toggleAction.isSelected());

View file

@ -206,7 +206,7 @@ public class ToolBarItemManager implements PropertyChangeListener, ActionListene
else { else {
return; // context is not valid, nothing to do return; // context is not valid, nothing to do
} }
tempContext.setSource(event.getSource()); tempContext.setSourceObject(event.getSource());
final ActionContext finalContext = tempContext; final ActionContext finalContext = tempContext;
// this gives the UI some time to repaint before executing the action // this gives the UI some time to repaint before executing the action

View file

@ -1326,7 +1326,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
} }
actionContext = newContext; actionContext = newContext;
actionContext.setSource(provider.getComponent()); actionContext.setSourceObject(provider.getComponent());
return actionContext; return actionContext;
}); });
@ -1349,7 +1349,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
ActionContext context = runSwing(() -> { ActionContext context = runSwing(() -> {
ActionContext actionContext = provider.getActionContext(null); ActionContext actionContext = provider.getActionContext(null);
if (actionContext != null) { if (actionContext != null) {
actionContext.setSource(provider.getComponent()); actionContext.setSourceObject(provider.getComponent());
} }
return actionContext; return actionContext;
}); });
@ -2188,6 +2188,25 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
return ref.get(); return ref.get();
} }
/**
* Creates a generic action context with no provider, with the given payload
* @param payload the generic object to put in the context
* @return the new context
*/
public ActionContext createContext(Object payload) {
return new ActionContext().setContextObject(payload);
}
/**
* Creates a generic action context with the given provider, with the given payload
* @param provider the provider
* @param payload the generic object to put in the context
* @return the new context
*/
public ActionContext createContext(ComponentProvider provider, Object payload) {
return new ActionContext(provider).setContextObject(payload);
}
//================================================================================================== //==================================================================================================
// Screen Capture // Screen Capture
//================================================================================================== //==================================================================================================

View file

@ -579,18 +579,6 @@ public class GhidraFileChooser extends DialogComponentProvider
//================================================================================================== //==================================================================================================
@Override @Override
public ActionContext getActionContext(MouseEvent event) {
if (event == null) {
return super.getActionContext(event);
}
return new ActionContext(null, event.getSource());
}
/**
* @see ghidra.util.filechooser.GhidraFileChooserListener#modelChanged()
*/
@Override
public void modelChanged() { public void modelChanged() {
SystemUtilities.runSwingLater(() -> { SystemUtilities.runSwingLater(() -> {
directoryListModel.update(); directoryListModel.update();
@ -598,9 +586,6 @@ public class GhidraFileChooser extends DialogComponentProvider
}); });
} }
/**
* @see java.io.FileFilter#accept(java.io.File)
*/
@Override @Override
public boolean accept(File file) { public boolean accept(File file) {
if (!showDotFiles) { if (!showDotFiles) {

View file

@ -32,7 +32,6 @@ import javax.swing.table.*;
import docking.*; import docking.*;
import docking.action.*; import docking.action.*;
import docking.actions.KeyBindingUtils; import docking.actions.KeyBindingUtils;
import docking.actions.PopupActionProvider;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import docking.widgets.dialogs.SettingsDialog; import docking.widgets.dialogs.SettingsDialog;
import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.filechooser.GhidraFileChooser;
@ -70,12 +69,13 @@ import resources.ResourceManager;
* *
* @see GTableFilterPanel * @see GTableFilterPanel
*/ */
public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProvider { public class GTable extends JTable implements KeyStrokeConsumer {
private static final String LAST_EXPORT_FILE = "LAST_EXPORT_DIR"; private static final String LAST_EXPORT_FILE = "LAST_EXPORT_DIR";
private int userDefinedRowHeight; private int userDefinedRowHeight;
private boolean isInitialized;
private boolean allowActions; private boolean allowActions;
private KeyListener autoLookupListener; private KeyListener autoLookupListener;
private long lastLookupTime; private long lastLookupTime;
@ -96,6 +96,8 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
/** A flag to signal that a copy operation is being performed. */ /** A flag to signal that a copy operation is being performed. */
private boolean copying; private boolean copying;
private static final String actionMenuGroup = "zzzTableGroup";
private DockingAction copyAction; private DockingAction copyAction;
private DockingAction copyColumnsAction; private DockingAction copyColumnsAction;
private DockingAction copyCurrentColumnAction; private DockingAction copyCurrentColumnAction;
@ -103,8 +105,6 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
private DockingAction exportAction; private DockingAction exportAction;
private DockingAction exportColumnsAction; private DockingAction exportColumnsAction;
private String actionMenuGroup = "zzzTableGroup";
private SelectionManager selectionManager; private SelectionManager selectionManager;
private Integer visibleRowCount; private Integer visibleRowCount;
@ -510,24 +510,6 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
return autoLookupKeyStrokeConsumer.isKeyConsumed(keyStroke); return autoLookupKeyStrokeConsumer.isKeyConsumed(keyStroke);
} }
@Override
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) {
// we want these top-level groups to all appear together, with no separator
tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1");
tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2");
tool.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3");
List<DockingActionIf> list = new ArrayList<>();
list.add(copyAction);
list.add(copyCurrentColumnAction);
list.add(copyColumnsAction);
list.add(selectAllAction);
list.add(exportAction);
list.add(exportColumnsAction);
return list;
}
private void init(boolean allowAutoEdit) { private void init(boolean allowAutoEdit) {
ToolTipManager.sharedInstance().unregisterComponent(this); ToolTipManager.sharedInstance().unregisterComponent(this);
ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().registerComponent(this);
@ -576,8 +558,27 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
} }
}); });
createPopupActions(); // updating the row height requires the 'isInitialized' to be set, so do it first
isInitialized = true;
initializeRowHeight(); initializeRowHeight();
DockingWindowManager.registerComponentLoadedListener(this, (dwm, provider) -> {
DockingTool tool = dwm.getTool();
regiserActions(tool, provider);
});
}
private void regiserActions(DockingTool tool, ComponentProvider provider) {
tool.setMenuGroup(new String[] { "Copy" }, actionMenuGroup, "1");
tool.setMenuGroup(new String[] { "Export" }, actionMenuGroup, "2");
tool.setMenuGroup(new String[] { "Select All" }, actionMenuGroup, "3");
String owner = getClass().getSimpleName();
if (provider != null) {
owner = provider.getOwner();
}
installTableActions(tool, owner);
} }
private void initializeHeader(JTableHeader header) { private void initializeHeader(JTableHeader header) {
@ -600,7 +601,8 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
} }
private void adjustRowHeight() { private void adjustRowHeight() {
if (copyAction == null) { // crude test to know if our constructor has finished
if (!isInitialized) {
return; // must be initializing return; // must be initializing
} }
@ -1153,12 +1155,20 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
return converted; return converted;
} }
private void createPopupActions() { /**
* A method that subclasses can override to signal that they wish not to have this table's
* built-in popup actions. Subclasses will almost never need to override this method.
*
* @return true if popup actions are supported
*/
protected boolean supportsPopupActions() {
return true;
}
private void installTableActions(DockingTool tool, String owner) {
int subGroupIndex = 1; // order by insertion int subGroupIndex = 1; // order by insertion
String owner = getClass().getSimpleName(); copyAction = new GTableAction("Table Data Copy", owner) {
owner = "GTable";
copyAction = new DockingAction("Table Data Copy", owner, KeyBindingType.SHARED) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
copying = true; copying = true;
@ -1188,13 +1198,11 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
copyAction.setHelpLocation(new HelpLocation("Tables", "Copy")); copyAction.setHelpLocation(new HelpLocation("Tables", "Copy"));
//@formatter:on //@formatter:on
copyCurrentColumnAction = copyCurrentColumnAction = new GTableAction("Table Data Copy Current Column", owner) {
new DockingAction("Table Data Copy Current Column", owner, KeyBindingType.SHARED) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
int column = getSelectedColumn(); int column = getSelectedColumn();
MouseEvent event = context.getMouseEvent(); MouseEvent event = context.getMouseEvent();
if (event != null) { if (event != null) {
column = columnAtPoint(event.getPoint()); column = columnAtPoint(event.getPoint());
@ -1226,8 +1234,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
copyCurrentColumnAction.setHelpLocation(new HelpLocation("Tables", "Copy_Current_Column")); copyCurrentColumnAction.setHelpLocation(new HelpLocation("Tables", "Copy_Current_Column"));
//@formatter:on //@formatter:on
copyColumnsAction = copyColumnsAction = new GTableAction("Table Data Copy by Columns", owner) {
new DockingAction("Table Data Copy by Columns", owner, KeyBindingType.SHARED) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
int[] userColumns = promptUserForColumns(); int[] userColumns = promptUserForColumns();
@ -1250,7 +1257,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
copyColumnsAction.setHelpLocation(new HelpLocation("Tables", "Copy_Columns")); copyColumnsAction.setHelpLocation(new HelpLocation("Tables", "Copy_Columns"));
//@formatter:on //@formatter:on
exportAction = new DockingAction("Table Data CSV Export", owner, KeyBindingType.SHARED) { exportAction = new GTableAction("Table Data CSV Export", owner) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
File file = chooseExportFile(); File file = chooseExportFile();
@ -1271,8 +1278,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
exportAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV")); exportAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV"));
//@formatter:on //@formatter:on
exportColumnsAction = exportColumnsAction = new GTableAction("Table Data CSV Export (by Columns)", owner) {
new DockingAction("Table Data CSV Export (by Columns)", owner, KeyBindingType.SHARED) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
int[] userColumns = promptUserForColumns(); int[] userColumns = promptUserForColumns();
@ -1304,7 +1310,7 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
exportColumnsAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV_Columns")); exportColumnsAction.setHelpLocation(new HelpLocation("Tables", "ExportCSV_Columns"));
//@formatter:on //@formatter:on
selectAllAction = new DockingAction("Table Select All", owner, KeyBindingType.SHARED) { selectAllAction = new GTableAction("Table Select All", owner) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
selectAll(); selectAll();
@ -1312,6 +1318,9 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
@Override @Override
public boolean isEnabledForContext(ActionContext context) { public boolean isEnabledForContext(ActionContext context) {
if (!super.isEnabledForContext(context)) {
return false;
}
return getSelectionModel().getSelectionMode() != ListSelectionModel.SINGLE_SELECTION; return getSelectionModel().getSelectionMode() != ListSelectionModel.SINGLE_SELECTION;
} }
}; };
@ -1332,9 +1341,20 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
selectAllAction.setHelpLocation(new HelpLocation("Tables", "SelectAll")); selectAllAction.setHelpLocation(new HelpLocation("Tables", "SelectAll"));
//@formatter:on //@formatter:on
KeyBindingUtils.registerAction(this, copyAction); // remove any conflicting key bindings that Java has installed on this component
KeyBindingUtils.registerAction(this, copyCurrentColumnAction); KeyBindingUtils.clearKeyBinding(this, copyAction);
KeyBindingUtils.registerAction(this, selectAllAction); KeyBindingUtils.clearKeyBinding(this, copyCurrentColumnAction);
KeyBindingUtils.clearKeyBinding(this, copyColumnsAction);
KeyBindingUtils.clearKeyBinding(this, selectAllAction);
KeyBindingUtils.clearKeyBinding(this, exportAction);
KeyBindingUtils.clearKeyBinding(this, exportColumnsAction);
tool.addAction(copyAction);
tool.addAction(copyCurrentColumnAction);
tool.addAction(copyColumnsAction);
tool.addAction(selectAllAction);
tool.addAction(exportAction);
tool.addAction(exportColumnsAction);
} }
private void copyColumns(int... copyColumns) { private void copyColumns(int... copyColumns) {
@ -1478,4 +1498,18 @@ public class GTable extends JTable implements KeyStrokeConsumer, PopupActionProv
// ignored // ignored
} }
} }
private abstract class GTableAction extends DockingAction {
GTableAction(String name, String owner) {
super(name, owner, KeyBindingType.SHARED);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
Component sourceComponent = context.getSourceComponent();
return sourceComponent == GTable.this;
}
}
} }

View file

@ -214,7 +214,7 @@ public class GTableFilterPanel<ROW_OBJECT> extends JPanel {
table.addPropertyChangeListener(badProgrammingPropertyChangeListener); table.addPropertyChangeListener(badProgrammingPropertyChangeListener);
DockingWindowManager.registerComponentLoadedListener(this, DockingWindowManager.registerComponentLoadedListener(this,
windowManager -> initialize(windowManager)); (windowManager, provider) -> initialize(windowManager));
} }
private void initialize(DockingWindowManager windowManager) { private void initialize(DockingWindowManager windowManager) {

View file

@ -106,7 +106,7 @@ public class TableColumnModelState implements SortListener {
// We want to load our state after the column model is loaded. We are using this // We want to load our state after the column model is loaded. We are using this
// listener to know when the table has been added to the component hierarchy, as its // listener to know when the table has been added to the component hierarchy, as its
// model has been loaded by then. // model has been loaded by then.
DockingWindowManager.registerComponentLoadedListener(table, windowManager -> { DockingWindowManager.registerComponentLoadedListener(table, (windowManager, provider) -> {
if (!enabled) { if (!enabled) {
setEnabled(true); setEnabled(true);
restoreState(); restoreState();

View file

@ -112,7 +112,7 @@ public class GTree extends JPanel implements BusyListener {
init(); init();
DockingWindowManager.registerComponentLoadedListener(this, DockingWindowManager.registerComponentLoadedListener(this,
windowManager -> filterProvider.loadFilterPreference(windowManager, (windowManager, provider) -> filterProvider.loadFilterPreference(windowManager,
uniquePreferenceKey)); uniquePreferenceKey));
filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> performNodeFiltering()); filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> performNodeFiltering());

View file

@ -18,8 +18,6 @@ package ghidra.util.bean;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.awt.event.MouseEvent;
import javax.swing.ButtonModel; import javax.swing.ButtonModel;
import org.junit.Before; import org.junit.Before;
@ -82,30 +80,15 @@ public class EmptyBorderButtonTest extends AbstractDockingTest {
} }
private void setButtonArmed(final boolean armed) { private void setButtonArmed(final boolean armed) {
runSwing(new Runnable() { runSwing(() -> buttonModel.setArmed(armed));
@Override
public void run() {
buttonModel.setArmed(armed);
}
});
} }
private void setButtonPressed(final boolean pressed) { private void setButtonPressed(final boolean pressed) {
runSwing(new Runnable() { runSwing(() -> buttonModel.setPressed(pressed));
@Override
public void run() {
buttonModel.setPressed(pressed);
}
});
} }
private void setRollover(final boolean isRollover) { private void setRollover(final boolean isRollover) {
runSwing(new Runnable() { runSwing(() -> buttonModel.setRollover(isRollover));
@Override
public void run() {
buttonModel.setRollover(isRollover);
}
});
} }
// public void testButtonBorderOnModalDialog() throws InterruptedException { // public void testButtonBorderOnModalDialog() throws InterruptedException {
@ -158,13 +141,8 @@ public class EmptyBorderButtonTest extends AbstractDockingTest {
return true; return true;
} }
}; };
ActionContextProvider contextProvider = new ActionContextProvider() { ActionContextProvider contextProvider =
e -> new ActionContext(null, e.getSource(), e.getComponent());
@Override
public ActionContext getActionContext(MouseEvent e) {
return new ActionContext(null, null, e.getSource());
}
};
action.setToolBarData(new ToolBarData(ResourceManager.getDefaultIcon())); action.setToolBarData(new ToolBarData(ResourceManager.getDefaultIcon()));
action.setEnabled(false); action.setEnabled(false);

View file

@ -20,8 +20,6 @@ import java.awt.event.KeyEvent;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import javax.swing.JMenuItem;
import org.jdom.Element; import org.jdom.Element;
import org.jdom.input.SAXBuilder; import org.jdom.input.SAXBuilder;
@ -511,7 +509,7 @@ class ToolActionManager implements ToolChestChangeListener {
ToolAction runAction = new ToolAction(toolName, "Run_Tool") { ToolAction runAction = new ToolAction(toolName, "Run_Tool") {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
String name = ((JMenuItem) context.getSourceObject()).getText(); String name = getName();
Workspace ws = plugin.getActiveWorkspace(); Workspace ws = plugin.getActiveWorkspace();
ToolChest toolChest = plugin.getActiveProject().getLocalToolChest(); ToolChest toolChest = plugin.getActiveProject().getLocalToolChest();
ws.runTool(toolChest.getToolTemplate(name)); ws.runTool(toolChest.getToolTemplate(name));
@ -529,7 +527,7 @@ class ToolActionManager implements ToolChestChangeListener {
ToolAction deleteAction = new ToolAction(toolName, "Delete_Tool") { ToolAction deleteAction = new ToolAction(toolName, "Delete_Tool") {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
String name = ((JMenuItem) context.getSourceObject()).getText(); String name = getName();
if (!plugin.confirmDelete(name + " from the project tool chest?")) { if (!plugin.confirmDelete(name + " from the project tool chest?")) {
return; return;
} }
@ -549,7 +547,7 @@ class ToolActionManager implements ToolChestChangeListener {
ToolAction exportAction = new ToolAction(toolName, "Export_Tool") { ToolAction exportAction = new ToolAction(toolName, "Export_Tool") {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
String name = ((JMenuItem) context.getSourceObject()).getText(); String name = getName();
ToolChest toolChest = plugin.getActiveProject().getLocalToolChest(); ToolChest toolChest = plugin.getActiveProject().getLocalToolChest();
plugin.exportToolConfig(toolChest.getToolTemplate(name), "Tool Menu"); plugin.exportToolConfig(toolChest.getToolTemplate(name), "Tool Menu");
} }

View file

@ -35,7 +35,7 @@ public class ProjectDataActionContext extends ActionContext implements DomainFil
public ProjectDataActionContext(ComponentProvider provider, ProjectData projectData, public ProjectDataActionContext(ComponentProvider provider, ProjectData projectData,
Object contextObject, List<DomainFolder> selectedFolders, Object contextObject, List<DomainFolder> selectedFolders,
List<DomainFile> selectedFiles, Component comp, boolean isActiveProject) { List<DomainFile> selectedFiles, Component comp, boolean isActiveProject) {
super(provider, contextObject); super(provider, contextObject, comp);
this.projectData = projectData; this.projectData = projectData;
this.selectedFolders = selectedFolders; this.selectedFolders = selectedFolders;
this.selectedFiles = selectedFiles; this.selectedFiles = selectedFiles;

View file

@ -25,8 +25,8 @@ import java.util.List;
import javax.swing.*; import javax.swing.*;
import docking.*; import docking.ActionContext;
import docking.action.DockingActionIf; import docking.ComponentProvider;
import docking.help.Help; import docking.help.Help;
import docking.help.HelpService; import docking.help.HelpService;
import docking.widgets.label.GHtmlLabel; import docking.widgets.label.GHtmlLabel;
@ -489,12 +489,8 @@ public class ProjectDataTablePanel extends JPanel {
} }
@Override @Override
public List<DockingActionIf> getPopupActions(DockingTool tool, ActionContext context) { protected boolean supportsPopupActions() {
return false;
// TODO we should at least add the 'copy' action
// the table's default actions aren't that useful in the Front End
return Collections.emptyList();
} }
} }
} }

View file

@ -135,7 +135,7 @@ public class VersionHistoryDialog extends DialogComponentProvider implements Pro
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
ActionContext actionContext = new ActionContext(null, versionPanel.getTable(), this); ActionContext actionContext = new ActionContext(null, this, versionPanel.getTable());
actionContext.setMouseEvent(event); actionContext.setMouseEvent(event);
return actionContext; return actionContext;
} }

View file

@ -441,6 +441,8 @@ public abstract class PluginTool extends AbstractDockingTool implements Tool, Se
winMgr.setVisible(false); winMgr.setVisible(false);
eventMgr.clearLastEvents(); eventMgr.clearLastEvents();
pluginMgr.dispose(); pluginMgr.dispose();
toolActions.removeActions(ToolConstants.TOOL_OWNER);
toolActions.dispose(); toolActions.dispose();
if (project != null) { if (project != null) {