From c17d11a8d14f686924f5762e0b7cea5ec565fb39 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Sat, 10 Aug 2024 13:39:07 -0400 Subject: [PATCH] GP-4691 - Program Tree - Updated navigation; updated keybindings; added an action to 'add to view' --- .../topics/ProgramTreePlugin/program_tree.htm | 28 +- .../core/datamgr/editor/EnumEditorPanel.java | 10 +- .../core/programtree/DragNDropTree.java | 8 +- .../plugin/core/programtree/PasteManager.java | 220 ++- .../core/programtree/ProgramDnDTree.java | 152 +- .../plugin/core/programtree/ProgramNode.java | 37 +- .../core/programtree/ProgramTreeAction.java | 71 +- .../programtree/ProgramTreeActionContext.java | 170 +++ .../programtree/ProgramTreeActionManager.java | 1270 +++++++---------- .../core/programtree/ProgramTreePanel.java | 18 +- .../core/programtree/ProgramTreePlugin.java | 72 +- .../programtree/ProgramTreeTransferable.java | 81 ++ .../core/programtree/TreeTransferable.java | 109 -- .../core/programtree/TreeViewProvider.java | 7 +- .../ViewManagerComponentProvider.java | 20 +- .../plugin/core/programtree/ViewPanel.java | 17 +- .../core/module/ModuleSortPluginTest.java | 12 +- .../AbstractProgramTreePluginTest.java | 14 +- .../programtree/ProgramTreePlugin1Test.java | 58 +- .../programtree/ProgramTreePlugin2Test.java | 107 +- .../programtree/ProgramTreePlugin3Test.java | 13 +- .../ProgramTreePluginShowInViewTest.java | 8 +- .../programtree/ViewManagerPluginTest.java | 37 +- 23 files changed, 1188 insertions(+), 1351 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionContext.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeTransferable.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeTransferable.java diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ProgramTreePlugin/program_tree.htm b/Ghidra/Features/Base/src/main/help/help/topics/ProgramTreePlugin/program_tree.htm index c8294e3f7c..200579b1a4 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/ProgramTreePlugin/program_tree.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/ProgramTreePlugin/program_tree.htm @@ -389,32 +389,46 @@ View option. -

Replace the View in the Code Browser with other - Folders/Fragments

+

Set the View in the Code Browser with Folders/Fragments

+

Add to the View in the Code Browser

+ + +

