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
|
* 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
|
* 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
|
* 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 {
|
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
|
* Sets this lazy node back to the "unloaded" state such that if
|
||||||
* its children are accessed, it will reload its children as needed.
|
* 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() {
|
public void unloadChildren() {
|
||||||
if (isLoaded()) {
|
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
|
@Override
|
||||||
public void addNode(GTreeNode node) {
|
public void addNode(GTreeNode node) {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
|
@ -66,9 +84,7 @@ public abstract class GTreeLazyNode extends GTreeNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAll() {
|
public void removeAll() {
|
||||||
if (isLoaded()) {
|
reload();
|
||||||
unloadChildren();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -441,6 +441,42 @@ public abstract class GTreeNode extends CoreGTreeNode implements Comparable<GTre
|
||||||
Swing.runNow(() -> doFireNodeChanged());
|
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) {
|
private GTreeNode[] getPathToRoot(GTreeNode node, int depth) {
|
||||||
GTreeNode[] returnNodes;
|
GTreeNode[] returnNodes;
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class GTreeRestoreTreeStateTask extends GTreeTask {
|
||||||
monitor.setMessage("Restoring tree selection state");
|
monitor.setMessage("Restoring tree selection state");
|
||||||
selectPathsInThisTask(state, monitor, true);
|
selectPathsInThisTask(state, monitor, true);
|
||||||
|
|
||||||
// this allows some tress to perform cleanup
|
// this allows some trees to perform cleanup
|
||||||
tree.expandedStateRestored(monitor);
|
tree.expandedStateRestored(monitor);
|
||||||
tree.clearFilterRestoreState();
|
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);
|
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
|
@Test
|
||||||
public void testChangeFilterSettingsWithFilterTextInPlace() {
|
public void testChangeFilterSettingsWithFilterTextInPlace() {
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,23 @@ import javax.swing.tree.TreePath;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.test.AbstractDockingTest;
|
||||||
import docking.widgets.tree.support.GTreeFilter;
|
import docking.widgets.tree.support.GTreeFilter;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
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 {
|
public class GTreeNodeTest {
|
||||||
private List<TestEvent> events = new ArrayList<>();
|
private List<TestEvent> events = new ArrayList<>();
|
||||||
private GTreeNode root;
|
private GTreeNode root;
|
||||||
|
@ -385,11 +398,35 @@ public class GTreeNodeTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoadAllOnLazyTre() throws CancelledException {
|
public void testLoadAllOnLazyTree() throws CancelledException {
|
||||||
GTreeNode node = new LazyGTestNode("test", 2);
|
GTreeNode node = new LazyTestNode("test", 2);
|
||||||
assertEquals(13, node.loadAll(TaskMonitor.DUMMY));
|
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
|
@Test
|
||||||
public void testStreamDepthFirst() {
|
public void testStreamDepthFirst() {
|
||||||
List<GTreeNode> collect = root.stream(true).collect(Collectors.toList());
|
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 {
|
private class TestNode extends GTestNode {
|
||||||
TestNode(String name) {
|
TestNode(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
|
@ -480,6 +544,7 @@ public class GTreeNodeTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum EventType {
|
enum EventType {
|
||||||
STRUCTURE_CHANGED, NODE_CHANGED, NODE_ADDED, NODE_REMOVED
|
STRUCTURE_CHANGED, NODE_CHANGED, NODE_ADDED, NODE_REMOVED
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue