private void createActions() {

		String expandMenu = Integer.toString(1);
		String selectionMenuGroup = Integer.toString(2);
		String goToMenu = Integer.toString(3);
		String newTreeMenu = Integer.toString(4);

		String homeToolbarGroup = Integer.toString(1);
		String filterOptionsToolbarGroup = Integer.toString(2);
		String navigationOptionsToolbarGroup = Integer.toString(3);

		//
		// fully expand
		//
		DockingAction fullyExpandNodesAction =
			new DockingAction(EXPAND_ACTION_NAME, plugin.getName()) {

				@Override
				public void actionPerformed(ActionContext context) {
					Object contextObject = context.getContextObject();
					GTree gTree = (GTree) contextObject;
					TreePath[] paths = gTree.getSelectionPaths();
					for (TreePath treePath : paths) {
						GTreeNode node = (GTreeNode) treePath.getLastPathComponent();
						int recurseDepth = callTreeOptions.getRecurseDepth();
						gTree.runTask(new ExpandToDepthTask(gTree, node, recurseDepth));
					}
				}

				@Override
				public boolean isEnabledForContext(ActionContext context) {
					Object contextObject = context.getContextObject();
					if (!(contextObject == outgoingTree || contextObject == incomingTree)) {
						return false;
					}
					return true;
				}

				@Override
				public boolean isAddToPopup(ActionContext context) {
					Object contextObject = context.getContextObject();
					if (!(contextObject == outgoingTree || contextObject == incomingTree)) {
						return false;
					}
					return true;
				}
			};
		fullyExpandNodesAction.setPopupMenuData(
			new MenuData(new String[] { "Expand Nodes to Depth Limit" }, EXPAND_ICON, expandMenu));
		fullyExpandNodesAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Expand_Nodes"));
		tool.addLocalAction(this, fullyExpandNodesAction);

		//
		// Collapse All
		//
		DockingAction collapseAllNodesAction = new DockingAction("Collapse All", plugin.getName()) {

			@Override
			public void actionPerformed(ActionContext context) {
				Object contextObject = context.getContextObject();
				GTree gTree = (GTree) contextObject;
				GTreeNode rootNode = gTree.getViewRoot();
				List<GTreeNode> children = rootNode.getChildren();
				for (GTreeNode child : children) {
					gTree.collapseAll(child);
				}
			}

			@Override
			public boolean isEnabledForContext(ActionContext context) {
				Object contextObject = context.getContextObject();
				if (!(contextObject == outgoingTree || contextObject == incomingTree)) {
					return false;
				}

				GTree gTree = (GTree) contextObject;
				if (gTree.getSelectionPaths().length != 1) {
					return false;
				}

				TreePath path = gTree.getSelectionPath();
				if (path == null) {
					return false;
				}

				GTreeNode node = (GTreeNode) path.getLastPathComponent();
				return (node instanceof CallNode);
			}

			@Override
			public boolean isAddToPopup(ActionContext context) {
				Object contextObject = context.getContextObject();
				if (!(contextObject instanceof GTree)) {
					return false;
				}

				GTree gTree = (GTree) contextObject;
				TreePath[] selectionPaths = gTree.getSelectionPaths();
				if (selectionPaths.length == 0) {
					return false;
				}
				return true;
			}
		};

		collapseAllNodesAction.setPopupMenuData(
			new MenuData(new String[] { "Collapse All Nodes" }, COLLAPSE_ICON, expandMenu));
		collapseAllNodesAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Collapse_Nodes"));
		tool.addLocalAction(this, collapseAllNodesAction);

		//
		// goto
		//
		DockingAction goToDestinationAction =
			new DockingAction("Go To Destination", plugin.getName()) {

				@Override
				public void actionPerformed(ActionContext context) {
					Object contextObject = context.getContextObject();
					GTree gTree = (GTree) contextObject;
					TreePath path = gTree.getSelectionPath();
					CallNode node = (CallNode) path.getLastPathComponent();
					goTo(node.getLocation());
				}

				@Override
				public boolean isEnabledForContext(ActionContext context) {
					Object contextObject = context.getContextObject();
					if (!(contextObject == outgoingTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					if (gTree.getSelectionPaths().length != 1) {
						return false;
					}

					TreePath path = gTree.getSelectionPath();
					if (path == null) {
						return false;
					}

					GTreeNode node = (GTreeNode) path.getLastPathComponent();
					return (node instanceof CallNode);
				}

				@Override
				public boolean isAddToPopup(ActionContext context) {
					Object contextObject = context.getContextObject();
					if (!(contextObject instanceof GTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					if (selectionPaths.length == 0) {
						return false;
					}

					for (TreePath path : selectionPaths) {
						GTreeNode node = (GTreeNode) path.getLastPathComponent();
						if (node instanceof OutgoingCallsRootNode ||
							node instanceof IncomingCallsRootNode) {
							return false;
						}
					}
					return true;
				}
			};
		goToDestinationAction.setPopupMenuData(
			new MenuData(new String[] { "Go To Destination" }, goToMenu));
		goToDestinationAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Goto_Destination"));
		tool.addLocalAction(this, goToDestinationAction);

		DockingAction goToSourceAction = new DockingAction("Go To Source", plugin.getName()) {

			@Override
			public void actionPerformed(ActionContext context) {
				Object contextObject = context.getContextObject();
				GTree gTree = (GTree) contextObject;
				TreePath path = gTree.getSelectionPath();
				CallNode node = (CallNode) path.getLastPathComponent();
				goTo(new ProgramLocation(currentProgram, node.getSourceAddress()));
			}

			@Override
			public boolean isEnabledForContext(ActionContext context) {
				Object contextObject = context.getContextObject();
				if (!(contextObject instanceof GTree)) {
					return false;
				}

				GTree gTree = (GTree) contextObject;
				if (gTree.getSelectionPaths().length != 1) {
					return false;
				}

				TreePath path = gTree.getSelectionPath();
				if (path == null) {
					return false;
				}

				GTreeNode node = (GTreeNode) path.getLastPathComponent();
				return (node instanceof CallNode);
			}

			@Override
			public boolean isAddToPopup(ActionContext context) {
				Object contextObject = context.getContextObject();
				if (!(contextObject instanceof GTree)) {
					return false;
				}

				GTree gTree = (GTree) contextObject;
				TreePath[] selectionPaths = gTree.getSelectionPaths();
				if (selectionPaths.length == 0) {
					return false;
				}

				for (TreePath path : selectionPaths) {
					GTreeNode node = (GTreeNode) path.getLastPathComponent();
					if (node.isRoot()) {
						return false;
					}
				}
				return true;
			}
		};
		goToSourceAction
				.setPopupMenuData(new MenuData(new String[] { "Go To Source" }, goToMenu));
		goToSourceAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Goto_Source"));
		tool.addLocalAction(this, goToSourceAction);

		//
		// Unify Functions action
		//
		unifyFunctionsAction = new ToggleDockingAction("Unify Functions", plugin.getName()) {
			@Override
			public void actionPerformed(ActionContext context) {
				callTreeOptions = callTreeOptions.withFilterDuplicates(isSelected());
				doUpdate();
			}
		};
		unifyFunctionsAction.setToolBarData(new ToolBarData(
			new GIcon("icon.plugin.calltree.unify.functions"), filterOptionsToolbarGroup, "1"));
		unifyFunctionsAction.setSelected(true);
		unifyFunctionsAction
				.setDescription("<html>Filters the trees to only show one node per function,<br>" +
					"regardless of how many incoming or outgoing references exist at a given level");
		unifyFunctionsAction
				.setHelpLocation(
					new HelpLocation(plugin.getName(), "Call_Tree_Action_Unify_Functions"));
		tool.addLocalAction(this, unifyFunctionsAction);

		//
		// Filter Non-calls action
		//
		filterNonCallsAction = new ToggleDockingAction("Filter Non-calls", plugin.getName()) {
			@Override
			public void actionPerformed(ActionContext context) {
				callTreeOptions = callTreeOptions.withFilterReferences(isSelected());
				doUpdate();
			}
		};
		filterNonCallsAction.setToolBarData(new ToolBarData(
			new GIcon("icon.plugin.calltree.filter.non.calls"), filterOptionsToolbarGroup, "2"));
		filterNonCallsAction.setSelected(true);
		filterNonCallsAction
				.setDescription("Filters the trees to only show function call references");
		filterNonCallsAction
				.setHelpLocation(
					new HelpLocation(plugin.getName(), "Call_Tree_Action_Filter_Calls"));
		tool.addLocalAction(this, filterNonCallsAction);

		//
		// Recurse Depth action
		//
		recurseDepthAction = new DockingAction("Recurse Depth", plugin.getName()) {
			@Override
			public void actionPerformed(ActionContext context) {
				int recurseDepth = callTreeOptions.getRecurseDepth();
				NumberInputDialog dialog =
					new NumberInputDialog("", "", recurseDepth, 0, Integer.MAX_VALUE, false);
				if (!dialog.show()) {
					return;
				}

				int newValue = dialog.getValue();
				setRecurseDepth(newValue);
			}
		};
		recurseDepthAction.setDescription(
			"<html>Recurse Depth<br><br>Limits the depth to " + "which recursing tree operations" +
				"<br> will go.  Example operations include <b>Expand All</b> and filtering");

		int recurseDepth = callTreeOptions.getRecurseDepth();
		recurseIcon = new NumberIcon(recurseDepth);
		recurseDepthAction
				.setToolBarData(new ToolBarData(recurseIcon, filterOptionsToolbarGroup, "3"));
		recurseDepthAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Action_Recurse_Depth"));

		tool.addLocalAction(this, recurseDepthAction);

		//
		// Navigate outgoing nodes on selection action
		//
		navigationOutgoingAction =
			new ToggleDockingAction("Navigate Outgoing Nodes", plugin.getName()) {

				// nothing to do here on selection
			};
		navigationOutgoingAction.setSelected(true);
		navigationOutgoingAction.setDescription("<html>Outgoing Navigation<br><br>" +
			"Toggled <b>on</b> triggers node selections<br>in the tree to navigate the " +
			"Listing to<br>the <b>source</b> location of the call");
		navigationOutgoingAction.setToolBarData(new ToolBarData(
			Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON, navigationOptionsToolbarGroup, "1"));
		navigationOutgoingAction
				.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Navigation"));
		tool.addLocalAction(this, navigationOutgoingAction);

		//
		// navigate incoming nodes on selection
		//
		navigateIncomingAction =
			new ToggleDockingAction("Navigation Incoming Location Changes", plugin.getName()) {
				@Override
				public void actionPerformed(ActionContext context) {
					// handled later as we receive events
				}

				@Override
				public void setSelected(boolean newValue) {
					super.setSelected(newValue);

					if (isSelected()) {
						setLocation(plugin.getCurrentLocation());
					}
				}
			};

		// note: the default state is to follow navigation events for the primary provider;
		//       non-primary providers will function like snapshots of the function with
		//       which they were activated.
		navigateIncomingAction.setSelected(isPrimary);
		navigateIncomingAction.setToolBarData(new ToolBarData(
			Icons.NAVIGATE_ON_INCOMING_EVENT_ICON, navigationOptionsToolbarGroup, "2"));
		navigateIncomingAction.setDescription("<html>Incoming Navigation" +
			"<br><br>Toggle <b>On</b>  - change the displayed " +
			"function on Listing navigation events" +
			"<br>Toggled <b>Off</b> - don't change the displayed function on Listing navigation events");
		navigateIncomingAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Action_Incoming_Navigation"));
		tool.addLocalAction(this, navigateIncomingAction);

		//
		// selection actions
		//
		DockingAction selectSourceAction =
			new DockingAction("Select Call Source", plugin.getName()) {

				@Override
				public void actionPerformed(ActionContext context) {
					GTree gTree = (GTree) context.getContextObject();
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					makeSelectionFromPaths(selectionPaths, true);
				}

				@Override
				public boolean isEnabledForContext(ActionContext context) {
					if (currentFunction == null) {
						return false;
					}

					Object contextObject = context.getContextObject();
					if (!(contextObject instanceof GTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					return selectionPaths.length > 0;
				}

				@Override
				public boolean isAddToPopup(ActionContext context) {
					Object contextObject = context.getContextObject();
					if (!(contextObject instanceof GTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					if (selectionPaths.length == 0) {
						return false;
					}

					for (TreePath path : selectionPaths) {
						GTreeNode node = (GTreeNode) path.getLastPathComponent();
						if (node.isRoot()) {
							return false;
						}
					}
					return true;
				}
			};
		Icon icon = new GIcon("icon.plugin.calltree.filter.select.source");
		selectSourceAction.setPopupMenuData(
			new MenuData(new String[] { "Select Source" }, icon, selectionMenuGroup));
		selectSourceAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Select_Source"));
		tool.addLocalAction(this, selectSourceAction);

		DockingAction selectDestinationAction =
			new DockingAction("Select Call Destination", plugin.getName()) {

				@Override
				public void actionPerformed(ActionContext context) {
					GTree gTree = (GTree) context.getContextObject();
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					makeSelectionFromPaths(selectionPaths, false);
				}

				@Override
				public boolean isEnabledForContext(ActionContext context) {
					if (currentFunction == null) {
						return false;
					}

					Object contextObject = context.getContextObject();
					if (!(contextObject == outgoingTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					return selectionPaths.length > 0;
				}

				@Override
				public boolean isAddToPopup(ActionContext context) {
					Object contextObject = context.getContextObject();
					if (!(contextObject instanceof GTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					if (selectionPaths.length == 0) {
						return false;
					}

					for (TreePath path : selectionPaths) {
						GTreeNode node = (GTreeNode) path.getLastPathComponent();
						if (node.isRoot()) {
							return false;
						}
					}

					return isEnabledForContext(context);
				}
			};
		selectDestinationAction.setPopupMenuData(
			new MenuData(new String[] { "Select Destination" }, icon, selectionMenuGroup));
		selectDestinationAction.setHelpLocation(
			new HelpLocation(plugin.getName(), "Call_Tree_Context_Action_Select_Destination"));
		tool.addLocalAction(this, selectDestinationAction);

		//
		// Home action
		//
		DockingAction homeAction = new DockingAction("Home", plugin.getName()) {
			@Override
			public void actionPerformed(ActionContext context) {
				FunctionSignatureFieldLocation location = new FunctionSignatureFieldLocation(
					currentProgram, currentFunction.getEntryPoint());
				goTo(location);
			}

			@Override
			public boolean isEnabledForContext(ActionContext context) {
				return currentFunction != null;
			}
		};
		homeAction.setToolBarData(new ToolBarData(Icons.HOME_ICON, homeToolbarGroup));
		homeAction.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Home"));
		tool.addLocalAction(this, homeAction);

		refreshAction = new DockingAction("Refresh", plugin.getName()) {
			@Override
			public void actionPerformed(ActionContext context) {
				reloadUpdateManager.updateNow();
			}
		};
		refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON, homeToolbarGroup));
		refreshAction.setEnabled(true);
		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>");
		refreshAction
				.setHelpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Refresh"));
		tool.addLocalAction(this, refreshAction);

		//
		// Show new call tree action
		//
		DockingAction newCallTree =
			new DockingAction("Show Call Trees For Function", plugin.getName()) {
				@Override
				public void actionPerformed(ActionContext context) {
					GTree gTree = (GTree) context.getContextObject();
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					CallNode callNode = (CallNode) selectionPaths[0].getLastPathComponent();
					plugin.showNewCallTree(callNode.getRemoteFunction());
				}

				@Override
				public boolean isEnabledForContext(ActionContext context) {
					if (currentFunction == null) {
						return false;
					}

					Object contextObject = context.getContextObject();
					if (!(contextObject instanceof GTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					if (selectionPaths.length != 1) {
						return false;
					}
					CallNode node = ((CallNode) selectionPaths[0].getLastPathComponent());
					// remoteFunction can be null for dead-end nodes
					Function remoteFunction = node.getRemoteFunction();
					String menuName =
						SHOW_CALL_TREE_MENU_PREFIX +
							(remoteFunction == null ? "" : remoteFunction.getName());
					MenuData data = getPopupMenuData().cloneData();
					data.setMenuPath(new String[] { menuName });
					setPopupMenuData(data);
					return remoteFunction != null;
				}

				@Override
				public boolean isAddToPopup(ActionContext context) {
					Object contextObject = context.getContextObject();
					if (!(contextObject instanceof GTree)) {
						return false;
					}

					GTree gTree = (GTree) contextObject;
					TreePath[] selectionPaths = gTree.getSelectionPaths();
					if (selectionPaths.length == 0) {
						return false;
					}

					for (TreePath path : selectionPaths) {
						GTreeNode node = (GTreeNode) path.getLastPathComponent();
						if (node.isRoot()) {
							return false;
						}
					}
					return true;
				}
			};
		newCallTree.setHelpLocation(new HelpLocation(plugin.getName(),
			"Call_Tree_Context_Action_Show_Call_Tree_For_Function"));
		newCallTree.setPopupMenuData(new MenuData(new String[] { SHOW_CALL_TREE_MENU_PREFIX },
			CallTreePlugin.PROVIDER_ICON, newTreeMenu));
		newCallTree.setDescription(
			"Show the Function Call Tree window for the function " + "selected in the call tree");
		tool.addLocalAction(this, newCallTree);

		//
		// Provider menu actions
		//
		//@formatter:off
		filterThunksAction = new ToggleActionBuilder("Filter Thunks", plugin.getName())
			.selected(false)
			.description("Thunk functions will not be shown in the tree when selected")
			.helpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Filter_Thunks"))
			.menuPath("Filter Thunks")
			.onAction(c -> {
				callTreeOptions = callTreeOptions.withFilterThunks(filterThunksAction.isSelected());
				doUpdate();
			})
			.buildAndInstallLocal(this);
		//@formatter:on

		//@formatter:off
		showNamespaceAction = new ToggleActionBuilder("Show Namespace", plugin.getName())
			.selected(false)
			.description("Function nodes will include the function namespace when selected")
			.helpLocation(new HelpLocation(plugin.getName(), "Call_Tree_Action_Show_Namespaces"))
			.menuPath("Show Namespace")
			.onAction(c -> {
				callTreeOptions = callTreeOptions.withShowNamespace(showNamespaceAction.isSelected());
				doUpdate();
			})
			.buildAndInstallLocal(this);
		//@formatter:on
	}
A variable used to restrict open-ended operations, like expanding all nodes or filtering.
Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java