mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-5258 - Fixed a Symbol Tree bug that caused an empty tree when showing the tree with a filter applied
This commit is contained in:
parent
d87b514baa
commit
9ae412a0b3
5 changed files with 60 additions and 39 deletions
|
@ -150,10 +150,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
private SymbolGTree createTree(SymbolTreeRootNode rootNode) {
|
private SymbolGTree createTree(SymbolTreeRootNode rootNode) {
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
GTreeNode oldRootNode = tree.getModelRoot();
|
|
||||||
tree.setRootNode(rootNode);
|
tree.setRootNode(rootNode);
|
||||||
|
|
||||||
oldRootNode.removeAll();// assist in cleanup a bit
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,10 @@ abstract class CoreGTreeNode implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
disconnect(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect(boolean dispose) {
|
||||||
List<GTreeNode> oldChildren;
|
List<GTreeNode> oldChildren;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
oldChildren = children;
|
oldChildren = children;
|
||||||
|
@ -262,7 +266,10 @@ abstract class CoreGTreeNode implements Cloneable {
|
||||||
|
|
||||||
if (oldChildren != null) {
|
if (oldChildren != null) {
|
||||||
for (GTreeNode node : oldChildren) {
|
for (GTreeNode node : oldChildren) {
|
||||||
node.dispose();
|
node.disconnect(dispose);
|
||||||
|
if (dispose) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
oldChildren.clear();
|
oldChildren.clear();
|
||||||
}
|
}
|
||||||
|
@ -270,23 +277,13 @@ abstract class CoreGTreeNode implements Cloneable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to dispose filtered "clone" nodes. When a filter is applied to the tree,
|
* 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
|
* the nodes that matched are "shallow" cloned. This is effectively a shallow dispose that will
|
||||||
* want to do a full dispose on the nodes, just clean up the parent-child references.
|
* clean up any children and disconnect this node from the true, but will *not* call dispose(),
|
||||||
|
* which would affect the original node from which this node was cloned.
|
||||||
*/
|
*/
|
||||||
final void disposeClones() {
|
final void disposeClone() {
|
||||||
List<GTreeNode> oldChildren;
|
// Do not dispose, as that will affect the clone's source too
|
||||||
synchronized (this) {
|
disconnect(false);
|
||||||
oldChildren = children;
|
|
||||||
children = null;
|
|
||||||
parent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldChildren != null) {
|
|
||||||
for (GTreeNode node : oldChildren) {
|
|
||||||
node.disposeClones();
|
|
||||||
}
|
|
||||||
oldChildren.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -285,12 +285,12 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
realModelRootNode.dispose();
|
realModelRootNode.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is a filter applied, clean up the filtered nodes. Note that filtered nodes
|
// 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
|
// 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
|
// 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.
|
// model nodes are disposed. The disposeClone() just breaks the child-parent ties.
|
||||||
if (realViewRootNode != null && realViewRootNode != realModelRootNode) {
|
if (realViewRootNode != null && realViewRootNode != realModelRootNode) {
|
||||||
realViewRootNode.disposeClones();
|
realViewRootNode.disposeClone();
|
||||||
}
|
}
|
||||||
|
|
||||||
filterProvider.dispose();
|
filterProvider.dispose();
|
||||||
|
@ -847,23 +847,40 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
Swing.runIfSwingOrRunLater(() -> {
|
Swing.runIfSwingOrRunLater(() -> {
|
||||||
worker.clearAllJobs();
|
worker.clearAllJobs();
|
||||||
rootNode.setParent(rootParent);
|
rootNode.setParent(rootParent);
|
||||||
|
GTreeNode oldModelRoot = realModelRootNode;
|
||||||
|
GTreeNode oldViewRoot = realViewRootNode;
|
||||||
realModelRootNode = rootNode;
|
realModelRootNode = rootNode;
|
||||||
realViewRootNode = rootNode;
|
realViewRootNode = rootNode;
|
||||||
GTreeNode oldRoot;
|
swingSetModelRootNode(rootNode);
|
||||||
oldRoot = swingSetModelRootNode(rootNode);
|
|
||||||
oldRoot.dispose();
|
disposeOldRoots(oldModelRoot, oldViewRoot);
|
||||||
|
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
filterUpdateManager.update();
|
filterUpdateManager.update();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void disposeOldRoots(GTreeNode oldModelRoot, GTreeNode oldViewRoot) {
|
||||||
|
if (oldModelRoot != null && oldModelRoot != realModelRootNode) {
|
||||||
|
oldModelRoot.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 disposeClone() just breaks the child-parent ties.
|
||||||
|
if (oldViewRoot != null && oldViewRoot != realViewRootNode) {
|
||||||
|
oldViewRoot.disposeClone(); // safe to call even if we disposed the same node above
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void swingSetFilteredRootNode(GTreeNode filteredRootNode) {
|
void swingSetFilteredRootNode(GTreeNode filteredRootNode) {
|
||||||
filteredRootNode.setParent(rootParent);
|
filteredRootNode.setParent(rootParent);
|
||||||
realViewRootNode = filteredRootNode;
|
realViewRootNode = filteredRootNode;
|
||||||
GTreeNode currentRoot = swingSetModelRootNode(filteredRootNode);
|
GTreeNode currentRoot = swingSetModelRootNode(filteredRootNode);
|
||||||
if (currentRoot != realModelRootNode) {
|
if (currentRoot != realModelRootNode) {
|
||||||
currentRoot.disposeClones();
|
currentRoot.disposeClone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -871,7 +888,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
realViewRootNode = realModelRootNode;
|
realViewRootNode = realModelRootNode;
|
||||||
GTreeNode currentRoot = swingSetModelRootNode(realModelRootNode);
|
GTreeNode currentRoot = swingSetModelRootNode(realModelRootNode);
|
||||||
if (currentRoot != realModelRootNode && currentRoot != null) {
|
if (currentRoot != realModelRootNode && currentRoot != null) {
|
||||||
currentRoot.disposeClones();
|
currentRoot.disposeClone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -53,4 +53,9 @@ class GTreeRootParentNode extends GTreeNode {
|
||||||
public boolean isLeaf() {
|
public boolean isLeaf() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "GTree Root Parent Node";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -125,20 +125,25 @@ public class GTreeModel implements TreeModel {
|
||||||
"GTreeModel.fireNodeStructuredChanged() must be " + "called from the AWT thread");
|
"GTreeModel.fireNodeStructuredChanged() must be " + "called from the AWT thread");
|
||||||
|
|
||||||
// If the tree is filtered and this is called on the original node, we have to
|
// If the tree is filtered and this is called on the original node, we have to
|
||||||
// translate the node to a view node (one the jtree knows).
|
// translate the node to a view node (one the tree knows).
|
||||||
GTreeNode viewNode = convertToViewNode(changedNode);
|
GTreeNode viewNode = convertToViewNode(changedNode);
|
||||||
if (viewNode == null) {
|
if (viewNode == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewNode != changedNode) {
|
if (viewNode != changedNode) {
|
||||||
// This means we are filtered and since the original node's children are invalid,
|
// The only time this can happen is when the tree is filtered. In this case, the
|
||||||
// then the filtered children are invalid also. So clear out the children by
|
// view node will be a clone of the changed node. We need to update the cloned
|
||||||
// setting an empty list as we don't want to trigger the node to regenerate its
|
// node to signal that the children have changed. If we set the children to null,
|
||||||
// children which happens if you set the children to null.
|
// then they will get reloaded when we fire the event. Instead, if we set the
|
||||||
|
// children to the empty list, the node will simply think there are no children and
|
||||||
|
// it will not get reloaded.
|
||||||
//
|
//
|
||||||
// This won't cause a second event to the jtree because we are protected
|
// After the events have been fired, there will eventually be a refilter operation
|
||||||
// by the isFiringNodeStructureChanged variable
|
// to update the view node with the correct children for the active filter.
|
||||||
|
//
|
||||||
|
// Note: this won't cause a second event to the tree because we are protected by
|
||||||
|
// the isFiringNodeStructureChanged flag at the top of this method.
|
||||||
viewNode.setChildren(Collections.emptyList());
|
viewNode.setChildren(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue