GP-1615 making rename work with filters on in the gtree

This commit is contained in:
ghidravore
2021-12-16 17:50:26 -05:00
committed by dragonmacher
parent b4ca96e5d9
commit 2a55632030
5 changed files with 107 additions and 35 deletions
@@ -107,6 +107,7 @@ public class GTree extends JPanel implements BusyListener {
private GTreeFilter filter;
private GTreeFilterProvider filterProvider;
private SwingUpdateManager filterUpdateManager;
private Set<GTreeNode> ignoredNodes = new HashSet<>();
/**
* Creates a GTree with the given root node. The created GTree will use a threaded model
@@ -241,6 +242,7 @@ public class GTree extends JPanel implements BusyListener {
}
public void dispose() {
ignoredNodes.clear();
filterUpdateManager.dispose();
worker.dispose();
@@ -269,11 +271,23 @@ public class GTree extends JPanel implements BusyListener {
}
public void filterChanged() {
ignoredNodes.clear();
updateModelFilter();
}
public void ignoreFilter(GTreeNode node) {
ignoredNodes.add(node);
updateModelFilter();
}
protected void updateModelFilter() {
filter = filterProvider.getFilter();
if (!ignoredNodes.isEmpty()) {
filter = new IgnoredNodesGtreeFilter(filter, ignoredNodes);
}
// TODO why?
filterUpdateManager.stop();
if (lastFilterTask != null) {
// it is safe to repeatedly call cancel
@@ -964,10 +978,16 @@ public class GTree extends JPanel implements BusyListener {
// the Swing thread. To deal with this, we use a construct that will run our request
// once the given node has been added to the parent.
//
BooleanSupplier isReady = () -> parent.getChild(childName) != null;
GTreeNode modelParent = getModelNodeForPath(parent.getTreePath());
BooleanSupplier isReady = () -> modelParent.getChild(childName) != null;
int expireMs = 3000;
ExpiringSwingTimer.runWhen(isReady, expireMs, () -> {
runTask(new GTreeStartEditingTask(GTree.this, tree, parent, childName));
if (isFiltered()) {
GTreeNode child = modelParent.getChild(childName);
ignoreFilter(child);
updateModelFilter();
}
runTask(new GTreeStartEditingTask(GTree.this, tree, modelParent, childName));
});
}
@@ -18,6 +18,7 @@ package docking.widgets.tree;
import javax.swing.JTree;
import javax.swing.tree.TreePath;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.task.TaskMonitor;
import ghidra.util.worker.PriorityJob;
@@ -61,7 +62,9 @@ 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)
Msg.debug(this, "before translate: " + path);
GTreeNode nodeForPath = SystemUtilities.runSwingNow(() -> tree.getViewNodeForPath(path));
Msg.debug(this, "After translate, node = " + nodeForPath);
if (nodeForPath != null) {
return nodeForPath.getTreePath();
}
@@ -0,0 +1,48 @@
/* ###
* 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.support;
import java.util.Set;
import docking.widgets.tree.GTreeNode;
/**
* GTreeFilter that allows for some nodes that are never filtered out.
*/
public class IgnoredNodesGtreeFilter implements GTreeFilter {
private GTreeFilter filter;
private Set<GTreeNode> ignoredNodes;
public IgnoredNodesGtreeFilter(GTreeFilter filter, Set<GTreeNode> ignoredNodes) {
this.filter = filter;
this.ignoredNodes = ignoredNodes;
}
@Override
public boolean acceptsNode(GTreeNode node) {
if (ignoredNodes.contains(node)) {
return true;
}
return filter.acceptsNode(node);
}
@Override
public boolean showFilterMatches() {
return filter.showFilterMatches();
}
}
@@ -15,8 +15,7 @@
*/
package docking.widgets.tree.tasks;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import javax.swing.CellEditor;
import javax.swing.JTree;
@@ -30,23 +29,22 @@ import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
public class GTreeStartEditingTask extends GTreeTask {
private final GTreeNode parent;
private final GTreeNode modelParent;
private final String childName;
private GTreeNode editNode;
public GTreeStartEditingTask(GTree gTree, JTree jTree, GTreeNode parent, String childName) {
super(gTree);
this.parent = parent;
this.modelParent = parent;
this.childName = childName;
}
public GTreeStartEditingTask(GTree gTree, JTree jTree, GTreeNode editNode) {
super(gTree);
this.parent = editNode.getParent();
this.modelParent = tree.getModelNodeForPath(editNode.getParent().getTreePath());
this.childName = editNode.getName();
this.editNode = editNode;
}
@@ -67,17 +65,16 @@ public class GTreeStartEditingTask extends GTreeTask {
}
private void edit() {
GTreeNode viewParent = tree.getViewNodeForPath(modelParent.getTreePath());
if (editNode == null) {
editNode = parent.getChild(childName);
editNode = viewParent.getChild(childName);
if (editNode == null) {
Msg.debug(this, "Can't find node \"" + childName + "\" to edit.");
return;
}
}
TreePath path = editNode.getTreePath();
final Set<GTreeNode> childrenBeforeEdit = new HashSet<>(parent.getChildren());
final Set<GTreeNode> childrenBeforeEdit = new HashSet<>(viewParent.getChildren());
final CellEditor cellEditor = tree.getCellEditor();
cellEditor.addCellEditorListener(new CellEditorListener() {
@@ -89,8 +86,10 @@ public class GTreeStartEditingTask extends GTreeTask {
@Override
public void editingStopped(ChangeEvent e) {
String newName = Objects.toString(cellEditor.getCellEditorValue());
cellEditor.removeCellEditorListener(this);
SystemUtilities.runSwingLater(this::reselectNodeHandlingPotentialChildChange);
SystemUtilities
.runSwingLater(() -> reselectNodeHandlingPotentialChildChange(newName));
}
/**
@@ -103,12 +102,12 @@ public class GTreeStartEditingTask extends GTreeTask {
*/
private void reselectNode() {
String newName = editNode.getName();
GTreeNode newChild = parent.getChild(newName);
if (newChild == null) {
GTreeNode newModelChild = modelParent.getChild(newName);
if (newModelChild == null) {
throw new AssertException("Unable to find new node by name: " + newName);
}
tree.setSelectedNode(newChild);
tree.setSelectedNode(tree.getViewNodeForPath(newModelChild.getTreePath()));
}
/**
@@ -125,25 +124,25 @@ public class GTreeStartEditingTask extends GTreeTask {
* the state of the edited node's parent, both before and after
* the edit.
*/
private void reselectNodeHandlingPotentialChildChange() {
SystemUtilities.runSwingLater(this::doReselectNodeHandlingPotentialChildChange);
private void reselectNodeHandlingPotentialChildChange(String newName) {
SystemUtilities
.runSwingLater(() -> doReselectNodeHandlingPotentialChildChange(newName));
}
private void doReselectNodeHandlingPotentialChildChange() {
Set<GTreeNode> childrenAfterEdit = new HashSet<>(parent.getChildren());
if (childrenAfterEdit.equals(childrenBeforeEdit)) {
reselectNode(); // default re-select--the original child is still there
return;
}
private void doReselectNodeHandlingPotentialChildChange(String newName) {
GTreeNode newModelChild = modelParent.getChild(newName);
List<GTreeNode> children = modelParent.getChildren();
Msg.debug(this, children.toString());
// we have to figure out the new node to select
childrenAfterEdit.removeAll(childrenBeforeEdit);
if (childrenAfterEdit.size() != 1) {
return; // no way for us to figure out the correct child to edit
}
if (newModelChild != null) {
tree.ignoreFilter(newModelChild);
GTreeNode newChild = CollectionUtils.any(childrenAfterEdit);
tree.setSelectedNode(newChild);
tree.setSelectedNode(newModelChild);
Msg.debug(this, "new child not null");
}
else {
Msg.debug(this, "child is null");
}
}
});
@@ -33,7 +33,8 @@ import ghidra.util.Swing;
import ghidra.util.exception.AssertException;
import resources.ResourceManager;
public class ProjectDataNewFolderAction<T extends ProjectTreeContext> extends ContextSpecificAction<T> {
public class ProjectDataNewFolderAction<T extends ProjectTreeContext>
extends ContextSpecificAction<T> {
private static Icon icon = ResourceManager.loadImage("images/folder_add.png");
@@ -66,8 +67,9 @@ public class ProjectDataNewFolderAction<T extends ProjectTreeContext> extends Co
Swing.runLater(() -> {
GTreeNode node = findNodeForFolder(tree, newFolder);
if (node != null) {
tree.ignoreFilter(node);
tree.setEditable(true);
tree.startEditing(node.getParent(), node.getName());
tree.startEditing(node);
}
});
@@ -79,7 +81,7 @@ public class ProjectDataNewFolderAction<T extends ProjectTreeContext> extends Co
return parentFolder.createFolder(name);
}
catch (InvalidNameException | IOException e) {
throw new AssertException("Unexpected Error creating new folder: "+name, e);
throw new AssertException("Unexpected Error creating new folder: " + name, e);
}
}