mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
GP-5834 small improvements to data graph
This commit is contained in:
parent
ec28004339
commit
ee9a0def13
12 changed files with 275 additions and 208 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,6 +44,16 @@ public interface Navigatable {
|
||||||
*/
|
*/
|
||||||
public boolean goTo(Program program, ProgramLocation location);
|
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
|
* Returns the current location of this Navigatable
|
||||||
*
|
*
|
||||||
|
|
|
@ -141,7 +141,7 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
|
||||||
sb.append("<TABLE>");
|
sb.append("<TABLE>");
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
DataType parentType = parent.getDataType();
|
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());
|
int offset = (int) data.getAddress().subtract(parent.getAddress());
|
||||||
sb.append(row("Offset: ", NumericUtilities.toHexString(offset)));
|
sb.append(row("Offset: ", NumericUtilities.toHexString(offset)));
|
||||||
sb.append(row("Field Name: ", nameLoc.getFieldName()));
|
sb.append(row("Field Name: ", nameLoc.getFieldName()));
|
||||||
|
@ -149,7 +149,7 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
|
||||||
DataTypeComponent dtc = pst.getComponentAt(offset);
|
DataTypeComponent dtc = pst.getComponentAt(offset);
|
||||||
String comment = dtc == null ? null : dtc.getComment();
|
String comment = dtc == null ? null : dtc.getComment();
|
||||||
if (comment != null) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String row(String... cols) {
|
private String row(Object... cols) {
|
||||||
StringBuilder sb = new StringBuilder("<TR>");
|
StringBuilder sb = new StringBuilder("<TR>");
|
||||||
for (String col : cols) {
|
for (Object col : cols) {
|
||||||
sb.append("<TD>").append(col).append("</TD>");
|
String escaped = escapeHtml(col);
|
||||||
|
sb.append("<TD>").append(escaped).append("</TD>");
|
||||||
}
|
}
|
||||||
sb.append("</TR>");
|
sb.append("</TR>");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String html(Object obj) {
|
private String escapeHtml(Object obj) {
|
||||||
return obj == null ? "" : HTMLUtilities.friendlyEncodeHTML(obj.toString());
|
return obj == null ? "" : HTMLUtilities.friendlyEncodeHTML(obj.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,10 +183,12 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
|
||||||
if (StringDataInstance.isString(dataInstance)) {
|
if (StringDataInstance.isString(dataInstance)) {
|
||||||
StringDataInstance sdi = StringDataInstance.getStringDataInstance(dataInstance);
|
StringDataInstance sdi = StringDataInstance.getStringDataInstance(dataInstance);
|
||||||
if (sdi.isShowTranslation()) {
|
if (sdi.isShowTranslation()) {
|
||||||
result += String.format("<br>Original value: %s", html(sdi.getStringValue()));
|
String escaped = escapeHtml(sdi.getStringValue());
|
||||||
|
result += String.format("<br>Original value: %s", escaped);
|
||||||
}
|
}
|
||||||
if (!sdi.isShowTranslation() && sdi.getTranslatedValue() != null) {
|
if (!sdi.isShowTranslation() && sdi.getTranslatedValue() != null) {
|
||||||
result += String.format("<br>Translated value: %s", html(sdi.getTranslatedValue()));
|
String escaped = escapeHtml(sdi.getTranslatedValue());
|
||||||
|
result += String.format("<br>Translated value: %s", escaped);
|
||||||
}
|
}
|
||||||
if (sdi.isMissingNullTerminator()) {
|
if (sdi.isMissingNullTerminator()) {
|
||||||
result += "<br>Missing NULL terminator.";
|
result += "<br>Missing NULL terminator.";
|
||||||
|
|
|
@ -141,6 +141,10 @@
|
||||||
|
|
||||||
<LI><A name="Delete_Vertex"><IMG alt="" src="Icons.CLOSE_ICON"> <B>Delete Vertex</B> - Removes this vertex and all vertices
|
<LI><A name="Delete_Vertex"><IMG alt="" src="Icons.CLOSE_ICON"> <B>Delete Vertex</B> - Removes this vertex and all vertices
|
||||||
that descend from this vertex.</A></LI>
|
that descend from this vertex.</A></LI>
|
||||||
|
|
||||||
|
<LI><A name="Reset_Location"><IMG alt="" src="Icons.REFRESH_ICON"> <B>Restore Location</B> - Moves the vertex
|
||||||
|
to its preferred location (only shows up if the vertex was manually moved).</A></LI>
|
||||||
|
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
<P><A name="Popups">The following popup actions are are available depending on where the
|
<P><A name="Popups">The following popup actions are are available depending on where the
|
||||||
|
@ -167,6 +171,9 @@
|
||||||
<LI><A name="Delete_Selected"><B>Delete Selected Vertices</B> - Deletes the selected
|
<LI><A name="Delete_Selected"><B>Delete Selected Vertices</B> - Deletes the selected
|
||||||
vertices and any descendants vertices (vertices that were discovered via exploring from
|
vertices and any descendants vertices (vertices that were discovered via exploring from
|
||||||
that vertex.)</A></LI>
|
that vertex.)</A></LI>
|
||||||
|
|
||||||
|
<LI><A name="Show_Popups"><B>Show Popups</B> - If selected, hovering over data rows in a data
|
||||||
|
vertex will show additional information for the data in that row.</A></LI>
|
||||||
</UL>
|
</UL>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
<H3>Selecting Vertices</H3>
|
<H3>Selecting Vertices</H3>
|
||||||
|
@ -227,6 +234,7 @@
|
||||||
vertices will show more information for each row in the display. In compact mode, a data
|
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
|
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.</A></LI>
|
generally show the datatype, field name, and its value.</A></LI>
|
||||||
|
|
||||||
</UL>
|
</UL>
|
||||||
</BLOCKQUOTE
|
</BLOCKQUOTE
|
||||||
<H2>Standard Graph Features and Actions</H2>
|
<H2>Standard Graph Features and Actions</H2>
|
||||||
|
|
|
@ -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<DataGraphProvider> 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);
|
|
||||||
}
|
|
|
@ -15,13 +15,22 @@
|
||||||
*/
|
*/
|
||||||
package datagraph;
|
package datagraph;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.context.ListingActionContext;
|
import ghidra.app.context.ListingActionContext;
|
||||||
|
import ghidra.app.events.AbstractLocationPluginEvent;
|
||||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
|
import ghidra.app.plugin.ProgramPlugin;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin for showing a graph of data from the listing.
|
* Plugin for showing a graph of data from the listing.
|
||||||
|
@ -47,29 +56,90 @@ import ghidra.program.util.ProgramLocation;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
//@formatter:on
|
//@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<DataGraphProvider> activeProviders = new HashSet<>();
|
||||||
|
private DegSharedConfig sharedConfig = new DegSharedConfig();
|
||||||
|
|
||||||
public DataGraphPlugin(PluginTool plugintool) {
|
public DataGraphPlugin(PluginTool plugintool) {
|
||||||
super(plugintool);
|
super(plugintool);
|
||||||
|
createActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processEvent(PluginEvent event) {
|
public void processEvent(PluginEvent event) {
|
||||||
if (event instanceof ProgramLocationPluginEvent ev) {
|
if (event instanceof AbstractLocationPluginEvent ev) {
|
||||||
ProgramLocation location = ev.getLocation();
|
ProgramLocation location = ev.getLocation();
|
||||||
goTo(location);
|
setLocation(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void fireLocationEvent(ProgramLocation location) {
|
* Pass incoming tool location events to each active provider.
|
||||||
firePluginEvent(new ProgramLocationPluginEvent(getName(), location, location.getProgram()));
|
* @param location the new tool location
|
||||||
|
*/
|
||||||
|
public void setLocation(ProgramLocation location) {
|
||||||
|
activeProviders.forEach(p -> p.setLocation(location));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
protected boolean isGraphActionEnabled(ListingActionContext context) {
|
||||||
if (context.getNavigatable().isDynamic()) {
|
return context.getCodeUnit() instanceof Data;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return super.isGraphActionEnabled(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,12 @@ import docking.action.ToggleDockingAction;
|
||||||
import docking.action.builder.ActionBuilder;
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.action.builder.ToggleActionBuilder;
|
import docking.action.builder.ToggleActionBuilder;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.graph.VisualGraphComponentProvider;
|
import ghidra.graph.VisualGraphComponentProvider;
|
||||||
import ghidra.graph.viewer.*;
|
import ghidra.graph.viewer.*;
|
||||||
import ghidra.graph.viewer.GraphComponent.SatellitePosition;
|
import ghidra.graph.viewer.GraphComponent.SatellitePosition;
|
||||||
import ghidra.graph.viewer.event.mouse.VertexMouseInfo;
|
import ghidra.graph.viewer.event.mouse.VertexMouseInfo;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramLocation;
|
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 GIcon RESET_ICON = new GIcon("icon.plugin.datagraph.action.viewer.reset");
|
||||||
private static final String NAME = "Data Graph";
|
private static final String NAME = "Data Graph";
|
||||||
|
|
||||||
private AbstractDataGraphPlugin plugin;
|
private DataGraphPlugin plugin;
|
||||||
private JPanel mainPanel;
|
private JPanel mainPanel;
|
||||||
|
|
||||||
private DegController controller;
|
private DegController controller;
|
||||||
private ToggleDockingAction navagateInAction;
|
private ToggleDockingAction navagateInAction;
|
||||||
private ToggleDockingAction navagateOutAction;
|
private ToggleDockingAction navagateOutAction;
|
||||||
private ToggleDockingAction expandedFormatAction;
|
private ToggleDockingAction expandedFormatAction;
|
||||||
|
private Navigatable navigatable;
|
||||||
|
private ToggleDockingAction togglePopups;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param plugin the DataGraphPlugin
|
* @param plugin the DataGraphPlugin
|
||||||
* @param data the initial data object to display in the graph.
|
* @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());
|
super(plugin.getTool(), NAME, plugin.getName());
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
controller = new DegController(this, data);
|
this.navigatable = navigatable;
|
||||||
createActions();
|
controller = new DegController(this, data, sharedConfig);
|
||||||
|
createActions(sharedConfig);
|
||||||
setTransient();
|
setTransient();
|
||||||
|
|
||||||
buildComponent();
|
buildComponent();
|
||||||
|
@ -77,6 +83,14 @@ public class DataGraphProvider
|
||||||
addSatelliteFeature(false, SatellitePosition.LOWER_LEFT);
|
addSatelliteFeature(false, SatellitePosition.LOWER_LEFT);
|
||||||
|
|
||||||
setHelpLocation(new HelpLocation("DataGraphPlugin", "DataGraphPlugin"));
|
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() {
|
private void buildComponent() {
|
||||||
|
@ -141,7 +155,7 @@ public class DataGraphProvider
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createActions() {
|
private void createActions(DegSharedConfig sharedConfig) {
|
||||||
new ActionBuilder("Select Home Vertex", plugin.getName())
|
new ActionBuilder("Select Home Vertex", plugin.getName())
|
||||||
.toolBarIcon(Icons.HOME_ICON)
|
.toolBarIcon(Icons.HOME_ICON)
|
||||||
.toolBarGroup("A")
|
.toolBarGroup("A")
|
||||||
|
@ -159,6 +173,7 @@ public class DataGraphProvider
|
||||||
.toolBarIcon(DETAILS_ICON)
|
.toolBarIcon(DETAILS_ICON)
|
||||||
.toolBarGroup("A")
|
.toolBarGroup("A")
|
||||||
.description("Show Expanded information in data vertices.")
|
.description("Show Expanded information in data vertices.")
|
||||||
|
.selected(!sharedConfig.useCompactFormat())
|
||||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Expanded_Format"))
|
.helpLocation(new HelpLocation("DataGraphPlugin", "Expanded_Format"))
|
||||||
.onAction(c -> controller.setCompactFormat(!expandedFormatAction.isSelected()))
|
.onAction(c -> controller.setCompactFormat(!expandedFormatAction.isSelected()))
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
@ -169,6 +184,7 @@ public class DataGraphProvider
|
||||||
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
||||||
.toolBarGroup("B")
|
.toolBarGroup("B")
|
||||||
.description("Attemps to select vertex corresponding to tool location changes.")
|
.description("Attemps to select vertex corresponding to tool location changes.")
|
||||||
|
.selected(sharedConfig.isNavigateIn())
|
||||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_In"))
|
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_In"))
|
||||||
.onAction(c -> controller.setNavigateIn(navagateInAction.isSelected()))
|
.onAction(c -> controller.setNavigateIn(navagateInAction.isSelected()))
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
@ -180,11 +196,22 @@ public class DataGraphProvider
|
||||||
.sharedKeyBinding()
|
.sharedKeyBinding()
|
||||||
.description(
|
.description(
|
||||||
"Selecting vetices or locations inside a vertex sends navigates the tool.")
|
"Selecting vetices or locations inside a vertex sends navigates the tool.")
|
||||||
|
.selected(sharedConfig.isNavigateOut())
|
||||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_Out"))
|
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_Out"))
|
||||||
.onAction(c -> controller.setNavigateOut(navagateOutAction.isSelected()))
|
.onAction(c -> controller.setNavigateOut(navagateOutAction.isSelected()))
|
||||||
.selected(true)
|
.selected(true)
|
||||||
.buildAndInstallLocal(this);
|
.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())
|
new ActionBuilder("Incoming References", plugin.getName())
|
||||||
.popupMenuPath("Add All Incoming References")
|
.popupMenuPath("Add All Incoming References")
|
||||||
.popupMenuGroup("A", "2")
|
.popupMenuGroup("A", "2")
|
||||||
|
@ -271,8 +298,8 @@ public class DataGraphProvider
|
||||||
return !v.isRoot();
|
return !v.isRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
void goTo(ProgramLocation location) {
|
void setLocation(ProgramLocation location) {
|
||||||
controller.locationChanged(location);
|
controller.setLocation(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canExpandRecursively(DegContext context) {
|
private boolean canExpandRecursively(DegContext context) {
|
||||||
|
@ -289,8 +316,7 @@ public class DataGraphProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigateOut(ProgramLocation location) {
|
public void navigateOut(ProgramLocation location) {
|
||||||
plugin.fireLocationEvent(location);
|
navigatable.goTo(location);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import java.util.stream.Collectors;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
import datagraph.DataGraphProvider;
|
import datagraph.DataGraphProvider;
|
||||||
|
import datagraph.DegSharedConfig;
|
||||||
import datagraph.data.graph.DegVertex.DegVertexStatus;
|
import datagraph.data.graph.DegVertex.DegVertexStatus;
|
||||||
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
|
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
|
||||||
import ghidra.app.util.XReferenceUtils;
|
import ghidra.app.util.XReferenceUtils;
|
||||||
|
@ -43,6 +44,7 @@ import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.Reference;
|
import ghidra.program.model.symbol.Reference;
|
||||||
import ghidra.program.model.symbol.ReferenceManager;
|
import ghidra.program.model.symbol.ReferenceManager;
|
||||||
|
import ghidra.program.util.AddressFieldLocation;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -62,14 +64,17 @@ public class DegController implements DomainObjectListener {
|
||||||
private boolean navigateIn = false;
|
private boolean navigateIn = false;
|
||||||
private boolean compactFormat = true;
|
private boolean compactFormat = true;
|
||||||
private DataGraphProvider provider;
|
private DataGraphProvider provider;
|
||||||
|
private DegSharedConfig sharedConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new data exploration graph controller.
|
* Constructs a new data exploration graph controller.
|
||||||
* @param provider The data graph provider that created this controller
|
* @param provider The data graph provider that created this controller
|
||||||
* @param data the initial data to display in the graph
|
* @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.provider = provider;
|
||||||
|
this.sharedConfig = sharedConfig;
|
||||||
this.program = data.getProgram();
|
this.program = data.getProgram();
|
||||||
view = new DegGraphView();
|
view = new DegGraphView();
|
||||||
DegVertex root = new DataDegVertex(this, data, null, true);
|
DegVertex root = new DataDegVertex(this, data, null, true);
|
||||||
|
@ -88,8 +93,9 @@ public class DegController implements DomainObjectListener {
|
||||||
*/
|
*/
|
||||||
public void navigateOut(Address address, int[] componentPath) {
|
public void navigateOut(Address address, int[] componentPath) {
|
||||||
if (navigateOut) {
|
if (navigateOut) {
|
||||||
|
// Using an address field location skips past pre or plate comments
|
||||||
ProgramLocation location =
|
ProgramLocation location =
|
||||||
new ProgramLocation(program, address, address, componentPath, null, 0, 0, 0);
|
new AddressFieldLocation(program, address, componentPath, address.toString(), 0);
|
||||||
provider.navigateOut(location);
|
provider.navigateOut(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,6 +232,7 @@ public class DegController implements DomainObjectListener {
|
||||||
public void orientAround(DegVertex newRoot) {
|
public void orientAround(DegVertex newRoot) {
|
||||||
graph.setRoot(newRoot);
|
graph.setRoot(newRoot);
|
||||||
relayoutGraphAndCenter(newRoot);
|
relayoutGraphAndCenter(newRoot);
|
||||||
|
provider.updateSubTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public VisualGraphView<DegVertex, DegEdge, DataExplorationGraph> getView() {
|
public VisualGraphView<DegVertex, DegEdge, DataExplorationGraph> getView() {
|
||||||
|
@ -247,6 +254,7 @@ public class DegController implements DomainObjectListener {
|
||||||
*/
|
*/
|
||||||
public void setNavigateOut(boolean b) {
|
public void setNavigateOut(boolean b) {
|
||||||
navigateOut = b;
|
navigateOut = b;
|
||||||
|
sharedConfig.setNavigateOut(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,6 +264,7 @@ public class DegController implements DomainObjectListener {
|
||||||
*/
|
*/
|
||||||
public void setNavigateIn(boolean b) {
|
public void setNavigateIn(boolean b) {
|
||||||
navigateIn = b;
|
navigateIn = b;
|
||||||
|
sharedConfig.setNavigateIn(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -270,6 +279,12 @@ public class DegController implements DomainObjectListener {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
relayoutGraph();
|
relayoutGraph();
|
||||||
|
sharedConfig.setCompactFormat(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPopupsVisible(boolean b) {
|
||||||
|
view.setPopupsVisible(b);
|
||||||
|
sharedConfig.setShowPopups(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCompactFormat() {
|
public boolean isCompactFormat() {
|
||||||
|
@ -330,7 +345,7 @@ public class DegController implements DomainObjectListener {
|
||||||
* graph will select that vertex.
|
* graph will select that vertex.
|
||||||
* @param location the new location for the tool
|
* @param location the new location for the tool
|
||||||
*/
|
*/
|
||||||
public void locationChanged(ProgramLocation location) {
|
public void setLocation(ProgramLocation location) {
|
||||||
if (!navigateIn) {
|
if (!navigateIn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,10 @@ import docking.action.DockingActionIf;
|
||||||
import docking.widgets.trable.GTrable;
|
import docking.widgets.trable.GTrable;
|
||||||
import docking.widgets.trable.GTrableColumnModel;
|
import docking.widgets.trable.GTrableColumnModel;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
|
import ghidra.util.HTMLUtilities;
|
||||||
|
import ghidra.util.NumericUtilities;
|
||||||
import ghidra.util.datastruct.Range;
|
import ghidra.util.datastruct.Range;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
|
|
||||||
|
@ -119,7 +122,34 @@ public class DataVertexPanel extends JPanel {
|
||||||
JComponent jComponent = (JComponent) source;
|
JComponent jComponent = (JComponent) source;
|
||||||
return jComponent.getToolTipText();
|
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("<TABLE>");
|
||||||
|
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("</TABLE>");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String row(Object... cols) {
|
||||||
|
StringBuilder sb = new StringBuilder("<TR>");
|
||||||
|
for (Object col : cols) {
|
||||||
|
String escaped = escapeHtml(col);
|
||||||
|
sb.append("<TD>").append(escaped).append("</TD>");
|
||||||
|
}
|
||||||
|
sb.append("</TR>");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String escapeHtml(Object obj) {
|
||||||
|
return obj == null ? "" : HTMLUtilities.friendlyEncodeHTML(obj.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -185,54 +185,57 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
|
|
||||||
private void createActions() {
|
private void createActions() {
|
||||||
|
|
||||||
new ActionBuilder("Graph Block Flow", getName()).menuPath(MENU_GRAPH, "&Block Flow")
|
new ActionBuilder("Graph Block Flow", getName())
|
||||||
.menuGroup(MENU_GRAPH, "A")
|
.menuPath(MENU_GRAPH, "&Block Flow")
|
||||||
|
.menuGroup("Code Graph", "A")
|
||||||
.onAction(c -> graphBlockFlow())
|
.onAction(c -> graphBlockFlow())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
new ActionBuilder("Graph Code Flow", getName()).menuPath(MENU_GRAPH, "C&ode Flow")
|
new ActionBuilder("Graph Code Flow", getName())
|
||||||
.menuGroup(MENU_GRAPH, "B")
|
.menuPath(MENU_GRAPH, "C&ode Flow")
|
||||||
|
.menuGroup("Code Graph", "B")
|
||||||
.onAction(c -> graphCodeFlow())
|
.onAction(c -> graphCodeFlow())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
new ActionBuilder("Graph Calls Using Default Model", getName())
|
new ActionBuilder("Graph Calls Using Default Model", getName())
|
||||||
.menuPath(MENU_GRAPH, "&Calls")
|
.menuPath(MENU_GRAPH, "&Calls")
|
||||||
.menuGroup(MENU_GRAPH, "C")
|
.menuGroup("Code Graph", "C")
|
||||||
.onAction(c -> createDefaultCallGraph())
|
.onAction(c -> createDefaultCallGraph())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.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");
|
HelpLocation helpLoc = new HelpLocation(getName(), "Data_Reference_Graph");
|
||||||
|
|
||||||
new ActionBuilder("Graph To/From Data References", getName())
|
new ActionBuilder("Graph To/From Data References", getName())
|
||||||
.menuPath(MENU_GRAPH, "Data", "To/From &References")
|
.menuPath(MENU_GRAPH, "Data References", "To/From &References")
|
||||||
.menuGroup(MENU_GRAPH, "Data")
|
.menuGroup("Graph", "Data")
|
||||||
.helpLocation(helpLoc)
|
.helpLocation(helpLoc)
|
||||||
.onAction(c -> graphDataReferences())
|
.onAction(c -> graphDataReferences())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
new ActionBuilder("Graph To Data References", getName())
|
new ActionBuilder("Graph To Data References", getName())
|
||||||
.menuPath(MENU_GRAPH, "Data", "&To References")
|
.menuPath(MENU_GRAPH, "Data References", "&To References")
|
||||||
.menuGroup(MENU_GRAPH, "Data")
|
.menuGroup("Graph", "Data")
|
||||||
.helpLocation(helpLoc)
|
.helpLocation(helpLoc)
|
||||||
.onAction(c -> graphToDataReferences())
|
.onAction(c -> graphToDataReferences())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
new ActionBuilder("Graph From Data References", getName())
|
new ActionBuilder("Graph From Data References", getName())
|
||||||
.menuPath(MENU_GRAPH, "Data", "&From References")
|
.menuPath(MENU_GRAPH, "Data References", "&From References")
|
||||||
.menuGroup(MENU_GRAPH, "Data")
|
.menuGroup("Graph", "Data")
|
||||||
.helpLocation(helpLoc)
|
.helpLocation(helpLoc)
|
||||||
.onAction(c -> graphFromDataReferences())
|
.onAction(c -> graphFromDataReferences())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
reuseGraphAction =
|
reuseGraphAction =
|
||||||
new ToggleActionBuilder("Reuse Graph", getName()).menuPath(MENU_GRAPH, "Reuse Graph")
|
new ToggleActionBuilder("Reuse Graph", getName())
|
||||||
|
.menuPath(MENU_GRAPH, "Reuse Graph")
|
||||||
.menuGroup("Graph Options")
|
.menuGroup("Graph Options")
|
||||||
.selected(reuseGraph)
|
.selected(reuseGraph)
|
||||||
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
||||||
|
@ -240,7 +243,8 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
appendGraphAction =
|
appendGraphAction =
|
||||||
new ToggleActionBuilder("Append Graph", getName()).menuPath(MENU_GRAPH, "Append Graph")
|
new ToggleActionBuilder("Append Graph", getName())
|
||||||
|
.menuPath(MENU_GRAPH, "Append Graph")
|
||||||
.menuGroup("Graph Options")
|
.menuGroup("Graph Options")
|
||||||
.selected(false)
|
.selected(false)
|
||||||
.onAction(c -> updateAppendAndReuseGraph())
|
.onAction(c -> updateAppendAndReuseGraph())
|
||||||
|
@ -297,13 +301,12 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
subUsingGraphActions.add(action);
|
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) {
|
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
|
||||||
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
||||||
.menuPath(MENU_GRAPH, "Calls Using Model", blockModelName)
|
.menuPath(MENU_GRAPH, "Calls Using Model", blockModelName)
|
||||||
.menuGroup(MENU_GRAPH, "C")
|
|
||||||
.helpLocation(helpLoc)
|
.helpLocation(helpLoc)
|
||||||
.onAction(c -> createCallGraphUsing(blockModelName))
|
.onAction(c -> createCallGraphUsing(blockModelName))
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
|
|
|
@ -370,6 +370,10 @@ public class GTrable<T> extends JComponent
|
||||||
columnModel.setWidth(width);
|
columnModel.setWidth(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getRow(Point p) {
|
||||||
|
return p.y / rowHeight;
|
||||||
|
}
|
||||||
|
|
||||||
private void notifySelectedRowConsumers() {
|
private void notifySelectedRowConsumers() {
|
||||||
for (Consumer<Integer> consumer : selectedRowConsumers) {
|
for (Consumer<Integer> consumer : selectedRowConsumers) {
|
||||||
consumer.accept(selectedRow);
|
consumer.accept(selectedRow);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue