Fixed open program dialog to not repeatedly load the root node

This commit is contained in:
dragonmacher 2025-02-25 09:14:56 -05:00
parent 2eff37f655
commit 1d5da6dae1
9 changed files with 268 additions and 165 deletions

View file

@ -4,9 +4,9 @@
* 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.
@ -318,7 +318,7 @@ public class FrontEndPlugin extends Plugin
r.run();
}
else {
SwingUtilities.invokeLater(r);
Swing.runLater(r);
}
}
}
@ -415,12 +415,12 @@ public class FrontEndPlugin extends Plugin
@Override
public void viewedProjectAdded(URL projectView) {
SwingUtilities.invokeLater(() -> rebuildRecentMenus());
Swing.runLater(() -> rebuildRecentMenus());
}
@Override
public void viewedProjectRemoved(URL projectView) {
SwingUtilities.invokeLater(() -> rebuildRecentMenus());
Swing.runLater(() -> rebuildRecentMenus());
}
/**
@ -536,7 +536,7 @@ public class FrontEndPlugin extends Plugin
void selectFiles(final Set<DomainFile> files) {
// Do this later in case any of the given files are newly created, which means that the
// GUIs may have not yet been notified.
SwingUtilities.invokeLater(() -> {
Swing.runLater(() -> {
// there was a delete bug; make the set unmodifiable to catch this earlier
Set<DomainFile> unmodifiableFiles = Collections.unmodifiableSet(files);
if (dataTablePanel.isCapacityExceeded()) {
@ -552,7 +552,7 @@ public class FrontEndPlugin extends Plugin
void selectFolder(final DomainFolder folder) {
// Do this later in case any of the given files are newly created, which means that the
// GUIs may have not yet been notified.
SwingUtilities.invokeLater(() -> {
Swing.runLater(() -> {
projectDataPanel.showTree();
dataTreePanel.selectDomainFolder(folder);
});

View file

@ -4,9 +4,9 @@
* 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.
@ -20,16 +20,18 @@ import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.GTreeLazyNode;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeSlowLoadingNode;
import ghidra.framework.model.*;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import resources.ResourceManager;
/**
* Class to represent a node in the Data tree.
*/
public class DomainFolderNode extends GTreeLazyNode implements Cuttable {
public class DomainFolderNode extends GTreeSlowLoadingNode implements Cuttable {
private static final Icon ENABLED_OPEN_FOLDER = DomainFolder.OPEN_FOLDER_ICON;
private static final Icon ENABLED_CLOSED_FOLDER = DomainFolder.CLOSED_FOLDER_ICON;
@ -127,30 +129,33 @@ public class DomainFolderNode extends GTreeLazyNode implements Cuttable {
}
@Override
protected List<GTreeNode> generateChildren() {
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
List<GTreeNode> children = new ArrayList<>();
if (domainFolder != null && !domainFolder.isEmpty()) {
if (domainFolder == null || domainFolder.isEmpty()) {
return children;
}
// NOTE: isEmpty() is used to avoid multiple failed connection attempts on this folder
// NOTE: isEmpty() is used to avoid multiple failed connection attempts on this folder
DomainFolder[] folders = domainFolder.getFolders();
for (DomainFolder folder : folders) {
children.add(new DomainFolderNode(folder, filter));
DomainFolder[] folders = domainFolder.getFolders();
for (DomainFolder folder : folders) {
monitor.checkCancelled();
children.add(new DomainFolderNode(folder, filter));
}
DomainFile[] files = domainFolder.getFiles();
for (DomainFile domainFile : files) {
monitor.checkCancelled();
if (domainFile.isLinkFile() && filter != null && filter.followLinkedFolders()) {
DomainFolder folder = domainFile.followLink();
if (folder != null) {
children.add(new DomainFolderNode(folder, filter));
continue;
}
}
DomainFile[] files = domainFolder.getFiles();
for (DomainFile domainFile : files) {
if (domainFile.isLinkFile() && filter != null && filter.followLinkedFolders()) {
DomainFolder folder = domainFile.followLink();
if (folder != null) {
children.add(new DomainFolderNode(folder, filter));
continue;
}
}
if (filter == null || filter.accept(domainFile)) {
children.add(new DomainFileNode(domainFile));
}
if (filter == null || filter.accept(domainFile)) {
children.add(new DomainFileNode(domainFile));
}
}
Collections.sort(children);

View file

@ -4,9 +4,9 @@
* 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.
@ -27,13 +27,15 @@ import javax.swing.tree.TreeSelectionModel;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.*;
import docking.widgets.tree.support.GTreeSelectionListener;
import ghidra.framework.main.FrontEndPlugin;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import help.Help;
import help.HelpService;
@ -100,6 +102,10 @@ public class ProjectDataTreePanel extends JPanel {
* @param projectData data that has the root folder for the project
*/
public void setProjectData(String projectName, ProjectData projectData) {
if (this.projectData == projectData) {
return; // this can happen during setup if listeners get activated
}
if (this.projectData != null) {
this.projectData.removeDomainFolderChangeListener(changeMgr);
}
@ -138,19 +144,6 @@ public class ProjectDataTreePanel extends JPanel {
oldRoot.removeAll();
}
/**
* Select the root data folder (not root node in the tree which
* shows the project name).
*/
public void selectRootDataFolder() {
tree.setSelectionPath(root.getTreePath());
}
public void selectDomainFolder(DomainFolder domainFolder) {
TreePath treePath = getTreePath(domainFolder);
tree.setSelectionPath(treePath);
}
private List<TreePath> getTreePaths(Set<DomainFile> files) {
List<TreePath> results = new ArrayList<>();
for (DomainFile file : files) {
@ -177,24 +170,25 @@ public class ProjectDataTreePanel extends JPanel {
}
/**
* Select the root data folder (not root node in the tree which shows the project name).
*/
public void selectRootDataFolder() {
tree.setSelectionPath(root.getTreePath());
}
public void selectDomainFolder(DomainFolder domainFolder) {
TreePath treePath = getTreePath(domainFolder);
tree.expandAndSelectPaths(List.of(treePath));
}
public void selectDomainFiles(Set<DomainFile> files) {
List<TreePath> treePaths = getTreePaths(files);
tree.setSelectionPaths(treePaths);
tree.expandAndSelectPaths(treePaths);
}
public void selectDomainFile(DomainFile domainFile) {
Iterator<GTreeNode> it = root.iterator(true);
while (it.hasNext()) {
GTreeNode child = it.next();
if (child instanceof DomainFileNode) {
DomainFile nodeFile = ((DomainFileNode) child).getDomainFile();
if (nodeFile.equals(domainFile)) {
tree.expandPath(child);
tree.setSelectedNode(child);
return;
}
}
}
selectDomainFiles(Set.of(domainFile));
}
public void setHelpLocation(HelpLocation helpLocation) {
@ -487,11 +481,34 @@ public class ProjectDataTreePanel extends JPanel {
* @param s node name
*/
public void findAndSelect(String s) {
if (projectData.getFileCount() < MAX_PROJECT_SIZE_TO_SEARCH) {
tree.expandTree(root);
FindAndSelectTask task = new FindAndSelectTask(tree, s);
tree.runTask(task);
}
//==================================================================================================
// Inner Classes
//==================================================================================================
private class FindAndSelectTask extends GTreeTask {
private String text;
FindAndSelectTask(GTree gTree, String text) {
super(gTree);
this.text = text;
}
@Override
public void run(TaskMonitor monitor) throws CancelledException {
if (projectData.getFileCount() > MAX_PROJECT_SIZE_TO_SEARCH) {
return;
}
for (Iterator<GTreeNode> it = root.iterator(true); it.hasNext();) {
monitor.checkCancelled();
GTreeNode node = it.next();
if (node.getName().equals(s)) {
if (node.getName().equals(text)) {
tree.setSelectedNode(node);
return;
}
@ -499,10 +516,6 @@ public class ProjectDataTreePanel extends JPanel {
}
}
//==================================================================================================
// Inner Classes
//==================================================================================================
private class MyMouseListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {