mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Added some convenience methods to GTree nodes.
This commit is contained in:
parent
3ce8d3fa39
commit
7cfa80f8ac
5 changed files with 143 additions and 7 deletions
|
@ -22,7 +22,9 @@ import java.util.List;
|
|||
* Also, children of this node can be unloaded by calling {@link #unloadChildren()}. This
|
||||
* can be used by nodes in large trees to save memory by unloading children that are no longer
|
||||
* in the current tree view (collapsed). Of course, that decision would need to be balanced
|
||||
* against the extra time to reload the nodes in the event that a filter is applied.
|
||||
* against the extra time to reload the nodes in the event that a filter is applied. Also, if
|
||||
* some external event occurs that changes the set of children for a GTreeLazyNode, you can call
|
||||
* {@link #reload()} to refresh the node's children.
|
||||
*/
|
||||
public abstract class GTreeLazyNode extends GTreeNode {
|
||||
|
||||
|
@ -36,6 +38,9 @@ public abstract class GTreeLazyNode extends GTreeNode {
|
|||
/**
|
||||
* Sets this lazy node back to the "unloaded" state such that if
|
||||
* its children are accessed, it will reload its children as needed.
|
||||
* NOTE: This method does not trigger a call to {@link #fireNodeChanged(GTreeNode, GTreeNode)}
|
||||
* because doing will often trigger a call from the JTree will will immediately cause the node
|
||||
* to reload its children. If that is the effect you want, call {@link #reload()}.
|
||||
*/
|
||||
public void unloadChildren() {
|
||||
if (isLoaded()) {
|
||||
|
@ -43,6 +48,19 @@ public abstract class GTreeLazyNode extends GTreeNode {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells this node that its children are stale and that it needs to regenerate them. This will
|
||||
* unload any existing children and call {@link #fireNodeStructureChanged(GTreeNode)} which will
|
||||
* inform the JTree that this node has changed and when the JTree queries this node for its children,
|
||||
* the {@link #generateChildren()} will get called to populate the node.
|
||||
*/
|
||||
public void reload() {
|
||||
if (isLoaded()) {
|
||||
unloadChildren();
|
||||
fireNodeStructureChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(GTreeNode node) {
|
||||
if (isLoaded()) {
|
||||
|
@ -66,9 +84,7 @@ public abstract class GTreeLazyNode extends GTreeNode {
|
|||
|
||||
@Override
|
||||
public void removeAll() {
|
||||
if (isLoaded()) {
|
||||
unloadChildren();
|
||||
}
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -441,6 +441,42 @@ public abstract class GTreeNode extends CoreGTreeNode implements Comparable<GTre
|
|||
Swing.runNow(() -> doFireNodeChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for expanding (opening) this node in the tree. If this node is not
|
||||
* currently attached to a visible tree, then this call does nothing
|
||||
*/
|
||||
public void expand() {
|
||||
GTree tree = getTree();
|
||||
if (tree != null) {
|
||||
tree.expandPath(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for collapsing (closing) this node in the tree. If this node is not
|
||||
* currently attached to a visible tree, then this call does nothing
|
||||
*/
|
||||
public void collapse() {
|
||||
GTree tree = getTree();
|
||||
if (tree != null) {
|
||||
tree.collapseAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method determining if this node is expanded in a tree. If the node is not
|
||||
* currently attached to a visible tree, then this call returns false
|
||||
*
|
||||
* @return true if the node is expanded in a currently visible tree.
|
||||
*/
|
||||
public boolean isExpanded() {
|
||||
GTree tree = getTree();
|
||||
if (tree != null) {
|
||||
return tree.isExpanded(this.getTreePath());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private GTreeNode[] getPathToRoot(GTreeNode node, int depth) {
|
||||
GTreeNode[] returnNodes;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ public class GTreeRestoreTreeStateTask extends GTreeTask {
|
|||
monitor.setMessage("Restoring tree selection state");
|
||||
selectPathsInThisTask(state, monitor, true);
|
||||
|
||||
// this allows some tress to perform cleanup
|
||||
// this allows some trees to perform cleanup
|
||||
tree.expandedStateRestored(monitor);
|
||||
tree.clearFilterRestoreState();
|
||||
}
|
||||
|
|
|
@ -187,6 +187,25 @@ public class GTreeTest extends AbstractDockingTest {
|
|||
assertNull("Found a node in the tree that should have been filtered out", node2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandCollapseNode() {
|
||||
GTreeNode node = findNodeInTree(NonLeafWithOneLevelOfChildrenNodeB.class.getSimpleName());
|
||||
assertTrue(!node.isExpanded());
|
||||
|
||||
node.expand();
|
||||
waitForTree();
|
||||
assertTrue(node.isExpanded());
|
||||
|
||||
node.collapse();
|
||||
waitForTree();
|
||||
assertTrue(!node.isExpanded());
|
||||
|
||||
GTreeNode root = node.getRoot();
|
||||
root.collapse();
|
||||
assertTrue(!root.isExpanded());
|
||||
assertTrue(gTree.getExpandedPaths().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeFilterSettingsWithFilterTextInPlace() {
|
||||
|
||||
|
|
|
@ -25,10 +25,23 @@ import javax.swing.tree.TreePath;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.test.AbstractDockingTest;
|
||||
import docking.widgets.tree.support.GTreeFilter;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Note: This test does not extend {@link AbstractDockingTest}. Extending that class sets up
|
||||
* {@link Swing#runNow(Runnable)} methods so that they actually run on the swing thread; otherwise
|
||||
* they run on the calling thread. Normally GTreeNode test would need that because the fire event
|
||||
* calls normally check that the events are being sent on the swing thread. In this file, all the
|
||||
* tests use {@link TestNode} or {@link LazyTestNode} which override the event sending methods to
|
||||
* instead put the events in a list so that the test can check that the correct events were generated.
|
||||
* Since the methods that check for being on the swing thread are overridden, we can get away with
|
||||
* not extending {@link AbstractDockingTest} and this allows the test to run about 100 times faster.
|
||||
*/
|
||||
|
||||
public class GTreeNodeTest {
|
||||
private List<TestEvent> events = new ArrayList<>();
|
||||
private GTreeNode root;
|
||||
|
@ -385,11 +398,35 @@ public class GTreeNodeTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testLoadAllOnLazyTre() throws CancelledException {
|
||||
GTreeNode node = new LazyGTestNode("test", 2);
|
||||
public void testLoadAllOnLazyTree() throws CancelledException {
|
||||
GTreeNode node = new LazyTestNode("test", 2);
|
||||
assertEquals(13, node.loadAll(TaskMonitor.DUMMY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnloadOnLazyNode() throws CancelledException {
|
||||
GTreeLazyNode node = new LazyTestNode("test", 2);
|
||||
node.loadAll(TaskMonitor.DUMMY);
|
||||
assertTrue(node.isLoaded());
|
||||
|
||||
events.clear();
|
||||
node.unloadChildren();
|
||||
assertTrue(!node.isLoaded());
|
||||
assertTrue(events.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReloadOnLazyNode() throws CancelledException {
|
||||
GTreeLazyNode node = new LazyTestNode("test", 2);
|
||||
node.loadAll(TaskMonitor.DUMMY);
|
||||
assertTrue(node.isLoaded());
|
||||
|
||||
events.clear();
|
||||
node.reload();
|
||||
assertTrue(!node.isLoaded());
|
||||
assertTrue(!events.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamDepthFirst() {
|
||||
List<GTreeNode> collect = root.stream(true).collect(Collectors.toList());
|
||||
|
@ -454,6 +491,33 @@ public class GTreeNodeTest {
|
|||
|
||||
}
|
||||
|
||||
private class LazyTestNode extends LazyGTestNode {
|
||||
|
||||
LazyTestNode(String name, int depth) {
|
||||
super(name, depth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFireNodeStructureChanged() {
|
||||
events.add(new TestEvent(EventType.STRUCTURE_CHANGED, null, this, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFireNodeChanged() {
|
||||
events.add(new TestEvent(EventType.NODE_CHANGED, getParent(), this, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFireNodeAdded(GTreeNode newNode) {
|
||||
events.add(new TestEvent(EventType.NODE_ADDED, this, newNode, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFireNodeRemoved(GTreeNode removedNode, int index) {
|
||||
events.add(new TestEvent(EventType.NODE_REMOVED, this, removedNode, -1));
|
||||
}
|
||||
}
|
||||
|
||||
private class TestNode extends GTestNode {
|
||||
TestNode(String name) {
|
||||
super(name);
|
||||
|
@ -480,6 +544,7 @@ public class GTreeNodeTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
enum EventType {
|
||||
STRUCTURE_CHANGED, NODE_CHANGED, NODE_ADDED, NODE_REMOVED
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue