mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
Changes to the GTree code to improve performance. Completely changed
GTreeNode base implementations and filtering code.
This commit is contained in:
parent
04f7366a62
commit
5c6b32714c
137 changed files with 2834 additions and 3081 deletions
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,6 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.calltree;
|
package ghidra.app.plugin.core.calltree;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import docking.widgets.tree.GTreeSlowLoadingNode;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -25,15 +31,6 @@ import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import javax.swing.tree.TreePath;
|
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
|
||||||
import docking.widgets.tree.GTreeSlowLoadingNode;
|
|
||||||
import docking.widgets.tree.support.GTreeFilter;
|
|
||||||
|
|
||||||
public abstract class CallNode extends GTreeSlowLoadingNode {
|
public abstract class CallNode extends GTreeSlowLoadingNode {
|
||||||
|
|
||||||
private boolean allowDuplicates;
|
private boolean allowDuplicates;
|
||||||
|
@ -124,13 +121,11 @@ public abstract class CallNode extends GTreeSlowLoadingNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void filter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
|
public int loadAll(TaskMonitor monitor) throws CancelledException {
|
||||||
throws CancelledException {
|
|
||||||
if (depth() > filterDepth.get()) {
|
if (depth() > filterDepth.get()) {
|
||||||
doSetActiveChildren(new ArrayList<GTreeNode>());
|
return 1;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
super.filter(filter, monitor, min, max);
|
return super.loadAll(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int depth() {
|
private int depth() {
|
||||||
|
|
|
@ -182,7 +182,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
Object contextObject = context.getContextObject();
|
Object contextObject = context.getContextObject();
|
||||||
GTree gTree = (GTree) contextObject;
|
GTree gTree = (GTree) contextObject;
|
||||||
GTreeRootNode rootNode = gTree.getRootNode();
|
GTreeNode rootNode = gTree.getViewRoot();
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode child : children) {
|
for (GTreeNode child : children) {
|
||||||
gTree.collapseAll(child);
|
gTree.collapseAll(child);
|
||||||
|
@ -283,7 +283,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
|
|
||||||
for (TreePath path : selectionPaths) {
|
for (TreePath path : selectionPaths) {
|
||||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
if (node instanceof GTreeRootNode) {
|
if (node instanceof GTreeNode) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
|
|
||||||
for (TreePath path : selectionPaths) {
|
for (TreePath path : selectionPaths) {
|
||||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
if (node instanceof GTreeRootNode) {
|
if (node.isRoot()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,7 +496,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
|
|
||||||
for (TreePath path : selectionPaths) {
|
for (TreePath path : selectionPaths) {
|
||||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
if (node instanceof GTreeRootNode) {
|
if (node.isRoot()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,7 +551,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
|
|
||||||
for (TreePath path : selectionPaths) {
|
for (TreePath path : selectionPaths) {
|
||||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
if (node instanceof GTreeRootNode) {
|
if (node.isRoot()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -660,7 +660,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
|
|
||||||
for (TreePath path : selectionPaths) {
|
for (TreePath path : selectionPaths) {
|
||||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
if (node instanceof GTreeRootNode) {
|
if (node.isRoot()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -915,7 +915,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearTrees() {
|
private void clearTrees() {
|
||||||
if (incomingTree.getRootNode() instanceof EmptyRootNode) {
|
if (incomingTree.getModelRoot() instanceof EmptyRootNode) {
|
||||||
// already empty
|
// already empty
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -937,7 +937,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateIncomingReferencs(Function function) {
|
private void updateIncomingReferencs(Function function) {
|
||||||
GTreeRootNode rootNode = null;
|
GTreeNode rootNode = null;
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
rootNode = new EmptyRootNode();
|
rootNode = new EmptyRootNode();
|
||||||
}
|
}
|
||||||
|
@ -949,7 +949,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOutgoingReferences(Function function) {
|
private void updateOutgoingReferences(Function function) {
|
||||||
GTreeRootNode rootNode = null;
|
GTreeNode rootNode = null;
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
rootNode = new EmptyRootNode();
|
rootNode = new EmptyRootNode();
|
||||||
}
|
}
|
||||||
|
@ -1083,12 +1083,12 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEmpty() {
|
private boolean isEmpty() {
|
||||||
GTreeRootNode rootNode = incomingTree.getRootNode();
|
GTreeNode rootNode = incomingTree.getModelRoot();
|
||||||
return rootNode instanceof EmptyRootNode;
|
return rootNode instanceof EmptyRootNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateRootNodes(Function function) {
|
private boolean updateRootNodes(Function function) {
|
||||||
CallNode callNode = (CallNode) incomingTree.getRootNode();
|
CallNode callNode = (CallNode) incomingTree.getModelRoot();
|
||||||
Function nodeFunction = callNode.getContainingFunction();
|
Function nodeFunction = callNode.getContainingFunction();
|
||||||
if (nodeFunction.equals(function)) {
|
if (nodeFunction.equals(function)) {
|
||||||
reloadUpdateManager.update();
|
reloadUpdateManager.update();
|
||||||
|
@ -1109,7 +1109,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
CallNode rootNode = (CallNode) tree.getRootNode();
|
CallNode rootNode = (CallNode) tree.getModelRoot();
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode node : children) {
|
for (GTreeNode node : children) {
|
||||||
updateFunction((CallNode) node);
|
updateFunction((CallNode) node);
|
||||||
|
@ -1117,7 +1117,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFunction(CallNode node) {
|
private void updateFunction(CallNode node) {
|
||||||
if (!node.isChildrenLoadedOrInProgress()) {
|
if (!node.isLoaded()) {
|
||||||
// children not loaded, don't force a load by asking for them
|
// children not loaded, don't force a load by asking for them
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1189,6 +1189,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
this.recurseIcon.setNumber(depth);
|
this.recurseIcon.setNumber(depth);
|
||||||
|
|
||||||
removeFilterCache();
|
removeFilterCache();
|
||||||
|
incomingTree.refilterLater();
|
||||||
|
outgoingTree.refilterLater();
|
||||||
|
|
||||||
saveRecurseDepth();
|
saveRecurseDepth();
|
||||||
}
|
}
|
||||||
|
@ -1203,9 +1205,9 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
// you have done. Normally this is not that big of a problem. However, if the loading
|
// you have done. Normally this is not that big of a problem. However, if the loading
|
||||||
// takes a long time, then you lose some work.
|
// takes a long time, then you lose some work.
|
||||||
//
|
//
|
||||||
GTreeRootNode rootNode = incomingTree.getRootNode();
|
GTreeNode rootNode = incomingTree.getModelRoot();
|
||||||
rootNode.removeAll();
|
rootNode.removeAll();
|
||||||
rootNode = outgoingTree.getRootNode();
|
rootNode = outgoingTree.getModelRoot();
|
||||||
rootNode.removeAll();
|
rootNode.removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1364,7 +1366,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PendingRootNode extends AbstractGTreeRootNode {
|
private class PendingRootNode extends GTreeNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon(boolean expanded) {
|
public Icon getIcon(boolean expanded) {
|
||||||
|
@ -1387,7 +1389,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EmptyRootNode extends AbstractGTreeRootNode {
|
private class EmptyRootNode extends GTreeNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon(boolean expanded) {
|
public Icon getIcon(boolean expanded) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,20 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.calltree;
|
package ghidra.app.plugin.core.calltree;
|
||||||
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.listing.Function;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.GTree;
|
import ghidra.program.model.address.Address;
|
||||||
import docking.widgets.tree.GTreeRootNode;
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
public class IncomingCallsRootNode extends IncomingCallNode implements GTreeRootNode {
|
public class IncomingCallsRootNode extends IncomingCallNode {
|
||||||
|
|
||||||
private GTree tree;
|
|
||||||
|
|
||||||
IncomingCallsRootNode(Program program, Function function, Address sourceAddress,
|
IncomingCallsRootNode(Program program, Function function, Address sourceAddress,
|
||||||
boolean filterDuplicates, AtomicInteger filterDepth) {
|
boolean filterDuplicates, AtomicInteger filterDepth) {
|
||||||
|
@ -52,14 +46,4 @@ public class IncomingCallsRootNode extends IncomingCallNode implements GTreeRoot
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Incoming References - " + name;
|
return "Incoming References - " + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTree getGTree() {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGTree(GTree tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,20 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.calltree;
|
package ghidra.app.plugin.core.calltree;
|
||||||
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.listing.Function;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.GTree;
|
import ghidra.program.model.address.Address;
|
||||||
import docking.widgets.tree.GTreeRootNode;
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
public class OutgoingCallsRootNode extends OutgoingCallNode implements GTreeRootNode {
|
public class OutgoingCallsRootNode extends OutgoingCallNode {
|
||||||
|
|
||||||
private GTree tree;
|
|
||||||
|
|
||||||
OutgoingCallsRootNode(Program program, Function function, Address sourceAddress,
|
OutgoingCallsRootNode(Program program, Function function, Address sourceAddress,
|
||||||
boolean filterDuplicates, AtomicInteger filterDepth) {
|
boolean filterDuplicates, AtomicInteger filterDepth) {
|
||||||
|
@ -62,14 +56,4 @@ public class OutgoingCallsRootNode extends OutgoingCallNode implements GTreeRoot
|
||||||
public String getToolTip() {
|
public String getToolTip() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTree getGTree() {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGTree(GTree tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,7 +454,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
previewScrollPane = new JScrollPane(previewPane);
|
previewScrollPane = new JScrollPane(previewPane);
|
||||||
|
|
||||||
DockingWindowManager.getHelpService().registerHelp(previewScrollPane,
|
DockingWindowManager.getHelpService()
|
||||||
|
.registerHelp(previewScrollPane,
|
||||||
new HelpLocation("DataTypeManagerPlugin", "Preview_Window"));
|
new HelpLocation("DataTypeManagerPlugin", "Preview_Window"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,8 +702,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArchiveNode getProgramArchiveNode() {
|
private ArchiveNode getProgramArchiveNode() {
|
||||||
GTreeNode rootNode = getGTree().getRootNode();
|
GTreeNode rootNode = getGTree().getModelRoot();
|
||||||
List<GTreeNode> children = rootNode.getAllChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode node : children) {
|
for (GTreeNode node : children) {
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
Archive archive = archiveNode.getArchive();
|
Archive archive = archiveNode.getArchive();
|
||||||
|
@ -714,8 +715,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArchiveNode getDataTypeArchiveNode(DataTypeArchive dataTypeArchive) {
|
private ArchiveNode getDataTypeArchiveNode(DataTypeArchive dataTypeArchive) {
|
||||||
GTreeNode rootNode = getGTree().getRootNode();
|
GTreeNode rootNode = getGTree().getModelRoot();
|
||||||
List<GTreeNode> children = rootNode.getAllChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode node : children) {
|
for (GTreeNode node : children) {
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
Archive archive = archiveNode.getArchive();
|
Archive archive = archiveNode.getArchive();
|
||||||
|
@ -754,7 +755,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
Category category = dataTypeManager.getCategory(dataType.getCategoryPath());
|
Category category = dataTypeManager.getCategory(dataType.getCategoryPath());
|
||||||
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getRootNode();
|
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getViewRoot();
|
||||||
ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
|
ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
|
||||||
if (archiveNode == null) {
|
if (archiveNode == null) {
|
||||||
plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " +
|
plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " +
|
||||||
|
@ -844,8 +845,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
void programRenamed() {
|
void programRenamed() {
|
||||||
ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getRootNode();
|
ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getModelRoot();
|
||||||
List<GTreeNode> allChildren = rootNode.getAllChildren();
|
List<GTreeNode> allChildren = rootNode.getChildren();
|
||||||
for (GTreeNode node : allChildren) {
|
for (GTreeNode node : allChildren) {
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
if (archiveNode.getArchive() instanceof ProgramArchive) {
|
if (archiveNode.getArchive() instanceof ProgramArchive) {
|
||||||
|
|
|
@ -219,8 +219,8 @@ class OpenDomainFileTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
|
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
for (GTreeNode node : rootNode) {
|
for (GTreeNode node : rootNode.getChildren()) {
|
||||||
if (node instanceof ArchiveNode) {
|
if (node instanceof ArchiveNode) {
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
if (archiveNode.getArchive() == archive) {
|
if (archiveNode.getArchive() == archive) {
|
||||||
|
|
|
@ -154,7 +154,7 @@ public class CollapseAllArchivesAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void collapseAll(GTree archiveGTree) {
|
private void collapseAll(GTree archiveGTree) {
|
||||||
GTreeNode rootNode = archiveGTree.getRootNode();
|
GTreeNode rootNode = archiveGTree.getViewRoot();
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode childNode : children) {
|
for (GTreeNode childNode : children) {
|
||||||
archiveGTree.collapseAll(childNode);
|
archiveGTree.collapseAll(childNode);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,19 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.ArchiveFileChooser;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
|
||||||
import ghidra.util.Msg;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.tree.GTreeRootNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
|
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||||
|
import ghidra.app.plugin.core.datamgr.archive.ArchiveFileChooser;
|
||||||
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class CreateArchiveAction extends DockingAction {
|
public class CreateArchiveAction extends DockingAction {
|
||||||
private final DataTypeManagerPlugin plugin;
|
private final DataTypeManagerPlugin plugin;
|
||||||
|
@ -64,7 +62,8 @@ public class CreateArchiveAction extends DockingAction {
|
||||||
Msg.trace(this, "Need to overwrite--showing dialog");
|
Msg.trace(this, "Need to overwrite--showing dialog");
|
||||||
if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(gTree,
|
if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(gTree,
|
||||||
"Overwrite Existing File?",
|
"Overwrite Existing File?",
|
||||||
"Do you want to overwrite existing file\n" + file.getAbsolutePath()) != OptionDialog.OPTION_ONE) {
|
"Do you want to overwrite existing file\n" +
|
||||||
|
file.getAbsolutePath()) != OptionDialog.OPTION_ONE) {
|
||||||
Msg.trace(this, "\tdo not overwrite was chosen");
|
Msg.trace(this, "\tdo not overwrite was chosen");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +78,7 @@ public class CreateArchiveAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) {
|
private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) {
|
||||||
GTreeRootNode rootNode = gTree.getRootNode();
|
GTreeNode rootNode = gTree.getModelRoot();
|
||||||
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), archive.getName() });
|
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), archive.getName() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,6 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.DockingAction;
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import docking.widgets.dialogs.InputDialog;
|
||||||
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
||||||
|
@ -27,15 +35,6 @@ import ghidra.program.model.data.Enum;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.tree.TreePath;
|
|
||||||
|
|
||||||
import docking.ActionContext;
|
|
||||||
import docking.action.DockingAction;
|
|
||||||
import docking.action.MenuData;
|
|
||||||
import docking.widgets.dialogs.InputDialog;
|
|
||||||
import docking.widgets.tree.*;
|
|
||||||
|
|
||||||
public class CreateEnumFromSelectionAction extends DockingAction {
|
public class CreateEnumFromSelectionAction extends DockingAction {
|
||||||
private final DataTypeManagerPlugin plugin;
|
private final DataTypeManagerPlugin plugin;
|
||||||
|
|
||||||
|
@ -131,7 +130,7 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
GTreeRootNode rootNode = gTree.getRootNode();
|
GTreeNode rootNode = gTree.getViewRoot();
|
||||||
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), parentNodeName,
|
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), parentNodeName,
|
||||||
newNodeName });
|
newNodeName });
|
||||||
}
|
}
|
||||||
|
@ -174,9 +173,9 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
||||||
|
|
||||||
// figure out size of the new enum using the max size of the selected enums
|
// figure out size of the new enum using the max size of the selected enums
|
||||||
int maxEnumSize = 1;
|
int maxEnumSize = 1;
|
||||||
for (int i = 0; i < enumArray.length; i++) {
|
for (Enum element : enumArray) {
|
||||||
if (maxEnumSize < enumArray[i].getLength()) {
|
if (maxEnumSize < element.getLength()) {
|
||||||
maxEnumSize = enumArray[i].getLength();
|
maxEnumSize = element.getLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SourceArchive sourceArchive = category.getDataTypeManager().getLocalSourceArchive();
|
SourceArchive sourceArchive = category.getDataTypeManager().getLocalSourceArchive();
|
||||||
|
@ -184,17 +183,18 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
||||||
new EnumDataType(category.getCategoryPath(), newName, maxEnumSize,
|
new EnumDataType(category.getCategoryPath(), newName, maxEnumSize,
|
||||||
category.getDataTypeManager());
|
category.getDataTypeManager());
|
||||||
|
|
||||||
for (int i = 0; i < enumArray.length; i++) {
|
for (Enum element : enumArray) {
|
||||||
String[] names = enumArray[i].getNames();
|
String[] names = element.getNames();
|
||||||
for (int j = 0; j < names.length; j++) {
|
for (String name : names) {
|
||||||
dataType.add(names[j], enumArray[i].getValue(names[j]));
|
dataType.add(name, element.getValue(name));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataType.setSourceArchive(sourceArchive);
|
dataType.setSourceArchive(sourceArchive);
|
||||||
int id = category.getDataTypeManager().startTransaction("Create New Enum Data Type");
|
int id = category.getDataTypeManager().startTransaction("Create New Enum Data Type");
|
||||||
category.getDataTypeManager().addDataType(dataType, DataTypeConflictHandler.REPLACE_HANDLER);
|
category.getDataTypeManager()
|
||||||
|
.addDataType(dataType, DataTypeConflictHandler.REPLACE_HANDLER);
|
||||||
category.getDataTypeManager().endTransaction(id, true);
|
category.getDataTypeManager().endTransaction(id, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,12 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
|
@ -29,6 +22,10 @@ import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
|
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||||
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
|
||||||
public class CreateProjectArchiveAction extends DockingAction {
|
public class CreateProjectArchiveAction extends DockingAction {
|
||||||
private final DataTypeManagerPlugin plugin;
|
private final DataTypeManagerPlugin plugin;
|
||||||
|
@ -56,9 +53,10 @@ public class CreateProjectArchiveAction extends DockingAction {
|
||||||
|
|
||||||
private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) {
|
private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// start an edit on the new temporary node name
|
// start an edit on the new temporary node name
|
||||||
GTreeNode node = gTree.getRootNode();
|
GTreeNode node = gTree.getViewRoot();
|
||||||
final GTreeNode child = node.getChild(archive.getName());
|
final GTreeNode child = node.getChild(archive.getName());
|
||||||
if (child != null) {
|
if (child != null) {
|
||||||
gTree.expandPath(node);
|
gTree.expandPath(node);
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class DeleteAction extends DockingAction {
|
||||||
private boolean containsUndeletableNodes(TreePath[] selectionPaths) {
|
private boolean containsUndeletableNodes(TreePath[] selectionPaths) {
|
||||||
for (TreePath path : selectionPaths) {
|
for (TreePath path : selectionPaths) {
|
||||||
DataTypeTreeNode node = (DataTypeTreeNode) path.getLastPathComponent();
|
DataTypeTreeNode node = (DataTypeTreeNode) path.getLastPathComponent();
|
||||||
if (node.isSystemNode() || (node instanceof ArchiveNode)) {
|
if (!node.canDelete() || (node instanceof ArchiveNode)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,6 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
|
@ -24,11 +26,6 @@ import ghidra.program.model.data.*;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
|
||||||
import docking.widgets.tree.GTreeRootNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to 1) hold related data for creating a new data type and 2) to validate the given
|
* A class to 1) hold related data for creating a new data type and 2) to validate the given
|
||||||
* data when the requested info is based upon the a disallowed condition (e.g., creating a data
|
* data when the requested info is based upon the a disallowed condition (e.g., creating a data
|
||||||
|
@ -82,7 +79,7 @@ class DerivativeDataTypeInfo {
|
||||||
private ArchiveNode getProgramArchiveNode(DataTypeArchiveGTree tree) {
|
private ArchiveNode getProgramArchiveNode(DataTypeArchiveGTree tree) {
|
||||||
|
|
||||||
DataTypeManager manager = plugin.getProgramDataTypeManager();
|
DataTypeManager manager = plugin.getProgramDataTypeManager();
|
||||||
GTreeRootNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode node : children) {
|
for (GTreeNode node : children) {
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
|
|
|
@ -149,8 +149,8 @@ public class DisassociateDataTypeAction extends DockingAction {
|
||||||
private void collapseArchiveNodes(DataTypeArchiveGTree tree) {
|
private void collapseArchiveNodes(DataTypeArchiveGTree tree) {
|
||||||
// Note: collapsing archive nodes will actually remove all the children of the archive
|
// Note: collapsing archive nodes will actually remove all the children of the archive
|
||||||
// which means no event processing and less memory consumption.
|
// which means no event processing and less memory consumption.
|
||||||
GTreeRootNode root = tree.getRootNode();
|
GTreeNode root = tree.getViewRoot();
|
||||||
List<GTreeNode> archives = root.getAllChildren();
|
List<GTreeNode> archives = root.getChildren();
|
||||||
archives.forEach(archive -> tree.collapseAll(archive));
|
archives.forEach(archive -> tree.collapseAll(archive));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,8 @@ public class ExportToHeaderAction extends DockingAction {
|
||||||
finally {
|
finally {
|
||||||
writer.close();
|
writer.close();
|
||||||
}
|
}
|
||||||
plugin.getTool().setStatusInfo(
|
plugin.getTool()
|
||||||
|
.setStatusInfo(
|
||||||
"Successfully exported data type(s) to " + file.getAbsolutePath());
|
"Successfully exported data type(s) to " + file.getAbsolutePath());
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
|
@ -242,7 +243,7 @@ public class ExportToHeaderAction extends DockingAction {
|
||||||
}
|
}
|
||||||
else if (last instanceof CategoryNode) {
|
else if (last instanceof CategoryNode) {
|
||||||
CategoryNode node = (CategoryNode) last;
|
CategoryNode node = (CategoryNode) last;
|
||||||
List<GTreeNode> children = node.getAllChildren();
|
List<GTreeNode> children = node.getChildren();
|
||||||
for (GTreeNode cnode : children) {
|
for (GTreeNode cnode : children) {
|
||||||
addToManager(cnode.getTreePath(), managersToDataTypesMap);
|
addToManager(cnode.getTreePath(), managersToDataTypesMap);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,20 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
|
||||||
import ghidra.util.HTMLUtilities;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import resources.ResourceManager;
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
import docking.action.ToolBarData;
|
import docking.action.ToolBarData;
|
||||||
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
|
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||||
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
|
import ghidra.util.HTMLUtilities;
|
||||||
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class FilterArraysAction extends ToggleDockingAction {
|
public class FilterArraysAction extends ToggleDockingAction {
|
||||||
|
|
||||||
|
@ -41,8 +40,8 @@ public class FilterArraysAction extends ToggleDockingAction {
|
||||||
|
|
||||||
this.setToolBarData(new ToolBarData(FILTER_ARRAYS_ICON, "filters"));
|
this.setToolBarData(new ToolBarData(FILTER_ARRAYS_ICON, "filters"));
|
||||||
|
|
||||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not Arrays are\n"
|
setDescription(HTMLUtilities.toHTML(
|
||||||
+ "displayed in the Data Type Manager tree."));
|
"Toggle whether or not Arrays are\n" + "displayed in the Data Type Manager tree."));
|
||||||
setSelected(true);
|
setSelected(true);
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +57,7 @@ public class FilterArraysAction extends ToggleDockingAction {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
DataTypeArchiveGTree gtree = (DataTypeArchiveGTree) context.getContextObject();
|
DataTypeArchiveGTree gtree = (DataTypeArchiveGTree) context.getContextObject();
|
||||||
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getRootNode());
|
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot());
|
||||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||||
gtree.enableArrayFilter(isSelected());
|
gtree.enableArrayFilter(isSelected());
|
||||||
gtree.expandPaths(expandedPaths);
|
gtree.expandPaths(expandedPaths);
|
||||||
|
|
|
@ -15,20 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
|
||||||
import ghidra.util.HTMLUtilities;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import resources.ResourceManager;
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
import docking.action.ToolBarData;
|
import docking.action.ToolBarData;
|
||||||
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
|
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||||
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
|
import ghidra.util.HTMLUtilities;
|
||||||
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class FilterPointersAction extends ToggleDockingAction {
|
public class FilterPointersAction extends ToggleDockingAction {
|
||||||
private static final Icon FILTER_POINTERS_ICON =
|
private static final Icon FILTER_POINTERS_ICON =
|
||||||
|
@ -40,8 +39,8 @@ public class FilterPointersAction extends ToggleDockingAction {
|
||||||
|
|
||||||
this.setToolBarData(new ToolBarData(FILTER_POINTERS_ICON, "filters"));
|
this.setToolBarData(new ToolBarData(FILTER_POINTERS_ICON, "filters"));
|
||||||
|
|
||||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not Pointers are\n"
|
setDescription(HTMLUtilities.toHTML(
|
||||||
+ "displayed in the Data Type Manager tree."));
|
"Toggle whether or not Pointers are\n" + "displayed in the Data Type Manager tree."));
|
||||||
setSelected(true);
|
setSelected(true);
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
}
|
}
|
||||||
|
@ -56,8 +55,9 @@ public class FilterPointersAction extends ToggleDockingAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
|
// this doesn't actually cause a filter task to happen, so we need to save and restore the state here
|
||||||
DataTypeArchiveGTree gtree = (DataTypeArchiveGTree) context.getContextObject();
|
DataTypeArchiveGTree gtree = (DataTypeArchiveGTree) context.getContextObject();
|
||||||
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getRootNode());
|
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot());
|
||||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||||
gtree.enablePointerFilter(isSelected());
|
gtree.enablePointerFilter(isSelected());
|
||||||
gtree.expandPaths(expandedPaths);
|
gtree.expandPaths(expandedPaths);
|
||||||
|
|
|
@ -91,8 +91,8 @@ public class OpenArchiveAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
|
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
List<GTreeNode> allChildren = rootNode.getAllChildren();
|
List<GTreeNode> allChildren = rootNode.getChildren();
|
||||||
for (GTreeNode node : allChildren) {
|
for (GTreeNode node : allChildren) {
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
if (archiveNode.getArchive() == archive) {
|
if (archiveNode.getArchive() == archive) {
|
||||||
|
|
|
@ -32,15 +32,17 @@ public class ArchiveNode extends CategoryNode {
|
||||||
protected ArchiveNodeCategoryChangeListener listener;
|
protected ArchiveNodeCategoryChangeListener listener;
|
||||||
private DataTypeManager dataTypeManager; // may be null
|
private DataTypeManager dataTypeManager; // may be null
|
||||||
|
|
||||||
public ArchiveNode(Archive archive) {
|
public ArchiveNode(Archive archive, ArrayPointerFilterState filterState) {
|
||||||
this(archive, archive.getDataTypeManager() == null ? null
|
this(archive, archive.getDataTypeManager() == null ? null
|
||||||
: archive.getDataTypeManager().getRootCategory());
|
: archive.getDataTypeManager().getRootCategory(),
|
||||||
|
filterState);
|
||||||
this.dataTypeManager = archive.getDataTypeManager();
|
this.dataTypeManager = archive.getDataTypeManager();
|
||||||
installDataTypeManagerListener();
|
installDataTypeManagerListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ArchiveNode(Archive archive, Category rootCategory) {
|
protected ArchiveNode(Archive archive, Category rootCategory,
|
||||||
super(rootCategory);
|
ArrayPointerFilterState filterState) {
|
||||||
|
super(rootCategory, filterState);
|
||||||
this.archive = archive;
|
this.archive = archive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ public class ArchiveNode extends CategoryNode {
|
||||||
protected void dataTypeManagerChanged() {
|
protected void dataTypeManagerChanged() {
|
||||||
installDataTypeManagerListener();
|
installDataTypeManagerListener();
|
||||||
// old children are no longer valid--clear the cache and fire a node structure changed event
|
// old children are no longer valid--clear the cache and fire a node structure changed event
|
||||||
removeAll();
|
setChildren(null);
|
||||||
nodeChanged(); // notify that this nodes display data has changed
|
nodeChanged(); // notify that this nodes display data has changed
|
||||||
structureChanged(); // notify that his children have been refreshed and the tree cache needs to be wiped.
|
structureChanged(); // notify that his children have been refreshed and the tree cache needs to be wiped.
|
||||||
|
|
||||||
|
@ -112,8 +114,7 @@ public class ArchiveNode extends CategoryNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void structureChanged() {
|
public void structureChanged() {
|
||||||
removeAll();
|
setChildren(null);
|
||||||
getTree().scheduleFilterTask(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void nodeChanged() {
|
public void nodeChanged() {
|
||||||
|
@ -206,7 +207,7 @@ public class ArchiveNode extends CategoryNode {
|
||||||
public CategoryNode findCategoryNode(Category localCategory, boolean loadChildren) {
|
public CategoryNode findCategoryNode(Category localCategory, boolean loadChildren) {
|
||||||
|
|
||||||
// if we don't have to loadChildren and we are not loaded get out.
|
// if we don't have to loadChildren and we are not loaded get out.
|
||||||
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
|
if (!loadChildren && !isLoaded()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +229,7 @@ public class ArchiveNode extends CategoryNode {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GTreeNode> children = node.getAllChildren();
|
List<GTreeNode> children = node.getChildren();
|
||||||
for (GTreeNode child : children) {
|
for (GTreeNode child : children) {
|
||||||
if (!(child instanceof CategoryNode)) {
|
if (!(child instanceof CategoryNode)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -19,21 +19,21 @@ import java.util.*;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
import ghidra.app.plugin.core.datamgr.archive.*;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.program.model.data.Category;
|
import ghidra.program.model.data.Category;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, DataTypeTreeNode {
|
public class ArchiveRootNode extends DataTypeTreeNode {
|
||||||
private static final String NAME = "Data Types";
|
private static final String NAME = "Data Types";
|
||||||
|
|
||||||
private GTree tree;
|
|
||||||
|
|
||||||
private final DataTypeManagerHandler archiveManager;
|
private final DataTypeManagerHandler archiveManager;
|
||||||
private RootNodeListener archiveListener;
|
private RootNodeListener archiveListener;
|
||||||
|
|
||||||
|
private ArrayPointerFilterState filterState = new ArrayPointerFilterState();
|
||||||
|
|
||||||
ArchiveRootNode(DataTypeManagerHandler archiveManager) {
|
ArchiveRootNode(DataTypeManagerHandler archiveManager) {
|
||||||
this.archiveManager = archiveManager;
|
this.archiveManager = archiveManager;
|
||||||
archiveListener = new RootNodeListener();
|
archiveListener = new RootNodeListener();
|
||||||
|
@ -76,21 +76,22 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
}
|
}
|
||||||
|
|
||||||
// a factory method to isolate non-OO inheritance checks
|
// a factory method to isolate non-OO inheritance checks
|
||||||
private static final GTreeNode createArchiveNode(Archive archive) {
|
private static final GTreeNode createArchiveNode(Archive archive,
|
||||||
|
ArrayPointerFilterState filterState) {
|
||||||
if (archive instanceof FileArchive) {
|
if (archive instanceof FileArchive) {
|
||||||
return new FileArchiveNode((FileArchive) archive);
|
return new FileArchiveNode((FileArchive) archive, filterState);
|
||||||
}
|
}
|
||||||
else if (archive instanceof ProjectArchive) {
|
else if (archive instanceof ProjectArchive) {
|
||||||
return new ProjectArchiveNode((ProjectArchive) archive);
|
return new ProjectArchiveNode((ProjectArchive) archive, filterState);
|
||||||
}
|
}
|
||||||
else if (archive instanceof InvalidFileArchive) {
|
else if (archive instanceof InvalidFileArchive) {
|
||||||
return new InvalidArchiveNode((InvalidFileArchive) archive);
|
return new InvalidArchiveNode((InvalidFileArchive) archive);
|
||||||
}
|
}
|
||||||
else if (archive instanceof ProgramArchive) {
|
else if (archive instanceof ProgramArchive) {
|
||||||
return new ProgramArchiveNode((ProgramArchive) archive);
|
return new ProgramArchiveNode((ProgramArchive) archive, filterState);
|
||||||
}
|
}
|
||||||
else if (archive instanceof BuiltInArchive) {
|
else if (archive instanceof BuiltInArchive) {
|
||||||
return new BuiltInArchiveNode((BuiltInArchive) archive);
|
return new BuiltInArchiveNode((BuiltInArchive) archive, filterState);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -137,12 +138,12 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSystemNode() {
|
public boolean canDelete() {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CategoryNode findCategoryNode(Category category) {
|
public CategoryNode findCategoryNode(Category category) {
|
||||||
Iterator<GTreeNode> iterator = getAllChildren().iterator();
|
Iterator<GTreeNode> iterator = getChildren().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
GTreeNode node = iterator.next();
|
GTreeNode node = iterator.next();
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
|
@ -155,7 +156,7 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArchiveNode getNodeForManager(DataTypeManager dtm) {
|
public ArchiveNode getNodeForManager(DataTypeManager dtm) {
|
||||||
Iterator<GTreeNode> iterator = getAllChildren().iterator();
|
Iterator<GTreeNode> iterator = getChildren().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
GTreeNode node = iterator.next();
|
GTreeNode node = iterator.next();
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
|
@ -170,17 +171,17 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GTreeNode> generateChildren() {
|
public List<GTreeNode> generateChildren() {
|
||||||
List<GTreeNode> children = new ArrayList<>();
|
List<GTreeNode> list = new ArrayList<>();
|
||||||
Iterator<Archive> iterator = archiveManager.getAllArchives().iterator();
|
Iterator<Archive> iterator = archiveManager.getAllArchives().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
children.add(createArchiveNode(iterator.next()));
|
list.add(createArchiveNode(iterator.next(), filterState));
|
||||||
}
|
}
|
||||||
Collections.sort(children);
|
Collections.sort(list);
|
||||||
return children;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArchiveNode getArchiveNode(Archive archive) {
|
private ArchiveNode getArchiveNode(Archive archive) {
|
||||||
List<GTreeNode> allChildrenList = getAllChildren();
|
List<GTreeNode> allChildrenList = getChildren();
|
||||||
for (GTreeNode node : allChildrenList) {
|
for (GTreeNode node : allChildrenList) {
|
||||||
if (node instanceof ArchiveNode) {
|
if (node instanceof ArchiveNode) {
|
||||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||||
|
@ -200,10 +201,10 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void archiveClosed(Archive archive) {
|
public void archiveClosed(Archive archive) {
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<GTreeNode> allChildrenList = getAllChildren();
|
List<GTreeNode> allChildrenList = getChildren();
|
||||||
Iterator<GTreeNode> iterator = allChildrenList.iterator();
|
Iterator<GTreeNode> iterator = allChildrenList.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
GTreeNode node = iterator.next();
|
GTreeNode node = iterator.next();
|
||||||
|
@ -218,9 +219,9 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void archiveOpened(Archive archive) {
|
public void archiveOpened(Archive archive) {
|
||||||
if (isChildrenLoadedOrInProgress()) {
|
if (isLoaded()) {
|
||||||
GTreeNode node = createArchiveNode(archive);
|
GTreeNode node = createArchiveNode(archive, filterState);
|
||||||
List<GTreeNode> allChildrenList = getAllChildren();
|
List<GTreeNode> allChildrenList = getChildren();
|
||||||
int index = Collections.binarySearch(allChildrenList, node);
|
int index = Collections.binarySearch(allChildrenList, node);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
index = -index - 1;
|
index = -index - 1;
|
||||||
|
@ -228,7 +229,7 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
addNode(index, node);
|
addNode(index, node);
|
||||||
// kick tree to refilter if filter in place so that nodes will stay expaned see
|
// kick tree to refilter if filter in place so that nodes will stay expaned see
|
||||||
// SCR #7895
|
// SCR #7895
|
||||||
tree.setFilterText(tree.getFilterText());
|
// tree.setFilterText(tree.getFilterText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,14 +250,12 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setFilterArray(boolean enabled) {
|
||||||
public GTree getGTree() {
|
filterState.setFilterArrays(enabled);
|
||||||
return tree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setFilterPointer(boolean enabled) {
|
||||||
public void setGTree(GTree tree) {
|
filterState.setFilterPointers(enabled);
|
||||||
this.tree = tree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* ###
|
||||||
|
* 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.datamgr.tree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to maintain the state of whether or not the Data Type Manager Tree is showing
|
||||||
|
* pointers and/or arrays.
|
||||||
|
*
|
||||||
|
* Note: Not sure if this needs to be synchronized or not. The set methods are called
|
||||||
|
* from the awt thread, but the getter methods are often called by a background worker
|
||||||
|
* thread. The theory is that we don't need to synchronize this because if the state
|
||||||
|
* of this object changes, the tree will later be rebuilt in a follow-up task.
|
||||||
|
*/
|
||||||
|
public class ArrayPointerFilterState {
|
||||||
|
private boolean filterArrays;
|
||||||
|
private boolean filterPointers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the tree should NOT show arrays
|
||||||
|
* @return true if the tree should NOT show arrays
|
||||||
|
*/
|
||||||
|
public boolean filterArrays() {
|
||||||
|
return filterArrays;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the tree should show arrays.
|
||||||
|
* @param filterArrays if true the tree will NOT show arrays.
|
||||||
|
*/
|
||||||
|
public void setFilterArrays(boolean filterArrays) {
|
||||||
|
this.filterArrays = filterArrays;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the tree should NOT show pointers
|
||||||
|
* @return true if the tree should NOT show pointers
|
||||||
|
*/
|
||||||
|
public boolean filterPointers() {
|
||||||
|
return filterPointers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the tree should show pointers.
|
||||||
|
* @param filterPointers if true the tree will NOT show pointers.
|
||||||
|
*/
|
||||||
|
public void setFilterPointers(boolean filterPointers) {
|
||||||
|
this.filterPointers = filterPointers;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,8 +22,8 @@ import resources.MultiIcon;
|
||||||
|
|
||||||
public class BuiltInArchiveNode extends ArchiveNode {
|
public class BuiltInArchiveNode extends ArchiveNode {
|
||||||
|
|
||||||
public BuiltInArchiveNode(BuiltInArchive archive) {
|
public BuiltInArchiveNode(BuiltInArchive archive, ArrayPointerFilterState filterState) {
|
||||||
super(archive);
|
super(archive, filterState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,7 +19,6 @@ import java.util.*;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeLazyNode;
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -27,14 +26,16 @@ import ghidra.util.*;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
public class CategoryNode extends DataTypeTreeNode {
|
||||||
|
|
||||||
private Category category;
|
private Category category;
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private boolean isCut;
|
private boolean isCut;
|
||||||
|
private ArrayPointerFilterState filterState;
|
||||||
|
|
||||||
public CategoryNode(Category category) {
|
public CategoryNode(Category category, ArrayPointerFilterState filterState) {
|
||||||
|
this.filterState = filterState;
|
||||||
setCategory(category);
|
setCategory(category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,49 +46,35 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected synchronized int doGetIndexOfChild(GTreeNode node, List<GTreeNode> children) {
|
|
||||||
if (children == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return Collections.binarySearch(children, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<GTreeNode> generateChildren() {
|
protected List<GTreeNode> generateChildren() {
|
||||||
if (category == null) {
|
if (category == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTypeArchiveGTree tree = (DataTypeArchiveGTree) getTree();
|
|
||||||
if (tree == null) {
|
|
||||||
return Collections.emptyList(); // must have been disposed
|
|
||||||
}
|
|
||||||
|
|
||||||
Category[] subCategories = category.getCategories();
|
Category[] subCategories = category.getCategories();
|
||||||
DataType[] dataTypes = category.getDataTypes();
|
DataType[] dataTypes = category.getDataTypes();
|
||||||
List<GTreeNode> children = new ArrayList<>(subCategories.length + dataTypes.length);
|
List<GTreeNode> list = new ArrayList<>(subCategories.length + dataTypes.length);
|
||||||
for (Category subCategory : subCategories) {
|
for (Category subCategory : subCategories) {
|
||||||
children.add(new CategoryNode(subCategory));
|
list.add(new CategoryNode(subCategory, filterState));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DataType dataType : dataTypes) {
|
for (DataType dataType : dataTypes) {
|
||||||
if (!isFilteredType(tree, dataType)) {
|
if (!isFilteredType(dataType)) {
|
||||||
children.add(new DataTypeNode(dataType));
|
list.add(new DataTypeNode(dataType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(children);
|
Collections.sort(list);
|
||||||
|
|
||||||
return children;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFilteredType(DataTypeArchiveGTree tree, DataType dataType) {
|
private boolean isFilteredType(DataType dataType) {
|
||||||
if (tree.isFilterArrays() && dataType instanceof Array) {
|
if (filterState.filterArrays() && dataType instanceof Array) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree.isFilterPointers() && (dataType instanceof Pointer) &&
|
if (filterState.filterPointers() && (dataType instanceof Pointer) &&
|
||||||
!(dataType.getDataTypeManager() instanceof BuiltInDataTypeManager)) {
|
!(dataType.getDataTypeManager() instanceof BuiltInDataTypeManager)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -172,12 +159,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
|
|
||||||
public void categoryAdded(Category newCategory) {
|
public void categoryAdded(Category newCategory) {
|
||||||
|
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CategoryNode node = new CategoryNode(newCategory);
|
CategoryNode node = new CategoryNode(newCategory, filterState);
|
||||||
List<GTreeNode> allChildrenList = getAllChildren();
|
List<GTreeNode> allChildrenList = getChildren();
|
||||||
int index = Collections.binarySearch(allChildrenList, node);
|
int index = Collections.binarySearch(allChildrenList, node);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
index = -index - 1;
|
index = -index - 1;
|
||||||
|
@ -186,7 +173,7 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dataTypeAdded(DataType dataType) {
|
public void dataTypeAdded(DataType dataType) {
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,12 +183,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFilteredType(tree, dataType)) {
|
if (isFilteredType(dataType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTypeNode node = new DataTypeNode(dataType);
|
DataTypeNode node = new DataTypeNode(dataType);
|
||||||
List<GTreeNode> allChildrenList = getAllChildren();
|
List<GTreeNode> allChildrenList = getChildren();
|
||||||
int index = Collections.binarySearch(allChildrenList, node);
|
int index = Collections.binarySearch(allChildrenList, node);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
if (node.getName().equals(allChildrenList.get(index).getName())) {
|
if (node.getName().equals(allChildrenList.get(index).getName())) {
|
||||||
|
@ -215,7 +202,10 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void categoryRemoved(String categoryName) {
|
public void categoryRemoved(String categoryName) {
|
||||||
for (GTreeNode node : getAllChildrenIfLoaded()) {
|
if (!isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (GTreeNode node : getChildren()) {
|
||||||
if ((node instanceof CategoryNode) && node.getName().equals(categoryName)) {
|
if ((node instanceof CategoryNode) && node.getName().equals(categoryName)) {
|
||||||
removeNode(node);
|
removeNode(node);
|
||||||
return;
|
return;
|
||||||
|
@ -224,8 +214,11 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dataTypeRemoved(String dataTypeName) {
|
public void dataTypeRemoved(String dataTypeName) {
|
||||||
|
if (!isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (GTreeNode node : getAllChildrenIfLoaded()) {
|
for (GTreeNode node : getChildren()) {
|
||||||
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
|
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
|
||||||
removeNode(node);
|
removeNode(node);
|
||||||
return;
|
return;
|
||||||
|
@ -234,8 +227,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dataTypeChanged(DataType dataType) {
|
public void dataTypeChanged(DataType dataType) {
|
||||||
|
if (!isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String dataTypeName = dataType.getName();
|
String dataTypeName = dataType.getName();
|
||||||
for (GTreeNode node : getAllChildrenIfLoaded()) {
|
for (GTreeNode node : getChildren()) {
|
||||||
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
|
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
|
||||||
((DataTypeNode) node).dataTypeChanged();
|
((DataTypeNode) node).dataTypeChanged();
|
||||||
return;
|
return;
|
||||||
|
@ -347,7 +344,7 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSystemNode() {
|
public boolean canDelete() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,9 +53,6 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
private MyFolderListener folderListener;
|
private MyFolderListener folderListener;
|
||||||
private DataTypeTreeExpansionListener cleanupListener = new DataTypeTreeExpansionListener();
|
private DataTypeTreeExpansionListener cleanupListener = new DataTypeTreeExpansionListener();
|
||||||
|
|
||||||
private boolean filterArrays = true;
|
|
||||||
private boolean filterPointers = true;
|
|
||||||
|
|
||||||
public DataTypeArchiveGTree(DataTypeManagerPlugin dataTypeManagerPlugin) {
|
public DataTypeArchiveGTree(DataTypeManagerPlugin dataTypeManagerPlugin) {
|
||||||
super(new ArchiveRootNode(dataTypeManagerPlugin.getDataTypeManagerHandler()));
|
super(new ArchiveRootNode(dataTypeManagerPlugin.getDataTypeManagerHandler()));
|
||||||
|
|
||||||
|
@ -65,7 +62,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
|
|
||||||
// setting the row height may provide speed improvements, as the tree does not have to
|
// setting the row height may provide speed improvements, as the tree does not have to
|
||||||
// ask for the height for each cell from the renderer.
|
// ask for the height for each cell from the renderer.
|
||||||
setRowHeight(getHeight(getRootNode(), renderer));
|
setRowHeight(getHeight(getViewRoot(), renderer));
|
||||||
setCellRenderer(renderer);
|
setCellRenderer(renderer);
|
||||||
Project project = plugin.getTool().getProject();
|
Project project = plugin.getTool().getProject();
|
||||||
if (project != null) {
|
if (project != null) {
|
||||||
|
@ -79,7 +76,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
addTreeExpansionListener(cleanupListener);
|
addTreeExpansionListener(cleanupListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getHeight(GTreeRootNode rootNode, DataTypeTreeRenderer renderer) {
|
private int getHeight(GTreeNode rootNode, DataTypeTreeRenderer renderer) {
|
||||||
Component c = renderer.getTreeCellRendererComponent(getJTree(), rootNode, false, false,
|
Component c = renderer.getTreeCellRendererComponent(getJTree(), rootNode, false, false,
|
||||||
false, 0, false);
|
false, 0, false);
|
||||||
Dimension size = c.getPreferredSize();
|
Dimension size = c.getPreferredSize();
|
||||||
|
@ -89,7 +86,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
@Override
|
@Override
|
||||||
public void expandedStateRestored(TaskMonitor monitor) {
|
public void expandedStateRestored(TaskMonitor monitor) {
|
||||||
// walk all of our nodes and reclaim any that aren't expanded
|
// walk all of our nodes and reclaim any that aren't expanded
|
||||||
GTreeRootNode rootNode = getRootNode();
|
GTreeNode rootNode = getViewRoot();
|
||||||
if (rootNode == null) {
|
if (rootNode == null) {
|
||||||
return; // in a state of flux; been disposed
|
return; // in a state of flux; been disposed
|
||||||
}
|
}
|
||||||
|
@ -108,7 +105,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
if (!isExpanded(node.getTreePath())) {
|
if (!isExpanded(node.getTreePath())) {
|
||||||
int leafCount = node.getLeafCount();
|
int leafCount = node.getLeafCount();
|
||||||
if (node instanceof GTreeLazyNode) {
|
if (node instanceof GTreeLazyNode) {
|
||||||
((GTreeLazyNode) node).removeAll();
|
((GTreeLazyNode) node).unloadChildren();
|
||||||
}
|
}
|
||||||
monitor.incrementProgress(leafCount);
|
monitor.incrementProgress(leafCount);
|
||||||
return;
|
return;
|
||||||
|
@ -122,7 +119,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
((ArchiveRootNode) getRootNode()).dispose();
|
((ArchiveRootNode) getModelRoot()).dispose();
|
||||||
PluginTool tool = plugin.getTool();
|
PluginTool tool = plugin.getTool();
|
||||||
if (tool == null) {
|
if (tool == null) {
|
||||||
return; // this can happen when the plugin is disposed off the swing thread
|
return; // this can happen when the plugin is disposed off the swing thread
|
||||||
|
@ -141,12 +138,14 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableArrayFilter(boolean enabled) {
|
public void enableArrayFilter(boolean enabled) {
|
||||||
this.filterArrays = enabled;
|
ArchiveRootNode root = (ArchiveRootNode) getModelRoot();
|
||||||
|
root.setFilterArray(enabled);
|
||||||
reloadTree();
|
reloadTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enablePointerFilter(boolean enabled) {
|
public void enablePointerFilter(boolean enabled) {
|
||||||
this.filterPointers = enabled;
|
ArchiveRootNode root = (ArchiveRootNode) getModelRoot();
|
||||||
|
root.setFilterPointer(enabled);
|
||||||
reloadTree();
|
reloadTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,22 +158,12 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
private void reloadTree() {
|
private void reloadTree() {
|
||||||
GTreeState treeState = getTreeState();
|
GTreeState treeState = getTreeState();
|
||||||
|
|
||||||
ArchiveRootNode rootNode = (ArchiveRootNode) getRootNode();
|
ArchiveRootNode rootNode = (ArchiveRootNode) getModelRoot();
|
||||||
rootNode.removeAll();
|
rootNode.unloadChildren();
|
||||||
// calling getChildCount() will "kick" the root node to reload its children
|
|
||||||
rootNode.getChildCount();
|
|
||||||
updateModelFilter();
|
updateModelFilter();
|
||||||
restoreTreeState(treeState);
|
restoreTreeState(treeState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFilterPointers() {
|
|
||||||
return filterPointers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFilterArrays() {
|
|
||||||
return filterArrays;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setNodeEditable(GTreeNode node) {
|
public void setNodeEditable(GTreeNode node) {
|
||||||
armedNode = node;
|
armedNode = node;
|
||||||
|
@ -314,7 +303,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
if ((node instanceof CategoryNode)) {
|
if ((node instanceof CategoryNode)) {
|
||||||
CategoryNode categoryNode = (CategoryNode) node;
|
CategoryNode categoryNode = (CategoryNode) node;
|
||||||
categoryNode.removeAll();
|
categoryNode.setChildren(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +451,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||||
List<GTreeNode> archiveNodes = getRootNode().getChildren();
|
List<GTreeNode> archiveNodes = getModelRoot().getChildren();
|
||||||
for (GTreeNode treeNode : archiveNodes) {
|
for (GTreeNode treeNode : archiveNodes) {
|
||||||
if (treeNode instanceof ProjectArchiveNode) {
|
if (treeNode instanceof ProjectArchiveNode) {
|
||||||
ProjectArchiveNode projectArchiveNode = (ProjectArchiveNode) treeNode;
|
ProjectArchiveNode projectArchiveNode = (ProjectArchiveNode) treeNode;
|
||||||
|
|
|
@ -15,13 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.tree;
|
package ghidra.app.plugin.core.datamgr.tree;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.app.util.ToolTipUtils;
|
import ghidra.app.util.ToolTipUtils;
|
||||||
|
@ -30,7 +32,7 @@ import ghidra.program.model.data.Enum;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode {
|
public class DataTypeNode extends DataTypeTreeNode {
|
||||||
private final DataType dataType;
|
private final DataType dataType;
|
||||||
private final String name;
|
private final String name;
|
||||||
private String displayName;
|
private String displayName;
|
||||||
|
@ -227,8 +229,8 @@ public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSystemNode() {
|
public boolean canDelete() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dataTypeStatusChanged() {
|
public void dataTypeStatusChanged() {
|
||||||
|
@ -268,4 +270,9 @@ public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode
|
||||||
|
|
||||||
return baseDisplayName;
|
return baseDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<GTreeNode> generateChildren() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -18,53 +17,56 @@ package ghidra.app.plugin.core.datamgr.tree;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import docking.widgets.tree.GTreeLazyNode;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single interface for unifying the handling of nodes that can be manipulated during
|
* A single interface for unifying the handling of nodes that can be manipulated during
|
||||||
* cut/copy/paste operations.
|
* cut/copy/paste operations.
|
||||||
*/
|
*/
|
||||||
public interface DataTypeTreeNode {
|
public abstract class DataTypeTreeNode extends GTreeLazyNode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this node can be cut and moved to a different location.
|
* Returns true if this node can be cut and moved to a different location.
|
||||||
* @return true if this node can be cut and moved to a different location.
|
* @return true if this node can be cut and moved to a different location.
|
||||||
*/
|
*/
|
||||||
public boolean canCut();
|
public abstract boolean canCut();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this nodes handles paste operations
|
* Returns true if this nodes handles paste operations
|
||||||
* @return true if this nodes handles paste operations
|
* @return true if this nodes handles paste operations
|
||||||
*/
|
*/
|
||||||
public boolean canPaste(List<GTreeNode> pastedNodes);
|
public abstract boolean canPaste(List<GTreeNode> pastedNodes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals to this node that it has been cut during a cut operation, for example, like during
|
* Signals to this node that it has been cut during a cut operation, for example, like during
|
||||||
* a cut/paste operation.
|
* a cut/paste operation.
|
||||||
* @param isCut true signals that the node has been cut; false that it is not cut.
|
* @param isCut true signals that the node has been cut; false that it is not cut.
|
||||||
*/
|
*/
|
||||||
public void setNodeCut(boolean isCut);
|
public abstract void setNodeCut(boolean isCut);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the node has been cut.
|
* Return true if the node has been cut.
|
||||||
* @return true if the node has been cut.
|
* @return true if the node has been cut.
|
||||||
*/
|
*/
|
||||||
public boolean isCut();
|
public abstract boolean isCut();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ArchiveNode for this tree node.
|
* Returns the ArchiveNode for this tree node.
|
||||||
* @return the ArchiveNode for this tree node.
|
* @return the ArchiveNode for this tree node.
|
||||||
*/
|
*/
|
||||||
public ArchiveNode getArchiveNode();
|
public abstract ArchiveNode getArchiveNode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this node is from an archive that can be modified.
|
* Returns true if this node is from an archive that can be modified.
|
||||||
* @return true if this node is from an archive that can be modified.
|
* @return true if this node is from an archive that can be modified.
|
||||||
*/
|
*/
|
||||||
public boolean isModifiable();
|
public abstract boolean isModifiable();
|
||||||
|
|
||||||
// TODO: bad name; can't rename it, can't delete it...it's special
|
/**
|
||||||
public boolean isSystemNode();
|
* Returns true if this node can be deleted
|
||||||
|
* @return true if this node can be deleted
|
||||||
|
*/
|
||||||
|
public abstract boolean canDelete();
|
||||||
|
|
||||||
public String getName();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,8 @@ public class DomainFileArchiveNode extends ArchiveNode {
|
||||||
|
|
||||||
private String domainFileInfoString;
|
private String domainFileInfoString;
|
||||||
|
|
||||||
public DomainFileArchiveNode(DomainFileArchive archive) {
|
public DomainFileArchiveNode(DomainFileArchive archive, ArrayPointerFilterState filterState) {
|
||||||
super(archive);
|
super(archive, filterState);
|
||||||
|
|
||||||
updateDomainFileInfo();
|
updateDomainFileInfo();
|
||||||
}
|
}
|
||||||
|
@ -109,8 +109,8 @@ public class DomainFileArchiveNode extends ArchiveNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSystemNode() {
|
public boolean canDelete() {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,8 +32,8 @@ public class FileArchiveNode extends ArchiveNode {
|
||||||
|
|
||||||
FileArchive fileArchive; // casted reference for easy access
|
FileArchive fileArchive; // casted reference for easy access
|
||||||
|
|
||||||
public FileArchiveNode(FileArchive archive) {
|
public FileArchiveNode(FileArchive archive, ArrayPointerFilterState filterState) {
|
||||||
super(archive);
|
super(archive, filterState);
|
||||||
this.fileArchive = archive;
|
this.fileArchive = archive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.HTMLUtilities;
|
||||||
public class InvalidArchiveNode extends ArchiveNode {
|
public class InvalidArchiveNode extends ArchiveNode {
|
||||||
|
|
||||||
public InvalidArchiveNode(InvalidFileArchive archive) {
|
public InvalidArchiveNode(InvalidFileArchive archive) {
|
||||||
super(archive);
|
super(archive, new ArrayPointerFilterState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,8 +73,8 @@ public class InvalidArchiveNode extends ArchiveNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSystemNode() {
|
public boolean canDelete() {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,8 +21,8 @@ import ghidra.util.HTMLUtilities;
|
||||||
|
|
||||||
public class ProgramArchiveNode extends DomainFileArchiveNode {
|
public class ProgramArchiveNode extends DomainFileArchiveNode {
|
||||||
|
|
||||||
public ProgramArchiveNode(ProgramArchive archive) {
|
public ProgramArchiveNode(ProgramArchive archive, ArrayPointerFilterState filterState) {
|
||||||
super(archive);
|
super(archive, filterState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -33,5 +33,4 @@ public class ProgramArchiveNode extends DomainFileArchiveNode {
|
||||||
}
|
}
|
||||||
return "[Unsaved New Program Archive]";
|
return "[Unsaved New Program Archive]";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,15 @@ import ghidra.util.HTMLUtilities;
|
||||||
|
|
||||||
public class ProjectArchiveNode extends DomainFileArchiveNode {
|
public class ProjectArchiveNode extends DomainFileArchiveNode {
|
||||||
|
|
||||||
public ProjectArchiveNode(ProjectArchive archive) {
|
public ProjectArchiveNode(ProjectArchive archive, ArrayPointerFilterState filterState) {
|
||||||
super(archive);
|
super(archive, filterState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dataTypeManagerChanged() {
|
protected void dataTypeManagerChanged() {
|
||||||
removeAll(); // old children are no longer valid.
|
setChildren(null); // old children are no longer valid.
|
||||||
installDataTypeManagerListener();
|
installDataTypeManagerListener();
|
||||||
nodeChanged();
|
nodeChanged();
|
||||||
fireNodeStructureChanged(this); // all the children have changed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -165,7 +165,7 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
GTreeRootNode root = tree.getRootNode();
|
GTreeNode root = tree.getViewRoot();
|
||||||
List<GTreeNode> dtNodes = new ArrayList<>();
|
List<GTreeNode> dtNodes = new ArrayList<>();
|
||||||
getDataTypeNodes(root, dtNodes);
|
getDataTypeNodes(root, dtNodes);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@ package ghidra.app.plugin.core.datamgr.util;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import docking.widgets.tree.GTreeState;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||||
|
@ -122,8 +123,8 @@ public class DataTypeTreeDeleteTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void collapseArchives(DataTypeArchiveGTree tree) {
|
private void collapseArchives(DataTypeArchiveGTree tree) {
|
||||||
GTreeRootNode root = tree.getRootNode();
|
GTreeNode root = tree.getModelRoot();
|
||||||
List<GTreeNode> children = root.getAllChildren();
|
List<GTreeNode> children = root.getChildren();
|
||||||
for (GTreeNode archive : children) {
|
for (GTreeNode archive : children) {
|
||||||
tree.collapseAll(archive);
|
tree.collapseAll(archive);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class RegisterTree extends GTree {
|
||||||
|
|
||||||
public RegisterTree() {
|
public RegisterTree() {
|
||||||
super(new RegisterTreeRootNode());
|
super(new RegisterTreeRootNode());
|
||||||
root = (RegisterTreeRootNode) getRootNode();
|
root = (RegisterTreeRootNode) getModelRoot();
|
||||||
|
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
|
|
||||||
|
@ -138,9 +138,9 @@ public class RegisterTree extends GTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class SearchableRegisterTreeNode extends AbstractGTreeNode {
|
abstract class SearchableRegisterTreeNode extends GTreeNode {
|
||||||
public GTreeNode findNode(Register register) {
|
public GTreeNode findNode(Register register) {
|
||||||
List<GTreeNode> allChildren = getAllChildren();
|
List<GTreeNode> allChildren = getChildren();
|
||||||
for (GTreeNode child : allChildren) {
|
for (GTreeNode child : allChildren) {
|
||||||
if (!(child instanceof RegisterTreeNode)) {
|
if (!(child instanceof RegisterTreeNode)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -160,9 +160,8 @@ abstract class SearchableRegisterTreeNode extends AbstractGTreeNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRootNode {
|
class RegisterTreeRootNode extends SearchableRegisterTreeNode {
|
||||||
private Register[] lastRegisters;
|
private Register[] lastRegisters;
|
||||||
private GTree tree;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon(boolean expanded) {
|
public Icon getIcon(boolean expanded) {
|
||||||
|
@ -197,7 +196,8 @@ class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRo
|
||||||
List<GTreeNode> nodes = new ArrayList<GTreeNode>();
|
List<GTreeNode> nodes = new ArrayList<GTreeNode>();
|
||||||
|
|
||||||
for (Register register : registers) {
|
for (Register register : registers) {
|
||||||
if (register.getBaseRegister() != register && !register.getParentRegister().isHidden()) {
|
if (register.getBaseRegister() != register &&
|
||||||
|
!register.getParentRegister().isHidden()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String groupName = register.getGroup();
|
String groupName = register.getGroup();
|
||||||
|
@ -217,17 +217,6 @@ class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRo
|
||||||
Collections.sort(nodes);
|
Collections.sort(nodes);
|
||||||
setChildren(nodes);
|
setChildren(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTree getGTree() {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGTree(GTree tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RegisterTreeNode extends SearchableRegisterTreeNode {
|
class RegisterTreeNode extends SearchableRegisterTreeNode {
|
||||||
|
|
|
@ -37,7 +37,8 @@ import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
import docking.widgets.pathmanager.PathManager;
|
import docking.widgets.pathmanager.PathManager;
|
||||||
import docking.widgets.pathmanager.PathManagerListener;
|
import docking.widgets.pathmanager.PathManagerListener;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.support.BreadthFirstIterator;
|
import docking.widgets.tree.support.BreadthFirstIterator;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import generic.util.Path;
|
import generic.util.Path;
|
||||||
|
@ -619,7 +620,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GTreeNode> toDelete = new LinkedList<>();
|
List<GTreeNode> toDelete = new LinkedList<>();
|
||||||
Iterator<GTreeNode> nodes = new BreadthFirstIterator(scriptCategoryTree, scriptRoot);
|
Iterator<GTreeNode> nodes = new BreadthFirstIterator(scriptRoot);
|
||||||
for (GTreeNode node : CollectionUtils.asIterable(nodes)) {
|
for (GTreeNode node : CollectionUtils.asIterable(nodes)) {
|
||||||
String[] path = getCategoryPath(node);
|
String[] path = getCategoryPath(node);
|
||||||
List<String> category = Arrays.asList(path);
|
List<String> category = Arrays.asList(path);
|
||||||
|
@ -780,7 +781,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
scriptCategoryTree.getSelectionModel().setSelectionMode(
|
scriptCategoryTree.getSelectionModel()
|
||||||
|
.setSelectionMode(
|
||||||
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||||
|
|
||||||
tableModel = new GhidraScriptTableModel(this);
|
tableModel = new GhidraScriptTableModel(this);
|
||||||
|
@ -1135,7 +1137,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||||
// with a filter, only things in the available children match the root node (this is
|
// with a filter, only things in the available children match the root node (this is
|
||||||
// so filtering in the tree will show all matching results when the
|
// so filtering in the tree will show all matching results when the
|
||||||
// root is selected, instead of all results).
|
// root is selected, instead of all results).
|
||||||
GTreeRootNode rootNode = scriptCategoryTree.getRootNode();
|
GTreeNode rootNode = scriptCategoryTree.getViewRoot();
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode node : children) {
|
for (GTreeNode node : children) {
|
||||||
String[] path = getCategoryPath(node);
|
String[] path = getCategoryPath(node);
|
||||||
|
|
|
@ -19,14 +19,14 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.AbstractGTreeRootNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptUtil;
|
||||||
import ghidra.app.script.ScriptInfo;
|
import ghidra.app.script.ScriptInfo;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
class RootNode extends AbstractGTreeRootNode {
|
class RootNode extends GTreeNode {
|
||||||
private static Icon icon = ResourceManager.loadImage("images/play.png");
|
private static Icon icon = ResourceManager.loadImage("images/play.png");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,7 +65,7 @@ class RootNode extends AbstractGTreeRootNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GTreeNode getChildRegardlessOfFilter(GTreeNode parent, String name) {
|
private GTreeNode getChildRegardlessOfFilter(GTreeNode parent, String name) {
|
||||||
List<GTreeNode> children = parent.getAllChildren();
|
List<GTreeNode> children = parent.getChildren();
|
||||||
for (GTreeNode child : children) {
|
for (GTreeNode child : children) {
|
||||||
if (child.getName().equals(name)) {
|
if (child.getName().equals(name)) {
|
||||||
return child;
|
return child;
|
||||||
|
@ -75,7 +75,7 @@ class RootNode extends AbstractGTreeRootNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertSorted(GTreeNode parent, GTreeNode newChild) {
|
private void insertSorted(GTreeNode parent, GTreeNode newChild) {
|
||||||
List<GTreeNode> allChildren = parent.getAllChildren();
|
List<GTreeNode> allChildren = parent.getChildren();
|
||||||
for (GTreeNode child : allChildren) {
|
for (GTreeNode child : allChildren) {
|
||||||
String nodeName = child.getName();
|
String nodeName = child.getName();
|
||||||
String newNodeName = newChild.getName();
|
String newNodeName = newChild.getName();
|
||||||
|
|
|
@ -17,10 +17,10 @@ package ghidra.app.plugin.core.script;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.AbstractGTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class ScriptCategoryNode extends AbstractGTreeNode {
|
public class ScriptCategoryNode extends GTreeNode {
|
||||||
|
|
||||||
private static Icon OPEN_FOLDER = ResourceManager.loadImage("images/openSmallFolder.png");
|
private static Icon OPEN_FOLDER = ResourceManager.loadImage("images/openSmallFolder.png");
|
||||||
private static Icon CLOSED_FOLDER = ResourceManager.loadImage("images/closedSmallFolder.png");
|
private static Icon CLOSED_FOLDER = ResourceManager.loadImage("images/closedSmallFolder.png");
|
||||||
|
@ -49,7 +49,7 @@ public class ScriptCategoryNode extends AbstractGTreeNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLeaf() {
|
public boolean isLeaf() {
|
||||||
return getAllChildCount() == 0;
|
return getChildCount() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ import java.awt.Component;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.support.GTreeRenderer;
|
import docking.widgets.tree.support.GTreeRenderer;
|
||||||
import ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
|
import ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
|
||||||
import ghidra.app.util.SymbolInspector;
|
import ghidra.app.util.SymbolInspector;
|
||||||
|
@ -34,7 +35,7 @@ public class SymbolGTree extends GTree {
|
||||||
private GTreeNode armedNode;
|
private GTreeNode armedNode;
|
||||||
private SymbolInspector symbolInspector;
|
private SymbolInspector symbolInspector;
|
||||||
|
|
||||||
public SymbolGTree(GTreeRootNode root, SymbolTreePlugin plugin) {
|
public SymbolGTree(GTreeNode root, SymbolTreePlugin plugin) {
|
||||||
super(root);
|
super(root);
|
||||||
symbolInspector = new SymbolInspector(plugin.getTool(), this);
|
symbolInspector = new SymbolInspector(plugin.getTool(), this);
|
||||||
setShowsRootHandles(true);
|
setShowsRootHandles(true);
|
||||||
|
|
|
@ -137,7 +137,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.getRootNode();
|
GTreeNode oldRootNode = tree.getModelRoot();
|
||||||
tree.setProgram(rootNode.getProgram());
|
tree.setProgram(rootNode.getProgram());
|
||||||
tree.setRootNode(rootNode);
|
tree.setRootNode(rootNode);
|
||||||
|
|
||||||
|
@ -362,8 +362,9 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rebuildTree() {
|
private void rebuildTree() {
|
||||||
GTreeNode node = tree.getRootNode();
|
SymbolTreeRootNode node = (SymbolTreeRootNode) tree.getModelRoot();
|
||||||
node.removeAll();
|
node.setChildren(null);
|
||||||
|
tree.refilterLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void symbolChanged(Symbol symbol, String oldName) {
|
private void symbolChanged(Symbol symbol, String oldName) {
|
||||||
|
@ -446,8 +447,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GTreeNode node = tree.getRootNode();
|
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) tree.getViewRoot();
|
||||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
|
||||||
tree.runTask(new SearchTask(tree, rootNode, symbol));
|
tree.runTask(new SearchTask(tree, rootNode, symbol));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,12 +535,12 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
@Override
|
@Override
|
||||||
void doRun(TaskMonitor monitor) throws CancelledException {
|
void doRun(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
GTreeNode node = tree.getRootNode();
|
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) tree.getModelRoot();
|
||||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
|
||||||
|
|
||||||
// the symbol may have been deleted while we are processing bulk changes
|
// the symbol may have been deleted while we are processing bulk changes
|
||||||
if (symbol.checkIsValid()) {
|
if (symbol.checkIsValid()) {
|
||||||
rootNode.symbolAdded(symbol);
|
GTreeNode newNode = rootNode.symbolAdded(symbol);
|
||||||
|
tree.refilterLater(newNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -562,15 +562,14 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
@Override
|
@Override
|
||||||
void doRun(TaskMonitor monitor) throws CancelledException {
|
void doRun(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
GTreeNode node = tree.getRootNode();
|
SymbolTreeRootNode root = (SymbolTreeRootNode) tree.getModelRoot();
|
||||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
root.symbolRemoved(symbol, monitor);
|
||||||
|
|
||||||
rootNode.symbolRemoved(symbol, oldName, monitor);
|
|
||||||
|
|
||||||
// the symbol may have been deleted while we are processing bulk changes
|
// the symbol may have been deleted while we are processing bulk changes
|
||||||
if (symbol.checkIsValid()) {
|
if (symbol.checkIsValid()) {
|
||||||
rootNode.symbolAdded(symbol);
|
root.symbolAdded(symbol);
|
||||||
}
|
}
|
||||||
|
tree.refilterLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,9 +581,9 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void doRun(TaskMonitor monitor) throws CancelledException {
|
void doRun(TaskMonitor monitor) throws CancelledException {
|
||||||
GTreeNode node = tree.getRootNode();
|
SymbolTreeRootNode root = (SymbolTreeRootNode) tree.getModelRoot();
|
||||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
root.symbolRemoved(symbol, monitor);
|
||||||
rootNode.symbolRemoved(symbol, monitor);
|
tree.refilterLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,18 +57,17 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void symbolAdded(Symbol symbol) {
|
public SymbolNode symbolAdded(Symbol symbol) {
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsSymbol(symbol)) {
|
if (!supportsSymbol(symbol)) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol.getSymbolType() == symbolCategory.getSymbolType()) {
|
if (symbol.getSymbolType() == symbolCategory.getSymbolType()) {
|
||||||
doAddSymbol(symbol, this); // add new Class symbol
|
return doAddSymbol(symbol, this); // add new Class symbol
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if the symbol is in a class namespace
|
// see if the symbol is in a class namespace
|
||||||
|
@ -77,9 +76,9 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
||||||
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
|
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
|
||||||
GTreeNode parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
|
GTreeNode parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||||
if (parentNode == null) {
|
if (parentNode == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
doAddSymbol(symbol, parentNode);
|
return doAddSymbol(symbol, parentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,6 +86,7 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
List<GTreeNode> list = new ArrayList<GTreeNode>();
|
List<GTreeNode> list = new ArrayList<GTreeNode>();
|
||||||
|
|
||||||
|
monitor.initialize(symbolTable.getNumSymbols());
|
||||||
SymbolType symbolType = symbolCategory.getSymbolType();
|
SymbolType symbolType = symbolCategory.getSymbolType();
|
||||||
SymbolIterator it = symbolTable.getDefinedSymbols();
|
SymbolIterator it = symbolTable.getDefinedSymbols();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
|
@ -95,6 +95,7 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
list.add(SymbolNode.createNode(s, program));
|
list.add(SymbolNode.createNode(s, program));
|
||||||
}
|
}
|
||||||
|
monitor.incrementProgress(1);
|
||||||
}
|
}
|
||||||
Collections.sort(list, getChildrenComparator());
|
Collections.sort(list, getChildrenComparator());
|
||||||
return list;
|
return list;
|
||||||
|
|
|
@ -27,10 +27,10 @@ import ghidra.program.model.symbol.Symbol;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class ClassSymbolNode extends SymbolNode {
|
public class ClassSymbolNode extends SymbolNode {
|
||||||
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
static final DataFlavor LOCAL_DATA_FLAVOR =
|
||||||
"Symbol Tree Data Flavor - Local Classes");
|
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Classes");
|
||||||
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
static final DataFlavor GLOBAL_DATA_FLAVOR =
|
||||||
"Symbol Tree Data Flavor - Global Classes");
|
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Classes");
|
||||||
|
|
||||||
private static Icon CLASS_ICON = ResourceManager.loadImage("images/class.png");
|
private static Icon CLASS_ICON = ResourceManager.loadImage("images/class.png");
|
||||||
private static Icon DISABLED_CLASS_ICONDISABLED_CLASS_ICON =
|
private static Icon DISABLED_CLASS_ICONDISABLED_CLASS_ICON =
|
||||||
|
|
|
@ -27,12 +27,12 @@ import ghidra.program.model.symbol.Symbol;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class CodeSymbolNode extends SymbolNode {
|
public class CodeSymbolNode extends SymbolNode {
|
||||||
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
static final DataFlavor LOCAL_DATA_FLAVOR =
|
||||||
"Symbol Tree Data Flavor - Local Labels");
|
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Labels");
|
||||||
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
static final DataFlavor GLOBAL_DATA_FLAVOR =
|
||||||
"Symbol Tree Data Flavor - Global Labels");
|
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Labels");
|
||||||
static final DataFlavor EXTERNAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
static final DataFlavor EXTERNAL_DATA_FLAVOR =
|
||||||
"Symbol Tree Data Flavor - External Data");
|
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - External Data");
|
||||||
|
|
||||||
private static final Icon CODE_ICON = ResourceManager.loadImage("images/label.png");
|
private static final Icon CODE_ICON = ResourceManager.loadImage("images/label.png");
|
||||||
private static final Icon PINNED_ICON = ResourceManager.loadImage("images/pin.png");
|
private static final Icon PINNED_ICON = ResourceManager.loadImage("images/pin.png");
|
||||||
|
|
|
@ -102,31 +102,30 @@ class FunctionCategoryNode extends SymbolCategoryNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void symbolAdded(Symbol symbol) {
|
public SymbolNode symbolAdded(Symbol symbol) {
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsSymbol(symbol)) {
|
if (!supportsSymbol(symbol)) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// variables and parameters will be beneath function nodes, and our parent method
|
// variables and parameters will be beneath function nodes, and our parent method
|
||||||
// will find them
|
// will find them
|
||||||
if (isVariableParameterOrCodeSymbol(symbol)) {
|
if (isVariableParameterOrCodeSymbol(symbol)) {
|
||||||
super.symbolAdded(symbol);
|
return super.symbolAdded(symbol);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this namespace will be beneath function nodes, and our parent method will find them
|
// this namespace will be beneath function nodes, and our parent method will find them
|
||||||
if (isChildNamespaceOfFunction(symbol)) {
|
if (isChildNamespaceOfFunction(symbol)) {
|
||||||
super.symbolAdded(symbol);
|
return super.symbolAdded(symbol);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...otherwise, we have a function and we need to add it as a child of our parent node
|
// ...otherwise, we have a function and we need to add it as a child of our parent node
|
||||||
GTreeNode newNode = SymbolNode.createNode(symbol, program);
|
SymbolNode newNode = SymbolNode.createNode(symbol, program);
|
||||||
doAddNode(this, newNode);
|
doAddNode(this, newNode);
|
||||||
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isChildNamespaceOfFunction(Symbol symbol) {
|
private boolean isChildNamespaceOfFunction(Symbol symbol) {
|
||||||
|
@ -171,7 +170,7 @@ class FunctionCategoryNode extends SymbolCategoryNode {
|
||||||
public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren, TaskMonitor monitor) {
|
public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren, TaskMonitor monitor) {
|
||||||
|
|
||||||
// if we don't have to loadChildren and we are not loaded get out.
|
// if we don't have to loadChildren and we are not loaded get out.
|
||||||
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
|
if (!loadChildren && !isLoaded()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,21 +85,22 @@ public class LabelCategoryNode extends SymbolCategoryNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void symbolAdded(Symbol symbol) {
|
public SymbolNode symbolAdded(Symbol symbol) {
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only include global symbols
|
// only include global symbols
|
||||||
if (!symbol.isGlobal()) {
|
if (!symbol.isGlobal()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsSymbol(symbol)) {
|
if (!supportsSymbol(symbol)) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
GTreeNode newNode = SymbolNode.createNode(symbol, program);
|
SymbolNode newNode = SymbolNode.createNode(symbol, program);
|
||||||
doAddNode(this, newNode);
|
doAddNode(this, newNode);
|
||||||
|
return newNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,5 +121,4 @@ public class LibrarySymbolNode extends SymbolNode {
|
||||||
public Namespace getNamespace() {
|
public Namespace getNamespace() {
|
||||||
return (Library) symbol.getObject();
|
return (Library) symbol.getObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,17 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.symboltree.nodes;
|
package ghidra.app.plugin.core.symboltree.nodes;
|
||||||
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.symbol.Symbol;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
|
|
||||||
public class LocalVariableSymbolNode extends SymbolNode {
|
public class LocalVariableSymbolNode extends SymbolNode {
|
||||||
|
|
||||||
public static final Icon LOCAL_VARIABLE_ICON = ResourceManager.loadImage("images/LocalVariable.gif");
|
public static final Icon LOCAL_VARIABLE_ICON =
|
||||||
|
ResourceManager.loadImage("images/LocalVariable.gif");
|
||||||
|
|
||||||
LocalVariableSymbolNode(Program program, Symbol symbol) {
|
LocalVariableSymbolNode(Program program, Symbol symbol) {
|
||||||
super(program, symbol);
|
super(program, symbol);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,23 +15,22 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.symboltree.nodes;
|
package ghidra.app.plugin.core.symboltree.nodes;
|
||||||
|
|
||||||
import ghidra.app.util.SelectionTransferData;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.symbol.Namespace;
|
|
||||||
import ghidra.program.model.symbol.Symbol;
|
|
||||||
|
|
||||||
import java.awt.datatransfer.DataFlavor;
|
import java.awt.datatransfer.DataFlavor;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
|
import ghidra.app.util.SelectionTransferData;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.symbol.Namespace;
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class NamespaceSymbolNode extends SymbolNode {
|
public class NamespaceSymbolNode extends SymbolNode {
|
||||||
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
static final DataFlavor LOCAL_DATA_FLAVOR =
|
||||||
"Symbol Tree Data Flavor - Local Namespaces");
|
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Namespaces");
|
||||||
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
static final DataFlavor GLOBAL_DATA_FLAVOR =
|
||||||
"Symbol Tree Data Flavor - Global Namespaces");
|
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Namespaces");
|
||||||
|
|
||||||
public static final Icon NAMESPACE_ICON = ResourceManager.loadImage("images/Namespace.gif");
|
public static final Icon NAMESPACE_ICON = ResourceManager.loadImage("images/Namespace.gif");
|
||||||
public static final Icon DISABLED_NAMESPACE_ICON =
|
public static final Icon DISABLED_NAMESPACE_ICON =
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.*;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.AbstractGTreeNode;
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.program.model.symbol.Namespace;
|
import ghidra.program.model.symbol.Namespace;
|
||||||
import ghidra.util.datastruct.IntArray;
|
import ghidra.util.datastruct.IntArray;
|
||||||
|
@ -32,7 +31,7 @@ import resources.ResourceManager;
|
||||||
* See {@link #computeChildren(List, int, GTreeNode, int, TaskMonitor)} for details on
|
* See {@link #computeChildren(List, int, GTreeNode, int, TaskMonitor)} for details on
|
||||||
* how this class works.
|
* how this class works.
|
||||||
*/
|
*/
|
||||||
public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNode {
|
public class OrganizationNode extends SymbolTreeNode {
|
||||||
static final Comparator<GTreeNode> COMPARATOR = new OrganizationNodeComparator();
|
static final Comparator<GTreeNode> COMPARATOR = new OrganizationNodeComparator();
|
||||||
|
|
||||||
private static Icon OPEN_FOLDER_GROUP_ICON =
|
private static Icon OPEN_FOLDER_GROUP_ICON =
|
||||||
|
@ -44,27 +43,28 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You cannot instantiate this class directly, instead use the factory method below
|
* You cannot instantiate this class directly, instead use the factory method below
|
||||||
* {@link #organize(List, int)}.
|
* {@link #organize(List, int, TaskMonitor)}
|
||||||
* @throws CancelledException
|
* @throws CancelledException if the operation is cancelled
|
||||||
*/
|
*/
|
||||||
private OrganizationNode(List<GTreeNode> list, int max, int parentLevel, GTreeNode parent,
|
private OrganizationNode(List<GTreeNode> list, int max, int parentLevel, TaskMonitor monitor)
|
||||||
int indexInParent, TaskMonitor monitor) throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
setChildren(computeChildren(list, max, this, parentLevel, monitor));
|
setChildren(computeChildren(list, max, this, parentLevel, monitor));
|
||||||
|
|
||||||
GTreeNode child = getChild(0);
|
GTreeNode child = getChild(0);
|
||||||
baseName = child.getName().substring(0, getPrefixSizeForGrouping(getAllChildren(), 1) + 1);
|
baseName = child.getName().substring(0, getPrefixSizeForGrouping(getChildren(), 1) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory method for creating OrganizationNode objects. See
|
* A factory method for creating OrganizationNode objects.
|
||||||
* {@link #computeChildren(List, int, GTreeNode, int)} for details .
|
* See {@link #computeChildren(List, int, GTreeNode, int, TaskMonitor)}
|
||||||
*
|
*
|
||||||
* @param nodes the original list of child nodes to be subdivided.
|
* @param nodes the original list of child nodes to be subdivided.
|
||||||
* @param max The max number of child nodes per parent node at any node level.
|
* @param max The max number of child nodes per parent node at any node level.
|
||||||
|
* @param monitor the task monitor used to cancel this operation
|
||||||
* @return A list of nodes that is based upon the given list, but subdivided as needed.
|
* @return A list of nodes that is based upon the given list, but subdivided as needed.
|
||||||
* @throws CancelledException
|
* @throws CancelledException if the operation is cancelled
|
||||||
* @see #computeChildren(List, int, GTreeNode, int)
|
* @see #computeChildren(List, int, GTreeNode, int, TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public static List<GTreeNode> organize(List<GTreeNode> nodes, int max, TaskMonitor monitor)
|
public static List<GTreeNode> organize(List<GTreeNode> nodes, int max, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
@ -106,19 +106,14 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
||||||
* @param maxNodes The max number of child nodes per parent node at any node level.
|
* @param maxNodes The max number of child nodes per parent node at any node level.
|
||||||
* @param parent The parent of the given <tt>children</tt>
|
* @param parent The parent of the given <tt>children</tt>
|
||||||
* @param parentLevel node depth in the tree of <b>Organization</b> nodes.
|
* @param parentLevel node depth in the tree of <b>Organization</b> nodes.
|
||||||
* @return the given <tt>list</tt> subgrouped as outlined above.
|
* @return the given <tt>list</tt> sub-grouped as outlined above.
|
||||||
* @throws CancelledException
|
* @throws CancelledException if the operation is cancelled
|
||||||
*/
|
*/
|
||||||
private static List<GTreeNode> computeChildren(List<GTreeNode> list, int maxNodes,
|
private static List<GTreeNode> computeChildren(List<GTreeNode> list, int maxNodes,
|
||||||
GTreeNode parent, int parentLevel, TaskMonitor monitor) throws CancelledException {
|
GTreeNode parent, int parentLevel, TaskMonitor monitor) throws CancelledException {
|
||||||
List<GTreeNode> children;
|
List<GTreeNode> children;
|
||||||
if (list.size() <= maxNodes) {
|
if (list.size() <= maxNodes) {
|
||||||
children = new ArrayList<>(list);
|
children = new ArrayList<>(list);
|
||||||
Iterator<GTreeNode> it = list.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
monitor.checkCanceled();
|
|
||||||
parent.addNode(it.next());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int characterOffset = getPrefixSizeForGrouping(list, maxNodes);
|
int characterOffset = getPrefixSizeForGrouping(list, maxNodes);
|
||||||
|
@ -133,13 +128,13 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
String str = list.get(i).getName();
|
String str = list.get(i).getName();
|
||||||
if (stringsDiffer(prevStr, str, characterOffset)) {
|
if (stringsDiffer(prevStr, str, characterOffset)) {
|
||||||
addNode(children, list, start, i - 1, maxNodes, parent, characterOffset,
|
addNode(children, list, start, i - 1, maxNodes, characterOffset,
|
||||||
monitor);
|
monitor);
|
||||||
start = i;
|
start = i;
|
||||||
}
|
}
|
||||||
prevStr = str;
|
prevStr = str;
|
||||||
}
|
}
|
||||||
addNode(children, list, start, end - 1, maxNodes, parent, characterOffset, monitor);
|
addNode(children, list, start, end - 1, maxNodes, characterOffset, monitor);
|
||||||
}
|
}
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
@ -148,22 +143,20 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
||||||
if (s1.length() <= diffLevel || s2.length() <= diffLevel) {
|
if (s1.length() <= diffLevel || s2.length() <= diffLevel) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return s1.substring(0, diffLevel + 1).compareToIgnoreCase(
|
return s1.substring(0, diffLevel + 1)
|
||||||
|
.compareToIgnoreCase(
|
||||||
s2.substring(0, diffLevel + 1)) != 0;
|
s2.substring(0, diffLevel + 1)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addNode(List<GTreeNode> children, List<GTreeNode> list, int start, int end,
|
private static void addNode(List<GTreeNode> children, List<GTreeNode> list, int start, int end,
|
||||||
int max, GTreeNode parent, int diffLevel, TaskMonitor monitor)
|
int max, int diffLevel, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
if (end - start > 0) {
|
if (end - start > 0) {
|
||||||
children.add(new OrganizationNode(list.subList(start, end + 1), max, diffLevel, parent,
|
children.add(new OrganizationNode(list.subList(start, end + 1), max, diffLevel,
|
||||||
children.size(), monitor));
|
monitor));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GTreeNode node = list.get(start);
|
GTreeNode node = list.get(start);
|
||||||
if (parent != null) {
|
|
||||||
parent.addNode(node);
|
|
||||||
}
|
|
||||||
children.add(node);
|
children.add(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,7 +285,7 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
||||||
* @param newNode the node to insert.
|
* @param newNode the node to insert.
|
||||||
*/
|
*/
|
||||||
public void insertNode(GTreeNode newNode) {
|
public void insertNode(GTreeNode newNode) {
|
||||||
int index = Collections.binarySearch(getAllChildren(), newNode, getChildrenComparator());
|
int index = Collections.binarySearch(getChildren(), newNode, getChildrenComparator());
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
// found a match
|
// found a match
|
||||||
GTreeNode matchingNode = getChild(index);
|
GTreeNode matchingNode = getChild(index);
|
||||||
|
@ -326,7 +319,7 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
||||||
// the old name will find the right parent, but not the actual current node, as
|
// the old name will find the right parent, but not the actual current node, as
|
||||||
// it has a new name.
|
// it has a new name.
|
||||||
//
|
//
|
||||||
return SymbolTreeNode.super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
return super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -365,4 +358,9 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
|
||||||
|
// not used, children generated in constructor
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.symboltree.nodes;
|
package ghidra.app.plugin.core.symboltree.nodes;
|
||||||
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.symbol.Symbol;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class ParameterSymbolNode extends SymbolNode {
|
public class ParameterSymbolNode extends SymbolNode {
|
||||||
|
|
|
@ -18,7 +18,7 @@ package ghidra.app.plugin.core.symboltree.nodes;
|
||||||
import java.awt.datatransfer.DataFlavor;
|
import java.awt.datatransfer.DataFlavor;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.symboltree.SymbolCategory;
|
import ghidra.app.plugin.core.symboltree.SymbolCategory;
|
||||||
import ghidra.program.model.address.GlobalNamespace;
|
import ghidra.program.model.address.GlobalNamespace;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -27,7 +27,7 @@ import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
import ghidra.util.task.TaskMonitorAdapter;
|
||||||
|
|
||||||
public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
public abstract class SymbolCategoryNode extends SymbolTreeNode {
|
||||||
protected SymbolCategory symbolCategory;
|
protected SymbolCategory symbolCategory;
|
||||||
protected SymbolTable symbolTable;
|
protected SymbolTable symbolTable;
|
||||||
protected GlobalNamespace globalNamespace;
|
protected GlobalNamespace globalNamespace;
|
||||||
|
@ -73,10 +73,12 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
||||||
List<GTreeNode> list = new ArrayList<>();
|
List<GTreeNode> list = new ArrayList<>();
|
||||||
|
|
||||||
SymbolType symbolType = symbolCategory.getSymbolType();
|
SymbolType symbolType = symbolCategory.getSymbolType();
|
||||||
|
monitor.initialize(symbolTable.getNumSymbols());
|
||||||
SymbolIterator it =
|
SymbolIterator it =
|
||||||
globalOnly ? symbolTable.getSymbols(globalNamespace) : symbolTable.getSymbolIterator();
|
globalOnly ? symbolTable.getSymbols(globalNamespace) : symbolTable.getSymbolIterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Symbol s = it.next();
|
Symbol s = it.next();
|
||||||
|
monitor.incrementProgress(1);
|
||||||
if (s != null && (s.getSymbolType() == symbolType)) {
|
if (s != null && (s.getSymbolType() == symbolType)) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
list.add(SymbolNode.createNode(s, program));
|
list.add(SymbolNode.createNode(s, program));
|
||||||
|
@ -151,20 +153,19 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
||||||
dataFlavor == ClassSymbolNode.LOCAL_DATA_FLAVOR;
|
dataFlavor == ClassSymbolNode.LOCAL_DATA_FLAVOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void symbolAdded(Symbol symbol) {
|
public SymbolNode symbolAdded(Symbol symbol) {
|
||||||
|
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsSymbol(symbol)) {
|
if (!supportsSymbol(symbol)) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
GTreeNode parentNode = this;
|
GTreeNode parentNode = this;
|
||||||
if (symbol.isGlobal()) {
|
if (symbol.isGlobal()) {
|
||||||
doAddSymbol(symbol, parentNode);
|
return doAddSymbol(symbol, parentNode);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Namespace parentNamespace = symbol.getParentNamespace();
|
Namespace parentNamespace = symbol.getParentNamespace();
|
||||||
|
@ -172,26 +173,27 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
||||||
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
|
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
|
||||||
parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
|
parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||||
if (parentNode == null) {
|
if (parentNode == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
doAddSymbol(symbol, parentNode);
|
return doAddSymbol(symbol, parentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doAddSymbol(Symbol symbol, GTreeNode parentNode) {
|
protected SymbolNode doAddSymbol(Symbol symbol, GTreeNode parentNode) {
|
||||||
if (!((AbstractGTreeNode) parentNode).isChildrenLoadedOrInProgress()) {
|
if (!parentNode.isLoaded()) {
|
||||||
return; // the node's not open, we don't care
|
return null; // the node's not open, we don't care
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolNode newNode = SymbolNode.createNode(symbol, program);
|
SymbolNode newNode = SymbolNode.createNode(symbol, program);
|
||||||
doAddNode(parentNode, newNode);
|
doAddNode(parentNode, newNode);
|
||||||
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doAddNode(GTreeNode parentNode, GTreeNode newNode) {
|
protected void doAddNode(GTreeNode parentNode, GTreeNode newNode) {
|
||||||
|
|
||||||
SymbolTreeNode symbolTreeNode = (SymbolTreeNode) parentNode;
|
SymbolTreeNode symbolTreeNode = (SymbolTreeNode) parentNode;
|
||||||
Comparator<GTreeNode> comparator = symbolTreeNode.getChildrenComparator();
|
Comparator<GTreeNode> comparator = symbolTreeNode.getChildrenComparator();
|
||||||
List<GTreeNode> children = parentNode.getAllChildren();
|
List<GTreeNode> children = parentNode.getChildren();
|
||||||
int index = Collections.binarySearch(children, newNode, comparator);
|
int index = Collections.binarySearch(children, newNode, comparator);
|
||||||
if (index >= 0) { // found a match
|
if (index >= 0) { // found a match
|
||||||
GTreeNode matchingNode = getChild(index);
|
GTreeNode matchingNode = getChild(index);
|
||||||
|
@ -215,7 +217,7 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
||||||
}
|
}
|
||||||
|
|
||||||
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
|
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (!isLoaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ import java.util.*;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeSlowLoadingNode;
|
|
||||||
import ghidra.app.cmd.label.CreateNamespacesCmd;
|
import ghidra.app.cmd.label.CreateNamespacesCmd;
|
||||||
import ghidra.app.util.SymbolPath;
|
import ghidra.app.util.SymbolPath;
|
||||||
import ghidra.program.model.address.GlobalNamespace;
|
import ghidra.program.model.address.GlobalNamespace;
|
||||||
|
@ -34,7 +33,7 @@ import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
import ghidra.util.exception.InvalidInputException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
public class SymbolNode extends SymbolTreeNode {
|
||||||
|
|
||||||
protected final Program program;
|
protected final Program program;
|
||||||
protected final Symbol symbol;
|
protected final Symbol symbol;
|
||||||
|
@ -42,6 +41,7 @@ public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
||||||
private boolean isCut;
|
private boolean isCut;
|
||||||
|
|
||||||
SymbolNode(Program program, Symbol symbol) {
|
SymbolNode(Program program, Symbol symbol) {
|
||||||
|
super();
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.symbol = symbol;
|
this.symbol = symbol;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SymbolTreeNode.super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
return super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SymbolNode createKeyNode(Symbol symbol, String searchSymbolName,
|
public static SymbolNode createKeyNode(Symbol symbol, String searchSymbolName,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.datatransfer.DataFlavor;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import docking.widgets.tree.GTreeSlowLoadingNode;
|
||||||
import ghidra.app.plugin.core.symboltree.SymbolTreeProvider;
|
import ghidra.app.plugin.core.symboltree.SymbolTreeProvider;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -27,10 +28,10 @@ import ghidra.program.model.symbol.Symbol;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for all nodes that live in the {@link SymbolTreeProvider Symbol Tree}.
|
* Base class for all nodes that live in the {@link SymbolTreeProvider Symbol Tree}.
|
||||||
*
|
*
|
||||||
* <p>All nodes will provide a way to search for the node that represents a given symbol. The
|
* <p>All nodes will provide a way to search for the node that represents a given symbol. The
|
||||||
* 'find' logic lives in this interface so all nodes have this capability. Some subclasses
|
* 'find' logic lives in this class so all nodes have this capability. Some subclasses
|
||||||
* of this interface, those with the potential for thousands of children, will break their
|
* of this interface, those with the potential for thousands of children, will break their
|
||||||
* children up into subgroups by name. The search algorithm in this class will uses each
|
* children up into subgroups by name. The search algorithm in this class will uses each
|
||||||
* node's {@link #getChildrenComparator()} method in order to be able to find the correct
|
* node's {@link #getChildrenComparator()} method in order to be able to find the correct
|
||||||
|
@ -38,7 +39,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* to keep its default {@link GTreeNode#compareTo(GTreeNode)} method, while allow each
|
* to keep its default {@link GTreeNode#compareTo(GTreeNode)} method, while allow each
|
||||||
* parent node to sort its children differently.
|
* parent node to sort its children differently.
|
||||||
*/
|
*/
|
||||||
public interface SymbolTreeNode {
|
public abstract class SymbolTreeNode extends GTreeSlowLoadingNode {
|
||||||
|
|
||||||
public static final int MAX_CHILD_NODES = 40;
|
public static final int MAX_CHILD_NODES = 40;
|
||||||
|
|
||||||
|
@ -94,39 +95,39 @@ public interface SymbolTreeNode {
|
||||||
* Returns true if this node can be cut and moved to a different location.
|
* Returns true if this node can be cut and moved to a different location.
|
||||||
* @return true if this node can be cut and moved to a different location.
|
* @return true if this node can be cut and moved to a different location.
|
||||||
*/
|
*/
|
||||||
public boolean canCut();
|
public abstract boolean canCut();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this nodes handles paste operations
|
* Returns true if this nodes handles paste operations
|
||||||
* @return true if this nodes handles paste operations
|
* @return true if this nodes handles paste operations
|
||||||
*/
|
*/
|
||||||
public boolean canPaste(List<GTreeNode> pastedNodes);
|
public abstract boolean canPaste(List<GTreeNode> pastedNodes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals to this node that it has been cut during a cut operation, for example, like during
|
* Signals to this node that it has been cut during a cut operation, for example, like during
|
||||||
* a cut/paste operation.
|
* a cut/paste operation.
|
||||||
* @param isCut true signals that the node has been cut; false that it is not cut.
|
* @param isCut true signals that the node has been cut; false that it is not cut.
|
||||||
*/
|
*/
|
||||||
public void setNodeCut(boolean isCut);
|
public abstract void setNodeCut(boolean isCut);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the node has been cut.
|
* Return true if the node has been cut.
|
||||||
* @return true if the node has been cut.
|
* @return true if the node has been cut.
|
||||||
*/
|
*/
|
||||||
public boolean isCut();
|
public abstract boolean isCut();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the data flavor that this node supports for dragging.
|
* Gets the data flavor that this node supports for dragging.
|
||||||
* @return the data flavor that this node supports for dragging.
|
* @return the data flavor that this node supports for dragging.
|
||||||
*/
|
*/
|
||||||
public DataFlavor getNodeDataFlavor();
|
public abstract DataFlavor getNodeDataFlavor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this node can accept any of the given data flavors for dropping.
|
* Returns true if this node can accept any of the given data flavors for dropping.
|
||||||
* @param dataFlavors the data flavors of an object being dragged.
|
* @param dataFlavors the data flavors of an object being dragged.
|
||||||
* @return true if this node can accept any of the given data flavors for dropping.
|
* @return true if this node can accept any of the given data flavors for dropping.
|
||||||
*/
|
*/
|
||||||
public boolean supportsDataFlavors(DataFlavor[] dataFlavors);
|
public abstract boolean supportsDataFlavors(DataFlavor[] dataFlavors);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the namespace for this symbol tree node. Not all implementations contain symbols,
|
* Returns the namespace for this symbol tree node. Not all implementations contain symbols,
|
||||||
|
@ -134,7 +135,7 @@ public interface SymbolTreeNode {
|
||||||
* namespace.
|
* namespace.
|
||||||
* @return the namespace for this symbol tree node.
|
* @return the namespace for this symbol tree node.
|
||||||
*/
|
*/
|
||||||
public Namespace getNamespace();
|
public abstract Namespace getNamespace();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the comparator used to sort the children of this node. This node will still
|
* Returns the comparator used to sort the children of this node. This node will still
|
||||||
|
@ -143,7 +144,7 @@ public interface SymbolTreeNode {
|
||||||
*
|
*
|
||||||
* @return the comparator used to sort this node's children
|
* @return the comparator used to sort this node's children
|
||||||
*/
|
*/
|
||||||
default public Comparator<GTreeNode> getChildrenComparator() {
|
public Comparator<GTreeNode> getChildrenComparator() {
|
||||||
return DEFAULT_NODE_COMPARATOR;
|
return DEFAULT_NODE_COMPARATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +153,7 @@ public interface SymbolTreeNode {
|
||||||
*
|
*
|
||||||
* @return the symbol for this node; null if it not associated with a symbol
|
* @return the symbol for this node; null if it not associated with a symbol
|
||||||
*/
|
*/
|
||||||
default public Symbol getSymbol() {
|
public Symbol getSymbol() {
|
||||||
// We use an odd inheritance hierarchy, where all nodes share this interface. Not all
|
// We use an odd inheritance hierarchy, where all nodes share this interface. Not all
|
||||||
// nodes have symbols, like a category node. Stub this method out here and allow
|
// nodes have symbols, like a category node. Stub this method out here and allow
|
||||||
// symbol nodes to return their value.
|
// symbol nodes to return their value.
|
||||||
|
@ -162,13 +163,9 @@ public interface SymbolTreeNode {
|
||||||
/**
|
/**
|
||||||
* Locates the node that contains the given symbol.
|
* Locates the node that contains the given symbol.
|
||||||
*
|
*
|
||||||
* <p><b>Note: </b>This is can degenerate into a brute-force search algorithm, but works in
|
* <p><b>Note: </b>This can degenerate into a brute-force search algorithm, but works in
|
||||||
* all normal cases using a binary search.
|
* all normal cases using a binary search.
|
||||||
*
|
*
|
||||||
* <p>Implementation Note: It is a bit unusual to put this logic into a <code>default</code>
|
|
||||||
* methods. However, this code needed to be shared by classes that do not share a
|
|
||||||
* concrete hierarchy, but do share this interface.
|
|
||||||
*
|
|
||||||
* @param key the node used to find an existing node. This node is a node created that is
|
* @param key the node used to find an existing node. This node is a node created that is
|
||||||
* used by the Comparators to perform binary searches. These can be fabricated
|
* used by the Comparators to perform binary searches. These can be fabricated
|
||||||
* by using {@link SymbolNode#createNode(Symbol, Program)}
|
* by using {@link SymbolNode#createNode(Symbol, Program)}
|
||||||
|
@ -177,15 +174,15 @@ public interface SymbolTreeNode {
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
* @return the node that contains the given symbol.
|
* @return the node that contains the given symbol.
|
||||||
*/
|
*/
|
||||||
default public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren,
|
public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren,
|
||||||
TaskMonitor monitor) {
|
TaskMonitor monitor) {
|
||||||
|
|
||||||
// if we don't have to loadChildren and we are not loaded get out.
|
// if we don't have to loadChildren and we are not loaded get out.
|
||||||
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
|
if (!loadChildren && !isLoaded()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GTreeNode> children = getAllChildren();
|
List<GTreeNode> children = getChildren();
|
||||||
int index = Collections.binarySearch(children, key, getChildrenComparator());
|
int index = Collections.binarySearch(children, key, getChildrenComparator());
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
GTreeNode node = children.get(index);
|
GTreeNode node = children.get(index);
|
||||||
|
@ -222,12 +219,4 @@ public interface SymbolTreeNode {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// CoreGTreeNode Methods - redefined here so this class can use them
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
public boolean isChildrenLoadedOrInProgress();
|
|
||||||
|
|
||||||
public List<GTreeNode> getAllChildren();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import java.util.*;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.symboltree.SymbolCategory;
|
import ghidra.app.plugin.core.symboltree.SymbolCategory;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
@ -30,10 +30,9 @@ import ghidra.program.model.symbol.SymbolType;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootNode {
|
public class SymbolTreeRootNode extends SymbolCategoryNode {
|
||||||
private static Icon GLOBAL_ICON = ResourceManager.loadImage("images/bullet_green.png");
|
private static Icon GLOBAL_ICON = ResourceManager.loadImage("images/bullet_green.png");
|
||||||
private final String name;
|
private final String name;
|
||||||
private GTree tree;
|
|
||||||
|
|
||||||
public SymbolTreeRootNode() {
|
public SymbolTreeRootNode() {
|
||||||
name = "No Symbol Tree";
|
name = "No Symbol Tree";
|
||||||
|
@ -229,19 +228,24 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void symbolAdded(Symbol symbol) {
|
public SymbolNode symbolAdded(Symbol symbol) {
|
||||||
List<GTreeNode> allChildren = getAllChildren();
|
SymbolNode returnNode = null;
|
||||||
|
List<GTreeNode> allChildren = getChildren();
|
||||||
for (GTreeNode gNode : allChildren) {
|
for (GTreeNode gNode : allChildren) {
|
||||||
SymbolCategoryNode symbolNode = (SymbolCategoryNode) gNode;
|
SymbolCategoryNode symbolNode = (SymbolCategoryNode) gNode;
|
||||||
symbolNode.symbolAdded(symbol);
|
SymbolNode newNode = symbolNode.symbolAdded(symbol);
|
||||||
|
if (newNode != null) {
|
||||||
|
returnNode = newNode; // doesn't matter which one we return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
|
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
|
||||||
|
|
||||||
// we have to loop--the symbol may exist in more than one category
|
// we have to loop--the symbol may exist in more than one category
|
||||||
List<GTreeNode> allChildren = getAllChildren();
|
List<GTreeNode> allChildren = getChildren();
|
||||||
for (GTreeNode gNode : allChildren) {
|
for (GTreeNode gNode : allChildren) {
|
||||||
SymbolCategoryNode symbolNode = (SymbolCategoryNode) gNode;
|
SymbolCategoryNode symbolNode = (SymbolCategoryNode) gNode;
|
||||||
symbolNode.symbolRemoved(symbol, oldName, monitor);
|
symbolNode.symbolRemoved(symbol, oldName, monitor);
|
||||||
|
@ -249,8 +253,7 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rebuild() {
|
public void rebuild() {
|
||||||
removeAll();
|
setChildren(null);
|
||||||
fireNodeStructureChanged(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -302,14 +305,4 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
|
||||||
public void setNodeCut(boolean isCut) {
|
public void setNodeCut(boolean isCut) {
|
||||||
throw new UnsupportedOperationException("Cannot cut the symbol tree root node");
|
throw new UnsupportedOperationException("Cannot cut the symbol tree root node");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTree getGTree() {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGTree(GTree tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -944,7 +944,8 @@ class FSBActionManager {
|
||||||
private void doOpenFileSystem(FSRL containerFSRL, TaskMonitor monitor) {
|
private void doOpenFileSystem(FSRL containerFSRL, TaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
monitor.setMessage("Probing " + containerFSRL.getName() + " for filesystems");
|
monitor.setMessage("Probing " + containerFSRL.getName() + " for filesystems");
|
||||||
FileSystemRef ref = FileSystemService.getInstance().probeFileForFilesystem(
|
FileSystemRef ref = FileSystemService.getInstance()
|
||||||
|
.probeFileForFilesystem(
|
||||||
containerFSRL, monitor, FileSystemProbeConflictResolver.GUI_PICKER);
|
containerFSRL, monitor, FileSystemProbeConflictResolver.GUI_PICKER);
|
||||||
if (ref == null) {
|
if (ref == null) {
|
||||||
Msg.showWarn(this, plugin.getTool().getActiveWindow(), "Open Filesystem",
|
Msg.showWarn(this, plugin.getTool().getActiveWindow(), "Open Filesystem",
|
||||||
|
@ -1051,7 +1052,7 @@ class FSBActionManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FSBRootNode node = (FSBRootNode) context.getContextObject();
|
FSBRootNode node = (FSBRootNode) context.getContextObject();
|
||||||
if (node == gTree.getRootNode()) {
|
if (node.equals(gTree.getModelRoot())) {
|
||||||
// Close entire window
|
// Close entire window
|
||||||
FileSystemRef fsRef = node.getFSRef();
|
FileSystemRef fsRef = node.getFSRef();
|
||||||
if (fsRef != null && !fsRef.isClosed() &&
|
if (fsRef != null && !fsRef.isClosed() &&
|
||||||
|
@ -1174,7 +1175,8 @@ class FSBActionManager {
|
||||||
gTree.runTask(monitor -> {
|
gTree.runTask(monitor -> {
|
||||||
try {
|
try {
|
||||||
FileSystemRef fsRef =
|
FileSystemRef fsRef =
|
||||||
FileSystemService.getInstance().probeFileForFilesystem(
|
FileSystemService.getInstance()
|
||||||
|
.probeFileForFilesystem(
|
||||||
containerFSRL, monitor,
|
containerFSRL, monitor,
|
||||||
FileSystemProbeConflictResolver.GUI_PICKER);
|
FileSystemProbeConflictResolver.GUI_PICKER);
|
||||||
if (fsRef == null) {
|
if (fsRef == null) {
|
||||||
|
@ -1183,7 +1185,7 @@ class FSBActionManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, gTree, fileNode);
|
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, fileNode);
|
||||||
FSBRootNode containingFSBRootNode =
|
FSBRootNode containingFSBRootNode =
|
||||||
FSBNode.findContainingFileSystemFSBRootNode(fileNode);
|
FSBNode.findContainingFileSystemFSBRootNode(fileNode);
|
||||||
if (containingFSBRootNode != null) {
|
if (containingFSBRootNode != null) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeSlowLoadingNode;
|
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -33,7 +32,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* <p>
|
* <p>
|
||||||
* Visible to just this package.
|
* Visible to just this package.
|
||||||
*/
|
*/
|
||||||
public class FSBDirNode extends GTreeSlowLoadingNode implements FSBNode {
|
public class FSBDirNode extends FSBNode {
|
||||||
private FSRL fsrl;
|
private FSRL fsrl;
|
||||||
|
|
||||||
FSBDirNode(FSRL fsrl) {
|
FSBDirNode(FSRL fsrl) {
|
||||||
|
|
|
@ -15,17 +15,22 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.plugins.fsbrowser;
|
package ghidra.plugins.fsbrowser;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.AbstractGTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GTreeNode that represents a file on a filesystem.
|
* GTreeNode that represents a file on a filesystem.
|
||||||
* <p>
|
* <p>
|
||||||
* Visible to just this package.
|
* Visible to just this package.
|
||||||
*/
|
*/
|
||||||
public class FSBFileNode extends AbstractGTreeNode implements FSBNode {
|
public class FSBFileNode extends FSBNode {
|
||||||
protected FSRL fsrl;
|
protected FSRL fsrl;
|
||||||
|
|
||||||
FSBFileNode(FSRL fsrl) {
|
FSBFileNode(FSRL fsrl) {
|
||||||
|
@ -62,4 +67,9 @@ public class FSBFileNode extends AbstractGTreeNode implements FSBNode {
|
||||||
return fsrl.hashCode();
|
return fsrl.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,13 @@ package ghidra.plugins.fsbrowser;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import docking.widgets.tree.GTreeSlowLoadingNode;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base interface for all filesystem browser gtree nodes.
|
* Base interface for all filesystem browser gtree nodes.
|
||||||
*/
|
*/
|
||||||
public interface FSBNode extends GTreeNode {
|
public abstract class FSBNode extends GTreeSlowLoadingNode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link FSRL} of the filesystem object that this node represents.
|
* Returns the {@link FSRL} of the filesystem object that this node represents.
|
||||||
|
@ -32,7 +33,7 @@ public interface FSBNode extends GTreeNode {
|
||||||
*
|
*
|
||||||
* @return {@link FSRL} of the filesystem object.
|
* @return {@link FSRL} of the filesystem object.
|
||||||
*/
|
*/
|
||||||
FSRL getFSRL();
|
public abstract FSRL getFSRL();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link FSBRootNode} that represents the root of the file system that
|
* Returns the {@link FSBRootNode} that represents the root of the file system that
|
||||||
|
@ -61,7 +62,7 @@ public interface FSBNode extends GTreeNode {
|
||||||
|
|
||||||
Collections.sort(files, FSUtilities.GFILE_NAME_TYPE_COMPARATOR);
|
Collections.sort(files, FSUtilities.GFILE_NAME_TYPE_COMPARATOR);
|
||||||
for (GFile child : files) {
|
for (GFile child : files) {
|
||||||
nodes.add(getNodeFromFile(child));
|
nodes.add((GTreeNode) getNodeFromFile(child));
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.*;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -33,10 +33,9 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* <p>
|
* <p>
|
||||||
* Visible to just this package.
|
* Visible to just this package.
|
||||||
*/
|
*/
|
||||||
public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode, FSBNode {
|
public class FSBRootNode extends FSBNode {
|
||||||
|
|
||||||
private FileSystemRef fsRef;
|
private FileSystemRef fsRef;
|
||||||
private GTree tree;
|
|
||||||
private FSBFileNode prevNode;
|
private FSBFileNode prevNode;
|
||||||
private List<FSBRootNode> subRootNodes = new ArrayList<>();
|
private List<FSBRootNode> subRootNodes = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -44,9 +43,8 @@ public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode,
|
||||||
this.fsRef = fsRef;
|
this.fsRef = fsRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSBRootNode(FileSystemRef fsRef, GTree gTree, FSBFileNode prevNode) {
|
FSBRootNode(FileSystemRef fsRef, FSBFileNode prevNode) {
|
||||||
this.fsRef = fsRef;
|
this.fsRef = fsRef;
|
||||||
this.tree = gTree;
|
|
||||||
this.prevNode = prevNode;
|
this.prevNode = prevNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,16 +104,6 @@ public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode,
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGTree(GTree tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTree getGTree() {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FSRL getFSRL() {
|
public FSRL getFSRL() {
|
||||||
return fsRef.getFilesystem().getFSRL();
|
return fsRef.getFilesystem().getFSRL();
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class FSBUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FSBRootNode getNodesRoot(FSBNode node) {
|
public static FSBRootNode getNodesRoot(FSBNode node) {
|
||||||
GTreeNode tmp = node;
|
GTreeNode tmp = (GTreeNode) node;
|
||||||
while (tmp != null && !(tmp instanceof FSBRootNode)) {
|
while (tmp != null && !(tmp instanceof FSBRootNode)) {
|
||||||
tmp = tmp.getParent();
|
tmp = tmp.getParent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -36,13 +36,11 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
private GTree gTree;
|
private GTree gTree;
|
||||||
private FilterTextField filterField;
|
private FilterTextField filterField;
|
||||||
|
|
||||||
private GTreeRootNode root;
|
|
||||||
|
|
||||||
private DockingWindowManager winMgr;
|
private DockingWindowManager winMgr;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
root = new TestRootNode();
|
GTreeNode root = new TestRootNode();
|
||||||
gTree = new GTree(root);
|
gTree = new GTree(root);
|
||||||
|
|
||||||
filterField = (FilterTextField) gTree.getFilterField();
|
filterField = (FilterTextField) gTree.getFilterField();
|
||||||
|
@ -63,10 +61,11 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
public void testContains() {
|
public void testContains() {
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, root.getChildCount());
|
assertEquals("Expected 4 of nodes to be in filtered tree!", 4,
|
||||||
|
viewRoot().getChildCount());
|
||||||
|
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
checkContainsNode("XABC");
|
checkContainsNode("XABC");
|
||||||
|
@ -74,34 +73,38 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
setFilterText("MMM");
|
setFilterText("MMM");
|
||||||
assertEquals("Expected 4 of nodes to be in filtered tree!", 0, root.getChildCount());
|
assertEquals("Expected 4 of nodes to be in filtered tree!", 0, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private GTreeNode viewRoot() {
|
||||||
|
return gTree.getViewRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiWordContains() {
|
public void testMultiWordContains() {
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ',
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ',
|
||||||
MultitermEvaluationMode.AND);
|
MultitermEvaluationMode.AND);
|
||||||
|
|
||||||
setFilterText("CX AB");
|
setFilterText("CX AB");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ', MultitermEvaluationMode.OR);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ', MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
setFilterText("CX AB");
|
setFilterText("CX AB");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -109,26 +112,26 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
for (char delim : FilterOptions.VALID_MULTITERM_DELIMITERS.toCharArray()) {
|
for (char delim : FilterOptions.VALID_MULTITERM_DELIMITERS.toCharArray()) {
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
MultitermEvaluationMode.AND);
|
MultitermEvaluationMode.AND);
|
||||||
|
|
||||||
setFilterText("CX" + delim + "AB");
|
setFilterText("CX" + delim + "AB");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
MultitermEvaluationMode.OR);
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
setFilterText("CX" + delim + "AB");
|
setFilterText("CX" + delim + "AB");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,7 +141,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
String delimPad = StringUtilities.pad("", ' ', 1);
|
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||||
|
|
||||||
|
@ -149,19 +152,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
String delimStr = delimPad + delim;
|
String delimStr = delimPad + delim;
|
||||||
|
|
||||||
setFilterText("CX" + delimStr + "AB");
|
setFilterText("CX" + delimStr + "AB");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
MultitermEvaluationMode.OR);
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
setFilterText("CX" + delimStr + "AB");
|
setFilterText("CX" + delimStr + "AB");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +174,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
String delimPad = StringUtilities.pad("", ' ', 1);
|
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||||
|
|
||||||
|
@ -182,19 +185,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
String delimStr = delim + delimPad;
|
String delimStr = delim + delimPad;
|
||||||
|
|
||||||
setFilterText("CX" + delimStr + "AB");
|
setFilterText("CX" + delimStr + "AB");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
MultitermEvaluationMode.OR);
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
setFilterText("CX" + delimStr + "AB");
|
setFilterText("CX" + delimStr + "AB");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +207,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
String delimPad = StringUtilities.pad("", ' ', 1);
|
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||||
|
|
||||||
|
@ -215,19 +218,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
String delimStr = delimPad + delim + delimPad;
|
String delimStr = delimPad + delim + delimPad;
|
||||||
|
|
||||||
setFilterText("CX" + delimStr + "AB");
|
setFilterText("CX" + delimStr + "AB");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||||
MultitermEvaluationMode.OR);
|
MultitermEvaluationMode.OR);
|
||||||
|
|
||||||
setFilterText("CX" + delimStr + "AB");
|
setFilterText("CX" + delimStr + "AB");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,10 +239,10 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
public void testInvertedContains() {
|
public void testInvertedContains() {
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, true);
|
setFilterOptions(TextFilterStrategy.CONTAINS, true);
|
||||||
|
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
checkDoesNotContainsNode("ABC");
|
checkDoesNotContainsNode("ABC");
|
||||||
checkDoesNotContainsNode("XABC");
|
checkDoesNotContainsNode("XABC");
|
||||||
|
@ -247,167 +250,168 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
checkDoesNotContainsNode("XABCX");
|
checkDoesNotContainsNode("XABCX");
|
||||||
|
|
||||||
setFilterText("MMM");
|
setFilterText("MMM");
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvertedMultiWordContains() {
|
public void testInvertedMultiWordContains() {
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.AND);
|
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.AND);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("CX AB");
|
setFilterText("CX AB");
|
||||||
|
|
||||||
checkDoesNotContainsNode("ABCX");
|
checkDoesNotContainsNode("ABCX");
|
||||||
checkDoesNotContainsNode("XABCX");
|
checkDoesNotContainsNode("XABCX");
|
||||||
assertEquals(3, root.getChildCount());
|
assertEquals(3, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.OR);
|
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.OR);
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("CX AB");
|
setFilterText("CX AB");
|
||||||
|
|
||||||
checkDoesNotContainsNode("ABCX");
|
checkDoesNotContainsNode("ABCX");
|
||||||
checkDoesNotContainsNode("XABCX");
|
checkDoesNotContainsNode("XABCX");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStartsWith() {
|
public void testStartsWith() {
|
||||||
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("MMM");
|
setFilterText("MMM");
|
||||||
assertEquals(0, root.getChildCount());
|
assertEquals(0, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvertedStartsWith() {
|
public void testInvertedStartsWith() {
|
||||||
setFilterOptions(TextFilterStrategy.STARTS_WITH, true);
|
setFilterOptions(TextFilterStrategy.STARTS_WITH, true);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkDoesNotContainsNode("ABC");
|
checkDoesNotContainsNode("ABC");
|
||||||
checkDoesNotContainsNode("ABCX");
|
checkDoesNotContainsNode("ABCX");
|
||||||
assertEquals(3, root.getChildCount());
|
assertEquals(3, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("MMM");
|
setFilterText("MMM");
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExactMatch() {
|
public void testExactMatch() {
|
||||||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("MMM");
|
setFilterText("MMM");
|
||||||
assertEquals(0, root.getChildCount());
|
assertEquals(0, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvertedExactMatch() {
|
public void testInvertedExactMatch() {
|
||||||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, true);
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, true);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkDoesNotContainsNode("ABC");
|
checkDoesNotContainsNode("ABC");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("MMM");
|
setFilterText("MMM");
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegExMatch() {
|
public void testRegExMatch() {
|
||||||
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, false);
|
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, false);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("^ABC$");
|
setFilterText("^ABC$");
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
assertEquals("Expected 1 node match exacly match ABC!", 1, root.getChildCount());
|
assertEquals("Expected 1 node match exacly match ABC!", 1, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
checkContainsNode("XABC");
|
checkContainsNode("XABC");
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
assertEquals("Expected 4 of nodes that contain the text ABC!", 4, root.getChildCount());
|
assertEquals("Expected 4 of nodes that contain the text ABC!", 4,
|
||||||
|
viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("XA.{0,2}X");
|
setFilterText("XA.{0,2}X");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("X{0,1}A.{0,2}X");
|
setFilterText("X{0,1}A.{0,2}X");
|
||||||
checkContainsNode("XABCX");
|
checkContainsNode("XABCX");
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvertedRegExMatch() {
|
public void testInvertedRegExMatch() {
|
||||||
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, true);
|
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, true);
|
||||||
// no filter text - make sure all 5 nodes are there
|
// no filter text - make sure all 5 nodes are there
|
||||||
assertEquals(5, root.getChildCount());
|
assertEquals(5, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("^ABC$");
|
setFilterText("^ABC$");
|
||||||
checkDoesNotContainsNode("ABC");
|
checkDoesNotContainsNode("ABC");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkDoesNotContainsNode("ABC");
|
checkDoesNotContainsNode("ABC");
|
||||||
checkDoesNotContainsNode("XABC");
|
checkDoesNotContainsNode("XABC");
|
||||||
checkDoesNotContainsNode("ABCX");
|
checkDoesNotContainsNode("ABCX");
|
||||||
checkDoesNotContainsNode("XABCX");
|
checkDoesNotContainsNode("XABCX");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("XA.{0,2}X");
|
setFilterText("XA.{0,2}X");
|
||||||
checkDoesNotContainsNode("XABCX");
|
checkDoesNotContainsNode("XABCX");
|
||||||
assertEquals(4, root.getChildCount());
|
assertEquals(4, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("X{0,1}A.{0,2}X");
|
setFilterText("X{0,1}A.{0,2}X");
|
||||||
checkDoesNotContainsNode("XABCX");
|
checkDoesNotContainsNode("XABCX");
|
||||||
checkDoesNotContainsNode("ABCX");
|
checkDoesNotContainsNode("ABCX");
|
||||||
assertEquals(3, root.getChildCount());
|
assertEquals(3, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -416,14 +420,14 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||||
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, root.getChildCount());
|
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, viewRoot().getChildCount());
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
checkContainsNode("XABC");
|
checkContainsNode("XABC");
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
|
@ -436,20 +440,20 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||||
setFilterText("ABC");
|
setFilterText("ABC");
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
Object originalValue = getInstanceField("uniquePreferenceKey", gTree);
|
Object originalValue = getInstanceField("uniquePreferenceKey", gTree);
|
||||||
setInstanceField("preferenceKey", gTree.getFilterProvider(), "XYZ");
|
setInstanceField("preferenceKey", gTree.getFilterProvider(), "XYZ");
|
||||||
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
checkContainsNode("ABCX");
|
checkContainsNode("ABCX");
|
||||||
assertEquals(2, root.getChildCount());
|
assertEquals(2, viewRoot().getChildCount());
|
||||||
|
|
||||||
setInstanceField("preferenceKey", gTree.getFilterProvider(), originalValue);
|
setInstanceField("preferenceKey", gTree.getFilterProvider(), originalValue);
|
||||||
setInstanceField("optionsSet", gTree.getFilterProvider(), false);
|
setInstanceField("optionsSet", gTree.getFilterProvider(), false);
|
||||||
restorePreferences();
|
restorePreferences();
|
||||||
checkContainsNode("ABC");
|
checkContainsNode("ABC");
|
||||||
assertEquals(1, root.getChildCount());
|
assertEquals(1, viewRoot().getChildCount());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,7 +469,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkContainsNode(String string) {
|
private void checkContainsNode(String string) {
|
||||||
List<GTreeNode> children = root.getChildren();
|
List<GTreeNode> children = viewRoot().getChildren();
|
||||||
for (GTreeNode gTreeNode : children) {
|
for (GTreeNode gTreeNode : children) {
|
||||||
if (gTreeNode.getName().equals(string)) {
|
if (gTreeNode.getName().equals(string)) {
|
||||||
return;
|
return;
|
||||||
|
@ -475,7 +479,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDoesNotContainsNode(String string) {
|
private void checkDoesNotContainsNode(String string) {
|
||||||
List<GTreeNode> children = root.getChildren();
|
List<GTreeNode> children = viewRoot().getChildren();
|
||||||
for (GTreeNode gTreeNode : children) {
|
for (GTreeNode gTreeNode : children) {
|
||||||
if (gTreeNode.getName().equals(string)) {
|
if (gTreeNode.getName().equals(string)) {
|
||||||
Assert.fail("Expected node " + string +
|
Assert.fail("Expected node " + string +
|
||||||
|
@ -517,7 +521,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
waitForTree(gTree);
|
waitForTree(gTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestRootNode extends AbstractGTreeRootNode {
|
private class TestRootNode extends GTreeNode {
|
||||||
|
|
||||||
TestRootNode() {
|
TestRootNode() {
|
||||||
List<GTreeNode> children = new ArrayList<>();
|
List<GTreeNode> children = new ArrayList<>();
|
||||||
|
@ -553,7 +557,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
||||||
/**
|
/**
|
||||||
* A basic leaf node
|
* A basic leaf node
|
||||||
*/
|
*/
|
||||||
private class LeafNode extends AbstractGTreeNode {
|
private class LeafNode extends GTreeNode {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|
|
@ -215,16 +215,16 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
String existingCaller = "Function_1000";// four levels back
|
String existingCaller = "Function_1000";// four levels back
|
||||||
setIncomingFilter(existingCaller);
|
setIncomingFilter(existingCaller);
|
||||||
assertIncomingMaxDepth(depth);
|
assertIncomingMaxDepth(depth, true);
|
||||||
|
|
||||||
assertIncomingNode(existingCaller, depth);
|
assertIncomingNode(existingCaller, depth);
|
||||||
|
|
||||||
depth = 3;
|
depth = 3;
|
||||||
setDepth(depth);
|
setDepth(depth);
|
||||||
setIncomingFilter(existingCaller);
|
setIncomingFilter(existingCaller);
|
||||||
assertIncomingMaxDepth(0);// filter no longer matches
|
assertIncomingMaxDepth(0, true);// filter no longer matches
|
||||||
|
|
||||||
assertIncomingNoNode(existingCaller, depth);
|
assertIncomingNoNode(existingCaller, depth, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -236,16 +236,16 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
String existingCallee = "Function_8000";
|
String existingCallee = "Function_8000";
|
||||||
setOutgoingFilter(existingCallee);
|
setOutgoingFilter(existingCallee);
|
||||||
assertOutgoingMaxDepth(depth);
|
assertOutgoingMaxDepth(depth, true);
|
||||||
|
|
||||||
assertOutgoingNode(existingCallee, depth);
|
assertOutgoingNode(existingCallee, depth);
|
||||||
|
|
||||||
depth = 2;
|
depth = 2;
|
||||||
setDepth(depth);
|
setDepth(depth);
|
||||||
setOutgoingFilter(existingCallee);
|
setOutgoingFilter(existingCallee);
|
||||||
assertOutgoingMaxDepth(0);// filter no longer matches
|
assertOutgoingMaxDepth(0, true);// filter no longer matches
|
||||||
|
|
||||||
assertOutgoingNoNode(existingCallee, depth);
|
assertOutgoingNoNode(existingCallee, depth, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -261,7 +261,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
String existingCaller = "2000";// at depth 3
|
String existingCaller = "2000";// at depth 3
|
||||||
setIncomingFilter(existingCaller);
|
setIncomingFilter(existingCaller);
|
||||||
assertIncomingMaxDepth(depth);
|
assertIncomingMaxDepth(depth, true);
|
||||||
|
|
||||||
assertIncomingNode(existingCaller, depth);
|
assertIncomingNode(existingCaller, depth);
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
existingCaller = "1000";// at depth 4
|
existingCaller = "1000";// at depth 4
|
||||||
setIncomingFilter(existingCaller);
|
setIncomingFilter(existingCaller);
|
||||||
|
|
||||||
assertIncomingMaxDepth(depth);
|
assertIncomingMaxDepth(depth, true);
|
||||||
assertIncomingNode(existingCaller, depth);
|
assertIncomingNode(existingCaller, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +331,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
fullyExpandIncomingNode(node);
|
fullyExpandIncomingNode(node);
|
||||||
|
|
||||||
assertIncomingMaxDepth(currentDepthSetting(provider));
|
assertIncomingMaxDepth(currentDepthSetting(provider), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -347,7 +347,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
fullyExpandOutgoingNode(node);
|
fullyExpandOutgoingNode(node);
|
||||||
|
|
||||||
assertOutgoingMaxDepth(currentDepthSetting(provider));
|
assertOutgoingMaxDepth(currentDepthSetting(provider), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -365,7 +365,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
int nodeDepth = node.getTreePath().getPathCount() - 1;// -1 for root node
|
int nodeDepth = node.getTreePath().getPathCount() - 1;// -1 for root node
|
||||||
int depth = 4 + nodeDepth;
|
int depth = 4 + nodeDepth;
|
||||||
assertIncomingMaxDepth(depth);
|
assertIncomingMaxDepth(depth, false);
|
||||||
assertDepth(node, depth);
|
assertDepth(node, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
fullyExpandOutgoingNode(node);
|
fullyExpandOutgoingNode(node);
|
||||||
|
|
||||||
int depth = currentDepthSetting(provider);
|
int depth = currentDepthSetting(provider);
|
||||||
assertOutgoingMaxDepth(depth);
|
assertOutgoingMaxDepth(depth, false);
|
||||||
assertDepth(node, depth);
|
assertDepth(node, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +422,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
//
|
//
|
||||||
|
|
||||||
myWaitForTree(incomingTree, provider);
|
myWaitForTree(incomingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
GTreeNode rootNode = getRootNode(incomingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -442,7 +442,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setProviderFunction("0x5000");
|
setProviderFunction("0x5000");
|
||||||
|
|
||||||
myWaitForTree(outgoingTree, provider);
|
myWaitForTree(outgoingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -462,7 +462,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setProviderFunction("0x5000");
|
setProviderFunction("0x5000");
|
||||||
|
|
||||||
myWaitForTree(outgoingTree, provider);
|
myWaitForTree(outgoingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -503,7 +503,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(currentSelection.isEmpty());
|
assertTrue(currentSelection.isEmpty());
|
||||||
|
|
||||||
myWaitForTree(outgoingTree, provider);
|
myWaitForTree(outgoingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -588,7 +588,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
myWaitForTree(outgoingTree, provider);
|
myWaitForTree(outgoingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -628,7 +628,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
myWaitForTree(outgoingTree, provider);
|
myWaitForTree(outgoingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -698,7 +698,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setProviderFunction(addrString);
|
setProviderFunction(addrString);
|
||||||
|
|
||||||
myWaitForTree(incomingTree, provider);
|
myWaitForTree(incomingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
GTreeNode rootNode = getRootNode(incomingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue("Incoming tree does not have callers as expected for function: " + addrString,
|
assertTrue("Incoming tree does not have callers as expected for function: " + addrString,
|
||||||
children.size() > 0);
|
children.size() > 0);
|
||||||
|
@ -713,7 +713,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setProviderFunction("0x5000");
|
setProviderFunction("0x5000");
|
||||||
|
|
||||||
myWaitForTree(incomingTree, provider);
|
myWaitForTree(incomingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
GTreeNode rootNode = getRootNode(incomingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -744,7 +744,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setProviderFunction("0x5000");
|
setProviderFunction("0x5000");
|
||||||
|
|
||||||
myWaitForTree(incomingTree, provider);
|
myWaitForTree(incomingTree, provider);
|
||||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
GTreeNode rootNode = getRootNode(incomingTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
@ -799,10 +799,14 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GTreeRootNode getRootNode(final GTree tree) {
|
private GTreeNode getRootNode(GTree tree) {
|
||||||
|
return getRootNode(tree, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GTreeNode getRootNode(final GTree tree, boolean filtered) {
|
||||||
myWaitForTree(tree, provider);
|
myWaitForTree(tree, provider);
|
||||||
final AtomicReference<GTreeRootNode> ref = new AtomicReference<>();
|
final AtomicReference<GTreeNode> ref = new AtomicReference<>();
|
||||||
runSwing(() -> ref.set(tree.getRootNode()));
|
runSwing(() -> ref.set(filtered ? tree.getViewRoot() : tree.getModelRoot()));
|
||||||
return ref.get();
|
return ref.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,8 +834,8 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
return functionManager.getFunctionAt(address);
|
return functionManager.getFunctionAt(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertOutgoingNoNode(String name, int depth) {
|
private void assertOutgoingNoNode(String name, int depth, boolean filtered) {
|
||||||
List<NodeDepthInfo> nodes = getNodesByDepth(false);
|
List<NodeDepthInfo> nodes = getNodesByDepth(false, filtered);
|
||||||
for (NodeDepthInfo info : nodes) {
|
for (NodeDepthInfo info : nodes) {
|
||||||
String nodeName = info.node.getName();
|
String nodeName = info.node.getName();
|
||||||
if (nodeName.indexOf(name) != -1) {
|
if (nodeName.indexOf(name) != -1) {
|
||||||
|
@ -861,8 +865,8 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
Assert.fail("Unable to find a node by name: " + name + " at depth: " + depth);
|
Assert.fail("Unable to find a node by name: " + name + " at depth: " + depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertOutgoingMaxDepth(int depth) {
|
private void assertOutgoingMaxDepth(int depth, boolean filtered) {
|
||||||
List<NodeDepthInfo> nodes = getNodesByDepth(false);
|
List<NodeDepthInfo> nodes = getNodesByDepth(false, filtered);
|
||||||
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
|
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
|
||||||
|
|
||||||
assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth);
|
assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth);
|
||||||
|
@ -888,7 +892,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GTreeNode selectIncomingNode(String text) {
|
private GTreeNode selectIncomingNode(String text) {
|
||||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
GTreeNode rootNode = getRootNode(incomingTree);
|
||||||
GTreeNode node = findNode(rootNode, text);
|
GTreeNode node = findNode(rootNode, text);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
incomingTree.setSelectedNode(node);
|
incomingTree.setSelectedNode(node);
|
||||||
|
@ -897,7 +901,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GTreeNode selectOutgoingNode(String text) {
|
private GTreeNode selectOutgoingNode(String text) {
|
||||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||||
GTreeNode node = findNode(rootNode, text);
|
GTreeNode node = findNode(rootNode, text);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
outgoingTree.setSelectedNode(node);
|
outgoingTree.setSelectedNode(node);
|
||||||
|
@ -912,7 +916,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node instanceof GTreeSlowLoadingNode) {
|
if (node instanceof GTreeSlowLoadingNode) {
|
||||||
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
|
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
return null;// children not loaded--don't load
|
return null;// children not loaded--don't load
|
||||||
}
|
}
|
||||||
|
@ -929,23 +933,15 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDepth(GTreeNode node, int depth) {
|
private void assertDepth(GTreeNode node, int depth) {
|
||||||
int currentDepth = 0;
|
int currentDepth = node.getTreePath().getPathCount() - 1;
|
||||||
GTreeNode parent = node.getParent();
|
|
||||||
while (parent != null) {
|
|
||||||
currentDepth++;
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDepth--;// the root is considered depth 0, so we have to subtract one
|
|
||||||
|
|
||||||
int maxNodeDepth = getMaxNodeDepth(node, currentDepth);
|
int maxNodeDepth = getMaxNodeDepth(node, currentDepth);
|
||||||
assertEquals("Node depth is not correct " + node, depth, maxNodeDepth);
|
assertEquals("Node depth is not correct " + node, depth, maxNodeDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getMaxNodeDepth(GTreeNode node, int currentDepth) {
|
private int getMaxNodeDepth(GTreeNode node, int currentDepth) {
|
||||||
int maxDepth = currentDepth + 1;
|
int maxDepth = currentDepth;
|
||||||
if (node instanceof GTreeSlowLoadingNode) {
|
if (node instanceof GTreeSlowLoadingNode) {
|
||||||
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
|
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
return maxDepth;// children not loaded--don't load
|
return maxDepth;// children not loaded--don't load
|
||||||
}
|
}
|
||||||
|
@ -991,8 +987,8 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
Assert.fail("Unable to find a node by name: " + name + " at depth: " + depth);
|
Assert.fail("Unable to find a node by name: " + name + " at depth: " + depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertIncomingNoNode(String name, int depth) {
|
private void assertIncomingNoNode(String name, int depth, boolean filtered) {
|
||||||
List<NodeDepthInfo> nodes = getNodesByDepth(true);
|
List<NodeDepthInfo> nodes = getNodesByDepth(true, filtered);
|
||||||
for (NodeDepthInfo info : nodes) {
|
for (NodeDepthInfo info : nodes) {
|
||||||
String nodeName = info.node.getName();
|
String nodeName = info.node.getName();
|
||||||
if (nodeName.indexOf(name) != -1) {
|
if (nodeName.indexOf(name) != -1) {
|
||||||
|
@ -1002,16 +998,21 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertIncomingMaxDepth(int depth) {
|
private void assertIncomingMaxDepth(int depth, boolean filtered) {
|
||||||
List<NodeDepthInfo> nodes = getNodesByDepth(true);
|
List<NodeDepthInfo> nodes = getNodesByDepth(true, filtered);
|
||||||
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
|
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
|
||||||
|
|
||||||
assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth);
|
assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<NodeDepthInfo> getNodesByDepth(boolean incoming) {
|
private List<NodeDepthInfo> getNodesByDepth(boolean incoming) {
|
||||||
|
return getNodesByDepth(incoming, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<NodeDepthInfo> getNodesByDepth(boolean incoming, boolean filtered) {
|
||||||
List<NodeDepthInfo> list = new ArrayList<>();
|
List<NodeDepthInfo> list = new ArrayList<>();
|
||||||
GTreeRootNode root = incoming ? getRootNode(incomingTree) : getRootNode(outgoingTree);
|
GTreeNode root =
|
||||||
|
incoming ? getRootNode(incomingTree, filtered) : getRootNode(outgoingTree, filtered);
|
||||||
accumulateNodeDepths(list, root, 0);
|
accumulateNodeDepths(list, root, 0);
|
||||||
Collections.sort(list);
|
Collections.sort(list);
|
||||||
return list;
|
return list;
|
||||||
|
@ -1025,7 +1026,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node instanceof GTreeSlowLoadingNode) {
|
if (node instanceof GTreeSlowLoadingNode) {
|
||||||
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
|
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
return;// children not loaded--don't load
|
return;// children not loaded--don't load
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ public class StructureEditorArchiveTest extends AbstractStructureEditorTest {
|
||||||
// openArchive = getDockingAction(plugin, "Open Data Type Archive");
|
// openArchive = getDockingAction(plugin, "Open Data Type Archive");
|
||||||
closeArchive = getAction(plugin, "Close Archive");
|
closeArchive = getAction(plugin, "Close Archive");
|
||||||
waitForTree(dtTree);
|
waitForTree(dtTree);
|
||||||
GTreeNode rootNode = dtTree.getRootNode();
|
GTreeNode rootNode = dtTree.getModelRoot();
|
||||||
GTreeNode newNode = rootNode.getChild("New Archive");
|
GTreeNode newNode = rootNode.getChild("New Archive");
|
||||||
selectNode(newNode);
|
selectNode(newNode);
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ public class StructureEditorArchiveTest extends AbstractStructureEditorTest {
|
||||||
assertTrue(comp0.getDataType().isEquivalent(DataType.DEFAULT));
|
assertTrue(comp0.getDataType().isEquivalent(DataType.DEFAULT));
|
||||||
assertTrue(comp1.getDataType().isEquivalent(new WordDataType()));
|
assertTrue(comp1.getDataType().isEquivalent(new WordDataType()));
|
||||||
|
|
||||||
GTreeNode rootNode = dtTree.getRootNode();
|
GTreeNode rootNode = dtTree.getModelRoot();
|
||||||
GTreeNode child = rootNode.getChild("New Archive");
|
GTreeNode child = rootNode.getChild("New Archive");
|
||||||
selectNode(child);
|
selectNode(child);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT
|
||||||
conflictHandlerModesAction);
|
conflictHandlerModesAction);
|
||||||
tree = provider.getGTree();
|
tree = provider.getGTree();
|
||||||
waitForTree();
|
waitForTree();
|
||||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
archiveRootNode = (ArchiveRootNode) tree.getViewRoot();
|
||||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr;
|
package ghidra.app.plugin.core.datamgr;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
@ -180,7 +179,7 @@ public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedInte
|
||||||
tree = provider.getGTree();
|
tree = provider.getGTree();
|
||||||
treeModelModListener = new TreeModelModCounter();
|
treeModelModListener = new TreeModelModCounter();
|
||||||
tree.addGTModelListener(treeModelModListener);
|
tree.addGTModelListener(treeModelModListener);
|
||||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
archiveRootNode = (ArchiveRootNode) tree.getViewRoot();
|
||||||
|
|
||||||
tool.showComponentProvider(provider, true);
|
tool.showComponentProvider(provider, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,7 +234,7 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
assertNotNull(archiveDtm);
|
assertNotNull(archiveDtm);
|
||||||
assertEquals("windows_vs12_32", archiveDtm.getName());
|
assertEquals("windows_vs12_32", archiveDtm.getName());
|
||||||
|
|
||||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||||
ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("windows_vs12_32");
|
ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("windows_vs12_32");
|
||||||
assertNotNull(archiveNode);
|
assertNotNull(archiveNode);
|
||||||
ArchiveNode programNode = (ArchiveNode) archiveRootNode.getChild(program.getName());
|
ArchiveNode programNode = (ArchiveNode) archiveRootNode.getChild(program.getName());
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
||||||
provider = plugin.getProvider();
|
provider = plugin.getProvider();
|
||||||
tree = provider.getGTree();
|
tree = provider.getGTree();
|
||||||
waitForTree();
|
waitForTree();
|
||||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ public class CreateLabelsFromEnumsTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
provider = plugin.getProvider();
|
provider = plugin.getProvider();
|
||||||
tree = provider.getGTree();
|
tree = provider.getGTree();
|
||||||
waitForTree();
|
waitForTree();
|
||||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||||
programNode = (ArchiveNode) archiveRootNode.getChild(testName.getMethodName());
|
programNode = (ArchiveNode) archiveRootNode.getChild(testName.getMethodName());
|
||||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
conflictHandlerModesAction);
|
conflictHandlerModesAction);
|
||||||
tree = provider.getGTree();
|
tree = provider.getGTree();
|
||||||
waitForTree();
|
waitForTree();
|
||||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||||
|
|
||||||
|
@ -144,7 +144,8 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
|
|
||||||
private ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> findConflictHandlerActionState(
|
private ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> findConflictHandlerActionState(
|
||||||
DataTypeConflictHandler.ConflictResolutionPolicy conflictMode) {
|
DataTypeConflictHandler.ConflictResolutionPolicy conflictMode) {
|
||||||
for (ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> actionState : conflictHandlerModesAction.getAllActionStates()) {
|
for (ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> actionState : conflictHandlerModesAction
|
||||||
|
.getAllActionStates()) {
|
||||||
if (actionState.getUserData() == conflictMode) {
|
if (actionState.getUserData() == conflictMode) {
|
||||||
return actionState;
|
return actionState;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr;
|
package ghidra.app.plugin.core.datamgr;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.startsWith;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
|
@ -110,7 +110,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
tree = provider.getGTree();
|
tree = provider.getGTree();
|
||||||
jTree = (JTree) invokeInstanceMethod("getJTree", tree);
|
jTree = (JTree) invokeInstanceMethod("getJTree", tree);
|
||||||
waitForTree();
|
waitForTree();
|
||||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
runSwing(() -> invokeInstanceMethod("openArchives", managerHandler,
|
runSwing(() -> invokeInstanceMethod("openArchives", managerHandler,
|
||||||
new Class[] { String[].class }, new Object[] { invalidNames }));
|
new Class[] { String[].class }, new Object[] { invalidNames }));
|
||||||
|
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
GTreeNode invalidChild = rootNode.getChild("BADARCHIVENAME");
|
GTreeNode invalidChild = rootNode.getChild("BADARCHIVENAME");
|
||||||
assertNull("Tree did not close invalid archive.", invalidChild);
|
assertNull("Tree did not close invalid archive.", invalidChild);
|
||||||
}
|
}
|
||||||
|
@ -523,7 +523,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
|
|
||||||
DataTypeNode myStructNode = (DataTypeNode) cat2Node.getChild("MyStruct");
|
DataTypeNode myStructNode = (DataTypeNode) cat2Node.getChild("MyStruct");
|
||||||
|
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
||||||
|
|
||||||
selectNode(myStructNode);
|
selectNode(myStructNode);
|
||||||
|
@ -550,7 +550,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
GTreeNode cat2Node = cat1Node.getChild("Category2");
|
GTreeNode cat2Node = cat1Node.getChild("Category2");
|
||||||
expandNode(cat2Node);
|
expandNode(cat2Node);
|
||||||
|
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
||||||
|
|
||||||
selectNode(cat2Node);
|
selectNode(cat2Node);
|
||||||
|
@ -575,14 +575,14 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
ProgramManager pm = tool.getService(ProgramManager.class);
|
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||||
pm.closeProgram();
|
pm.closeProgram();
|
||||||
});
|
});
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
assertEquals(1, rootNode.getChildCount());
|
assertEquals(1, rootNode.getChildCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExpandAll() throws Exception {
|
public void testExpandAll() throws Exception {
|
||||||
|
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
selectNode(rootNode);
|
selectNode(rootNode);
|
||||||
DockingActionIf expandAction = getAction(plugin, "Expand All");
|
DockingActionIf expandAction = getAction(plugin, "Expand All");
|
||||||
assertTrue(expandAction.isEnabledForContext(treeContext));
|
assertTrue(expandAction.isEnabledForContext(treeContext));
|
||||||
|
@ -608,7 +608,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
@Test
|
@Test
|
||||||
public void testCollapseAll() throws Exception {
|
public void testCollapseAll() throws Exception {
|
||||||
|
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
selectNode(rootNode);
|
selectNode(rootNode);
|
||||||
DockingActionIf collapseAction = getAction(plugin, "Collapse All");
|
DockingActionIf collapseAction = getAction(plugin, "Collapse All");
|
||||||
assertTrue(collapseAction.isEnabledForContext(treeContext));
|
assertTrue(collapseAction.isEnabledForContext(treeContext));
|
||||||
|
@ -645,7 +645,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRefreshBuiltins() throws Exception {
|
public void testRefreshBuiltins() throws Exception {
|
||||||
GTreeNode treeRoot = tree.getRootNode();
|
GTreeNode treeRoot = tree.getModelRoot();
|
||||||
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
|
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
|
||||||
|
|
||||||
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
|
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
|
||||||
|
@ -850,7 +850,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArchiveNode getBuiltInNode() {
|
private ArchiveNode getBuiltInNode() {
|
||||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||||
ArchiveNode builtinNode = (ArchiveNode) archiveRootNode.getChild(BUILTIN_NAME);
|
ArchiveNode builtinNode = (ArchiveNode) archiveRootNode.getChild(BUILTIN_NAME);
|
||||||
assertNotNull(builtinNode);
|
assertNotNull(builtinNode);
|
||||||
return builtinNode;
|
return builtinNode;
|
||||||
|
@ -936,7 +936,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSingleFilterMatch(String[] path) {
|
private void assertSingleFilterMatch(String[] path) {
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getViewRoot();
|
||||||
|
|
||||||
GTreeNode node = rootNode;
|
GTreeNode node = rootNode;
|
||||||
for (int i = 0; i < path.length; i++) {
|
for (int i = 0; i < path.length; i++) {
|
||||||
|
@ -962,7 +962,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertEmptyTree() {
|
private void assertEmptyTree() {
|
||||||
final GTreeNode rootNode = tree.getRootNode();
|
final GTreeNode rootNode = tree.getViewRoot();
|
||||||
final Integer[] box = new Integer[1];
|
final Integer[] box = new Integer[1];
|
||||||
runSwing(() -> box[0] = rootNode.getChildCount());
|
runSwing(() -> box[0] = rootNode.getChildCount());
|
||||||
assertEquals("Root node is not empty as expected", 0, (int) box[0]);
|
assertEquals("Root node is not empty as expected", 0, (int) box[0]);
|
||||||
|
@ -1061,7 +1061,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkNodesCollapsed(GTreeNode parent) {
|
private void checkNodesCollapsed(GTreeNode parent) {
|
||||||
if (parent != tree.getRootNode()) {
|
if (parent != tree.getModelRoot()) {
|
||||||
assertTrue(!tree.isExpanded(parent.getTreePath()));
|
assertTrue(!tree.isExpanded(parent.getTreePath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ public class DataTypeTestUtils {
|
||||||
waitForTree(plugin);
|
waitForTree(plugin);
|
||||||
|
|
||||||
GTree tree = plugin.getProvider().getGTree();
|
GTree tree = plugin.getProvider().getGTree();
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
|
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ public class DataTypeTestUtils {
|
||||||
dataTypeManagerHandler.openArchive(file, checkout, isUserAction);
|
dataTypeManagerHandler.openArchive(file, checkout, isUserAction);
|
||||||
|
|
||||||
archiveTree = plugin.getProvider().getGTree();
|
archiveTree = plugin.getProvider().getGTree();
|
||||||
GTreeNode rootNode = archiveTree.getRootNode();
|
GTreeNode rootNode = archiveTree.getViewRoot();
|
||||||
waitForTree(plugin);
|
waitForTree(plugin);
|
||||||
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
|
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ public class DataTypeTestUtils {
|
||||||
|
|
||||||
String archiveNodeName = trimFullArchiveName(archiveName);
|
String archiveNodeName = trimFullArchiveName(archiveName);
|
||||||
GTree tree = plugin.getProvider().getGTree();
|
GTree tree = plugin.getProvider().getGTree();
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
ArchiveNode archiveNode = (ArchiveNode) rootNode.getChild(archiveNodeName);
|
ArchiveNode archiveNode = (ArchiveNode) rootNode.getChild(archiveNodeName);
|
||||||
if (archiveNode == null) {
|
if (archiveNode == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
tree = provider.getGTree();
|
tree = provider.getGTree();
|
||||||
waitForTree();
|
waitForTree();
|
||||||
|
|
||||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||||
builtInNode = (ArchiveNode) archiveRootNode.getChild("BuiltInTypes");
|
builtInNode = (ArchiveNode) archiveRootNode.getChild("BuiltInTypes");
|
||||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_NAME);
|
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_NAME);
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
GTree gtree = (GTree) getInstanceField("tree", d);
|
GTree gtree = (GTree) getInstanceField("tree", d);
|
||||||
waitForTree(gtree);
|
waitForTree(gtree);
|
||||||
|
|
||||||
ArchiveRootNode root = (ArchiveRootNode) gtree.getRootNode();
|
ArchiveRootNode root = (ArchiveRootNode) gtree.getModelRoot();
|
||||||
ArchiveNode programNode1 = (ArchiveNode) root.getChild(PROGRAM_NAME);
|
ArchiveNode programNode1 = (ArchiveNode) root.getChild(PROGRAM_NAME);
|
||||||
|
|
||||||
assertNotNull("could not find " + PROGRAM_NAME + " in " + root, programNode1);
|
assertNotNull("could not find " + PROGRAM_NAME + " in " + root, programNode1);
|
||||||
|
|
|
@ -40,7 +40,8 @@ import docking.widgets.filter.FilterTextField;
|
||||||
import docking.widgets.pathmanager.PathManager;
|
import docking.widgets.pathmanager.PathManager;
|
||||||
import docking.widgets.table.GDynamicColumnTableModel;
|
import docking.widgets.table.GDynamicColumnTableModel;
|
||||||
import docking.widgets.table.RowObjectTableModel;
|
import docking.widgets.table.RowObjectTableModel;
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import generic.test.TestUtils;
|
import generic.test.TestUtils;
|
||||||
import generic.util.Path;
|
import generic.util.Path;
|
||||||
|
@ -194,7 +195,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||||
waitForTree(categoryTree);
|
waitForTree(categoryTree);
|
||||||
JTree jTree = (JTree) invokeInstanceMethod("getJTree", categoryTree);
|
JTree jTree = (JTree) invokeInstanceMethod("getJTree", categoryTree);
|
||||||
assertNotNull(jTree);
|
assertNotNull(jTree);
|
||||||
GTreeNode child = categoryTree.getRootNode().getChild(category);
|
GTreeNode child = categoryTree.getModelRoot().getChild(category);
|
||||||
categoryTree.setSelectedNode(child);
|
categoryTree.setSelectedNode(child);
|
||||||
waitForTree(categoryTree);
|
waitForTree(categoryTree);
|
||||||
TreePath path = child.getTreePath();
|
TreePath path = child.getTreePath();
|
||||||
|
@ -716,7 +717,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||||
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
|
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
|
||||||
waitForTree(tree);
|
waitForTree(tree);
|
||||||
|
|
||||||
GTreeNode parentNode = tree.getRootNode();
|
GTreeNode parentNode = tree.getModelRoot();
|
||||||
|
|
||||||
String[] parts = newCategory.split("\\.");
|
String[] parts = newCategory.split("\\.");
|
||||||
for (String category : parts) {
|
for (String category : parts) {
|
||||||
|
@ -726,7 +727,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||||
}
|
}
|
||||||
|
|
||||||
protected GTreeNode findChildByName(GTreeNode node, String name) {
|
protected GTreeNode findChildByName(GTreeNode node, String name) {
|
||||||
List<GTreeNode> children = node.getAllChildren();
|
List<GTreeNode> children = node.getChildren();
|
||||||
for (GTreeNode child : children) {
|
for (GTreeNode child : children) {
|
||||||
if (child.getName().equals(name)) {
|
if (child.getName().equals(name)) {
|
||||||
return child;
|
return child;
|
||||||
|
@ -739,8 +740,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||||
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
|
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
|
||||||
waitForTree(tree);
|
waitForTree(tree);
|
||||||
|
|
||||||
GTreeRootNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
List<GTreeNode> children = rootNode.getAllChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
for (GTreeNode node : children) {
|
for (GTreeNode node : children) {
|
||||||
if (node.getName().equals(oldCategory)) {
|
if (node.getName().equals(oldCategory)) {
|
||||||
Assert.fail("Category in tree when expected not to be: " + oldCategory);
|
Assert.fail("Category in tree when expected not to be: " + oldCategory);
|
||||||
|
|
|
@ -297,7 +297,7 @@ public class SymbolTreePlugin3Test extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GTreeNode> children = nsParentNode.getAllChildren();
|
List<GTreeNode> children = nsParentNode.getChildren();
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
List<String> symbolNames =
|
List<String> symbolNames =
|
||||||
|
@ -342,7 +342,7 @@ public class SymbolTreePlugin3Test extends AbstractGhidraHeadedIntegrationTest {
|
||||||
util.waitForTree();
|
util.waitForTree();
|
||||||
|
|
||||||
GTreeNode cnode = rootNode.getChild(4);
|
GTreeNode cnode = rootNode.getChild(4);
|
||||||
List<GTreeNode> children = cnode.getAllChildren();
|
List<GTreeNode> children = cnode.getChildren();
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
List<String> symbolNames =
|
List<String> symbolNames =
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.symboltree;
|
package ghidra.app.plugin.core.symboltree;
|
||||||
|
|
||||||
import static generic.test.AbstractGTest.waitForCondition;
|
import static generic.test.AbstractGTest.*;
|
||||||
import static generic.test.AbstractGenericTest.*;
|
import static generic.test.AbstractGenericTest.*;
|
||||||
import static ghidra.test.AbstractGhidraHeadedIntegrationTest.getAction;
|
import static ghidra.test.AbstractGhidraHeadedIntegrationTest.*;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.datatransfer.Clipboard;
|
import java.awt.datatransfer.Clipboard;
|
||||||
|
@ -38,7 +37,8 @@ import docking.ActionContext;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
import docking.test.AbstractDockingTest;
|
import docking.test.AbstractDockingTest;
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.support.GTreeNodeTransferable;
|
import docking.widgets.tree.support.GTreeNodeTransferable;
|
||||||
import ghidra.app.plugin.core.symboltree.nodes.*;
|
import ghidra.app.plugin.core.symboltree.nodes.*;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
|
@ -58,7 +58,7 @@ class SymbolTreeTestUtils {
|
||||||
private SymbolTreePlugin plugin;
|
private SymbolTreePlugin plugin;
|
||||||
private DockingActionIf symTreeAction;
|
private DockingActionIf symTreeAction;
|
||||||
private SymbolGTree tree;
|
private SymbolGTree tree;
|
||||||
private GTreeRootNode rootGTreeNode;
|
private GTreeNode rootGTreeNode;
|
||||||
private SymbolTreeProvider provider;
|
private SymbolTreeProvider provider;
|
||||||
private DockingActionIf renameAction;
|
private DockingActionIf renameAction;
|
||||||
private DockingActionIf cutAction;
|
private DockingActionIf cutAction;
|
||||||
|
@ -224,7 +224,7 @@ class SymbolTreeTestUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
void collapseTree() {
|
void collapseTree() {
|
||||||
GTreeRootNode root = tree.getRootNode();
|
GTreeNode root = tree.getViewRoot();
|
||||||
List<GTreeNode> topLevelNodes = root.getChildren();
|
List<GTreeNode> topLevelNodes = root.getChildren();
|
||||||
topLevelNodes.forEach(n -> tree.collapseAll(n));
|
topLevelNodes.forEach(n -> tree.collapseAll(n));
|
||||||
waitForTree();
|
waitForTree();
|
||||||
|
@ -334,7 +334,7 @@ class SymbolTreeTestUtils {
|
||||||
provider = plugin.getProvider();
|
provider = plugin.getProvider();
|
||||||
tree = findComponent(provider.getComponent(), SymbolGTree.class);
|
tree = findComponent(provider.getComponent(), SymbolGTree.class);
|
||||||
waitForTree();
|
waitForTree();
|
||||||
rootGTreeNode = tree.getRootNode();
|
rootGTreeNode = tree.getViewRoot();
|
||||||
renameAction = getAction(plugin, "Rename Symbol");
|
renameAction = getAction(plugin, "Rename Symbol");
|
||||||
assertNotNull(renameAction);
|
assertNotNull(renameAction);
|
||||||
cutAction = getAction(plugin, "Cut SymbolTree Node");
|
cutAction = getAction(plugin, "Cut SymbolTree Node");
|
||||||
|
@ -406,7 +406,7 @@ class SymbolTreeTestUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GTreeNode getNode(GTree tree, String... path) {
|
public static GTreeNode getNode(GTree tree, String... path) {
|
||||||
GTreeRootNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
String rootName = path[0];
|
String rootName = path[0];
|
||||||
if (!rootNode.getName().equals(rootName)) {
|
if (!rootNode.getName().equals(rootName)) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
|
|
|
@ -188,7 +188,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
||||||
assertTrue("Did not find the data type chooser tree",
|
assertTrue("Did not find the data type chooser tree",
|
||||||
(provider instanceof DataTypeChooserDialog));
|
(provider instanceof DataTypeChooserDialog));
|
||||||
GTree gTree = (GTree) getInstanceField("tree", provider);
|
GTree gTree = (GTree) getInstanceField("tree", provider);
|
||||||
GTreeNode rootNode = gTree.getRootNode();
|
GTreeNode rootNode = gTree.getModelRoot();
|
||||||
waitForTree(gTree);
|
waitForTree(gTree);
|
||||||
final GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
final GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
||||||
final DataTypeNode doubleNode = (DataTypeNode) builtInNode.getChild("double");
|
final DataTypeNode doubleNode = (DataTypeNode) builtInNode.getChild("double");
|
||||||
|
@ -743,7 +743,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
||||||
|
|
||||||
private void pickSingleDataType(DataTypeChooserDialog chooserDialog) {
|
private void pickSingleDataType(DataTypeChooserDialog chooserDialog) {
|
||||||
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
|
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
|
||||||
GTreeNode rootNode = gTree.getRootNode();
|
GTreeNode rootNode = gTree.getModelRoot();
|
||||||
waitForTree(gTree);
|
waitForTree(gTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertEquals(1, children.size());// one archive
|
assertEquals(1, children.size());// one archive
|
||||||
|
@ -764,7 +764,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
||||||
|
|
||||||
private void pickFromMultipleDataTypes(DataTypeChooserDialog chooserDialog) {
|
private void pickFromMultipleDataTypes(DataTypeChooserDialog chooserDialog) {
|
||||||
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
|
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
|
||||||
GTreeNode rootNode = gTree.getRootNode();
|
GTreeNode rootNode = gTree.getModelRoot();
|
||||||
waitForTree(gTree);
|
waitForTree(gTree);
|
||||||
List<GTreeNode> children = rootNode.getChildren();
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
assertEquals(2, children.size());// two archives
|
assertEquals(2, children.size());// two archives
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.main;
|
package ghidra.framework.main;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@ -25,7 +24,8 @@ import javax.swing.tree.TreeModel;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.framework.main.datatree.ProjectDataTreePanel;
|
import ghidra.framework.main.datatree.ProjectDataTreePanel;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
|
@ -62,8 +62,8 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
Program p = builder.getProgram();
|
Program p = builder.getProgram();
|
||||||
rootFolder.createFile("notepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
|
rootFolder.createFile("notepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||||
rootFolder.createFile("XNotepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
|
rootFolder.createFile("XNotepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||||
for (int i = 0; i < names.length; i++) {
|
for (String name : names) {
|
||||||
rootFolder.createFile(names[i], p, TaskMonitorAdapter.DUMMY_MONITOR);
|
rootFolder.createFile(name, p, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||||
}
|
}
|
||||||
builder.dispose();
|
builder.dispose();
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
final AtomicBoolean result = new AtomicBoolean(false);
|
final AtomicBoolean result = new AtomicBoolean(false);
|
||||||
final GTree gTree = getGTree();
|
final GTree gTree = getGTree();
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
GTreeRootNode node = gTree.getRootNode();
|
GTreeNode node = gTree.getViewRoot();
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
gTree.expandPath(node);
|
gTree.expandPath(node);
|
||||||
gTree.setSelectedNode(node);
|
gTree.setSelectedNode(node);
|
||||||
|
@ -307,7 +307,7 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
final AtomicBoolean result = new AtomicBoolean(false);
|
final AtomicBoolean result = new AtomicBoolean(false);
|
||||||
final GTree gTree = getGTree();
|
final GTree gTree = getGTree();
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
GTreeRootNode root = gTree.getRootNode();
|
GTreeNode root = gTree.getModelRoot();
|
||||||
GTreeNode node = root.getChild(name);
|
GTreeNode node = root.getChild(name);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
gTree.expandPath(node);
|
gTree.expandPath(node);
|
||||||
|
|
|
@ -34,7 +34,6 @@ import docking.action.ToggleDockingAction;
|
||||||
import docking.test.AbstractDockingTest;
|
import docking.test.AbstractDockingTest;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeRootNode;
|
|
||||||
import docking.widgets.tree.support.*;
|
import docking.widgets.tree.support.*;
|
||||||
import ghidra.framework.data.DomainObjectAdapter;
|
import ghidra.framework.data.DomainObjectAdapter;
|
||||||
import ghidra.framework.main.FrontEndTool;
|
import ghidra.framework.main.FrontEndTool;
|
||||||
|
@ -58,7 +57,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
private TestEnv env;
|
private TestEnv env;
|
||||||
private DataTree tree;
|
private DataTree tree;
|
||||||
private DomainFolder rootFolder;
|
private DomainFolder rootFolder;
|
||||||
private GTreeRootNode rootNode;
|
private GTreeNode rootNode;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
@ -82,7 +81,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
rootFolder.createFile("tms", p, TaskMonitor.DUMMY);
|
rootFolder.createFile("tms", p, TaskMonitor.DUMMY);
|
||||||
p.release(this);
|
p.release(this);
|
||||||
|
|
||||||
rootNode = tree.getRootNode();
|
rootNode = tree.getModelRoot();
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,7 +716,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
performAction(selectAction, getDomainFileActionContext(), true);
|
performAction(selectAction, getDomainFileActionContext(), true);
|
||||||
waitForTree();
|
waitForTree();
|
||||||
|
|
||||||
BreadthFirstIterator it = new BreadthFirstIterator(tree, rootNode);
|
BreadthFirstIterator it = new BreadthFirstIterator(rootNode);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
GTreeNode node = it.next();
|
GTreeNode node = it.next();
|
||||||
assertTrue(tree.isPathSelected(node.getTreePath()));
|
assertTrue(tree.isPathSelected(node.getTreePath()));
|
||||||
|
|
|
@ -37,6 +37,7 @@ import docking.options.editor.OptionsPanel;
|
||||||
import docking.tool.util.DockingToolConstants;
|
import docking.tool.util.DockingToolConstants;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import docking.widgets.tree.GTree;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import generic.io.NullWriter;
|
import generic.io.NullWriter;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.data.DataPlugin;
|
import ghidra.app.plugin.core.data.DataPlugin;
|
||||||
|
@ -367,7 +368,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
// this is an instance of OptionsNode
|
// this is an instance of OptionsNode
|
||||||
GTree tree = (GTree) getInstanceField("gTree", optionsPanel);
|
GTree tree = (GTree) getInstanceField("gTree", optionsPanel);
|
||||||
Object keyBindingsNode = getGTreeNode(tree.getRootNode(), "Key Bindings");
|
Object keyBindingsNode = getGTreeNode(tree.getModelRoot(), "Key Bindings");
|
||||||
selectNode(tree, keyBindingsNode);
|
selectNode(tree, keyBindingsNode);
|
||||||
|
|
||||||
debug("ee");
|
debug("ee");
|
||||||
|
@ -407,17 +408,18 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
SwingUtilities.invokeAndWait(() -> tree.setSelectionPath(path));
|
SwingUtilities.invokeAndWait(() -> tree.setSelectionPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getGTreeNode(Object parent, String nodeName) throws Exception {
|
private GTreeNode getGTreeNode(GTreeNode parent, String nodeName) throws Exception {
|
||||||
List<?> children = (List<?>) getInstanceField("allChildrenList", parent);
|
if (!parent.isLoaded()) {
|
||||||
if (children == null) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (Object rootChild : children) {
|
|
||||||
|
List<GTreeNode> children = parent.getChildren();
|
||||||
|
for (GTreeNode rootChild : children) {
|
||||||
String name = (String) invokeInstanceMethod("getName", rootChild);
|
String name = (String) invokeInstanceMethod("getName", rootChild);
|
||||||
if (nodeName.equals(name)) {
|
if (nodeName.equals(name)) {
|
||||||
return rootChild;
|
return rootChild;
|
||||||
}
|
}
|
||||||
Object foundNode = getGTreeNode(rootChild, nodeName);
|
GTreeNode foundNode = getGTreeNode(rootChild, nodeName);
|
||||||
if (foundNode != null) {
|
if (foundNode != null) {
|
||||||
return foundNode;
|
return foundNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,8 @@ import docking.tool.ToolConstants;
|
||||||
import docking.widgets.MultiLineLabel;
|
import docking.widgets.MultiLineLabel;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import docking.widgets.table.RowObjectFilterModel;
|
import docking.widgets.table.RowObjectFilterModel;
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import generic.test.TestUtils;
|
import generic.test.TestUtils;
|
||||||
import ghidra.GhidraOptions;
|
import ghidra.GhidraOptions;
|
||||||
import ghidra.app.plugin.core.console.ConsolePlugin;
|
import ghidra.app.plugin.core.console.ConsolePlugin;
|
||||||
|
@ -836,8 +837,8 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForThreadedModel() throws InterruptedException {
|
private void waitForThreadedModel() throws InterruptedException {
|
||||||
GTreeRootNode root = (GTreeRootNode) treeModel.getRoot();
|
GTreeNode root = (GTreeNode) treeModel.getRoot();
|
||||||
GTree gTree = root.getGTree();
|
GTree gTree = root.getTree();
|
||||||
while (gTree.isBusy()) {
|
while (gTree.isBusy()) {
|
||||||
Thread.sleep(50);
|
Thread.sleep(50);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.bitpatterns.info;
|
package ghidra.bitpatterns.info;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.AbstractGTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ import resources.ResourceManager;
|
||||||
* An object of this class represents a node in a tree of instruction sequences.
|
* An object of this class represents a node in a tree of instruction sequences.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class FunctionBitPatternsGTreeNode extends AbstractGTreeNode {
|
public class FunctionBitPatternsGTreeNode extends GTreeNode {
|
||||||
|
|
||||||
private static final Icon DISABLED_ICON = ResourceManager.loadImage("images/ledred.png");
|
private static final Icon DISABLED_ICON = ResourceManager.loadImage("images/ledred.png");
|
||||||
|
|
||||||
|
@ -65,7 +64,7 @@ public class FunctionBitPatternsGTreeNode extends AbstractGTreeNode {
|
||||||
for (GTreeNode node : getChildren()) {
|
for (GTreeNode node : getChildren()) {
|
||||||
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
|
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
|
||||||
}
|
}
|
||||||
List<GTreeNode> children = getChildren();
|
List<GTreeNode> children = new ArrayList<>(getChildren());
|
||||||
Collections.sort(children);
|
Collections.sort(children);
|
||||||
setChildren(children);
|
setChildren(children);
|
||||||
//now set isLeaf
|
//now set isLeaf
|
||||||
|
|
|
@ -15,12 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.bitpatterns.info;
|
package ghidra.bitpatterns.info;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.widgets.tree.AbstractGTreeRootNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +29,7 @@ import docking.widgets.tree.GTreeNode;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class FunctionBitPatternsGTreeRootNode extends AbstractGTreeRootNode {
|
public class FunctionBitPatternsGTreeRootNode extends GTreeNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -54,7 +53,7 @@ public class FunctionBitPatternsGTreeRootNode extends AbstractGTreeRootNode {
|
||||||
for (GTreeNode node : getChildren()) {
|
for (GTreeNode node : getChildren()) {
|
||||||
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
|
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
|
||||||
}
|
}
|
||||||
List<GTreeNode> children = getChildren();
|
List<GTreeNode> children = new ArrayList<>(getChildren());
|
||||||
Collections.sort(children);
|
Collections.sort(children);
|
||||||
setChildren(children);
|
setChildren(children);
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,8 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
|
||||||
if (outputDirectory == null) {
|
if (outputDirectory == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fsbContext.getTree().runTask(
|
fsbContext.getTree()
|
||||||
|
.runTask(
|
||||||
monitor -> doExportToEclipse(fsrl, outputDirectory, monitor));
|
monitor -> doExportToEclipse(fsrl, outputDirectory, monitor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +279,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
|
||||||
writer.open();
|
writer.open();
|
||||||
try {
|
try {
|
||||||
// gTree.expandAll( node );
|
// gTree.expandAll( node );
|
||||||
writeFile(writer, node.getAllChildren());
|
writeFile(writer, node.getChildren());
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
writer.close();
|
writer.close();
|
||||||
|
@ -303,7 +304,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
|
||||||
writer.write(childFSRL.getName());
|
writer.write(childFSRL.getName());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
writeFile(writer, child.getAllChildren());
|
writeFile(writer, child.getChildren());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class TreeTestUtils {
|
||||||
if (text.equals(node.getName())) {
|
if (text.equals(node.getName())) {
|
||||||
return node.getTreePath();
|
return node.getTreePath();
|
||||||
}
|
}
|
||||||
List<GTreeNode> allChildren = node.getAllChildren();
|
List<GTreeNode> allChildren = node.getChildren();
|
||||||
for (GTreeNode childNode : allChildren) {
|
for (GTreeNode childNode : allChildren) {
|
||||||
TreePath treePath = findPathToText(tree, childNode, text);
|
TreePath treePath = findPathToText(tree, childNode, text);
|
||||||
if (treePath != null) {
|
if (treePath != null) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ import ghidra.util.*;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.html.HTMLElement;
|
import ghidra.util.html.HTMLElement;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
import util.CollectionUtils;
|
||||||
|
|
||||||
public class ErrLogExpandableDialog extends DialogComponentProvider {
|
public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
public static ImageIcon IMG_REPORT = ResourceManager.loadImage("images/report.png");
|
public static ImageIcon IMG_REPORT = ResourceManager.loadImage("images/report.png");
|
||||||
|
@ -246,10 +247,9 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (TreePath path : root.allPaths()) {
|
for (GTreeNode node : CollectionUtils.asIterable(root.iterator(true))) {
|
||||||
Object last = path.getLastPathComponent();
|
if (node instanceof ReportExceptionNode) {
|
||||||
if (last instanceof ReportExceptionNode) {
|
excTree.expandTree(node);
|
||||||
excTree.expandTree((GTreeNode) last);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,8 +406,8 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
indent += 1;
|
indent += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean doAll = (included == null || !containsAny(included, cur.getAllChildren()));
|
boolean doAll = (included == null || !containsAny(included, cur.getChildren()));
|
||||||
for (GTreeNode node : cur.getAllChildren()) {
|
for (GTreeNode node : cur.getChildren()) {
|
||||||
if (node instanceof NodeWithText && (doAll || included.contains(node))) {
|
if (node instanceof NodeWithText && (doAll || included.contains(node))) {
|
||||||
NodeWithText nwt = (NodeWithText) node;
|
NodeWithText nwt = (NodeWithText) node;
|
||||||
|
|
||||||
|
@ -427,7 +427,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ReportRootNode extends AbstractGTreeRootNode implements NodeWithText {
|
static class ReportRootNode extends GTreeNode implements NodeWithText {
|
||||||
protected Collection<? extends Throwable> report;
|
protected Collection<? extends Throwable> report;
|
||||||
protected String title;
|
protected String title;
|
||||||
protected boolean loaded = false;
|
protected boolean loaded = false;
|
||||||
|
@ -435,6 +435,9 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
public ReportRootNode(String title, Collection<? extends Throwable> report) {
|
public ReportRootNode(String title, Collection<? extends Throwable> report) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.report = report;
|
this.report = report;
|
||||||
|
for (Throwable exc : report) {
|
||||||
|
addNode(new ReportExceptionNode(exc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -442,16 +445,6 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void loadChildren() {
|
|
||||||
if (!loaded) {
|
|
||||||
loaded = true;
|
|
||||||
for (Throwable exc : report) {
|
|
||||||
addNode(new ReportExceptionNode(exc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon(boolean expanded) {
|
public Icon getIcon(boolean expanded) {
|
||||||
return IMG_REPORT;
|
return IMG_REPORT;
|
||||||
|
@ -483,7 +476,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ReportExceptionNode extends AbstractGTreeNode implements NodeWithText {
|
static class ReportExceptionNode extends GTreeLazyNode implements NodeWithText {
|
||||||
protected Throwable exc;
|
protected Throwable exc;
|
||||||
protected boolean loaded = false;
|
protected boolean loaded = false;
|
||||||
|
|
||||||
|
@ -497,22 +490,21 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void loadChildren() {
|
protected List<GTreeNode> generateChildren() {
|
||||||
if (!loaded) {
|
List<GTreeNode> list = new ArrayList<GTreeNode>();
|
||||||
loaded = true;
|
list.add(new ReportStackTraceNode(exc));
|
||||||
addNode(new ReportStackTraceNode(exc));
|
|
||||||
Throwable c = exc.getCause();
|
Throwable c = exc.getCause();
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
if (c instanceof MultipleCauses) {
|
if (c instanceof MultipleCauses) {
|
||||||
for (Throwable t : ((MultipleCauses) c).getCauses()) {
|
for (Throwable t : ((MultipleCauses) c).getCauses()) {
|
||||||
addNode(new ReportExceptionNode(t));
|
list.add(new ReportExceptionNode(t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
addNode(new ReportCauseNode(c));
|
list.add(new ReportCauseNode(c));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -553,7 +545,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ReportStackTraceNode extends AbstractGTreeNode implements NodeWithText {
|
static class ReportStackTraceNode extends GTreeLazyNode implements NodeWithText {
|
||||||
protected Throwable exc;
|
protected Throwable exc;
|
||||||
protected boolean loaded = false;
|
protected boolean loaded = false;
|
||||||
|
|
||||||
|
@ -567,13 +559,12 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadChildren() {
|
protected List<GTreeNode> generateChildren() {
|
||||||
if (!loaded) {
|
List<GTreeNode> list = new ArrayList<>();
|
||||||
loaded = true;
|
|
||||||
for (StackTraceElement te : exc.getStackTrace()) {
|
for (StackTraceElement te : exc.getStackTrace()) {
|
||||||
addNode(new ReportStackFrameNode(te));
|
list.add(new ReportStackFrameNode(te));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -628,7 +619,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ReportStackFrameNode extends AbstractGTreeNode implements NodeWithText {
|
static class ReportStackFrameNode extends GTreeNode implements NodeWithText {
|
||||||
private StackTraceElement te;
|
private StackTraceElement te;
|
||||||
|
|
||||||
public ReportStackFrameNode(StackTraceElement te) {
|
public ReportStackFrameNode(StackTraceElement te) {
|
||||||
|
|
|
@ -32,7 +32,8 @@ import docking.help.HelpService;
|
||||||
import docking.widgets.MultiLineLabel;
|
import docking.widgets.MultiLineLabel;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.label.GIconLabel;
|
import docking.widgets.label.GIconLabel;
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTree;
|
||||||
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.internal.DefaultGTreeDataTransformer;
|
import docking.widgets.tree.internal.DefaultGTreeDataTransformer;
|
||||||
import ghidra.framework.options.*;
|
import ghidra.framework.options.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
@ -231,7 +232,7 @@ public class OptionsPanel extends JPanel {
|
||||||
public void displayCategory(String category, String filterText) {
|
public void displayCategory(String category, String filterText) {
|
||||||
String escapedDelimiter = Pattern.quote(Options.DELIMITER_STRING);
|
String escapedDelimiter = Pattern.quote(Options.DELIMITER_STRING);
|
||||||
|
|
||||||
GTreeRootNode root = gTree.getRootNode();
|
GTreeNode root = gTree.getModelRoot();
|
||||||
category = root.getName() + Options.DELIMITER_STRING + category;
|
category = root.getName() + Options.DELIMITER_STRING + category;
|
||||||
String[] categories = category.split(escapedDelimiter);
|
String[] categories = category.split(escapedDelimiter);
|
||||||
gTree.setFilterText(filterText);
|
gTree.setFilterText(filterText);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,17 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package docking.options.editor;
|
package docking.options.editor;
|
||||||
|
|
||||||
import ghidra.framework.options.Options;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
import docking.widgets.tree.GTreeNode;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
|
||||||
class OptionsRootTreeNode extends OptionsTreeNode implements GTreeRootNode {
|
class OptionsRootTreeNode extends OptionsTreeNode {
|
||||||
private Options[] options;
|
private Options[] options;
|
||||||
private GTree tree;
|
|
||||||
|
|
||||||
OptionsRootTreeNode(String name, Options[] options) {
|
OptionsRootTreeNode(String name, Options[] options) {
|
||||||
super(name, null);
|
super(name, null);
|
||||||
|
@ -55,14 +52,4 @@ class OptionsRootTreeNode extends OptionsTreeNode implements GTreeRootNode {
|
||||||
protected JComponent getEditorComponent() {
|
protected JComponent getEditorComponent() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTree getGTree() {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGTree(GTree tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ class OptionsTreeNode extends GTreeLazyNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLeaf() {
|
public boolean isLeaf() {
|
||||||
return getAllChildCount() == 0;
|
return getChildCount() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2071,7 +2071,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GTreeNode getNode(GTree tree, String... path) {
|
public static GTreeNode getNode(GTree tree, String... path) {
|
||||||
GTreeRootNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getModelRoot();
|
||||||
String rootName = path[0];
|
String rootName = path[0];
|
||||||
if (!rootNode.getName().equals(rootName)) {
|
if (!rootNode.getName().equals(rootName)) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
|
|
|
@ -1,484 +0,0 @@
|
||||||
/* ###
|
|
||||||
* 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 docking.widgets.tree;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import javax.swing.tree.TreePath;
|
|
||||||
|
|
||||||
import docking.widgets.tree.support.GTreeFilter;
|
|
||||||
import ghidra.util.exception.AssertException;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for GTNodes. To create a simple GTNode where nodes will be added immediately
|
|
||||||
* using the addNode() methods, simply extend this class and implement the following methods:
|
|
||||||
|
|
||||||
* <ul>
|
|
||||||
* <li>getName()</li>
|
|
||||||
* <li>getToolTip()</li>
|
|
||||||
* <li>isLeaf()</li>
|
|
||||||
* <li>getIcon()</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <a name="usage"></a>Usage Notes:
|
|
||||||
* <ul>
|
|
||||||
* <li>The <b><tt>equals()</tt></b> method: The <tt>GTree</tt> has the ability to remember expanded and
|
|
||||||
* selected states. This will only work if the nodes in the saved state can be matched
|
|
||||||
* with the nodes in the <tt>GTree</tt>. Java will do this by using the <tt>equals()</tt> method.
|
|
||||||
* There is a potential problem with this usage. If nodes within the <tt>GTree</tt> get rebuilt (
|
|
||||||
* i.e., new nodes are created), then, by default, the expanded and selected state
|
|
||||||
* feature will be unable to find the correct nodes, since the default <tt>equals()</tt>
|
|
||||||
* method on <tt>GTreeNode</tt> performs a comparison based upon instances. To fix this problem you
|
|
||||||
* must override the <tt>equals()</tt> method that can find the same logical nodes when
|
|
||||||
* the instances in memory have changed; typically this is done by overriding <tt>equals()</tt>
|
|
||||||
* to compare by node name.
|
|
||||||
* <p><br>
|
|
||||||
* <p>
|
|
||||||
* The <tt>GTreeNode</tt> has already overridden {@link #hashCode()} so that the node name is
|
|
||||||
* used to generate the correct value. If you override the {@link #equals(Object)} method,
|
|
||||||
* <b>and you do not compare only by {@link #getName()}, then you must also override the
|
|
||||||
* {@link #hashCode()} method to generate a value based upon the same algorithm used by the
|
|
||||||
* new <tt>equals()</tt> method.</b>
|
|
||||||
* <p><br>
|
|
||||||
* <p>
|
|
||||||
* As a rule of thumb, unless you want to allow multiple nodes under one parent with the
|
|
||||||
* same name, then it is a swell idea to override the <tt>equals()</tt> method to compare on
|
|
||||||
* {@link #getName()}, as outlined above.
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public abstract class AbstractGTreeNode extends CoreGTreeNode {
|
|
||||||
|
|
||||||
private AtomicBoolean isFiltering = new AtomicBoolean();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This will be called when it is time to load children. Some subclasses may not use this
|
|
||||||
* method, but may instead have children externally added.
|
|
||||||
*/
|
|
||||||
protected void loadChildren() {
|
|
||||||
// I don't use this...subclasses might
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addNode(GTreeNode node) {
|
|
||||||
addNode(-1, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addNode(int index, GTreeNode node) {
|
|
||||||
doAddNode(index, node);
|
|
||||||
GTreeFilter filter = getFilter();
|
|
||||||
if (filter != null) {
|
|
||||||
GTree tree = getTree();
|
|
||||||
tree.scheduleFilterTask(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(GTreeNode node) {
|
|
||||||
return getName().compareToIgnoreCase(node.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<GTreeNode> getAllChildren() {
|
|
||||||
// TODO this seemed like an unnecessary and inconsistent optimization, as
|
|
||||||
// the loadChildren() call will not perform excess work when called repeatedly, even
|
|
||||||
// if the children are empty (i.e., isLeaf()); remove after a bit
|
|
||||||
// if (isLeaf()) {
|
|
||||||
// return Collections.emptyList();
|
|
||||||
// }
|
|
||||||
loadChildren();
|
|
||||||
return doGetAllChildren();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<GTreeNode> getChildren() {
|
|
||||||
// TODO this seemed like an unnecessary and inconsistent optimization, as
|
|
||||||
// the loadChildren() call will not perform excess work when called repeatedly, even
|
|
||||||
// if the children are empty (i.e., isLeaf()); remove after a bit
|
|
||||||
// if (isLeaf()) {
|
|
||||||
// return Collections.emptyList();
|
|
||||||
// }
|
|
||||||
loadChildren();
|
|
||||||
return doGetActiveChildren();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getChildCount() {
|
|
||||||
loadChildren();
|
|
||||||
return doGetChildCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAllChildCount() {
|
|
||||||
loadChildren();
|
|
||||||
return doGetAllChildCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTreeNode getChild(String name) {
|
|
||||||
List<GTreeNode> children = getChildren();
|
|
||||||
for (GTreeNode child : children) {
|
|
||||||
if (child.getName().equals(name)) {
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTreeNode getChild(int index) {
|
|
||||||
loadChildren();
|
|
||||||
return doGetChild(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getNodeCount() {
|
|
||||||
List<GTreeNode> children = getChildren();
|
|
||||||
int count = 1;
|
|
||||||
for (GTreeNode child : children) {
|
|
||||||
count += child.getNodeCount();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLeafCount() {
|
|
||||||
if (isLeaf()) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<GTreeNode> children = getChildren();
|
|
||||||
int count = 0;
|
|
||||||
for (GTreeNode child : children) {
|
|
||||||
count += child.getLeafCount();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIndexInParent() {
|
|
||||||
GTreeNode myParent = getParent();
|
|
||||||
if (myParent != null) {
|
|
||||||
return myParent.getIndexOfChild(this);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIndexOfChild(GTreeNode node) {
|
|
||||||
loadChildren();
|
|
||||||
return doGetIndexOfChild(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TreePath getTreePath() {
|
|
||||||
return new TreePath(getPathToRoot(this, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeAll() {
|
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<GTreeNode> allChildren = getAllChildren();
|
|
||||||
|
|
||||||
doSetChildren(null, true);
|
|
||||||
for (GTreeNode gTreeNode : allChildren) {
|
|
||||||
gTreeNode.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setChildren(List<GTreeNode> childList) {
|
|
||||||
doSetChildren(childList, true);
|
|
||||||
if (isFiltering.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GTreeFilter filter = getFilter();
|
|
||||||
if (filter != null) {
|
|
||||||
GTree tree = getTree();
|
|
||||||
tree.scheduleFilterTask(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAncestor(GTreeNode node) {
|
|
||||||
GTreeNode nodeParent = node.getParent();
|
|
||||||
while (nodeParent != null) {
|
|
||||||
if (nodeParent.equals(this)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
nodeParent = nodeParent.getParent();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AllPathsIterator implements Iterator<TreePath> {
|
|
||||||
private TreePath ancestry;
|
|
||||||
private TreePath nextPath;
|
|
||||||
private Iterator<GTreeNode> childIt = null;
|
|
||||||
private Iterator<TreePath> childPathIt = null;
|
|
||||||
|
|
||||||
public AllPathsIterator(TreePath path) {
|
|
||||||
ancestry = path;
|
|
||||||
nextPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return nextPath != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TreePath next() {
|
|
||||||
TreePath n = nextPath;
|
|
||||||
loadNext();
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void loadNext() {
|
|
||||||
if (childIt == null) {
|
|
||||||
Object obj = ancestry.getLastPathComponent();
|
|
||||||
assert obj instanceof GTreeNode;
|
|
||||||
childIt = ((GTreeNode) obj).iterator();
|
|
||||||
}
|
|
||||||
if (childPathIt == null || !childPathIt.hasNext()) {
|
|
||||||
if (childIt.hasNext()) {
|
|
||||||
childPathIt = new AllPathsIterator(ancestry.pathByAddingChild(childIt.next()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
childPathIt = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (childPathIt != null && childPathIt.hasNext()) {
|
|
||||||
nextPath = childPathIt.next();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nextPath = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<TreePath> allPaths() {
|
|
||||||
return new AllPathsIterable(new TreePath(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<GTreeNode> iterator() {
|
|
||||||
return getChildren().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void filter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
|
|
||||||
throws CancelledException {
|
|
||||||
|
|
||||||
if (isFiltering.get()) {
|
|
||||||
stopCurrentFilterAndRestart();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFiltering.set(true);
|
|
||||||
try {
|
|
||||||
doFilter(filter, monitor, min, max);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
isFiltering.set(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopCurrentFilterAndRestart() {
|
|
||||||
GTree tree = getTree();
|
|
||||||
if (tree != null) {
|
|
||||||
// assume that filtering will be done later when we are made be part of a tree
|
|
||||||
tree.refilter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doFilter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
|
|
||||||
throws CancelledException {
|
|
||||||
|
|
||||||
if (isLeaf()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<GTreeNode> allChildren = getAllChildren();
|
|
||||||
if (allChildren.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<GTreeNode> newChildren = allChildren;
|
|
||||||
try {
|
|
||||||
setInProgress();
|
|
||||||
List<GTreeNode> filteredChildren = new ArrayList<GTreeNode>();
|
|
||||||
monitor.setProgress(min);
|
|
||||||
int progressChunkSize = (max - min) / (allChildren.size());
|
|
||||||
int childMin = min;
|
|
||||||
for (GTreeNode child : allChildren) {
|
|
||||||
monitor.checkCanceled();
|
|
||||||
child.filter(filter, monitor, childMin, childMin + progressChunkSize);
|
|
||||||
if (filter.acceptsNode(child) || child.getChildCount() > 0) {
|
|
||||||
filteredChildren.add(child);
|
|
||||||
}
|
|
||||||
childMin += progressChunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
newChildren = filteredChildren;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// if an exception occurs, then the default children will be restored
|
|
||||||
doSetActiveChildren(newChildren);
|
|
||||||
}
|
|
||||||
monitor.setProgress(max);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearFilter() {
|
|
||||||
if (isLeaf()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isChildrenLoaded()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<GTreeNode> allChildren = getAllChildren();
|
|
||||||
if (allChildren.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setInProgress();
|
|
||||||
for (GTreeNode child : allChildren) {
|
|
||||||
child.clearFilter();
|
|
||||||
}
|
|
||||||
doResetActiveChildren();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFilteredOut() {
|
|
||||||
if (getParent() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return getIndexInParent() < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void valueChanged(Object newValue) {
|
|
||||||
// Overridden in subclasses
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEditable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
/**
|
|
||||||
* The hashCode() method has been overridden so that it will work in the hashtables inside of JTree.
|
|
||||||
* This assumes that if the .equals method is overridden, then the names will match which will
|
|
||||||
* make this hashCode implementation valid. If for some reason .equals is overriden such that
|
|
||||||
* two node may be equal even if their names don't match, then the hashCode method must also be
|
|
||||||
* overridden.
|
|
||||||
*
|
|
||||||
* @see <a href="#usage">GTreeNode Usage</a>
|
|
||||||
*/
|
|
||||||
public int hashCode() {
|
|
||||||
return getName().hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTreeRootNode getRoot() {
|
|
||||||
GTreeNode myParent = getParent();
|
|
||||||
if (myParent == null) {
|
|
||||||
if (this instanceof GTreeRootNode) {
|
|
||||||
return (GTreeRootNode) this;
|
|
||||||
}
|
|
||||||
throw new AssertException(
|
|
||||||
"Found a root node that is not an instance of GTreeRootNode--stop it!");
|
|
||||||
}
|
|
||||||
return myParent.getRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GTree getTree() {
|
|
||||||
GTreeNode myParent = getParent();
|
|
||||||
if (myParent != null) {
|
|
||||||
return myParent.getTree();
|
|
||||||
}
|
|
||||||
if (this instanceof GTreeRootNode) {
|
|
||||||
return ((GTreeRootNode) this).getGTree();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private GTreeNode[] getPathToRoot(GTreeNode node, int depth) {
|
|
||||||
GTreeNode[] returnNodes;
|
|
||||||
|
|
||||||
/* Check for null, in case someone passed in a null node, or
|
|
||||||
they passed in an element that isn't rooted at root. */
|
|
||||||
if (node == null) {
|
|
||||||
if (depth == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
returnNodes = new GTreeNode[depth];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
depth++;
|
|
||||||
returnNodes = getPathToRoot(node.getParent(), depth);
|
|
||||||
returnNodes[returnNodes.length - depth] = node;
|
|
||||||
}
|
|
||||||
return returnNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected GTreeFilter getFilter() {
|
|
||||||
GTree tree = getTree();
|
|
||||||
if (tree != null) {
|
|
||||||
return tree.getFilter();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class AllPathsIterable implements Iterable<TreePath> {
|
|
||||||
private TreePath path;
|
|
||||||
|
|
||||||
public AllPathsIterable(TreePath path) {
|
|
||||||
this.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<TreePath> iterator() {
|
|
||||||
return new AllPathsIterator(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,385 +15,199 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import docking.widgets.tree.internal.InProgressGTreeNode;
|
import docking.widgets.tree.internal.InProgressGTreeNode;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is not meant to be subclassed directly. Instead, you should extend
|
* This class exists to help prevent threading errors in {@link GTreeNode} and subclasses,
|
||||||
* {@link AbstractGTreeNode}.
|
* by privately maintaining synchronous access to the parent and children of a node.
|
||||||
* <p>
|
* <P>
|
||||||
* This class is responsible for mutating/managing the children and parent of this node. These
|
* This implementation uses a {@link CopyOnWriteArrayList} to store its children. The theory is
|
||||||
* items are sensitive to threading issues, which this class is designed to handle.
|
* that this will allow direct thread-safe access to the children without having to worry about
|
||||||
* <p>
|
* {@link ConcurrentModificationException}s. Also, the assumption is that accessing the children
|
||||||
* The pattern used by this class is to create <tt>doXXX</tt> methods for the public mutator
|
* will occur much more frequently than modifying the children. This should only be a problem if
|
||||||
* methods of the {@link GTreeNode} interface.
|
* a direct descendent of AbstractGTreeNode creates it children by calling
|
||||||
|
* addNode many times. But in that case, the tree should be using Lazy or
|
||||||
|
* SlowLoading nodes which always load into another list first and all the children will be set
|
||||||
|
* on a node in a single operation.
|
||||||
|
* <P>
|
||||||
|
* Subclasses that need access to the children
|
||||||
|
* can call the {@link #children()} method which will ensure that the children are
|
||||||
|
* loaded (not null). Since this class uses a {@link CopyOnWriteArrayList}, subclasses that call
|
||||||
|
* the {@link #children()} method can safely operate and iterate on the list they get back without
|
||||||
|
* having to worry about getting a {@link ConcurrentModificationException}.
|
||||||
|
* <P>
|
||||||
|
* This class uses synchronization to assure that the parent/children relationship is stable across
|
||||||
|
* threads. To avoid deadlocks, the sychronization strategy is that if you have the lock on
|
||||||
|
* a parent node, you can safely acquire the lock on any of its descendants, put never its
|
||||||
|
* ancestors. To facilitate this strategy, the {@link #getParent()} is not synchronized, but it
|
||||||
|
* is made volatile to assure the current value is always used.
|
||||||
*/
|
*/
|
||||||
abstract class CoreGTreeNode implements GTreeNode {
|
abstract class CoreGTreeNode implements Cloneable {
|
||||||
private static InProgressGTreeNode IN_PROGRESS_NODE = new InProgressGTreeNode();
|
// the parent is volatile to facilitate the synchronization strategy (see comments above)
|
||||||
private static List<GTreeNode> IN_PROGRESS_CHILDREN =
|
private volatile GTreeNode parent;
|
||||||
Collections.unmodifiableList(Arrays.asList(new GTreeNode[] { IN_PROGRESS_NODE }));
|
private List<GTreeNode> children;
|
||||||
|
|
||||||
private GTreeNode parent;
|
/**
|
||||||
private List<GTreeNode> allChildrenList = null;
|
* Returns the parent of this node.
|
||||||
private List<GTreeNode> activeChildrenList = null;
|
*
|
||||||
|
* Note: this method is deliberately not synchronized (See comments above)
|
||||||
@Override
|
* @return the parent of this node.
|
||||||
public synchronized GTreeNode getParent() {
|
*/
|
||||||
|
public final GTreeNode getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void dispose() {
|
* Sets the parent of this node. This method should only be used by a parent
|
||||||
parent = null;
|
* node when a new child is added to that parent node.
|
||||||
if (allChildrenList == null) {
|
* @param parent the node that this node is being added to.
|
||||||
return;
|
*/
|
||||||
|
synchronized final void setParent(GTreeNode parent) {
|
||||||
|
if (this.parent != null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Attempted to assign a node to a parent more than once!");
|
||||||
}
|
}
|
||||||
for (GTreeNode node : allChildrenList) {
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// provides direct access to the children list
|
||||||
|
protected final List<GTreeNode> children() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The generateChildren must be called outside the synchronized scope because
|
||||||
|
// if it is slow it will lock out other threads. Keep in mind that if this is
|
||||||
|
// called outside the swing thread then this doesn't return
|
||||||
|
// until the work is completed (even for slow loading nodes - they only offload
|
||||||
|
// the children loading in another task if called on the swing thread)
|
||||||
|
List<GTreeNode> newChildren = generateChildren();
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
// null implies cancelled
|
||||||
|
if (newChildren == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be tricky. If we are in the swing thread and the generate children
|
||||||
|
// is deferred to a background thread and we are about to set an in-progress node,
|
||||||
|
// then it is possible that the background thread got here first and we are about
|
||||||
|
// to overwrite the actual children with an in-progress node. Check for that case.
|
||||||
|
if (isInProgress(newChildren) && children != null) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
doSetChildren(newChildren);
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses implement this method to initially load the children.
|
||||||
|
* @return a list of the initial children for this node.
|
||||||
|
*/
|
||||||
|
protected abstract List<GTreeNode> generateChildren();
|
||||||
|
|
||||||
|
protected synchronized void doSetChildren(List<GTreeNode> childList) {
|
||||||
|
List<GTreeNode> oldChildren = children;
|
||||||
|
children = null;
|
||||||
|
|
||||||
|
if (oldChildren != null) {
|
||||||
|
for (GTreeNode node : oldChildren) {
|
||||||
|
node.setParent(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childList != null) {
|
||||||
|
for (GTreeNode node : childList) {
|
||||||
|
node.setParent((GTreeNode) this);
|
||||||
|
}
|
||||||
|
children = new CopyOnWriteArrayList<GTreeNode>(childList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldChildren != null) {
|
||||||
|
for (GTreeNode node : oldChildren) {
|
||||||
node.dispose();
|
node.dispose();
|
||||||
}
|
}
|
||||||
allChildrenList = null;
|
|
||||||
activeChildrenList = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized boolean isInProgress() {
|
|
||||||
return activeChildrenList == IN_PROGRESS_CHILDREN;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setInProgress() {
|
|
||||||
doSetActiveChildren(IN_PROGRESS_CHILDREN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean isChildrenLoadedOrInProgress() {
|
|
||||||
return activeChildrenList != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized boolean isChildrenLoaded() {
|
|
||||||
return allChildrenList != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized int doGetChildCount() {
|
|
||||||
if (activeChildrenList != null) {
|
|
||||||
return activeChildrenList.size();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized int doGetAllChildCount() {
|
|
||||||
if (allChildrenList != null) {
|
|
||||||
return allChildrenList.size();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized List<GTreeNode> doGetAllChildren() {
|
|
||||||
if (allChildrenList == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return new ArrayList<GTreeNode>(allChildrenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized List<GTreeNode> doGetActiveChildren() {
|
|
||||||
if (activeChildrenList == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return new ArrayList<GTreeNode>(activeChildrenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized GTreeNode doGetChild(int index) {
|
|
||||||
if (activeChildrenList == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (index < 0 || index >= activeChildrenList.size()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return activeChildrenList.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized int doGetIndexOfChild(GTreeNode node) {
|
|
||||||
if (activeChildrenList == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return activeChildrenList.indexOf(node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses can override this method to perform faster lookups of a node; for
|
* Creates a clone of this node. The clone should contain a shallow copy of all the node's
|
||||||
* example, if the subclass has a sorted list of children, then a binary search can
|
* attributes except that the parent and children are null.
|
||||||
* be used.
|
* @return the clone of this object.
|
||||||
*
|
* @throws CloneNotSupportedException if some implementation prevents itself from being cloned.
|
||||||
* @param node the node whose index we seek
|
|
||||||
* @param children the children who contain the given node (may be null)
|
|
||||||
* @return the index of the given child in the given list
|
|
||||||
*/
|
*/
|
||||||
protected synchronized int doGetIndexOfChild(GTreeNode node, List<GTreeNode> children) {
|
@Override
|
||||||
|
public GTreeNode clone() throws CloneNotSupportedException {
|
||||||
|
CoreGTreeNode clone = (GTreeNode) super.clone();
|
||||||
|
clone.parent = null;
|
||||||
|
clone.children = null;
|
||||||
|
return (GTreeNode) clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
|
||||||
|
List<GTreeNode> oldChildren;
|
||||||
|
synchronized (this) {
|
||||||
|
oldChildren = children;
|
||||||
|
children = null;
|
||||||
|
parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldChildren != null) {
|
||||||
|
for (GTreeNode node : oldChildren) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
oldChildren.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the node is in the process of loading its children.
|
||||||
|
* See {@link GTreeSlowLoadingNode}
|
||||||
|
* @return true if the node is in the process of loading its children.
|
||||||
|
*/
|
||||||
|
public synchronized final boolean isInProgress() {
|
||||||
|
return isInProgress(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the children for this node have been loaded yet. Some GTree nodes are lazy in that they
|
||||||
|
* don't load their children until needed. Nodes that have the IN_PROGRESS node as it child
|
||||||
|
* is considered loaded if in the swing thread, otherwise they are considered not loaded.
|
||||||
|
* @return true if the children for this node have been loaded.
|
||||||
|
*/
|
||||||
|
public synchronized boolean isLoaded() {
|
||||||
if (children == null) {
|
if (children == null) {
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
return children.indexOf(node);
|
if (Swing.isSwingThread()) {
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// Setter/Mutator Methods
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
protected void doAddNode(final int index, final GTreeNode child) {
|
|
||||||
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
swingAddNode(index, child);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemUtilities.runSwingNow(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
swingAddNode(index, child);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void swingAddNode(int index, GTreeNode child) {
|
|
||||||
//
|
|
||||||
// The following code is 'Swing Atomic'--it all (manipulation and notification) happens
|
|
||||||
// in the Swing thread together, which synchronizes it with other Swing operations.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Synchronized so that the accessor methods do not try to read while we are writing.
|
|
||||||
synchronized (this) {
|
|
||||||
if (allChildrenList == null) {
|
|
||||||
allChildrenList = new ArrayList<GTreeNode>();
|
|
||||||
activeChildrenList = allChildrenList;
|
|
||||||
}
|
|
||||||
if (allChildrenList.contains(child)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((CoreGTreeNode) child).parent = this;
|
|
||||||
|
|
||||||
if (index < 0 || index >= allChildrenList.size()) {
|
|
||||||
index = allChildrenList.size();
|
|
||||||
}
|
|
||||||
allChildrenList.add(index, child);
|
|
||||||
}
|
|
||||||
|
|
||||||
// can't be in synchronized block!
|
|
||||||
fireNodeAdded(this, child);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeNode(final GTreeNode node) {
|
|
||||||
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
swingRemoveNode(node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemUtilities.runSwingNow(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
swingRemoveNode(node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void swingRemoveNode(GTreeNode node) {
|
|
||||||
int index;
|
|
||||||
synchronized (this) {
|
|
||||||
((CoreGTreeNode) node).parent = null;
|
|
||||||
if (activeChildrenList == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = activeChildrenList.indexOf(node);
|
|
||||||
if (index >= 0) {
|
|
||||||
activeChildrenList.remove(index);
|
|
||||||
}
|
|
||||||
if (allChildrenList != activeChildrenList && allChildrenList != null) {
|
|
||||||
allChildrenList.remove(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// can't be in synchronized block!
|
|
||||||
if (index >= 0) {
|
|
||||||
fireNodeRemoved(this, node, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doSetChildren(final List<GTreeNode> childList, final boolean notify) {
|
|
||||||
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
swingSetChildren(childList, notify, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemUtilities.runSwingNow(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
swingSetChildren(childList, notify, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void swingSetChildren(List<GTreeNode> childList, boolean notify,
|
|
||||||
boolean onlyIfInProgress) {
|
|
||||||
//
|
|
||||||
// The following code is 'Swing Atomic'--it all (manipulation and notification) happens
|
|
||||||
// in the Swing thread together, which synchronizes it with other Swing operations.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Synchronized so that the accessor methods do not try to read while we are writing.
|
|
||||||
synchronized (this) {
|
|
||||||
if (childList == null) {
|
|
||||||
allChildrenList = null;
|
|
||||||
activeChildrenList = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (onlyIfInProgress && !isInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (GTreeNode child : childList) {
|
|
||||||
((CoreGTreeNode) child).parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
allChildrenList = new ArrayList<GTreeNode>(childList);
|
|
||||||
activeChildrenList = allChildrenList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// can't be in synchronized block!
|
|
||||||
if (notify) {
|
|
||||||
notifyNodeStructureChanged(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doSetActiveChildren(final List<GTreeNode> childList) {
|
|
||||||
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
swingSetActiveChilren(childList);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemUtilities.runSwingNow(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
swingSetActiveChilren(childList);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void swingSetActiveChilren(List<GTreeNode> childList) {
|
|
||||||
//
|
|
||||||
// The following code is 'Swing Atomic'--it all (manipulation and notification) happens
|
|
||||||
// in the Swing thread together, which synchronizes it with other Swing operations.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Synchronized so that the accessor methods do not try to read while we are writing.
|
|
||||||
synchronized (this) {
|
|
||||||
activeChildrenList = childList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// can't be in synchronized block!
|
|
||||||
notifyNodeStructureChanged(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method to clear any filtered items by restoring the active children of this
|
|
||||||
* node to be the complete set of children.
|
|
||||||
*/
|
|
||||||
protected void doResetActiveChildren() {
|
|
||||||
doSetActiveChildren(allChildrenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// Utility Methods
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void fireNodeChanged(final GTreeNode parentNode, final GTreeNode node) {
|
|
||||||
|
|
||||||
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
notifyNodeChanged(parentNode, node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyNodeChanged(GTreeNode parentNode, GTreeNode node) {
|
|
||||||
if (isAnyAncestorInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GTree tree = getTree();
|
|
||||||
if (isInValidTree(tree)) {
|
|
||||||
tree.getModel().fireNodeDataChanged(parentNode, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInValidTree(GTree tree) {
|
|
||||||
return tree != null && !tree.isDisposed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void fireNodeStructureChanged(final GTreeNode node) {
|
|
||||||
|
|
||||||
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
notifyNodeStructureChanged(node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyNodeStructureChanged(GTreeNode node) {
|
|
||||||
if (isAnyAncestorInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GTree tree = getTree();
|
|
||||||
if (isInValidTree(tree)) {
|
|
||||||
tree.getModel().fireNodeStructureChanged(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fireNodeAdded(GTreeNode parentNode, GTreeNode newNode) {
|
|
||||||
// assumption: we are always called in the Swing thread.
|
|
||||||
if (!isAnyAncestorInProgress()) {
|
|
||||||
GTree tree = getTree();
|
|
||||||
if (isInValidTree(tree)) {
|
|
||||||
tree.getModel().fireNodeAdded(parentNode, newNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fireNodeRemoved(GTreeNode parentNode, GTreeNode removedNode, int deletedChildIndex) {
|
|
||||||
// assumption: we are always called in the Swing thread.
|
|
||||||
if (!isAnyAncestorInProgress()) {
|
|
||||||
GTree tree = getTree();
|
|
||||||
if (isInValidTree(tree)) {
|
|
||||||
tree.getModel().fireNodeRemoved(parentNode, removedNode, deletedChildIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAnyAncestorInProgress() {
|
|
||||||
GTreeNode node = this;
|
|
||||||
while (node != null) {
|
|
||||||
if (node.isInProgress()) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
node = node.getParent();
|
return !isInProgress(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the node is in the process of loading its children. For nodes
|
||||||
|
* that directly extend GTreeNode, this is always false. See {@link GTreeSlowLoadingNode}
|
||||||
|
* for information on nodes that that can be in the progress of loading.
|
||||||
|
* @param childList the list to test.
|
||||||
|
* @return true if the node is in the progress of loading its children.
|
||||||
|
*/
|
||||||
|
private boolean isInProgress(List<GTreeNode> childList) {
|
||||||
|
if (childList != null && childList.size() == 1 &&
|
||||||
|
childList.get(0) instanceof InProgressGTreeNode) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// End Utility Methods
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.USER_GENERATED;
|
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.*;
|
||||||
import static ghidra.util.SystemUtilities.runSwingNow;
|
import static ghidra.util.SystemUtilities.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.dnd.Autoscroll;
|
import java.awt.dnd.Autoscroll;
|
||||||
|
@ -62,7 +62,15 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
* this variable around, we can give this node to clients, regardless of the root node
|
* this variable around, we can give this node to clients, regardless of the root node
|
||||||
* visible in the tree.
|
* visible in the tree.
|
||||||
*/
|
*/
|
||||||
private GTreeRootNode realRootNode;
|
private GTreeNode realRootNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rootParent is a node that is assigned as the parent to the realRootNode. It's primary purpose is
|
||||||
|
* to allow nodes access to the tree. It overrides the getTree() method on GTreeNode to return
|
||||||
|
* this tree. This eliminated the need for clients to create special root nodes that had
|
||||||
|
* public setTree/getTree methods.
|
||||||
|
*/
|
||||||
|
private GTreeRootParentNode rootParent = new GTreeRootParentNode(this);
|
||||||
|
|
||||||
private JScrollPane scrollPane;
|
private JScrollPane scrollPane;
|
||||||
private GTreeRenderer renderer;
|
private GTreeRenderer renderer;
|
||||||
|
@ -90,9 +98,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
private GTreeFilter filter;
|
private GTreeFilter filter;
|
||||||
private GTreeFilterProvider filterProvider;
|
private GTreeFilterProvider filterProvider;
|
||||||
private List<GTreeNode> nodesToBeFiltered = new ArrayList<>();
|
|
||||||
private SwingUpdateManager filterUpdateManager;
|
private SwingUpdateManager filterUpdateManager;
|
||||||
private int MAX_BUFFERED_FILTERED = 10;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a GTree with the given root node. The created GTree will use a threaded model
|
* Creates a GTree with the given root node. The created GTree will use a threaded model
|
||||||
|
@ -100,13 +106,13 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
*
|
*
|
||||||
* @param root The root node of the tree.
|
* @param root The root node of the tree.
|
||||||
*/
|
*/
|
||||||
public GTree(GTreeRootNode root) {
|
public GTree(GTreeNode root) {
|
||||||
uniquePreferenceKey = generateFilterPreferenceKey();
|
uniquePreferenceKey = generateFilterPreferenceKey();
|
||||||
this.realRootNode = root;
|
this.realRootNode = root;
|
||||||
monitor = new TaskMonitorComponent();
|
monitor = new TaskMonitorComponent();
|
||||||
monitor.setShowProgressValue(false);// the tree's progress is fabricated--don't paint it
|
monitor.setShowProgressValue(false);// the tree's progress is fabricated--don't paint it
|
||||||
worker = new PriorityWorker("GTree Worker", monitor);
|
worker = new PriorityWorker("GTree Worker", monitor);
|
||||||
root.setGTree(this);
|
root.setParent(rootParent);
|
||||||
this.model = new GTreeModel(root);
|
this.model = new GTreeModel(root);
|
||||||
worker.setBusyListener(this);
|
worker.setBusyListener(this);
|
||||||
init();
|
init();
|
||||||
|
@ -115,7 +121,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
(windowManager, provider) -> filterProvider.loadFilterPreference(windowManager,
|
(windowManager, provider) -> filterProvider.loadFilterPreference(windowManager,
|
||||||
uniquePreferenceKey));
|
uniquePreferenceKey));
|
||||||
|
|
||||||
filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> performNodeFiltering());
|
filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> updateModelFilter());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,7 +161,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
return localMonitor;
|
return localMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TaskMonitorAdapter.DUMMY_MONITOR;
|
return TaskMonitor.DUMMY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -166,6 +172,14 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
filterProvider.setEnabled(enabled);
|
filterProvider.setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns tree event notifications on/off
|
||||||
|
* @param b true to enable events, false to disable events
|
||||||
|
*/
|
||||||
|
public void setEventsEnabled(boolean b) {
|
||||||
|
model.setEventsEnabled(b);
|
||||||
|
}
|
||||||
|
|
||||||
public void setDragNDropHandler(GTreeDragNDropHandler dragNDropHandler) {
|
public void setDragNDropHandler(GTreeDragNDropHandler dragNDropHandler) {
|
||||||
this.dragNDropHandler = dragNDropHandler;
|
this.dragNDropHandler = dragNDropHandler;
|
||||||
new GTreeDragNDropAdapter(this, tree, dragNDropHandler);
|
new GTreeDragNDropAdapter(this, tree, dragNDropHandler);
|
||||||
|
@ -247,7 +261,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
filterUpdateManager.dispose();
|
filterUpdateManager.dispose();
|
||||||
worker.dispose();
|
worker.dispose();
|
||||||
GTreeRootNode root = model.getModelRoot();
|
GTreeNode root = model.getModelRoot();
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
root.dispose();
|
root.dispose();
|
||||||
}
|
}
|
||||||
|
@ -272,7 +286,6 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
protected void updateModelFilter() {
|
protected void updateModelFilter() {
|
||||||
filter = filterProvider.getFilter();
|
filter = filterProvider.getFilter();
|
||||||
|
|
||||||
modificationID.incrementAndGet();
|
modificationID.incrementAndGet();
|
||||||
|
|
||||||
if (lastFilterTask != null) {
|
if (lastFilterTask != null) {
|
||||||
|
@ -280,7 +293,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
lastFilterTask.cancel();
|
lastFilterTask.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
lastFilterTask = new GTreeFilterTask(this, getRootNode(), filter);
|
lastFilterTask = new GTreeFilterTask(this, filter);
|
||||||
|
|
||||||
if (isFilteringEnabled()) {
|
if (isFilteringEnabled()) {
|
||||||
worker.schedule(lastFilterTask);
|
worker.schedule(lastFilterTask);
|
||||||
|
@ -351,7 +364,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expandAll() {
|
public void expandAll() {
|
||||||
runTask(new GTreeExpandAllTask(this, getRootNode()));
|
runTask(new GTreeExpandAllTask(this, getViewRoot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void collapseAll(GTreeNode node) {
|
public void collapseAll(GTreeNode node) {
|
||||||
|
@ -378,7 +391,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expandPaths(TreePath[] paths) {
|
public void expandPaths(TreePath[] paths) {
|
||||||
runTask(new GTreeExpandPathsTask(this, tree, Arrays.asList(paths)));
|
runTask(new GTreeExpandPathsTask(this, Arrays.asList(paths)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expandPaths(List<TreePath> pathsList) {
|
public void expandPaths(List<TreePath> pathsList) {
|
||||||
|
@ -492,7 +505,11 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
tree.setScrollableUnitIncrement(increment);
|
tree.setScrollableUnitIncrement(increment);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected GTreeModel getModel() {
|
/**
|
||||||
|
* Returns the model for this tree
|
||||||
|
* @return the model for this tree
|
||||||
|
*/
|
||||||
|
public GTreeModel getModel() {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,6 +520,10 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current viewport position of the scrollable tree.
|
||||||
|
* @return the current viewport position of the scrollable tree.
|
||||||
|
*/
|
||||||
public Point getViewPosition() {
|
public Point getViewPosition() {
|
||||||
JViewport viewport = scrollPane.getViewport();
|
JViewport viewport = scrollPane.getViewport();
|
||||||
Point p = viewport.getViewPosition();
|
Point p = viewport.getViewPosition();
|
||||||
|
@ -529,35 +550,56 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the node for the given path. This is useful if the node that is in the path has
|
* Gets the model node for the given path. This is useful if the node that is in the path has
|
||||||
* been replaced by a new node that is equal, but a different instance.
|
* been replaced by a new node that is equal, but a different instance. One way this happens
|
||||||
|
* is if the tree is filtered and therefor the displayed nodes are clones of the model nodes. This
|
||||||
|
* can also happen if the tree nodes are rebuilt for some reason.
|
||||||
*
|
*
|
||||||
* @param path the path of the node
|
* @param path the path of the node
|
||||||
* @return the current node in the tree
|
* @return the corresponding model node in the tree. If the tree is filtered the viewed node will
|
||||||
|
* be a clone of the corresponding model node.
|
||||||
*/
|
*/
|
||||||
public GTreeNode getNodeForPath(TreePath path) {
|
public GTreeNode getModelNodeForPath(TreePath path) {
|
||||||
if (path == null) {
|
return getNodeForPath(getModelRoot(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the view node for the given path. This is useful to translate to a tree path that
|
||||||
|
* is valid for the currently displayed tree. (Remember that if the tree is filtered,
|
||||||
|
* then the displayed nodes are clones of the model nodes.)
|
||||||
|
*
|
||||||
|
* @param path the path of the node
|
||||||
|
* @return the current node in the displayed (possibly filtered) tree
|
||||||
|
*/
|
||||||
|
public GTreeNode getViewNodeForPath(TreePath path) {
|
||||||
|
return getNodeForPath(getViewRoot(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GTreeNode getNodeForPath(GTreeNode root, TreePath path) {
|
||||||
|
if (path == null || root == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
if (path.getPathCount() == 1) {
|
if (path.getPathCount() == 1) {
|
||||||
Object lastPathComponent = path.getLastPathComponent();
|
if (root.getId() == node.getId()) {
|
||||||
GTreeNode rootNode = getRootNode();
|
return root;
|
||||||
if (rootNode.equals(lastPathComponent)) {
|
|
||||||
return rootNode;
|
|
||||||
}
|
}
|
||||||
return null; // invalid path--the root of the path is not equal to our root!
|
return null; // invalid path--the root of the path is not equal to our root!
|
||||||
}
|
}
|
||||||
|
if (node.getRoot() == root) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
GTreeNode parentNode = getNodeForPath(path.getParentPath());
|
GTreeNode parentNode = getNodeForPath(root, path.getParentPath());
|
||||||
if (parentNode == null) {
|
if (parentNode == null) {
|
||||||
return null; // must be a path we don't have
|
return null; // must be a path we don't have
|
||||||
}
|
}
|
||||||
|
|
||||||
Object lastPathComponent = path.getLastPathComponent();
|
GTreeNode lastPathComponent = (GTreeNode) path.getLastPathComponent();
|
||||||
List<GTreeNode> children = parentNode.getChildren();
|
List<GTreeNode> children = parentNode.getChildren();
|
||||||
for (GTreeNode child : children) {
|
for (GTreeNode child : children) {
|
||||||
if (child.equals(lastPathComponent)) {
|
if (child.getId() == lastPathComponent.getId()) {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -616,7 +658,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
isFilteringEnabled = enabled;
|
isFilteringEnabled = enabled;
|
||||||
setFilterFieldEnabled(enabled);
|
setFilterFieldEnabled(enabled);
|
||||||
validate();
|
validate();
|
||||||
refilter();
|
refilterNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -685,13 +727,20 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
*
|
*
|
||||||
* @param rootNode The node to set.
|
* @param rootNode The node to set.
|
||||||
*/
|
*/
|
||||||
public void setRootNode(GTreeRootNode rootNode) {
|
public void setRootNode(GTreeNode rootNode) {
|
||||||
|
GTreeNode oldRoot = doSetRootNode(rootNode, true);
|
||||||
|
oldRoot.dispose();
|
||||||
|
if (filter != null) {
|
||||||
|
filterUpdateManager.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GTreeNode doSetRootNode(GTreeNode rootNode, boolean waitForJobs) {
|
||||||
worker.clearAllJobs();
|
worker.clearAllJobs();
|
||||||
GTreeRootNode root = model.getModelRoot();
|
GTreeNode root = model.getModelRoot();
|
||||||
root.dispose();
|
|
||||||
|
|
||||||
this.realRootNode = rootNode;
|
this.realRootNode = rootNode;
|
||||||
rootNode.setGTree(this);
|
rootNode.setParent(rootParent);
|
||||||
|
|
||||||
//
|
//
|
||||||
// We need to use our standard 'worker pipeline' for mutations to the tree. This means
|
// We need to use our standard 'worker pipeline' for mutations to the tree. This means
|
||||||
|
@ -706,23 +755,54 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
runTask(new SetRootNodeTask(this, rootNode, model));
|
runTask(new SetRootNodeTask(this, rootNode, model));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (waitForJobs) {
|
||||||
worker.waitUntilNoJobsScheduled(Integer.MAX_VALUE);
|
worker.waitUntilNoJobsScheduled(Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
monitor.clearCanceled();
|
monitor.clearCanceled();
|
||||||
model.setRootNode(rootNode);
|
model.setRootNode(rootNode);
|
||||||
}
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFilteredRootNode(GTreeNode filteredRootNode) {
|
||||||
|
GTreeNode currentRoot = (GTreeNode) model.getRoot();
|
||||||
|
model.setRootNode(filteredRootNode);
|
||||||
|
if (currentRoot != realRootNode) {
|
||||||
|
currentRoot.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreNonFilteredRootNode() {
|
||||||
|
GTreeNode currentRoot = (GTreeNode) model.getRoot();
|
||||||
|
model.setRootNode(realRootNode);
|
||||||
|
if (currentRoot != realRootNode) {
|
||||||
|
currentRoot.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method always returns the root node given by the client, whether from the
|
* This method returns the root node that was provided to the tree by the client, whether from the
|
||||||
* constructor or from {@link #setRootNode(GTreeRootNode)}. There is a chance that the
|
* constructor or from {@link #setRootNode(GTreeNode)}.
|
||||||
* root node being used by the GUI is an "In Progress" node that is a placeholder used while
|
* This node represents the data model and always contains all the nodes regardless of any filter
|
||||||
* this threaded tree is setting the root node.
|
* being applied. If a filter is applied to the tree, then this is not the actual root node being
|
||||||
* @return
|
* displayed by the {@link JTree}.
|
||||||
|
* @return the root node as provided by the client.
|
||||||
*/
|
*/
|
||||||
public GTreeRootNode getRootNode() {
|
public GTreeNode getModelRoot() {
|
||||||
return realRootNode;
|
return realRootNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the root node currently being displayed by the {@link JTree}. If there
|
||||||
|
* are no filters applied, then this will be the same as the model root (See {@link #getModelRoot()}).
|
||||||
|
* If a filter is applied, then this will be a clone of the model root that contains clones of all
|
||||||
|
* nodes matching the filter.
|
||||||
|
* @return the root node currently being display by the {@link JTree}
|
||||||
|
*/
|
||||||
|
public GTreeNode getViewRoot() {
|
||||||
|
return (GTreeNode) model.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is useful for debugging tree problems. Don't know where else to put it.
|
* This method is useful for debugging tree problems. Don't know where else to put it.
|
||||||
* @param name - Use this to indicate what tree event occurred ("node inserted" "node removed", etc.)
|
* @param name - Use this to indicate what tree event occurred ("node inserted" "node removed", etc.)
|
||||||
|
@ -979,8 +1059,35 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refilter() {
|
/**
|
||||||
updateModelFilter();
|
* Causes the tree to refilter immediately (before this method returns)
|
||||||
|
*/
|
||||||
|
public void refilterNow() {
|
||||||
|
if (isFilteringEnabled && filter != null) {
|
||||||
|
filterUpdateManager.updateNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the tree to refilter some time later
|
||||||
|
*/
|
||||||
|
public void refilterLater() {
|
||||||
|
if (isFilteringEnabled && filter != null) {
|
||||||
|
filterUpdateManager.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-filters the tree if the newNode should be included in the current filter results. If
|
||||||
|
* the new node doesn't match the filter, there is no need to refilter the tree.
|
||||||
|
* @param newNode the node that may cause the tree to refilter.
|
||||||
|
*/
|
||||||
|
public void refilterLater(GTreeNode newNode) {
|
||||||
|
if (isFilteringEnabled && filter != null) {
|
||||||
|
if (filter.acceptsNode(newNode)) {
|
||||||
|
filterUpdateManager.updateLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GTreeFilter getFilter() {
|
public GTreeFilter getFilter() {
|
||||||
|
@ -1020,38 +1127,6 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void scheduleFilterTask(GTreeNode node) {
|
|
||||||
if (!isFilteringEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (nodesToBeFiltered.size() <= MAX_BUFFERED_FILTERED) {
|
|
||||||
nodesToBeFiltered.add(node);
|
|
||||||
}
|
|
||||||
filterUpdateManager.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void performNodeFiltering() {
|
|
||||||
if (!isFilteringEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (nodesToBeFiltered.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (worker.isBusy()) {
|
|
||||||
filterUpdateManager.updateLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (nodesToBeFiltered.size() >= MAX_BUFFERED_FILTERED) {
|
|
||||||
worker.schedule(new GTreeFilterTask(this, getRootNode(), filter));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (GTreeNode node : nodesToBeFiltered) {
|
|
||||||
worker.schedule(new GTreeFilterTask(this, node, filter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nodesToBeFiltered.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void runBulkTask(GTreeBulkTask task) {
|
public void runBulkTask(GTreeBulkTask task) {
|
||||||
worker.schedule(task);
|
worker.schedule(task);
|
||||||
}
|
}
|
||||||
|
@ -1074,7 +1149,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
GTreeRootNode rootNode = getRootNode();
|
GTreeNode rootNode = getModelRoot();
|
||||||
if (rootNode == null) {
|
if (rootNode == null) {
|
||||||
return "GTree - no root node";
|
return "GTree - no root node";
|
||||||
}
|
}
|
||||||
|
@ -1091,7 +1166,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearSizeCache() {
|
public void clearSizeCache() {
|
||||||
recurseClearSizeCache(getRootNode());
|
recurseClearSizeCache(getViewRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recurseClearSizeCache(GTreeNode node) {
|
private void recurseClearSizeCache(GTreeNode node) {
|
||||||
|
@ -1369,4 +1444,5 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
|
|
||||||
return stackTrace[creatorIndex].getClassName();
|
return stackTrace[creatorIndex].getClassName();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,23 +13,22 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree.tasks;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import docking.widgets.tree.*;
|
|
||||||
import docking.widgets.tree.support.GTreeFilter;
|
import docking.widgets.tree.support.GTreeFilter;
|
||||||
|
import docking.widgets.tree.tasks.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class GTreeFilterTask extends GTreeTask {
|
public class GTreeFilterTask extends GTreeTask {
|
||||||
|
|
||||||
private final GTreeNode node;
|
|
||||||
private final GTreeFilter filter;
|
private final GTreeFilter filter;
|
||||||
private final GTreeState defaultRestoreState;
|
private final GTreeState defaultRestoreState;
|
||||||
private boolean cancelledProgramatically;
|
private boolean cancelledProgramatically;
|
||||||
|
|
||||||
public GTreeFilterTask(GTree tree, GTreeNode node, GTreeFilter filter) {
|
public GTreeFilterTask(GTree tree, GTreeFilter filter) {
|
||||||
super(tree);
|
super(tree);
|
||||||
this.node = node;
|
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
|
||||||
// save this now, before we modify the tree
|
// save this now, before we modify the tree
|
||||||
|
@ -39,31 +38,44 @@ public class GTreeFilterTask extends GTreeTask {
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
node.clearFilter();
|
tree.restoreNonFilteredRootNode();
|
||||||
restoreInSameTask(monitor);
|
restoreInSameTask(monitor);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor.setMessage("Filtering...");
|
GTreeNode root = tree.getModelRoot();
|
||||||
monitor.initialize(1000000000);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
node.filter(filter, monitor, 0, 1000000000);
|
monitor.setMessage("Loading/Organizing Tree ....");
|
||||||
|
|
||||||
|
// disable tree events while loading to prevent unnecessary events from slowing
|
||||||
|
// down the operation
|
||||||
|
tree.setEventsEnabled(false);
|
||||||
|
int nodeCount = root.loadAll(monitor);
|
||||||
|
tree.setEventsEnabled(true);
|
||||||
|
monitor.setMessage("Filtering...");
|
||||||
|
monitor.initialize(nodeCount);
|
||||||
|
GTreeNode filtered = root.filter(filter, monitor);
|
||||||
|
runOnSwingThread(() -> tree.setFilteredRootNode(filtered));
|
||||||
if (filter.showFilterMatches()) {
|
if (filter.showFilterMatches()) {
|
||||||
expandInSameTask(monitor);
|
expandInSameTask(monitor, filtered);
|
||||||
restoreInSameTask(monitor);
|
restoreInSameTask(monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (CloneNotSupportedException e) {
|
||||||
|
Msg.error(this, "Got Unexpected CloneNotSupportedException", e);
|
||||||
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
if (!cancelledProgramatically) {
|
if (!cancelledProgramatically) {
|
||||||
tree.runTask(new GTreeClearTreeFilterTask(tree));
|
tree.runTask(new GTreeClearTreeFilterTask(tree));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
tree.setEventsEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expandInSameTask(TaskMonitor monitor) {
|
private void expandInSameTask(TaskMonitor monitor, GTreeNode filtered) {
|
||||||
GTreeExpandAllTask expandTask = new GTreeExpandAllTask(tree, node);
|
GTreeExpandAllTask expandTask = new GTreeExpandAllTask(tree, filtered);
|
||||||
expandTask.run(monitor);
|
expandTask.run(monitor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,53 +15,67 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for GTNodes that want to use a lazy loading approach. By using lazy
|
* Base class for GTreeNodes that populate their children on demand (typically when expanded).
|
||||||
* nodes, you don't have to create all the nodes up front and the nodes will only
|
* Also, children of this node can be unloaded by calling {@link #unloadChildren()}. This
|
||||||
* be created as needed. If you extend this base class, you have to implement one
|
* can be used by nodes in large trees to save memory by unloading children that are no longer
|
||||||
* additional method than if you extended AbstractGTreeNode and that is generateChildren().
|
* in the current tree view (collapsed). Of course, that decision would need to be balanced
|
||||||
* The generateChildren() method will be called automatically when needed.
|
* against the extra time to reload the nodes in the event that a filter is applied.
|
||||||
*/
|
*/
|
||||||
|
public abstract class GTreeLazyNode extends GTreeNode {
|
||||||
|
|
||||||
public abstract class GTreeLazyNode extends AbstractGTreeNode {
|
/**
|
||||||
|
* Subclasses must be able to generate their children nodes on demand by implementing this method.
|
||||||
|
* @return the list of GTreeNodes that make up the children for this node.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
protected abstract List<GTreeNode> generateChildren();
|
protected abstract List<GTreeNode> generateChildren();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this lazy node back to the "unloaded" state such that if
|
||||||
|
* its children are accessed, it will reload its children as needed.
|
||||||
|
*/
|
||||||
|
public void unloadChildren() {
|
||||||
|
if (isLoaded()) {
|
||||||
|
doSetChildren(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void loadChildren() {
|
public void addNode(GTreeNode node) {
|
||||||
if (isChildrenLoadedOrInProgress()) {
|
if (isLoaded()) {
|
||||||
return;
|
super.addNode(node);
|
||||||
}
|
}
|
||||||
List<GTreeNode> generateChildren = generateChildren();
|
|
||||||
if (isChildrenLoadedOrInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
doSetChildren(generateChildren, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addNode(int index, GTreeNode node) {
|
public void addNode(int index, GTreeNode node) {
|
||||||
if (!isChildrenLoadedOrInProgress()) {
|
if (isLoaded()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.addNode(index, node);
|
super.addNode(index, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A convenience method to return this node's children if they are loaded; an empty list
|
|
||||||
* if they are not loaded. This allows clients that don't care either way to use the
|
|
||||||
* list returned here without checking for null.
|
|
||||||
*
|
|
||||||
* @return the loaded children
|
|
||||||
*/
|
|
||||||
public List<GTreeNode> getAllChildrenIfLoaded() {
|
|
||||||
if (isChildrenLoadedOrInProgress()) {
|
|
||||||
return getAllChildren();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// not loaded; do not load
|
@Override
|
||||||
return Collections.emptyList();
|
public void addNodes(List<GTreeNode> nodes) {
|
||||||
|
if (isLoaded()) {
|
||||||
|
super.addNodes(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll() {
|
||||||
|
if (isLoaded()) {
|
||||||
|
unloadChildren();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeNode(GTreeNode node) {
|
||||||
|
if (!isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.removeNode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,61 +15,103 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import docking.widgets.tree.support.GTreeFilter;
|
import docking.widgets.tree.support.*;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import util.CollectionUtils;
|
||||||
|
|
||||||
public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
/**
|
||||||
|
* Base implementation for GTree nodes. Direct subclasses of this class are expected to have
|
||||||
|
* all their children in hand when initially constructed (either in their constructor or externally
|
||||||
|
* using {@link #addNode(GTreeNode)} or {@link #setChildren(List)}. For large trees, subclasses
|
||||||
|
* should instead extend {@link GTreeLazyNode} or {@link GTreeSlowLoadingNode}
|
||||||
|
*/
|
||||||
|
public abstract class GTreeNode extends CoreGTreeNode implements Comparable<GTreeNode> {
|
||||||
|
private static AtomicLong NEXT_ID = new AtomicLong();
|
||||||
|
|
||||||
|
private final long id;
|
||||||
|
|
||||||
|
protected GTreeNode() {
|
||||||
|
id = NEXT_ID.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<GTreeNode> generateChildren() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the node to be displayed in the tree
|
* Returns the name of the node to be displayed in the tree
|
||||||
* @return the name of the node.
|
* @return the name of the node.
|
||||||
*/
|
*/
|
||||||
public String getName();
|
public abstract String getName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Icon to be displayed for this node in the tree.
|
* Returns the Icon to be displayed for this node in the tree.
|
||||||
* @param expanded true if the node is expanded.
|
* @param expanded true if the node is expanded.
|
||||||
* @return the icon to be displayed for this node in the tree.
|
* @return the icon to be displayed for this node in the tree.
|
||||||
*/
|
*/
|
||||||
public Icon getIcon(boolean expanded);
|
public abstract Icon getIcon(boolean expanded);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the string to be displayed as a tooltip when the user
|
* Returns the string to be displayed as a tooltip when the user
|
||||||
* hovers the mouse on this node in the tree.
|
* hovers the mouse on this node in the tree.
|
||||||
* @return the tooltip to be displayed.
|
* @return the tooltip to be displayed.
|
||||||
*/
|
*/
|
||||||
public String getToolTip();
|
public abstract String getToolTip();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this node never has children.
|
* Returns true if this node never has children.
|
||||||
* @return true if this node is a leaf.
|
* @return true if this node is a leaf.
|
||||||
*/
|
*/
|
||||||
public boolean isLeaf();
|
public abstract boolean isLeaf();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(GTreeNode node) {
|
||||||
|
return getName().compareToIgnoreCase(node.getName());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given node as a child to this node.
|
* Adds the given node as a child to this node. Note: this method may be inefficient so if you
|
||||||
|
* have many nodes to add, you should use either {@link #addNodes(List)} or {@link #setChildren(List)}
|
||||||
* @param node the node to add as a child.
|
* @param node the node to add as a child.
|
||||||
*/
|
*/
|
||||||
public void addNode(GTreeNode node);
|
public void addNode(GTreeNode node) {
|
||||||
|
children().add(node);
|
||||||
|
node.setParent(this);
|
||||||
|
fireNodeAdded(this, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given nodes as children to this node.
|
||||||
|
* @param nodes the nodes to add.
|
||||||
|
*/
|
||||||
|
public void addNodes(List<GTreeNode> nodes) {
|
||||||
|
for (GTreeNode node : nodes) {
|
||||||
|
node.setParent(this);
|
||||||
|
}
|
||||||
|
children().addAll(nodes);
|
||||||
|
fireNodeStructureChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given node at the given index as a child to this node.
|
* Adds the given node at the given index as a child to this node.
|
||||||
* @param index the index to place the node.
|
* @param index the index to place the node.
|
||||||
* @param node the node to add as a child of this node.
|
* @param node the node to add as a child of this node.
|
||||||
*/
|
*/
|
||||||
public void addNode(int index, GTreeNode node);
|
public void addNode(int index, GTreeNode node) {
|
||||||
|
children().add(index, node);
|
||||||
/**
|
node.setParent(this);
|
||||||
* Returns the list of children including those that have been filtered out.
|
fireNodeAdded(this, node);
|
||||||
* @return the list of all children of this node including those that are filtered out.
|
}
|
||||||
*/
|
|
||||||
public List<GTreeNode> getAllChildren();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all of the <b>visible</b> children of this node. If there are filtered nodes, then
|
* Returns all of the <b>visible</b> children of this node. If there are filtered nodes, then
|
||||||
|
@ -78,28 +120,32 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
||||||
* @return all of the <b>visible</b> children of this node. If there are filtered nodes, then
|
* @return all of the <b>visible</b> children of this node. If there are filtered nodes, then
|
||||||
* they will not be returned.
|
* they will not be returned.
|
||||||
*/
|
*/
|
||||||
public List<GTreeNode> getChildren();
|
public List<GTreeNode> getChildren() {
|
||||||
|
return Collections.unmodifiableList(children());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of <b>visible</b> children of this node. Does not include
|
* Returns the number of <b>visible</b> children of this node. Does not include
|
||||||
* nodes that are current filtered out.
|
* nodes that are current filtered out.
|
||||||
* @return the number of <b>visible</b> children of this node.
|
* @return the number of <b>visible</b> children of this node.
|
||||||
*/
|
*/
|
||||||
public int getChildCount();
|
public int getChildCount() {
|
||||||
|
return children().size();
|
||||||
/**
|
}
|
||||||
* Returns the number of <b>all</b> children of this node. Includes nodes that
|
|
||||||
* are currently filtered out.
|
|
||||||
* @return the number of <b>all</b? children of this node.
|
|
||||||
*/
|
|
||||||
public int getAllChildCount();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the child node of this node with the given name.
|
* Returns the child node of this node with the given name.
|
||||||
* @param name the name of the child to be returned.
|
* @param name the name of the child to be returned.
|
||||||
* @return the child with the given name.
|
* @return the child with the given name.
|
||||||
*/
|
*/
|
||||||
public GTreeNode getChild(String name);
|
public GTreeNode getChild(String name) {
|
||||||
|
for (GTreeNode node : children()) {
|
||||||
|
if (name.equals(node.getName())) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the child node at the given index. Returns null if the index is out of
|
* Returns the child node at the given index. Returns null if the index is out of
|
||||||
|
@ -107,26 +153,46 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
||||||
* @param index the index of the child to be returned.
|
* @param index the index of the child to be returned.
|
||||||
* @return the child at the given index.
|
* @return the child at the given index.
|
||||||
*/
|
*/
|
||||||
public GTreeNode getChild(int index);
|
public GTreeNode getChild(int index) {
|
||||||
|
return children().get(index);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of nodes in the subtree rooted at this node. Leaf
|
* Returns the total number of nodes in the subtree rooted at this node. Leaf
|
||||||
* nodes return 1.
|
* nodes return 1.
|
||||||
* @return the number of nodes from this node downward.
|
* @return the number of nodes from this node downward.
|
||||||
*/
|
*/
|
||||||
public int getNodeCount();
|
public int getNodeCount() {
|
||||||
|
int count = 1;
|
||||||
|
for (GTreeNode node : children()) {
|
||||||
|
count += node.getNodeCount();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of leaf nodes in the subtree from this node.
|
* Returns the total number of leaf nodes in the subtree from this node.
|
||||||
* @return the total number of leaf nodes in the subtree from this node.
|
* @return the total number of leaf nodes in the subtree from this node.
|
||||||
*/
|
*/
|
||||||
public int getLeafCount();
|
public int getLeafCount() {
|
||||||
|
int count = 0;
|
||||||
|
for (GTreeNode node : children()) {
|
||||||
|
count += node.getLeafCount();
|
||||||
|
}
|
||||||
|
return count == 0 ? 1 : count; // if my child count == 0, return 1 since I am a leaf
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of this node within its parent node.
|
* Returns the index of this node within its parent node.
|
||||||
* @return the index of this node within its parent node.
|
* @return the index of this node within its parent node.
|
||||||
*/
|
*/
|
||||||
public int getIndexInParent();
|
public int getIndexInParent() {
|
||||||
|
GTreeNode parent = getParent();
|
||||||
|
if (parent == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return parent.getIndexOfChild(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the given node within this node. -1 is returned
|
* Returns the index of the given node within this node. -1 is returned
|
||||||
|
@ -134,59 +200,74 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
||||||
* @param node whose index we want.
|
* @param node whose index we want.
|
||||||
* @return the index of the given node within this node.
|
* @return the index of the given node within this node.
|
||||||
*/
|
*/
|
||||||
public int getIndexOfChild(GTreeNode node);
|
public int getIndexOfChild(GTreeNode node) {
|
||||||
|
return children().indexOf(node);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the TreePath for this node.
|
* Returns the TreePath for this node.
|
||||||
* @return the TreePath for this node.
|
* @return the TreePath for this node.
|
||||||
*/
|
*/
|
||||||
public TreePath getTreePath();
|
public TreePath getTreePath() {
|
||||||
|
return new TreePath(getPathToRoot(this, 0));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all children from this node. The children nodes will be disposed.
|
* Removes all children from this node. The children nodes will be disposed.
|
||||||
*/
|
*/
|
||||||
public void removeAll();
|
public void removeAll() {
|
||||||
|
if (!isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<GTreeNode> children = children();
|
||||||
|
if (children != null) {
|
||||||
|
for (GTreeNode child : children) {
|
||||||
|
child.dispose();
|
||||||
|
}
|
||||||
|
children().clear();
|
||||||
|
fireNodeStructureChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the given node from this node.
|
* Remove the given node from this node.
|
||||||
* @param node the to be removed.
|
* @param node the to be removed.
|
||||||
*/
|
*/
|
||||||
public void removeNode(GTreeNode node);
|
public void removeNode(GTreeNode node) {
|
||||||
|
if (!isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<GTreeNode> children = children();
|
||||||
|
if (children.remove(node)) {
|
||||||
|
node.setParent(null);
|
||||||
|
fireNodeStructureChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the children on this node. Any existing current children will be dispose.
|
* Sets the children on this node. Any existing current children will be dispose.
|
||||||
* @param childList this list of nodes to be set as children of this node.
|
* @param childList this list of nodes to be set as children of this node.
|
||||||
*/
|
*/
|
||||||
public void setChildren(List<GTreeNode> childList);
|
public void setChildren(List<GTreeNode> childList) {
|
||||||
|
doSetChildren(childList);
|
||||||
|
fireNodeStructureChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given node is a child of this node or one of its children.
|
* Returns true if the given node is a child of this node or one of its children.
|
||||||
* @param node the potential descendant node to check
|
* @param node the potential descendant node to check
|
||||||
|
* @return true if the given node is a child of this node or one of its children.
|
||||||
*/
|
*/
|
||||||
public boolean isAncestor(GTreeNode node);
|
public boolean isAncestor(GTreeNode node) {
|
||||||
|
GTreeNode nodeParent = node.getParent();
|
||||||
/**
|
while (nodeParent != null) {
|
||||||
* Applies the the given filter to the subtree of this node. Nodes will be
|
if (nodeParent.equals(this)) {
|
||||||
* filtered out if the node and all of its descendants are not accepted by the filter. In
|
return true;
|
||||||
* other words, a node will remain if it or any of its descendants are accepted by the filter.
|
}
|
||||||
* @param filter the filter being applied.
|
nodeParent = nodeParent.getParent();
|
||||||
* @param monitor a TaskMonitor for tracking the progress and cancelling.
|
}
|
||||||
* @param min the min value to use for the progress bar for this subtree.
|
return false;
|
||||||
* @param max the max value to use for the progress bar for this subtree.
|
}
|
||||||
* @throws CancelledException if the operation is cancelled via the TaskMonitor.
|
|
||||||
*/
|
|
||||||
public void filter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
|
|
||||||
throws CancelledException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes any filtering on this subtree.
|
|
||||||
*/
|
|
||||||
public void clearFilter();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this node is filtered and not in the current view
|
|
||||||
*/
|
|
||||||
public boolean isFilteredOut();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification method called when a cell editor completes editing to notify this
|
* Notification method called when a cell editor completes editing to notify this
|
||||||
|
@ -195,7 +276,9 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
||||||
* @param newValue the new value provided by the cell editor.
|
* @param newValue the new value provided by the cell editor.
|
||||||
* @see #isEditable()
|
* @see #isEditable()
|
||||||
*/
|
*/
|
||||||
public void valueChanged(Object newValue);
|
public void valueChanged(Object newValue) {
|
||||||
|
// Overridden in subclasses
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this node is allowed to be edited in the tree. You must override this
|
* Returns true if this node is allowed to be edited in the tree. You must override this
|
||||||
|
@ -204,49 +287,216 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
||||||
* @return true if this node is allowed to be edited in the tree.
|
* @return true if this node is allowed to be edited in the tree.
|
||||||
* @see #valueChanged(Object)
|
* @see #valueChanged(Object)
|
||||||
*/
|
*/
|
||||||
public boolean isEditable();
|
public boolean isEditable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the rootNode for this tree or null if there is no parent path to a
|
* Returns the rootNode for this tree or null if there is no parent path to a
|
||||||
* GTRootNode.
|
* GTRootNode.
|
||||||
* @return the rootNode for this tree.
|
* @return the rootNode for this tree.
|
||||||
*/
|
*/
|
||||||
public GTreeRootNode getRoot();
|
public GTreeNode getRoot() {
|
||||||
|
GTreeNode myParent = getParent();
|
||||||
|
if (myParent == null || myParent instanceof GTreeRootParentNode) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return myParent.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the GTTree that contains this node.
|
* Returns true if this is a root node
|
||||||
* @return the GTTree that contains this node.
|
* @return true if this is a root node
|
||||||
*/
|
*/
|
||||||
public GTree getTree();
|
public boolean isRoot() {
|
||||||
|
return getRoot() == this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes this node and all of its descendants.
|
* Returns the GTree that this node is attached to
|
||||||
|
* @return the GTree that this node is attached to
|
||||||
*/
|
*/
|
||||||
public void dispose();
|
public GTree getTree() {
|
||||||
|
GTreeNode parent = getParent();
|
||||||
|
if (parent != null) {
|
||||||
|
return parent.getTree();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this node is currently being modified.
|
* Generates a filtered copy of this node and its children.
|
||||||
* @return true if this node is currently being modified.
|
* <P>
|
||||||
|
* A node will be included if it or any of its descendants are accepted by the filter.
|
||||||
|
* NOTE: the filter will only be applied to a nodes children if they are loaded. So to
|
||||||
|
* perform a filter on all the nodes in the tree, the {@link #loadAll(TaskMonitor)} should
|
||||||
|
* be called before the filter call.
|
||||||
|
* @param filter the filter being applied.
|
||||||
|
* @param monitor a TaskMonitor for tracking the progress and cancelling.
|
||||||
|
* @return A copy of this node and its children that matches the filter or null
|
||||||
|
* if this node and none of its children match the filter.
|
||||||
|
* @throws CancelledException if the operation is cancelled via the TaskMonitor.
|
||||||
|
* @throws CloneNotSupportedException if any nodes in the tree explicitly prevents cloning.
|
||||||
*/
|
*/
|
||||||
public boolean isInProgress();
|
|
||||||
|
public GTreeNode filter(GTreeFilter filter, TaskMonitor monitor)
|
||||||
|
throws CancelledException, CloneNotSupportedException {
|
||||||
|
List<GTreeNode> list = new ArrayList<>();
|
||||||
|
|
||||||
|
if (isLoaded()) {
|
||||||
|
for (GTreeNode child : children()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
GTreeNode filtered = child.filter(filter, monitor);
|
||||||
|
if (filtered != null) {
|
||||||
|
list.add(filtered);
|
||||||
|
}
|
||||||
|
monitor.incrementProgress(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRoot() || !list.isEmpty() || filter.acceptsNode(this) || getParent() == null) {
|
||||||
|
GTreeNode clone = clone();
|
||||||
|
clone.doSetChildren(list);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes any lazy or slow loading nodes in the tree to load their children so that the tree
|
||||||
|
* is fully loaded. Nodes that are already loaded (including normal nodes which are always loaded)
|
||||||
|
* do nothing except recursively call {@link #loadAll(TaskMonitor)} on their children.
|
||||||
|
* @param monitor the TaskMonitor to monitor progress and provide cancel checking
|
||||||
|
* @return the total number of nodes in the subtree of this node.
|
||||||
|
* @throws CancelledException if the operation is cancelled using the monitor.
|
||||||
|
*/
|
||||||
|
public int loadAll(TaskMonitor monitor) throws CancelledException {
|
||||||
|
List<GTreeNode> children = children();
|
||||||
|
monitor = new TreeTaskMonitor(monitor, children.size());
|
||||||
|
int count = 1;
|
||||||
|
for (GTreeNode child : children) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
count += child.loadAll(monitor);
|
||||||
|
monitor.incrementProgress(1);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (int) id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GTreeNode other = (GTreeNode) obj;
|
||||||
|
return id == other.id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the tree that the node has different children. This method
|
* Notifies the tree that the node has different children. This method
|
||||||
* @param node the node that has changed.
|
* @param node the node that has changed.
|
||||||
*/
|
*/
|
||||||
public void fireNodeStructureChanged(GTreeNode node);
|
public void fireNodeStructureChanged(GTreeNode node) {
|
||||||
|
GTree tree = getTree();
|
||||||
|
if (tree != null) {
|
||||||
|
Swing.runNow(() -> tree.getModel().fireNodeStructureChanged(node));
|
||||||
|
tree.refilterLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the tree that a node has changed.
|
* Notifies the tree that a node has changed.
|
||||||
* @param parentNode the node that contains the node that was changed.
|
* @param parentNode the node that contains the node that was changed.
|
||||||
* @param node the that changed.
|
* @param node the that changed.
|
||||||
*/
|
*/
|
||||||
public void fireNodeChanged(GTreeNode parentNode, GTreeNode node);
|
public void fireNodeChanged(GTreeNode parentNode, GTreeNode node) {
|
||||||
|
GTree tree = getTree();
|
||||||
|
if (tree != null) {
|
||||||
|
Swing.runNow(() -> tree.getModel().fireNodeDataChanged(parentNode, node));
|
||||||
|
tree.refilterLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the parent of this node.
|
* Returns a stream of the GTree nodes in the subtree of this node.
|
||||||
* @return the parent of this node.
|
* @param depthFirst if true, the nodes will be streamed in depth-first order, otherwise breadth-first order
|
||||||
|
* @return a stream of the GTree nodes in the subtree of this node.
|
||||||
*/
|
*/
|
||||||
public GTreeNode getParent();
|
public Stream<GTreeNode> stream(boolean depthFirst) {
|
||||||
|
return CollectionUtils.asStream(iterator(depthFirst));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator of the GTree nodes in the subtree of this node.
|
||||||
|
* @param depthFirst if true, the nodes will be returned in depth-first order, otherwise breadth-first order
|
||||||
|
* @return an iterator of the GTree nodes in the subtree of this node.
|
||||||
|
*/
|
||||||
|
public Iterator<GTreeNode> iterator(boolean depthFirst) {
|
||||||
|
if (depthFirst) {
|
||||||
|
return new DepthFirstIterator(this);
|
||||||
|
}
|
||||||
|
return new BreadthFirstIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an id for this node that is unique among all GTreeNodes in this running JVM.
|
||||||
|
* If this node is cloned, the clone will have the same id.
|
||||||
|
* @return the unique id for this node.
|
||||||
|
*/
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireNodeAdded(GTreeNode parentNode, GTreeNode newNode) {
|
||||||
|
GTree tree = getTree();
|
||||||
|
if (tree != null) {
|
||||||
|
Swing.runNow(() -> tree.getModel().fireNodeAdded(parentNode, newNode));
|
||||||
|
tree.refilterLater(newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireNodeRemoved(GTreeNode parentNode, GTreeNode removedNode,
|
||||||
|
int deletedChildIndex) {
|
||||||
|
|
||||||
|
GTree tree = getTree();
|
||||||
|
if (tree != null) {
|
||||||
|
Swing.runNow(
|
||||||
|
() -> tree.getModel().fireNodeRemoved(parentNode, removedNode, deletedChildIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GTreeNode[] getPathToRoot(GTreeNode node, int depth) {
|
||||||
|
GTreeNode[] returnNodes;
|
||||||
|
|
||||||
|
/* Check for null, in case someone passed in a null node, or
|
||||||
|
they passed in an element that isn't rooted at root. */
|
||||||
|
if (node == null || node instanceof GTreeRootParentNode) {
|
||||||
|
if (depth == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
returnNodes = new GTreeNode[depth];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
depth++;
|
||||||
|
returnNodes = getPathToRoot(node.getParent(), depth);
|
||||||
|
returnNodes[returnNodes.length - depth] = node;
|
||||||
|
}
|
||||||
|
return returnNodes;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,23 +15,42 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple base class for GTRootNodes. If your root node has different internal logic
|
* Artificial node used by the GTree to set as a parent on the real root node of a GTree. It allows
|
||||||
* than other nodes, then extend this class. If you root node has the same internal
|
* nodes to access the GTree because it overrides getTree to return the GTree. This eliminates the
|
||||||
* logic as other nodes, then it is probably better to extend your other node class and
|
* need for clients to create special root nodes that have getTree/setTree
|
||||||
* implement the getGTree() and setGTree() methods yourself.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractGTreeRootNode extends AbstractGTreeNode implements GTreeRootNode {
|
class GTreeRootParentNode extends GTreeNode {
|
||||||
private GTree tree;
|
private GTree tree;
|
||||||
|
|
||||||
@Override
|
GTreeRootParentNode(GTree tree) {
|
||||||
public void setGTree(GTree tree) {
|
|
||||||
this.tree = tree;
|
this.tree = tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GTree getGTree() {
|
public GTree getTree() {
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon(boolean expanded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,120 +15,102 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree;
|
package docking.widgets.tree;
|
||||||
|
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
import ghidra.util.exception.AssertException;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import docking.widgets.tree.internal.InProgressGTreeNode;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import docking.widgets.tree.tasks.GTreeLoadChildrenTask;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for GTNodes that want to use a lazy loading approach, but the loading may
|
* Base class for nodes that generate their children on demand, but because generating their children
|
||||||
* be slow and therefor should be done in another thread. By using SlowLoadingNode
|
* is slow, that operation is moved to a background thread. While the children are being generated,
|
||||||
* nodes, you don't have to create all the nodes up front and the nodes will only
|
* an {@link InProgressGTreeNode} will appear in the tree until the {@link LoadChildrenTask} has completed.
|
||||||
* be created as needed. If you extend this base class, you have to implement one
|
|
||||||
* additional method than if you extended AbstractGTreeNode and that is
|
|
||||||
* generateChildren(TaskMonitor monitor).
|
|
||||||
* The generateChildren(TaskMonitor monitor) method will be called
|
|
||||||
* automatically from a task thread when needed. While the loading is taking place,
|
|
||||||
* An "In Progress" node will be displayed.
|
|
||||||
*/
|
*/
|
||||||
public abstract class GTreeSlowLoadingNode extends AbstractGTreeNode {
|
public abstract class GTreeSlowLoadingNode extends GTreeLazyNode {
|
||||||
public abstract List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException;
|
|
||||||
|
/**
|
||||||
|
* Subclass must implement this method to generate their children. This operation will always be
|
||||||
|
* performed in a background thread (i.e. Not the swing thread)
|
||||||
|
* @param monitor a TaskMonitor for reporting progress and cancel notification.
|
||||||
|
* @return the list of children for this node.
|
||||||
|
* @throws CancelledException if the monitor is cancelled
|
||||||
|
*/
|
||||||
|
public abstract List<GTreeNode> generateChildren(TaskMonitor monitor)
|
||||||
|
throws CancelledException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void loadChildren() {
|
protected final List<GTreeNode> generateChildren() {
|
||||||
|
|
||||||
final GTree tree = getTree();
|
final GTree tree = getTree();
|
||||||
if (SystemUtilities.isEventDispatchThread()) {
|
if (Swing.isSwingThread() && tree != null) {
|
||||||
if (isChildrenLoadedOrInProgress()) {
|
LoadChildrenTask loadTask = new LoadChildrenTask(tree);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setInProgress(); // this will make isChildrenLoaded() return true for any subsequent calls.
|
|
||||||
if (tree != null) {
|
|
||||||
GTreeLoadChildrenTask loadTask = new GTreeLoadChildrenTask(tree, this);
|
|
||||||
tree.runTask(loadTask);
|
tree.runTask(loadTask);
|
||||||
return;
|
return CollectionUtils.asList(new InProgressGTreeNode());
|
||||||
}
|
}
|
||||||
|
return generateChildrenNow(getMonitor(tree));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChildrenLoadedOrInProgress() && !isInProgress()) {
|
@Override
|
||||||
return; // fully loaded
|
public int loadAll(TaskMonitor monitor) throws CancelledException {
|
||||||
|
if (!isLoaded()) {
|
||||||
|
monitor = new TreeTaskMonitor(monitor, 2);
|
||||||
|
doSetChildren(generateChildren(new TreeTaskMonitor(monitor, 0)));
|
||||||
|
monitor.incrementProgress(1);
|
||||||
|
}
|
||||||
|
return super.loadAll(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
setInProgress();
|
private List<GTreeNode> generateChildrenNow(TaskMonitor monitor) {
|
||||||
doLoadChildren(tree, getMonitor(tree));
|
try {
|
||||||
|
return generateChildren(monitor);
|
||||||
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TaskMonitor getMonitor(GTree tree) {
|
private TaskMonitor getMonitor(GTree tree) {
|
||||||
if (tree == null) {
|
if (tree == null) {
|
||||||
return TaskMonitorAdapter.DUMMY_MONITOR;
|
return TaskMonitor.DUMMY;
|
||||||
}
|
}
|
||||||
return tree.getThreadLocalMonitor();
|
return tree.getThreadLocalMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doLoadChildren(final GTree tree, TaskMonitor monitor) {
|
private class LoadChildrenTask extends GTreeTask {
|
||||||
if (isChildrenLoaded()) {
|
|
||||||
// Odd case where we have been told to load even though we are already loaded.
|
LoadChildrenTask(GTree tree) {
|
||||||
// Probably in the middle of a filter job. Need to reset the active chi
|
super(tree);
|
||||||
// in any case. Calling setChildren will effectively set the allChildren to its
|
}
|
||||||
// current contents, but will also set the active children.
|
|
||||||
setChildren(doGetAllChildren());
|
@Override
|
||||||
|
public void run(TaskMonitor monitor) {
|
||||||
|
if (isLoaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long progressValue = monitor.getProgress();
|
long progressValue = monitor.getProgress();
|
||||||
long maxValue = monitor.getMaximum();
|
long maxValue = monitor.getMaximum();
|
||||||
|
monitor.setMessage("Loading children");
|
||||||
try {
|
try {
|
||||||
setChildren(generateChildren(monitor));
|
setChildren(generateChildren(monitor));
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
SystemUtilities.runSwingNow(new Runnable() {
|
if (!tree.isDisposed()) {
|
||||||
|
runOnSwingThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (tree != null) {
|
tree.collapseAll(tree.getViewRoot());
|
||||||
tree.collapseAll(tree.getRootNode());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
doSetChildren(null, true);
|
}
|
||||||
|
doSetChildren(null);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// restore monitor min/max/progress values to original state since we don't know
|
|
||||||
// where we fit into the bigger progress picture.
|
|
||||||
monitor.initialize(maxValue);
|
monitor.initialize(maxValue);
|
||||||
monitor.setProgress(progressValue);
|
monitor.setProgress(progressValue);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void swingSetChildren(List<GTreeNode> childList, boolean notify,
|
|
||||||
boolean onlyIfInProgress) {
|
|
||||||
// intentionally ignore 'onlyIfInProgress'
|
|
||||||
super.swingSetChildren(childList, notify, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: you cannot call this method from the Swing thread, as the data may not have been
|
|
||||||
* loaded. Instead, this method should be called from a {@link GTreeTask}.
|
|
||||||
*
|
|
||||||
* @param index The index where the node should be inserted
|
|
||||||
* @param node The node to insert
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void addNode(int index, GTreeNode node) {
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
throw new AssertException(
|
|
||||||
"You may not invoke this method on a GTReeSlowLoadingNode from the Swing thread");
|
|
||||||
}
|
|
||||||
super.addNode(index, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,11 +56,10 @@ public class GTreeState {
|
||||||
private GTree tree;
|
private GTree tree;
|
||||||
|
|
||||||
public GTreeState(GTree tree) {
|
public GTreeState(GTree tree) {
|
||||||
this(tree, tree.getRootNode());
|
this(tree, tree.getViewRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
public GTreeState(GTree tree, GTreeNode node) {
|
public GTreeState(GTree tree, GTreeNode node) {
|
||||||
|
|
||||||
this.tree = tree;
|
this.tree = tree;
|
||||||
expandedPaths = tree.getExpandedPaths(node);
|
expandedPaths = tree.getExpandedPaths(node);
|
||||||
selectionPaths = getSelectionPaths(node);
|
selectionPaths = getSelectionPaths(node);
|
||||||
|
@ -166,7 +165,7 @@ public class GTreeState {
|
||||||
|
|
||||||
private List<TreePath> getSelectionPaths(GTreeNode node) {
|
private List<TreePath> getSelectionPaths(GTreeNode node) {
|
||||||
TreePath[] allSelectionPaths = tree.getSelectionPaths();
|
TreePath[] allSelectionPaths = tree.getSelectionPaths();
|
||||||
if (node == tree.getRootNode()) {
|
if (node == tree.getViewRoot()) {
|
||||||
return CollectionUtils.asList(allSelectionPaths);
|
return CollectionUtils.asList(allSelectionPaths);
|
||||||
}
|
}
|
||||||
if (allSelectionPaths == null) {
|
if (allSelectionPaths == null) {
|
||||||
|
|
|
@ -60,7 +60,7 @@ public abstract class GTreeTask extends PriorityJob {
|
||||||
// note: call this on the Swing thread, since the Swing thread maintains the node state
|
// note: call this on the Swing thread, since the Swing thread maintains the node state
|
||||||
// (we have seen errors where the tree will return nodes that are in the process
|
// (we have seen errors where the tree will return nodes that are in the process
|
||||||
// of being disposed)
|
// of being disposed)
|
||||||
GTreeNode nodeForPath = SystemUtilities.runSwingNow(() -> tree.getNodeForPath(path));
|
GTreeNode nodeForPath = SystemUtilities.runSwingNow(() -> tree.getViewNodeForPath(path));
|
||||||
if (nodeForPath != null) {
|
if (nodeForPath != null) {
|
||||||
return nodeForPath.getTreePath();
|
return nodeForPath.getTreePath();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
/* ###
|
||||||
|
* 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 docking.widgets.tree;
|
||||||
|
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.CancelledListener;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TaskMonitor implementation that is useful for monitor work when traversing trees.
|
||||||
|
*
|
||||||
|
* It works by subdividing the distance of the top-most progress bar (represented by the top-most
|
||||||
|
* monitor) into equal size chunks depending on how many children have to be visited. For example,
|
||||||
|
* assume the root node has 5 children, then the task bar for that node would increment 20% of
|
||||||
|
* the bar as it completed work on each of its children. Now, assume each child of the root node
|
||||||
|
* has 10 children. The task monitor for each root child will operate entirely with its 20% as
|
||||||
|
* mentioned above. So the first child of the first child will increment the progress bar
|
||||||
|
* 2% (10% of 20%) when it is complete.
|
||||||
|
*/
|
||||||
|
public class TreeTaskMonitor implements TaskMonitor {
|
||||||
|
// initialize to a huge number that can be subdivided many times
|
||||||
|
private static long MAX_VALUE = 0x1000_0000_0000_0000L;
|
||||||
|
private final TaskMonitor monitor;
|
||||||
|
|
||||||
|
// This monitor operates on a sub-range of top-most monitor. The range min/max define that range
|
||||||
|
private final long currentRangeMin;
|
||||||
|
private final long currentRangeMax;
|
||||||
|
|
||||||
|
// The amount of one increment increase the top-most monitor by this amount
|
||||||
|
private long chunkSize;
|
||||||
|
|
||||||
|
// These values are the max and progress for this sub-monitor
|
||||||
|
private long max;
|
||||||
|
private long progress;
|
||||||
|
|
||||||
|
public TreeTaskMonitor(TaskMonitor monitor, long max) {
|
||||||
|
if (monitor instanceof TreeTaskMonitor) {
|
||||||
|
TreeTaskMonitor treeTaskMonitor = (TreeTaskMonitor) monitor;
|
||||||
|
this.monitor = treeTaskMonitor.monitor;
|
||||||
|
currentRangeMin = treeTaskMonitor.getTrueProgress();
|
||||||
|
currentRangeMax = currentRangeMin + treeTaskMonitor.chunkSize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.monitor = monitor;
|
||||||
|
currentRangeMin = 0;
|
||||||
|
currentRangeMax = MAX_VALUE;
|
||||||
|
monitor.initialize(MAX_VALUE);
|
||||||
|
}
|
||||||
|
setMaximum(max);
|
||||||
|
progress = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getTrueProgress() {
|
||||||
|
return monitor.getProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return monitor.isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShowProgressValue(boolean showProgressValue) {
|
||||||
|
monitor.setShowProgressValue(showProgressValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMessage(String message) {
|
||||||
|
monitor.setMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return monitor.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProgress(long value) {
|
||||||
|
this.progress = value;
|
||||||
|
monitor.setProgress(currentRangeMin + value * chunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(long maxValue) {
|
||||||
|
setMaximum(maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMaximum(long maxValue) {
|
||||||
|
if (maxValue > 0) {
|
||||||
|
this.max = maxValue;
|
||||||
|
|
||||||
|
// The size of the current window/section of the overall monitor
|
||||||
|
long currentRange = currentRangeMax - currentRangeMin;
|
||||||
|
|
||||||
|
// the size of one increment within the current range
|
||||||
|
chunkSize = Math.max(currentRange / max, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.max = 0;
|
||||||
|
chunkSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMaximum() {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIndeterminate(boolean indeterminate) {
|
||||||
|
monitor.setIndeterminate(indeterminate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isIndeterminate() {
|
||||||
|
return monitor.isIndeterminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCanceled() throws CancelledException {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementProgress(long incrementAmount) {
|
||||||
|
progress += incrementAmount;
|
||||||
|
monitor.setProgress(currentRangeMin + progress * chunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getProgress() {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
monitor.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCancelledListener(CancelledListener listener) {
|
||||||
|
monitor.addCancelledListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCancelledListener(CancelledListener listener) {
|
||||||
|
monitor.removeCancelledListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCancelEnabled(boolean enable) {
|
||||||
|
monitor.setCancelEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelEnabled() {
|
||||||
|
return monitor.isCancelEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearCanceled() {
|
||||||
|
monitor.clearCanceled();
|
||||||
|
}
|
||||||
|
}
|
|
@ -221,7 +221,7 @@ public class GTreeDragNDropAdapter implements DragSourceListener, DragGestureLis
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < selectionPaths.length; i++) {
|
for (int i = 0; i < selectionPaths.length; i++) {
|
||||||
list.add((AbstractGTreeNode) selectionPaths[i].getLastPathComponent());
|
list.add((GTreeNode) selectionPaths[i].getLastPathComponent());
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -16,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.tree.internal;
|
package docking.widgets.tree.internal;
|
||||||
|
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -27,13 +24,14 @@ import javax.swing.tree.TreeModel;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeRootNode;
|
import ghidra.util.SystemUtilities;
|
||||||
|
|
||||||
public class GTreeModel implements TreeModel {
|
public class GTreeModel implements TreeModel {
|
||||||
|
|
||||||
private GTreeRootNode root;
|
private GTreeNode root;
|
||||||
private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
|
private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
|
||||||
private boolean isFiringNodeStructureChanged;
|
private boolean isFiringNodeStructureChanged;
|
||||||
|
private volatile boolean eventsEnabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a GTreeModel with the given root node.
|
* Constructs a GTreeModel with the given root node.
|
||||||
|
@ -42,64 +40,72 @@ public class GTreeModel implements TreeModel {
|
||||||
* @param isThreaded True signals to perform all tree tasks in a threaded environment to
|
* @param isThreaded True signals to perform all tree tasks in a threaded environment to
|
||||||
* avoid hanging the swing thread.
|
* avoid hanging the swing thread.
|
||||||
*/
|
*/
|
||||||
public GTreeModel(GTreeRootNode root) {
|
public GTreeModel(GTreeNode root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRootNode(GTreeRootNode root) {
|
public void setRootNode(GTreeNode root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
fireRootChanged();
|
fireRootChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getRoot() {
|
public Object getRoot() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GTreeRootNode getModelRoot() {
|
public GTreeNode getModelRoot() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addTreeModelListener(TreeModelListener l) {
|
public void addTreeModelListener(TreeModelListener l) {
|
||||||
listeners.add(l);
|
listeners.add(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeTreeModelListener(TreeModelListener l) {
|
public void removeTreeModelListener(TreeModelListener l) {
|
||||||
listeners.remove(l);
|
listeners.remove(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getChild(Object parent, int index) {
|
public Object getChild(Object parent, int index) {
|
||||||
GTreeNode gTreeParent = (GTreeNode) parent;
|
GTreeNode gTreeParent = (GTreeNode) parent;
|
||||||
return gTreeParent.getChild(index);
|
return gTreeParent.getChild(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getChildCount(Object parent) {
|
public int getChildCount(Object parent) {
|
||||||
GTreeNode gTreeParent = (GTreeNode) parent;
|
GTreeNode gTreeParent = (GTreeNode) parent;
|
||||||
return gTreeParent.getChildCount();
|
return gTreeParent.getChildCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getIndexOfChild(Object parent, Object child) {
|
public int getIndexOfChild(Object parent, Object child) {
|
||||||
GTreeNode gTreeParent = (GTreeNode) parent;
|
GTreeNode gTreeParent = (GTreeNode) parent;
|
||||||
return gTreeParent.getIndexOfChild((GTreeNode) child);
|
return gTreeParent.getIndexOfChild((GTreeNode) child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isLeaf(Object node) {
|
public boolean isLeaf(Object node) {
|
||||||
GTreeNode gTreeNode = (GTreeNode) node;
|
GTreeNode gTreeNode = (GTreeNode) node;
|
||||||
return gTreeNode.isLeaf();
|
return gTreeNode.isLeaf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void valueForPathChanged(TreePath path, Object newValue) {
|
public void valueForPathChanged(TreePath path, Object newValue) {
|
||||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||||
node.valueChanged(newValue);
|
node.valueChanged(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fireNodeStructureChanged(final GTreeNode changedNode) {
|
public void fireNodeStructureChanged(final GTreeNode changedNode) {
|
||||||
if (isFiringNodeStructureChanged) {
|
if (!eventsEnabled || isFiringNodeStructureChanged) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
isFiringNodeStructureChanged = true;
|
isFiringNodeStructureChanged = true;
|
||||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeStructuredChanged() must be "
|
SystemUtilities.assertThisIsTheSwingThread(
|
||||||
+ "called from the AWT thread");
|
"GTreeModel.fireNodeStructuredChanged() must be " + "called from the AWT thread");
|
||||||
|
|
||||||
TreeModelEvent event = new TreeModelEvent(this, changedNode.getTreePath());
|
TreeModelEvent event = new TreeModelEvent(this, changedNode.getTreePath());
|
||||||
for (TreeModelListener listener : listeners) {
|
for (TreeModelListener listener : listeners) {
|
||||||
|
@ -112,7 +118,11 @@ public class GTreeModel implements TreeModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fireRootChanged() {
|
public void fireRootChanged() {
|
||||||
|
if (!eventsEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
|
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
GTreeNode rootNode = root;
|
GTreeNode rootNode = root;
|
||||||
if (rootNode != null) {
|
if (rootNode != null) {
|
||||||
|
@ -123,8 +133,11 @@ public class GTreeModel implements TreeModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fireNodeDataChanged(final GTreeNode parentNode, final GTreeNode changedNode) {
|
public void fireNodeDataChanged(final GTreeNode parentNode, final GTreeNode changedNode) {
|
||||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeChanged() must be "
|
if (!eventsEnabled) {
|
||||||
+ "called from the AWT thread");
|
return;
|
||||||
|
}
|
||||||
|
SystemUtilities.assertThisIsTheSwingThread(
|
||||||
|
"GTreeModel.fireNodeDataChanged() must be " + "called from the AWT thread");
|
||||||
|
|
||||||
TreeModelEvent event;
|
TreeModelEvent event;
|
||||||
if (parentNode == null) { // special case when root node changes.
|
if (parentNode == null) { // special case when root node changes.
|
||||||
|
@ -145,8 +158,11 @@ public class GTreeModel implements TreeModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fireNodeAdded(final GTreeNode parentNode, final GTreeNode newNode) {
|
public void fireNodeAdded(final GTreeNode parentNode, final GTreeNode newNode) {
|
||||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeAdded() must be "
|
if (!eventsEnabled) {
|
||||||
+ "called from the AWT thread");
|
return;
|
||||||
|
}
|
||||||
|
SystemUtilities.assertThisIsTheSwingThread(
|
||||||
|
"GTreeModel.fireNodeAdded() must be " + "called from the AWT thread");
|
||||||
|
|
||||||
int indexInParent = newNode.getIndexInParent();
|
int indexInParent = newNode.getIndexInParent();
|
||||||
if (indexInParent < 0) {
|
if (indexInParent < 0) {
|
||||||
|
@ -164,8 +180,8 @@ public class GTreeModel implements TreeModel {
|
||||||
public void fireNodeRemoved(final GTreeNode parentNode, final GTreeNode removedNode,
|
public void fireNodeRemoved(final GTreeNode parentNode, final GTreeNode removedNode,
|
||||||
final int oldIndexInParent) {
|
final int oldIndexInParent) {
|
||||||
|
|
||||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeRemoved() must be "
|
SystemUtilities.assertThisIsTheSwingThread(
|
||||||
+ "called from the AWT thread");
|
"GTreeModel.fireNodeRemoved() must be " + "called from the AWT thread");
|
||||||
|
|
||||||
TreeModelEvent event =
|
TreeModelEvent event =
|
||||||
new TreeModelEvent(this, parentNode.getTreePath(), new int[] { oldIndexInParent },
|
new TreeModelEvent(this, parentNode.getTreePath(), new int[] { oldIndexInParent },
|
||||||
|
@ -178,4 +194,8 @@ public class GTreeModel implements TreeModel {
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
root = null;
|
root = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEventsEnabled(boolean b) {
|
||||||
|
eventsEnabled = b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue