diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableModel.java index 266c6d310b..91ef7d4966 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableModel.java @@ -51,6 +51,11 @@ public class GTreeTableModel extends ThreadedTableMode throws IllegalArgumentException { return rowObject; } + + @Override + public Comparator getComparator() { + return Comparator.comparingInt(GTreeTableNode::getIndex); + } } private GTreeTableNode rootNode; @@ -68,7 +73,7 @@ public class GTreeTableModel extends ThreadedTableMode @Override protected TableColumnDescriptor createTableColumnDescriptor() { final TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); - descriptor.addVisibleColumn(new TreeColumn(treeColumnName())); + descriptor.addVisibleColumn(new TreeColumn(treeColumnName()), 0, true); return descriptor; } @@ -122,6 +127,7 @@ public class GTreeTableModel extends ThreadedTableMode protected void doLoad(Accumulator accumulator, TaskMonitor monitor) throws CancelledException { if (rootNode != null) { + rootNode.reindex(); for (final GTreeTableNode c : rootNode.expandedDescendants()) { monitor.checkCancelled(); if (c.isVisible()) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableNode.java index fdedf4f7b7..3b53facc1e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/gtreetable/GTreeTableNode.java @@ -15,13 +15,12 @@ */ package docking.widgets.gtreetable; +import javax.swing.*; import java.io.Serializable; import java.util.*; import java.util.function.Consumer; import java.util.function.Predicate; -import javax.swing.Icon; - public class GTreeTableNode implements Serializable { private class EachAncestorIterator implements Iterator { GTreeTableNode curNode = getParent(); @@ -53,7 +52,22 @@ public class GTreeTableNode implements Serializable { nodes.addAll(curNode.getChildren()); return curNode; } + } + private class EachDecendantDFSIterator implements Iterator { + final List nodes = new ArrayList<>(children); + + @Override + public boolean hasNext() { + return !nodes.isEmpty(); + } + + @Override + public GTreeTableNode next() { + final GTreeTableNode curNode = nodes.removeFirst(); + nodes.addAll(0, curNode.getChildren()); + return curNode; + } } private class EachExpandedIterator implements Iterator { @@ -68,11 +82,10 @@ public class GTreeTableNode implements Serializable { public GTreeTableNode next() { final GTreeTableNode curNode = nodes.removeFirst(); if (curNode.isExpanded()) { - nodes.addAll((curNode.getChildren())); + nodes.addAll(curNode.getChildren()); } return curNode; } - } protected final String name; @@ -85,6 +98,8 @@ public class GTreeTableNode implements Serializable { protected boolean visible; + private int index; + public GTreeTableNode(final String name) { this.name = name; expanded = false; @@ -92,6 +107,16 @@ public class GTreeTableNode implements Serializable { visible = true; } + /** + * Reindex the tree for sorting + */ + void reindex() { + int i = 0; + for (GTreeTableNode node : getRoot().descendantsDFS()) { + node.index = i++; + } + } + /** * Add new child to this node's children * @@ -146,6 +171,15 @@ public class GTreeTableNode implements Serializable { return EachDecendantIterator::new; } + /** + * Get an iterable of all the descendants of this node in DFS order + * + * @return An iterable of descendants + */ + public Iterable descendantsDFS() { + return EachDecendantDFSIterator::new; + } + /** * Get an iterable of all the expanded descendants of this node * @@ -209,6 +243,21 @@ public class GTreeTableNode implements Serializable { } } + /** + * Perform an action on each descendant of this node in DFS order + * + * @param action To perform on each descendant + */ + public void forEachDescendantDFS(final Consumer action) { + final List nodes = new ArrayList<>(children); + + while (!nodes.isEmpty()) { + final GTreeTableNode curNode = nodes.removeFirst(); + action.accept(curNode); + nodes.addAll(0, curNode.getChildren()); + } + } + /** * Perform an action on each expanded descendant of this node * @@ -454,6 +503,15 @@ public class GTreeTableNode implements Serializable { child.setParent(null); } + /** + * Get this node's index in the tree, for sorting purposes + * + * @return Index in tree + */ + int getIndex() { + return index; + } + /** * Change node expanded state *