From ee9a0def13684a5356ea84bea6fb9ba241691bc7 Mon Sep 17 00:00:00 2001
From: ghidragon <106987263+ghidragon@users.noreply.github.com>
Date: Fri, 29 Aug 2025 19:46:12 -0400
Subject: [PATCH] GP-5834 small improvements to data graph
---
.../graph/data/DebuggerDataGraphPlugin.java | 76 ---------------
.../main/java/ghidra/app/nav/Navigatable.java | 10 ++
.../hover/DataTypeListingHover.java | 19 ++--
.../topics/DataGraphPlugin/Data_Graph.html | 8 ++
.../datagraph/AbstractDataGraphPlugin.java | 84 -----------------
.../main/java/datagraph/DataGraphPlugin.java | 92 ++++++++++++++++---
.../java/datagraph/DataGraphProvider.java | 44 +++++++--
.../main/java/datagraph/DegSharedConfig.java | 58 ++++++++++++
.../datagraph/data/graph/DegController.java | 21 ++++-
.../data/graph/panel/DataVertexPanel.java | 32 ++++++-
.../graph/program/ProgramGraphPlugin.java | 35 +++----
.../java/docking/widgets/trable/GTrable.java | 4 +
12 files changed, 275 insertions(+), 208 deletions(-)
delete mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/graph/data/DebuggerDataGraphPlugin.java
delete mode 100644 Ghidra/Features/DataGraph/src/main/java/datagraph/AbstractDataGraphPlugin.java
create mode 100644 Ghidra/Features/DataGraph/src/main/java/datagraph/DegSharedConfig.java
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/graph/data/DebuggerDataGraphPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/graph/data/DebuggerDataGraphPlugin.java
deleted file mode 100644
index e1846e08be..0000000000
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/graph/data/DebuggerDataGraphPlugin.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/* ###
- * 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 ghidra.app.plugin.core.debug.gui.graph.data;
-
-import datagraph.AbstractDataGraphPlugin;
-import ghidra.app.context.ListingActionContext;
-import ghidra.app.plugin.PluginCategoryNames;
-import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
-import ghidra.app.plugin.core.debug.event.TraceLocationPluginEvent;
-import ghidra.framework.plugintool.*;
-import ghidra.framework.plugintool.util.PluginStatus;
-import ghidra.program.util.ProgramLocation;
-
-/**
- * Plugin for showing a graph of data from the listing.
- */
-//@formatter:off
-@PluginInfo(
- status = PluginStatus.RELEASED,
- packageName = DebuggerPluginPackage.NAME,
- category = PluginCategoryNames.DEBUGGER,
- shortDescription = "Debugger Data Graph",
- description = """
- Plugin for displaying graphs of data objects in memory. From any data object in the
- listing, the user can display a graph of that data object. Initially, a graph will be shown
- with one vertex that has a scrollable view of the values in memory associated with that data.
- Also, any pointers or references from or to that data can be explored by following the
- references and creating additional vertices for the referenced code or data.
- """,
- eventsConsumed = {
- TraceLocationPluginEvent.class,
- },
- eventsProduced = {
- TraceLocationPluginEvent.class,
- }
-)
-//@formatter:on
-public class DebuggerDataGraphPlugin extends AbstractDataGraphPlugin {
- public DebuggerDataGraphPlugin(PluginTool plugintool) {
- super(plugintool);
- }
-
- @Override
- public void processEvent(PluginEvent event) {
- if (event instanceof TraceLocationPluginEvent ev) {
- ProgramLocation location = ev.getLocation();
- goTo(location);
- }
- }
-
- @Override
- public void fireLocationEvent(ProgramLocation location) {
- firePluginEvent(new TraceLocationPluginEvent(getName(), location));
- }
-
- @Override
- protected boolean isGraphActionEnabled(ListingActionContext context) {
- if (!context.getNavigatable().isDynamic()) {
- return false;
- }
- return super.isGraphActionEnabled(context);
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java
index c354a0ca25..cbf9b133a6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/nav/Navigatable.java
@@ -44,6 +44,16 @@ public interface Navigatable {
*/
public boolean goTo(Program program, ProgramLocation location);
+ /**
+ * Commands this navigatable to goto (display) the given location, using the program
+ * in the location.
+ * @param location the location in that program to display
+ * @return true if the goto was successful
+ */
+ public default boolean goTo(ProgramLocation location) {
+ return goTo(location.getProgram(), location);
+ }
+
/**
* Returns the current location of this Navigatable
*
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/DataTypeListingHover.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/DataTypeListingHover.java
index 2263c30d47..52f5f852a0 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/DataTypeListingHover.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/DataTypeListingHover.java
@@ -141,7 +141,7 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
sb.append("
");
if (parent != null) {
DataType parentType = parent.getDataType();
- sb.append(row("Parent: ", html(parentType.getDataTypePath())));
+ sb.append(row("Parent: ", parentType.getDataTypePath()));
int offset = (int) data.getAddress().subtract(parent.getAddress());
sb.append(row("Offset: ", NumericUtilities.toHexString(offset)));
sb.append(row("Field Name: ", nameLoc.getFieldName()));
@@ -149,7 +149,7 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
DataTypeComponent dtc = pst.getComponentAt(offset);
String comment = dtc == null ? null : dtc.getComment();
if (comment != null) {
- sb.append(row("Comment: ", html(comment)));
+ sb.append(row("Comment: ", comment));
}
}
}
@@ -160,16 +160,17 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
return null;
}
- private String row(String... cols) {
+ private String row(Object... cols) {
StringBuilder sb = new StringBuilder("");
- for (String col : cols) {
- sb.append("").append(col).append(" | ");
+ for (Object col : cols) {
+ String escaped = escapeHtml(col);
+ sb.append("").append(escaped).append(" | ");
}
sb.append("
");
return sb.toString();
}
- private String html(Object obj) {
+ private String escapeHtml(Object obj) {
return obj == null ? "" : HTMLUtilities.friendlyEncodeHTML(obj.toString());
}
@@ -182,10 +183,12 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
if (StringDataInstance.isString(dataInstance)) {
StringDataInstance sdi = StringDataInstance.getStringDataInstance(dataInstance);
if (sdi.isShowTranslation()) {
- result += String.format("
Original value: %s", html(sdi.getStringValue()));
+ String escaped = escapeHtml(sdi.getStringValue());
+ result += String.format("
Original value: %s", escaped);
}
if (!sdi.isShowTranslation() && sdi.getTranslatedValue() != null) {
- result += String.format("
Translated value: %s", html(sdi.getTranslatedValue()));
+ String escaped = escapeHtml(sdi.getTranslatedValue());
+ result += String.format("
Translated value: %s", escaped);
}
if (sdi.isMissingNullTerminator()) {
result += "
Missing NULL terminator.";
diff --git a/Ghidra/Features/DataGraph/src/main/help/help/topics/DataGraphPlugin/Data_Graph.html b/Ghidra/Features/DataGraph/src/main/help/help/topics/DataGraphPlugin/Data_Graph.html
index 979f025f00..62a7399460 100644
--- a/Ghidra/Features/DataGraph/src/main/help/help/topics/DataGraphPlugin/Data_Graph.html
+++ b/Ghidra/Features/DataGraph/src/main/help/help/topics/DataGraphPlugin/Data_Graph.html
@@ -141,6 +141,10 @@
 Delete Vertex - Removes this vertex and all vertices
that descend from this vertex.
+
+
 Restore Location - Moves the vertex
+ to its preferred location (only shows up if the vertex was manually moved).
+
The following popup actions are are available depending on where the
@@ -167,6 +171,9 @@
Delete Selected Vertices - Deletes the selected
vertices and any descendants vertices (vertices that were discovered via exploring from
that vertex.)
+
+ Show Popups - If selected, hovering over data rows in a data
+ vertex will show additional information for the data in that row.
Selecting Vertices
@@ -227,6 +234,7 @@
vertices will show more information for each row in the display. In compact mode, a data
row will generally show the field name and its value. In expanded mode, a data row will
generally show the datatype, field name, and its value.
+
Standard Graph Features and Actions
diff --git a/Ghidra/Features/DataGraph/src/main/java/datagraph/AbstractDataGraphPlugin.java b/Ghidra/Features/DataGraph/src/main/java/datagraph/AbstractDataGraphPlugin.java
deleted file mode 100644
index 20a54fca50..0000000000
--- a/Ghidra/Features/DataGraph/src/main/java/datagraph/AbstractDataGraphPlugin.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/* ###
- * 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 datagraph;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import docking.action.builder.ActionBuilder;
-import ghidra.app.context.ListingActionContext;
-import ghidra.app.plugin.ProgramPlugin;
-import ghidra.framework.plugintool.PluginTool;
-import ghidra.program.model.listing.Data;
-import ghidra.program.util.ProgramLocation;
-import ghidra.util.HelpLocation;
-
-/**
- * Base class for plugins that show a graph of data from program.
- */
-
-public abstract class AbstractDataGraphPlugin extends ProgramPlugin {
- private Set activeProviders = new HashSet<>();
-
- public AbstractDataGraphPlugin(PluginTool plugintool) {
- super(plugintool);
- createActions();
- }
-
- public void goTo(ProgramLocation location) {
- activeProviders.forEach(p -> p.goTo(location));
- }
-
- private void createActions() {
-
- new ActionBuilder("Display Data Graph", getName())
- .popupMenuPath("Data", "Display Data Graph")
- .keyBinding("ctrl G")
- .helpLocation(new HelpLocation("DataGraphPlugin", "Data_Graph"))
- .withContext(ListingActionContext.class)
- .enabledWhen(this::isGraphActionEnabled)
- .onAction(this::showDataGraph)
- .buildAndInstall(tool);
- }
-
- protected boolean isGraphActionEnabled(ListingActionContext context) {
- return context.getCodeUnit() instanceof Data;
- }
-
- private void showDataGraph(ListingActionContext context) {
- Data data = (Data) context.getCodeUnit();
- // the data from the context may be an internal sub-data, we want the outermost data.
- data = getTopLevelData(data);
- DataGraphProvider provider = new DataGraphProvider(this, data);
- activeProviders.add(provider);
- tool.showComponentProvider(provider, true);
- }
-
- private Data getTopLevelData(Data data) {
- Data parent = data.getParent();
- while (parent != null) {
- data = parent;
- parent = data.getParent();
- }
- return data;
- }
-
- void removeProvider(DataGraphProvider provider) {
- activeProviders.remove(provider);
- }
-
- public abstract void fireLocationEvent(ProgramLocation location);
-}
diff --git a/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphPlugin.java b/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphPlugin.java
index 229daf7e6d..ba07f040d1 100644
--- a/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphPlugin.java
+++ b/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphPlugin.java
@@ -15,13 +15,22 @@
*/
package datagraph;
+import java.util.HashSet;
+import java.util.Set;
+
+import docking.action.builder.ActionBuilder;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ListingActionContext;
+import ghidra.app.events.AbstractLocationPluginEvent;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.plugin.PluginCategoryNames;
+import ghidra.app.plugin.ProgramPlugin;
+import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
+import ghidra.program.model.listing.Data;
import ghidra.program.util.ProgramLocation;
+import ghidra.util.HelpLocation;
/**
* Plugin for showing a graph of data from the listing.
@@ -40,36 +49,97 @@ import ghidra.program.util.ProgramLocation;
references and creating additional vertices for the referenced code or data.
""",
eventsConsumed = {
- ProgramLocationPluginEvent.class,
+ ProgramLocationPluginEvent.class,
},
eventsProduced = {
ProgramLocationPluginEvent.class,
}
)
//@formatter:on
-public class DataGraphPlugin extends AbstractDataGraphPlugin {
+public class DataGraphPlugin extends ProgramPlugin {
+ private static final String NAVIGATE_IN = "Navigate In";
+ private static final String NAVIGATE_OUT = "Navigate Out";
+ private static final String COMPACT_FORMAT = "Compact Format";
+ private static final String SHOW_POPUPS = "Show Popups";
+ private Set activeProviders = new HashSet<>();
+ private DegSharedConfig sharedConfig = new DegSharedConfig();
+
public DataGraphPlugin(PluginTool plugintool) {
super(plugintool);
+ createActions();
}
@Override
public void processEvent(PluginEvent event) {
- if (event instanceof ProgramLocationPluginEvent ev) {
+ if (event instanceof AbstractLocationPluginEvent ev) {
ProgramLocation location = ev.getLocation();
- goTo(location);
+ setLocation(location);
}
}
- @Override
- public void fireLocationEvent(ProgramLocation location) {
- firePluginEvent(new ProgramLocationPluginEvent(getName(), location, location.getProgram()));
+ /**
+ * Pass incoming tool location events to each active provider.
+ * @param location the new tool location
+ */
+ public void setLocation(ProgramLocation location) {
+ activeProviders.forEach(p -> p.setLocation(location));
}
@Override
+ public void readConfigState(SaveState saveState) {
+ sharedConfig.setNavigateIn(saveState.getBoolean(NAVIGATE_IN, false));
+ sharedConfig.setNavigateOut(saveState.getBoolean(NAVIGATE_OUT, true));
+ sharedConfig.setCompactFormat(saveState.getBoolean(COMPACT_FORMAT, true));
+ sharedConfig.setShowPopups(saveState.getBoolean(SHOW_POPUPS, true));
+ }
+
+ @Override
+ public void writeConfigState(SaveState saveState) {
+ saveState.putBoolean(NAVIGATE_IN, sharedConfig.isNavigateIn());
+ saveState.putBoolean(NAVIGATE_OUT, sharedConfig.isNavigateOut());
+ saveState.putBoolean(COMPACT_FORMAT, sharedConfig.useCompactFormat());
+ saveState.putBoolean(SHOW_POPUPS, sharedConfig.isShowPopups());
+ }
+
+ private void createActions() {
+
+ new ActionBuilder("Display Data Graph", getName())
+ .menuPath("&Graph", "Data")
+ .menuGroup("Graph", "Data")
+ .popupMenuPath("Data", "Display Data Graph")
+ .keyBinding("ctrl G")
+ .helpLocation(new HelpLocation("DataGraphPlugin", "Data_Graph"))
+ .withContext(ListingActionContext.class)
+ .enabledWhen(this::isGraphActionEnabled)
+ .onAction(this::showDataGraph)
+ .buildAndInstall(tool);
+ }
+
protected boolean isGraphActionEnabled(ListingActionContext context) {
- if (context.getNavigatable().isDynamic()) {
- return false;
- }
- return super.isGraphActionEnabled(context);
+ return context.getCodeUnit() instanceof Data;
}
+
+ private void showDataGraph(ListingActionContext context) {
+ Data data = (Data) context.getCodeUnit();
+ // the data from the context may be an internal sub-data, we want the outermost data.
+ data = getTopLevelData(data);
+ DataGraphProvider provider =
+ new DataGraphProvider(this, context.getNavigatable(), data, sharedConfig);
+ activeProviders.add(provider);
+ tool.showComponentProvider(provider, true);
+ }
+
+ private Data getTopLevelData(Data data) {
+ Data parent = data.getParent();
+ while (parent != null) {
+ data = parent;
+ parent = data.getParent();
+ }
+ return data;
+ }
+
+ void removeProvider(DataGraphProvider provider) {
+ activeProviders.remove(provider);
+ }
+
}
diff --git a/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphProvider.java b/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphProvider.java
index 7f7491103e..7cb30ac2c8 100644
--- a/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphProvider.java
+++ b/Ghidra/Features/DataGraph/src/main/java/datagraph/DataGraphProvider.java
@@ -29,10 +29,12 @@ import docking.action.ToggleDockingAction;
import docking.action.builder.ActionBuilder;
import docking.action.builder.ToggleActionBuilder;
import generic.theme.GIcon;
+import ghidra.app.nav.Navigatable;
import ghidra.graph.VisualGraphComponentProvider;
import ghidra.graph.viewer.*;
import ghidra.graph.viewer.GraphComponent.SatellitePosition;
import ghidra.graph.viewer.event.mouse.VertexMouseInfo;
+import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
@@ -52,24 +54,28 @@ public class DataGraphProvider
private static final GIcon RESET_ICON = new GIcon("icon.plugin.datagraph.action.viewer.reset");
private static final String NAME = "Data Graph";
- private AbstractDataGraphPlugin plugin;
+ private DataGraphPlugin plugin;
private JPanel mainPanel;
private DegController controller;
private ToggleDockingAction navagateInAction;
private ToggleDockingAction navagateOutAction;
private ToggleDockingAction expandedFormatAction;
+ private Navigatable navigatable;
+ private ToggleDockingAction togglePopups;
/**
* Constructor
* @param plugin the DataGraphPlugin
* @param data the initial data object to display in the graph.
*/
- public DataGraphProvider(AbstractDataGraphPlugin plugin, Data data) {
+ public DataGraphProvider(DataGraphPlugin plugin, Navigatable navigatable, Data data,
+ DegSharedConfig sharedConfig) {
super(plugin.getTool(), NAME, plugin.getName());
this.plugin = plugin;
- controller = new DegController(this, data);
- createActions();
+ this.navigatable = navigatable;
+ controller = new DegController(this, data, sharedConfig);
+ createActions(sharedConfig);
setTransient();
buildComponent();
@@ -77,6 +83,14 @@ public class DataGraphProvider
addSatelliteFeature(false, SatellitePosition.LOWER_LEFT);
setHelpLocation(new HelpLocation("DataGraphPlugin", "DataGraphPlugin"));
+ updateSubTitle();
+ }
+
+ public void updateSubTitle() {
+ Program program = controller.getProgram();
+ DataExplorationGraph graph = controller.getGraph();
+ Address address = graph.getRoot().getAddress();
+ setSubTitle(program.getName() + " @ " + address);
}
private void buildComponent() {
@@ -141,7 +155,7 @@ public class DataGraphProvider
return controller;
}
- private void createActions() {
+ private void createActions(DegSharedConfig sharedConfig) {
new ActionBuilder("Select Home Vertex", plugin.getName())
.toolBarIcon(Icons.HOME_ICON)
.toolBarGroup("A")
@@ -159,6 +173,7 @@ public class DataGraphProvider
.toolBarIcon(DETAILS_ICON)
.toolBarGroup("A")
.description("Show Expanded information in data vertices.")
+ .selected(!sharedConfig.useCompactFormat())
.helpLocation(new HelpLocation("DataGraphPlugin", "Expanded_Format"))
.onAction(c -> controller.setCompactFormat(!expandedFormatAction.isSelected()))
.buildAndInstallLocal(this);
@@ -169,6 +184,7 @@ public class DataGraphProvider
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.toolBarGroup("B")
.description("Attemps to select vertex corresponding to tool location changes.")
+ .selected(sharedConfig.isNavigateIn())
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_In"))
.onAction(c -> controller.setNavigateIn(navagateInAction.isSelected()))
.buildAndInstallLocal(this);
@@ -180,11 +196,22 @@ public class DataGraphProvider
.sharedKeyBinding()
.description(
"Selecting vetices or locations inside a vertex sends navigates the tool.")
+ .selected(sharedConfig.isNavigateOut())
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_Out"))
.onAction(c -> controller.setNavigateOut(navagateOutAction.isSelected()))
.selected(true)
.buildAndInstallLocal(this);
+ togglePopups = new ToggleActionBuilder("Display Popup Windows", plugin.getName())
+ .popupMenuPath("Display Popup Windows")
+ .description("Toggles whether or not to show tooltips")
+ .selected(sharedConfig.isShowPopups())
+ .helpLocation(new HelpLocation("DataGraphPlugin", "Show_Popups"))
+ .withContext(DegContext.class)
+ .popupWhen(c -> c.getVertex() == null)
+ .onAction(c -> controller.setPopupsVisible(togglePopups.isSelected()))
+ .buildAndInstallLocal(this);
+
new ActionBuilder("Incoming References", plugin.getName())
.popupMenuPath("Add All Incoming References")
.popupMenuGroup("A", "2")
@@ -271,8 +298,8 @@ public class DataGraphProvider
return !v.isRoot();
}
- void goTo(ProgramLocation location) {
- controller.locationChanged(location);
+ void setLocation(ProgramLocation location) {
+ controller.setLocation(location);
}
private boolean canExpandRecursively(DegContext context) {
@@ -289,8 +316,7 @@ public class DataGraphProvider
}
public void navigateOut(ProgramLocation location) {
- plugin.fireLocationEvent(location);
-
+ navigatable.goTo(location);
}
}
diff --git a/Ghidra/Features/DataGraph/src/main/java/datagraph/DegSharedConfig.java b/Ghidra/Features/DataGraph/src/main/java/datagraph/DegSharedConfig.java
new file mode 100644
index 0000000000..38234e8b63
--- /dev/null
+++ b/Ghidra/Features/DataGraph/src/main/java/datagraph/DegSharedConfig.java
@@ -0,0 +1,58 @@
+/* ###
+ * 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 datagraph;
+
+// Simple storage of shared data graph configuration states. If any provider changes any of these,
+// then that will be the value going forward. In other words, the last one in wins.
+public class DegSharedConfig {
+ private boolean navigateIn = false;
+ private boolean navigateOut = true;
+ private boolean showPopups = true;
+ private boolean useCompactFormat = true;
+
+ public boolean isNavigateIn() {
+ return navigateIn;
+ }
+
+ public void setNavigateIn(boolean navigateIn) {
+ this.navigateIn = navigateIn;
+ }
+
+ public boolean isNavigateOut() {
+ return navigateOut;
+ }
+
+ public void setNavigateOut(boolean navigateOut) {
+ this.navigateOut = navigateOut;
+ }
+
+ public boolean isShowPopups() {
+ return showPopups;
+ }
+
+ public void setShowPopups(boolean showPopups) {
+ this.showPopups = showPopups;
+ }
+
+ public boolean useCompactFormat() {
+ return useCompactFormat;
+ }
+
+ public void setCompactFormat(boolean useCompactFormat) {
+ this.useCompactFormat = useCompactFormat;
+ }
+
+}
diff --git a/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/DegController.java b/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/DegController.java
index 7278855a23..978335df1a 100644
--- a/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/DegController.java
+++ b/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/DegController.java
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
import javax.swing.JComponent;
import datagraph.DataGraphProvider;
+import datagraph.DegSharedConfig;
import datagraph.data.graph.DegVertex.DegVertexStatus;
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
import ghidra.app.util.XReferenceUtils;
@@ -43,6 +44,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceManager;
+import ghidra.program.util.AddressFieldLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
@@ -62,14 +64,17 @@ public class DegController implements DomainObjectListener {
private boolean navigateIn = false;
private boolean compactFormat = true;
private DataGraphProvider provider;
+ private DegSharedConfig sharedConfig;
/**
* Constructs a new data exploration graph controller.
* @param provider The data graph provider that created this controller
* @param data the initial data to display in the graph
+ * @param sharedConfig the shared data graph configuration state
*/
- public DegController(DataGraphProvider provider, Data data) {
+ public DegController(DataGraphProvider provider, Data data, DegSharedConfig sharedConfig) {
this.provider = provider;
+ this.sharedConfig = sharedConfig;
this.program = data.getProgram();
view = new DegGraphView();
DegVertex root = new DataDegVertex(this, data, null, true);
@@ -88,8 +93,9 @@ public class DegController implements DomainObjectListener {
*/
public void navigateOut(Address address, int[] componentPath) {
if (navigateOut) {
+ // Using an address field location skips past pre or plate comments
ProgramLocation location =
- new ProgramLocation(program, address, address, componentPath, null, 0, 0, 0);
+ new AddressFieldLocation(program, address, componentPath, address.toString(), 0);
provider.navigateOut(location);
}
}
@@ -226,6 +232,7 @@ public class DegController implements DomainObjectListener {
public void orientAround(DegVertex newRoot) {
graph.setRoot(newRoot);
relayoutGraphAndCenter(newRoot);
+ provider.updateSubTitle();
}
public VisualGraphView getView() {
@@ -247,6 +254,7 @@ public class DegController implements DomainObjectListener {
*/
public void setNavigateOut(boolean b) {
navigateOut = b;
+ sharedConfig.setNavigateOut(b);
}
/**
@@ -256,6 +264,7 @@ public class DegController implements DomainObjectListener {
*/
public void setNavigateIn(boolean b) {
navigateIn = b;
+ sharedConfig.setNavigateIn(b);
}
/**
@@ -270,6 +279,12 @@ public class DegController implements DomainObjectListener {
}
});
relayoutGraph();
+ sharedConfig.setCompactFormat(b);
+ }
+
+ public void setPopupsVisible(boolean b) {
+ view.setPopupsVisible(b);
+ sharedConfig.setShowPopups(b);
}
public boolean isCompactFormat() {
@@ -330,7 +345,7 @@ public class DegController implements DomainObjectListener {
* graph will select that vertex.
* @param location the new location for the tool
*/
- public void locationChanged(ProgramLocation location) {
+ public void setLocation(ProgramLocation location) {
if (!navigateIn) {
return;
}
diff --git a/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/panel/DataVertexPanel.java b/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/panel/DataVertexPanel.java
index fdecb8b583..85d4216a81 100644
--- a/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/panel/DataVertexPanel.java
+++ b/Ghidra/Features/DataGraph/src/main/java/datagraph/data/graph/panel/DataVertexPanel.java
@@ -34,7 +34,10 @@ import docking.action.DockingActionIf;
import docking.widgets.trable.GTrable;
import docking.widgets.trable.GTrableColumnModel;
import ghidra.program.model.address.Address;
+import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Data;
+import ghidra.util.HTMLUtilities;
+import ghidra.util.NumericUtilities;
import ghidra.util.datastruct.Range;
import resources.Icons;
@@ -119,7 +122,34 @@ public class DataVertexPanel extends JPanel {
JComponent jComponent = (JComponent) source;
return jComponent.getToolTipText();
}
- return null;
+ int row = gTrable.getRow(event.getPoint());
+ DataRowObject rowObject = model.getRow(row);
+ Data data = rowObject.getData();
+ StringBuilder sb = new StringBuilder(HTMLUtilities.HTML);
+ sb.append("");
+ DataType dataType = data.getDataType();
+ Address address = data.getAddress();
+ int rootOffset = data.getRootOffset();
+
+ sb.append(row("Address: ", address.toString()));
+ sb.append(row("Offset: ", NumericUtilities.toHexString(rootOffset)));
+ sb.append(row("Data Type: ", dataType.getName()));
+ sb.append("
");
+ return sb.toString();
+ }
+
+ private String row(Object... cols) {
+ StringBuilder sb = new StringBuilder("");
+ for (Object col : cols) {
+ String escaped = escapeHtml(col);
+ sb.append("").append(escaped).append(" | ");
+ }
+ sb.append("
");
+ return sb.toString();
+ }
+
+ private String escapeHtml(Object obj) {
+ return obj == null ? "" : HTMLUtilities.friendlyEncodeHTML(obj.toString());
}
/**
diff --git a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java
index f1b99a81cf..bc8728c72b 100644
--- a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java
+++ b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java
@@ -185,54 +185,57 @@ public class ProgramGraphPlugin extends ProgramPlugin
private void createActions() {
- new ActionBuilder("Graph Block Flow", getName()).menuPath(MENU_GRAPH, "&Block Flow")
- .menuGroup(MENU_GRAPH, "A")
+ new ActionBuilder("Graph Block Flow", getName())
+ .menuPath(MENU_GRAPH, "&Block Flow")
+ .menuGroup("Code Graph", "A")
.onAction(c -> graphBlockFlow())
.enabledWhen(this::canGraph)
.buildAndInstall(tool);
- new ActionBuilder("Graph Code Flow", getName()).menuPath(MENU_GRAPH, "C&ode Flow")
- .menuGroup(MENU_GRAPH, "B")
+ new ActionBuilder("Graph Code Flow", getName())
+ .menuPath(MENU_GRAPH, "C&ode Flow")
+ .menuGroup("Code Graph", "B")
.onAction(c -> graphCodeFlow())
.enabledWhen(this::canGraph)
.buildAndInstall(tool);
new ActionBuilder("Graph Calls Using Default Model", getName())
.menuPath(MENU_GRAPH, "&Calls")
- .menuGroup(MENU_GRAPH, "C")
+ .menuGroup("Code Graph", "C")
.onAction(c -> createDefaultCallGraph())
.enabledWhen(this::canGraph)
.buildAndInstall(tool);
- tool.setMenuGroup(new String[] { MENU_GRAPH, "Data" }, "Graph", "Data");
+ tool.setMenuGroup(new String[] { MENU_GRAPH, "Data References" }, "Graph", "Data");
HelpLocation helpLoc = new HelpLocation(getName(), "Data_Reference_Graph");
new ActionBuilder("Graph To/From Data References", getName())
- .menuPath(MENU_GRAPH, "Data", "To/From &References")
- .menuGroup(MENU_GRAPH, "Data")
+ .menuPath(MENU_GRAPH, "Data References", "To/From &References")
+ .menuGroup("Graph", "Data")
.helpLocation(helpLoc)
.onAction(c -> graphDataReferences())
.enabledWhen(this::canGraph)
.buildAndInstall(tool);
new ActionBuilder("Graph To Data References", getName())
- .menuPath(MENU_GRAPH, "Data", "&To References")
- .menuGroup(MENU_GRAPH, "Data")
+ .menuPath(MENU_GRAPH, "Data References", "&To References")
+ .menuGroup("Graph", "Data")
.helpLocation(helpLoc)
.onAction(c -> graphToDataReferences())
.enabledWhen(this::canGraph)
.buildAndInstall(tool);
new ActionBuilder("Graph From Data References", getName())
- .menuPath(MENU_GRAPH, "Data", "&From References")
- .menuGroup(MENU_GRAPH, "Data")
+ .menuPath(MENU_GRAPH, "Data References", "&From References")
+ .menuGroup("Graph", "Data")
.helpLocation(helpLoc)
.onAction(c -> graphFromDataReferences())
.enabledWhen(this::canGraph)
.buildAndInstall(tool);
reuseGraphAction =
- new ToggleActionBuilder("Reuse Graph", getName()).menuPath(MENU_GRAPH, "Reuse Graph")
+ new ToggleActionBuilder("Reuse Graph", getName())
+ .menuPath(MENU_GRAPH, "Reuse Graph")
.menuGroup("Graph Options")
.selected(reuseGraph)
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
@@ -240,7 +243,8 @@ public class ProgramGraphPlugin extends ProgramPlugin
.buildAndInstall(tool);
appendGraphAction =
- new ToggleActionBuilder("Append Graph", getName()).menuPath(MENU_GRAPH, "Append Graph")
+ new ToggleActionBuilder("Append Graph", getName())
+ .menuPath(MENU_GRAPH, "Append Graph")
.menuGroup("Graph Options")
.selected(false)
.onAction(c -> updateAppendAndReuseGraph())
@@ -297,13 +301,12 @@ public class ProgramGraphPlugin extends ProgramPlugin
subUsingGraphActions.add(action);
}
- tool.setMenuGroup(new String[] { MENU_GRAPH, "Calls Using Model" }, "Graph", "C");
+ tool.setMenuGroup(new String[] { MENU_GRAPH, "Calls Using Model" }, "Code Graph", "D");
}
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
.menuPath(MENU_GRAPH, "Calls Using Model", blockModelName)
- .menuGroup(MENU_GRAPH, "C")
.helpLocation(helpLoc)
.onAction(c -> createCallGraphUsing(blockModelName))
.enabledWhen(this::canGraph)
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/trable/GTrable.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/trable/GTrable.java
index e6a71501fc..0279dbb88f 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/trable/GTrable.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/trable/GTrable.java
@@ -370,6 +370,10 @@ public class GTrable extends JComponent
columnModel.setWidth(width);
}
+ public int getRow(Point p) {
+ return p.y / rowHeight;
+ }
+
private void notifySelectedRowConsumers() {
for (Consumer consumer : selectedRowConsumers) {
consumer.accept(selectedRow);