GP-4691 - Program Tree - Updated navigation; updated keybindings; added an action to 'add to view'

This commit is contained in:
dragonmacher 2024-08-10 13:39:07 -04:00
parent e88fe40a1e
commit c17d11a8d1
23 changed files with 1188 additions and 1351 deletions

View file

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

View file

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

View file

@ -4,9 +4,9 @@
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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 // data flavors that this tree can support
protected DataFlavor[] acceptableFlavors; protected DataFlavor[] acceptableFlavors;
protected TreeTransferable transferable; protected ProgramTreeTransferable transferable;
protected Color nonSelectionDragColor; protected Color nonSelectionDragColor;
protected int relativeMousePos; // mouse position within the node 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(); nodes[i] = (ProgramNode) selectionPaths[i].getLastPathComponent();
} }
transferable = new TreeTransferable(nodes); transferable = new ProgramTreeTransferable(nodes);
draggedNodes = nodes; draggedNodes = nodes;
return transferable; return transferable;
} }

View file

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

View file

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

View file

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

View file

@ -1,13 +1,12 @@
/* ### /* ###
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -28,38 +27,40 @@ import docking.action.KeyBindingData;
*/ */
abstract class ProgramTreeAction extends DockingAction { abstract class ProgramTreeAction extends DockingAction {
final static int SINGLE_SELECTION=0; final static int SINGLE_SELECTION = 0;
final static int MULTI_SELECTION=1; final static int MULTI_SELECTION = 1;
private int selectionType; private int selectionType;
ProgramTreeAction(String name, String owner, String[] defaultPopupPath, KeyStroke defaultKeyBinding) { ProgramTreeAction(String name, String owner, KeyStroke defaultKeyBinding) {
this(name, owner, defaultPopupPath, this(name, owner, defaultKeyBinding, MULTI_SELECTION);
defaultKeyBinding, MULTI_SELECTION); }
}
ProgramTreeAction(String name, String owner, ProgramTreeAction(String name, String owner, KeyStroke defaultKeyBinding, int selectionType) {
String[] defaultPopupPath, super(name, owner);
KeyStroke defaultKeyBinding,int selectionType) {
super(name, owner); this.selectionType = selectionType;
if (defaultKeyBinding != null) {
this.selectionType = selectionType; setKeyBindingData(new KeyBindingData(defaultKeyBinding));
if (defaultKeyBinding != null) { }
setKeyBindingData( new KeyBindingData( defaultKeyBinding ) ); setEnabled(false);
} }
setEnabled(false);
} @Override
@Override public boolean isValidContext(ActionContext context) {
public boolean isValidContext(ActionContext context) { return context.getContextObject() instanceof ProgramNode;
return context.getContextObject() instanceof ProgramNode; }
}
@Override @Override
public boolean isAddToPopup(ActionContext context) { public boolean isAddToPopup(ActionContext context) {
return true; return true;
} }
/**
* Get the selection type associated with this action. /**
*/ * Get the selection type associated with this action.
int getSelectionType() { * @return the selection type
return selectionType; */
} 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"); * 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.*; import java.util.*;
import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.*; import javax.swing.event.*;
@ -56,9 +57,9 @@ class ProgramTreePanel extends JPanel implements ChangeListener {
initialize(); initialize();
// Disable tree expand/collapse on double-click. // Disable tree expand/collapse on double-click.
if (tree != null) { tree.setToggleClickCount(0);
tree.setToggleClickCount(0);
} tree.addTreeSelectionListener(e -> plugin.contextChanged());
} }
// ChangeListener interface method // ChangeListener interface method
@ -160,10 +161,9 @@ class ProgramTreePanel extends JPanel implements ChangeListener {
* Get the currently viewed group paths. * Get the currently viewed group paths.
*/ */
GroupView getGroupView() { GroupView getGroupView() {
ArrayList<TreePath> viewList = tree.getViewList(); List<TreePath> viewList = tree.getViewList();
ArrayList<GroupPath> list = new ArrayList<GroupPath>(); ArrayList<GroupPath> list = new ArrayList<GroupPath>();
for (int i = 0; i < viewList.size(); i++) { for (TreePath p : viewList) {
TreePath p = viewList.get(i);
ProgramNode node = (ProgramNode) p.getLastPathComponent(); ProgramNode node = (ProgramNode) p.getLastPathComponent();
GroupPath gp = node.getGroupPath(); GroupPath gp = node.getGroupPath();
if (gp != null) { if (gp != null) {
@ -191,7 +191,7 @@ class ProgramTreePanel extends JPanel implements ChangeListener {
} }
ProgramNode prepareSelectionForPopup(MouseEvent event) { ProgramNode prepareSelectionForPopup(MouseEvent event) {
return tree.prepareSelectionForPopup(event); return tree.adjustSelectionForPopup(event);
} }
GroupPath[] getViewedGroups() { GroupPath[] getViewedGroups() {

View file

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

View file

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

View file

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

View file

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

View file

@ -4,9 +4,9 @@
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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.awt.Component;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
@ -87,6 +88,15 @@ public abstract class AbstractProgramTreePluginTest extends AbstractGhidraHeaded
return context; 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) { protected void setTreeView(final String viewName) {
tree = plugin.getTree(viewName); tree = plugin.getTree(viewName);
root = (ProgramNode) tree.getModel().getRoot(); root = (ProgramNode) tree.getModel().getRoot();

View file

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

View file

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

View file

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

View file

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

View file

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