Merge remote-tracking branch

'origin/GP-3812-dragonmacher-symbol-tree-collapse-all' (Closes #5731)
This commit is contained in:
Ryan Kurtz 2023-09-26 13:01:01 -04:00
commit 1b4368d0fa
5 changed files with 227 additions and 24 deletions

View file

@ -351,7 +351,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
} }
}; };
goToSourceAction goToSourceAction
.setPopupMenuData(new MenuData(new String[] { "Go To Call Source" }, goToMenu)); .setPopupMenuData(new MenuData(new String[] { "Go To Call Source" }, goToMenu));
goToSourceAction.setHelpLocation( goToSourceAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Goto_Source")); new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Goto_Source"));
tool.addLocalAction(this, goToSourceAction); tool.addLocalAction(this, goToSourceAction);
@ -365,12 +365,11 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
doUpdate(); doUpdate();
} }
}; };
filterDuplicates filterDuplicates.setToolBarData(new ToolBarData(
.setToolBarData(new ToolBarData(new GIcon("icon.plugin.calltree.filter.duplicates"), new GIcon("icon.plugin.calltree.filter.duplicates"), filterOptionsToolbarGroup, "1"));
filterOptionsToolbarGroup, "1"));
filterDuplicates.setSelected(true); filterDuplicates.setSelected(true);
filterDuplicates filterDuplicates
.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Filter")); .setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Filter"));
tool.addLocalAction(this, filterDuplicates); tool.addLocalAction(this, filterDuplicates);
// //
@ -394,7 +393,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
"<br> will go. Example operations include <b>Expand All</b> and filtering"); "<br> will go. Example operations include <b>Expand All</b> and filtering");
recurseIcon = new NumberIcon(recurseDepth.get()); recurseIcon = new NumberIcon(recurseDepth.get());
recurseDepthAction recurseDepthAction
.setToolBarData(new ToolBarData(recurseIcon, filterOptionsToolbarGroup, "2")); .setToolBarData(new ToolBarData(recurseIcon, filterOptionsToolbarGroup, "2"));
recurseDepthAction.setHelpLocation( recurseDepthAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Action_Recurse_Depth")); new HelpLocation(plugin.getName(), "Call_Tree_Action_Recurse_Depth"));
@ -415,7 +414,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
navigationOutgoingAction.setToolBarData(new ToolBarData( navigationOutgoingAction.setToolBarData(new ToolBarData(
Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON, navigationOptionsToolbarGroup, "1")); Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON, navigationOptionsToolbarGroup, "1"));
navigationOutgoingAction navigationOutgoingAction
.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Navigation")); .setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Navigation"));
tool.addLocalAction(this, navigationOutgoingAction); tool.addLocalAction(this, navigationOutgoingAction);
// //
@ -596,7 +595,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
refreshAction.setDescription("<html>Push at any time to refresh the current trees.<br>" + refreshAction.setDescription("<html>Push at any time to refresh the current trees.<br>" +
"This is highlighted when the data <i>may</i> be stale.<br>"); "This is highlighted when the data <i>may</i> be stale.<br>");
refreshAction refreshAction
.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Refresh")); .setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Refresh"));
tool.addLocalAction(this, refreshAction); tool.addLocalAction(this, refreshAction);
// //
@ -812,7 +811,14 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
} }
private GTree createTree() { private GTree createTree() {
GTree tree = new GTree(new EmptyRootNode()); GTree tree = new GTree(new EmptyRootNode()) {
@Override
protected boolean supportsPopupActions() {
// The base tree adds collapse/ expand actions, which we already provide, so signal
// that we do not want those actions.
return false;
}
};
tree.setPaintHandlesForLeafNodes(false); tree.setPaintHandlesForLeafNodes(false);
// tree.setFilterVisible(false); // tree.setFilterVisible(false);
return tree; return tree;

View file

@ -119,6 +119,13 @@ public class DataTypeArchiveGTree extends GTree {
} }
} }
@Override
protected boolean supportsPopupActions() {
// The base tree adds collapse/ expand actions, which we already provide, so signal that we
// do not want those actions.
return false;
}
@Override @Override
public void dispose() { public void dispose() {
((ArchiveRootNode) getModelRoot()).dispose(); ((ArchiveRootNode) getModelRoot()).dispose();

View file

@ -19,6 +19,7 @@ import docking.Tool;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import docking.widgets.table.GTable; import docking.widgets.table.GTable;
import docking.widgets.tree.GTree;
/** /**
* A place used to hold {@link DockingActionIf}s that are meant to be used by components. Some * A place used to hold {@link DockingActionIf}s that are meant to be used by components. Some
@ -35,5 +36,7 @@ public class SharedActionRegistry {
*/ */
public static void installSharedActions(Tool tool, ToolActions toolActions) { public static void installSharedActions(Tool tool, ToolActions toolActions) {
GTable.createSharedActions(tool, toolActions, ToolConstants.SHARED_OWNER); GTable.createSharedActions(tool, toolActions, ToolConstants.SHARED_OWNER);
GTree.createSharedActions(tool, toolActions, ToolConstants.SHARED_OWNER);
} }
} }

