diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java index 5a431906c6..e57ea2fafd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java @@ -1171,7 +1171,10 @@ class FSBActionManager { public void actionPerformed(ActionContext context) { FSRL containerFSRL = FSBUtils.getFileFSRLFromContext(context); if (containerFSRL != null && context.getContextObject() instanceof FSBFileNode) { - FSBFileNode fileNode = (FSBFileNode) context.getContextObject(); + FSBFileNode xfileNode = (FSBFileNode) context.getContextObject(); + FSBFileNode modelFileNode = + (FSBFileNode) gTree.getModelNodeForPath(xfileNode.getTreePath()); + gTree.runTask(monitor -> { try { FileSystemRef fsRef = @@ -1185,17 +1188,12 @@ class FSBActionManager { return; } - FSBRootNode nestedRootNode = new FSBRootNode(fsRef, fileNode); - FSBRootNode containingFSBRootNode = - FSBNode.findContainingFileSystemFSBRootNode(fileNode); - if (containingFSBRootNode != null) { - containingFSBRootNode.getSubRootNodes().add(nestedRootNode); - } + FSBRootNode nestedRootNode = new FSBRootNode(fsRef, modelFileNode); nestedRootNode.setChildren(nestedRootNode.generateChildren(monitor)); - int indexInParent = fileNode.getIndexInParent(); - GTreeNode parent = fileNode.getParent(); - parent.removeNode(fileNode); + int indexInParent = modelFileNode.getIndexInParent(); + GTreeNode parent = modelFileNode.getParent(); + parent.removeNode(modelFileNode); parent.addNode(indexInParent, nestedRootNode); gTree.expandPath(nestedRootNode); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java index b9ec427dd1..2590e571f1 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java @@ -260,6 +260,11 @@ abstract class CoreGTreeNode implements Cloneable { } } + /** + * This is used to dispose filtered "clone" nodes. When a filter is applied to the tree, + * the nodes that matched are "shallow" cloned, so when the filter is removed, we don't + * want to do a full dispose on the nodes, just clean up the parent-child references. + */ final void disposeClones() { List oldChildren; synchronized (this) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java index 7fbaeb61fc..303d4fe19a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java @@ -243,12 +243,17 @@ public class GTree extends JPanel implements BusyListener { public void dispose() { filterUpdateManager.dispose(); worker.dispose(); - GTreeNode root = model.getModelRoot(); - if (root != null) { - root.dispose(); + + if (realModelRootNode != null) { + realModelRootNode.dispose(); + } + // if there is a filter applied, clean up the filtered nodes. Note that filtered nodes + // are expected to be shallow clones of the model nodes, so we don't want to call full + // dispose on the filtered nodes because internal clean-up should happen when the + // model nodes are disposed. The disposeClones just breaks the child-parent ties. + if (realViewRootNode != null && realViewRootNode != realModelRootNode) { + realViewRootNode.disposeClones(); } - realModelRootNode.dispose(); - realViewRootNode.dispose(); model.dispose(); }