Merge remote-tracking branch 'origin/GP-4691-dragonmacher-program-tree-mousing--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-08-12 06:37:52 -04:00
commit d007200b11
23 changed files with 1188 additions and 1351 deletions

View File

@ -389,32 +389,46 @@
View</FONT></B> option.</LI>
</UL>
<H3><A name="Replace_View"></A>Replace the View in the Code Browser with other
Folders/Fragments</H3>
<H3><A name="Set_View"></A>Set the View in the Code Browser with Folders/Fragments</H3>
<UL>
<LI>
To replace the view in the code browser with other folders and fragments,
To set the view in the code browser with folders and fragments,
<OL>
<LI>Select a folder or fragment (or select multiple folders and fragments),&nbsp;</LI>
<LI>Right mouse-click and choose the <B>Replace View</B> option. The code browser now
<LI>Right mouse-click and choose the <B>Set View</B> option. The code browser now
shows the code units for these folders and fragments.</LI>
</OL>
</LI>
<BLOCKQUOTE>
<P><IMG src="help/shared/note.png"> The program tree can be configured, via tool
options, such that a double-click performs a simple navigation, or the
<B>Replace View</B> action. The default
behavior for a double-click is to perform the <B>Replace View</B> action.</P>
options, such that a double-click performs the <B>Set View</B> action. The default
behavior for a double-click is to navigate to the first address of the clicked
fragment.</P>
<P>(Double-clicking on a folder always causes it to expand if it is collapsed and to
collapse if it is expanded.)</P>
</BLOCKQUOTE>
</UL>
<H3><A name="Add_to_View"></A>Add to the View in the Code Browser</H3>
<UL>
<LI>
To add a folder or fragment to the view in the code browser,
<OL>
<LI>Select a folder or fragment (or select multiple folders and fragments),&nbsp;</LI>
<LI>Right mouse-click and choose the <B>Add to View</B> option. The code browser now
shows the code units for these folders and fragments.</LI>
</OL>
</LI>
</UL>
<H3>Navigation</H3>
<BLOCKQUOTE>

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -33,12 +33,8 @@ import docking.widgets.textfield.GValidatedTextField;
import docking.widgets.textfield.GValidatedTextField.LongField.LongValidator;
import docking.widgets.textfield.GValidatedTextField.ValidationFailedException;
import docking.widgets.textfield.GValidatedTextField.ValidationMessageListener;
import generic.theme.Gui;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.DataTypeArchive;
import ghidra.program.model.listing.Program;
import ghidra.util.*;
import ghidra.util.table.GhidraTable;
@ -670,7 +666,7 @@ class EnumEditorPanel extends JPanel {
private class EnumValueRenderer extends GTableCellRenderer {
EnumValueRenderer() {
setFont(Gui.getFont("font.monospaced"));
setFont(getFixedWidthFont());
}
@Override

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -61,7 +61,7 @@ public abstract class DragNDropTree extends JTree implements Draggable, Droppabl
// data flavors that this tree can support
protected DataFlavor[] acceptableFlavors;
protected TreeTransferable transferable;
protected ProgramTreeTransferable transferable;
protected Color nonSelectionDragColor;
protected int relativeMousePos; // mouse position within the node
@ -128,7 +128,7 @@ public abstract class DragNDropTree extends JTree implements Draggable, Droppabl
nodes[i] = (ProgramNode) selectionPaths[i].getLastPathComponent();
}
transferable = new TreeTransferable(nodes);
transferable = new ProgramTreeTransferable(nodes);
draggedNodes = nodes;
return transferable;
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,10 +17,9 @@ package ghidra.app.plugin.core.programtree;
import java.awt.datatransfer.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import docking.dnd.GClipboard;
@ -29,90 +28,66 @@ import ghidra.util.Msg;
import ghidra.util.exception.NotFoundException;
/**
* Manage paste operations for the tree.
* Manage paste operations for the Program Tree.
*/
class PasteManager {
private ProgramTreeActionManager actionMgr;
private ProgramDnDTree tree;
private DefaultTreeModel treeModel;
private Clipboard cutClipboard;
private ProgramTreeActionManager actionManager;
private String lastGroupPasted;
/**
* Constructor
*/
PasteManager(ProgramTreeActionManager actionMgr) {
this.actionMgr = actionMgr;
cutClipboard = actionMgr.getCutClipboard();
PasteManager(ProgramTreeActionManager actionManager) {
this.actionManager = actionManager;
}
/**
* Return true if the pasteNode can be pasted at the destNode.
* @param destNode destination node for where the pasteNode will be pasted
* @param pasteNode node to paste
* @param isCutOperation true if the operation was "cut" versus "copy"
*/
boolean isPasteAllowed(ProgramNode destNode, ProgramNode pasteNode, boolean isCutOperation) {
if (destNode.getProgram() != pasteNode.getProgram() ||
destNode.getRoot() != pasteNode.getRoot()) {
return false;
}
try {
if (destNode.getName().equals(pasteNode.getName())) {
return false;
}
if (destNode.isNodeAncestor(pasteNode)) {
return false;
}
if (destNode.isFragment() && pasteNode.isModule()) {
if (isCutOperation && !pasteNode.getModule().isDescendant(destNode.getFragment())) {
if (destNode.getName().equals(pasteNode.getName())) {
return false;
}
return true; // pasted module can be flattened onto
// destination fragment
if (destNode.isNodeAncestor(pasteNode)) {
return false;
}
if (destNode.isFragment() && pasteNode.isModule()) {
if (isCutOperation && !pasteNode.getModule().isDescendant(destNode.getFragment())) {
return true; // pasted module can be flattened onto destination fragment
}
return false;
}
if (destNode.isFragment() && pasteNode.isFragment()) {
if (isCutOperation) {
return true;
}
return false;
}
if (destNode.isModule()) {
ProgramModule destModule = destNode.getModule();
if (pasteNode.isModule()) {
if (pasteNode.getModule().isDescendant(destModule)) {
return false;
}
return false;
}
if (destNode.isFragment() && pasteNode.isFragment()) {
if (isCutOperation) {
return true;
}
return false;
}
if (destNode.isModule()) {
ProgramModule destModule = destNode.getModule();
if (pasteNode.isModule()) {
if (pasteNode.getModule().isDescendant(destModule)) {
return false;
}
if (!isCutOperation && destModule.contains(pasteNode.getModule())) {
return false;
}
}
else if (!isCutOperation && destModule.contains(pasteNode.getFragment())) {
if (!isCutOperation && destModule.contains(pasteNode.getModule())) {
return false;
}
}
else if (!isCutOperation && destModule.contains(pasteNode.getFragment())) {
return false;
}
}
return true;
}
catch (RuntimeException e) {
// this is a hack for unknown reasons
}
return false;
return true;
}
/**
* Do the paste operation.
* @param destNode destination node for where the paste the contents of
* system clipboard.
*/
@SuppressWarnings("unchecked")
// cast is OK, it is data that we are expecting
void paste(ProgramNode destNode) {
@SuppressWarnings("unchecked") // cast is OK, it is data that we are expecting
void paste(ProgramDnDTree tree, ProgramNode destNode) {
int transactionID = tree.startTransaction("Paste");
if (transactionID < 0) {
@ -125,34 +100,32 @@ class PasteManager {
Clipboard systemClipboard = GClipboard.getSystemClipboard();
Transferable t = systemClipboard.getContents(tree);
try {
if (t == null || !t.isDataFlavorSupported(TreeTransferable.localTreeNodeFlavor)) {
if (t == null ||
!t.isDataFlavorSupported(ProgramTreeTransferable.localTreeNodeFlavor)) {
return;
}
tree.setBusyCursor(true);
lastGroupPasted = null;
ArrayList<ProgramNode> list =
(ArrayList<ProgramNode>) t.getTransferData(TreeTransferable.localTreeNodeFlavor);
List<ProgramNode> list =
(List<ProgramNode>) t.getTransferData(ProgramTreeTransferable.localTreeNodeFlavor);
if (list == null) {
// SCR 7990--something bad has happened to the copy buffer
return;
}
for (int i = 0; i < list.size(); i++) {
ProgramNode tnode = list.get(i);
if (destNode.getRoot() != tnode.getRoot()) {
for (ProgramNode node : list) {
if (destNode.getRoot() != node.getRoot()) {
lastGroupPasted = null;
break;
}
if (!destNode.getName().equals(tnode.getName())) {
if (pasteGroup(destNode, tnode)) {
if (!(destNode.isFragment() && tnode.isModule())) {
if (!destNode.getName().equals(node.getName())) {
if (pasteGroup(tree, destNode, node)) {
if (!(destNode.isFragment() && node.isModule())) {
// this was not a "flatten module" operation
// so we can leave the busy cursor set
// until the domain object event comes in
lastGroupPasted = tnode.getName();
lastGroupPasted = node.getName();
}
}
}
@ -163,18 +136,14 @@ class PasteManager {
}
// do "cut" operations now if there are any
actionMgr.checkClipboard(true);
actionMgr.clearSystemClipboard();
actionMgr.enablePasteAction(false);
actionManager.cutClipboardNodes(tree);
tree.removeSelectionPath(path);
tree.addSelectionPath(path);
}
catch (UnsupportedFlavorException e) {
// data flavor is not supported
Msg.showError(this, null, "Paste from Clipboard Failed",
"Data flavor in clipboard is not supported.", e);
}
catch (IOException e) {
// data is no longer available
@ -182,33 +151,19 @@ class PasteManager {
"Data is no longer available for paste operation", e);
}
catch (Exception e) {
Msg.showError(this, null, null, null, e);
Msg.showError(this, null, "Unexpected Exception Pasting",
"Unexpected exception pasting nodes", e);
}
finally {
tree.endTransaction(transactionID, true);
}
}
/**
* Get the name of the last group that was pasted.
*/
String getLastGroupPasted() {
return lastGroupPasted;
}
/**
* Method setProgramTreeView.
* @param tree
*/
void setProgramTreeView(ProgramDnDTree tree) {
this.tree = tree;
treeModel = (DefaultTreeModel) tree.getModel();
}
/**
* Paste the group at nodeToPaste at destNode.
*/
private boolean pasteGroup(ProgramNode destNode, ProgramNode nodeToPaste) {
private boolean pasteGroup(ProgramDnDTree tree, ProgramNode destNode, ProgramNode nodeToPaste) {
if (destNode.isFragment()) {
// can paste either a fragment or a module onto a fragment;
@ -216,11 +171,12 @@ class PasteManager {
// descendant fragments are moved to the destination fragment.
try {
tree.mergeGroup(nodeToPaste.getGroup(), destNode.getFragment());
actionMgr.removeFromClipboard(cutClipboard, nodeToPaste);
actionManager.removeFromClipboard(tree, nodeToPaste);
return true;
}
catch (ConcurrentModificationException e) {
// ha!
}
catch (Exception e) {
Msg.showError(this, null, null, "Error Merging Fragments", e);
@ -231,30 +187,24 @@ class PasteManager {
ProgramModule targetModule = destNode.getModule();
if (targetModule == null) {
nodeToPaste.setDeleted(false);
treeModel.reload(nodeToPaste);
actionManager.clearCut(nodeToPaste);
Msg.showError(this, null, "Paste from Clipboard Failed",
"Paste of " + nodeToPaste + " at\n" + destNode.getName() + " is not allowed.");
return false;
}
return pasteNode(destNode, nodeToPaste);
return pasteNode(tree, destNode, nodeToPaste);
}
/**
* Paste the node at the destination node.
*/
private boolean pasteNode(ProgramNode destNode, ProgramNode nodeToPaste) {
private boolean pasteNode(ProgramDnDTree tree, ProgramNode destNode, ProgramNode nodeToPaste) {
ProgramModule targetModule = destNode.getModule();
// make sure we have something to paste
ProgramModule module = nodeToPaste.getModule();
ProgramFragment fragment = nodeToPaste.getFragment();
if (module == null && fragment == null) {
nodeToPaste.setDeleted(false);
treeModel.reload(nodeToPaste);
actionManager.clearCut(nodeToPaste);
Msg.showError(this, null, "Paste from Clipboard Failed",
"Could not paste " + nodeToPaste + " at " + targetModule.getName());
return false;
@ -263,10 +213,10 @@ class PasteManager {
boolean pasteOK = false;
try {
if (module != null) {
pasteOK = pasteModule(destNode, nodeToPaste, targetModule, module);
pasteOK = pasteModule(tree, destNode, nodeToPaste, targetModule, module);
}
else {
pasteOK = pasteFragment(nodeToPaste, targetModule, fragment);
pasteOK = pasteFragment(tree, nodeToPaste, targetModule, fragment);
}
// don't match the expansion state unless the destination
@ -281,37 +231,35 @@ class PasteManager {
}
catch (CircularDependencyException e) {
removeFromClipboard(nodeToPaste);
Msg.showError(this, null, "Paste from Clipboard Failed", e.getMessage());
}
catch (DuplicateGroupException e) {
nodeToPaste.setDeleted(false);
tree.reloadNode(nodeToPaste);
// handled below
}
catch (NotFoundException e) {
removeFromClipboard(nodeToPaste);
nodeToPaste.setDeleted(false);
Msg.showError(this, null, "Paste from Clipboard Failed", e.getMessage());
}
removeFromClipboard(tree, nodeToPaste);
return false;
}
/**
* Paste the fragment at the given module.
*/
private boolean pasteFragment(ProgramNode nodeToPaste, ProgramModule targetModule,
ProgramFragment fragment) throws NotFoundException, DuplicateGroupException {
private boolean pasteFragment(ProgramDnDTree tree, ProgramNode nodeToPaste,
ProgramModule targetModule, ProgramFragment fragment)
throws NotFoundException, DuplicateGroupException {
boolean pasteOK = false;
if (targetModule.contains(fragment)) {
if (targetModule.equals(nodeToPaste.getParentModule())) {
removeFromClipboard(nodeToPaste);
removeFromClipboard(tree, nodeToPaste);
}
}
else if (actionMgr.clipboardContains(nodeToPaste)) {
else if (actionManager.clipboardContains(nodeToPaste)) {
targetModule.reparent(nodeToPaste.getName(), nodeToPaste.getParentModule());
removeFromClipboard(nodeToPaste);
removeFromClipboard(tree, nodeToPaste);
pasteOK = true;
}
else {
@ -321,7 +269,7 @@ class PasteManager {
return pasteOK;
}
private boolean pasteModule(ProgramNode destNode, ProgramNode nodeToPaste,
private boolean pasteModule(ProgramDnDTree tree, ProgramNode destNode, ProgramNode nodeToPaste,
ProgramModule targetModule, ProgramModule module)
throws NotFoundException, CircularDependencyException, DuplicateGroupException {
@ -332,12 +280,12 @@ class PasteManager {
}
if (targetModule.contains(module)) {
if (targetModule.equals(nodeToPaste.getParentModule())) {
removeFromClipboard(nodeToPaste);
removeFromClipboard(tree, nodeToPaste);
}
}
else if (actionMgr.clipboardContains(nodeToPaste)) {
else if (actionManager.clipboardContains(nodeToPaste)) {
targetModule.reparent(nodeToPaste.getName(), nodeToPaste.getParentModule());
removeFromClipboard(nodeToPaste);
removeFromClipboard(tree, nodeToPaste);
pasteOK = true;
}
else {
@ -352,15 +300,9 @@ class PasteManager {
return pasteOK;
}
/**
* Remove the given node from the cut clipboard, so that "cut" changes
* will not be applied.
* @param node node to remove from the cut clipboard
*/
private void removeFromClipboard(ProgramNode node) {
actionMgr.removeFromClipboard(cutClipboard, node);
node.setDeleted(false);
tree.reloadNode(node);
private void removeFromClipboard(ProgramDnDTree tree, ProgramNode node) {
actionManager.removeFromClipboard(tree, node);
actionManager.clearCut(node);
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -29,7 +29,6 @@ import javax.swing.event.ChangeEvent;
import javax.swing.tree.*;
import docking.DockingUtils;
import docking.action.DockingAction;
import docking.actions.KeyBindingUtils;
import docking.dnd.DropTgtAdapter;
import docking.widgets.JTreeMouseListenerDelegate;
@ -55,8 +54,8 @@ public class ProgramDnDTree extends DragNDropTree {
private Program program;
private Listing listing;
private ArrayList<ProgramNode> nodeList; // list of nodes from preorder enumeration
private ArrayList<TreePath> viewList; // list of tree paths that are being viewed.
private List<ProgramNode> nodeList; // list of nodes from preorder enumeration
private List<TreePath> viewList; // list of tree paths that are being viewed.
//keeps track of module/fragment names to come up with a default name, e.g., New Folder (2)
private StringKeyIndexer nameIndexer;
@ -272,7 +271,7 @@ public class ProgramDnDTree extends DragNDropTree {
}
static DataFlavor[] getDataFlavors() {
return new DataFlavor[] { TreeTransferable.localTreeNodeFlavor,
return new DataFlavor[] { ProgramTreeTransferable.localTreeNodeFlavor,
GroupTransferable.localGroupFlavor, // a test data flavor
DataFlavor.stringFlavor, // a test data flavor
SelectionTransferable.localProgramSelectionFlavor };
@ -317,7 +316,7 @@ public class ProgramDnDTree extends DragNDropTree {
return false;
}
}
else if (chosen.equals(TreeTransferable.localTreeNodeFlavor)) {
else if (chosen.equals(ProgramTreeTransferable.localTreeNodeFlavor)) {
// fromObject is null, so we know this is
// from another tree, so don't allow the drop
return false;
@ -385,6 +384,8 @@ public class ProgramDnDTree extends DragNDropTree {
* that could potentially take a long time. The cursor is reset in the
* domain object change listener when the event comes in for the group
* that was last "pasted."
*
* @param busy true to use the busy cursor
*/
void setBusyCursor(boolean busy) {
if (busy) {
@ -406,19 +407,11 @@ public class ProgramDnDTree extends DragNDropTree {
treeListener = null;
}
/**
* Get the view list.
*
* @return ArrayList list of tree paths in the view
*/
ArrayList<TreePath> getViewList() {
List<TreePath> getViewList() {
return viewList;
}
/**
* Get the node list.
*/
ArrayList<ProgramNode> getNodeList() {
List<ProgramNode> getNodeList() {
return nodeList;
}
@ -435,10 +428,6 @@ public class ProgramDnDTree extends DragNDropTree {
}
}
/**
* Adds path to the view.
* @param path
*/
void addToView(TreePath path) {
if (path == null) {
return;
@ -666,36 +655,6 @@ public class ProgramDnDTree extends DragNDropTree {
}
}
/**
* Return whether the given action should be added to popup, based
* on what is currently selected. Called by the ProgramTreeAction.
*/
boolean addActionToPopup(DockingAction action) {
if (!(action instanceof ProgramTreeAction)) {
return true;
}
ProgramTreeAction a = (ProgramTreeAction) action;
int selectionCount = getSelectionCount();
if (a.getSelectionType() == ProgramTreeAction.SINGLE_SELECTION) {
if (selectionCount == 1) {
return true;
}
else if (selectionCount == 0) {
return true;
}
return false;
}
// allow 1 or many in selection
if (selectionCount > 0) {
return true;
}
return false;
}
/**
* Generate a unique name to be used as the default when
* a Module or Fragment is created.
@ -730,25 +689,6 @@ public class ProgramDnDTree extends DragNDropTree {
}
}
/**
* Build a list of selected ProgramNodes in postorder.
*/
// our data; we know it's good
ArrayList<ProgramNode> getSortedSelection() {
ArrayList<ProgramNode> list = new ArrayList<>();
Enumeration<? extends TreeNode> it = root.postorderEnumeration();
while (it.hasMoreElements()) {
ProgramNode node = (ProgramNode) it.nextElement();
if (isPathSelected(node.getTreePath())) {
list.add(node);
}
}
return list;
}
/**
* Expand all descendants starting at node.
*/
void expandNode(ProgramNode node) {
expandPath(node.getTreePath());
@ -837,7 +777,7 @@ public class ProgramDnDTree extends DragNDropTree {
* @param sb string buffer to use if there was an error
* @return true if group was removed
*/
boolean removeGroup(ProgramNode node, StringBuffer sb) {
boolean removeGroup(ProgramNode node, StringBuilder sb) {
boolean changesMade = false;
@ -869,6 +809,7 @@ public class ProgramDnDTree extends DragNDropTree {
* @param parent parent of the group to be inserted
* @param group group to add
* @param index index of new child
* @return the new child node
*/
ProgramNode insertGroup(ProgramNode parent, Group group, int index) {
@ -1086,64 +1027,45 @@ public class ProgramDnDTree extends DragNDropTree {
}
}
/**
* Disable all actions.
*/
void disableActions() {
actionManager.disableActions();
}
/**
* Adjust the selection based on the given popupPoint.
* @param event mouse event
* @return node that is selected
*/
ProgramNode prepareSelectionForPopup(MouseEvent event) {
// adjust the selection based on the popup location
ProgramNode adjustSelectionForPopup(MouseEvent event) {
synchronized (root) {
if (event != null && event.getSource() != this) {
return null;
}
Point popupPoint = event != null ? event.getPoint() : null;
int nselected = getSelectionCount();
TreePath selPath = null;
if (popupPoint != null) {
selPath = getPathForLocation((int) popupPoint.getX(), (int) popupPoint.getY());
}
else {
selPath = getSelectionPath();
}
ProgramNode node = null;
if (selPath != null) {
node = (ProgramNode) selPath.getLastPathComponent();
return getMouseNode(popupPoint);
}
if (nselected <= 1) {
int nselected = getSelectionCount();
if (nselected < 1) {
return null; // nothing selected in the tree
}
if (selPath != null && !isPathSelected(selPath)) {
setSelectionPath(selPath);
actionManager.adjustSingleActions(node);
return node;
}
if (selPath != null) {
actionManager.adjustSingleActions(node);
return node;
}
actionManager.disableActions();
return null;
TreePath activePath = getSelectionPath();
return (ProgramNode) activePath.getLastPathComponent();
}
}
private ProgramNode getMouseNode(Point popupPoint) {
TreePath mousePath = getPathForLocation((int) popupPoint.getX(), (int) popupPoint.getY());
if (mousePath != null) {
ProgramNode node = (ProgramNode) mousePath.getLastPathComponent();
if (!isPathSelected(mousePath)) {
setSelectionPath(mousePath); // add the right-clicked node to the selection
}
// if the path at the mouse pointer is in the selection OR
// the path is null, then adjust the multi-popup menu.
if ((selPath != null && isPathSelected(selPath)) || selPath == null) {
actionManager.adjustMultiActions();
return node;
}
// force the selection to be where the mouse pointer is
setSelectionPath(selPath);
actionManager.adjustSingleActions(node);
return node;
}
return null;
}
/**
@ -1171,9 +1093,7 @@ public class ProgramDnDTree extends DragNDropTree {
ArrayList<ProgramNode> list = new ArrayList<>();
for (int i = 0; i < nodeList.size(); i++) {
ProgramNode node = nodeList.get(i);
for (ProgramNode node : nodeList) {
if (node.getName().equals(groupName)) {
list.add(node);
}
@ -1358,9 +1278,7 @@ public class ProgramDnDTree extends DragNDropTree {
ArrayList<ProgramNode> list = new ArrayList<>();
for (int i = 0; i < nodeList.size(); i++) {
ProgramNode node = nodeList.get(i);
for (ProgramNode node : nodeList) {
Group group = node.getGroup();
if (group != null && group.equals(g)) {
list.add(node);

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -45,6 +45,8 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Construct a new ProgramNode with the given Group.
* @param program the program
* @param g the group
*/
ProgramNode(Program program, Group g) {
this(program, g, g.getName());
@ -52,6 +54,8 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Construct a new ProgramNode with the given name.
* @param program the program
* @param name the name
*/
ProgramNode(Program program, String name) {
this(program, null, name);
@ -60,6 +64,9 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Create a new ProgramNode with the given group and name;
* use name for the displayed name of this node.
* @param program the program
* @param g the group
* @param name the name
*/
ProgramNode(Program program, Group g, String name) {
super(name);
@ -148,6 +155,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Get the name for this node.
* @return he name for this node.
*/
public String getName() {
return name;
@ -155,6 +163,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Get the group for this node.
* @return the group for this node.
*/
public Group getGroup() {
return group;
@ -162,6 +171,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Returns true if this node represents a Fragment.
* @return true if this node represents a Fragment.
*/
public boolean isFragment() {
return fragment != null;
@ -169,6 +179,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Returns true if this node represents a Module.
* @return true if this node represents a Module.
*/
public boolean isModule() {
return module != null;
@ -192,6 +203,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Get the program for this node.
* @return the program for this node.
*/
public Program getProgram() {
return program;
@ -199,6 +211,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Return true if the node is in the view.
* @return true if the node is in the view.
*/
public boolean isInView() {
return isInView;
@ -206,14 +219,12 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Get the group path for this node.
* @return the group path for this node.
*/
public GroupPath getGroupPath() {
return groupPath;
}
/////////////////////////////////////////////////////////////
// package-level methods
/////////////////////////////////////////////////////////////
/**
* Mark this node as having been populated (visited).
*/
@ -227,6 +238,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Return true if this node was visited.
* @return true if this node was visited.
*/
boolean wasVisited() {
return visited;
@ -238,6 +250,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Get the tree path for this node.
* @return the tree path for this node.
*/
TreePath getTreePath() {
return path;
@ -245,6 +258,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Set the tree path for this node.
* @param path the tree path for this node.
*/
void setTreePath(TreePath path) {
this.path = path;
@ -252,6 +266,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Get the parent module for this node.
* @return the parent module for this node.
*/
ProgramModule getParentModule() {
return parentModule;
@ -259,6 +274,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Set the parent module for this node.
* @param parent the parents
*/
void setParentModule(ProgramModule parent) {
parentModule = parent;
@ -266,6 +282,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Set the name for this node.
* @param name the name
*/
void setName(String name) {
this.name = name;
@ -273,8 +290,8 @@ public class ProgramNode extends DefaultMutableTreeNode {
}
/**
* Set this node to be deleted so that it can be
* rendered as such.
* Set this node to be deleted so that it can be rendered as such.
* @param deleted true if deleted
*/
void setDeleted(boolean deleted) {
this.deleted = deleted;
@ -282,6 +299,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Returns whether this node is marked as deleted.
* @return whether this node is marked as deleted.
*/
boolean isDeleted() {
return deleted;
@ -289,6 +307,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Set the group path for this node.
* @param groupPath the path
*/
void setGroupPath(GroupPath groupPath) {
this.groupPath = groupPath;
@ -296,6 +315,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Mark this node as being in some view.
* @param isInView true if in some view.
*/
void setInView(boolean isInView) {
this.isInView = isInView;
@ -326,6 +346,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Set the tree; this method is only called on the root node.
* @param tree the tree
*/
void setTree(ProgramDnDTree tree) {
this.tree = tree;
@ -358,7 +379,7 @@ public class ProgramNode extends DefaultMutableTreeNode {
/**
* Get the node named childName.
* @param childName
* @param childName the name
* @return null if the node does not allow children, or the name was
* not found.
*/

View File

@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -28,38 +27,40 @@ import docking.action.KeyBindingData;
*/
abstract class ProgramTreeAction extends DockingAction {
final static int SINGLE_SELECTION=0;
final static int MULTI_SELECTION=1;
final static int SINGLE_SELECTION = 0;
final static int MULTI_SELECTION = 1;
private int selectionType;
private int selectionType;
ProgramTreeAction(String name, String owner, String[] defaultPopupPath, KeyStroke defaultKeyBinding) {
this(name, owner, defaultPopupPath,
defaultKeyBinding, MULTI_SELECTION);
}
ProgramTreeAction(String name, String owner,
String[] defaultPopupPath,
KeyStroke defaultKeyBinding,int selectionType) {
super(name, owner);
this.selectionType = selectionType;
if (defaultKeyBinding != null) {
setKeyBindingData( new KeyBindingData( defaultKeyBinding ) );
}
setEnabled(false);
}
@Override
public boolean isValidContext(ActionContext context) {
return context.getContextObject() instanceof ProgramNode;
}
@Override
public boolean isAddToPopup(ActionContext context) {
return true;
}
/**
* Get the selection type associated with this action.
*/
int getSelectionType() {
return selectionType;
}
ProgramTreeAction(String name, String owner, KeyStroke defaultKeyBinding) {
this(name, owner, defaultKeyBinding, MULTI_SELECTION);
}
ProgramTreeAction(String name, String owner, KeyStroke defaultKeyBinding, int selectionType) {
super(name, owner);
this.selectionType = selectionType;
if (defaultKeyBinding != null) {
setKeyBindingData(new KeyBindingData(defaultKeyBinding));
}
setEnabled(false);
}
@Override
public boolean isValidContext(ActionContext context) {
return context.getContextObject() instanceof ProgramNode;
}
@Override
public boolean isAddToPopup(ActionContext context) {
return true;
}
/**
* Get the selection type associated with this action.
* @return the selection type
*/
int getSelectionType() {
return selectionType;
}
}

View File

@ -0,0 +1,170 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.programtree;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import ghidra.app.context.ProgramActionContext;
import ghidra.program.model.listing.Program;
/**
* A context object for the {@link ProgramTreePlugin}.
*/
public class ProgramTreeActionContext extends ProgramActionContext {
private ViewManagerComponentProvider provider;
public ProgramTreeActionContext(ViewManagerComponentProvider provider, Program program,
ViewPanel viewPanel, Object contextObject) {
super(provider, program, viewPanel, contextObject);
this.provider = provider;
}
public ProgramDnDTree getTree() {
ViewProviderService viewProvider = provider.getCurrentViewProvider();
if (!(viewProvider instanceof TreeViewProvider treeProvider)) {
return null;
}
ProgramTreePanel treePanel = treeProvider.getViewComponent();
return treePanel.getDnDTree();
}
public TreePath[] getSelectionPaths() {
ProgramDnDTree tree = getTree();
if (tree == null) {
return null;
}
return tree.getSelectionPaths();
}
public ProgramNode getLeadSelectedNode() {
ProgramNode node = getSingleSelectedNode();
if (node != null) {
return node; // only one node selected
}
ProgramDnDTree tree = getTree();
if (tree == null) {
return null;
}
int n = tree.getSelectionCount();
if (n == 0) {
return null;
}
TreePath path = tree.getSelectionPath();
if (n > 1) {
path = tree.getLeadSelectionPath();
}
return (ProgramNode) path.getLastPathComponent();
}
public ProgramNode getSingleSelectedNode() {
TreePath[] paths = getSelectionPaths();
if (paths == null || paths.length != 1) {
return null;
}
return (ProgramNode) paths[0].getLastPathComponent();
}
public boolean hasSingleNodeSelection() {
ProgramDnDTree tree = getTree();
if (tree == null) {
return false;
}
return tree.getSelectionCount() == 1;
}
public boolean isOnlyRootNodeSelected() {
ProgramDnDTree tree = getTree();
if (tree == null) {
return false;
}
TreePath[] paths = tree.getSelectionPaths();
if (paths == null || paths.length != 1) {
return false;
}
ProgramNode node = (ProgramNode) paths[0].getLastPathComponent();
DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel();
ProgramNode root = (ProgramNode) treeModel.getRoot();
return node == root;
}
/**
* Returns true if the selected paths: 1) do not contain the root node and 2) for each folder,
* either all children are selected or no children are selected.
*
* @return true if the criteria above are met
*/
public boolean hasFullNodeMultiSelection() {
ProgramDnDTree tree = getTree();
if (tree == null) {
return false;
}
TreePath[] paths = tree.getSelectionPaths();
if (paths == null) {
return false;
}
DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel();
ProgramNode root = (ProgramNode) treeModel.getRoot();
for (TreePath path : paths) {
ProgramNode node = (ProgramNode) path.getLastPathComponent();
if (node == root) {
return false;
}
if (hasMixedChildSelection(node, paths)) {
return false;
}
}
return true;
}
private boolean hasMixedChildSelection(ProgramNode node, TreePath[] selectedPaths) {
if (!node.getAllowsChildren()) {
return false;
}
int nchild = node.getChildCount();
int numberSelected = 0;
for (int i = 0; i < nchild; i++) {
ProgramNode child = (ProgramNode) node.getChildAt(i);
TreePath childPath = child.getTreePath();
// see if childPath is in selected list
for (TreePath element : selectedPaths) {
if (childPath.equals(element)) {
++numberSelected;
break;
}
}
}
if (numberSelected == 0 || numberSelected == nchild) {
return false;
}
return true;
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -19,6 +19,7 @@ import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
@ -56,9 +57,9 @@ class ProgramTreePanel extends JPanel implements ChangeListener {
initialize();
// Disable tree expand/collapse on double-click.
if (tree != null) {
tree.setToggleClickCount(0);
}
tree.setToggleClickCount(0);
tree.addTreeSelectionListener(e -> plugin.contextChanged());
}
// ChangeListener interface method
@ -160,10 +161,9 @@ class ProgramTreePanel extends JPanel implements ChangeListener {
* Get the currently viewed group paths.
*/
GroupView getGroupView() {
ArrayList<TreePath> viewList = tree.getViewList();
List<TreePath> viewList = tree.getViewList();
ArrayList<GroupPath> list = new ArrayList<GroupPath>();
for (int i = 0; i < viewList.size(); i++) {
TreePath p = viewList.get(i);
for (TreePath p : viewList) {
ProgramNode node = (ProgramNode) p.getLastPathComponent();
GroupPath gp = node.getGroupPath();
if (gp != null) {
@ -191,7 +191,7 @@ class ProgramTreePanel extends JPanel implements ChangeListener {
}
ProgramNode prepareSelectionForPopup(MouseEvent event) {
return tree.prepareSelectionForPopup(event);
return tree.adjustSelectionForPopup(event);
}
GroupPath[] getViewedGroups() {

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -83,14 +83,15 @@ public class ProgramTreePlugin extends ProgramPlugin
private final static Icon NAVIGATION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
private ViewManagerComponentProvider componentProvider;
private ProgramTreeActionManager actionManager;
private TreeViewProvider defaultProvider;
private TreeViewProvider currentProvider;
private Map<String, TreeViewProvider> providerMap;// map of view providers, key is the name
private GoToService goToService;
private ViewManagerService viewManagerService;
private ProgramTreeActionManager actionManager;
private TreeViewProvider currentProvider;
private ViewManagerComponentProvider viewProvider;
private ProgramListener programListener;
private TreeViewProvider defaultProvider;
private boolean firingGoTo;
private RunManager runManager;
private DockingAction createAction;
@ -99,31 +100,30 @@ public class ProgramTreePlugin extends ProgramPlugin
private JPopupMenu popup;
/**
* Tree signals that a user double-click will replace the view with the
* current node
* Tree signals that a user double-click will replace the view with the current node
*/
private boolean isReplaceViewMode = true;
private boolean isReplaceViewMode = false;
public ProgramTreePlugin(PluginTool tool) {
super(tool);
viewProvider = new ViewManagerComponentProvider(tool, getName());
registerServiceProvided(ViewManagerService.class, viewProvider);
componentProvider = new ViewManagerComponentProvider(tool, getName());
registerServiceProvided(ViewManagerService.class, componentProvider);
providerMap = new HashMap<>();
actionManager = new ProgramTreeActionManager(this);
registerActions();
registerProviderActions();
programListener = new ProgramListener(this);
runManager = new RunManager();
runManager.showProgressBar(false);
createActions();
createPluginActions();
// show default provider
defaultProvider = addTreeView(DEFAULT_TREE_NAME);
initOptions(tool.getOptions(PROGRAM_TREE_OPTION_NAME));
}
@Override
@ -131,7 +131,7 @@ public class ProgramTreePlugin extends ProgramPlugin
if (interfaceClass != ViewProviderService.class) {
return;
}
viewProvider.serviceAdded((ViewProviderService) service);
componentProvider.serviceAdded((ViewProviderService) service);
}
/**
@ -142,7 +142,7 @@ public class ProgramTreePlugin extends ProgramPlugin
if (interfaceClass != ViewProviderService.class) {
return;
}
viewProvider.serviceRemoved((ViewProviderService) service);
componentProvider.serviceRemoved((ViewProviderService) service);
}
private void initOptions(ToolOptions options) {
@ -218,7 +218,7 @@ public class ProgramTreePlugin extends ProgramPlugin
programListener.dispose();
}
viewProvider.dispose();
componentProvider.dispose();
super.dispose();
}
@ -246,7 +246,7 @@ public class ProgramTreePlugin extends ProgramPlugin
*/
@Override
public void writeDataState(SaveState saveState) {
viewProvider.writeDataState(saveState);
componentProvider.writeDataState(saveState);
saveState.putInt(NUMBER_OF_VIEWS, providerMap.size());
int idx = 0;
@ -312,7 +312,7 @@ public class ProgramTreePlugin extends ProgramPlugin
restoreTreeViews();
viewProvider.readDataState(saveState);
componentProvider.readDataState(saveState);
}
private void restoreTreeViews() {
@ -332,7 +332,7 @@ public class ProgramTreePlugin extends ProgramPlugin
list.add(provider);
}
viewProvider.treeViewsRestored(list);
componentProvider.treeViewsRestored(list);
}
@Override
@ -369,12 +369,11 @@ public class ProgramTreePlugin extends ProgramPlugin
protected void programActivated(Program program) {
program.addListener(programListener);
setProgram(program);
viewProvider.setCurrentProgram(program);
componentProvider.setCurrentProgram(program);
}
private void removeStaleProviders(ArrayList<TreeViewProvider> providerList) {
HashMap<String, TreeViewProvider> map = new HashMap<>(providerMap);
Map<String, TreeViewProvider> map = new HashMap<>(providerMap);
for (String treeName : map.keySet()) {
TreeViewProvider provider = map.get(treeName);
if (!providerList.contains(provider)) {
@ -384,9 +383,6 @@ public class ProgramTreePlugin extends ProgramPlugin
}
}
/**
* Initialization method: Get the services we need.
*/
@Override
protected void init() {
goToService = tool.getService(GoToService.class);
@ -414,6 +410,10 @@ public class ProgramTreePlugin extends ProgramPlugin
}
}
void contextChanged() {
tool.contextChanged(componentProvider);
}
void treeViewAdded(String treeName) {
TreeViewProvider provider = providerMap.get(treeName);
if (provider == null) {
@ -430,10 +430,8 @@ public class ProgramTreePlugin extends ProgramPlugin
currentProvider.replaceView(node);
}
// If the node is NOT the root node, just go to the location
// of the first address in the fragment. If it's root, we
// need to get the lowest address of any item in the
// current view.
// If the node is NOT the root node, just go to the location of the first address in the
// fragment. If it's the root, we need to get the lowest address in the current view.
if (node.isFragment()) {
goTo(node.getFragment());
}
@ -445,7 +443,7 @@ public class ProgramTreePlugin extends ProgramPlugin
}
}
void goTo(ProgramFragment fragment) {
private void goTo(ProgramFragment fragment) {
Address minAddress = fragment.getMinAddress();
@ -671,6 +669,10 @@ public class ProgramTreePlugin extends ProgramPlugin
reloadTree(tree, false);
}
void repaintProvider() {
componentProvider.getComponent().repaint();
}
/**
* Remember expansion and selection state, and the reload the program
* because it just got restored from an undo operation.
@ -781,10 +783,10 @@ public class ProgramTreePlugin extends ProgramPlugin
return currentProgram.getListing().getRootModule(treeName) != null;
}
private void registerActions() {
private void registerProviderActions() {
DockingAction[] actions = actionManager.getActions();
for (DockingAction element : actions) {
tool.addAction(element);
for (DockingAction action : actions) {
tool.addLocalAction(componentProvider, action);
}
}
@ -936,7 +938,7 @@ public class ProgramTreePlugin extends ProgramPlugin
/**
* Create the local actions that are shared among all the providers.
*/
private void createActions() {
private void createPluginActions() {
openAction = new DockingAction("Open Tree View", getName()) {
@Override

View File

@ -0,0 +1,81 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.programtree;
import java.awt.datatransfer.*;
import java.io.IOException;
import java.util.*;
import docking.dnd.GenericDataFlavor;
/**
* Defines data that is available for drag/drop and clipboard transfers.
* The data is an ArrayList of ProgramNode objects.
*/
class ProgramTreeTransferable implements Transferable, ClipboardOwner {
public static DataFlavor localTreeNodeFlavor = createLocalTreeNodeFlavor();
// create a data flavor that is an ArrayList of ProgramNode objects
private static DataFlavor createLocalTreeNodeFlavor() {
return new GenericDataFlavor(
DataFlavor.javaJVMLocalObjectMimeType + "; class=java.util.ArrayList",
"Local list of Tree Nodes");
}
private static DataFlavor[] flavors = { localTreeNodeFlavor };
private static List<DataFlavor> flavorList = Arrays.asList(flavors);
private List<ProgramNode> nodeList;
ProgramTreeTransferable(ProgramNode[] nodes) {
nodeList = new ArrayList<ProgramNode>(Arrays.asList(nodes));
}
@Override
public synchronized DataFlavor[] getTransferDataFlavors() {
return flavors;
}
@Override
public boolean isDataFlavorSupported(DataFlavor f) {
return flavorList.contains(f);
}
@Override
public synchronized Object getTransferData(DataFlavor f)
throws UnsupportedFlavorException, IOException {
if (f.equals(localTreeNodeFlavor)) {
return nodeList;
}
throw new UnsupportedFlavorException(f);
}
@Override
public String toString() {
return "TreeTransferable";
}
@Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
// nothing to do
}
void clearTransferData() {
nodeList = null;
}
}

View File

@ -1,109 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.programtree;
import ghidra.util.Msg;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import docking.dnd.GenericDataFlavor;
/**
* Defines data that is available for drag/drop and clipboard transfers.
* The data is an ArrayList of ProgramNode objects.
*/
class TreeTransferable implements Transferable, ClipboardOwner {
public static DataFlavor localTreeNodeFlavor = createLocalTreeNodeFlavor();
// create a data flavor that is an ArrayList of
// ProgramNode objects
private static DataFlavor createLocalTreeNodeFlavor() {
try {
return new GenericDataFlavor(
DataFlavor.javaJVMLocalObjectMimeType+
"; class=java.util.ArrayList",
"Local list of Tree Nodes");
}catch (Exception e) {
Msg.showError(TreeTransferable.class, null, null, null, e);
}
return null;
}
private static DataFlavor []flavors= { localTreeNodeFlavor };
private static List<DataFlavor> flavorList = Arrays.asList(flavors);
private ArrayList<ProgramNode> nodeList;
/**
* Constructor
*/
TreeTransferable(ProgramNode []nodes) {
nodeList = new ArrayList<ProgramNode>(Arrays.asList(nodes));
}
/**
* Return all data flavors that this class supports.
*/
public synchronized DataFlavor []getTransferDataFlavors() {
return flavors;
}
/**
* Return whether the specified data flavor is supported.
*/
public boolean isDataFlavorSupported(DataFlavor f) {
return flavorList.contains(f);
}
/**
* Return the transfer data with the given data flavor.
*/
public synchronized Object getTransferData(DataFlavor f)
throws UnsupportedFlavorException, IOException {
if (f.equals(localTreeNodeFlavor)) {
return nodeList;
}
throw new UnsupportedFlavorException(f);
}
/**
* Get the string representation for this transferable.
*/
@Override
public String toString() {
return "TreeTransferable";
}
/**
* ClipboardOwner interface method.
*/
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}
void clearTransferData() {
nodeList = null;
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -19,7 +19,6 @@ 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;
@ -76,7 +75,7 @@ class TreeViewProvider implements ViewProviderService {
}
@Override
public JComponent getViewComponent() {
public ProgramTreePanel getViewComponent() {
return treePanel;
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,8 +16,7 @@
package ghidra.app.plugin.core.programtree;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.*;
import javax.swing.JComponent;
@ -40,7 +39,7 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
public static final String CURRENT_VIEW = "Current Viewname";
private ViewPanel viewPanel;
private ArrayList<ViewChangeListener> listeners;
private List<ViewChangeListener> listeners;
private Program currentProgram;
private String restoredViewName;
@ -72,6 +71,8 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
String currentName = NAME;
ComponentProvider.registerProviderNameOwnerChange(intermediateName, currentOwner,
currentName, currentOwner);
addToTool();
}
void serviceAdded(ViewProviderService service) {
@ -186,11 +187,14 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter
}
if (event != null) {
return new ProgramActionContext(this, currentProgram, viewPanel,
getActivePopupObject(event));
// this should be a ProgramNode or a tab
Object clickedObject = getActivePopupObject(event);
return new ProgramTreeActionContext(this, currentProgram, viewPanel, clickedObject);
}
return new ProgramActionContext(this, currentProgram, viewPanel, getFocusedContext());
// this should be a ProgramNode
Object focusedObject = getFocusedContext();
return new ProgramTreeActionContext(this, currentProgram, viewPanel, focusedObject);
}
private Object getFocusedContext() {

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,8 +17,7 @@ package ghidra.app.plugin.core.programtree;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
@ -43,7 +42,7 @@ class ViewPanel extends JPanel implements ChangeListener {
private JTabbedPane tabbedPane;
private ViewManagerComponentProvider provider;
private HashMap<String, ViewProviderService> map;
private Map<String, ViewProviderService> map;
private DockingAction closeAction;
private DockingAction deleteAction;
private DockingAction renameAction;
@ -71,10 +70,6 @@ class ViewPanel extends JPanel implements ChangeListener {
*/
void addView(ViewProviderService vp) {
if (!provider.isInTool()) {
provider.addToTool();
}
String name = vp.getViewName();
if (map.remove(name) != null) {
map.put(name, vp);
@ -127,10 +122,6 @@ class ViewPanel extends JPanel implements ChangeListener {
tabbedPane.addChangeListener(this);
}
/*if (isEmpty()) {
provider.removeFromTool();
}*/
return true;
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,8 +15,7 @@
*/
package ghidra.app.plugin.core.module;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import java.util.*;
@ -54,8 +53,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
tool.addPlugin(ProgramTreePlugin.class.getName());
tool.addPlugin(ModuleSortPlugin.class.getName());
List<Plugin> list = tool.getManagedPlugins();
for (int i = 0; i < list.size(); i++) {
Plugin p = list.get(i);
for (Plugin p : list) {
if (p.getClass() == ModuleSortPlugin.class) {
plugin = (ModuleSortPlugin) p;
break;
@ -200,8 +198,6 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest {
ViewManagerService vmService = tool.getService(ViewManagerService.class);
ViewProviderService vps = vmService.getCurrentViewProvider();
Object context = vps.getActivePopupObject(null);
for (DockingActionIf action : actions) {
if (action.getName().indexOf("Address") > 0) {
action.actionPerformed(vps.getActionContext(null));

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.programtree;
import java.awt.Component;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import javax.swing.tree.TreePath;
@ -87,6 +88,15 @@ public abstract class AbstractProgramTreePluginTest extends AbstractGhidraHeaded
return context;
}
protected void performTreeAction(DockingActionIf action) {
performAction(action, getActionContext(), true);
}
protected void waitForTreeEdit() {
BooleanSupplier bs = () -> runSwing(() -> tree.isEditing());
waitFor(bs);
}
protected void setTreeView(final String viewName) {
tree = plugin.getTree(viewName);
root = (ProgramNode) tree.getModel().getRoot();

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -157,7 +157,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
DockingActionIf createFolderAction = getAction("Create Folder");
String newFolderName = tree.getNewFolderName();
performAction(createFolderAction);
performTreeAction(createFolderAction);
commitEdit();
waitForProgram(program);
@ -181,7 +181,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
DockingActionIf createFragmentAction = getAction("Create Fragment");
String newFragName = tree.getNewFragmentName();
performAction(createFragmentAction);
performTreeAction(createFragmentAction);
commitEdit();
waitForProgram(program);
@ -206,7 +206,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
int childCount = root.getChildCount();
DockingActionIf createFolderAction = getAction("Create Folder");
performAction(createFolderAction);
performTreeAction(createFolderAction);
commitEdit();
waitForProgram(program);
@ -221,9 +221,9 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
String newName = tree.getNewFolderName();
DockingActionIf createFolderAction = getAction("Create Folder");
performAction(createFolderAction, getActionContext(), true);
performTreeAction(createFolderAction);
waitForBusyTool(tool);
waitForTreeEdit();
String currentText = setEditorText("test1");
assertEquals(newName, currentText);
@ -241,7 +241,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
int childCount = root.getChildCount();
DockingActionIf createFragmentAction = getAction("Create Fragment");
performAction(createFragmentAction, getActionContext(), true);
performTreeAction(createFragmentAction);
commitEdit();
waitFor(() -> root.getChildCount() == childCount + 1);
@ -256,9 +256,9 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
String newName = tree.getNewFragmentName();
DockingActionIf createFragmentAction = getAction("Create Fragment");
performAction(createFragmentAction, getActionContext(), true);
performTreeAction(createFragmentAction);
waitForBusyTool(tool);
waitForTreeEdit();
String currentText = setEditorText("test1");
assertEquals(newName, currentText);
@ -278,7 +278,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
setSelectionPath(node);
DockingActionIf deleteAction = getAction("Delete");
performAction(deleteAction, getActionContext(), true);
performTreeAction(deleteAction);
waitForProgram(program);
assertEquals(childCount - 1, root.getChildCount());
@ -409,7 +409,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
DockingActionIf action = getAction(plugin, "Rename folder/fragment");
assertTrue(action.isEnabledForContext(getActionContext()));
performAction(action);
performTreeAction(action);
String currentText = setEditorText("printf");
assertEquals("submodule", currentText);
}
@ -429,7 +429,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
setSelectionPath(nodes[0]);
DockingActionIf action = getAction(plugin, "Rename folder/fragment");
assertTrue(action.isEnabledForContext(getActionContext()));
performAction(action);
performTreeAction(action);
String currentText = setEditorText(".data");
ProgramModule module = getModule(root, "Module-1");
@ -451,7 +451,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
setSelectionPath(nodes[0]);
DockingActionIf action = getAction(plugin, "Rename folder/fragment");
assertTrue(action.isEnabledForContext(getActionContext()));
performAction(action);
performTreeAction(action);
String currentText = setEditorText("My Module-1");
assertEquals("My Module-1", currentText);
waitForProgram(program);
@ -648,7 +648,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
DockingActionIf removeAction = getAction("Remove");
assertTrue(removeAction.isEnabledForContext(getActionContext()));
performAction(removeAction, getActionContext(), true);
performTreeAction(removeAction);
AddressSet set = new AddressSet();
set.add(node.getFragment());
@ -705,13 +705,13 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
}
setSelectionPaths(node);
DockingActionIf replaceAction = getAction("Replace");
performAction(replaceAction);
DockingActionIf replaceAction = getAction("Set View");
performTreeAction(replaceAction);
assertTrue(getView().hasSameAddresses(node.getModule().getAddressSet()));
DockingActionIf removeAction = getAction("Remove");
performAction(removeAction);
performTreeAction(removeAction);
assertTrue(getView().isEmpty());
assertTrue(cbPlugin.getView().isEmpty());
@ -750,7 +750,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
setSelectionPaths(node0, node2);
DockingActionIf removeAction = getAction("Remove");
performAction(removeAction);
performTreeAction(removeAction);
AddressSet set = new AddressSet();
set.add(node0.getFragment());
@ -799,7 +799,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
setSelectionPath(nodes[0]);
DockingActionIf removeAction = getAction("Remove");
performAction(removeAction);
performTreeAction(removeAction);
// verify that all the descendants of the folder are removed from the view
assertFalse(getView().contains(child1.getFragment()));
@ -819,8 +819,8 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
ProgramNode child = (ProgramNode) node.getChildAt(0);
setSelectionPath(child);
DockingActionIf replaceAction = getAction("Replace");
performAction(replaceAction);
DockingActionIf replaceAction = getAction("Set View");
performTreeAction(replaceAction);
assertTrue(getView().hasSameAddresses(child.getFragment()));
assertTrue(getView().hasSameAddresses(cbPlugin.getView()));
@ -841,9 +841,9 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
ProgramNode node = (ProgramNode) root.getChildAt(6);
setSelectionPath(node);
DockingActionIf replaceAction = getAction("Replace");
DockingActionIf replaceAction = getAction("Set View");
assertTrue(replaceAction.isEnabledForContext(getActionContext()));
performAction(replaceAction, getActionContext(), true);
performTreeAction(replaceAction);
assertTrue(plugin.getView().hasSameAddresses(node.getModule().getAddressSet()));
assertPluginViewAppliedToTool();
@ -869,8 +869,8 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
// descendant of that folder, and not in the view
setSelectionPaths(dllsNode, sscanfNode);
DockingActionIf replaceAction = getAction("Replace");
performAction(replaceAction);
DockingActionIf replaceAction = getAction("Set View");
performTreeAction(replaceAction);
AddressSet set = new AddressSet();
set.add(dllsNode.getModule().getAddressSet());
@ -921,7 +921,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
setSelectionPath(dllsNode);
DockingActionIf mergeAction = getAction("Merge");
performAction(mergeAction);
performTreeAction(mergeAction);
waitForProgram(program);
int count = nodes[0].getChildCount();
@ -954,7 +954,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
setSelectionPaths(dNodes[0], bNodes[0]);
DockingActionIf mergeAction = getAction("Merge");
performAction(mergeAction, getActionContext(), true);
performTreeAction(mergeAction);
waitForProgram(program);
assertEquals(7, root.getChildCount());
@ -1104,7 +1104,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest {
assertTrue(showAction.isEnabledForContext(getActionContext()));
String[] treeNames = program.getListing().getTreeNames();
performAction(showAction);
performTreeAction(showAction);
JPopupMenu menu = plugin.getPopupMenu();
assertNotNull(menu);

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -24,7 +24,6 @@ import javax.swing.tree.TreePath;
import org.junit.*;
import docking.DefaultActionContext;
import docking.action.DockingActionIf;
import generic.theme.GIcon;
import ghidra.program.database.ProgramBuilder;
@ -108,14 +107,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(node);
assertTrue(copyAction.isEnabled());
performAction(copyAction);
performTreeAction(copyAction);
node = (ProgramNode) root.getChildAt(5);
int origCount = node.getChildCount();
setSelectionPath(node);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
expandNode(node);
@ -142,7 +141,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode node = (ProgramNode) root.getChildAt(0);
setSelectionPath(node);
assertTrue(copyAction.isEnabled());
performAction(copyAction);
performTreeAction(copyAction);
setSelectionPath(root);
@ -161,7 +160,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(node);
assertTrue(copyAction.isEnabled());
performAction(copyAction, true);
performTreeAction(copyAction);
ProgramNode[] nodes = findNodes("C");
setSelectionPath(nodes[0]);
@ -180,7 +179,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
// select USER32.DLL
setSelectionPath(fnode);
performAction(copyAction);
performTreeAction(copyAction);
// select DLLs
node = (ProgramNode) root.getChildAt(6);
setSelectionPath(node);
@ -200,10 +199,10 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(cpNode);
performAction(copyAction);
performTreeAction(copyAction);
setSelectionPath(pasteNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
assertEquals(childCount + 1, pasteNode.getChildCount());
@ -228,7 +227,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode cnode2 = (ProgramNode) node.getChildAt(1);
setSelectionPaths(cnode1, cnode2);
assertTrue(copyAction.isEnabled());
performAction(copyAction);
performTreeAction(copyAction);
// create a new module and paste fragments there
tx(program, () -> {
root.getModule().createModule("Test");
@ -238,7 +237,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(destNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
visitNode(destNode);
@ -253,7 +252,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode cnode2 = (ProgramNode) node.getChildAt(1);
setSelectionPaths(cnode1, cnode2);
assertTrue(copyAction.isEnabled());
performAction(copyAction);
performTreeAction(copyAction);
// create a new module and paste fragments there
tx(program, () -> {
ProgramModule m = root.getModule().createModule("Test");
@ -266,7 +265,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(destNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
expandNode(root);
@ -300,7 +299,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode cnode2 = (ProgramNode) node.getChildAt(1);
setSelectionPaths(cnode1, cnode2);
assertTrue(copyAction.isEnabled());
performAction(copyAction);
performTreeAction(copyAction);
// get node for DLLs
ProgramNode destNode = (ProgramNode) root.getChildAt(5);
@ -308,7 +307,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(destNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
expandNode(root);
@ -341,12 +340,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
// set the view to Functions
setSelectionPath(node);
setViewPaths(node);
performAction(copyAction);
performTreeAction(copyAction);
ProgramNode subrNode = (ProgramNode) root.getChildAt(8);
setSelectionPath(subrNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
// verify the view is not affected
@ -375,10 +374,10 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(subrNode);
performAction(copyAction);
performTreeAction(copyAction);
setSelectionPath(node);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
assertTrue(plugin.getView().hasSameAddresses(node.getModule().getAddressSet()));
@ -399,12 +398,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode child = (ProgramNode) node.getChildAt(0);
setSelectionPath(child);
performAction(copyAction);
performTreeAction(copyAction);
ProgramNode subrNode = (ProgramNode) root.getChildAt(8);
setSelectionPath(subrNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
// verify the view is not affected
@ -426,14 +425,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(fnode);
assertTrue(cutAction.isEnabled());
performAction(cutAction);
performTreeAction(cutAction);
ProgramNode funcNode = (ProgramNode) root.getChildAt(6);// Functions
ProgramNode destNode = (ProgramNode) funcNode.getChildAt(0);// doStuff fragment
// now select the doStuff fragment as the destination node
setSelectionPath(destNode);
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
assertTrue(destNode.getFragment().contains(fnodeSet));
@ -479,14 +478,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(fnode);
assertTrue(cutAction.isEnabled());
performAction(cutAction);
performTreeAction(cutAction);
ProgramNode funcNode = (ProgramNode) root.getChildAt(6);// Functions
ProgramNode destNode = (ProgramNode) funcNode.getChildAt(0);// ghidra fragment
// now select the ghidra fragment as the destination node
setSelectionPath(destNode);
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
assertTrue(destNode.getFragment().contains(fnodeSet));
@ -523,14 +522,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(node);
assertTrue(cutAction.isEnabled());
performAction(cutAction);
performTreeAction(cutAction);
node = (ProgramNode) root.getChildAt(5);//DLLs
int origCount = node.getChildCount();
setSelectionPath(node);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
expandNode(node);
@ -558,10 +557,10 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(cNode);
// cut Functions
performAction(cutAction);
performTreeAction(cutAction);
setSelectionPath(destNode);// paste at DLLs
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
assertEquals(childCount + 1, destNode.getChildCount());
@ -604,12 +603,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(stringsNode);
// cut Strings
performAction(cutAction);
performTreeAction(cutAction);
// paste at Functions
ProgramNode funcNode = root.getChild("Functions");
setSelectionPath(funcNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
// Strings, L should be expanded
@ -636,13 +635,13 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(stringsNode);
// cut Strings
performAction(cutAction);
performTreeAction(cutAction);
// paste at Functions
ProgramNode funcNode = root.getChild("Functions");
collapsePath(funcNode.getTreePath());
setSelectionPath(funcNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
// Functions should remain collapsed
assertTrue(tree.isCollapsed(funcNode.getTreePath()));
@ -663,13 +662,13 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(stringsNode);
// cut Strings
performAction(cutAction);
performTreeAction(cutAction);
// paste at Functions
ProgramNode funcNode = root.getChild("Functions");
expandPath(funcNode.getTreePath());
setSelectionPath(funcNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
stringsNode = funcNode.getChild("Strings");
assertTrue(tree.isCollapsed(stringsNode.getTreePath()));
@ -690,14 +689,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPath(stringsNode);
// cut Strings
performAction(cutAction);
performTreeAction(cutAction);
// paste at Functions
ProgramNode funcNode = root.getChild("Functions");
visitNode(funcNode);
collapsePath(funcNode.getTreePath());
setSelectionPath(funcNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
assertTrue(tree.isCollapsed(funcNode.getTreePath()));
}
@ -721,14 +720,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setSelectionPaths(new TreePath[] { rsrcNode.getTreePath(), textNode.getTreePath() });
// cut Strings
runSwing(() -> cutAction.actionPerformed(new DefaultActionContext()));
performTreeAction(cutAction);
// select Strings (has no fragments)
ProgramNode stringsNode = root.getChild("Strings");
visitNode(stringsNode);
setSelectionPath(stringsNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
assertEquals(3, stringsNode.getChildCount());
assertEquals("rsrc", stringsNode.getChildAt(1).toString());
@ -788,7 +787,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
addSelectionPath(cutNodes[i].getTreePath());
}
performAction(cutAction);
performTreeAction(cutAction);
// select a destination fragment
ProgramNode stringsNode = root.getChild("Strings");
@ -798,7 +797,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode fnode = cNode.getChild("testl");
setSelectionPath(fnode);
performAction(pasteAction);
performTreeAction(pasteAction);
for (ProgramNode cutNode : cutNodes) {
assertNull(listing.getFragment("Main Tree", cutNode.getName()));
@ -859,7 +858,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode testNode = root.getChild("Test");
setSelectionPath(testNode);
performAction(cutAction);
performTreeAction(cutAction);
// paste at 010074d4
ProgramNode stringsNode = root.getChild("Strings");
@ -869,7 +868,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode fnode = cNode.getChild("testl");
setSelectionPath(fnode);
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
root = (ProgramNode) tree.getModel().getRoot();
@ -937,12 +936,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
// set the view to DLLs
setSelectionPath(node);
setViewPaths(new TreePath[] { node.getTreePath() });
performAction(copyAction);
performTreeAction(copyAction);
ProgramNode everythingNode = root.getChild("Everything");
setSelectionPath(everythingNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
waitForProgram(program);
// cut a fragment in the view and paste onto a collapsed folder
@ -953,7 +952,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
ProgramNode dataNode = root.getChild(".data");
setSelectionPath(dataNode);
setViewPaths(new TreePath[] { dataNode.getTreePath() });
performAction(cutAction);
performTreeAction(cutAction);
// select DLLs in Everything
ProgramNode evNode = root.getChild("Everything");
@ -962,7 +961,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
visitNode(dllsNode);
setSelectionPath(dllsNode);
performAction(pasteAction);
performTreeAction(pasteAction);
// first occurrence of DLLs should have icon for descendant in view
ProgramNode[] nodes = findNodes("DLLs");
@ -991,13 +990,13 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
assertTrue(plugin.getView().hasSameAddresses(set));
setSelectionPath(debugNode);
performAction(cutAction);
performTreeAction(cutAction);
// paste to an expanded folder not in the view
ProgramNode subrNode = root.getChild("Subroutines");
expandNode(subrNode);
setSelectionPath(subrNode);
performAction(pasteAction);
performTreeAction(pasteAction);
assertTrue(plugin.getView().hasSameAddresses(set));
}
@ -1012,14 +1011,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
setViewPaths(dataNode, debugNode);
setSelectionPath(debugNode);
performAction(cutAction);
performTreeAction(cutAction);
// paste onto another fragment that is not in the view
ProgramNode funcNode = root.getChild("Functions");
visitNode(funcNode);
ProgramNode sscanfNode = funcNode.getChild("sscanf");
setSelectionPath(sscanfNode);
performAction(pasteAction);
performTreeAction(pasteAction);
assertTrue(plugin.getView().hasSameAddresses(dataNode.getFragment()));
}
@ -1042,11 +1041,11 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest {
// cut first fragment in Subroutines
setSelectionPath(node);
performAction(cutAction);
performTreeAction(cutAction);
// paste at the debug node
setSelectionPath(debugNode);
performAction(pasteAction);
performTreeAction(pasteAction);
assertTrue(plugin.getView().contains(set));
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -428,8 +428,7 @@ public class ProgramTreePlugin3Test extends AbstractProgramTreePluginTest {
waitForSwing();
Component comp = getCellRendererComponentForLeaf(n, row);
assertEquals(new GIcon(DnDTreeCellRenderer.VIEWED_FRAGMENT),
((JLabel) comp).getIcon());
assertEquals(new GIcon(DnDTreeCellRenderer.VIEWED_FRAGMENT), ((JLabel) comp).getIcon());
}
@Test
@ -1380,7 +1379,7 @@ public class ProgramTreePlugin3Test extends AbstractProgramTreePluginTest {
AtomicReference<Exception> ref = new AtomicReference<>();
runSwing(() -> {
try {
tree.processDropRequest(node, list, TreeTransferable.localTreeNodeFlavor,
tree.processDropRequest(node, list, ProgramTreeTransferable.localTreeNodeFlavor,
dropAction);
}
catch (NotFoundException | CircularDependencyException | DuplicateGroupException e) {
@ -1443,12 +1442,12 @@ public class ProgramTreePlugin3Test extends AbstractProgramTreePluginTest {
setSelectionPath(node);
setViewPaths(node);
performAction(copyAction);
performTreeAction(copyAction);
ProgramNode everythingNode = root.getChild(dst);
setSelectionPath(everythingNode);
assertTrue(pasteAction.isEnabled());
performAction(pasteAction);
performTreeAction(pasteAction);
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -277,7 +277,7 @@ public class ProgramTreePluginShowInViewTest extends AbstractGhidraHeadedIntegra
ProgramNode textNode = root.getChild(".text");
setSelectionPath(textNode.getTreePath());
DockingActionIf replaceAction = getAction(plugin, "Replace View");
DockingActionIf replaceAction = getAction(plugin, "Set View");
ActionContext context = getActionContext();
performAction(replaceAction, context, true);
@ -312,7 +312,7 @@ public class ProgramTreePluginShowInViewTest extends AbstractGhidraHeadedIntegra
ProgramNode rsrcNode = root.getChild(".rsrc");
addSelectionPath(rsrcNode.getTreePath());
DockingActionIf replaceAction = getAction(plugin, "Replace View");
DockingActionIf replaceAction = getAction(plugin, "Set View");
ActionContext context = getActionContext();
performAction(replaceAction, context, true);

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -136,8 +136,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(treeNames.length, tabbedPane.getTabCount());
assertEquals(DEFAULT_TREE_NAME + "(1)", vps.getViewName());
assertEquals(tabbedPane.getSelectedComponent(), vps.getViewComponent());
SwingUtilities
.invokeAndWait(() -> createTreeAction.actionPerformed(new DefaultActionContext()));
performAction(createTreeAction);
program.flushEvents();
vps = provider.getCurrentViewProvider();
treeNames = program.getListing().getTreeNames();
@ -152,8 +151,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testUndoRedo() throws Exception {
ProgramTreePlugin treePlugin = env.getPlugin(ProgramTreePlugin.class);
final DockingActionIf createTreeAction = getAction(treePlugin, "Create Default Tree View");
SwingUtilities
.invokeAndWait(() -> createTreeAction.actionPerformed(new DefaultActionContext()));
performAction(createTreeAction);
program.flushEvents();
env.showTool();
ViewProviderService vps = provider.getCurrentViewProvider();
@ -163,8 +161,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(treeNames.length, tabbedPane.getTabCount());
assertEquals(DEFAULT_TREE_NAME + "(1)", vps.getViewName());
assertEquals(tabbedPane.getSelectedComponent(), vps.getViewComponent());
SwingUtilities
.invokeAndWait(() -> createTreeAction.actionPerformed(new DefaultActionContext()));
performAction(createTreeAction);
program.flushEvents();
vps = provider.getCurrentViewProvider();
treeNames = program.getListing().getTreeNames();
@ -237,7 +234,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testCloseView() throws Exception {
// close "Program Tree"
final DockingActionIf closeAction = getAction(plugin, "Close Tree View");
SwingUtilities.invokeAndWait(() -> closeAction.actionPerformed(new DefaultActionContext()));
performAction(closeAction);
waitForBusyTool(tool);
@ -261,8 +258,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
setCurrentViewProvider("Tree Two");
final DockingActionIf deleteAction = getAction(plugin, "Delete Tree View");
SwingUtilities
.invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext()));
performAction(deleteAction);
waitForBusyTool(tool);
@ -300,31 +296,26 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
setCurrentViewProvider("Main Tree");
SwingUtilities
.invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext()));
performAction(deleteAction);
waitForBusyTool(tool);
setCurrentViewProvider("Tree One");
SwingUtilities
.invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext()));
performAction(deleteAction);
waitForBusyTool(tool);
setCurrentViewProvider("Tree Two");
SwingUtilities
.invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext()));
performAction(deleteAction);
waitForBusyTool(tool);
setCurrentViewProvider("Tree Three");
SwingUtilities
.invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext()));
performAction(deleteAction);
waitForBusyTool(tool);
// attempt to delete the last view
SwingUtilities
.invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext()));
performAction(deleteAction);
waitForBusyTool(tool);
ViewProviderService vps = provider.getCurrentViewProvider();
@ -341,10 +332,10 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
final DockingActionIf closeAction = getAction(plugin, "Close Tree View");
setCurrentViewProvider(DEFAULT_TREE_NAME);
SwingUtilities.invokeAndWait(() -> closeAction.actionPerformed(new DefaultActionContext()));
performAction(closeAction);
setCurrentViewProvider("Main Tree");
SwingUtilities.invokeAndWait(() -> {
runSwing(() -> {
closeAction.actionPerformed(new DefaultActionContext());
provider.setCurrentViewProvider("Tree One");
closeAction.actionPerformed(new DefaultActionContext());