View file

@ -15,13 +15,13 @@
*/ */
package docking.widgets.tree; package docking.widgets.tree;
import static docking.action.MenuData.*;
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.*; import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.*;
import static ghidra.util.SystemUtilities.*; import static ghidra.util.SystemUtilities.*;
import java.awt.*; import java.awt.*;
import java.awt.dnd.Autoscroll; import java.awt.dnd.Autoscroll;
import java.awt.event.MouseEvent; import java.awt.event.*;
import java.awt.event.MouseListener;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
@ -36,8 +36,10 @@ import javax.swing.tree.*;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import docking.DockingWindowManager; import docking.*;
import docking.action.*;
import docking.actions.KeyBindingUtils; import docking.actions.KeyBindingUtils;
import docking.actions.ToolActions;
import docking.widgets.JTreeMouseListenerDelegate; import docking.widgets.JTreeMouseListenerDelegate;
import docking.widgets.filter.FilterTextField; import docking.widgets.filter.FilterTextField;
import docking.widgets.table.AutoscrollAdapter; import docking.widgets.table.AutoscrollAdapter;
@ -51,6 +53,7 @@ import ghidra.util.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.*; import ghidra.util.task.*;
import ghidra.util.worker.PriorityWorker; import ghidra.util.worker.PriorityWorker;
import resources.Icons;
/** /**
* Class for creating a JTree that supports filtering, threading, and a progress bar. * Class for creating a JTree that supports filtering, threading, and a progress bar.
@ -1394,6 +1397,16 @@ public class GTree extends JPanel implements BusyListener {
node.fireNodeChanged(); node.fireNodeChanged();
} }
/**
* A method that subclasses can override to signal that they wish not to have this tree's
* built-in popup actions. Subclasses will almost never need to override this method.
*
* @return true if popup actions are supported
*/
protected boolean supportsPopupActions() {
return true;
}
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
@ -1440,11 +1453,15 @@ public class GTree extends JPanel implements BusyListener {
this.scrollableUnitIncrementOverride = increment; this.scrollableUnitIncrementOverride = increment;
} }
GTree getGTree() {
return GTree.this;
}
@Override @Override
public String getToolTipText(MouseEvent event) { public String getToolTipText(MouseEvent event) {
// Use the GTree's method so clients can override the behavior; provide the // Use the GTree's method so clients can override the behavior; provide the
// default method below so we they can get the default behavior when needed. // default method below so we they can get the default behavior when needed.
return GTree.this.getToolTipText(event); return getGTree().getToolTipText(event);
} }
public String getDefaultToolTipText(MouseEvent event) { public String getDefaultToolTipText(MouseEvent event) {
@ -1463,7 +1480,7 @@ public class GTree extends JPanel implements BusyListener {
@Override @Override
public boolean isPathEditable(TreePath path) { public boolean isPathEditable(TreePath path) {
return GTree.this.isPathEditable(path); return getGTree().isPathEditable(path);
} }
@Override @Override
@ -1568,10 +1585,173 @@ public class GTree extends JPanel implements BusyListener {
} }
} }
private abstract static class GTreeAction extends DockingAction
implements ComponentBasedDockingAction {
GTreeAction(String name, String owner) {
super(name, owner);
}
@Override
public boolean isAddToPopup(ActionContext context) {
if (!isEnabledForContext(context)) {
return false;
}
GTree gTree = getTree(context);
return gTree.supportsPopupActions();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return getTree(context) != null;
}
@Override
public boolean isValidComponentContext(ActionContext context) {
return getTree(context) != null;
}
protected GTree getTree(ActionContext context) {
Component c = context.getSourceComponent();
if (c instanceof GTree) {
return (GTree) c;
}
if (c instanceof AutoScrollTree) {
return ((AutoScrollTree) c).getGTree();
}
return null;
}
}
//================================================================================================== //==================================================================================================
// Static Methods // Static Methods
//================================================================================================== //==================================================================================================
public static void createSharedActions(Tool tool, ToolActions toolActions, String owner) {
String actionMenuGroup = "zzzTreeGroup";
tool.setMenuGroup(new String[] { "Collapse" }, actionMenuGroup, "1");
tool.setMenuGroup(new String[] { "Expand" }, actionMenuGroup, "2");
int subGroupIndex = 1; // order by insertion
GTreeAction collapseAction = new GTreeAction("Tree Collapse Node", owner) {
@Override
public void actionPerformed(ActionContext context) {
GTree gTree = getTree(context);
List<GTreeNode> nodes = gTree.getSelectedNodes();
for (GTreeNode node : nodes) {
gTree.collapseAll(node);
}
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!super.isEnabledForContext(context)) {
return false;
}
GTree gTree = getTree(context);
List<GTreeNode> nodes = gTree.getSelectedNodes();
return !nodes.isEmpty();
}
};
//@formatter:off
collapseAction.setPopupMenuData(new MenuData(
new String[] { "Collapse" },
Icons.COLLAPSE_ALL_ICON,
actionMenuGroup, NO_MNEMONIC,
Integer.toString(subGroupIndex++)
)
);
collapseAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK));
collapseAction.setHelpLocation(new HelpLocation("Trees", "Collapse"));
//@formatter:on
GTreeAction expandAction = new GTreeAction("Tree Expand Node", owner) {
@Override
public void actionPerformed(ActionContext context) {
GTree gTree = getTree(context);
List<GTreeNode> nodes = gTree.getSelectedNodes();
for (GTreeNode node : nodes) {
gTree.expandTree(node);
}
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!super.isEnabledForContext(context)) {
return false;
}
GTree gTree = getTree(context);
List<GTreeNode> nodes = gTree.getSelectedNodes();
return !nodes.isEmpty();
}
};
//@formatter:off
expandAction.setPopupMenuData(new MenuData(
new String[] { "Exapnd" },
Icons.EXPAND_ALL_ICON,
actionMenuGroup, NO_MNEMONIC,
Integer.toString(subGroupIndex++)
)
);
expandAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK));
expandAction.setHelpLocation(new HelpLocation("Trees", "Expand"));
//@formatter:on
GTreeAction collapseTreeAction = new GTreeAction("Tree Collapse All", owner) {
@Override
public void actionPerformed(ActionContext context) {
GTree gTree = getTree(context);
GTreeNode root = gTree.getViewRoot();
gTree.collapseAll(root);
}
};
//@formatter:off
collapseTreeAction.setPopupMenuData(new MenuData(
new String[] { "Collapse Tree" },
null,
actionMenuGroup, NO_MNEMONIC,
Integer.toString(subGroupIndex++)
)
);
collapseTreeAction.setHelpLocation(new HelpLocation("Trees", "Collapse_Tree"));
//@formatter:on
GTreeAction expandTreeAction = new GTreeAction("Tree Expand All", owner) {
@Override
public void actionPerformed(ActionContext context) {
GTree gTree = getTree(context);
gTree.expandAll();
}
};
//@formatter:off
expandTreeAction.setPopupMenuData(new MenuData(
new String[] { "Expand Tree" },
null,
actionMenuGroup, NO_MNEMONIC,
Integer.toString(subGroupIndex++)
)
);
expandTreeAction.setHelpLocation(new HelpLocation("Trees", "Expand_Tree"));
//@formatter:on
// these actions are self-explanatory and do need help
collapseAction.markHelpUnnecessary();
expandAction.markHelpUnnecessary();
collapseTreeAction.markHelpUnnecessary();
expandTreeAction.markHelpUnnecessary();
toolActions.addGlobalAction(collapseAction);
toolActions.addGlobalAction(expandAction);
toolActions.addGlobalAction(collapseTreeAction);
toolActions.addGlobalAction(expandTreeAction);
}
private static String generateFilterPreferenceKey() { private static String generateFilterPreferenceKey() {
Throwable throwable = new Throwable(); Throwable throwable = new Throwable();
StackTraceElement[] stackTrace = throwable.getStackTrace(); StackTraceElement[] stackTrace = throwable.getStackTrace();

View file

@ -63,6 +63,13 @@ public class DataTree extends GTree {
KeyStroke.getKeyStroke(KeyEvent.VK_X, DockingUtils.CONTROL_KEY_MODIFIER_MASK)); KeyStroke.getKeyStroke(KeyEvent.VK_X, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
} }
@Override
protected boolean supportsPopupActions() {
// The base tree adds collapse/ expand actions, which we already provide, so signal that we
// do not want those actions.
return false;
}
void setProjectActive(boolean isActive) { void setProjectActive(boolean isActive) {
if (dragNDropHandler != null) { if (dragNDropHandler != null) {
dragNDropHandler.setProjectActive(isActive); dragNDropHandler.setProjectActive(isActive);