mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 13:44:50 +08:00
GP-1615 making rename work with filters on in the gtree
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
+48
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+29
-30
@@ -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");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+5
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user