mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +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
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +15,13 @@
|
|||
*/
|
||||
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.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -25,15 +31,6 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.util.exception.CancelledException;
|
||||
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 {
|
||||
|
||||
private boolean allowDuplicates;
|
||||
|
@ -124,13 +121,11 @@ public abstract class CallNode extends GTreeSlowLoadingNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void filter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
|
||||
throws CancelledException {
|
||||
public int loadAll(TaskMonitor monitor) throws CancelledException {
|
||||
if (depth() > filterDepth.get()) {
|
||||
doSetActiveChildren(new ArrayList<GTreeNode>());
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
super.filter(filter, monitor, min, max);
|
||||
return super.loadAll(monitor);
|
||||
}
|
||||
|
||||
private int depth() {
|
||||
|
|
|
@ -182,7 +182,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
public void actionPerformed(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gTree = (GTree) contextObject;
|
||||
GTreeRootNode rootNode = gTree.getRootNode();
|
||||
GTreeNode rootNode = gTree.getViewRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode child : children) {
|
||||
gTree.collapseAll(child);
|
||||
|
@ -283,7 +283,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
|
||||
for (TreePath path : selectionPaths) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (node instanceof GTreeRootNode) {
|
||||
if (node instanceof GTreeNode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
|
||||
for (TreePath path : selectionPaths) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (node instanceof GTreeRootNode) {
|
||||
if (node.isRoot()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -496,7 +496,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
|
||||
for (TreePath path : selectionPaths) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (node instanceof GTreeRootNode) {
|
||||
if (node.isRoot()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -551,7 +551,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
|
||||
for (TreePath path : selectionPaths) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (node instanceof GTreeRootNode) {
|
||||
if (node.isRoot()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -660,7 +660,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
|
||||
for (TreePath path : selectionPaths) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (node instanceof GTreeRootNode) {
|
||||
if (node.isRoot()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -915,7 +915,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
}
|
||||
|
||||
private void clearTrees() {
|
||||
if (incomingTree.getRootNode() instanceof EmptyRootNode) {
|
||||
if (incomingTree.getModelRoot() instanceof EmptyRootNode) {
|
||||
// already empty
|
||||
return;
|
||||
}
|
||||
|
@ -937,7 +937,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
}
|
||||
|
||||
private void updateIncomingReferencs(Function function) {
|
||||
GTreeRootNode rootNode = null;
|
||||
GTreeNode rootNode = null;
|
||||
if (function == null) {
|
||||
rootNode = new EmptyRootNode();
|
||||
}
|
||||
|
@ -949,7 +949,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
}
|
||||
|
||||
private void updateOutgoingReferences(Function function) {
|
||||
GTreeRootNode rootNode = null;
|
||||
GTreeNode rootNode = null;
|
||||
if (function == null) {
|
||||
rootNode = new EmptyRootNode();
|
||||
}
|
||||
|
@ -1083,12 +1083,12 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
GTreeRootNode rootNode = incomingTree.getRootNode();
|
||||
GTreeNode rootNode = incomingTree.getModelRoot();
|
||||
return rootNode instanceof EmptyRootNode;
|
||||
}
|
||||
|
||||
private boolean updateRootNodes(Function function) {
|
||||
CallNode callNode = (CallNode) incomingTree.getRootNode();
|
||||
CallNode callNode = (CallNode) incomingTree.getModelRoot();
|
||||
Function nodeFunction = callNode.getContainingFunction();
|
||||
if (nodeFunction.equals(function)) {
|
||||
reloadUpdateManager.update();
|
||||
|
@ -1109,7 +1109,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
CallNode rootNode = (CallNode) tree.getRootNode();
|
||||
CallNode rootNode = (CallNode) tree.getModelRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
updateFunction((CallNode) node);
|
||||
|
@ -1117,7 +1117,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
}
|
||||
|
||||
private void updateFunction(CallNode node) {
|
||||
if (!node.isChildrenLoadedOrInProgress()) {
|
||||
if (!node.isLoaded()) {
|
||||
// children not loaded, don't force a load by asking for them
|
||||
return;
|
||||
}
|
||||
|
@ -1189,6 +1189,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
this.recurseIcon.setNumber(depth);
|
||||
|
||||
removeFilterCache();
|
||||
incomingTree.refilterLater();
|
||||
outgoingTree.refilterLater();
|
||||
|
||||
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
|
||||
// takes a long time, then you lose some work.
|
||||
//
|
||||
GTreeRootNode rootNode = incomingTree.getRootNode();
|
||||
GTreeNode rootNode = incomingTree.getModelRoot();
|
||||
rootNode.removeAll();
|
||||
rootNode = outgoingTree.getRootNode();
|
||||
rootNode = outgoingTree.getModelRoot();
|
||||
rootNode.removeAll();
|
||||
}
|
||||
|
||||
|
@ -1364,7 +1366,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||
}
|
||||
}
|
||||
|
||||
private class PendingRootNode extends AbstractGTreeRootNode {
|
||||
private class PendingRootNode extends GTreeNode {
|
||||
|
||||
@Override
|
||||
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
|
||||
public Icon getIcon(boolean expanded) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,20 +15,15 @@
|
|||
*/
|
||||
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 javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeRootNode;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class IncomingCallsRootNode extends IncomingCallNode implements GTreeRootNode {
|
||||
|
||||
private GTree tree;
|
||||
public class IncomingCallsRootNode extends IncomingCallNode {
|
||||
|
||||
IncomingCallsRootNode(Program program, Function function, Address sourceAddress,
|
||||
boolean filterDuplicates, AtomicInteger filterDepth) {
|
||||
|
@ -52,14 +46,4 @@ public class IncomingCallsRootNode extends IncomingCallNode implements GTreeRoot
|
|||
public String getName() {
|
||||
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
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,20 +15,15 @@
|
|||
*/
|
||||
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 javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeRootNode;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class OutgoingCallsRootNode extends OutgoingCallNode implements GTreeRootNode {
|
||||
|
||||
private GTree tree;
|
||||
public class OutgoingCallsRootNode extends OutgoingCallNode {
|
||||
|
||||
OutgoingCallsRootNode(Program program, Function function, Address sourceAddress,
|
||||
boolean filterDuplicates, AtomicInteger filterDepth) {
|
||||
|
@ -62,14 +56,4 @@ public class OutgoingCallsRootNode extends OutgoingCallNode implements GTreeRoot
|
|||
public String getToolTip() {
|
||||
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);
|
||||
|
||||
DockingWindowManager.getHelpService().registerHelp(previewScrollPane,
|
||||
DockingWindowManager.getHelpService()
|
||||
.registerHelp(previewScrollPane,
|
||||
new HelpLocation("DataTypeManagerPlugin", "Preview_Window"));
|
||||
}
|
||||
|
||||
|
@ -701,8 +702,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
private ArchiveNode getProgramArchiveNode() {
|
||||
GTreeNode rootNode = getGTree().getRootNode();
|
||||
List<GTreeNode> children = rootNode.getAllChildren();
|
||||
GTreeNode rootNode = getGTree().getModelRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
Archive archive = archiveNode.getArchive();
|
||||
|
@ -714,8 +715,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
private ArchiveNode getDataTypeArchiveNode(DataTypeArchive dataTypeArchive) {
|
||||
GTreeNode rootNode = getGTree().getRootNode();
|
||||
List<GTreeNode> children = rootNode.getAllChildren();
|
||||
GTreeNode rootNode = getGTree().getModelRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
Archive archive = archiveNode.getArchive();
|
||||
|
@ -754,7 +755,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
Category category = dataTypeManager.getCategory(dataType.getCategoryPath());
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getRootNode();
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getViewRoot();
|
||||
ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
|
||||
if (archiveNode == null) {
|
||||
plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " +
|
||||
|
@ -844,8 +845,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
void programRenamed() {
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getRootNode();
|
||||
List<GTreeNode> allChildren = rootNode.getAllChildren();
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getModelRoot();
|
||||
List<GTreeNode> allChildren = rootNode.getChildren();
|
||||
for (GTreeNode node : allChildren) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.getArchive() instanceof ProgramArchive) {
|
||||
|
|
|
@ -219,8 +219,8 @@ class OpenDomainFileTask extends Task {
|
|||
}
|
||||
|
||||
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
for (GTreeNode node : rootNode) {
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
for (GTreeNode node : rootNode.getChildren()) {
|
||||
if (node instanceof ArchiveNode) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.getArchive() == archive) {
|
||||
|
|
|
@ -154,7 +154,7 @@ public class CollapseAllArchivesAction extends DockingAction {
|
|||
}
|
||||
|
||||
private void collapseAll(GTree archiveGTree) {
|
||||
GTreeNode rootNode = archiveGTree.getRootNode();
|
||||
GTreeNode rootNode = archiveGTree.getViewRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode childNode : children) {
|
||||
archiveGTree.collapseAll(childNode);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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;
|
||||
|
||||
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 docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
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 {
|
||||
private final DataTypeManagerPlugin plugin;
|
||||
|
@ -64,7 +62,8 @@ public class CreateArchiveAction extends DockingAction {
|
|||
Msg.trace(this, "Need to overwrite--showing dialog");
|
||||
if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(gTree,
|
||||
"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");
|
||||
return;
|
||||
}
|
||||
|
@ -79,7 +78,7 @@ public class CreateArchiveAction extends DockingAction {
|
|||
}
|
||||
|
||||
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() });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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;
|
||||
|
||||
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.DataTypesActionContext;
|
||||
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.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 {
|
||||
private final DataTypeManagerPlugin plugin;
|
||||
|
||||
|
@ -131,7 +130,7 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
|||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GTreeRootNode rootNode = gTree.getRootNode();
|
||||
GTreeNode rootNode = gTree.getViewRoot();
|
||||
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), parentNodeName,
|
||||
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
|
||||
int maxEnumSize = 1;
|
||||
for (int i = 0; i < enumArray.length; i++) {
|
||||
if (maxEnumSize < enumArray[i].getLength()) {
|
||||
maxEnumSize = enumArray[i].getLength();
|
||||
for (Enum element : enumArray) {
|
||||
if (maxEnumSize < element.getLength()) {
|
||||
maxEnumSize = element.getLength();
|
||||
}
|
||||
}
|
||||
SourceArchive sourceArchive = category.getDataTypeManager().getLocalSourceArchive();
|
||||
|
@ -184,17 +183,18 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
|||
new EnumDataType(category.getCategoryPath(), newName, maxEnumSize,
|
||||
category.getDataTypeManager());
|
||||
|
||||
for (int i = 0; i < enumArray.length; i++) {
|
||||
String[] names = enumArray[i].getNames();
|
||||
for (int j = 0; j < names.length; j++) {
|
||||
dataType.add(names[j], enumArray[i].getValue(names[j]));
|
||||
for (Enum element : enumArray) {
|
||||
String[] names = element.getNames();
|
||||
for (String name : names) {
|
||||
dataType.add(name, element.getValue(name));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
dataType.setSourceArchive(sourceArchive);
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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;
|
||||
|
||||
|
||||
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.tree.TreePath;
|
||||
|
||||
|
@ -29,6 +22,10 @@ import docking.ActionContext;
|
|||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
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 {
|
||||
private final DataTypeManagerPlugin plugin;
|
||||
|
@ -37,7 +34,7 @@ public class CreateProjectArchiveAction extends DockingAction {
|
|||
super("New Project Data Type Archive", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
setMenuBarData( new MenuData( new String[]{"New Project Archive..."}, null, "Archive" ) );
|
||||
setMenuBarData(new MenuData(new String[] { "New Project Archive..." }, null, "Archive"));
|
||||
setDescription("Creates a new project data type archive in this data type manager.");
|
||||
setEnabled(true);
|
||||
}
|
||||
|
@ -47,21 +44,22 @@ public class CreateProjectArchiveAction extends DockingAction {
|
|||
try {
|
||||
Archive newArchive = plugin.getDataTypeManagerHandler().createProjectArchive();
|
||||
DataTypeArchiveGTree gTree = plugin.getProvider().getGTree();
|
||||
selectNewArchive( newArchive, gTree );
|
||||
selectNewArchive(newArchive, gTree);
|
||||
}
|
||||
catch (CancelledException ce) {
|
||||
plugin.getTool().setStatusInfo("Create project archive was cancelled.");
|
||||
}
|
||||
}
|
||||
|
||||
private void selectNewArchive( final Archive archive, final DataTypeArchiveGTree gTree ) {
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// start an edit on the new temporary node name
|
||||
GTreeNode node = gTree.getRootNode();
|
||||
final GTreeNode child = node.getChild( archive.getName() );
|
||||
if ( child != null ) {
|
||||
gTree.expandPath( node );
|
||||
GTreeNode node = gTree.getViewRoot();
|
||||
final GTreeNode child = node.getChild(archive.getName());
|
||||
if (child != null) {
|
||||
gTree.expandPath(node);
|
||||
TreePath path = child.getTreePath();
|
||||
gTree.scrollPathToVisible(path);
|
||||
gTree.setSelectedNode(child);
|
||||
|
|
|
@ -84,7 +84,7 @@ public class DeleteAction extends DockingAction {
|
|||
private boolean containsUndeletableNodes(TreePath[] selectionPaths) {
|
||||
for (TreePath path : selectionPaths) {
|
||||
DataTypeTreeNode node = (DataTypeTreeNode) path.getLastPathComponent();
|
||||
if (node.isSystemNode() || (node instanceof ArchiveNode)) {
|
||||
if (!node.canDelete() || (node instanceof ArchiveNode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.ArchiveNode;
|
||||
|
@ -24,11 +26,6 @@ import ghidra.program.model.data.*;
|
|||
import ghidra.util.SystemUtilities;
|
||||
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
|
||||
* 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) {
|
||||
|
||||
DataTypeManager manager = plugin.getProgramDataTypeManager();
|
||||
GTreeRootNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
|
|
|
@ -149,8 +149,8 @@ public class DisassociateDataTypeAction extends DockingAction {
|
|||
private void collapseArchiveNodes(DataTypeArchiveGTree tree) {
|
||||
// Note: collapsing archive nodes will actually remove all the children of the archive
|
||||
// which means no event processing and less memory consumption.
|
||||
GTreeRootNode root = tree.getRootNode();
|
||||
List<GTreeNode> archives = root.getAllChildren();
|
||||
GTreeNode root = tree.getViewRoot();
|
||||
List<GTreeNode> archives = root.getChildren();
|
||||
archives.forEach(archive -> tree.collapseAll(archive));
|
||||
}
|
||||
|
||||
|
|
|
@ -210,7 +210,8 @@ public class ExportToHeaderAction extends DockingAction {
|
|||
finally {
|
||||
writer.close();
|
||||
}
|
||||
plugin.getTool().setStatusInfo(
|
||||
plugin.getTool()
|
||||
.setStatusInfo(
|
||||
"Successfully exported data type(s) to " + file.getAbsolutePath());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
|
@ -242,7 +243,7 @@ public class ExportToHeaderAction extends DockingAction {
|
|||
}
|
||||
else if (last instanceof CategoryNode) {
|
||||
CategoryNode node = (CategoryNode) last;
|
||||
List<GTreeNode> children = node.getAllChildren();
|
||||
List<GTreeNode> children = node.getChildren();
|
||||
for (GTreeNode cnode : children) {
|
||||
addToManager(cnode.getTreePath(), managersToDataTypesMap);
|
||||
}
|
||||
|
|
|
@ -15,20 +15,19 @@
|
|||
*/
|
||||
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 javax.swing.Icon;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.ActionContext;
|
||||
import docking.action.ToggleDockingAction;
|
||||
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 {
|
||||
|
||||
|
@ -41,8 +40,8 @@ public class FilterArraysAction extends ToggleDockingAction {
|
|||
|
||||
this.setToolBarData(new ToolBarData(FILTER_ARRAYS_ICON, "filters"));
|
||||
|
||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not Arrays are\n"
|
||||
+ "displayed in the Data Type Manager tree."));
|
||||
setDescription(HTMLUtilities.toHTML(
|
||||
"Toggle whether or not Arrays are\n" + "displayed in the Data Type Manager tree."));
|
||||
setSelected(true);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
@ -58,7 +57,7 @@ public class FilterArraysAction extends ToggleDockingAction {
|
|||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DataTypeArchiveGTree gtree = (DataTypeArchiveGTree) context.getContextObject();
|
||||
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getRootNode());
|
||||
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot());
|
||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||
gtree.enableArrayFilter(isSelected());
|
||||
gtree.expandPaths(expandedPaths);
|
||||
|
|
|
@ -15,20 +15,19 @@
|
|||
*/
|
||||
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 javax.swing.Icon;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.ActionContext;
|
||||
import docking.action.ToggleDockingAction;
|
||||
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 {
|
||||
private static final Icon FILTER_POINTERS_ICON =
|
||||
|
@ -40,8 +39,8 @@ public class FilterPointersAction extends ToggleDockingAction {
|
|||
|
||||
this.setToolBarData(new ToolBarData(FILTER_POINTERS_ICON, "filters"));
|
||||
|
||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not Pointers are\n"
|
||||
+ "displayed in the Data Type Manager tree."));
|
||||
setDescription(HTMLUtilities.toHTML(
|
||||
"Toggle whether or not Pointers are\n" + "displayed in the Data Type Manager tree."));
|
||||
setSelected(true);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
@ -56,8 +55,9 @@ public class FilterPointersAction extends ToggleDockingAction {
|
|||
|
||||
@Override
|
||||
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();
|
||||
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getRootNode());
|
||||
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot());
|
||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||
gtree.enablePointerFilter(isSelected());
|
||||
gtree.expandPaths(expandedPaths);
|
||||
|
|
|
@ -91,8 +91,8 @@ public class OpenArchiveAction extends DockingAction {
|
|||
}
|
||||
|
||||
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
List<GTreeNode> allChildren = rootNode.getAllChildren();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
List<GTreeNode> allChildren = rootNode.getChildren();
|
||||
for (GTreeNode node : allChildren) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.getArchive() == archive) {
|
||||
|
|
|
@ -32,15 +32,17 @@ public class ArchiveNode extends CategoryNode {
|
|||
protected ArchiveNodeCategoryChangeListener listener;
|
||||
private DataTypeManager dataTypeManager; // may be null
|
||||
|
||||
public ArchiveNode(Archive archive) {
|
||||
public ArchiveNode(Archive archive, ArrayPointerFilterState filterState) {
|
||||
this(archive, archive.getDataTypeManager() == null ? null
|
||||
: archive.getDataTypeManager().getRootCategory());
|
||||
: archive.getDataTypeManager().getRootCategory(),
|
||||
filterState);
|
||||
this.dataTypeManager = archive.getDataTypeManager();
|
||||
installDataTypeManagerListener();
|
||||
}
|
||||
|
||||
protected ArchiveNode(Archive archive, Category rootCategory) {
|
||||
super(rootCategory);
|
||||
protected ArchiveNode(Archive archive, Category rootCategory,
|
||||
ArrayPointerFilterState filterState) {
|
||||
super(rootCategory, filterState);
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
|
@ -51,7 +53,7 @@ public class ArchiveNode extends CategoryNode {
|
|||
protected void dataTypeManagerChanged() {
|
||||
installDataTypeManagerListener();
|
||||
// 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
|
||||
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() {
|
||||
removeAll();
|
||||
getTree().scheduleFilterTask(this);
|
||||
setChildren(null);
|
||||
}
|
||||
|
||||
public void nodeChanged() {
|
||||
|
@ -206,7 +207,7 @@ public class ArchiveNode extends CategoryNode {
|
|||
public CategoryNode findCategoryNode(Category localCategory, boolean loadChildren) {
|
||||
|
||||
// if we don't have to loadChildren and we are not loaded get out.
|
||||
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
|
||||
if (!loadChildren && !isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -228,7 +229,7 @@ public class ArchiveNode extends CategoryNode {
|
|||
return null;
|
||||
}
|
||||
|
||||
List<GTreeNode> children = node.getAllChildren();
|
||||
List<GTreeNode> children = node.getChildren();
|
||||
for (GTreeNode child : children) {
|
||||
if (!(child instanceof CategoryNode)) {
|
||||
continue;
|
||||
|
|
|
@ -19,21 +19,21 @@ import java.util.*;
|
|||
|
||||
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.util.DataTypeUtils;
|
||||
import ghidra.program.model.data.Category;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
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 GTree tree;
|
||||
|
||||
private final DataTypeManagerHandler archiveManager;
|
||||
private RootNodeListener archiveListener;
|
||||
|
||||
private ArrayPointerFilterState filterState = new ArrayPointerFilterState();
|
||||
|
||||
ArchiveRootNode(DataTypeManagerHandler archiveManager) {
|
||||
this.archiveManager = archiveManager;
|
||||
archiveListener = new RootNodeListener();
|
||||
|
@ -76,21 +76,22 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
}
|
||||
|
||||
// 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) {
|
||||
return new FileArchiveNode((FileArchive) archive);
|
||||
return new FileArchiveNode((FileArchive) archive, filterState);
|
||||
}
|
||||
else if (archive instanceof ProjectArchive) {
|
||||
return new ProjectArchiveNode((ProjectArchive) archive);
|
||||
return new ProjectArchiveNode((ProjectArchive) archive, filterState);
|
||||
}
|
||||
else if (archive instanceof InvalidFileArchive) {
|
||||
return new InvalidArchiveNode((InvalidFileArchive) archive);
|
||||
}
|
||||
else if (archive instanceof ProgramArchive) {
|
||||
return new ProgramArchiveNode((ProgramArchive) archive);
|
||||
return new ProgramArchiveNode((ProgramArchive) archive, filterState);
|
||||
}
|
||||
else if (archive instanceof BuiltInArchive) {
|
||||
return new BuiltInArchiveNode((BuiltInArchive) archive);
|
||||
return new BuiltInArchiveNode((BuiltInArchive) archive, filterState);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -137,12 +138,12 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemNode() {
|
||||
return true;
|
||||
public boolean canDelete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public CategoryNode findCategoryNode(Category category) {
|
||||
Iterator<GTreeNode> iterator = getAllChildren().iterator();
|
||||
Iterator<GTreeNode> iterator = getChildren().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
GTreeNode node = iterator.next();
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
|
@ -155,7 +156,7 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
}
|
||||
|
||||
public ArchiveNode getNodeForManager(DataTypeManager dtm) {
|
||||
Iterator<GTreeNode> iterator = getAllChildren().iterator();
|
||||
Iterator<GTreeNode> iterator = getChildren().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
GTreeNode node = iterator.next();
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
|
@ -170,17 +171,17 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
|
||||
@Override
|
||||
public List<GTreeNode> generateChildren() {
|
||||
List<GTreeNode> children = new ArrayList<>();
|
||||
List<GTreeNode> list = new ArrayList<>();
|
||||
Iterator<Archive> iterator = archiveManager.getAllArchives().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
children.add(createArchiveNode(iterator.next()));
|
||||
list.add(createArchiveNode(iterator.next(), filterState));
|
||||
}
|
||||
Collections.sort(children);
|
||||
return children;
|
||||
Collections.sort(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private ArchiveNode getArchiveNode(Archive archive) {
|
||||
List<GTreeNode> allChildrenList = getAllChildren();
|
||||
List<GTreeNode> allChildrenList = getChildren();
|
||||
for (GTreeNode node : allChildrenList) {
|
||||
if (node instanceof ArchiveNode) {
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
|
@ -200,10 +201,10 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
|
||||
@Override
|
||||
public void archiveClosed(Archive archive) {
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
List<GTreeNode> allChildrenList = getAllChildren();
|
||||
List<GTreeNode> allChildrenList = getChildren();
|
||||
Iterator<GTreeNode> iterator = allChildrenList.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
GTreeNode node = iterator.next();
|
||||
|
@ -218,9 +219,9 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
|
||||
@Override
|
||||
public void archiveOpened(Archive archive) {
|
||||
if (isChildrenLoadedOrInProgress()) {
|
||||
GTreeNode node = createArchiveNode(archive);
|
||||
List<GTreeNode> allChildrenList = getAllChildren();
|
||||
if (isLoaded()) {
|
||||
GTreeNode node = createArchiveNode(archive, filterState);
|
||||
List<GTreeNode> allChildrenList = getChildren();
|
||||
int index = Collections.binarySearch(allChildrenList, node);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
|
@ -228,7 +229,7 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
addNode(index, node);
|
||||
// kick tree to refilter if filter in place so that nodes will stay expaned see
|
||||
// SCR #7895
|
||||
tree.setFilterText(tree.getFilterText());
|
||||
// tree.setFilterText(tree.getFilterText());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,14 +250,12 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTree getGTree() {
|
||||
return tree;
|
||||
public void setFilterArray(boolean enabled) {
|
||||
filterState.setFilterArrays(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGTree(GTree tree) {
|
||||
this.tree = tree;
|
||||
public void setFilterPointer(boolean enabled) {
|
||||
filterState.setFilterPointers(enabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 BuiltInArchiveNode(BuiltInArchive archive) {
|
||||
super(archive);
|
||||
public BuiltInArchiveNode(BuiltInArchive archive, ArrayPointerFilterState filterState) {
|
||||
super(archive, filterState);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.util.*;
|
|||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.GTreeLazyNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||
import ghidra.program.model.data.*;
|
||||
|
@ -27,14 +26,16 @@ import ghidra.util.*;
|
|||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
||||
public class CategoryNode extends DataTypeTreeNode {
|
||||
|
||||
private Category category;
|
||||
private String name;
|
||||
|
||||
private boolean isCut;
|
||||
private ArrayPointerFilterState filterState;
|
||||
|
||||
public CategoryNode(Category category) {
|
||||
public CategoryNode(Category category, ArrayPointerFilterState filterState) {
|
||||
this.filterState = filterState;
|
||||
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
|
||||
protected List<GTreeNode> generateChildren() {
|
||||
if (category == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
DataTypeArchiveGTree tree = (DataTypeArchiveGTree) getTree();
|
||||
if (tree == null) {
|
||||
return Collections.emptyList(); // must have been disposed
|
||||
}
|
||||
|
||||
Category[] subCategories = category.getCategories();
|
||||
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) {
|
||||
children.add(new CategoryNode(subCategory));
|
||||
list.add(new CategoryNode(subCategory, filterState));
|
||||
}
|
||||
|
||||
for (DataType dataType : dataTypes) {
|
||||
if (!isFilteredType(tree, dataType)) {
|
||||
children.add(new DataTypeNode(dataType));
|
||||
if (!isFilteredType(dataType)) {
|
||||
list.add(new DataTypeNode(dataType));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(children);
|
||||
Collections.sort(list);
|
||||
|
||||
return children;
|
||||
return list;
|
||||
}
|
||||
|
||||
private boolean isFilteredType(DataTypeArchiveGTree tree, DataType dataType) {
|
||||
if (tree.isFilterArrays() && dataType instanceof Array) {
|
||||
private boolean isFilteredType(DataType dataType) {
|
||||
if (filterState.filterArrays() && dataType instanceof Array) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tree.isFilterPointers() && (dataType instanceof Pointer) &&
|
||||
if (filterState.filterPointers() && (dataType instanceof Pointer) &&
|
||||
!(dataType.getDataTypeManager() instanceof BuiltInDataTypeManager)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -172,12 +159,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
|||
|
||||
public void categoryAdded(Category newCategory) {
|
||||
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CategoryNode node = new CategoryNode(newCategory);
|
||||
List<GTreeNode> allChildrenList = getAllChildren();
|
||||
CategoryNode node = new CategoryNode(newCategory, filterState);
|
||||
List<GTreeNode> allChildrenList = getChildren();
|
||||
int index = Collections.binarySearch(allChildrenList, node);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
|
@ -186,7 +173,7 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
|||
}
|
||||
|
||||
public void dataTypeAdded(DataType dataType) {
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -196,12 +183,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
|||
return;
|
||||
}
|
||||
|
||||
if (isFilteredType(tree, dataType)) {
|
||||
if (isFilteredType(dataType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeNode node = new DataTypeNode(dataType);
|
||||
List<GTreeNode> allChildrenList = getAllChildren();
|
||||
List<GTreeNode> allChildrenList = getChildren();
|
||||
int index = Collections.binarySearch(allChildrenList, node);
|
||||
if (index >= 0) {
|
||||
if (node.getName().equals(allChildrenList.get(index).getName())) {
|
||||
|
@ -215,7 +202,10 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
|||
}
|
||||
|
||||
public void categoryRemoved(String categoryName) {
|
||||
for (GTreeNode node : getAllChildrenIfLoaded()) {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
for (GTreeNode node : getChildren()) {
|
||||
if ((node instanceof CategoryNode) && node.getName().equals(categoryName)) {
|
||||
removeNode(node);
|
||||
return;
|
||||
|
@ -224,8 +214,11 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
|||
}
|
||||
|
||||
public void dataTypeRemoved(String dataTypeName) {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (GTreeNode node : getAllChildrenIfLoaded()) {
|
||||
for (GTreeNode node : getChildren()) {
|
||||
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
|
||||
removeNode(node);
|
||||
return;
|
||||
|
@ -234,8 +227,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
|||
}
|
||||
|
||||
public void dataTypeChanged(DataType dataType) {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String dataTypeName = dataType.getName();
|
||||
for (GTreeNode node : getAllChildrenIfLoaded()) {
|
||||
for (GTreeNode node : getChildren()) {
|
||||
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
|
||||
((DataTypeNode) node).dataTypeChanged();
|
||||
return;
|
||||
|
@ -347,7 +344,7 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemNode() {
|
||||
return false;
|
||||
public boolean canDelete() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,9 +53,6 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
private MyFolderListener folderListener;
|
||||
private DataTypeTreeExpansionListener cleanupListener = new DataTypeTreeExpansionListener();
|
||||
|
||||
private boolean filterArrays = true;
|
||||
private boolean filterPointers = true;
|
||||
|
||||
public DataTypeArchiveGTree(DataTypeManagerPlugin dataTypeManagerPlugin) {
|
||||
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
|
||||
// ask for the height for each cell from the renderer.
|
||||
setRowHeight(getHeight(getRootNode(), renderer));
|
||||
setRowHeight(getHeight(getViewRoot(), renderer));
|
||||
setCellRenderer(renderer);
|
||||
Project project = plugin.getTool().getProject();
|
||||
if (project != null) {
|
||||
|
@ -79,7 +76,7 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
addTreeExpansionListener(cleanupListener);
|
||||
}
|
||||
|
||||
private int getHeight(GTreeRootNode rootNode, DataTypeTreeRenderer renderer) {
|
||||
private int getHeight(GTreeNode rootNode, DataTypeTreeRenderer renderer) {
|
||||
Component c = renderer.getTreeCellRendererComponent(getJTree(), rootNode, false, false,
|
||||
false, 0, false);
|
||||
Dimension size = c.getPreferredSize();
|
||||
|
@ -89,7 +86,7 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
@Override
|
||||
public void expandedStateRestored(TaskMonitor monitor) {
|
||||
// walk all of our nodes and reclaim any that aren't expanded
|
||||
GTreeRootNode rootNode = getRootNode();
|
||||
GTreeNode rootNode = getViewRoot();
|
||||
if (rootNode == null) {
|
||||
return; // in a state of flux; been disposed
|
||||
}
|
||||
|
@ -108,7 +105,7 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
if (!isExpanded(node.getTreePath())) {
|
||||
int leafCount = node.getLeafCount();
|
||||
if (node instanceof GTreeLazyNode) {
|
||||
((GTreeLazyNode) node).removeAll();
|
||||
((GTreeLazyNode) node).unloadChildren();
|
||||
}
|
||||
monitor.incrementProgress(leafCount);
|
||||
return;
|
||||
|
@ -122,7 +119,7 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
|
||||
@Override
|
||||
public void dispose() {
|
||||
((ArchiveRootNode) getRootNode()).dispose();
|
||||
((ArchiveRootNode) getModelRoot()).dispose();
|
||||
PluginTool tool = plugin.getTool();
|
||||
if (tool == null) {
|
||||
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) {
|
||||
this.filterArrays = enabled;
|
||||
ArchiveRootNode root = (ArchiveRootNode) getModelRoot();
|
||||
root.setFilterArray(enabled);
|
||||
reloadTree();
|
||||
}
|
||||
|
||||
public void enablePointerFilter(boolean enabled) {
|
||||
this.filterPointers = enabled;
|
||||
ArchiveRootNode root = (ArchiveRootNode) getModelRoot();
|
||||
root.setFilterPointer(enabled);
|
||||
reloadTree();
|
||||
}
|
||||
|
||||
|
@ -159,22 +158,12 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
private void reloadTree() {
|
||||
GTreeState treeState = getTreeState();
|
||||
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) getRootNode();
|
||||
rootNode.removeAll();
|
||||
// calling getChildCount() will "kick" the root node to reload its children
|
||||
rootNode.getChildCount();
|
||||
ArchiveRootNode rootNode = (ArchiveRootNode) getModelRoot();
|
||||
rootNode.unloadChildren();
|
||||
updateModelFilter();
|
||||
restoreTreeState(treeState);
|
||||
}
|
||||
|
||||
public boolean isFilterPointers() {
|
||||
return filterPointers;
|
||||
}
|
||||
|
||||
public boolean isFilterArrays() {
|
||||
return filterArrays;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodeEditable(GTreeNode node) {
|
||||
armedNode = node;
|
||||
|
@ -314,7 +303,7 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if ((node instanceof CategoryNode)) {
|
||||
CategoryNode categoryNode = (CategoryNode) node;
|
||||
categoryNode.removeAll();
|
||||
categoryNode.setChildren(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,7 +451,7 @@ public class DataTypeArchiveGTree extends GTree {
|
|||
|
||||
@Override
|
||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
List<GTreeNode> archiveNodes = getRootNode().getChildren();
|
||||
List<GTreeNode> archiveNodes = getModelRoot().getChildren();
|
||||
for (GTreeNode treeNode : archiveNodes) {
|
||||
if (treeNode instanceof ProjectArchiveNode) {
|
||||
ProjectArchiveNode projectArchiveNode = (ProjectArchiveNode) treeNode;
|
||||
|
|
|
@ -15,13 +15,15 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.datamgr.tree;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
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.util.DataTypeUtils;
|
||||
import ghidra.app.util.ToolTipUtils;
|
||||
|
@ -30,7 +32,7 @@ import ghidra.program.model.data.Enum;
|
|||
import ghidra.util.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode {
|
||||
public class DataTypeNode extends DataTypeTreeNode {
|
||||
private final DataType dataType;
|
||||
private final String name;
|
||||
private String displayName;
|
||||
|
@ -227,8 +229,8 @@ public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemNode() {
|
||||
return false;
|
||||
public boolean canDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void dataTypeStatusChanged() {
|
||||
|
@ -268,4 +270,9 @@ public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode
|
|||
|
||||
return baseDisplayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<GTreeNode> generateChildren() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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 docking.widgets.tree.GTreeLazyNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
|
||||
/**
|
||||
* A single interface for unifying the handling of nodes that can be manipulated during
|
||||
* 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.
|
||||
* @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
|
||||
* @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
|
||||
* a cut/paste operation.
|
||||
* @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.
|
||||
*/
|
||||
public boolean isCut();
|
||||
public abstract boolean isCut();
|
||||
|
||||
/**
|
||||
* Returns 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.
|
||||
* @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;
|
||||
|
||||
public DomainFileArchiveNode(DomainFileArchive archive) {
|
||||
super(archive);
|
||||
public DomainFileArchiveNode(DomainFileArchive archive, ArrayPointerFilterState filterState) {
|
||||
super(archive, filterState);
|
||||
|
||||
updateDomainFileInfo();
|
||||
}
|
||||
|
@ -109,8 +109,8 @@ public class DomainFileArchiveNode extends ArchiveNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemNode() {
|
||||
return true;
|
||||
public boolean canDelete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -32,8 +32,8 @@ public class FileArchiveNode extends ArchiveNode {
|
|||
|
||||
FileArchive fileArchive; // casted reference for easy access
|
||||
|
||||
public FileArchiveNode(FileArchive archive) {
|
||||
super(archive);
|
||||
public FileArchiveNode(FileArchive archive, ArrayPointerFilterState filterState) {
|
||||
super(archive, filterState);
|
||||
this.fileArchive = archive;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.HTMLUtilities;
|
|||
public class InvalidArchiveNode extends ArchiveNode {
|
||||
|
||||
public InvalidArchiveNode(InvalidFileArchive archive) {
|
||||
super(archive);
|
||||
super(archive, new ArrayPointerFilterState());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,8 +73,8 @@ public class InvalidArchiveNode extends ArchiveNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemNode() {
|
||||
return true;
|
||||
public boolean canDelete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,8 +21,8 @@ import ghidra.util.HTMLUtilities;
|
|||
|
||||
public class ProgramArchiveNode extends DomainFileArchiveNode {
|
||||
|
||||
public ProgramArchiveNode(ProgramArchive archive) {
|
||||
super(archive);
|
||||
public ProgramArchiveNode(ProgramArchive archive, ArrayPointerFilterState filterState) {
|
||||
super(archive, filterState);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,5 +33,4 @@ public class ProgramArchiveNode extends DomainFileArchiveNode {
|
|||
}
|
||||
return "[Unsaved New Program Archive]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,16 +21,15 @@ import ghidra.util.HTMLUtilities;
|
|||
|
||||
public class ProjectArchiveNode extends DomainFileArchiveNode {
|
||||
|
||||
public ProjectArchiveNode(ProjectArchive archive) {
|
||||
super(archive);
|
||||
public ProjectArchiveNode(ProjectArchive archive, ArrayPointerFilterState filterState) {
|
||||
super(archive, filterState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dataTypeManagerChanged() {
|
||||
removeAll(); // old children are no longer valid.
|
||||
setChildren(null); // old children are no longer valid.
|
||||
installDataTypeManagerListener();
|
||||
nodeChanged();
|
||||
fireNodeStructureChanged(this); // all the children have changed.
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -165,7 +165,7 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
|
|||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
GTreeRootNode root = tree.getRootNode();
|
||||
GTreeNode root = tree.getViewRoot();
|
||||
List<GTreeNode> dtNodes = new ArrayList<>();
|
||||
getDataTypeNodes(root, dtNodes);
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ package ghidra.app.plugin.core.datamgr.util;
|
|||
import java.util.*;
|
||||
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.DataTypesProvider;
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
|
@ -122,8 +123,8 @@ public class DataTypeTreeDeleteTask extends Task {
|
|||
}
|
||||
|
||||
private void collapseArchives(DataTypeArchiveGTree tree) {
|
||||
GTreeRootNode root = tree.getRootNode();
|
||||
List<GTreeNode> children = root.getAllChildren();
|
||||
GTreeNode root = tree.getModelRoot();
|
||||
List<GTreeNode> children = root.getChildren();
|
||||
for (GTreeNode archive : children) {
|
||||
tree.collapseAll(archive);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public class RegisterTree extends GTree {
|
|||
|
||||
public RegisterTree() {
|
||||
super(new RegisterTreeRootNode());
|
||||
root = (RegisterTreeRootNode) getRootNode();
|
||||
root = (RegisterTreeRootNode) getModelRoot();
|
||||
|
||||
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) {
|
||||
List<GTreeNode> allChildren = getAllChildren();
|
||||
List<GTreeNode> allChildren = getChildren();
|
||||
for (GTreeNode child : allChildren) {
|
||||
if (!(child instanceof RegisterTreeNode)) {
|
||||
continue;
|
||||
|
@ -160,9 +160,8 @@ abstract class SearchableRegisterTreeNode extends AbstractGTreeNode {
|
|||
}
|
||||
}
|
||||
|
||||
class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRootNode {
|
||||
class RegisterTreeRootNode extends SearchableRegisterTreeNode {
|
||||
private Register[] lastRegisters;
|
||||
private GTree tree;
|
||||
|
||||
@Override
|
||||
public Icon getIcon(boolean expanded) {
|
||||
|
@ -197,7 +196,8 @@ class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRo
|
|||
List<GTreeNode> nodes = new ArrayList<GTreeNode>();
|
||||
|
||||
for (Register register : registers) {
|
||||
if (register.getBaseRegister() != register && !register.getParentRegister().isHidden()) {
|
||||
if (register.getBaseRegister() != register &&
|
||||
!register.getParentRegister().isHidden()) {
|
||||
continue;
|
||||
}
|
||||
String groupName = register.getGroup();
|
||||
|
@ -217,17 +217,6 @@ class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRo
|
|||
Collections.sort(nodes);
|
||||
setChildren(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTree getGTree() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGTree(GTree tree) {
|
||||
this.tree = tree;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterTreeNode extends SearchableRegisterTreeNode {
|
||||
|
|
|
@ -37,7 +37,8 @@ import docking.widgets.filechooser.GhidraFileChooserMode;
|
|||
import docking.widgets.pathmanager.PathManager;
|
||||
import docking.widgets.pathmanager.PathManagerListener;
|
||||
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 generic.jar.ResourceFile;
|
||||
import generic.util.Path;
|
||||
|
@ -619,7 +620,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
List<GTreeNode> toDelete = new LinkedList<>();
|
||||
Iterator<GTreeNode> nodes = new BreadthFirstIterator(scriptCategoryTree, scriptRoot);
|
||||
Iterator<GTreeNode> nodes = new BreadthFirstIterator(scriptRoot);
|
||||
for (GTreeNode node : CollectionUtils.asIterable(nodes)) {
|
||||
String[] path = getCategoryPath(node);
|
||||
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);
|
||||
|
||||
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
|
||||
// so filtering in the tree will show all matching results when the
|
||||
// root is selected, instead of all results).
|
||||
GTreeRootNode rootNode = scriptCategoryTree.getRootNode();
|
||||
GTreeNode rootNode = scriptCategoryTree.getViewRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
String[] path = getCategoryPath(node);
|
||||
|
|
|
@ -19,14 +19,14 @@ import java.util.List;
|
|||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.AbstractGTreeRootNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import resources.ResourceManager;
|
||||
|
||||
class RootNode extends AbstractGTreeRootNode {
|
||||
class RootNode extends GTreeNode {
|
||||
private static Icon icon = ResourceManager.loadImage("images/play.png");
|
||||
|
||||
@Override
|
||||
|
@ -65,7 +65,7 @@ class RootNode extends AbstractGTreeRootNode {
|
|||
}
|
||||
|
||||
private GTreeNode getChildRegardlessOfFilter(GTreeNode parent, String name) {
|
||||
List<GTreeNode> children = parent.getAllChildren();
|
||||
List<GTreeNode> children = parent.getChildren();
|
||||
for (GTreeNode child : children) {
|
||||
if (child.getName().equals(name)) {
|
||||
return child;
|
||||
|
@ -75,7 +75,7 @@ class RootNode extends AbstractGTreeRootNode {
|
|||
}
|
||||
|
||||
private void insertSorted(GTreeNode parent, GTreeNode newChild) {
|
||||
List<GTreeNode> allChildren = parent.getAllChildren();
|
||||
List<GTreeNode> allChildren = parent.getChildren();
|
||||
for (GTreeNode child : allChildren) {
|
||||
String nodeName = child.getName();
|
||||
String newNodeName = newChild.getName();
|
||||
|
|
|
@ -17,10 +17,10 @@ package ghidra.app.plugin.core.script;
|
|||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.AbstractGTreeNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
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 CLOSED_FOLDER = ResourceManager.loadImage("images/closedSmallFolder.png");
|
||||
|
@ -49,7 +49,7 @@ public class ScriptCategoryNode extends AbstractGTreeNode {
|
|||
|
||||
@Override
|
||||
public boolean isLeaf() {
|
||||
return getAllChildCount() == 0;
|
||||
return getChildCount() == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ import java.awt.Component;
|
|||
import javax.swing.*;
|
||||
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 ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
|
||||
import ghidra.app.util.SymbolInspector;
|
||||
|
@ -34,7 +35,7 @@ public class SymbolGTree extends GTree {
|
|||
private GTreeNode armedNode;
|
||||
private SymbolInspector symbolInspector;
|
||||
|
||||
public SymbolGTree(GTreeRootNode root, SymbolTreePlugin plugin) {
|
||||
public SymbolGTree(GTreeNode root, SymbolTreePlugin plugin) {
|
||||
super(root);
|
||||
symbolInspector = new SymbolInspector(plugin.getTool(), this);
|
||||
setShowsRootHandles(true);
|
||||
|
|
|
@ -137,7 +137,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
|
||||
private SymbolGTree createTree(SymbolTreeRootNode rootNode) {
|
||||
if (tree != null) {
|
||||
GTreeNode oldRootNode = tree.getRootNode();
|
||||
GTreeNode oldRootNode = tree.getModelRoot();
|
||||
tree.setProgram(rootNode.getProgram());
|
||||
tree.setRootNode(rootNode);
|
||||
|
||||
|
@ -362,8 +362,9 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
private void rebuildTree() {
|
||||
GTreeNode node = tree.getRootNode();
|
||||
node.removeAll();
|
||||
SymbolTreeRootNode node = (SymbolTreeRootNode) tree.getModelRoot();
|
||||
node.setChildren(null);
|
||||
tree.refilterLater();
|
||||
}
|
||||
|
||||
private void symbolChanged(Symbol symbol, String oldName) {
|
||||
|
@ -446,8 +447,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
return;
|
||||
}
|
||||
|
||||
GTreeNode node = tree.getRootNode();
|
||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) tree.getViewRoot();
|
||||
tree.runTask(new SearchTask(tree, rootNode, symbol));
|
||||
}
|
||||
|
||||
|
@ -535,12 +535,12 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
void doRun(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
GTreeNode node = tree.getRootNode();
|
||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) tree.getModelRoot();
|
||||
|
||||
// the symbol may have been deleted while we are processing bulk changes
|
||||
if (symbol.checkIsValid()) {
|
||||
rootNode.symbolAdded(symbol);
|
||||
GTreeNode newNode = rootNode.symbolAdded(symbol);
|
||||
tree.refilterLater(newNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -562,15 +562,14 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
void doRun(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
GTreeNode node = tree.getRootNode();
|
||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
||||
|
||||
rootNode.symbolRemoved(symbol, oldName, monitor);
|
||||
SymbolTreeRootNode root = (SymbolTreeRootNode) tree.getModelRoot();
|
||||
root.symbolRemoved(symbol, monitor);
|
||||
|
||||
// the symbol may have been deleted while we are processing bulk changes
|
||||
if (symbol.checkIsValid()) {
|
||||
rootNode.symbolAdded(symbol);
|
||||
root.symbolAdded(symbol);
|
||||
}
|
||||
tree.refilterLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,9 +581,9 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
|
||||
@Override
|
||||
void doRun(TaskMonitor monitor) throws CancelledException {
|
||||
GTreeNode node = tree.getRootNode();
|
||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
|
||||
rootNode.symbolRemoved(symbol, monitor);
|
||||
SymbolTreeRootNode root = (SymbolTreeRootNode) tree.getModelRoot();
|
||||
root.symbolRemoved(symbol, monitor);
|
||||
tree.refilterLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,18 +57,17 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void symbolAdded(Symbol symbol) {
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
public SymbolNode symbolAdded(Symbol symbol) {
|
||||
if (!isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!supportsSymbol(symbol)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (symbol.getSymbolType() == symbolCategory.getSymbolType()) {
|
||||
doAddSymbol(symbol, this); // add new Class symbol
|
||||
return;
|
||||
return doAddSymbol(symbol, this); // add new Class symbol
|
||||
}
|
||||
|
||||
// see if the symbol is in a class namespace
|
||||
|
@ -77,9 +76,9 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
|||
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
|
||||
GTreeNode parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
if (parentNode == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
doAddSymbol(symbol, parentNode);
|
||||
return doAddSymbol(symbol, parentNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,6 +86,7 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
|||
throws CancelledException {
|
||||
List<GTreeNode> list = new ArrayList<GTreeNode>();
|
||||
|
||||
monitor.initialize(symbolTable.getNumSymbols());
|
||||
SymbolType symbolType = symbolCategory.getSymbolType();
|
||||
SymbolIterator it = symbolTable.getDefinedSymbols();
|
||||
while (it.hasNext()) {
|
||||
|
@ -95,6 +95,7 @@ public class ClassCategoryNode extends SymbolCategoryNode {
|
|||
monitor.checkCanceled();
|
||||
list.add(SymbolNode.createNode(s, program));
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
Collections.sort(list, getChildrenComparator());
|
||||
return list;
|
||||
|
|
|
@ -27,10 +27,10 @@ import ghidra.program.model.symbol.Symbol;
|
|||
import resources.ResourceManager;
|
||||
|
||||
public class ClassSymbolNode extends SymbolNode {
|
||||
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
||||
"Symbol Tree Data Flavor - Local Classes");
|
||||
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
||||
"Symbol Tree Data Flavor - Global Classes");
|
||||
static final DataFlavor LOCAL_DATA_FLAVOR =
|
||||
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Classes");
|
||||
static final DataFlavor GLOBAL_DATA_FLAVOR =
|
||||
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Classes");
|
||||
|
||||
private static Icon CLASS_ICON = ResourceManager.loadImage("images/class.png");
|
||||
private static Icon DISABLED_CLASS_ICONDISABLED_CLASS_ICON =
|
||||
|
|
|
@ -27,12 +27,12 @@ import ghidra.program.model.symbol.Symbol;
|
|||
import resources.ResourceManager;
|
||||
|
||||
public class CodeSymbolNode extends SymbolNode {
|
||||
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
||||
"Symbol Tree Data Flavor - Local Labels");
|
||||
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
||||
"Symbol Tree Data Flavor - Global Labels");
|
||||
static final DataFlavor EXTERNAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
||||
"Symbol Tree Data Flavor - External Data");
|
||||
static final DataFlavor LOCAL_DATA_FLAVOR =
|
||||
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Labels");
|
||||
static final DataFlavor GLOBAL_DATA_FLAVOR =
|
||||
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Labels");
|
||||
static final DataFlavor EXTERNAL_DATA_FLAVOR =
|
||||
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - External Data");
|
||||
|
||||
private static final Icon CODE_ICON = ResourceManager.loadImage("images/label.png");
|
||||
private static final Icon PINNED_ICON = ResourceManager.loadImage("images/pin.png");
|
||||
|
|
|
@ -102,31 +102,30 @@ class FunctionCategoryNode extends SymbolCategoryNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void symbolAdded(Symbol symbol) {
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
public SymbolNode symbolAdded(Symbol symbol) {
|
||||
if (!isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!supportsSymbol(symbol)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// variables and parameters will be beneath function nodes, and our parent method
|
||||
// will find them
|
||||
if (isVariableParameterOrCodeSymbol(symbol)) {
|
||||
super.symbolAdded(symbol);
|
||||
return;
|
||||
return super.symbolAdded(symbol);
|
||||
}
|
||||
|
||||
// this namespace will be beneath function nodes, and our parent method will find them
|
||||
if (isChildNamespaceOfFunction(symbol)) {
|
||||
super.symbolAdded(symbol);
|
||||
return;
|
||||
return super.symbolAdded(symbol);
|
||||
}
|
||||
|
||||
// ...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);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
private boolean isChildNamespaceOfFunction(Symbol symbol) {
|
||||
|
@ -171,7 +170,7 @@ class FunctionCategoryNode extends SymbolCategoryNode {
|
|||
public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren, TaskMonitor monitor) {
|
||||
|
||||
// if we don't have to loadChildren and we are not loaded get out.
|
||||
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
|
||||
if (!loadChildren && !isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,21 +85,22 @@ public class LabelCategoryNode extends SymbolCategoryNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void symbolAdded(Symbol symbol) {
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
public SymbolNode symbolAdded(Symbol symbol) {
|
||||
if (!isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// only include global symbols
|
||||
if (!symbol.isGlobal()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!supportsSymbol(symbol)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
GTreeNode newNode = SymbolNode.createNode(symbol, program);
|
||||
SymbolNode newNode = SymbolNode.createNode(symbol, program);
|
||||
doAddNode(this, newNode);
|
||||
return newNode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,5 +121,4 @@ public class LibrarySymbolNode extends SymbolNode {
|
|||
public Namespace getNamespace() {
|
||||
return (Library) symbol.getObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,20 +15,19 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.symboltree.nodes;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
||||
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) {
|
||||
super( program, symbol );
|
||||
super(program, symbol);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,6 +37,6 @@ public class LocalVariableSymbolNode extends SymbolNode {
|
|||
|
||||
@Override
|
||||
public void setNodeCut(boolean isCut) {
|
||||
throw new UnsupportedOperationException( "Cannot cut a local variable node" );
|
||||
throw new UnsupportedOperationException("Cannot cut a local variable node");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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;
|
||||
|
||||
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 javax.swing.Icon;
|
||||
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;
|
||||
|
||||
public class NamespaceSymbolNode extends SymbolNode {
|
||||
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
||||
"Symbol Tree Data Flavor - Local Namespaces");
|
||||
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
|
||||
"Symbol Tree Data Flavor - Global Namespaces");
|
||||
static final DataFlavor LOCAL_DATA_FLAVOR =
|
||||
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Namespaces");
|
||||
static final DataFlavor GLOBAL_DATA_FLAVOR =
|
||||
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Namespaces");
|
||||
|
||||
public static final Icon NAMESPACE_ICON = ResourceManager.loadImage("images/Namespace.gif");
|
||||
public static final Icon DISABLED_NAMESPACE_ICON =
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.*;
|
|||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.AbstractGTreeNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.util.datastruct.IntArray;
|
||||
|
@ -32,7 +31,7 @@ import resources.ResourceManager;
|
|||
* See {@link #computeChildren(List, int, GTreeNode, int, TaskMonitor)} for details on
|
||||
* how this class works.
|
||||
*/
|
||||
public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNode {
|
||||
public class OrganizationNode extends SymbolTreeNode {
|
||||
static final Comparator<GTreeNode> COMPARATOR = new OrganizationNodeComparator();
|
||||
|
||||
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
|
||||
* {@link #organize(List, int)}.
|
||||
* @throws CancelledException
|
||||
* {@link #organize(List, int, TaskMonitor)}
|
||||
* @throws CancelledException if the operation is cancelled
|
||||
*/
|
||||
private OrganizationNode(List<GTreeNode> list, int max, int parentLevel, GTreeNode parent,
|
||||
int indexInParent, TaskMonitor monitor) throws CancelledException {
|
||||
private OrganizationNode(List<GTreeNode> list, int max, int parentLevel, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
setChildren(computeChildren(list, max, this, parentLevel, monitor));
|
||||
|
||||
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
|
||||
* {@link #computeChildren(List, int, GTreeNode, int)} for details .
|
||||
* A factory method for creating OrganizationNode objects.
|
||||
* See {@link #computeChildren(List, int, GTreeNode, int, TaskMonitor)}
|
||||
*
|
||||
* @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 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.
|
||||
* @throws CancelledException
|
||||
* @see #computeChildren(List, int, GTreeNode, int)
|
||||
* @throws CancelledException if the operation is cancelled
|
||||
* @see #computeChildren(List, int, GTreeNode, int, TaskMonitor)
|
||||
*/
|
||||
public static List<GTreeNode> organize(List<GTreeNode> nodes, int max, TaskMonitor monitor)
|
||||
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 parent The parent of the given <tt>children</tt>
|
||||
* @param parentLevel node depth in the tree of <b>Organization</b> nodes.
|
||||
* @return the given <tt>list</tt> subgrouped as outlined above.
|
||||
* @throws CancelledException
|
||||
* @return the given <tt>list</tt> sub-grouped as outlined above.
|
||||
* @throws CancelledException if the operation is cancelled
|
||||
*/
|
||||
private static List<GTreeNode> computeChildren(List<GTreeNode> list, int maxNodes,
|
||||
GTreeNode parent, int parentLevel, TaskMonitor monitor) throws CancelledException {
|
||||
List<GTreeNode> children;
|
||||
if (list.size() <= maxNodes) {
|
||||
children = new ArrayList<>(list);
|
||||
Iterator<GTreeNode> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
parent.addNode(it.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
int characterOffset = getPrefixSizeForGrouping(list, maxNodes);
|
||||
|
@ -133,13 +128,13 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
|||
monitor.checkCanceled();
|
||||
String str = list.get(i).getName();
|
||||
if (stringsDiffer(prevStr, str, characterOffset)) {
|
||||
addNode(children, list, start, i - 1, maxNodes, parent, characterOffset,
|
||||
addNode(children, list, start, i - 1, maxNodes, characterOffset,
|
||||
monitor);
|
||||
start = i;
|
||||
}
|
||||
prevStr = str;
|
||||
}
|
||||
addNode(children, list, start, end - 1, maxNodes, parent, characterOffset, monitor);
|
||||
addNode(children, list, start, end - 1, maxNodes, characterOffset, monitor);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
@ -148,22 +143,20 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
|||
if (s1.length() <= diffLevel || s2.length() <= diffLevel) {
|
||||
return true;
|
||||
}
|
||||
return s1.substring(0, diffLevel + 1).compareToIgnoreCase(
|
||||
return s1.substring(0, diffLevel + 1)
|
||||
.compareToIgnoreCase(
|
||||
s2.substring(0, diffLevel + 1)) != 0;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (end - start > 0) {
|
||||
children.add(new OrganizationNode(list.subList(start, end + 1), max, diffLevel, parent,
|
||||
children.size(), monitor));
|
||||
children.add(new OrganizationNode(list.subList(start, end + 1), max, diffLevel,
|
||||
monitor));
|
||||
}
|
||||
else {
|
||||
GTreeNode node = list.get(start);
|
||||
if (parent != null) {
|
||||
parent.addNode(node);
|
||||
}
|
||||
children.add(node);
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +285,7 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
|
|||
* @param newNode the node to insert.
|
||||
*/
|
||||
public void insertNode(GTreeNode newNode) {
|
||||
int index = Collections.binarySearch(getAllChildren(), newNode, getChildrenComparator());
|
||||
int index = Collections.binarySearch(getChildren(), newNode, getChildrenComparator());
|
||||
if (index >= 0) {
|
||||
// found a match
|
||||
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
|
||||
// it has a new name.
|
||||
//
|
||||
return SymbolTreeNode.super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
||||
return super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
||||
}
|
||||
|
||||
@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
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ParameterSymbolNode extends SymbolNode {
|
||||
|
|
|
@ -18,7 +18,7 @@ package ghidra.app.plugin.core.symboltree.nodes;
|
|||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.util.*;
|
||||
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.symboltree.SymbolCategory;
|
||||
import ghidra.program.model.address.GlobalNamespace;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -27,7 +27,7 @@ import ghidra.util.exception.CancelledException;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
||||
public abstract class SymbolCategoryNode extends SymbolTreeNode {
|
||||
protected SymbolCategory symbolCategory;
|
||||
protected SymbolTable symbolTable;
|
||||
protected GlobalNamespace globalNamespace;
|
||||
|
@ -73,10 +73,12 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
|||
List<GTreeNode> list = new ArrayList<>();
|
||||
|
||||
SymbolType symbolType = symbolCategory.getSymbolType();
|
||||
monitor.initialize(symbolTable.getNumSymbols());
|
||||
SymbolIterator it =
|
||||
globalOnly ? symbolTable.getSymbols(globalNamespace) : symbolTable.getSymbolIterator();
|
||||
while (it.hasNext()) {
|
||||
Symbol s = it.next();
|
||||
monitor.incrementProgress(1);
|
||||
if (s != null && (s.getSymbolType() == symbolType)) {
|
||||
monitor.checkCanceled();
|
||||
list.add(SymbolNode.createNode(s, program));
|
||||
|
@ -151,20 +153,19 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
|||
dataFlavor == ClassSymbolNode.LOCAL_DATA_FLAVOR;
|
||||
}
|
||||
|
||||
public void symbolAdded(Symbol symbol) {
|
||||
public SymbolNode symbolAdded(Symbol symbol) {
|
||||
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
if (!isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!supportsSymbol(symbol)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
GTreeNode parentNode = this;
|
||||
if (symbol.isGlobal()) {
|
||||
doAddSymbol(symbol, parentNode);
|
||||
return;
|
||||
return doAddSymbol(symbol, parentNode);
|
||||
}
|
||||
|
||||
Namespace parentNamespace = symbol.getParentNamespace();
|
||||
|
@ -172,26 +173,27 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
|||
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
|
||||
parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
if (parentNode == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
doAddSymbol(symbol, parentNode);
|
||||
return doAddSymbol(symbol, parentNode);
|
||||
}
|
||||
|
||||
protected void doAddSymbol(Symbol symbol, GTreeNode parentNode) {
|
||||
if (!((AbstractGTreeNode) parentNode).isChildrenLoadedOrInProgress()) {
|
||||
return; // the node's not open, we don't care
|
||||
protected SymbolNode doAddSymbol(Symbol symbol, GTreeNode parentNode) {
|
||||
if (!parentNode.isLoaded()) {
|
||||
return null; // the node's not open, we don't care
|
||||
}
|
||||
|
||||
SymbolNode newNode = SymbolNode.createNode(symbol, program);
|
||||
doAddNode(parentNode, newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
protected void doAddNode(GTreeNode parentNode, GTreeNode newNode) {
|
||||
|
||||
SymbolTreeNode symbolTreeNode = (SymbolTreeNode) parentNode;
|
||||
Comparator<GTreeNode> comparator = symbolTreeNode.getChildrenComparator();
|
||||
List<GTreeNode> children = parentNode.getAllChildren();
|
||||
List<GTreeNode> children = parentNode.getChildren();
|
||||
int index = Collections.binarySearch(children, newNode, comparator);
|
||||
if (index >= 0) { // found a match
|
||||
GTreeNode matchingNode = getChild(index);
|
||||
|
@ -215,7 +217,7 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
|
|||
}
|
||||
|
||||
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.*;
|
|||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeSlowLoadingNode;
|
||||
import ghidra.app.cmd.label.CreateNamespacesCmd;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.program.model.address.GlobalNamespace;
|
||||
|
@ -34,7 +33,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
||||
public class SymbolNode extends SymbolTreeNode {
|
||||
|
||||
protected final Program program;
|
||||
protected final Symbol symbol;
|
||||
|
@ -42,6 +41,7 @@ public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
|||
private boolean isCut;
|
||||
|
||||
SymbolNode(Program program, Symbol symbol) {
|
||||
super();
|
||||
this.program = program;
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
|
|||
return this;
|
||||
}
|
||||
|
||||
return SymbolTreeNode.super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
||||
return super.findSymbolTreeNode(key, loadChildren, taskMonitor);
|
||||
}
|
||||
|
||||
public static SymbolNode createKeyNode(Symbol symbol, String searchSymbolName,
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.datatransfer.DataFlavor;
|
|||
import java.util.*;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeSlowLoadingNode;
|
||||
import ghidra.app.plugin.core.symboltree.SymbolTreeProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -27,10 +28,10 @@ import ghidra.program.model.symbol.Symbol;
|
|||
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
|
||||
* '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
|
||||
* 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
|
||||
|
@ -38,7 +39,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
* to keep its default {@link GTreeNode#compareTo(GTreeNode)} method, while allow each
|
||||
* parent node to sort its children differently.
|
||||
*/
|
||||
public interface SymbolTreeNode {
|
||||
public abstract class SymbolTreeNode extends GTreeSlowLoadingNode {
|
||||
|
||||
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.
|
||||
* @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
|
||||
* @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
|
||||
* a cut/paste operation.
|
||||
* @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.
|
||||
*/
|
||||
public boolean isCut();
|
||||
public abstract boolean isCut();
|
||||
|
||||
/**
|
||||
* Gets 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.
|
||||
* @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.
|
||||
*/
|
||||
public boolean supportsDataFlavors(DataFlavor[] dataFlavors);
|
||||
public abstract boolean supportsDataFlavors(DataFlavor[] dataFlavors);
|
||||
|
||||
/**
|
||||
* Returns the namespace for this symbol tree node. Not all implementations contain symbols,
|
||||
|
@ -134,7 +135,7 @@ public interface SymbolTreeNode {
|
|||
* namespace.
|
||||
* @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
|
||||
|
@ -143,7 +144,7 @@ public interface SymbolTreeNode {
|
|||
*
|
||||
* @return the comparator used to sort this node's children
|
||||
*/
|
||||
default public Comparator<GTreeNode> getChildrenComparator() {
|
||||
public Comparator<GTreeNode> getChildrenComparator() {
|
||||
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
|
||||
*/
|
||||
default public Symbol getSymbol() {
|
||||
public Symbol getSymbol() {
|
||||
// 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
|
||||
// symbol nodes to return their value.
|
||||
|
@ -162,13 +163,9 @@ public interface SymbolTreeNode {
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* <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
|
||||
* used by the Comparators to perform binary searches. These can be fabricated
|
||||
* by using {@link SymbolNode#createNode(Symbol, Program)}
|
||||
|
@ -177,15 +174,15 @@ public interface SymbolTreeNode {
|
|||
* @param monitor the task monitor
|
||||
* @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) {
|
||||
|
||||
// if we don't have to loadChildren and we are not loaded get out.
|
||||
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
|
||||
if (!loadChildren && !isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<GTreeNode> children = getAllChildren();
|
||||
List<GTreeNode> children = getChildren();
|
||||
int index = Collections.binarySearch(children, key, getChildrenComparator());
|
||||
if (index >= 0) {
|
||||
GTreeNode node = children.get(index);
|
||||
|
@ -222,12 +219,4 @@ public interface SymbolTreeNode {
|
|||
|
||||
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 docking.widgets.tree.*;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.symboltree.SymbolCategory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
@ -30,10 +30,9 @@ import ghidra.program.model.symbol.SymbolType;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
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 final String name;
|
||||
private GTree tree;
|
||||
|
||||
public SymbolTreeRootNode() {
|
||||
name = "No Symbol Tree";
|
||||
|
@ -229,19 +228,24 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
|
|||
}
|
||||
|
||||
@Override
|
||||
public void symbolAdded(Symbol symbol) {
|
||||
List<GTreeNode> allChildren = getAllChildren();
|
||||
public SymbolNode symbolAdded(Symbol symbol) {
|
||||
SymbolNode returnNode = null;
|
||||
List<GTreeNode> allChildren = getChildren();
|
||||
for (GTreeNode gNode : allChildren) {
|
||||
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
|
||||
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
|
||||
|
||||
// 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) {
|
||||
SymbolCategoryNode symbolNode = (SymbolCategoryNode) gNode;
|
||||
symbolNode.symbolRemoved(symbol, oldName, monitor);
|
||||
|
@ -249,8 +253,7 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
|
|||
}
|
||||
|
||||
public void rebuild() {
|
||||
removeAll();
|
||||
fireNodeStructureChanged(this);
|
||||
setChildren(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -302,14 +305,4 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
|
|||
public void setNodeCut(boolean isCut) {
|
||||
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) {
|
||||
try {
|
||||
monitor.setMessage("Probing " + containerFSRL.getName() + " for filesystems");
|
||||
FileSystemRef ref = FileSystemService.getInstance().probeFileForFilesystem(
|
||||
FileSystemRef ref = FileSystemService.getInstance()
|
||||
.probeFileForFilesystem(
|
||||
containerFSRL, monitor, FileSystemProbeConflictResolver.GUI_PICKER);
|
||||
if (ref == null) {
|
||||
Msg.showWarn(this, plugin.getTool().getActiveWindow(), "Open Filesystem",
|
||||
|
@ -1051,7 +1052,7 @@ class FSBActionManager {
|
|||
return;
|
||||
}
|
||||
FSBRootNode node = (FSBRootNode) context.getContextObject();
|
||||
if (node == gTree.getRootNode()) {
|
||||
if (node.equals(gTree.getModelRoot())) {
|
||||
// Close entire window
|
||||
FileSystemRef fsRef = node.getFSRef();
|
||||
if (fsRef != null && !fsRef.isClosed() &&
|
||||
|
@ -1174,7 +1175,8 @@ class FSBActionManager {
|
|||
gTree.runTask(monitor -> {
|
||||
try {
|
||||
FileSystemRef fsRef =
|
||||
FileSystemService.getInstance().probeFileForFilesystem(
|
||||
FileSystemService.getInstance()
|
||||
.probeFileForFilesystem(
|
||||
containerFSRL, monitor,
|
||||
FileSystemProbeConflictResolver.GUI_PICKER);
|
||||
if (fsRef == null) {
|
||||
|
@ -1183,7 +1185,7 @@ class FSBActionManager {
|
|||
return;
|
||||
}
|
||||
|
||||
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, gTree, fileNode);
|
||||
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, fileNode);
|
||||
FSBRootNode containingFSBRootNode =
|
||||
FSBNode.findContainingFileSystemFSBRootNode(fileNode);
|
||||
if (containingFSBRootNode != null) {
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
|||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeSlowLoadingNode;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -33,7 +32,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
* <p>
|
||||
* Visible to just this package.
|
||||
*/
|
||||
public class FSBDirNode extends GTreeSlowLoadingNode implements FSBNode {
|
||||
public class FSBDirNode extends FSBNode {
|
||||
private FSRL fsrl;
|
||||
|
||||
FSBDirNode(FSRL fsrl) {
|
||||
|
|
|
@ -15,17 +15,22 @@
|
|||
*/
|
||||
package ghidra.plugins.fsbrowser;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.AbstractGTreeNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* GTreeNode that represents a file on a filesystem.
|
||||
* <p>
|
||||
* Visible to just this package.
|
||||
*/
|
||||
public class FSBFileNode extends AbstractGTreeNode implements FSBNode {
|
||||
public class FSBFileNode extends FSBNode {
|
||||
protected FSRL fsrl;
|
||||
|
||||
FSBFileNode(FSRL fsrl) {
|
||||
|
@ -62,4 +67,9 @@ public class FSBFileNode extends AbstractGTreeNode implements FSBNode {
|
|||
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 docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeSlowLoadingNode;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -32,7 +33,7 @@ public interface FSBNode extends GTreeNode {
|
|||
*
|
||||
* @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
|
||||
|
@ -61,7 +62,7 @@ public interface FSBNode extends GTreeNode {
|
|||
|
||||
Collections.sort(files, FSUtilities.GFILE_NAME_TYPE_COMPARATOR);
|
||||
for (GFile child : files) {
|
||||
nodes.add(getNodeFromFile(child));
|
||||
nodes.add((GTreeNode) getNodeFromFile(child));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.*;
|
|||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -33,10 +33,9 @@ import ghidra.util.task.TaskMonitor;
|
|||
* <p>
|
||||
* Visible to just this package.
|
||||
*/
|
||||
public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode, FSBNode {
|
||||
public class FSBRootNode extends FSBNode {
|
||||
|
||||
private FileSystemRef fsRef;
|
||||
private GTree tree;
|
||||
private FSBFileNode prevNode;
|
||||
private List<FSBRootNode> subRootNodes = new ArrayList<>();
|
||||
|
||||
|
@ -44,9 +43,8 @@ public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode,
|
|||
this.fsRef = fsRef;
|
||||
}
|
||||
|
||||
FSBRootNode(FileSystemRef fsRef, GTree gTree, FSBFileNode prevNode) {
|
||||
FSBRootNode(FileSystemRef fsRef, FSBFileNode prevNode) {
|
||||
this.fsRef = fsRef;
|
||||
this.tree = gTree;
|
||||
this.prevNode = prevNode;
|
||||
}
|
||||
|
||||
|
@ -106,16 +104,6 @@ public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode,
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGTree(GTree tree) {
|
||||
this.tree = tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTree getGTree() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRL getFSRL() {
|
||||
return fsRef.getFilesystem().getFSRL();
|
||||
|
|
|
@ -60,7 +60,7 @@ public class FSBUtils {
|
|||
}
|
||||
|
||||
public static FSBRootNode getNodesRoot(FSBNode node) {
|
||||
GTreeNode tmp = node;
|
||||
GTreeNode tmp = (GTreeNode) node;
|
||||
while (tmp != null && !(tmp instanceof FSBRootNode)) {
|
||||
tmp = tmp.getParent();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package docking.widgets.tree;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -36,13 +36,11 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
private GTree gTree;
|
||||
private FilterTextField filterField;
|
||||
|
||||
private GTreeRootNode root;
|
||||
|
||||
private DockingWindowManager winMgr;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
root = new TestRootNode();
|
||||
GTreeNode root = new TestRootNode();
|
||||
gTree = new GTree(root);
|
||||
|
||||
filterField = (FilterTextField) gTree.getFilterField();
|
||||
|
@ -63,10 +61,11 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
public void testContains() {
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
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("XABC");
|
||||
|
@ -74,34 +73,38 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
checkContainsNode("XABCX");
|
||||
|
||||
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("");
|
||||
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
|
||||
public void testMultiWordContains() {
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ',
|
||||
MultitermEvaluationMode.AND);
|
||||
|
||||
setFilterText("CX AB");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ', MultitermEvaluationMode.OR);
|
||||
|
||||
setFilterText("CX AB");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
checkContainsNode("ABCX");
|
||||
checkContainsNode("XABCX");
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -109,26 +112,26 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false);
|
||||
// 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()) {
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||
MultitermEvaluationMode.AND);
|
||||
|
||||
setFilterText("CX" + delim + "AB");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||
MultitermEvaluationMode.OR);
|
||||
|
||||
setFilterText("CX" + delim + "AB");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
checkContainsNode("ABCX");
|
||||
checkContainsNode("XABCX");
|
||||
|
||||
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);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||
|
||||
|
@ -149,19 +152,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
String delimStr = delimPad + delim;
|
||||
|
||||
setFilterText("CX" + delimStr + "AB");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||
MultitermEvaluationMode.OR);
|
||||
|
||||
setFilterText("CX" + delimStr + "AB");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
checkContainsNode("ABCX");
|
||||
checkContainsNode("XABCX");
|
||||
|
||||
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);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||
|
||||
|
@ -182,19 +185,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
String delimStr = delim + delimPad;
|
||||
|
||||
setFilterText("CX" + delimStr + "AB");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||
MultitermEvaluationMode.OR);
|
||||
|
||||
setFilterText("CX" + delimStr + "AB");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
checkContainsNode("ABCX");
|
||||
checkContainsNode("XABCX");
|
||||
|
||||
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);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
String delimPad = StringUtilities.pad("", ' ', 1);
|
||||
|
||||
|
@ -215,19 +218,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
String delimStr = delimPad + delim + delimPad;
|
||||
|
||||
setFilterText("CX" + delimStr + "AB");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
|
||||
MultitermEvaluationMode.OR);
|
||||
|
||||
setFilterText("CX" + delimStr + "AB");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
checkContainsNode("ABCX");
|
||||
checkContainsNode("XABCX");
|
||||
|
||||
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() {
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, true);
|
||||
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("ABC");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
checkDoesNotContainsNode("ABC");
|
||||
checkDoesNotContainsNode("XABC");
|
||||
|
@ -247,167 +250,168 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
checkDoesNotContainsNode("XABCX");
|
||||
|
||||
setFilterText("MMM");
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvertedMultiWordContains() {
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.AND);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("CX AB");
|
||||
|
||||
checkDoesNotContainsNode("ABCX");
|
||||
checkDoesNotContainsNode("XABCX");
|
||||
assertEquals(3, root.getChildCount());
|
||||
assertEquals(3, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.OR);
|
||||
setFilterText("");
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("CX AB");
|
||||
|
||||
checkDoesNotContainsNode("ABCX");
|
||||
checkDoesNotContainsNode("XABCX");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWith() {
|
||||
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("ABC");
|
||||
checkContainsNode("ABC");
|
||||
checkContainsNode("ABCX");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("MMM");
|
||||
assertEquals(0, root.getChildCount());
|
||||
assertEquals(0, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvertedStartsWith() {
|
||||
setFilterOptions(TextFilterStrategy.STARTS_WITH, true);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("ABC");
|
||||
checkDoesNotContainsNode("ABC");
|
||||
checkDoesNotContainsNode("ABCX");
|
||||
assertEquals(3, root.getChildCount());
|
||||
assertEquals(3, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("MMM");
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExactMatch() {
|
||||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("ABC");
|
||||
checkContainsNode("ABC");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("MMM");
|
||||
assertEquals(0, root.getChildCount());
|
||||
assertEquals(0, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvertedExactMatch() {
|
||||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, true);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("ABC");
|
||||
checkDoesNotContainsNode("ABC");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("MMM");
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegExMatch() {
|
||||
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, false);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("^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");
|
||||
checkContainsNode("ABC");
|
||||
checkContainsNode("XABC");
|
||||
checkContainsNode("ABCX");
|
||||
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");
|
||||
checkContainsNode("XABCX");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("X{0,1}A.{0,2}X");
|
||||
checkContainsNode("XABCX");
|
||||
checkContainsNode("ABCX");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvertedRegExMatch() {
|
||||
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, true);
|
||||
// no filter text - make sure all 5 nodes are there
|
||||
assertEquals(5, root.getChildCount());
|
||||
assertEquals(5, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("^ABC$");
|
||||
checkDoesNotContainsNode("ABC");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("ABC");
|
||||
checkDoesNotContainsNode("ABC");
|
||||
checkDoesNotContainsNode("XABC");
|
||||
checkDoesNotContainsNode("ABCX");
|
||||
checkDoesNotContainsNode("XABCX");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("XA.{0,2}X");
|
||||
checkDoesNotContainsNode("XABCX");
|
||||
assertEquals(4, root.getChildCount());
|
||||
assertEquals(4, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("X{0,1}A.{0,2}X");
|
||||
checkDoesNotContainsNode("XABCX");
|
||||
checkDoesNotContainsNode("ABCX");
|
||||
assertEquals(3, root.getChildCount());
|
||||
assertEquals(3, viewRoot().getChildCount());
|
||||
|
||||
setFilterText("");
|
||||
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
|
||||
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -416,14 +420,14 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
setFilterText("ABC");
|
||||
checkContainsNode("ABC");
|
||||
checkContainsNode("ABCX");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||
checkContainsNode("ABC");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
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("XABC");
|
||||
checkContainsNode("ABCX");
|
||||
|
@ -436,20 +440,20 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
|
||||
setFilterText("ABC");
|
||||
checkContainsNode("ABC");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
Object originalValue = getInstanceField("uniquePreferenceKey", gTree);
|
||||
setInstanceField("preferenceKey", gTree.getFilterProvider(), "XYZ");
|
||||
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||
checkContainsNode("ABC");
|
||||
checkContainsNode("ABCX");
|
||||
assertEquals(2, root.getChildCount());
|
||||
assertEquals(2, viewRoot().getChildCount());
|
||||
|
||||
setInstanceField("preferenceKey", gTree.getFilterProvider(), originalValue);
|
||||
setInstanceField("optionsSet", gTree.getFilterProvider(), false);
|
||||
restorePreferences();
|
||||
checkContainsNode("ABC");
|
||||
assertEquals(1, root.getChildCount());
|
||||
assertEquals(1, viewRoot().getChildCount());
|
||||
|
||||
}
|
||||
|
||||
|
@ -465,7 +469,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
private void checkContainsNode(String string) {
|
||||
List<GTreeNode> children = root.getChildren();
|
||||
List<GTreeNode> children = viewRoot().getChildren();
|
||||
for (GTreeNode gTreeNode : children) {
|
||||
if (gTreeNode.getName().equals(string)) {
|
||||
return;
|
||||
|
@ -475,7 +479,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
}
|
||||
|
||||
private void checkDoesNotContainsNode(String string) {
|
||||
List<GTreeNode> children = root.getChildren();
|
||||
List<GTreeNode> children = viewRoot().getChildren();
|
||||
for (GTreeNode gTreeNode : children) {
|
||||
if (gTreeNode.getName().equals(string)) {
|
||||
Assert.fail("Expected node " + string +
|
||||
|
@ -517,7 +521,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
waitForTree(gTree);
|
||||
}
|
||||
|
||||
private class TestRootNode extends AbstractGTreeRootNode {
|
||||
private class TestRootNode extends GTreeNode {
|
||||
|
||||
TestRootNode() {
|
||||
List<GTreeNode> children = new ArrayList<>();
|
||||
|
@ -553,7 +557,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
|
|||
/**
|
||||
* A basic leaf node
|
||||
*/
|
||||
private class LeafNode extends AbstractGTreeNode {
|
||||
private class LeafNode extends GTreeNode {
|
||||
|
||||
private final String name;
|
||||
|
||||
|
|
|
@ -215,16 +215,16 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
String existingCaller = "Function_1000";// four levels back
|
||||
setIncomingFilter(existingCaller);
|
||||
assertIncomingMaxDepth(depth);
|
||||
assertIncomingMaxDepth(depth, true);
|
||||
|
||||
assertIncomingNode(existingCaller, depth);
|
||||
|
||||
depth = 3;
|
||||
setDepth(depth);
|
||||
setIncomingFilter(existingCaller);
|
||||
assertIncomingMaxDepth(0);// filter no longer matches
|
||||
assertIncomingMaxDepth(0, true);// filter no longer matches
|
||||
|
||||
assertIncomingNoNode(existingCaller, depth);
|
||||
assertIncomingNoNode(existingCaller, depth, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -236,16 +236,16 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
String existingCallee = "Function_8000";
|
||||
setOutgoingFilter(existingCallee);
|
||||
assertOutgoingMaxDepth(depth);
|
||||
assertOutgoingMaxDepth(depth, true);
|
||||
|
||||
assertOutgoingNode(existingCallee, depth);
|
||||
|
||||
depth = 2;
|
||||
setDepth(depth);
|
||||
setOutgoingFilter(existingCallee);
|
||||
assertOutgoingMaxDepth(0);// filter no longer matches
|
||||
assertOutgoingMaxDepth(0, true);// filter no longer matches
|
||||
|
||||
assertOutgoingNoNode(existingCallee, depth);
|
||||
assertOutgoingNoNode(existingCallee, depth, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -261,7 +261,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
String existingCaller = "2000";// at depth 3
|
||||
setIncomingFilter(existingCaller);
|
||||
assertIncomingMaxDepth(depth);
|
||||
assertIncomingMaxDepth(depth, true);
|
||||
|
||||
assertIncomingNode(existingCaller, depth);
|
||||
|
||||
|
@ -271,7 +271,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
existingCaller = "1000";// at depth 4
|
||||
setIncomingFilter(existingCaller);
|
||||
|
||||
assertIncomingMaxDepth(depth);
|
||||
assertIncomingMaxDepth(depth, true);
|
||||
assertIncomingNode(existingCaller, depth);
|
||||
}
|
||||
|
||||
|
@ -331,7 +331,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
fullyExpandIncomingNode(node);
|
||||
|
||||
assertIncomingMaxDepth(currentDepthSetting(provider));
|
||||
assertIncomingMaxDepth(currentDepthSetting(provider), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -347,7 +347,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
fullyExpandOutgoingNode(node);
|
||||
|
||||
assertOutgoingMaxDepth(currentDepthSetting(provider));
|
||||
assertOutgoingMaxDepth(currentDepthSetting(provider), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -365,7 +365,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
int nodeDepth = node.getTreePath().getPathCount() - 1;// -1 for root node
|
||||
int depth = 4 + nodeDepth;
|
||||
assertIncomingMaxDepth(depth);
|
||||
assertIncomingMaxDepth(depth, false);
|
||||
assertDepth(node, depth);
|
||||
}
|
||||
|
||||
|
@ -383,7 +383,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
fullyExpandOutgoingNode(node);
|
||||
|
||||
int depth = currentDepthSetting(provider);
|
||||
assertOutgoingMaxDepth(depth);
|
||||
assertOutgoingMaxDepth(depth, false);
|
||||
assertDepth(node, depth);
|
||||
}
|
||||
|
||||
|
@ -422,7 +422,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
//
|
||||
|
||||
myWaitForTree(incomingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
||||
GTreeNode rootNode = getRootNode(incomingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
||||
|
@ -442,7 +442,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
setProviderFunction("0x5000");
|
||||
|
||||
myWaitForTree(outgoingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||
|
@ -462,7 +462,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
setProviderFunction("0x5000");
|
||||
|
||||
myWaitForTree(outgoingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||
|
@ -503,7 +503,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(currentSelection.isEmpty());
|
||||
|
||||
myWaitForTree(outgoingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||
|
@ -588,7 +588,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
myWaitForTree(outgoingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||
|
@ -628,7 +628,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
myWaitForTree(outgoingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||
|
@ -698,7 +698,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
setProviderFunction(addrString);
|
||||
|
||||
myWaitForTree(incomingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
||||
GTreeNode rootNode = getRootNode(incomingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue("Incoming tree does not have callers as expected for function: " + addrString,
|
||||
children.size() > 0);
|
||||
|
@ -713,7 +713,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
setProviderFunction("0x5000");
|
||||
|
||||
myWaitForTree(incomingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
||||
GTreeNode rootNode = getRootNode(incomingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
|
||||
|
@ -744,7 +744,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
setProviderFunction("0x5000");
|
||||
|
||||
myWaitForTree(incomingTree, provider);
|
||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
||||
GTreeNode rootNode = getRootNode(incomingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"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);
|
||||
final AtomicReference<GTreeRootNode> ref = new AtomicReference<>();
|
||||
runSwing(() -> ref.set(tree.getRootNode()));
|
||||
final AtomicReference<GTreeNode> ref = new AtomicReference<>();
|
||||
runSwing(() -> ref.set(filtered ? tree.getViewRoot() : tree.getModelRoot()));
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
|
@ -830,8 +834,8 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
return functionManager.getFunctionAt(address);
|
||||
}
|
||||
|
||||
private void assertOutgoingNoNode(String name, int depth) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(false);
|
||||
private void assertOutgoingNoNode(String name, int depth, boolean filtered) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(false, filtered);
|
||||
for (NodeDepthInfo info : nodes) {
|
||||
String nodeName = info.node.getName();
|
||||
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);
|
||||
}
|
||||
|
||||
private void assertOutgoingMaxDepth(int depth) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(false);
|
||||
private void assertOutgoingMaxDepth(int depth, boolean filtered) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(false, filtered);
|
||||
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
|
||||
|
||||
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) {
|
||||
GTreeRootNode rootNode = getRootNode(incomingTree);
|
||||
GTreeNode rootNode = getRootNode(incomingTree);
|
||||
GTreeNode node = findNode(rootNode, text);
|
||||
assertNotNull(node);
|
||||
incomingTree.setSelectedNode(node);
|
||||
|
@ -897,7 +901,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private GTreeNode selectOutgoingNode(String text) {
|
||||
GTreeRootNode rootNode = getRootNode(outgoingTree);
|
||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||
GTreeNode node = findNode(rootNode, text);
|
||||
assertNotNull(node);
|
||||
outgoingTree.setSelectedNode(node);
|
||||
|
@ -912,7 +916,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
if (node instanceof GTreeSlowLoadingNode) {
|
||||
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
|
||||
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
|
||||
if (!loaded) {
|
||||
return null;// children not loaded--don't load
|
||||
}
|
||||
|
@ -929,23 +933,15 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void assertDepth(GTreeNode node, int depth) {
|
||||
int currentDepth = 0;
|
||||
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 currentDepth = node.getTreePath().getPathCount() - 1;
|
||||
int maxNodeDepth = getMaxNodeDepth(node, currentDepth);
|
||||
assertEquals("Node depth is not correct " + node, depth, maxNodeDepth);
|
||||
}
|
||||
|
||||
private int getMaxNodeDepth(GTreeNode node, int currentDepth) {
|
||||
int maxDepth = currentDepth + 1;
|
||||
int maxDepth = currentDepth;
|
||||
if (node instanceof GTreeSlowLoadingNode) {
|
||||
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
|
||||
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
|
||||
if (!loaded) {
|
||||
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);
|
||||
}
|
||||
|
||||
private void assertIncomingNoNode(String name, int depth) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(true);
|
||||
private void assertIncomingNoNode(String name, int depth, boolean filtered) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(true, filtered);
|
||||
for (NodeDepthInfo info : nodes) {
|
||||
String nodeName = info.node.getName();
|
||||
if (nodeName.indexOf(name) != -1) {
|
||||
|
@ -1002,16 +998,21 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void assertIncomingMaxDepth(int depth) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(true);
|
||||
private void assertIncomingMaxDepth(int depth, boolean filtered) {
|
||||
List<NodeDepthInfo> nodes = getNodesByDepth(true, filtered);
|
||||
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
|
||||
|
||||
assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth);
|
||||
}
|
||||
|
||||
private List<NodeDepthInfo> getNodesByDepth(boolean incoming) {
|
||||
return getNodesByDepth(incoming, false);
|
||||
}
|
||||
|
||||
private List<NodeDepthInfo> getNodesByDepth(boolean incoming, boolean filtered) {
|
||||
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);
|
||||
Collections.sort(list);
|
||||
return list;
|
||||
|
@ -1025,7 +1026,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
if (node instanceof GTreeSlowLoadingNode) {
|
||||
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
|
||||
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
|
||||
if (!loaded) {
|
||||
return;// children not loaded--don't load
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ public class StructureEditorArchiveTest extends AbstractStructureEditorTest {
|
|||
// openArchive = getDockingAction(plugin, "Open Data Type Archive");
|
||||
closeArchive = getAction(plugin, "Close Archive");
|
||||
waitForTree(dtTree);
|
||||
GTreeNode rootNode = dtTree.getRootNode();
|
||||
GTreeNode rootNode = dtTree.getModelRoot();
|
||||
GTreeNode newNode = rootNode.getChild("New Archive");
|
||||
selectNode(newNode);
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ public class StructureEditorArchiveTest extends AbstractStructureEditorTest {
|
|||
assertTrue(comp0.getDataType().isEquivalent(DataType.DEFAULT));
|
||||
assertTrue(comp1.getDataType().isEquivalent(new WordDataType()));
|
||||
|
||||
GTreeNode rootNode = dtTree.getRootNode();
|
||||
GTreeNode rootNode = dtTree.getModelRoot();
|
||||
GTreeNode child = rootNode.getChild("New Archive");
|
||||
selectNode(child);
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT
|
|||
conflictHandlerModesAction);
|
||||
tree = provider.getGTree();
|
||||
waitForTree();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getViewRoot();
|
||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
@ -180,7 +179,7 @@ public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedInte
|
|||
tree = provider.getGTree();
|
||||
treeModelModListener = new TreeModelModCounter();
|
||||
tree.addGTModelListener(treeModelModListener);
|
||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getViewRoot();
|
||||
|
||||
tool.showComponentProvider(provider, true);
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe
|
|||
assertNotNull(archiveDtm);
|
||||
assertEquals("windows_vs12_32", archiveDtm.getName());
|
||||
|
||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||
ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("windows_vs12_32");
|
||||
assertNotNull(archiveNode);
|
||||
ArchiveNode programNode = (ArchiveNode) archiveRootNode.getChild(program.getName());
|
||||
|
|
|
@ -79,7 +79,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
provider = plugin.getProvider();
|
||||
tree = provider.getGTree();
|
||||
waitForTree();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ public class CreateLabelsFromEnumsTest extends AbstractGhidraHeadedIntegrationTe
|
|||
provider = plugin.getProvider();
|
||||
tree = provider.getGTree();
|
||||
waitForTree();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||
programNode = (ArchiveNode) archiveRootNode.getChild(testName.getMethodName());
|
||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
conflictHandlerModesAction);
|
||||
tree = provider.getGTree();
|
||||
waitForTree();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||
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(
|
||||
DataTypeConflictHandler.ConflictResolutionPolicy conflictMode) {
|
||||
for (ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> actionState : conflictHandlerModesAction.getAllActionStates()) {
|
||||
for (ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> actionState : conflictHandlerModesAction
|
||||
.getAllActionStates()) {
|
||||
if (actionState.getUserData() == conflictMode) {
|
||||
return actionState;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Container;
|
||||
|
@ -110,7 +110,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
tree = provider.getGTree();
|
||||
jTree = (JTree) invokeInstanceMethod("getJTree", tree);
|
||||
waitForTree();
|
||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
|
||||
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,
|
||||
new Class[] { String[].class }, new Object[] { invalidNames }));
|
||||
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
GTreeNode invalidChild = rootNode.getChild("BADARCHIVENAME");
|
||||
assertNull("Tree did not close invalid archive.", invalidChild);
|
||||
}
|
||||
|
@ -523,7 +523,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
|
||||
DataTypeNode myStructNode = (DataTypeNode) cat2Node.getChild("MyStruct");
|
||||
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
||||
|
||||
selectNode(myStructNode);
|
||||
|
@ -550,7 +550,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
GTreeNode cat2Node = cat1Node.getChild("Category2");
|
||||
expandNode(cat2Node);
|
||||
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
||||
|
||||
selectNode(cat2Node);
|
||||
|
@ -575,14 +575,14 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||
pm.closeProgram();
|
||||
});
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
assertEquals(1, rootNode.getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandAll() throws Exception {
|
||||
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
selectNode(rootNode);
|
||||
DockingActionIf expandAction = getAction(plugin, "Expand All");
|
||||
assertTrue(expandAction.isEnabledForContext(treeContext));
|
||||
|
@ -608,7 +608,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
@Test
|
||||
public void testCollapseAll() throws Exception {
|
||||
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
selectNode(rootNode);
|
||||
DockingActionIf collapseAction = getAction(plugin, "Collapse All");
|
||||
assertTrue(collapseAction.isEnabledForContext(treeContext));
|
||||
|
@ -645,7 +645,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
|
||||
@Test
|
||||
public void testRefreshBuiltins() throws Exception {
|
||||
GTreeNode treeRoot = tree.getRootNode();
|
||||
GTreeNode treeRoot = tree.getModelRoot();
|
||||
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
|
||||
|
||||
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
|
||||
|
@ -850,7 +850,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
}
|
||||
|
||||
private ArchiveNode getBuiltInNode() {
|
||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||
ArchiveNode builtinNode = (ArchiveNode) archiveRootNode.getChild(BUILTIN_NAME);
|
||||
assertNotNull(builtinNode);
|
||||
return builtinNode;
|
||||
|
@ -936,7 +936,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
}
|
||||
|
||||
private void assertSingleFilterMatch(String[] path) {
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getViewRoot();
|
||||
|
||||
GTreeNode node = rootNode;
|
||||
for (int i = 0; i < path.length; i++) {
|
||||
|
@ -962,7 +962,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
}
|
||||
|
||||
private void assertEmptyTree() {
|
||||
final GTreeNode rootNode = tree.getRootNode();
|
||||
final GTreeNode rootNode = tree.getViewRoot();
|
||||
final Integer[] box = new Integer[1];
|
||||
runSwing(() -> box[0] = rootNode.getChildCount());
|
||||
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) {
|
||||
if (parent != tree.getRootNode()) {
|
||||
if (parent != tree.getModelRoot()) {
|
||||
assertTrue(!tree.isExpanded(parent.getTreePath()));
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ public class DataTypeTestUtils {
|
|||
waitForTree(plugin);
|
||||
|
||||
GTree tree = plugin.getProvider().getGTree();
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ public class DataTypeTestUtils {
|
|||
dataTypeManagerHandler.openArchive(file, checkout, isUserAction);
|
||||
|
||||
archiveTree = plugin.getProvider().getGTree();
|
||||
GTreeNode rootNode = archiveTree.getRootNode();
|
||||
GTreeNode rootNode = archiveTree.getViewRoot();
|
||||
waitForTree(plugin);
|
||||
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ public class DataTypeTestUtils {
|
|||
|
||||
String archiveNodeName = trimFullArchiveName(archiveName);
|
||||
GTree tree = plugin.getProvider().getGTree();
|
||||
GTreeNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
ArchiveNode archiveNode = (ArchiveNode) rootNode.getChild(archiveNodeName);
|
||||
if (archiveNode == null) {
|
||||
throw new IllegalArgumentException(
|
||||
|
|
|
@ -69,7 +69,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
tree = provider.getGTree();
|
||||
waitForTree();
|
||||
|
||||
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
|
||||
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
|
||||
builtInNode = (ArchiveNode) archiveRootNode.getChild("BuiltInTypes");
|
||||
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_NAME);
|
||||
|
||||
|
@ -349,7 +349,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
GTree gtree = (GTree) getInstanceField("tree", d);
|
||||
waitForTree(gtree);
|
||||
|
||||
ArchiveRootNode root = (ArchiveRootNode) gtree.getRootNode();
|
||||
ArchiveRootNode root = (ArchiveRootNode) gtree.getModelRoot();
|
||||
ArchiveNode programNode1 = (ArchiveNode) root.getChild(PROGRAM_NAME);
|
||||
|
||||
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.table.GDynamicColumnTableModel;
|
||||
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.test.TestUtils;
|
||||
import generic.util.Path;
|
||||
|
@ -194,7 +195,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
waitForTree(categoryTree);
|
||||
JTree jTree = (JTree) invokeInstanceMethod("getJTree", categoryTree);
|
||||
assertNotNull(jTree);
|
||||
GTreeNode child = categoryTree.getRootNode().getChild(category);
|
||||
GTreeNode child = categoryTree.getModelRoot().getChild(category);
|
||||
categoryTree.setSelectedNode(child);
|
||||
waitForTree(categoryTree);
|
||||
TreePath path = child.getTreePath();
|
||||
|
@ -716,7 +717,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
|
||||
waitForTree(tree);
|
||||
|
||||
GTreeNode parentNode = tree.getRootNode();
|
||||
GTreeNode parentNode = tree.getModelRoot();
|
||||
|
||||
String[] parts = newCategory.split("\\.");
|
||||
for (String category : parts) {
|
||||
|
@ -726,7 +727,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
}
|
||||
|
||||
protected GTreeNode findChildByName(GTreeNode node, String name) {
|
||||
List<GTreeNode> children = node.getAllChildren();
|
||||
List<GTreeNode> children = node.getChildren();
|
||||
for (GTreeNode child : children) {
|
||||
if (child.getName().equals(name)) {
|
||||
return child;
|
||||
|
@ -739,8 +740,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
|
||||
waitForTree(tree);
|
||||
|
||||
GTreeRootNode rootNode = tree.getRootNode();
|
||||
List<GTreeNode> children = rootNode.getAllChildren();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
if (node.getName().equals(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
|
||||
List<String> symbolNames =
|
||||
|
@ -342,7 +342,7 @@ public class SymbolTreePlugin3Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
util.waitForTree();
|
||||
|
||||
GTreeNode cnode = rootNode.getChild(4);
|
||||
List<GTreeNode> children = cnode.getAllChildren();
|
||||
List<GTreeNode> children = cnode.getChildren();
|
||||
|
||||
//@formatter:off
|
||||
List<String> symbolNames =
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.symboltree;
|
||||
|
||||
import static generic.test.AbstractGTest.waitForCondition;
|
||||
import static generic.test.AbstractGTest.*;
|
||||
import static generic.test.AbstractGenericTest.*;
|
||||
import static ghidra.test.AbstractGhidraHeadedIntegrationTest.getAction;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static ghidra.test.AbstractGhidraHeadedIntegrationTest.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Container;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
|
@ -38,7 +37,8 @@ import docking.ActionContext;
|
|||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingAction;
|
||||
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 ghidra.app.plugin.core.symboltree.nodes.*;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
|
@ -58,7 +58,7 @@ class SymbolTreeTestUtils {
|
|||
private SymbolTreePlugin plugin;
|
||||
private DockingActionIf symTreeAction;
|
||||
private SymbolGTree tree;
|
||||
private GTreeRootNode rootGTreeNode;
|
||||
private GTreeNode rootGTreeNode;
|
||||
private SymbolTreeProvider provider;
|
||||
private DockingActionIf renameAction;
|
||||
private DockingActionIf cutAction;
|
||||
|
@ -224,7 +224,7 @@ class SymbolTreeTestUtils {
|
|||
}
|
||||
|
||||
void collapseTree() {
|
||||
GTreeRootNode root = tree.getRootNode();
|
||||
GTreeNode root = tree.getViewRoot();
|
||||
List<GTreeNode> topLevelNodes = root.getChildren();
|
||||
topLevelNodes.forEach(n -> tree.collapseAll(n));
|
||||
waitForTree();
|
||||
|
@ -334,7 +334,7 @@ class SymbolTreeTestUtils {
|
|||
provider = plugin.getProvider();
|
||||
tree = findComponent(provider.getComponent(), SymbolGTree.class);
|
||||
waitForTree();
|
||||
rootGTreeNode = tree.getRootNode();
|
||||
rootGTreeNode = tree.getViewRoot();
|
||||
renameAction = getAction(plugin, "Rename Symbol");
|
||||
assertNotNull(renameAction);
|
||||
cutAction = getAction(plugin, "Cut SymbolTree Node");
|
||||
|
@ -406,7 +406,7 @@ class SymbolTreeTestUtils {
|
|||
}
|
||||
|
||||
public static GTreeNode getNode(GTree tree, String... path) {
|
||||
GTreeRootNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
String rootName = path[0];
|
||||
if (!rootNode.getName().equals(rootName)) {
|
||||
throw new RuntimeException(
|
||||
|
|
|
@ -188,7 +188,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
|||
assertTrue("Did not find the data type chooser tree",
|
||||
(provider instanceof DataTypeChooserDialog));
|
||||
GTree gTree = (GTree) getInstanceField("tree", provider);
|
||||
GTreeNode rootNode = gTree.getRootNode();
|
||||
GTreeNode rootNode = gTree.getModelRoot();
|
||||
waitForTree(gTree);
|
||||
final GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
|
||||
final DataTypeNode doubleNode = (DataTypeNode) builtInNode.getChild("double");
|
||||
|
@ -743,7 +743,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
|||
|
||||
private void pickSingleDataType(DataTypeChooserDialog chooserDialog) {
|
||||
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
|
||||
GTreeNode rootNode = gTree.getRootNode();
|
||||
GTreeNode rootNode = gTree.getModelRoot();
|
||||
waitForTree(gTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertEquals(1, children.size());// one archive
|
||||
|
@ -764,7 +764,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
|||
|
||||
private void pickFromMultipleDataTypes(DataTypeChooserDialog chooserDialog) {
|
||||
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
|
||||
GTreeNode rootNode = gTree.getRootNode();
|
||||
GTreeNode rootNode = gTree.getModelRoot();
|
||||
waitForTree(gTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertEquals(2, children.size());// two archives
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.framework.main;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
@ -25,7 +24,8 @@ import javax.swing.tree.TreeModel;
|
|||
|
||||
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.model.*;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
|
@ -62,8 +62,8 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
Program p = builder.getProgram();
|
||||
rootFolder.createFile("notepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
rootFolder.createFile("XNotepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
rootFolder.createFile(names[i], p, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
for (String name : names) {
|
||||
rootFolder.createFile(name, p, TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
}
|
||||
builder.dispose();
|
||||
|
||||
|
@ -249,7 +249,7 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
final AtomicBoolean result = new AtomicBoolean(false);
|
||||
final GTree gTree = getGTree();
|
||||
runSwing(() -> {
|
||||
GTreeRootNode node = gTree.getRootNode();
|
||||
GTreeNode node = gTree.getViewRoot();
|
||||
if (node != null) {
|
||||
gTree.expandPath(node);
|
||||
gTree.setSelectedNode(node);
|
||||
|
@ -307,7 +307,7 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
final AtomicBoolean result = new AtomicBoolean(false);
|
||||
final GTree gTree = getGTree();
|
||||
runSwing(() -> {
|
||||
GTreeRootNode root = gTree.getRootNode();
|
||||
GTreeNode root = gTree.getModelRoot();
|
||||
GTreeNode node = root.getChild(name);
|
||||
if (node != null) {
|
||||
gTree.expandPath(node);
|
||||
|
|
|
@ -34,7 +34,6 @@ import docking.action.ToggleDockingAction;
|
|||
import docking.test.AbstractDockingTest;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeRootNode;
|
||||
import docking.widgets.tree.support.*;
|
||||
import ghidra.framework.data.DomainObjectAdapter;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
|
@ -58,7 +57,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
|
|||
private TestEnv env;
|
||||
private DataTree tree;
|
||||
private DomainFolder rootFolder;
|
||||
private GTreeRootNode rootNode;
|
||||
private GTreeNode rootNode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
@ -82,7 +81,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
|
|||
rootFolder.createFile("tms", p, TaskMonitor.DUMMY);
|
||||
p.release(this);
|
||||
|
||||
rootNode = tree.getRootNode();
|
||||
rootNode = tree.getModelRoot();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
|
@ -717,7 +716,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
|
|||
performAction(selectAction, getDomainFileActionContext(), true);
|
||||
waitForTree();
|
||||
|
||||
BreadthFirstIterator it = new BreadthFirstIterator(tree, rootNode);
|
||||
BreadthFirstIterator it = new BreadthFirstIterator(rootNode);
|
||||
while (it.hasNext()) {
|
||||
GTreeNode node = it.next();
|
||||
assertTrue(tree.isPathSelected(node.getTreePath()));
|
||||
|
|
|
@ -37,6 +37,7 @@ import docking.options.editor.OptionsPanel;
|
|||
import docking.tool.util.DockingToolConstants;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import generic.io.NullWriter;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.data.DataPlugin;
|
||||
|
@ -367,7 +368,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
// this is an instance of OptionsNode
|
||||
GTree tree = (GTree) getInstanceField("gTree", optionsPanel);
|
||||
Object keyBindingsNode = getGTreeNode(tree.getRootNode(), "Key Bindings");
|
||||
Object keyBindingsNode = getGTreeNode(tree.getModelRoot(), "Key Bindings");
|
||||
selectNode(tree, keyBindingsNode);
|
||||
|
||||
debug("ee");
|
||||
|
@ -407,17 +408,18 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
SwingUtilities.invokeAndWait(() -> tree.setSelectionPath(path));
|
||||
}
|
||||
|
||||
private Object getGTreeNode(Object parent, String nodeName) throws Exception {
|
||||
List<?> children = (List<?>) getInstanceField("allChildrenList", parent);
|
||||
if (children == null) {
|
||||
private GTreeNode getGTreeNode(GTreeNode parent, String nodeName) throws Exception {
|
||||
if (!parent.isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
for (Object rootChild : children) {
|
||||
|
||||
List<GTreeNode> children = parent.getChildren();
|
||||
for (GTreeNode rootChild : children) {
|
||||
String name = (String) invokeInstanceMethod("getName", rootChild);
|
||||
if (nodeName.equals(name)) {
|
||||
return rootChild;
|
||||
}
|
||||
Object foundNode = getGTreeNode(rootChild, nodeName);
|
||||
GTreeNode foundNode = getGTreeNode(rootChild, nodeName);
|
||||
if (foundNode != null) {
|
||||
return foundNode;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ import docking.tool.ToolConstants;
|
|||
import docking.widgets.MultiLineLabel;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.table.RowObjectFilterModel;
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import generic.test.TestUtils;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.plugin.core.console.ConsolePlugin;
|
||||
|
@ -836,8 +837,8 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void waitForThreadedModel() throws InterruptedException {
|
||||
GTreeRootNode root = (GTreeRootNode) treeModel.getRoot();
|
||||
GTree gTree = root.getGTree();
|
||||
GTreeNode root = (GTreeNode) treeModel.getRoot();
|
||||
GTree gTree = root.getTree();
|
||||
while (gTree.isBusy()) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,11 @@
|
|||
*/
|
||||
package ghidra.bitpatterns.info;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.AbstractGTreeNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
@ -29,7 +28,7 @@ import resources.ResourceManager;
|
|||
* 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");
|
||||
|
||||
|
@ -65,7 +64,7 @@ public class FunctionBitPatternsGTreeNode extends AbstractGTreeNode {
|
|||
for (GTreeNode node : getChildren()) {
|
||||
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
|
||||
}
|
||||
List<GTreeNode> children = getChildren();
|
||||
List<GTreeNode> children = new ArrayList<>(getChildren());
|
||||
Collections.sort(children);
|
||||
setChildren(children);
|
||||
//now set isLeaf
|
||||
|
|
|
@ -15,12 +15,11 @@
|
|||
*/
|
||||
package ghidra.bitpatterns.info;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.widgets.tree.AbstractGTreeRootNode;
|
||||
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
|
||||
public String getName() {
|
||||
|
@ -54,7 +53,7 @@ public class FunctionBitPatternsGTreeRootNode extends AbstractGTreeRootNode {
|
|||
for (GTreeNode node : getChildren()) {
|
||||
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
|
||||
}
|
||||
List<GTreeNode> children = getChildren();
|
||||
List<GTreeNode> children = new ArrayList<>(getChildren());
|
||||
Collections.sort(children);
|
||||
setChildren(children);
|
||||
}
|
||||
|
|
|
@ -115,7 +115,8 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
|
|||
if (outputDirectory == null) {
|
||||
return;
|
||||
}
|
||||
fsbContext.getTree().runTask(
|
||||
fsbContext.getTree()
|
||||
.runTask(
|
||||
monitor -> doExportToEclipse(fsrl, outputDirectory, monitor));
|
||||
}
|
||||
}
|
||||
|
@ -278,7 +279,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
|
|||
writer.open();
|
||||
try {
|
||||
// gTree.expandAll( node );
|
||||
writeFile(writer, node.getAllChildren());
|
||||
writeFile(writer, node.getChildren());
|
||||
}
|
||||
finally {
|
||||
writer.close();
|
||||
|
@ -303,7 +304,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
|
|||
writer.write(childFSRL.getName());
|
||||
}
|
||||
else {
|
||||
writeFile(writer, child.getAllChildren());
|
||||
writeFile(writer, child.getChildren());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public class TreeTestUtils {
|
|||
if (text.equals(node.getName())) {
|
||||
return node.getTreePath();
|
||||
}
|
||||
List<GTreeNode> allChildren = node.getAllChildren();
|
||||
List<GTreeNode> allChildren = node.getChildren();
|
||||
for (GTreeNode childNode : allChildren) {
|
||||
TreePath treePath = findPathToText(tree, childNode, text);
|
||||
if (treePath != null) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import ghidra.util.*;
|
|||
import ghidra.util.exception.*;
|
||||
import ghidra.util.html.HTMLElement;
|
||||
import resources.ResourceManager;
|
||||
import util.CollectionUtils;
|
||||
|
||||
public class ErrLogExpandableDialog extends DialogComponentProvider {
|
||||
public static ImageIcon IMG_REPORT = ResourceManager.loadImage("images/report.png");
|
||||
|
@ -246,10 +247,9 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
|||
}
|
||||
};
|
||||
|
||||
for (TreePath path : root.allPaths()) {
|
||||
Object last = path.getLastPathComponent();
|
||||
if (last instanceof ReportExceptionNode) {
|
||||
excTree.expandTree((GTreeNode) last);
|
||||
for (GTreeNode node : CollectionUtils.asIterable(root.iterator(true))) {
|
||||
if (node instanceof ReportExceptionNode) {
|
||||
excTree.expandTree(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,8 +406,8 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
|||
indent += 1;
|
||||
}
|
||||
}
|
||||
boolean doAll = (included == null || !containsAny(included, cur.getAllChildren()));
|
||||
for (GTreeNode node : cur.getAllChildren()) {
|
||||
boolean doAll = (included == null || !containsAny(included, cur.getChildren()));
|
||||
for (GTreeNode node : cur.getChildren()) {
|
||||
if (node instanceof NodeWithText && (doAll || included.contains(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 String title;
|
||||
protected boolean loaded = false;
|
||||
|
@ -435,6 +435,9 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
|||
public ReportRootNode(String title, Collection<? extends Throwable> report) {
|
||||
this.title = title;
|
||||
this.report = report;
|
||||
for (Throwable exc : report) {
|
||||
addNode(new ReportExceptionNode(exc));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -442,16 +445,6 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
|||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadChildren() {
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
for (Throwable exc : report) {
|
||||
addNode(new ReportExceptionNode(exc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon(boolean expanded) {
|
||||
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 boolean loaded = false;
|
||||
|
||||
|
@ -497,22 +490,21 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadChildren() {
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
addNode(new ReportStackTraceNode(exc));
|
||||
protected List<GTreeNode> generateChildren() {
|
||||
List<GTreeNode> list = new ArrayList<GTreeNode>();
|
||||
list.add(new ReportStackTraceNode(exc));
|
||||
Throwable c = exc.getCause();
|
||||
if (c != null) {
|
||||
if (c instanceof MultipleCauses) {
|
||||
for (Throwable t : ((MultipleCauses) c).getCauses()) {
|
||||
addNode(new ReportExceptionNode(t));
|
||||
list.add(new ReportExceptionNode(t));
|
||||
}
|
||||
}
|
||||
else {
|
||||
addNode(new ReportCauseNode(c));
|
||||
}
|
||||
list.add(new ReportCauseNode(c));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@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 boolean loaded = false;
|
||||
|
||||
|
@ -567,13 +559,12 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void loadChildren() {
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
protected List<GTreeNode> generateChildren() {
|
||||
List<GTreeNode> list = new ArrayList<>();
|
||||
for (StackTraceElement te : exc.getStackTrace()) {
|
||||
addNode(new ReportStackFrameNode(te));
|
||||
}
|
||||
list.add(new ReportStackFrameNode(te));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
public ReportStackFrameNode(StackTraceElement te) {
|
||||
|
|
|
@ -32,7 +32,8 @@ import docking.help.HelpService;
|
|||
import docking.widgets.MultiLineLabel;
|
||||
import docking.widgets.OptionDialog;
|
||||
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 ghidra.framework.options.*;
|
||||
import ghidra.util.*;
|
||||
|
@ -231,7 +232,7 @@ public class OptionsPanel extends JPanel {
|
|||
public void displayCategory(String category, String filterText) {
|
||||
String escapedDelimiter = Pattern.quote(Options.DELIMITER_STRING);
|
||||
|
||||
GTreeRootNode root = gTree.getRootNode();
|
||||
GTreeNode root = gTree.getModelRoot();
|
||||
category = root.getName() + Options.DELIMITER_STRING + category;
|
||||
String[] categories = category.split(escapedDelimiter);
|
||||
gTree.setFilterText(filterText);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,17 +15,15 @@
|
|||
*/
|
||||
package docking.options.editor;
|
||||
|
||||
import ghidra.framework.options.Options;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
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 GTree tree;
|
||||
|
||||
OptionsRootTreeNode(String name, Options[] options) {
|
||||
super(name, null);
|
||||
|
@ -55,14 +52,4 @@ class OptionsRootTreeNode extends OptionsTreeNode implements GTreeRootNode {
|
|||
protected JComponent getEditorComponent() {
|
||||
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
|
||||
public boolean isLeaf() {
|
||||
return getAllChildCount() == 0;
|
||||
return getChildCount() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2071,7 +2071,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
|||
}
|
||||
|
||||
public static GTreeNode getNode(GTree tree, String... path) {
|
||||
GTreeRootNode rootNode = tree.getRootNode();
|
||||
GTreeNode rootNode = tree.getModelRoot();
|
||||
String rootName = path[0];
|
||||
if (!rootNode.getName().equals(rootName)) {
|
||||
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;
|
||||
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import docking.widgets.tree.internal.InProgressGTreeNode;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
* This class is not meant to be subclassed directly. Instead, you should extend
|
||||
* {@link AbstractGTreeNode}.
|
||||
* <p>
|
||||
* This class is responsible for mutating/managing the children and parent of this node. These
|
||||
* items are sensitive to threading issues, which this class is designed to handle.
|
||||
* <p>
|
||||
* The pattern used by this class is to create <tt>doXXX</tt> methods for the public mutator
|
||||
* methods of the {@link GTreeNode} interface.
|
||||
* This class exists to help prevent threading errors in {@link GTreeNode} and subclasses,
|
||||
* by privately maintaining synchronous access to the parent and children of a node.
|
||||
* <P>
|
||||
* This implementation uses a {@link CopyOnWriteArrayList} to store its children. The theory is
|
||||
* that this will allow direct thread-safe access to the children without having to worry about
|
||||
* {@link ConcurrentModificationException}s. Also, the assumption is that accessing the children
|
||||
* will occur much more frequently than modifying the children. This should only be a problem if
|
||||
* 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 {
|
||||
private static InProgressGTreeNode IN_PROGRESS_NODE = new InProgressGTreeNode();
|
||||
private static List<GTreeNode> IN_PROGRESS_CHILDREN =
|
||||
Collections.unmodifiableList(Arrays.asList(new GTreeNode[] { IN_PROGRESS_NODE }));
|
||||
abstract class CoreGTreeNode implements Cloneable {
|
||||
// the parent is volatile to facilitate the synchronization strategy (see comments above)
|
||||
private volatile GTreeNode parent;
|
||||
private List<GTreeNode> children;
|
||||
|
||||
private GTreeNode parent;
|
||||
private List<GTreeNode> allChildrenList = null;
|
||||
private List<GTreeNode> activeChildrenList = null;
|
||||
|
||||
@Override
|
||||
public synchronized GTreeNode getParent() {
|
||||
/**
|
||||
* Returns the parent of this node.
|
||||
*
|
||||
* Note: this method is deliberately not synchronized (See comments above)
|
||||
* @return the parent of this node.
|
||||
*/
|
||||
public final GTreeNode getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
parent = null;
|
||||
if (allChildrenList == null) {
|
||||
return;
|
||||
/**
|
||||
* Sets the parent of this node. This method should only be used by a parent
|
||||
* node when a new child is added to that parent node.
|
||||
* @param parent the node that this node is being added to.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
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
|
||||
* example, if the subclass has a sorted list of children, then a binary search can
|
||||
* be used.
|
||||
*
|
||||
* @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
|
||||
* Creates a clone of this node. The clone should contain a shallow copy of all the node's
|
||||
* attributes except that the parent and children are null.
|
||||
* @return the clone of this object.
|
||||
* @throws CloneNotSupportedException if some implementation prevents itself from being cloned.
|
||||
*/
|
||||
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) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
return children.indexOf(node);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// 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()) {
|
||||
if (Swing.isSwingThread()) {
|
||||
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;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// End Utility Methods
|
||||
//==================================================================================================
|
||||
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package docking.widgets.tree;
|
||||
|
||||
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.USER_GENERATED;
|
||||
import static ghidra.util.SystemUtilities.runSwingNow;
|
||||
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.*;
|
||||
import static ghidra.util.SystemUtilities.*;
|
||||
|
||||
import java.awt.*;
|
||||
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
|
||||
* 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 GTreeRenderer renderer;
|
||||
|
@ -90,9 +98,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
|
||||
private GTreeFilter filter;
|
||||
private GTreeFilterProvider filterProvider;
|
||||
private List<GTreeNode> nodesToBeFiltered = new ArrayList<>();
|
||||
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
|
||||
|
@ -100,13 +106,13 @@ public class GTree extends JPanel implements BusyListener {
|
|||
*
|
||||
* @param root The root node of the tree.
|
||||
*/
|
||||
public GTree(GTreeRootNode root) {
|
||||
public GTree(GTreeNode root) {
|
||||
uniquePreferenceKey = generateFilterPreferenceKey();
|
||||
this.realRootNode = root;
|
||||
monitor = new TaskMonitorComponent();
|
||||
monitor.setShowProgressValue(false);// the tree's progress is fabricated--don't paint it
|
||||
worker = new PriorityWorker("GTree Worker", monitor);
|
||||
root.setGTree(this);
|
||||
root.setParent(rootParent);
|
||||
this.model = new GTreeModel(root);
|
||||
worker.setBusyListener(this);
|
||||
init();
|
||||
|
@ -115,7 +121,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
(windowManager, provider) -> filterProvider.loadFilterPreference(windowManager,
|
||||
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 TaskMonitorAdapter.DUMMY_MONITOR;
|
||||
return TaskMonitor.DUMMY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -166,6 +172,14 @@ public class GTree extends JPanel implements BusyListener {
|
|||
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) {
|
||||
this.dragNDropHandler = dragNDropHandler;
|
||||
new GTreeDragNDropAdapter(this, tree, dragNDropHandler);
|
||||
|
@ -247,7 +261,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
public void dispose() {
|
||||
filterUpdateManager.dispose();
|
||||
worker.dispose();
|
||||
GTreeRootNode root = model.getModelRoot();
|
||||
GTreeNode root = model.getModelRoot();
|
||||
if (root != null) {
|
||||
root.dispose();
|
||||
}
|
||||
|
@ -272,7 +286,6 @@ public class GTree extends JPanel implements BusyListener {
|
|||
|
||||
protected void updateModelFilter() {
|
||||
filter = filterProvider.getFilter();
|
||||
|
||||
modificationID.incrementAndGet();
|
||||
|
||||
if (lastFilterTask != null) {
|
||||
|
@ -280,7 +293,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
lastFilterTask.cancel();
|
||||
}
|
||||
|
||||
lastFilterTask = new GTreeFilterTask(this, getRootNode(), filter);
|
||||
lastFilterTask = new GTreeFilterTask(this, filter);
|
||||
|
||||
if (isFilteringEnabled()) {
|
||||
worker.schedule(lastFilterTask);
|
||||
|
@ -351,7 +364,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
}
|
||||
|
||||
public void expandAll() {
|
||||
runTask(new GTreeExpandAllTask(this, getRootNode()));
|
||||
runTask(new GTreeExpandAllTask(this, getViewRoot()));
|
||||
}
|
||||
|
||||
public void collapseAll(GTreeNode node) {
|
||||
|
@ -378,7 +391,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -492,7 +505,11 @@ public class GTree extends JPanel implements BusyListener {
|
|||
tree.setScrollableUnitIncrement(increment);
|
||||
}
|
||||
|
||||
protected GTreeModel getModel() {
|
||||
/**
|
||||
* Returns the model for this tree
|
||||
* @return the model for this tree
|
||||
*/
|
||||
public GTreeModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
|
@ -503,6 +520,10 @@ public class GTree extends JPanel implements BusyListener {
|
|||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current viewport position of the scrollable tree.
|
||||
* @return the current viewport position of the scrollable tree.
|
||||
*/
|
||||
public Point getViewPosition() {
|
||||
JViewport viewport = scrollPane.getViewport();
|
||||
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
|
||||
* been replaced by a new node that is equal, but a different instance.
|
||||
* 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. 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
|
||||
* @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) {
|
||||
if (path == null) {
|
||||
public GTreeNode getModelNodeForPath(TreePath path) {
|
||||
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;
|
||||
}
|
||||
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (path.getPathCount() == 1) {
|
||||
Object lastPathComponent = path.getLastPathComponent();
|
||||
GTreeNode rootNode = getRootNode();
|
||||
if (rootNode.equals(lastPathComponent)) {
|
||||
return rootNode;
|
||||
if (root.getId() == node.getId()) {
|
||||
return 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) {
|
||||
return null; // must be a path we don't have
|
||||
}
|
||||
|
||||
Object lastPathComponent = path.getLastPathComponent();
|
||||
GTreeNode lastPathComponent = (GTreeNode) path.getLastPathComponent();
|
||||
List<GTreeNode> children = parentNode.getChildren();
|
||||
for (GTreeNode child : children) {
|
||||
if (child.equals(lastPathComponent)) {
|
||||
if (child.getId() == lastPathComponent.getId()) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
@ -616,7 +658,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
isFilteringEnabled = enabled;
|
||||
setFilterFieldEnabled(enabled);
|
||||
validate();
|
||||
refilter();
|
||||
refilterNow();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -685,13 +727,20 @@ public class GTree extends JPanel implements BusyListener {
|
|||
*
|
||||
* @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();
|
||||
GTreeRootNode root = model.getModelRoot();
|
||||
root.dispose();
|
||||
GTreeNode root = model.getModelRoot();
|
||||
|
||||
this.realRootNode = rootNode;
|
||||
rootNode.setGTree(this);
|
||||
rootNode.setParent(rootParent);
|
||||
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
else {
|
||||
if (waitForJobs) {
|
||||
worker.waitUntilNoJobsScheduled(Integer.MAX_VALUE);
|
||||
}
|
||||
monitor.clearCanceled();
|
||||
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
|
||||
* constructor or from {@link #setRootNode(GTreeRootNode)}. There is a chance that the
|
||||
* root node being used by the GUI is an "In Progress" node that is a placeholder used while
|
||||
* this threaded tree is setting the root node.
|
||||
* @return
|
||||
* This method returns the root node that was provided to the tree by the client, whether from the
|
||||
* constructor or from {@link #setRootNode(GTreeNode)}.
|
||||
* This node represents the data model and always contains all the nodes regardless of any filter
|
||||
* being applied. If a filter is applied to the tree, then this is not the actual root node being
|
||||
* displayed by the {@link JTree}.
|
||||
* @return the root node as provided by the client.
|
||||
*/
|
||||
public GTreeRootNode getRootNode() {
|
||||
public GTreeNode getModelRoot() {
|
||||
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.
|
||||
* @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() {
|
||||
|
@ -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) {
|
||||
worker.schedule(task);
|
||||
}
|
||||
|
@ -1074,7 +1149,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
GTreeRootNode rootNode = getRootNode();
|
||||
GTreeNode rootNode = getModelRoot();
|
||||
if (rootNode == null) {
|
||||
return "GTree - no root node";
|
||||
}
|
||||
|
@ -1091,7 +1166,7 @@ public class GTree extends JPanel implements BusyListener {
|
|||
}
|
||||
|
||||
public void clearSizeCache() {
|
||||
recurseClearSizeCache(getRootNode());
|
||||
recurseClearSizeCache(getViewRoot());
|
||||
}
|
||||
|
||||
private void recurseClearSizeCache(GTreeNode node) {
|
||||
|
@ -1369,4 +1444,5 @@ public class GTree extends JPanel implements BusyListener {
|
|||
|
||||
return stackTrace[creatorIndex].getClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,23 +13,22 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.tasks.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class GTreeFilterTask extends GTreeTask {
|
||||
|
||||
private final GTreeNode node;
|
||||
private final GTreeFilter filter;
|
||||
private final GTreeState defaultRestoreState;
|
||||
private boolean cancelledProgramatically;
|
||||
|
||||
public GTreeFilterTask(GTree tree, GTreeNode node, GTreeFilter filter) {
|
||||
public GTreeFilterTask(GTree tree, GTreeFilter filter) {
|
||||
super(tree);
|
||||
this.node = node;
|
||||
this.filter = filter;
|
||||
|
||||
// save this now, before we modify the tree
|
||||
|
@ -39,31 +38,44 @@ public class GTreeFilterTask extends GTreeTask {
|
|||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
if (filter == null) {
|
||||
node.clearFilter();
|
||||
tree.restoreNonFilteredRootNode();
|
||||
restoreInSameTask(monitor);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor.setMessage("Filtering...");
|
||||
monitor.initialize(1000000000);
|
||||
|
||||
GTreeNode root = tree.getModelRoot();
|
||||
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()) {
|
||||
expandInSameTask(monitor);
|
||||
expandInSameTask(monitor, filtered);
|
||||
restoreInSameTask(monitor);
|
||||
}
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
Msg.error(this, "Got Unexpected CloneNotSupportedException", e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
if (!cancelledProgramatically) {
|
||||
tree.runTask(new GTreeClearTreeFilterTask(tree));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
tree.setEventsEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void expandInSameTask(TaskMonitor monitor) {
|
||||
GTreeExpandAllTask expandTask = new GTreeExpandAllTask(tree, node);
|
||||
private void expandInSameTask(TaskMonitor monitor, GTreeNode filtered) {
|
||||
GTreeExpandAllTask expandTask = new GTreeExpandAllTask(tree, filtered);
|
||||
expandTask.run(monitor);
|
||||
}
|
||||
|
|
@ -15,53 +15,67 @@
|
|||
*/
|
||||
package docking.widgets.tree;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for GTNodes that want to use a lazy loading approach. By using lazy
|
||||
* nodes, you don't have to create all the nodes up front and the nodes will only
|
||||
* 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().
|
||||
* The generateChildren() method will be called automatically when needed.
|
||||
* Base class for GTreeNodes that populate their children on demand (typically when expanded).
|
||||
* Also, children of this node can be unloaded by calling {@link #unloadChildren()}. This
|
||||
* can be used by nodes in large trees to save memory by unloading children that are no longer
|
||||
* in the current tree view (collapsed). Of course, that decision would need to be balanced
|
||||
* against the extra time to reload the nodes in the event that a filter is applied.
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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
|
||||
protected final void loadChildren() {
|
||||
if (isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
public void addNode(GTreeNode node) {
|
||||
if (isLoaded()) {
|
||||
super.addNode(node);
|
||||
}
|
||||
List<GTreeNode> generateChildren = generateChildren();
|
||||
if (isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
}
|
||||
doSetChildren(generateChildren, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(int index, GTreeNode node) {
|
||||
if (!isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
}
|
||||
if (isLoaded()) {
|
||||
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
|
||||
return Collections.emptyList();
|
||||
@Override
|
||||
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;
|
||||
|
||||
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.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.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
|
||||
* @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.
|
||||
* @param expanded true if the node is expanded.
|
||||
* @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
|
||||
* hovers the mouse on this node in the tree.
|
||||
* @return the tooltip to be displayed.
|
||||
*/
|
||||
public String getToolTip();
|
||||
public abstract String getToolTip();
|
||||
|
||||
/**
|
||||
* Returns true if this node never has children.
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
* @param index the index to place the node.
|
||||
* @param node the node to add as a child of this node.
|
||||
*/
|
||||
public void addNode(int index, GTreeNode node);
|
||||
|
||||
/**
|
||||
* Returns the list of children including those that have been filtered out.
|
||||
* @return the list of all children of this node including those that are filtered out.
|
||||
*/
|
||||
public List<GTreeNode> getAllChildren();
|
||||
public void addNode(int index, GTreeNode node) {
|
||||
children().add(index, node);
|
||||
node.setParent(this);
|
||||
fireNodeAdded(this, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* nodes that are current filtered out.
|
||||
* @return the number of <b>visible</b> children of this node.
|
||||
*/
|
||||
public int getChildCount();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
public int getChildCount() {
|
||||
return children().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child node of this node with the given name.
|
||||
* @param name the name of the child to be returned.
|
||||
* @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
|
||||
|
@ -107,26 +153,46 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
|||
* @param index the index of the child to be returned.
|
||||
* @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
|
||||
* nodes return 1.
|
||||
* @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.
|
||||
* @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.
|
||||
* @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
|
||||
|
@ -134,59 +200,74 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
|
|||
* @param node whose index we want.
|
||||
* @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.
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
* @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.
|
||||
* @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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Applies the the given filter to the subtree of this node. Nodes will be
|
||||
* filtered out if the node and all of its descendants are not accepted by the filter. In
|
||||
* other words, a node will remain if it or any of its descendants are accepted by the filter.
|
||||
* @param filter the filter being applied.
|
||||
* @param monitor a TaskMonitor for tracking the progress and cancelling.
|
||||
* @param min the min value to use for the progress bar for this subtree.
|
||||
* @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();
|
||||
public boolean isAncestor(GTreeNode node) {
|
||||
GTreeNode nodeParent = node.getParent();
|
||||
while (nodeParent != null) {
|
||||
if (nodeParent.equals(this)) {
|
||||
return true;
|
||||
}
|
||||
nodeParent = nodeParent.getParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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
|
||||
|
@ -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.
|
||||
* @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
|
||||
* GTRootNode.
|
||||
* @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.
|
||||
* @return the GTTree that contains this node.
|
||||
* Returns true if this is a root 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.
|
||||
* @return true if this node is currently being modified.
|
||||
* Generates a filtered copy of this node and its children.
|
||||
* <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
|
||||
* @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.
|
||||
* @param parentNode the node that contains the node that was 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.
|
||||
* @return the parent of this node.
|
||||
* Returns a stream of the GTree nodes in the subtree 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;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
/**
|
||||
* Simple base class for GTRootNodes. If your root node has different internal logic
|
||||
* than other nodes, then extend this class. If you root node has the same internal
|
||||
* logic as other nodes, then it is probably better to extend your other node class and
|
||||
* implement the getGTree() and setGTree() methods yourself.
|
||||
*
|
||||
* Artificial node used by the GTree to set as a parent on the real root node of a GTree. It allows
|
||||
* nodes to access the GTree because it overrides getTree to return the GTree. This eliminates the
|
||||
* need for clients to create special root nodes that have getTree/setTree
|
||||
*/
|
||||
public abstract class AbstractGTreeRootNode extends AbstractGTreeNode implements GTreeRootNode {
|
||||
class GTreeRootParentNode extends GTreeNode {
|
||||
private GTree tree;
|
||||
|
||||
@Override
|
||||
public void setGTree(GTree tree) {
|
||||
GTreeRootParentNode(GTree tree) {
|
||||
this.tree = tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTree getGTree() {
|
||||
public GTree getTree() {
|
||||
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
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,120 +15,102 @@
|
|||
*/
|
||||
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 javax.swing.SwingUtilities;
|
||||
|
||||
import docking.widgets.tree.tasks.GTreeLoadChildrenTask;
|
||||
import docking.widgets.tree.internal.InProgressGTreeNode;
|
||||
import ghidra.util.Swing;
|
||||
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
|
||||
* be slow and therefor should be done in another thread. By using SlowLoadingNode
|
||||
* nodes, you don't have to create all the nodes up front and the nodes will only
|
||||
* 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.
|
||||
* Base class for nodes that generate their children on demand, but because generating their children
|
||||
* is slow, that operation is moved to a background thread. While the children are being generated,
|
||||
* an {@link InProgressGTreeNode} will appear in the tree until the {@link LoadChildrenTask} has completed.
|
||||
*/
|
||||
public abstract class GTreeSlowLoadingNode extends AbstractGTreeNode {
|
||||
public abstract List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException;
|
||||
public abstract class GTreeSlowLoadingNode extends GTreeLazyNode {
|
||||
|
||||
/**
|
||||
* 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
|
||||
protected final void loadChildren() {
|
||||
|
||||
protected final List<GTreeNode> generateChildren() {
|
||||
final GTree tree = getTree();
|
||||
if (SystemUtilities.isEventDispatchThread()) {
|
||||
if (isChildrenLoadedOrInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setInProgress(); // this will make isChildrenLoaded() return true for any subsequent calls.
|
||||
if (tree != null) {
|
||||
GTreeLoadChildrenTask loadTask = new GTreeLoadChildrenTask(tree, this);
|
||||
if (Swing.isSwingThread() && tree != null) {
|
||||
LoadChildrenTask loadTask = new LoadChildrenTask(tree);
|
||||
tree.runTask(loadTask);
|
||||
return;
|
||||
return CollectionUtils.asList(new InProgressGTreeNode());
|
||||
}
|
||||
return generateChildrenNow(getMonitor(tree));
|
||||
}
|
||||
|
||||
if (isChildrenLoadedOrInProgress() && !isInProgress()) {
|
||||
return; // fully loaded
|
||||
@Override
|
||||
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();
|
||||
doLoadChildren(tree, getMonitor(tree));
|
||||
private List<GTreeNode> generateChildrenNow(TaskMonitor monitor) {
|
||||
try {
|
||||
return generateChildren(monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private TaskMonitor getMonitor(GTree tree) {
|
||||
if (tree == null) {
|
||||
return TaskMonitorAdapter.DUMMY_MONITOR;
|
||||
return TaskMonitor.DUMMY;
|
||||
}
|
||||
return tree.getThreadLocalMonitor();
|
||||
}
|
||||
|
||||
private void doLoadChildren(final GTree tree, TaskMonitor monitor) {
|
||||
if (isChildrenLoaded()) {
|
||||
// Odd case where we have been told to load even though we are already loaded.
|
||||
// Probably in the middle of a filter job. Need to reset the active chi
|
||||
// in any case. Calling setChildren will effectively set the allChildren to its
|
||||
// current contents, but will also set the active children.
|
||||
setChildren(doGetAllChildren());
|
||||
private class LoadChildrenTask extends GTreeTask {
|
||||
|
||||
LoadChildrenTask(GTree tree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
if (isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
long progressValue = monitor.getProgress();
|
||||
long maxValue = monitor.getMaximum();
|
||||
monitor.setMessage("Loading children");
|
||||
try {
|
||||
setChildren(generateChildren(monitor));
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
SystemUtilities.runSwingNow(new Runnable() {
|
||||
if (!tree.isDisposed()) {
|
||||
runOnSwingThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (tree != null) {
|
||||
tree.collapseAll(tree.getRootNode());
|
||||
}
|
||||
tree.collapseAll(tree.getViewRoot());
|
||||
}
|
||||
});
|
||||
doSetChildren(null, true);
|
||||
}
|
||||
doSetChildren(null);
|
||||
}
|
||||
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.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;
|
||||
|
||||
public GTreeState(GTree tree) {
|
||||
this(tree, tree.getRootNode());
|
||||
this(tree, tree.getViewRoot());
|
||||
}
|
||||
|
||||
public GTreeState(GTree tree, GTreeNode node) {
|
||||
|
||||
this.tree = tree;
|
||||
expandedPaths = tree.getExpandedPaths(node);
|
||||
selectionPaths = getSelectionPaths(node);
|
||||
|
@ -166,7 +165,7 @@ public class GTreeState {
|
|||
|
||||
private List<TreePath> getSelectionPaths(GTreeNode node) {
|
||||
TreePath[] allSelectionPaths = tree.getSelectionPaths();
|
||||
if (node == tree.getRootNode()) {
|
||||
if (node == tree.getViewRoot()) {
|
||||
return CollectionUtils.asList(allSelectionPaths);
|
||||
}
|
||||
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
|
||||
// (we have seen errors where the tree will return nodes that are in the process
|
||||
// of being disposed)
|
||||
GTreeNode nodeForPath = SystemUtilities.runSwingNow(() -> tree.getNodeForPath(path));
|
||||
GTreeNode nodeForPath = SystemUtilities.runSwingNow(() -> tree.getViewNodeForPath(path));
|
||||
if (nodeForPath != null) {
|
||||
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++) {
|
||||
list.add((AbstractGTreeNode) selectionPaths[i].getLastPathComponent());
|
||||
list.add((GTreeNode) selectionPaths[i].getLastPathComponent());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,8 +15,6 @@
|
|||
*/
|
||||
package docking.widgets.tree.internal;
|
||||
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -27,13 +24,14 @@ import javax.swing.tree.TreeModel;
|
|||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeRootNode;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
public class GTreeModel implements TreeModel {
|
||||
|
||||
private GTreeRootNode root;
|
||||
private GTreeNode root;
|
||||
private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
|
||||
private boolean isFiringNodeStructureChanged;
|
||||
private volatile boolean eventsEnabled = true;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* avoid hanging the swing thread.
|
||||
*/
|
||||
public GTreeModel(GTreeRootNode root) {
|
||||
public GTreeModel(GTreeNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public void setRootNode(GTreeRootNode root) {
|
||||
public void setRootNode(GTreeNode root) {
|
||||
this.root = root;
|
||||
fireRootChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public GTreeRootNode getModelRoot() {
|
||||
public GTreeNode getModelRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTreeModelListener(TreeModelListener l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTreeModelListener(TreeModelListener l) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChild(Object parent, int index) {
|
||||
GTreeNode gTreeParent = (GTreeNode) parent;
|
||||
return gTreeParent.getChild(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildCount(Object parent) {
|
||||
GTreeNode gTreeParent = (GTreeNode) parent;
|
||||
return gTreeParent.getChildCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndexOfChild(Object parent, Object child) {
|
||||
GTreeNode gTreeParent = (GTreeNode) parent;
|
||||
return gTreeParent.getIndexOfChild((GTreeNode) child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeaf(Object node) {
|
||||
GTreeNode gTreeNode = (GTreeNode) node;
|
||||
return gTreeNode.isLeaf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueForPathChanged(TreePath path, Object newValue) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
node.valueChanged(newValue);
|
||||
}
|
||||
|
||||
public void fireNodeStructureChanged(final GTreeNode changedNode) {
|
||||
if (isFiringNodeStructureChanged) {
|
||||
if (!eventsEnabled || isFiringNodeStructureChanged) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isFiringNodeStructureChanged = true;
|
||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeStructuredChanged() must be "
|
||||
+ "called from the AWT thread");
|
||||
SystemUtilities.assertThisIsTheSwingThread(
|
||||
"GTreeModel.fireNodeStructuredChanged() must be " + "called from the AWT thread");
|
||||
|
||||
TreeModelEvent event = new TreeModelEvent(this, changedNode.getTreePath());
|
||||
for (TreeModelListener listener : listeners) {
|
||||
|
@ -112,7 +118,11 @@ public class GTreeModel implements TreeModel {
|
|||
}
|
||||
|
||||
public void fireRootChanged() {
|
||||
if (!eventsEnabled) {
|
||||
return;
|
||||
}
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GTreeNode rootNode = root;
|
||||
if (rootNode != null) {
|
||||
|
@ -123,8 +133,11 @@ public class GTreeModel implements TreeModel {
|
|||
}
|
||||
|
||||
public void fireNodeDataChanged(final GTreeNode parentNode, final GTreeNode changedNode) {
|
||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeChanged() must be "
|
||||
+ "called from the AWT thread");
|
||||
if (!eventsEnabled) {
|
||||
return;
|
||||
}
|
||||
SystemUtilities.assertThisIsTheSwingThread(
|
||||
"GTreeModel.fireNodeDataChanged() must be " + "called from the AWT thread");
|
||||
|
||||
TreeModelEvent event;
|
||||
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) {
|
||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeAdded() must be "
|
||||
+ "called from the AWT thread");
|
||||
if (!eventsEnabled) {
|
||||
return;
|
||||
}
|
||||
SystemUtilities.assertThisIsTheSwingThread(
|
||||
"GTreeModel.fireNodeAdded() must be " + "called from the AWT thread");
|
||||
|
||||
int indexInParent = newNode.getIndexInParent();
|
||||
if (indexInParent < 0) {
|
||||
|
@ -164,8 +180,8 @@ public class GTreeModel implements TreeModel {
|
|||
public void fireNodeRemoved(final GTreeNode parentNode, final GTreeNode removedNode,
|
||||
final int oldIndexInParent) {
|
||||
|
||||
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeRemoved() must be "
|
||||
+ "called from the AWT thread");
|
||||
SystemUtilities.assertThisIsTheSwingThread(
|
||||
"GTreeModel.fireNodeRemoved() must be " + "called from the AWT thread");
|
||||
|
||||
TreeModelEvent event =
|
||||
new TreeModelEvent(this, parentNode.getTreePath(), new int[] { oldIndexInParent },
|
||||
|
@ -178,4 +194,8 @@ public class GTreeModel implements TreeModel {
|
|||
public void dispose() {
|
||||
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