Navigation

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorPanel.java index dd1c3a1518..1a1da3fc33 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorPanel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -33,12 +33,8 @@ import docking.widgets.textfield.GValidatedTextField; import docking.widgets.textfield.GValidatedTextField.LongField.LongValidator; import docking.widgets.textfield.GValidatedTextField.ValidationFailedException; import docking.widgets.textfield.GValidatedTextField.ValidationMessageListener; -import generic.theme.Gui; import ghidra.docking.settings.Settings; import ghidra.program.model.data.*; -import ghidra.program.model.data.Enum; -import ghidra.program.model.listing.DataTypeArchive; -import ghidra.program.model.listing.Program; import ghidra.util.*; import ghidra.util.table.GhidraTable; @@ -670,7 +666,7 @@ class EnumEditorPanel extends JPanel { private class EnumValueRenderer extends GTableCellRenderer { EnumValueRenderer() { - setFont(Gui.getFont("font.monospaced")); + setFont(getFixedWidthFont()); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java index b9f01e2cbe..a76dfd3270 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/DragNDropTree.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -61,7 +61,7 @@ public abstract class DragNDropTree extends JTree implements Draggable, Droppabl // data flavors that this tree can support protected DataFlavor[] acceptableFlavors; - protected TreeTransferable transferable; + protected ProgramTreeTransferable transferable; protected Color nonSelectionDragColor; protected int relativeMousePos; // mouse position within the node @@ -128,7 +128,7 @@ public abstract class DragNDropTree extends JTree implements Draggable, Droppabl nodes[i] = (ProgramNode) selectionPaths[i].getLastPathComponent(); } - transferable = new TreeTransferable(nodes); + transferable = new ProgramTreeTransferable(nodes); draggedNodes = nodes; return transferable; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/PasteManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/PasteManager.java index e82fa8a0b5..3aea917c67 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/PasteManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/PasteManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,10 +17,9 @@ package ghidra.app.plugin.core.programtree; import java.awt.datatransfer.*; import java.io.IOException; -import java.util.ArrayList; import java.util.ConcurrentModificationException; +import java.util.List; -import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import docking.dnd.GClipboard; @@ -29,90 +28,66 @@ import ghidra.util.Msg; import ghidra.util.exception.NotFoundException; /** - * Manage paste operations for the tree. + * Manage paste operations for the Program Tree. */ class PasteManager { - private ProgramTreeActionManager actionMgr; - private ProgramDnDTree tree; - private DefaultTreeModel treeModel; - private Clipboard cutClipboard; + private ProgramTreeActionManager actionManager; private String lastGroupPasted; - /** - * Constructor - */ - PasteManager(ProgramTreeActionManager actionMgr) { - this.actionMgr = actionMgr; - cutClipboard = actionMgr.getCutClipboard(); + PasteManager(ProgramTreeActionManager actionManager) { + this.actionManager = actionManager; } - /** - * Return true if the pasteNode can be pasted at the destNode. - * @param destNode destination node for where the pasteNode will be pasted - * @param pasteNode node to paste - * @param isCutOperation true if the operation was "cut" versus "copy" - */ boolean isPasteAllowed(ProgramNode destNode, ProgramNode pasteNode, boolean isCutOperation) { if (destNode.getProgram() != pasteNode.getProgram() || destNode.getRoot() != pasteNode.getRoot()) { return false; } - try { - if (destNode.getName().equals(pasteNode.getName())) { - return false; - } - if (destNode.isNodeAncestor(pasteNode)) { - return false; - } - if (destNode.isFragment() && pasteNode.isModule()) { - if (isCutOperation && !pasteNode.getModule().isDescendant(destNode.getFragment())) { + if (destNode.getName().equals(pasteNode.getName())) { + return false; + } - return true; // pasted module can be flattened onto - // destination fragment + if (destNode.isNodeAncestor(pasteNode)) { + return false; + } + + if (destNode.isFragment() && pasteNode.isModule()) { + if (isCutOperation && !pasteNode.getModule().isDescendant(destNode.getFragment())) { + return true; // pasted module can be flattened onto destination fragment + } + return false; + } + + if (destNode.isFragment() && pasteNode.isFragment()) { + if (isCutOperation) { + return true; + } + return false; + } + + if (destNode.isModule()) { + ProgramModule destModule = destNode.getModule(); + + if (pasteNode.isModule()) { + if (pasteNode.getModule().isDescendant(destModule)) { + return false; } - return false; - } - - if (destNode.isFragment() && pasteNode.isFragment()) { - if (isCutOperation) { - return true; - } - return false; - } - if (destNode.isModule()) { - ProgramModule destModule = destNode.getModule(); - - if (pasteNode.isModule()) { - if (pasteNode.getModule().isDescendant(destModule)) { - return false; - } - if (!isCutOperation && destModule.contains(pasteNode.getModule())) { - return false; - } - } - else if (!isCutOperation && destModule.contains(pasteNode.getFragment())) { + if (!isCutOperation && destModule.contains(pasteNode.getModule())) { return false; } } + else if (!isCutOperation && destModule.contains(pasteNode.getFragment())) { + return false; + } + } - return true; - } - catch (RuntimeException e) { - // this is a hack for unknown reasons - } - return false; + return true; } - /** - * Do the paste operation. - * @param destNode destination node for where the paste the contents of - * system clipboard. - */ - @SuppressWarnings("unchecked") - // cast is OK, it is data that we are expecting - void paste(ProgramNode destNode) { + @SuppressWarnings("unchecked") // cast is OK, it is data that we are expecting + void paste(ProgramDnDTree tree, ProgramNode destNode) { int transactionID = tree.startTransaction("Paste"); if (transactionID < 0) { @@ -125,34 +100,32 @@ class PasteManager { Clipboard systemClipboard = GClipboard.getSystemClipboard(); Transferable t = systemClipboard.getContents(tree); try { - if (t == null || !t.isDataFlavorSupported(TreeTransferable.localTreeNodeFlavor)) { + if (t == null || + !t.isDataFlavorSupported(ProgramTreeTransferable.localTreeNodeFlavor)) { return; } + tree.setBusyCursor(true); lastGroupPasted = null; - ArrayList list = - (ArrayList) t.getTransferData(TreeTransferable.localTreeNodeFlavor); - + List list = + (List) t.getTransferData(ProgramTreeTransferable.localTreeNodeFlavor); if (list == null) { - // SCR 7990--something bad has happened to the copy buffer return; } - for (int i = 0; i < list.size(); i++) { - ProgramNode tnode = list.get(i); - - if (destNode.getRoot() != tnode.getRoot()) { + for (ProgramNode node : list) { + if (destNode.getRoot() != node.getRoot()) { lastGroupPasted = null; break; } - if (!destNode.getName().equals(tnode.getName())) { - if (pasteGroup(destNode, tnode)) { - if (!(destNode.isFragment() && tnode.isModule())) { + if (!destNode.getName().equals(node.getName())) { + if (pasteGroup(tree, destNode, node)) { + if (!(destNode.isFragment() && node.isModule())) { // this was not a "flatten module" operation // so we can leave the busy cursor set // until the domain object event comes in - lastGroupPasted = tnode.getName(); + lastGroupPasted = node.getName(); } } } @@ -163,18 +136,14 @@ class PasteManager { } // do "cut" operations now if there are any - actionMgr.checkClipboard(true); - actionMgr.clearSystemClipboard(); - actionMgr.enablePasteAction(false); + actionManager.cutClipboardNodes(tree); tree.removeSelectionPath(path); tree.addSelectionPath(path); - } catch (UnsupportedFlavorException e) { // data flavor is not supported Msg.showError(this, null, "Paste from Clipboard Failed", "Data flavor in clipboard is not supported.", e); - } catch (IOException e) { // data is no longer available @@ -182,33 +151,19 @@ class PasteManager { "Data is no longer available for paste operation", e); } catch (Exception e) { - Msg.showError(this, null, null, null, e); + Msg.showError(this, null, "Unexpected Exception Pasting", + "Unexpected exception pasting nodes", e); } finally { tree.endTransaction(transactionID, true); } } - /** - * Get the name of the last group that was pasted. - */ String getLastGroupPasted() { return lastGroupPasted; } - /** - * Method setProgramTreeView. - * @param tree - */ - void setProgramTreeView(ProgramDnDTree tree) { - this.tree = tree; - treeModel = (DefaultTreeModel) tree.getModel(); - } - - /** - * Paste the group at nodeToPaste at destNode. - */ - private boolean pasteGroup(ProgramNode destNode, ProgramNode nodeToPaste) { + private boolean pasteGroup(ProgramDnDTree tree, ProgramNode destNode, ProgramNode nodeToPaste) { if (destNode.isFragment()) { // can paste either a fragment or a module onto a fragment; @@ -216,11 +171,12 @@ class PasteManager { // descendant fragments are moved to the destination fragment. try { tree.mergeGroup(nodeToPaste.getGroup(), destNode.getFragment()); - actionMgr.removeFromClipboard(cutClipboard, nodeToPaste); + actionManager.removeFromClipboard(tree, nodeToPaste); return true; } catch (ConcurrentModificationException e) { + // ha! } catch (Exception e) { Msg.showError(this, null, null, "Error Merging Fragments", e); @@ -231,30 +187,24 @@ class PasteManager { ProgramModule targetModule = destNode.getModule(); if (targetModule == null) { - nodeToPaste.setDeleted(false); - treeModel.reload(nodeToPaste); + actionManager.clearCut(nodeToPaste); Msg.showError(this, null, "Paste from Clipboard Failed", "Paste of " + nodeToPaste + " at\n" + destNode.getName() + " is not allowed."); return false; } - return pasteNode(destNode, nodeToPaste); + return pasteNode(tree, destNode, nodeToPaste); } - /** - * Paste the node at the destination node. - */ - private boolean pasteNode(ProgramNode destNode, ProgramNode nodeToPaste) { + private boolean pasteNode(ProgramDnDTree tree, ProgramNode destNode, ProgramNode nodeToPaste) { ProgramModule targetModule = destNode.getModule(); - // make sure we have something to paste ProgramModule module = nodeToPaste.getModule(); ProgramFragment fragment = nodeToPaste.getFragment(); if (module == null && fragment == null) { - nodeToPaste.setDeleted(false); - treeModel.reload(nodeToPaste); + actionManager.clearCut(nodeToPaste); Msg.showError(this, null, "Paste from Clipboard Failed", "Could not paste " + nodeToPaste + " at " + targetModule.getName()); return false; @@ -263,10 +213,10 @@ class PasteManager { boolean pasteOK = false; try { if (module != null) { - pasteOK = pasteModule(destNode, nodeToPaste, targetModule, module); + pasteOK = pasteModule(tree, destNode, nodeToPaste, targetModule, module); } else { - pasteOK = pasteFragment(nodeToPaste, targetModule, fragment); + pasteOK = pasteFragment(tree, nodeToPaste, targetModule, fragment); } // don't match the expansion state unless the destination @@ -281,37 +231,35 @@ class PasteManager { } catch (CircularDependencyException e) { - removeFromClipboard(nodeToPaste); Msg.showError(this, null, "Paste from Clipboard Failed", e.getMessage()); } catch (DuplicateGroupException e) { - nodeToPaste.setDeleted(false); - tree.reloadNode(nodeToPaste); - + // handled below } catch (NotFoundException e) { - removeFromClipboard(nodeToPaste); - nodeToPaste.setDeleted(false); Msg.showError(this, null, "Paste from Clipboard Failed", e.getMessage()); } + + removeFromClipboard(tree, nodeToPaste); return false; } /** * Paste the fragment at the given module. */ - private boolean pasteFragment(ProgramNode nodeToPaste, ProgramModule targetModule, - ProgramFragment fragment) throws NotFoundException, DuplicateGroupException { + private boolean pasteFragment(ProgramDnDTree tree, ProgramNode nodeToPaste, + ProgramModule targetModule, ProgramFragment fragment) + throws NotFoundException, DuplicateGroupException { boolean pasteOK = false; if (targetModule.contains(fragment)) { if (targetModule.equals(nodeToPaste.getParentModule())) { - removeFromClipboard(nodeToPaste); + removeFromClipboard(tree, nodeToPaste); } } - else if (actionMgr.clipboardContains(nodeToPaste)) { + else if (actionManager.clipboardContains(nodeToPaste)) { targetModule.reparent(nodeToPaste.getName(), nodeToPaste.getParentModule()); - removeFromClipboard(nodeToPaste); + removeFromClipboard(tree, nodeToPaste); pasteOK = true; } else { @@ -321,7 +269,7 @@ class PasteManager { return pasteOK; } - private boolean pasteModule(ProgramNode destNode, ProgramNode nodeToPaste, + private boolean pasteModule(ProgramDnDTree tree, ProgramNode destNode, ProgramNode nodeToPaste, ProgramModule targetModule, ProgramModule module) throws NotFoundException, CircularDependencyException, DuplicateGroupException { @@ -332,12 +280,12 @@ class PasteManager { } if (targetModule.contains(module)) { if (targetModule.equals(nodeToPaste.getParentModule())) { - removeFromClipboard(nodeToPaste); + removeFromClipboard(tree, nodeToPaste); } } - else if (actionMgr.clipboardContains(nodeToPaste)) { + else if (actionManager.clipboardContains(nodeToPaste)) { targetModule.reparent(nodeToPaste.getName(), nodeToPaste.getParentModule()); - removeFromClipboard(nodeToPaste); + removeFromClipboard(tree, nodeToPaste); pasteOK = true; } else { @@ -352,15 +300,9 @@ class PasteManager { return pasteOK; } - /** - * Remove the given node from the cut clipboard, so that "cut" changes - * will not be applied. - * @param node node to remove from the cut clipboard - */ - private void removeFromClipboard(ProgramNode node) { - actionMgr.removeFromClipboard(cutClipboard, node); - node.setDeleted(false); - tree.reloadNode(node); + private void removeFromClipboard(ProgramDnDTree tree, ProgramNode node) { + actionManager.removeFromClipboard(tree, node); + actionManager.clearCut(node); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java index 568b556aaf..8a08fa5520 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramDnDTree.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,7 +29,6 @@ import javax.swing.event.ChangeEvent; import javax.swing.tree.*; import docking.DockingUtils; -import docking.action.DockingAction; import docking.actions.KeyBindingUtils; import docking.dnd.DropTgtAdapter; import docking.widgets.JTreeMouseListenerDelegate; @@ -55,8 +54,8 @@ public class ProgramDnDTree extends DragNDropTree { private Program program; private Listing listing; - private ArrayList nodeList; // list of nodes from preorder enumeration - private ArrayList viewList; // list of tree paths that are being viewed. + private List nodeList; // list of nodes from preorder enumeration + private List viewList; // list of tree paths that are being viewed. //keeps track of module/fragment names to come up with a default name, e.g., New Folder (2) private StringKeyIndexer nameIndexer; @@ -272,7 +271,7 @@ public class ProgramDnDTree extends DragNDropTree { } static DataFlavor[] getDataFlavors() { - return new DataFlavor[] { TreeTransferable.localTreeNodeFlavor, + return new DataFlavor[] { ProgramTreeTransferable.localTreeNodeFlavor, GroupTransferable.localGroupFlavor, // a test data flavor DataFlavor.stringFlavor, // a test data flavor SelectionTransferable.localProgramSelectionFlavor }; @@ -317,7 +316,7 @@ public class ProgramDnDTree extends DragNDropTree { return false; } } - else if (chosen.equals(TreeTransferable.localTreeNodeFlavor)) { + else if (chosen.equals(ProgramTreeTransferable.localTreeNodeFlavor)) { // fromObject is null, so we know this is // from another tree, so don't allow the drop return false; @@ -385,6 +384,8 @@ public class ProgramDnDTree extends DragNDropTree { * that could potentially take a long time. The cursor is reset in the * domain object change listener when the event comes in for the group * that was last "pasted." + * + * @param busy true to use the busy cursor */ void setBusyCursor(boolean busy) { if (busy) { @@ -406,19 +407,11 @@ public class ProgramDnDTree extends DragNDropTree { treeListener = null; } - /** - * Get the view list. - * - * @return ArrayList list of tree paths in the view - */ - ArrayList getViewList() { + List getViewList() { return viewList; } - /** - * Get the node list. - */ - ArrayList getNodeList() { + List getNodeList() { return nodeList; } @@ -435,10 +428,6 @@ public class ProgramDnDTree extends DragNDropTree { } } - /** - * Adds path to the view. - * @param path - */ void addToView(TreePath path) { if (path == null) { return; @@ -666,36 +655,6 @@ public class ProgramDnDTree extends DragNDropTree { } } - /** - * Return whether the given action should be added to popup, based - * on what is currently selected. Called by the ProgramTreeAction. - */ - boolean addActionToPopup(DockingAction action) { - - if (!(action instanceof ProgramTreeAction)) { - return true; - } - - ProgramTreeAction a = (ProgramTreeAction) action; - - int selectionCount = getSelectionCount(); - if (a.getSelectionType() == ProgramTreeAction.SINGLE_SELECTION) { - - if (selectionCount == 1) { - return true; - } - else if (selectionCount == 0) { - return true; - } - return false; - } - // allow 1 or many in selection - if (selectionCount > 0) { - return true; - } - return false; - } - /** * Generate a unique name to be used as the default when * a Module or Fragment is created. @@ -730,25 +689,6 @@ public class ProgramDnDTree extends DragNDropTree { } } - /** - * Build a list of selected ProgramNodes in postorder. - */ - // our data; we know it's good - ArrayList getSortedSelection() { - ArrayList list = new ArrayList<>(); - Enumeration it = root.postorderEnumeration(); - while (it.hasMoreElements()) { - ProgramNode node = (ProgramNode) it.nextElement(); - if (isPathSelected(node.getTreePath())) { - list.add(node); - } - } - return list; - } - - /** - * Expand all descendants starting at node. - */ void expandNode(ProgramNode node) { expandPath(node.getTreePath()); @@ -837,7 +777,7 @@ public class ProgramDnDTree extends DragNDropTree { * @param sb string buffer to use if there was an error * @return true if group was removed */ - boolean removeGroup(ProgramNode node, StringBuffer sb) { + boolean removeGroup(ProgramNode node, StringBuilder sb) { boolean changesMade = false; @@ -869,6 +809,7 @@ public class ProgramDnDTree extends DragNDropTree { * @param parent parent of the group to be inserted * @param group group to add * @param index index of new child + * @return the new child node */ ProgramNode insertGroup(ProgramNode parent, Group group, int index) { @@ -1086,64 +1027,45 @@ public class ProgramDnDTree extends DragNDropTree { } } - /** - * Disable all actions. - */ - void disableActions() { - actionManager.disableActions(); - } - /** * Adjust the selection based on the given popupPoint. * @param event mouse event * @return node that is selected */ - ProgramNode prepareSelectionForPopup(MouseEvent event) { - // adjust the selection based on the popup location + ProgramNode adjustSelectionForPopup(MouseEvent event) { + synchronized (root) { if (event != null && event.getSource() != this) { return null; } + Point popupPoint = event != null ? event.getPoint() : null; - int nselected = getSelectionCount(); - TreePath selPath = null; if (popupPoint != null) { - selPath = getPathForLocation((int) popupPoint.getX(), (int) popupPoint.getY()); - } - else { - selPath = getSelectionPath(); - } - ProgramNode node = null; - if (selPath != null) { - node = (ProgramNode) selPath.getLastPathComponent(); + return getMouseNode(popupPoint); } - if (nselected <= 1) { + int nselected = getSelectionCount(); + if (nselected < 1) { + return null; // nothing selected in the tree + } - if (selPath != null && !isPathSelected(selPath)) { - setSelectionPath(selPath); - actionManager.adjustSingleActions(node); - return node; - } - if (selPath != null) { - actionManager.adjustSingleActions(node); - return node; - } - actionManager.disableActions(); - return null; + TreePath activePath = getSelectionPath(); + return (ProgramNode) activePath.getLastPathComponent(); + } + } + + private ProgramNode getMouseNode(Point popupPoint) { + + TreePath mousePath = getPathForLocation((int) popupPoint.getX(), (int) popupPoint.getY()); + if (mousePath != null) { + ProgramNode node = (ProgramNode) mousePath.getLastPathComponent(); + if (!isPathSelected(mousePath)) { + setSelectionPath(mousePath); // add the right-clicked node to the selection } - // if the path at the mouse pointer is in the selection OR - // the path is null, then adjust the multi-popup menu. - if ((selPath != null && isPathSelected(selPath)) || selPath == null) { - actionManager.adjustMultiActions(); - return node; - } - // force the selection to be where the mouse pointer is - setSelectionPath(selPath); - actionManager.adjustSingleActions(node); return node; } + return null; } /** @@ -1171,9 +1093,7 @@ public class ProgramDnDTree extends DragNDropTree { ArrayList list = new ArrayList<>(); - for (int i = 0; i < nodeList.size(); i++) { - ProgramNode node = nodeList.get(i); - + for (ProgramNode node : nodeList) { if (node.getName().equals(groupName)) { list.add(node); } @@ -1358,9 +1278,7 @@ public class ProgramDnDTree extends DragNDropTree { ArrayList list = new ArrayList<>(); - for (int i = 0; i < nodeList.size(); i++) { - ProgramNode node = nodeList.get(i); - + for (ProgramNode node : nodeList) { Group group = node.getGroup(); if (group != null && group.equals(g)) { list.add(node); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramNode.java index 65c2b2fa8e..41538bad44 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramNode.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -45,6 +45,8 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Construct a new ProgramNode with the given Group. + * @param program the program + * @param g the group */ ProgramNode(Program program, Group g) { this(program, g, g.getName()); @@ -52,6 +54,8 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Construct a new ProgramNode with the given name. + * @param program the program + * @param name the name */ ProgramNode(Program program, String name) { this(program, null, name); @@ -60,6 +64,9 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Create a new ProgramNode with the given group and name; * use name for the displayed name of this node. + * @param program the program + * @param g the group + * @param name the name */ ProgramNode(Program program, Group g, String name) { super(name); @@ -148,6 +155,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Get the name for this node. + * @return he name for this node. */ public String getName() { return name; @@ -155,6 +163,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Get the group for this node. + * @return the group for this node. */ public Group getGroup() { return group; @@ -162,6 +171,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Returns true if this node represents a Fragment. + * @return true if this node represents a Fragment. */ public boolean isFragment() { return fragment != null; @@ -169,6 +179,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Returns true if this node represents a Module. + * @return true if this node represents a Module. */ public boolean isModule() { return module != null; @@ -192,6 +203,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Get the program for this node. + * @return the program for this node. */ public Program getProgram() { return program; @@ -199,6 +211,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Return true if the node is in the view. + * @return true if the node is in the view. */ public boolean isInView() { return isInView; @@ -206,14 +219,12 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Get the group path for this node. + * @return the group path for this node. */ public GroupPath getGroupPath() { return groupPath; } - ///////////////////////////////////////////////////////////// - // package-level methods - ///////////////////////////////////////////////////////////// /** * Mark this node as having been populated (visited). */ @@ -227,6 +238,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Return true if this node was visited. + * @return true if this node was visited. */ boolean wasVisited() { return visited; @@ -238,6 +250,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Get the tree path for this node. + * @return the tree path for this node. */ TreePath getTreePath() { return path; @@ -245,6 +258,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Set the tree path for this node. + * @param path the tree path for this node. */ void setTreePath(TreePath path) { this.path = path; @@ -252,6 +266,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Get the parent module for this node. + * @return the parent module for this node. */ ProgramModule getParentModule() { return parentModule; @@ -259,6 +274,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Set the parent module for this node. + * @param parent the parents */ void setParentModule(ProgramModule parent) { parentModule = parent; @@ -266,6 +282,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Set the name for this node. + * @param name the name */ void setName(String name) { this.name = name; @@ -273,8 +290,8 @@ public class ProgramNode extends DefaultMutableTreeNode { } /** - * Set this node to be deleted so that it can be - * rendered as such. + * Set this node to be deleted so that it can be rendered as such. + * @param deleted true if deleted */ void setDeleted(boolean deleted) { this.deleted = deleted; @@ -282,6 +299,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Returns whether this node is marked as deleted. + * @return whether this node is marked as deleted. */ boolean isDeleted() { return deleted; @@ -289,6 +307,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Set the group path for this node. + * @param groupPath the path */ void setGroupPath(GroupPath groupPath) { this.groupPath = groupPath; @@ -296,6 +315,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Mark this node as being in some view. + * @param isInView true if in some view. */ void setInView(boolean isInView) { this.isInView = isInView; @@ -326,6 +346,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Set the tree; this method is only called on the root node. + * @param tree the tree */ void setTree(ProgramDnDTree tree) { this.tree = tree; @@ -358,7 +379,7 @@ public class ProgramNode extends DefaultMutableTreeNode { /** * Get the node named childName. - * @param childName + * @param childName the name * @return null if the node does not allow children, or the name was * not found. */ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeAction.java index 072f43a198..9f6ffe15c1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeAction.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,38 +27,40 @@ import docking.action.KeyBindingData; */ abstract class ProgramTreeAction extends DockingAction { - final static int SINGLE_SELECTION=0; - final static int MULTI_SELECTION=1; + final static int SINGLE_SELECTION = 0; + final static int MULTI_SELECTION = 1; - private int selectionType; + private int selectionType; - ProgramTreeAction(String name, String owner, String[] defaultPopupPath, KeyStroke defaultKeyBinding) { - this(name, owner, defaultPopupPath, - defaultKeyBinding, MULTI_SELECTION); - } - ProgramTreeAction(String name, String owner, - String[] defaultPopupPath, - KeyStroke defaultKeyBinding,int selectionType) { - super(name, owner); - - this.selectionType = selectionType; - if (defaultKeyBinding != null) { - setKeyBindingData( new KeyBindingData( defaultKeyBinding ) ); - } - setEnabled(false); - } - @Override - public boolean isValidContext(ActionContext context) { - return context.getContextObject() instanceof ProgramNode; - } - @Override - public boolean isAddToPopup(ActionContext context) { - return true; - } - /** - * Get the selection type associated with this action. - */ - int getSelectionType() { - return selectionType; - } + ProgramTreeAction(String name, String owner, KeyStroke defaultKeyBinding) { + this(name, owner, defaultKeyBinding, MULTI_SELECTION); + } + + ProgramTreeAction(String name, String owner, KeyStroke defaultKeyBinding, int selectionType) { + super(name, owner); + + this.selectionType = selectionType; + if (defaultKeyBinding != null) { + setKeyBindingData(new KeyBindingData(defaultKeyBinding)); + } + setEnabled(false); + } + + @Override + public boolean isValidContext(ActionContext context) { + return context.getContextObject() instanceof ProgramNode; + } + + @Override + public boolean isAddToPopup(ActionContext context) { + return true; + } + + /** + * Get the selection type associated with this action. + * @return the selection type + */ + int getSelectionType() { + return selectionType; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionContext.java new file mode 100644 index 0000000000..fcd6cd3f11 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionContext.java @@ -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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionManager.java index 6f8f1f53a7..f6a78e6c56 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeActionManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,6 @@ package ghidra.app.plugin.core.programtree; import java.awt.datatransfer.*; -import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.*; @@ -24,18 +23,18 @@ import java.util.function.Consumer; import java.util.function.Supplier; import javax.swing.KeyStroke; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; +import org.apache.commons.lang3.exception.ExceptionUtils; + import docking.ActionContext; import docking.action.*; import docking.dnd.GClipboard; -import docking.widgets.tree.GTreeNode; import generic.timer.ExpiringSwingTimer; import ghidra.app.cmd.module.*; import ghidra.framework.cmd.CompoundCmd; +import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; import ghidra.program.model.listing.*; import ghidra.util.Msg; @@ -46,15 +45,12 @@ import ghidra.util.exception.NotEmptyException; * Class to manage actions and popup menus for the program tree. */ class ProgramTreeActionManager implements ClipboardOwner { -// popup for multi selection - private Clipboard tempClipboard; // temporary clipboard used for the - // "cut" operation - private ProgramDnDTree tree; // tree currently in the view + + private Clipboard tempClipboard; // temporary clipboard used for the "cut" operation private ProgramNode root; private Program program; - // actions private DockingAction cutAction; private DockingAction copyAction; private DockingAction pasteAction; @@ -67,37 +63,30 @@ class ProgramTreeActionManager implements ClipboardOwner { private DockingAction collapseAction; private DockingAction goToViewAction; private DockingAction removeViewAction; - private DockingAction replaceViewAction; + private DockingAction setViewAction; + private DockingAction addToViewAction; private DockingAction[] actions; - private PasteManager pasteMgr; // handles the paste operations - private ArrayList viewList; - private SelectionListener selectionListener; + private PasteManager pasteManager; + private List viewList; private ProgramTreePlugin plugin; - private boolean replacingView; + private boolean isSettingView; ProgramTreeActionManager(ProgramTreePlugin plugin) { this.plugin = plugin; - tempClipboard = new Clipboard("ProgramTree"); - selectionListener = new SelectionListener(); - pasteMgr = new PasteManager(this); + this.tempClipboard = new Clipboard("ProgramTree"); + this.pasteManager = new PasteManager(this); createActions(plugin.getName()); } void setProgramTreeView(String treeName, ProgramDnDTree tree) { - if (this.tree != null) { - this.tree.removeTreeSelectionListener(selectionListener); - } - - this.tree = tree; - pasteMgr.setProgramTreeView(tree); - if (tree != null) { DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel(); root = (ProgramNode) treeModel.getRoot(); + + // TODO should not need this viewList = tree.getViewList(); - tree.addTreeSelectionListener(selectionListener); } } @@ -111,13 +100,9 @@ class ProgramTreeActionManager implements ClipboardOwner { public void lostOwnership(Clipboard clipboard, Transferable contents) { // check the temporary clipboard and revert "cut" operations // back to normal in the program node info. - checkClipboard(false); - + clearCutClipboardNodes(); } - /** - * Set the program, and update the root node. - */ void setProgram(Program program) { this.program = program; tempClipboard.setContents(null, this); @@ -127,14 +112,7 @@ class ProgramTreeActionManager implements ClipboardOwner { } String getLastGroupPasted() { - return pasteMgr.getLastGroupPasted(); - } - - /** - * Return true if actions were created for the tree. - */ - boolean actionsCreated() { - return cutAction != null; + return pasteManager.getLastGroupPasted(); } /** @@ -146,332 +124,407 @@ class ProgramTreeActionManager implements ClipboardOwner { return actions; } - /** - * Enable or disable actions according to the given - * node. Called by the ProgramTree when the selection changes. - */ - void adjustSingleActions(ProgramNode node) { - - if (program == null) { - disableActions(); - return; - } - - try { - //add menu items according to the selected node - TreePath path = node.getTreePath(); - - pasteAction.setEnabled(isPasteOk(node)); - renameAction.setEnabled(true); - goToViewAction.setEnabled(true); - - replaceViewAction.setEnabled(true); - removeViewAction.setEnabled(true); - - if (node == root) { - copyAction.setEnabled(false); - cutAction.setEnabled(false); - deleteAction.setEnabled(false); - expandAction.setEnabled(!allPathsExpanded(path)); - collapseAction.setEnabled(!allPathsCollapsed(path)); - createFolderAction.setEnabled(true); - createFragmentAction.setEnabled(true); - mergeAction.setEnabled(false); - return; - } - - // node is either a Module or Fragment - copyAction.setEnabled(true); - cutAction.setEnabled(true); - setDeleteActionEnabled(); - - if (node.isFragment()) { - createFolderAction.setEnabled(false); - createFragmentAction.setEnabled(false); - expandAction.setEnabled(false); - mergeAction.setEnabled(false); - collapseAction.setEnabled(false); - - } - else { - createFolderAction.setEnabled(true); - createFragmentAction.setEnabled(true); - expandAction.setEnabled(!allPathsExpanded(path)); - mergeAction.setEnabled(true); - collapseAction.setEnabled(!allPathsCollapsed(path)); - } - } - catch (ConcurrentModificationException e) { - } - } - - /** - * Enable the actions according to what is selected. - * If a "root"-type node is selected, then either all of its children - * must be selected, or none of its children can be selected. - * If the "root"-type node is not selected, then any of its children - * may be selected. - * Called by the ProgramTree when the selection changes. - */ - void adjustMultiActions() { - - cutAction.setEnabled(false); - copyAction.setEnabled(false); - deleteAction.setEnabled(false); - replaceViewAction.setEnabled(false); - - try { - if (validMultiSelection()) { - copyAction.setEnabled(true); - cutAction.setEnabled(true); - setDeleteActionEnabled(); - replaceViewAction.setEnabled(true); - enableViewActions(); - } - enableMergeAction(); - } - catch (ConcurrentModificationException e) { - } - } - - /** - * Disable menu options for the single selection actions. - */ - void disableActions() { - goToViewAction.setEnabled(false); - removeViewAction.setEnabled(false); - replaceViewAction.setEnabled(false); - cutAction.setEnabled(false); - copyAction.setEnabled(false); - pasteAction.setEnabled(false); - deleteAction.setEnabled(false); - renameAction.setEnabled(false); - expandAction.setEnabled(false); - collapseAction.setEnabled(false); - createFolderAction.setEnabled(false); - createFragmentAction.setEnabled(false); - mergeAction.setEnabled(false); - } - - /** - * Create actions for the given owner. - * @param owner - */ private void createActions(String owner) { List list = new ArrayList<>(); goToViewAction = new ProgramTreeAction("Go To start of folder/fragment in View", owner, - new String[] { "Go To in View" }, - KeyStroke.getKeyStroke(KeyEvent.VK_G, InputEvent.CTRL_MASK)) { + KeyStroke.getKeyStroke("ENTER")) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (context instanceof ProgramTreeActionContext ptac) { + return ptac.hasSingleNodeSelection() || ptac.hasFullNodeMultiSelection(); + } + return false; + } + @Override public void actionPerformed(ActionContext context) { - addToView(); + goToView((ProgramTreeActionContext) context); } }; - goToViewAction.setEnabled(false); - goToViewAction - .setPopupMenuData(new MenuData(new String[] { "Go To in View" }, null, "aview")); + goToViewAction.setPopupMenuData(new MenuData(new String[] { "Go To in View" }, "aview")); list.add(goToViewAction); removeViewAction = new ProgramTreeAction("Remove folder/fragment from View", owner, - new String[] { "Remove From View" }, - KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK)) { + KeyStroke.getKeyStroke("R")) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (ptac.hasSingleNodeSelection() || ptac.hasFullNodeMultiSelection()) { + + TreePath[] paths = ptac.getSelectionPaths(); + for (TreePath path : paths) { + ProgramNode node = (ProgramNode) path.getLastPathComponent(); + if (node.isInView()) { + return true; + } + } + } + return false; + } + @Override public void actionPerformed(ActionContext context) { - removeFromView(); + removeFromView((ProgramTreeActionContext) context); } }; - removeViewAction.setEnabled(false); removeViewAction - .setPopupMenuData(new MenuData(new String[] { "Remove from View" }, null, "aview")); + .setPopupMenuData(new MenuData(new String[] { "Remove from View" }, "aview")); list.add(removeViewAction); - replaceViewAction = - new ProgramTreeAction("Replace View", owner, new String[] { "Replace View" }, - KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK)) { - @Override - public void actionPerformed(ActionContext context) { - replaceView(); + setViewAction = new ProgramTreeAction("Set View", owner, KeyStroke.getKeyStroke("S")) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (context instanceof ProgramTreeActionContext ptac) { + return ptac.hasSingleNodeSelection() || ptac.hasFullNodeMultiSelection(); } - }; - replaceViewAction.setEnabled(false); + return false; + } - replaceViewAction - .setPopupMenuData(new MenuData(new String[] { "Replace View" }, null, "aview")); + @Override + public void actionPerformed(ActionContext context) { + setView((ProgramTreeActionContext) context); + } + }; - list.add(replaceViewAction); + setViewAction.setPopupMenuData(new MenuData(new String[] { "Set View" }, "aview")); - cutAction = - new ProgramTreeAction("Cut folder/fragment", owner, new String[] { "Cut" }, null) { - @Override - public void actionPerformed(ActionContext context) { - cut(); + list.add(setViewAction); + + addToViewAction = new ProgramTreeAction("Add to View", owner, KeyStroke.getKeyStroke("A")) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (context instanceof ProgramTreeActionContext ptac) { + return ptac.hasSingleNodeSelection() || ptac.hasFullNodeMultiSelection(); } - }; - cutAction.setEnabled(false); + return false; + } -// ACTIONS - auto generated - cutAction.setPopupMenuData(new MenuData(new String[] { "Cut" }, null, "edit")); + @Override + public void actionPerformed(ActionContext context) { + addToView((ProgramTreeActionContext) context); + } + }; + + addToViewAction.setPopupMenuData(new MenuData(new String[] { "Add to View" }, "aview")); + + list.add(addToViewAction); + + cutAction = new ProgramTreeAction("Cut folder/fragment", owner, null) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (ptac.hasFullNodeMultiSelection()) { + return true; + } + + if (ptac.hasSingleNodeSelection()) { + return !ptac.isOnlyRootNodeSelected(); + } + + return false; + } + + @Override + public void actionPerformed(ActionContext context) { + cut((ProgramTreeActionContext) context); + } + }; + + cutAction.setPopupMenuData(new MenuData(new String[] { "Cut" }, "edit")); list.add(cutAction); - copyAction = - new ProgramTreeAction("Copy folder/fragment", owner, new String[] { "Copy" }, null) { - @Override - public void actionPerformed(ActionContext context) { - copy(); - } - }; - copyAction.setEnabled(false); + copyAction = new ProgramTreeAction("Copy folder/fragment", owner, null) { -// ACTIONS - auto generated - copyAction.setPopupMenuData(new MenuData(new String[] { "Copy" }, null, "edit")); + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (ptac.hasFullNodeMultiSelection()) { + return true; + } + + if (ptac.hasSingleNodeSelection()) { + return !ptac.isOnlyRootNodeSelected(); + } + + return false; + } + + @Override + public void actionPerformed(ActionContext context) { + copy((ProgramTreeActionContext) context); + } + }; + + copyAction.setPopupMenuData(new MenuData(new String[] { "Copy" }, "edit")); list.add(copyAction); - pasteAction = new ProgramTreeAction("Paste folder/fragment", owner, - new String[] { "Paste" }, null, ProgramTreeAction.SINGLE_SELECTION) { + pasteAction = new ProgramTreeAction("Paste folder/fragment", owner, null, + ProgramTreeAction.SINGLE_SELECTION) { + @Override - public void actionPerformed(ActionContext context) { - TreePath path = tree.getSelectionPath(); - ProgramNode node = (ProgramNode) path.getLastPathComponent(); - pasteMgr.paste(node); + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + ProgramNode node = ptac.getSingleSelectedNode(); + return node != null && isPasteOk(node); } + @Override + public void actionPerformed(ActionContext context) { + ProgramTreeActionContext ptac = (ProgramTreeActionContext) context; + ProgramNode node = ptac.getSingleSelectedNode(); + ProgramDnDTree tree = ptac.getTree(); + pasteManager.paste(tree, node); + } }; - pasteAction.setEnabled(false); -// ACTIONS - auto generated - pasteAction.setPopupMenuData(new MenuData(new String[] { "Paste" }, null, "edit")); + pasteAction.setPopupMenuData(new MenuData(new String[] { "Paste" }, "edit")); list.add(pasteAction); - createFolderAction = new ProgramTreeAction("Create Folder", owner, - new String[] { "Create Folder" }, null, ProgramTreeAction.SINGLE_SELECTION) { + createFolderAction = new ProgramTreeAction("Create Folder", owner, null, + ProgramTreeAction.SINGLE_SELECTION) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (!ptac.hasSingleNodeSelection()) { + return false; + } + + if (ptac.isOnlyRootNodeSelected()) { + return true; + } + + ProgramNode node = ptac.getSingleSelectedNode(); + return !node.isFragment(); + } + @Override public void actionPerformed(ActionContext context) { - createFolder(); + createFolder((ProgramTreeActionContext) context); } }; - createFolderAction.setEnabled(false); -// ACTIONS - auto generated - createFolderAction.setPopupMenuData( - new MenuData(new String[] { "Create Folder" }, null, "createGroup")); + createFolderAction + .setPopupMenuData(new MenuData(new String[] { "Create Folder" }, "createGroup")); list.add(createFolderAction); - createFragmentAction = new ProgramTreeAction("Create Fragment", owner, - new String[] { "Create Fragment" }, null, ProgramTreeAction.SINGLE_SELECTION) { + createFragmentAction = new ProgramTreeAction("Create Fragment", owner, null, + ProgramTreeAction.SINGLE_SELECTION) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (!ptac.hasSingleNodeSelection()) { + return false; + } + + if (ptac.isOnlyRootNodeSelected()) { + return true; + } + + ProgramNode node = ptac.getSingleSelectedNode(); + return !node.isFragment(); + } + @Override public void actionPerformed(ActionContext context) { - createFragment((ProgramNode) tree.getLastSelectedPathComponent()); + createFragment((ProgramTreeActionContext) context); } }; - createFragmentAction.setEnabled(false); -// ACTIONS - auto generated - createFragmentAction.setPopupMenuData( - new MenuData(new String[] { "Create Fragment" }, null, "createGroup")); + createFragmentAction + .setPopupMenuData(new MenuData(new String[] { "Create Fragment" }, "createGroup")); list.add(createFragmentAction); - mergeAction = new ProgramTreeAction("Merge folder/fragment with Parent", owner, - new String[] { "Merge with Parent" }, null, ProgramTreeAction.SINGLE_SELECTION) { + mergeAction = new ProgramTreeAction("Merge folder/fragment with Parent", owner, null, + ProgramTreeAction.SINGLE_SELECTION) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (ptac.hasSingleNodeSelection()) { + ProgramNode node = ptac.getSingleSelectedNode(); + if (ptac.isOnlyRootNodeSelected() || node.isFragment()) { + return false; + } + } + + return isMergeEnabled(ptac); + } + @Override public void actionPerformed(ActionContext context) { - merge(); + merge((ProgramTreeActionContext) context); } }; - mergeAction.setEnabled(false); -// ACTIONS - auto generated - mergeAction.setPopupMenuData( - new MenuData(new String[] { "Merge with Parent" }, null, "merge")); + mergeAction.setPopupMenuData(new MenuData(new String[] { "Merge with Parent" }, "merge")); list.add(mergeAction); - deleteAction = new ProgramTreeAction("Delete folder/fragment", owner, - new String[] { "Delete" }, null) { + deleteAction = new ProgramTreeAction("Delete folder/fragment", owner, null) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + return isDeleteEnabled(ptac); + } + @Override public void actionPerformed(ActionContext context) { - delete(); + delete((ProgramTreeActionContext) context); } }; - deleteAction.setEnabled(false); -// ACTIONS - auto generated - deleteAction.setPopupMenuData(new MenuData(new String[] { "Delete" }, null, "delete")); + deleteAction.setPopupMenuData(new MenuData(new String[] { "Delete" }, "delete")); deleteAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_DELETE, 0)); list.add(deleteAction); - renameAction = new ProgramTreeAction("Rename folder/fragment", owner, - new String[] { "Rename" }, null, ProgramTreeAction.SINGLE_SELECTION) { + renameAction = new ProgramTreeAction("Rename folder/fragment", owner, null, + ProgramTreeAction.SINGLE_SELECTION) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (context instanceof ProgramTreeActionContext ptac) { + return ptac.hasSingleNodeSelection(); + } + return false; + } + @Override public void actionPerformed(ActionContext context) { + ProgramDnDTree tree = ((ProgramTreeActionContext) context).getTree(); tree.rename(); } }; - renameAction.setEnabled(false); -// ACTIONS - auto generated - renameAction.setPopupMenuData(new MenuData(new String[] { "Rename" }, null, "delete")); + renameAction.setPopupMenuData(new MenuData(new String[] { "Rename" }, "delete")); list.add(renameAction); - expandAction = new ProgramTreeAction("Expand All folders/fragments", owner, - new String[] { "Expand ALL" }, null, ProgramTreeAction.SINGLE_SELECTION) { + expandAction = new ProgramTreeAction("Expand All folders/fragments", owner, null, + ProgramTreeAction.SINGLE_SELECTION) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (!ptac.hasSingleNodeSelection()) { + return false; + } + + if (ptac.isOnlyRootNodeSelected()) { + return !allPathsExpanded(ptac); + } + + ProgramNode node = ptac.getSingleSelectedNode(); + if (node.isFragment()) { + return false; + } + + return !allPathsExpanded(ptac); + } + @Override public void actionPerformed(ActionContext context) { - expand(); + expand((ProgramTreeActionContext) context); } }; - expandAction.setEnabled(false); -// ACTIONS - auto generated - expandAction.setPopupMenuData(new MenuData(new String[] { "Expand All" }, null, "expand")); + expandAction.setPopupMenuData(new MenuData(new String[] { "Expand All" }, "expand")); list.add(expandAction); - collapseAction = new ProgramTreeAction("Collapse All folders/fragments", owner, - new String[] { "Collapse ALL" }, null, ProgramTreeAction.SINGLE_SELECTION) { + collapseAction = new ProgramTreeAction("Collapse All folders/fragments", owner, null, + ProgramTreeAction.SINGLE_SELECTION) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof ProgramTreeActionContext ptac)) { + return false; + } + + if (!ptac.hasSingleNodeSelection()) { + return false; + } + + if (ptac.isOnlyRootNodeSelected()) { + return !allPathsCollapsed(ptac); + } + + ProgramNode node = ptac.getSingleSelectedNode(); + if (node.isFragment()) { + return false; + } + + return !allPathsCollapsed(ptac); + } + @Override public void actionPerformed(ActionContext context) { - collapse(); + collapse((ProgramTreeActionContext) context); } }; - collapseAction.setEnabled(false); -// ACTIONS - auto generated - collapseAction - .setPopupMenuData(new MenuData(new String[] { "Collapse All" }, null, "expand")); + collapseAction.setPopupMenuData(new MenuData(new String[] { "Collapse All" }, "expand")); list.add(collapseAction); actions = new DockingAction[list.size()]; actions = list.toArray(actions); - - } - - /** - * Get the temporary clipboard the holds the "cut" nodes. - */ - Clipboard getCutClipboard() { - return tempClipboard; } /** * Remove node from the list of ProgramNodes in the clipboard. This method is called * if there was a problem pasting a group. + * @param tree the tree + * @param node the node */ - void removeFromClipboard(Clipboard clipboard, ProgramNode node) { + void removeFromClipboard(ProgramDnDTree tree, ProgramNode node) { try { List list = getProgramNodeListFromClipboard(); @@ -482,49 +535,38 @@ class ProgramTreeActionManager implements ClipboardOwner { list.remove(node); } - node.setDeleted(false); - tree.reloadNode(node); + clearCut(node); if (listSize == 0) { - if (clipboard == GClipboard.getSystemClipboard()) { - doClearSystemClipboard(clipboard); - } - else { - clipboard.setContents(null, this); - } + tempClipboard.setContents(null, this); } } catch (UnsupportedFlavorException e) { // data flavor is not supported - Msg.showError(this, null, "Cut from Clipboard " + clipboard.getName() + " Failed", + Msg.showError(this, null, "Cut from Clipboard " + tempClipboard.getName() + " Failed", "Data flavor in clipboard is not supported.", e); - } catch (IOException e) { // data is no longer available - Msg.showError(this, null, "Cut from Clipboard " + clipboard.getName() + " Failed", + Msg.showError(this, null, "Cut from Clipboard " + tempClipboard.getName() + " Failed", "Data is no longer available for paste operation", e); } catch (Exception e) { - String message = e.getMessage(); - Msg.showError(this, null, "Cut from Clipboard " + clipboard.getName() + " Failed", - message == null ? e.getClass().getSimpleName() : message, e); + String message = ExceptionUtils.getMessage(e); + Msg.showError(this, null, "Cut from Clipboard " + tempClipboard.getName() + " Failed", + message, e); } } - void enablePasteAction(boolean enabled) { - pasteAction.setEnabled(enabled); + void clearCut(ProgramNode node) { + node.setDeleted(false); + plugin.repaintProvider(); } - /** - * Return true if the given node is on the temporary "cut" clipboard. - */ boolean clipboardContains(ProgramNode node) { try { List list = getProgramNodeListFromClipboard(); - if (list == null) { - // SCR 7990--something bad has happened to the copy buffer return false; } @@ -533,7 +575,6 @@ class ProgramTreeActionManager implements ClipboardOwner { catch (UnsupportedFlavorException e) { // data flavor is not supported throw new AssertException("Data flavor in clipboard is not supported."); - } catch (IOException e) { // data is no longer available @@ -541,13 +582,13 @@ class ProgramTreeActionManager implements ClipboardOwner { "Data is no longer available for paste operation", e); } catch (Exception e) { + Msg.error(this, "Unexpected exception checking clipboard", e); } return false; } @SuppressWarnings("unchecked") - // the cast is generating the warning, but we verified the - // type is correct + // the cast is generating the warning, but we verified the type is correct private List getProgramNodeListFromClipboard() throws UnsupportedFlavorException, IOException { List nodeList = Collections.emptyList(); @@ -555,101 +596,80 @@ class ProgramTreeActionManager implements ClipboardOwner { if (t == null) { return nodeList; } - if (!t.isDataFlavorSupported(TreeTransferable.localTreeNodeFlavor)) { + if (!t.isDataFlavorSupported(ProgramTreeTransferable.localTreeNodeFlavor)) { return nodeList; } List list = - (List) t.getTransferData(TreeTransferable.localTreeNodeFlavor); + (List) t.getTransferData(ProgramTreeTransferable.localTreeNodeFlavor); return list; } - /** - * Check clipboard for cut operations. - * @param applyCutChanges true if cut operation should be performed; - * false means that the nodes will revert to their normal icons - * (i.e., not showing as "cut") - */ - void checkClipboard(boolean applyCutChanges) { - - // cut the contents of the clipboard from the program, as - // a paste was not done... - try { - List list = getProgramNodeListFromClipboard(); - - if (list == null) { - // SCR 7990--something bad has happened to the copy buffer - return; + void cutClipboardNodes(ProgramDnDTree tree) { + // cut the contents of the clipboard from the program, as a paste was not done + List list = getNodesFromClipboard(); + for (ProgramNode node : list) { + if (tree.getModel().getRoot() != node.getRoot()) { + break; } - for (ProgramNode node : list) { - if (tree.getModel().getRoot() != node.getRoot()) { - break; - } + ProgramModule parentModule = node.getParentModule(); + Group group = node.getGroup(); + try { + parentModule.removeChild(group.getName()); + } + catch (NotEmptyException e) { + clearCut(node); + } + catch (ConcurrentModificationException e) { + // ha! + } + } - if (applyCutChanges) { + clearSystemClipboard(); + tempClipboard.setContents(null, this); + } - ProgramModule parentModule = node.getParentModule(); - Group group = node.getGroup(); - try { - parentModule.removeChild(group.getName()); - } - catch (NotEmptyException e) { - node.setDeleted(false); - tree.reloadNode(node); - //Err.log(e, "Cut from Clipboard Failed"); - } - catch (ConcurrentModificationException e) { - } - } - else { - // reverse the indication that tree node is being cut - // first make sure that the group still exists - try { - Group g = node.getGroup(); - // call a method on group to make sure it is still valid - g.getName(); + void clearCutClipboardNodes() { - node.setDeleted(false); - tree.reloadNode(node); - } - catch (ConcurrentModificationException e) { - } - } + // cut the contents of the clipboard from the program, as a paste was not done + List list = getNodesFromClipboard(); + for (ProgramNode node : list) { + node.setDeleted(false); + } + plugin.repaintProvider(); + tempClipboard.setContents(null, this); + } + + private List getNodesFromClipboard() { + try { + List list = getProgramNodeListFromClipboard(); + if (list != null) { + return list; } } catch (UnsupportedFlavorException e) { - // data flavor is not supported throw new AssertException("Data flavor in clipboard is not supported."); - - } - catch (IOException e) { - // data is no longer available - Msg.showError(this, null, "Cut from Clipboard Failed", - "Data is no longer available for paste operation", e); - } - catch (Exception e) { - } - - try { - tempClipboard.setContents(null, this); } catch (Exception e) { + Msg.error(this, "Unexpected exception checking clipboard", e); } + return List.of(); } /** * Clear the system clipboard if there is tree transferable data on it. */ - void clearSystemClipboard() { + void clearSystemClipboard() { try { Clipboard systemClipboard = GClipboard.getSystemClipboard(); - if (!systemClipboard.isDataFlavorAvailable(TreeTransferable.localTreeNodeFlavor)) { + if (!systemClipboard + .isDataFlavorAvailable(ProgramTreeTransferable.localTreeNodeFlavor)) { return; } - Object data = systemClipboard.getData(TreeTransferable.localTreeNodeFlavor); + Object data = systemClipboard.getData(ProgramTreeTransferable.localTreeNodeFlavor); if (data == null) { return; } @@ -665,7 +685,7 @@ class ProgramTreeActionManager implements ClipboardOwner { private void doClearSystemClipboard(Clipboard systemClipboard) { // for some reason setting the contents to null for the system clipboard causes a // NullPointerException, so just set it with an empty transferable. - TreeTransferable dummyContents = new TreeTransferable(new ProgramNode[0]); + ProgramTreeTransferable dummyContents = new ProgramTreeTransferable(new ProgramNode[0]); systemClipboard.setContents(dummyContents, (clipboard, contents) -> { // a dummy implementation that will not prevent this plugin from being // reclaimed when it is disposed @@ -673,106 +693,24 @@ class ProgramTreeActionManager implements ClipboardOwner { } boolean isReplacingView() { - return replacingView; + return isSettingView; } - //////////////////////////////////////////////////////////////////////// - // ** private methods ** - //////////////////////////////////////////////////////////////////////// - - /** - * Validate the selection for a case of the multi-selection - * for either the popup menu or the plugin action. - * @return true if the root node is not selected, or - * a node and all of its children are selected or only - * a parent node is selected; return false if this is not - * the case. - */ - private boolean validMultiSelection() { - - TreePath[] paths = tree.getSelectionPaths(); - if (paths == null) { - return false; - } - TreePath rootPath = root.getTreePath(); - for (TreePath element : paths) { - if (element.equals(rootPath)) { - return false; - } - } - + private void addToView(ProgramTreeActionContext context) { + ProgramDnDTree tree = context.getTree(); + TreePath[] paths = context.getSelectionPaths(); for (TreePath path : paths) { - - ProgramNode node = (ProgramNode) path.getLastPathComponent(); - // if the node allows children, then verify that - // either (1) only the parent is selected, or - // (2) all children are selected - if (node.getAllowsChildren() && !validPathSelection(node, paths)) { - return false; - } + updateViewList(tree, path); } - - return true; + tree.fireTreeViewChanged(); } - /** - * Enable the view range actions according what is already in the - * the view. - */ - private void enableViewActions() { - goToViewAction.setEnabled(true); - removeViewAction.setEnabled(true); - } + private void goToView(ProgramTreeActionContext context) { - /** - * For a multi-selection case, verifies that a selection - * from the node's level is valid. - * Returns true if (1) either the paths of all children of node - * are selected, or if (2) none of the paths of all children of node - * are selected. - * Returns false if not all of the children of node are selected - */ - private boolean validPathSelection(ProgramNode node, TreePath[] selectedPaths) { + // the selected node must be represented in the view for the 'go to' to work + addToView(context); - 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 true; - } - return false; - } - - /////////////////////////////////////////////////////////////////// - // ** menu item callback methods for single selection popup - /////////////////////////////////////////////////////////////////// - - private void addToView() { - - TreePath path = tree.getSelectionPath(); - - if (tree.getSelectionCount() > 1) { - path = tree.getLeadSelectionPath(); - } - - TreePath[] paths = tree.getSelectionPaths(); - for (TreePath element : paths) { - updateViewList(element); - } - - ProgramNode node = (ProgramNode) path.getLastPathComponent(); + ProgramNode node = context.getLeadSelectedNode(); ProgramFragment f = node.getFragment(); Address addr = null; if (f != null && !f.isEmpty()) { @@ -783,114 +721,97 @@ class ProgramTreeActionManager implements ClipboardOwner { addr = module.getFirstAddress(); } - //notify listeners of change - tree.fireTreeViewChanged(); if (addr != null) { + ProgramDnDTree tree = context.getTree(); tree.goTo(addr); } } - /** - * Remove the selected path from the current view. - */ - private void removeFromView() { + private void removeFromView(ProgramTreeActionContext context) { - if (tree.getSelectionCount() == 1) { - TreePath path = tree.getSelectionPath(); + ProgramDnDTree tree = context.getTree(); + TreePath[] paths = context.getSelectionPaths(); + if (paths.length == 1) { + TreePath path = paths[0]; if (viewList.contains(path)) { - removePathFromView(path, true); + tree.removeFromView(path); } else { - // remove all descendants from the view - removeChildFromView((ProgramNode) path.getLastPathComponent()); - tree.fireTreeViewChanged(); + // remove all descendants from the view + ProgramNode node = (ProgramNode) path.getLastPathComponent(); + removeChildFromView(tree, node); } } else { - removeRangeFromView(); - } - } - - /** - * Remove path from the view. - * @param path tree path - * @param fireEvent true means to fire the tree view changed - */ - private void removePathFromView(TreePath path, boolean fireEvent) { - if (viewList.contains(path)) { - tree.removeFromView(path); - if (fireEvent) { - tree.fireTreeViewChanged(); + for (TreePath path : paths) { + tree.removeFromView(path); } } + + tree.fireTreeViewChanged(); } /** * Recursively remove nodes from view. */ - private void removeChildFromView(ProgramNode node) { + private void removeChildFromView(ProgramDnDTree tree, ProgramNode node) { for (int i = 0; i < node.getChildCount(); i++) { ProgramNode child = (ProgramNode) node.getChildAt(i); if (child.getAllowsChildren()) { - removeChildFromView(child); + removeChildFromView(tree, child); } else { - removePathFromView(child.getTreePath(), false); + TreePath path = child.getTreePath(); + if (viewList.contains(path)) { + tree.removeFromView(path); + } } } } - /** - * Replace the current view with the selected paths. - */ - private void replaceView() { - replacingView = true; + private void setView(ProgramTreeActionContext context) { + isSettingView = true; try { - tree.setViewPaths(tree.getSelectionPaths()); + ProgramDnDTree tree = context.getTree(); + tree.setViewPaths(context.getSelectionPaths()); } finally { - replacingView = false; + isSettingView = false; } } - /** - * Put selected paths on the clipboard. - */ - private void cut() { + private void cut(ProgramTreeActionContext context) { // revert the "cut" if something is in the temporary clipboard - checkClipboard(false); + clearCutClipboardNodes(); - if (tree.getSelectionCount() == 1) { + TreePath[] paths = context.getSelectionPaths(); + if (paths.length == 0) { + return; + } - // cut to clipboard - TreePath[] paths = new TreePath[] { tree.getSelectionPath() }; - setClipboardContents(GClipboard.getSystemClipboard(), paths); - // put on the temporary clipboard - setClipboardContents(tempClipboard, paths); - setNodesDeleted(paths); - } - else { - cutRange(); - } + // cut to clipboard + setClipboardContents(GClipboard.getSystemClipboard(), paths); + // put on the temporary clipboard + setClipboardContents(tempClipboard, paths); + setNodesDeleted(paths); } /** * Put selected paths on the clipboard; clear the temp clipboard * if something was there. */ - private void copy() { + private void copy(ProgramTreeActionContext context) { // revert the "cut" if something is in the temporary clipboard - checkClipboard(false); + clearCutClipboardNodes(); - if (tree.getSelectionCount() == 1) { - // copy to clipboard - setClipboardContents(GClipboard.getSystemClipboard(), - new TreePath[] { tree.getSelectionPath() }); - } - else { - copyRange(); + TreePath[] paths = context.getSelectionPaths(); + if (paths.length == 0) { + return; } + + // copy to clipboard + setClipboardContents(GClipboard.getSystemClipboard(), paths); } /** @@ -902,16 +823,18 @@ class ProgramTreeActionManager implements ClipboardOwner { for (TreePath element : paths) { ProgramNode node = (ProgramNode) element.getLastPathComponent(); node.setDeleted(true); - tree.reloadNode(node); } + + plugin.repaintProvider(); } /** * Delete node(s) from the tree; called from the action listener * on the menu. */ - private void delete() { + private void delete(ProgramTreeActionContext context) { + ProgramDnDTree tree = context.getTree(); int transactionID = tree.startTransaction("Delete"); if (transactionID < 0) { return; @@ -919,46 +842,47 @@ class ProgramTreeActionManager implements ClipboardOwner { boolean success = false; try { synchronized (root) { - try { - success = deleteRange(); - } - catch (Exception e) { - Msg.showError(this, null, null, null, e); - } + TreePath[] paths = context.getSelectionPaths(); + success = delete(tree, paths); } } finally { tree.endTransaction(transactionID, success); } - } - /** - * Expand the first selected node; called from an action listener - * on a menu. - */ - private void expand() { - TreePath path = tree.getLeadSelectionPath(); - tree.expandNode((ProgramNode) tree.getLastSelectedPathComponent()); - expandAction.setEnabled(!allPathsExpanded(path)); - collapseAction.setEnabled(!allPathsCollapsed(path)); + private boolean delete(ProgramDnDTree tree, TreePath[] paths) { + + boolean changesMade = false; + StringBuilder sb = new StringBuilder(); + + for (TreePath element : paths) { + ProgramNode node = (ProgramNode) element.getLastPathComponent(); + if (tree.removeGroup(node, sb)) { + changesMade = true; + } + } + + if (sb.length() > 0) { + sb.insert(0, "Failed to delete the following:\n"); + Msg.showWarn(getClass(), null, "Delete Failed", sb.toString()); + } + return changesMade; } - /** - * Collapse the first selected node; called from an action listener - * on a menu. - */ - private void collapse() { - TreePath path = tree.getLeadSelectionPath(); - collapseNode((ProgramNode) tree.getLastSelectedPathComponent()); - expandAction.setEnabled(!allPathsExpanded(path)); - collapseAction.setEnabled(!allPathsCollapsed(path)); + void expand(ProgramTreeActionContext context) { + ProgramDnDTree tree = context.getTree(); + tree.expandNode(context.getSingleSelectedNode()); + plugin.contextChanged(); } - /** - * Collapse all descendants starting at node. - */ - private void collapseNode(ProgramNode node) { + private void collapse(ProgramTreeActionContext context) { + ProgramDnDTree tree = context.getTree(); + collapseNode(tree, context.getSingleSelectedNode()); + plugin.contextChanged(); + } + + private void collapseNode(ProgramDnDTree tree, ProgramNode node) { int nchild = node.getChildCount(); for (int i = 0; i < nchild; i++) { @@ -968,7 +892,7 @@ class ProgramTreeActionManager implements ClipboardOwner { if (child.equals(node) || child.isLeaf()) { continue; } - collapseNode(child); + collapseNode(tree, child); } tree.collapsePath(node.getTreePath()); } @@ -977,29 +901,31 @@ class ProgramTreeActionManager implements ClipboardOwner { * Merge a module with its parent. The module is deleted if it has * no other parents; called by the action listener on a menu. */ - private void merge() { + private void merge(ProgramTreeActionContext context) { synchronized (root) { - ArrayList list = tree.getSortedSelection(); - CompoundCmd compCmd = new CompoundCmd("Merge with Parent"); + + CompoundCmd cmd = new CompoundCmd<>("Merge with Parent"); + ProgramDnDTree tree = context.getTree(); String treeName = tree.getTreeName(); - for (ProgramNode node : list) { - tree.removeSelectionPath(node.getTreePath()); + TreePath[] paths = context.getSelectionPaths(); + for (TreePath path : paths) { + + ProgramNode node = (ProgramNode) path.getLastPathComponent(); + tree.removeSelectionPath(path); ProgramNode parentNode = (ProgramNode) node.getParent(); if (node.isModule() && parentNode != null) { - compCmd.add(new MergeFolderCmd(treeName, node.getName(), parentNode.getName())); + cmd.add(new MergeFolderCmd(treeName, node.getName(), parentNode.getName())); } } - if (!plugin.getTool().execute(compCmd, program)) { - plugin.getTool().setStatusInfo(compCmd.getStatusMsg()); + + PluginTool tool = plugin.getTool(); + if (!tool.execute(cmd, program)) { + tool.setStatusInfo(cmd.getStatusMsg()); } } } - /** - * Create a new empty fragment; called from an action listener on - * a menu. - */ - private void createFragment(ProgramNode node) { + private void createFragment(ProgramTreeActionContext context) { synchronized (root) { String errMsg = null; @@ -1007,54 +933,52 @@ class ProgramTreeActionManager implements ClipboardOwner { // sync program so we don't get a deadlock -- // an event gets generated because of the add module. synchronized (program) { + ProgramDnDTree tree = context.getTree(); String name = tree.getNewFragmentName(); String treeName = tree.getTreeName(); + ProgramNode node = context.getSingleSelectedNode(); CreateFragmentCmd cmd = new CreateFragmentCmd(treeName, name, node.getName()); if (tree.getTool().execute(cmd, program)) { ProgramFragment f = program.getListing().getFragment(treeName, name); - initiateCellEditor(node, f); + initiateCellEditor(tree, node, f); } else { errMsg = cmd.getStatusMsg(); } } if (errMsg != null) { - Msg.showError(this, tree, "Create Fragment Failed", errMsg); + Msg.showError(this, null, "Create Fragment Failed", errMsg); } } } - /** - * Create a new empty module; called from an action listener on - * a menu. - */ - private void createFolder() { - String errorMsg = null; - + private void createFolder(ProgramTreeActionContext context) { + String errorMessage = null; synchronized (root) { // sync program so we don't get a deadlock -- // an event gets generated because of the add module. synchronized (program) { - ProgramNode node = (ProgramNode) tree.getLastSelectedPathComponent(); + ProgramNode node = context.getSingleSelectedNode(); // if the node has not been yet visited, then when the group is added via the // command below, the new child node in the parent will not be found node.visit(); + ProgramDnDTree tree = context.getTree(); String name = tree.getNewFolderName(); String treeName = tree.getTreeName(); CreateFolderCommand cmd = new CreateFolderCommand(treeName, name, node.getName()); if (tree.getTool().execute(cmd, program)) { ProgramModule m = program.getListing().getModule(treeName, name); - initiateCellEditor(node, m); + initiateCellEditor(tree, node, m); } else { - errorMsg = cmd.getStatusMsg(); + errorMessage = cmd.getStatusMsg(); } } } - if (errorMsg != null) { - Msg.showError(this, tree, "Create Folder Failed", errorMsg); + if (errorMessage != null) { + Msg.showError(this, null, "Create Folder Failed", errorMessage); } } @@ -1075,11 +999,7 @@ class ProgramTreeActionManager implements ClipboardOwner { ExpiringSwingTimer.get(supplier, expireMs, consumer); } - /** - * Find the node corresponding to the given group. Start the cell - * editor for the child node. - */ - private void initiateCellEditor(ProgramNode parent, Group group) { + private void initiateCellEditor(ProgramDnDTree tree, ProgramNode parent, Group group) { if (!parent.wasVisited()) { tree.visitNode(parent); } @@ -1090,79 +1010,6 @@ class ProgramTreeActionManager implements ClipboardOwner { }); } - /////////////////////////////////////////////////////////////////////// - // *** callbacks for multi selection popup - /////////////////////////////////////////////////////////////////////// - /** - * Cut a range of nodes and put them on the clipboard; - * called from an action listener on a menu. - */ - private void cutRange() { - - TreePath[] paths = tree.getSelectionPaths(); - - if (paths.length == 0) { - return; - } - // cut to clipboard - setClipboardContents(GClipboard.getSystemClipboard(), paths); - // put on the temporary clipboard - setClipboardContents(tempClipboard, paths); - setNodesDeleted(paths); - } - - /** - * Copy a range of nodes and put them on the clipboard; - * called from an action listener on a menu. - */ - private void copyRange() { - - // generate a list of selected modules that are - // "root"-type modules, i.e., not submodules within the selection. - TreePath[] paths = tree.getSelectionPaths(); - - if (paths.length == 0) { - return; - } - // cut to clipboard - setClipboardContents(GClipboard.getSystemClipboard(), paths); - } - - /** - * Delete a range of Modules; called from an action listener on a menu. - * @return true if program was affected - */ - private boolean deleteRange() { - - TreePath[] paths = tree.getSelectionPaths(); - - boolean changesMade = false; - StringBuffer sb = new StringBuffer(); - - for (TreePath element : paths) { - ProgramNode node = (ProgramNode) element.getLastPathComponent(); - if (tree.removeGroup(node, sb)) { - changesMade = true; - } - } - - if (sb.length() > 0) { - sb.insert(0, "Failed to delete the following:\n"); - Msg.showWarn(getClass(), tree, "Delete Failed", sb.toString()); - } - return changesMade; - } - - private void removeRangeFromView() { - TreePath[] selPaths = tree.getSelectionPaths(); - for (TreePath element : selPaths) { - tree.removeFromView(element); - } - if (selPaths.length > 0) { - tree.fireTreeViewChanged(); - } - } - /** * Create a TreeTransferable object from the given paths, and * use it set the clipboard contents. @@ -1174,14 +1021,23 @@ class ProgramTreeActionManager implements ClipboardOwner { nodes[i] = (ProgramNode) paths[i].getLastPathComponent(); } - TreeTransferable contents = new TreeTransferable(nodes); + ProgramTreeTransferable contents = new ProgramTreeTransferable(nodes); clipboard.setContents(contents, this); } - /** - * Return true if this path has all of its sub-paths expanded. - */ - private boolean allPathsExpanded(TreePath path) { + private boolean allPathsExpanded(ProgramTreeActionContext context) { + + if (!context.hasSingleNodeSelection()) { + return false; + } + + ProgramDnDTree tree = context.getTree(); + TreePath[] paths = context.getSelectionPaths(); + TreePath path = paths[0]; + return allPathsExpanded(tree, path); + } + + private boolean allPathsExpanded(ProgramDnDTree tree, TreePath path) { ProgramNode node = (ProgramNode) path.getLastPathComponent(); if (node.isLeaf()) { @@ -1204,7 +1060,7 @@ class ProgramTreeActionManager implements ClipboardOwner { return false; } - if (!allPathsExpanded(child.getTreePath())) { + if (!allPathsExpanded(tree, child.getTreePath())) { return false; } } @@ -1214,18 +1070,26 @@ class ProgramTreeActionManager implements ClipboardOwner { return true; } - /** - * Return true if this path has all of its sub-paths collapsed. - */ - private boolean allPathsCollapsed(TreePath path) { + private boolean allPathsCollapsed(ProgramTreeActionContext context) { + + if (!context.hasSingleNodeSelection()) { + return false; + } + + ProgramDnDTree tree = context.getTree(); + TreePath[] paths = context.getSelectionPaths(); + TreePath path = paths[0]; + return allPathsCollapsed(tree, path); + } + + private boolean allPathsCollapsed(ProgramDnDTree tree, TreePath path) { ProgramNode node = (ProgramNode) path.getLastPathComponent(); if (tree.isExpanded(path)) { return false; } - boolean allLeaves = true; // variable for knowing whether - // all children are leaves + boolean allLeaves = true; // variable for knowing whether all children are leaves int nchild = node.getChildCount(); for (int i = 0; i < nchild; i++) { ProgramNode child = (ProgramNode) node.getChildAt(i); @@ -1237,7 +1101,7 @@ class ProgramTreeActionManager implements ClipboardOwner { return false; } - if (!allPathsCollapsed(child.getTreePath())) { + if (!allPathsCollapsed(tree, child.getTreePath())) { return false; } } @@ -1247,43 +1111,31 @@ class ProgramTreeActionManager implements ClipboardOwner { return true; } - /** - * Returns true if the paste operation is valid for - * the given node. - * If the node and node to paste have the same name, then return - * false. - */ @SuppressWarnings("unchecked") // the cast is safe, since we checked the flavor private boolean isPasteOk(ProgramNode destNode) { boolean isCutOperation = false; Clipboard systemClipboard = GClipboard.getSystemClipboard(); - if (!systemClipboard.isDataFlavorAvailable(TreeTransferable.localTreeNodeFlavor)) { + if (!systemClipboard.isDataFlavorAvailable(ProgramTreeTransferable.localTreeNodeFlavor)) { return false; } - try { - // we will put items on the 'tempClipboard' when the cut action is executed - Transferable temp = tempClipboard.getContents(this); - isCutOperation = (temp != null); - } - catch (Exception e) { - // bad stuff on the clipboard, so ignore it - return false; - } + // we will put items on the 'tempClipboard' when the cut action is executed + Transferable temp = tempClipboard.getContents(this); + isCutOperation = (temp != null); try { - List list = - (List) systemClipboard.getData(TreeTransferable.localTreeNodeFlavor); + List list = (List) systemClipboard + .getData(ProgramTreeTransferable.localTreeNodeFlavor); if (list == null) { - // SCR 7990--something bad has happened to the copy buffer return false; } boolean pasteEnabled = false; for (ProgramNode pasteNode : list) { - boolean pasteAllowed = pasteMgr.isPasteAllowed(destNode, pasteNode, isCutOperation); + boolean pasteAllowed = + pasteManager.isPasteAllowed(destNode, pasteNode, isCutOperation); if (isCutOperation && !pasteAllowed) { // for cut operation all nodes must be able to be pasted at destNode return false; @@ -1307,25 +1159,19 @@ class ProgramTreeActionManager implements ClipboardOwner { "Data is no longer available for paste operation", e); } catch (Exception e) { - String msg = e.getMessage(); - if (msg == null) { - msg = e.toString(); - } - Msg.showError(this, null, "Check Clipboard Failed", msg, e); + String message = ExceptionUtils.getMessage(e); + Msg.showError(this, null, "Check Clipboard Failed", message, e); } return false; } /** - * Update the view list if the the given path is the an ancestor of any of - * the paths currently in the view; remove the descendant and add the - * ancestor path. - * - * @param path - * path the check against the view list - * + * Update the view list if the the given path is the an ancestor of any of the paths currently + * in the view; remove the descendant and add the ancestor path. + * @param tree the tree + * @param path path the check against the view list */ - private void updateViewList(TreePath path) { + private void updateViewList(ProgramDnDTree tree, TreePath path) { ProgramNode node = (ProgramNode) path.getLastPathComponent(); if (!tree.hasAncestorsInView(node) && !viewList.contains(path)) { for (int i = 0; i < viewList.size(); i++) { @@ -1339,79 +1185,45 @@ class ProgramTreeActionManager implements ClipboardOwner { } } - private void selectionChanged() { - // adjust actions according to what is selected - int count = tree.getSelectionCount(); - disableActions(); - if (count == 1) { - adjustSingleActions((ProgramNode) tree.getSelectionPath().getLastPathComponent()); - } - else if (validMultiSelection()) { - copyAction.setEnabled(true); - cutAction.setEnabled(true); - deleteAction.setEnabled(true); - replaceViewAction.setEnabled(true); - enableViewActions(); - } - enableMergeAction(); - } + private boolean isDeleteEnabled(ProgramTreeActionContext context) { - private void setDeleteActionEnabled() { - deleteAction.setEnabled(false); + TreePath[] paths = context.getSelectionPaths(); + if (paths == null) { + return false; + } - TreePath[] paths = tree.getSelectionPaths(); for (TreePath element : paths) { ProgramNode node = (ProgramNode) element.getLastPathComponent(); if (node.isFragment()) { ProgramFragment f = node.getFragment(); if (f.isEmpty() || (!f.isEmpty() && node.getFragment().getNumParents() > 1)) { - deleteAction.setEnabled(true); - break; + return true; } } else { ProgramModule m = node.getModule(); if (m.getNumChildren() == 0 || m.getNumParents() > 1) { - deleteAction.setEnabled(true); - break; + return true; } } } + return false; } - private void enableMergeAction() { - mergeAction.setEnabled(false); - TreePath[] paths = tree.getSelectionPaths(); + private boolean isMergeEnabled(ProgramTreeActionContext context) { + TreePath[] paths = context.getSelectionPaths(); if (paths == null) { - return; + return false; } + for (TreePath element : paths) { ProgramNode node = (ProgramNode) element.getLastPathComponent(); if (node.isModule()) { - mergeAction.setEnabled(true); - return; + return true; } } - } - - /** - * A listener for selection events on the ProgramDnDTree. - */ - private class SelectionListener implements TreeSelectionListener { - - /** - * Called whenever the value of the selection changes. - */ - @Override - public void valueChanged(TreeSelectionEvent e) { - if (program == null) { - return; - } - - selectionChanged(); - } - + return false; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePanel.java index 4861757941..5868430e8f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePanel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,6 +19,7 @@ import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.*; +import java.util.List; import javax.swing.*; import javax.swing.event.*; @@ -56,9 +57,9 @@ class ProgramTreePanel extends JPanel implements ChangeListener { initialize(); // Disable tree expand/collapse on double-click. - if (tree != null) { - tree.setToggleClickCount(0); - } + tree.setToggleClickCount(0); + + tree.addTreeSelectionListener(e -> plugin.contextChanged()); } // ChangeListener interface method @@ -160,10 +161,9 @@ class ProgramTreePanel extends JPanel implements ChangeListener { * Get the currently viewed group paths. */ GroupView getGroupView() { - ArrayList viewList = tree.getViewList(); + List viewList = tree.getViewList(); ArrayList list = new ArrayList(); - for (int i = 0; i < viewList.size(); i++) { - TreePath p = viewList.get(i); + for (TreePath p : viewList) { ProgramNode node = (ProgramNode) p.getLastPathComponent(); GroupPath gp = node.getGroupPath(); if (gp != null) { @@ -191,7 +191,7 @@ class ProgramTreePanel extends JPanel implements ChangeListener { } ProgramNode prepareSelectionForPopup(MouseEvent event) { - return tree.prepareSelectionForPopup(event); + return tree.adjustSelectionForPopup(event); } GroupPath[] getViewedGroups() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java index 5b9719ea87..8f4b99f393 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -83,14 +83,15 @@ public class ProgramTreePlugin extends ProgramPlugin private final static Icon NAVIGATION_ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON; + private ViewManagerComponentProvider componentProvider; + private ProgramTreeActionManager actionManager; + private TreeViewProvider defaultProvider; + private TreeViewProvider currentProvider; private Map providerMap;// map of view providers, key is the name private GoToService goToService; private ViewManagerService viewManagerService; - private ProgramTreeActionManager actionManager; - private TreeViewProvider currentProvider; - private ViewManagerComponentProvider viewProvider; + private ProgramListener programListener; - private TreeViewProvider defaultProvider; private boolean firingGoTo; private RunManager runManager; private DockingAction createAction; @@ -99,31 +100,30 @@ public class ProgramTreePlugin extends ProgramPlugin private JPopupMenu popup; /** - * Tree signals that a user double-click will replace the view with the - * current node + * Tree signals that a user double-click will replace the view with the current node */ - private boolean isReplaceViewMode = true; + private boolean isReplaceViewMode = false; public ProgramTreePlugin(PluginTool tool) { super(tool); - viewProvider = new ViewManagerComponentProvider(tool, getName()); - registerServiceProvided(ViewManagerService.class, viewProvider); + componentProvider = new ViewManagerComponentProvider(tool, getName()); + registerServiceProvided(ViewManagerService.class, componentProvider); providerMap = new HashMap<>(); actionManager = new ProgramTreeActionManager(this); - registerActions(); + registerProviderActions(); + programListener = new ProgramListener(this); runManager = new RunManager(); runManager.showProgressBar(false); - createActions(); + createPluginActions(); // show default provider defaultProvider = addTreeView(DEFAULT_TREE_NAME); initOptions(tool.getOptions(PROGRAM_TREE_OPTION_NAME)); - } @Override @@ -131,7 +131,7 @@ public class ProgramTreePlugin extends ProgramPlugin if (interfaceClass != ViewProviderService.class) { return; } - viewProvider.serviceAdded((ViewProviderService) service); + componentProvider.serviceAdded((ViewProviderService) service); } /** @@ -142,7 +142,7 @@ public class ProgramTreePlugin extends ProgramPlugin if (interfaceClass != ViewProviderService.class) { return; } - viewProvider.serviceRemoved((ViewProviderService) service); + componentProvider.serviceRemoved((ViewProviderService) service); } private void initOptions(ToolOptions options) { @@ -218,7 +218,7 @@ public class ProgramTreePlugin extends ProgramPlugin programListener.dispose(); } - viewProvider.dispose(); + componentProvider.dispose(); super.dispose(); } @@ -246,7 +246,7 @@ public class ProgramTreePlugin extends ProgramPlugin */ @Override public void writeDataState(SaveState saveState) { - viewProvider.writeDataState(saveState); + componentProvider.writeDataState(saveState); saveState.putInt(NUMBER_OF_VIEWS, providerMap.size()); int idx = 0; @@ -312,7 +312,7 @@ public class ProgramTreePlugin extends ProgramPlugin restoreTreeViews(); - viewProvider.readDataState(saveState); + componentProvider.readDataState(saveState); } private void restoreTreeViews() { @@ -332,7 +332,7 @@ public class ProgramTreePlugin extends ProgramPlugin list.add(provider); } - viewProvider.treeViewsRestored(list); + componentProvider.treeViewsRestored(list); } @Override @@ -369,12 +369,11 @@ public class ProgramTreePlugin extends ProgramPlugin protected void programActivated(Program program) { program.addListener(programListener); setProgram(program); - viewProvider.setCurrentProgram(program); + componentProvider.setCurrentProgram(program); } private void removeStaleProviders(ArrayList providerList) { - HashMap map = new HashMap<>(providerMap); - + Map map = new HashMap<>(providerMap); for (String treeName : map.keySet()) { TreeViewProvider provider = map.get(treeName); if (!providerList.contains(provider)) { @@ -384,9 +383,6 @@ public class ProgramTreePlugin extends ProgramPlugin } } - /** - * Initialization method: Get the services we need. - */ @Override protected void init() { goToService = tool.getService(GoToService.class); @@ -414,6 +410,10 @@ public class ProgramTreePlugin extends ProgramPlugin } } + void contextChanged() { + tool.contextChanged(componentProvider); + } + void treeViewAdded(String treeName) { TreeViewProvider provider = providerMap.get(treeName); if (provider == null) { @@ -430,10 +430,8 @@ public class ProgramTreePlugin extends ProgramPlugin currentProvider.replaceView(node); } - // If the node is NOT the root node, just go to the location - // of the first address in the fragment. If it's root, we - // need to get the lowest address of any item in the - // current view. + // If the node is NOT the root node, just go to the location of the first address in the + // fragment. If it's the root, we need to get the lowest address in the current view. if (node.isFragment()) { goTo(node.getFragment()); } @@ -445,7 +443,7 @@ public class ProgramTreePlugin extends ProgramPlugin } } - void goTo(ProgramFragment fragment) { + private void goTo(ProgramFragment fragment) { Address minAddress = fragment.getMinAddress(); @@ -671,6 +669,10 @@ public class ProgramTreePlugin extends ProgramPlugin reloadTree(tree, false); } + void repaintProvider() { + componentProvider.getComponent().repaint(); + } + /** * Remember expansion and selection state, and the reload the program * because it just got restored from an undo operation. @@ -781,10 +783,10 @@ public class ProgramTreePlugin extends ProgramPlugin return currentProgram.getListing().getRootModule(treeName) != null; } - private void registerActions() { + private void registerProviderActions() { DockingAction[] actions = actionManager.getActions(); - for (DockingAction element : actions) { - tool.addAction(element); + for (DockingAction action : actions) { + tool.addLocalAction(componentProvider, action); } } @@ -936,7 +938,7 @@ public class ProgramTreePlugin extends ProgramPlugin /** * Create the local actions that are shared among all the providers. */ - private void createActions() { + private void createPluginActions() { openAction = new DockingAction("Open Tree View", getName()) { @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeTransferable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeTransferable.java new file mode 100644 index 0000000000..c064d8b36a --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ProgramTreeTransferable.java @@ -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 flavorList = Arrays.asList(flavors); + private List nodeList; + + ProgramTreeTransferable(ProgramNode[] nodes) { + nodeList = new ArrayList(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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeTransferable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeTransferable.java deleted file mode 100644 index 28b1630eb6..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeTransferable.java +++ /dev/null @@ -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 flavorList = Arrays.asList(flavors); - private ArrayList nodeList; - - /** - * Constructor - */ - TreeTransferable(ProgramNode []nodes) { - nodeList = new ArrayList(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; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java index 956f25539b..4d13d969a0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/TreeViewProvider.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,6 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.LinkedList; -import javax.swing.JComponent; import javax.swing.event.ChangeEvent; import docking.ActionContext; @@ -76,7 +75,7 @@ class TreeViewProvider implements ViewProviderService { } @Override - public JComponent getViewComponent() { + public ProgramTreePanel getViewComponent() { return treePanel; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java index 58d28a1989..4c38288bfb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewManagerComponentProvider.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,8 +16,7 @@ package ghidra.app.plugin.core.programtree; import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Collection; +import java.util.*; import javax.swing.JComponent; @@ -40,7 +39,7 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter public static final String CURRENT_VIEW = "Current Viewname"; private ViewPanel viewPanel; - private ArrayList listeners; + private List listeners; private Program currentProgram; private String restoredViewName; @@ -72,6 +71,8 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter String currentName = NAME; ComponentProvider.registerProviderNameOwnerChange(intermediateName, currentOwner, currentName, currentOwner); + + addToTool(); } void serviceAdded(ViewProviderService service) { @@ -186,11 +187,14 @@ public class ViewManagerComponentProvider extends ComponentProviderAdapter } if (event != null) { - return new ProgramActionContext(this, currentProgram, viewPanel, - getActivePopupObject(event)); + // this should be a ProgramNode or a tab + Object clickedObject = getActivePopupObject(event); + return new ProgramTreeActionContext(this, currentProgram, viewPanel, clickedObject); } - return new ProgramActionContext(this, currentProgram, viewPanel, getFocusedContext()); + // this should be a ProgramNode + Object focusedObject = getFocusedContext(); + return new ProgramTreeActionContext(this, currentProgram, viewPanel, focusedObject); } private Object getFocusedContext() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java index 6a15324a38..c7e56cb09e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/programtree/ViewPanel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,8 +17,7 @@ package ghidra.app.plugin.core.programtree; import java.awt.*; import java.awt.event.MouseEvent; -import java.util.Collection; -import java.util.HashMap; +import java.util.*; import javax.swing.*; import javax.swing.event.ChangeEvent; @@ -43,7 +42,7 @@ class ViewPanel extends JPanel implements ChangeListener { private JTabbedPane tabbedPane; private ViewManagerComponentProvider provider; - private HashMap map; + private Map map; private DockingAction closeAction; private DockingAction deleteAction; private DockingAction renameAction; @@ -71,10 +70,6 @@ class ViewPanel extends JPanel implements ChangeListener { */ void addView(ViewProviderService vp) { - if (!provider.isInTool()) { - provider.addToTool(); - } - String name = vp.getViewName(); if (map.remove(name) != null) { map.put(name, vp); @@ -127,10 +122,6 @@ class ViewPanel extends JPanel implements ChangeListener { tabbedPane.addChangeListener(this); } - /*if (isEmpty()) { - provider.removeFromTool(); - }*/ - return true; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java index e7dfe706fa..dcb1286a2a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/module/ModuleSortPluginTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.module; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.*; @@ -54,8 +53,7 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest { tool.addPlugin(ProgramTreePlugin.class.getName()); tool.addPlugin(ModuleSortPlugin.class.getName()); List list = tool.getManagedPlugins(); - for (int i = 0; i < list.size(); i++) { - Plugin p = list.get(i); + for (Plugin p : list) { if (p.getClass() == ModuleSortPlugin.class) { plugin = (ModuleSortPlugin) p; break; @@ -200,8 +198,6 @@ public class ModuleSortPluginTest extends AbstractGhidraHeadedIntegrationTest { ViewManagerService vmService = tool.getService(ViewManagerService.class); ViewProviderService vps = vmService.getCurrentViewProvider(); - Object context = vps.getActivePopupObject(null); - for (DockingActionIf action : actions) { if (action.getName().indexOf("Address") > 0) { action.actionPerformed(vps.getActionContext(null)); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/AbstractProgramTreePluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/AbstractProgramTreePluginTest.java index 234dcb8e7c..faffab3cb6 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/AbstractProgramTreePluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/AbstractProgramTreePluginTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,6 +18,7 @@ package ghidra.app.plugin.core.programtree; import java.awt.Component; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BooleanSupplier; import javax.swing.tree.TreePath; @@ -87,6 +88,15 @@ public abstract class AbstractProgramTreePluginTest extends AbstractGhidraHeaded return context; } + protected void performTreeAction(DockingActionIf action) { + performAction(action, getActionContext(), true); + } + + protected void waitForTreeEdit() { + BooleanSupplier bs = () -> runSwing(() -> tree.isEditing()); + waitFor(bs); + } + protected void setTreeView(final String viewName) { tree = plugin.getTree(viewName); root = (ProgramNode) tree.getModel().getRoot(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin1Test.java index a09aa83329..47af3dc115 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin1Test.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -157,7 +157,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { DockingActionIf createFolderAction = getAction("Create Folder"); String newFolderName = tree.getNewFolderName(); - performAction(createFolderAction); + performTreeAction(createFolderAction); commitEdit(); waitForProgram(program); @@ -181,7 +181,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { DockingActionIf createFragmentAction = getAction("Create Fragment"); String newFragName = tree.getNewFragmentName(); - performAction(createFragmentAction); + performTreeAction(createFragmentAction); commitEdit(); waitForProgram(program); @@ -206,7 +206,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { int childCount = root.getChildCount(); DockingActionIf createFolderAction = getAction("Create Folder"); - performAction(createFolderAction); + performTreeAction(createFolderAction); commitEdit(); waitForProgram(program); @@ -221,9 +221,9 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { String newName = tree.getNewFolderName(); DockingActionIf createFolderAction = getAction("Create Folder"); - performAction(createFolderAction, getActionContext(), true); - + performTreeAction(createFolderAction); waitForBusyTool(tool); + waitForTreeEdit(); String currentText = setEditorText("test1"); assertEquals(newName, currentText); @@ -241,7 +241,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { int childCount = root.getChildCount(); DockingActionIf createFragmentAction = getAction("Create Fragment"); - performAction(createFragmentAction, getActionContext(), true); + performTreeAction(createFragmentAction); commitEdit(); waitFor(() -> root.getChildCount() == childCount + 1); @@ -256,9 +256,9 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { String newName = tree.getNewFragmentName(); DockingActionIf createFragmentAction = getAction("Create Fragment"); - performAction(createFragmentAction, getActionContext(), true); - + performTreeAction(createFragmentAction); waitForBusyTool(tool); + waitForTreeEdit(); String currentText = setEditorText("test1"); assertEquals(newName, currentText); @@ -278,7 +278,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { setSelectionPath(node); DockingActionIf deleteAction = getAction("Delete"); - performAction(deleteAction, getActionContext(), true); + performTreeAction(deleteAction); waitForProgram(program); assertEquals(childCount - 1, root.getChildCount()); @@ -409,7 +409,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { DockingActionIf action = getAction(plugin, "Rename folder/fragment"); assertTrue(action.isEnabledForContext(getActionContext())); - performAction(action); + performTreeAction(action); String currentText = setEditorText("printf"); assertEquals("submodule", currentText); } @@ -429,7 +429,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { setSelectionPath(nodes[0]); DockingActionIf action = getAction(plugin, "Rename folder/fragment"); assertTrue(action.isEnabledForContext(getActionContext())); - performAction(action); + performTreeAction(action); String currentText = setEditorText(".data"); ProgramModule module = getModule(root, "Module-1"); @@ -451,7 +451,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { setSelectionPath(nodes[0]); DockingActionIf action = getAction(plugin, "Rename folder/fragment"); assertTrue(action.isEnabledForContext(getActionContext())); - performAction(action); + performTreeAction(action); String currentText = setEditorText("My Module-1"); assertEquals("My Module-1", currentText); waitForProgram(program); @@ -648,7 +648,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { DockingActionIf removeAction = getAction("Remove"); assertTrue(removeAction.isEnabledForContext(getActionContext())); - performAction(removeAction, getActionContext(), true); + performTreeAction(removeAction); AddressSet set = new AddressSet(); set.add(node.getFragment()); @@ -705,13 +705,13 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { } setSelectionPaths(node); - DockingActionIf replaceAction = getAction("Replace"); - performAction(replaceAction); + DockingActionIf replaceAction = getAction("Set View"); + performTreeAction(replaceAction); assertTrue(getView().hasSameAddresses(node.getModule().getAddressSet())); DockingActionIf removeAction = getAction("Remove"); - performAction(removeAction); + performTreeAction(removeAction); assertTrue(getView().isEmpty()); assertTrue(cbPlugin.getView().isEmpty()); @@ -750,7 +750,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { setSelectionPaths(node0, node2); DockingActionIf removeAction = getAction("Remove"); - performAction(removeAction); + performTreeAction(removeAction); AddressSet set = new AddressSet(); set.add(node0.getFragment()); @@ -799,7 +799,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { setSelectionPath(nodes[0]); DockingActionIf removeAction = getAction("Remove"); - performAction(removeAction); + performTreeAction(removeAction); // verify that all the descendants of the folder are removed from the view assertFalse(getView().contains(child1.getFragment())); @@ -819,8 +819,8 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { ProgramNode child = (ProgramNode) node.getChildAt(0); setSelectionPath(child); - DockingActionIf replaceAction = getAction("Replace"); - performAction(replaceAction); + DockingActionIf replaceAction = getAction("Set View"); + performTreeAction(replaceAction); assertTrue(getView().hasSameAddresses(child.getFragment())); assertTrue(getView().hasSameAddresses(cbPlugin.getView())); @@ -841,9 +841,9 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { ProgramNode node = (ProgramNode) root.getChildAt(6); setSelectionPath(node); - DockingActionIf replaceAction = getAction("Replace"); + DockingActionIf replaceAction = getAction("Set View"); assertTrue(replaceAction.isEnabledForContext(getActionContext())); - performAction(replaceAction, getActionContext(), true); + performTreeAction(replaceAction); assertTrue(plugin.getView().hasSameAddresses(node.getModule().getAddressSet())); assertPluginViewAppliedToTool(); @@ -869,8 +869,8 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { // descendant of that folder, and not in the view setSelectionPaths(dllsNode, sscanfNode); - DockingActionIf replaceAction = getAction("Replace"); - performAction(replaceAction); + DockingActionIf replaceAction = getAction("Set View"); + performTreeAction(replaceAction); AddressSet set = new AddressSet(); set.add(dllsNode.getModule().getAddressSet()); @@ -921,7 +921,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { setSelectionPath(dllsNode); DockingActionIf mergeAction = getAction("Merge"); - performAction(mergeAction); + performTreeAction(mergeAction); waitForProgram(program); int count = nodes[0].getChildCount(); @@ -954,7 +954,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { setSelectionPaths(dNodes[0], bNodes[0]); DockingActionIf mergeAction = getAction("Merge"); - performAction(mergeAction, getActionContext(), true); + performTreeAction(mergeAction); waitForProgram(program); assertEquals(7, root.getChildCount()); @@ -1104,7 +1104,7 @@ public class ProgramTreePlugin1Test extends AbstractProgramTreePluginTest { assertTrue(showAction.isEnabledForContext(getActionContext())); String[] treeNames = program.getListing().getTreeNames(); - performAction(showAction); + performTreeAction(showAction); JPopupMenu menu = plugin.getPopupMenu(); assertNotNull(menu); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin2Test.java index 5ac7ca0490..4574023c0d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin2Test.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,7 +24,6 @@ import javax.swing.tree.TreePath; import org.junit.*; -import docking.DefaultActionContext; import docking.action.DockingActionIf; import generic.theme.GIcon; import ghidra.program.database.ProgramBuilder; @@ -108,14 +107,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(node); assertTrue(copyAction.isEnabled()); - performAction(copyAction); + performTreeAction(copyAction); node = (ProgramNode) root.getChildAt(5); int origCount = node.getChildCount(); setSelectionPath(node); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); expandNode(node); @@ -142,7 +141,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode node = (ProgramNode) root.getChildAt(0); setSelectionPath(node); assertTrue(copyAction.isEnabled()); - performAction(copyAction); + performTreeAction(copyAction); setSelectionPath(root); @@ -161,7 +160,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(node); assertTrue(copyAction.isEnabled()); - performAction(copyAction, true); + performTreeAction(copyAction); ProgramNode[] nodes = findNodes("C"); setSelectionPath(nodes[0]); @@ -180,7 +179,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { // select USER32.DLL setSelectionPath(fnode); - performAction(copyAction); + performTreeAction(copyAction); // select DLLs node = (ProgramNode) root.getChildAt(6); setSelectionPath(node); @@ -200,10 +199,10 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(cpNode); - performAction(copyAction); + performTreeAction(copyAction); setSelectionPath(pasteNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); assertEquals(childCount + 1, pasteNode.getChildCount()); @@ -228,7 +227,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode cnode2 = (ProgramNode) node.getChildAt(1); setSelectionPaths(cnode1, cnode2); assertTrue(copyAction.isEnabled()); - performAction(copyAction); + performTreeAction(copyAction); // create a new module and paste fragments there tx(program, () -> { root.getModule().createModule("Test"); @@ -238,7 +237,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(destNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); visitNode(destNode); @@ -253,7 +252,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode cnode2 = (ProgramNode) node.getChildAt(1); setSelectionPaths(cnode1, cnode2); assertTrue(copyAction.isEnabled()); - performAction(copyAction); + performTreeAction(copyAction); // create a new module and paste fragments there tx(program, () -> { ProgramModule m = root.getModule().createModule("Test"); @@ -266,7 +265,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(destNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); expandNode(root); @@ -300,7 +299,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode cnode2 = (ProgramNode) node.getChildAt(1); setSelectionPaths(cnode1, cnode2); assertTrue(copyAction.isEnabled()); - performAction(copyAction); + performTreeAction(copyAction); // get node for DLLs ProgramNode destNode = (ProgramNode) root.getChildAt(5); @@ -308,7 +307,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(destNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); expandNode(root); @@ -341,12 +340,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { // set the view to Functions setSelectionPath(node); setViewPaths(node); - performAction(copyAction); + performTreeAction(copyAction); ProgramNode subrNode = (ProgramNode) root.getChildAt(8); setSelectionPath(subrNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); // verify the view is not affected @@ -375,10 +374,10 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(subrNode); - performAction(copyAction); + performTreeAction(copyAction); setSelectionPath(node); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); assertTrue(plugin.getView().hasSameAddresses(node.getModule().getAddressSet())); @@ -399,12 +398,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode child = (ProgramNode) node.getChildAt(0); setSelectionPath(child); - performAction(copyAction); + performTreeAction(copyAction); ProgramNode subrNode = (ProgramNode) root.getChildAt(8); setSelectionPath(subrNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); // verify the view is not affected @@ -426,14 +425,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(fnode); assertTrue(cutAction.isEnabled()); - performAction(cutAction); + performTreeAction(cutAction); ProgramNode funcNode = (ProgramNode) root.getChildAt(6);// Functions ProgramNode destNode = (ProgramNode) funcNode.getChildAt(0);// doStuff fragment // now select the doStuff fragment as the destination node setSelectionPath(destNode); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); assertTrue(destNode.getFragment().contains(fnodeSet)); @@ -479,14 +478,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(fnode); assertTrue(cutAction.isEnabled()); - performAction(cutAction); + performTreeAction(cutAction); ProgramNode funcNode = (ProgramNode) root.getChildAt(6);// Functions ProgramNode destNode = (ProgramNode) funcNode.getChildAt(0);// ghidra fragment // now select the ghidra fragment as the destination node setSelectionPath(destNode); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); assertTrue(destNode.getFragment().contains(fnodeSet)); @@ -523,14 +522,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(node); assertTrue(cutAction.isEnabled()); - performAction(cutAction); + performTreeAction(cutAction); node = (ProgramNode) root.getChildAt(5);//DLLs int origCount = node.getChildCount(); setSelectionPath(node); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); expandNode(node); @@ -558,10 +557,10 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(cNode); // cut Functions - performAction(cutAction); + performTreeAction(cutAction); setSelectionPath(destNode);// paste at DLLs assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); assertEquals(childCount + 1, destNode.getChildCount()); @@ -604,12 +603,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(stringsNode); // cut Strings - performAction(cutAction); + performTreeAction(cutAction); // paste at Functions ProgramNode funcNode = root.getChild("Functions"); setSelectionPath(funcNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); // Strings, L should be expanded @@ -636,13 +635,13 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(stringsNode); // cut Strings - performAction(cutAction); + performTreeAction(cutAction); // paste at Functions ProgramNode funcNode = root.getChild("Functions"); collapsePath(funcNode.getTreePath()); setSelectionPath(funcNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); // Functions should remain collapsed assertTrue(tree.isCollapsed(funcNode.getTreePath())); @@ -663,13 +662,13 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(stringsNode); // cut Strings - performAction(cutAction); + performTreeAction(cutAction); // paste at Functions ProgramNode funcNode = root.getChild("Functions"); expandPath(funcNode.getTreePath()); setSelectionPath(funcNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); stringsNode = funcNode.getChild("Strings"); assertTrue(tree.isCollapsed(stringsNode.getTreePath())); @@ -690,14 +689,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPath(stringsNode); // cut Strings - performAction(cutAction); + performTreeAction(cutAction); // paste at Functions ProgramNode funcNode = root.getChild("Functions"); visitNode(funcNode); collapsePath(funcNode.getTreePath()); setSelectionPath(funcNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); assertTrue(tree.isCollapsed(funcNode.getTreePath())); } @@ -721,14 +720,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setSelectionPaths(new TreePath[] { rsrcNode.getTreePath(), textNode.getTreePath() }); // cut Strings - runSwing(() -> cutAction.actionPerformed(new DefaultActionContext())); + performTreeAction(cutAction); // select Strings (has no fragments) ProgramNode stringsNode = root.getChild("Strings"); visitNode(stringsNode); setSelectionPath(stringsNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); assertEquals(3, stringsNode.getChildCount()); assertEquals("rsrc", stringsNode.getChildAt(1).toString()); @@ -788,7 +787,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { addSelectionPath(cutNodes[i].getTreePath()); } - performAction(cutAction); + performTreeAction(cutAction); // select a destination fragment ProgramNode stringsNode = root.getChild("Strings"); @@ -798,7 +797,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode fnode = cNode.getChild("testl"); setSelectionPath(fnode); - performAction(pasteAction); + performTreeAction(pasteAction); for (ProgramNode cutNode : cutNodes) { assertNull(listing.getFragment("Main Tree", cutNode.getName())); @@ -859,7 +858,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode testNode = root.getChild("Test"); setSelectionPath(testNode); - performAction(cutAction); + performTreeAction(cutAction); // paste at 010074d4 ProgramNode stringsNode = root.getChild("Strings"); @@ -869,7 +868,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode fnode = cNode.getChild("testl"); setSelectionPath(fnode); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); root = (ProgramNode) tree.getModel().getRoot(); @@ -937,12 +936,12 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { // set the view to DLLs setSelectionPath(node); setViewPaths(new TreePath[] { node.getTreePath() }); - performAction(copyAction); + performTreeAction(copyAction); ProgramNode everythingNode = root.getChild("Everything"); setSelectionPath(everythingNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); waitForProgram(program); // cut a fragment in the view and paste onto a collapsed folder @@ -953,7 +952,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { ProgramNode dataNode = root.getChild(".data"); setSelectionPath(dataNode); setViewPaths(new TreePath[] { dataNode.getTreePath() }); - performAction(cutAction); + performTreeAction(cutAction); // select DLLs in Everything ProgramNode evNode = root.getChild("Everything"); @@ -962,7 +961,7 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { visitNode(dllsNode); setSelectionPath(dllsNode); - performAction(pasteAction); + performTreeAction(pasteAction); // first occurrence of DLLs should have icon for descendant in view ProgramNode[] nodes = findNodes("DLLs"); @@ -991,13 +990,13 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { assertTrue(plugin.getView().hasSameAddresses(set)); setSelectionPath(debugNode); - performAction(cutAction); + performTreeAction(cutAction); // paste to an expanded folder not in the view ProgramNode subrNode = root.getChild("Subroutines"); expandNode(subrNode); setSelectionPath(subrNode); - performAction(pasteAction); + performTreeAction(pasteAction); assertTrue(plugin.getView().hasSameAddresses(set)); } @@ -1012,14 +1011,14 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { setViewPaths(dataNode, debugNode); setSelectionPath(debugNode); - performAction(cutAction); + performTreeAction(cutAction); // paste onto another fragment that is not in the view ProgramNode funcNode = root.getChild("Functions"); visitNode(funcNode); ProgramNode sscanfNode = funcNode.getChild("sscanf"); setSelectionPath(sscanfNode); - performAction(pasteAction); + performTreeAction(pasteAction); assertTrue(plugin.getView().hasSameAddresses(dataNode.getFragment())); } @@ -1042,11 +1041,11 @@ public class ProgramTreePlugin2Test extends AbstractProgramTreePluginTest { // cut first fragment in Subroutines setSelectionPath(node); - performAction(cutAction); + performTreeAction(cutAction); // paste at the debug node setSelectionPath(debugNode); - performAction(pasteAction); + performTreeAction(pasteAction); assertTrue(plugin.getView().contains(set)); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin3Test.java index f88e97078e..ab0385b63c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePlugin3Test.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -428,8 +428,7 @@ public class ProgramTreePlugin3Test extends AbstractProgramTreePluginTest { waitForSwing(); Component comp = getCellRendererComponentForLeaf(n, row); - assertEquals(new GIcon(DnDTreeCellRenderer.VIEWED_FRAGMENT), - ((JLabel) comp).getIcon()); + assertEquals(new GIcon(DnDTreeCellRenderer.VIEWED_FRAGMENT), ((JLabel) comp).getIcon()); } @Test @@ -1380,7 +1379,7 @@ public class ProgramTreePlugin3Test extends AbstractProgramTreePluginTest { AtomicReference ref = new AtomicReference<>(); runSwing(() -> { try { - tree.processDropRequest(node, list, TreeTransferable.localTreeNodeFlavor, + tree.processDropRequest(node, list, ProgramTreeTransferable.localTreeNodeFlavor, dropAction); } catch (NotFoundException | CircularDependencyException | DuplicateGroupException e) { @@ -1443,12 +1442,12 @@ public class ProgramTreePlugin3Test extends AbstractProgramTreePluginTest { setSelectionPath(node); setViewPaths(node); - performAction(copyAction); + performTreeAction(copyAction); ProgramNode everythingNode = root.getChild(dst); setSelectionPath(everythingNode); assertTrue(pasteAction.isEnabled()); - performAction(pasteAction); + performTreeAction(pasteAction); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePluginShowInViewTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePluginShowInViewTest.java index af616f17b0..9d67bcab9d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePluginShowInViewTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ProgramTreePluginShowInViewTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -277,7 +277,7 @@ public class ProgramTreePluginShowInViewTest extends AbstractGhidraHeadedIntegra ProgramNode textNode = root.getChild(".text"); setSelectionPath(textNode.getTreePath()); - DockingActionIf replaceAction = getAction(plugin, "Replace View"); + DockingActionIf replaceAction = getAction(plugin, "Set View"); ActionContext context = getActionContext(); performAction(replaceAction, context, true); @@ -312,7 +312,7 @@ public class ProgramTreePluginShowInViewTest extends AbstractGhidraHeadedIntegra ProgramNode rsrcNode = root.getChild(".rsrc"); addSelectionPath(rsrcNode.getTreePath()); - DockingActionIf replaceAction = getAction(plugin, "Replace View"); + DockingActionIf replaceAction = getAction(plugin, "Set View"); ActionContext context = getActionContext(); performAction(replaceAction, context, true); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java index 29a8f5ee64..48a3ad9bed 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/programtree/ViewManagerPluginTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -136,8 +136,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(treeNames.length, tabbedPane.getTabCount()); assertEquals(DEFAULT_TREE_NAME + "(1)", vps.getViewName()); assertEquals(tabbedPane.getSelectedComponent(), vps.getViewComponent()); - SwingUtilities - .invokeAndWait(() -> createTreeAction.actionPerformed(new DefaultActionContext())); + performAction(createTreeAction); program.flushEvents(); vps = provider.getCurrentViewProvider(); treeNames = program.getListing().getTreeNames(); @@ -152,8 +151,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { public void testUndoRedo() throws Exception { ProgramTreePlugin treePlugin = env.getPlugin(ProgramTreePlugin.class); final DockingActionIf createTreeAction = getAction(treePlugin, "Create Default Tree View"); - SwingUtilities - .invokeAndWait(() -> createTreeAction.actionPerformed(new DefaultActionContext())); + performAction(createTreeAction); program.flushEvents(); env.showTool(); ViewProviderService vps = provider.getCurrentViewProvider(); @@ -163,8 +161,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(treeNames.length, tabbedPane.getTabCount()); assertEquals(DEFAULT_TREE_NAME + "(1)", vps.getViewName()); assertEquals(tabbedPane.getSelectedComponent(), vps.getViewComponent()); - SwingUtilities - .invokeAndWait(() -> createTreeAction.actionPerformed(new DefaultActionContext())); + performAction(createTreeAction); program.flushEvents(); vps = provider.getCurrentViewProvider(); treeNames = program.getListing().getTreeNames(); @@ -237,7 +234,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { public void testCloseView() throws Exception { // close "Program Tree" final DockingActionIf closeAction = getAction(plugin, "Close Tree View"); - SwingUtilities.invokeAndWait(() -> closeAction.actionPerformed(new DefaultActionContext())); + performAction(closeAction); waitForBusyTool(tool); @@ -261,8 +258,7 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { setCurrentViewProvider("Tree Two"); final DockingActionIf deleteAction = getAction(plugin, "Delete Tree View"); - SwingUtilities - .invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext())); + performAction(deleteAction); waitForBusyTool(tool); @@ -300,31 +296,26 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { setCurrentViewProvider("Main Tree"); - SwingUtilities - .invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext())); + performAction(deleteAction); waitForBusyTool(tool); setCurrentViewProvider("Tree One"); - SwingUtilities - .invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext())); + performAction(deleteAction); waitForBusyTool(tool); setCurrentViewProvider("Tree Two"); - SwingUtilities - .invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext())); + performAction(deleteAction); waitForBusyTool(tool); setCurrentViewProvider("Tree Three"); - SwingUtilities - .invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext())); + performAction(deleteAction); waitForBusyTool(tool); // attempt to delete the last view - SwingUtilities - .invokeAndWait(() -> deleteAction.actionPerformed(new DefaultActionContext())); + performAction(deleteAction); waitForBusyTool(tool); ViewProviderService vps = provider.getCurrentViewProvider(); @@ -341,10 +332,10 @@ public class ViewManagerPluginTest extends AbstractGhidraHeadedIntegrationTest { final DockingActionIf closeAction = getAction(plugin, "Close Tree View"); setCurrentViewProvider(DEFAULT_TREE_NAME); - SwingUtilities.invokeAndWait(() -> closeAction.actionPerformed(new DefaultActionContext())); + performAction(closeAction); setCurrentViewProvider("Main Tree"); - SwingUtilities.invokeAndWait(() -> { + runSwing(() -> { closeAction.actionPerformed(new DefaultActionContext()); provider.setCurrentViewProvider("Tree One"); closeAction.actionPerformed(new DefaultActionContext());