GP-2737 - Function Graph - Added support for rendering Area Markers. Added support for a breakpoint margin area in each vertex.

This commit is contained in:
dragonmacher 2023-10-23 10:59:35 -04:00
parent 7e4d2bcfaa
commit 54a240f3b8
11 changed files with 240 additions and 34 deletions

View file

@ -30,6 +30,7 @@ dependencies {
api project(':Base') api project(':Base')
api project(':ByteViewer') api project(':ByteViewer')
api project(':Decompiler') api project(':Decompiler')
api project(':FunctionGraph')
api project(':ProposedUtils') api project(':ProposedUtils')
testImplementation project(path: ':Generic', configuration: 'testArtifacts') testImplementation project(path: ':Generic', configuration: 'testArtifacts')

View file

@ -41,6 +41,9 @@ import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent;
import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.plugin.core.functiongraph.FunctionGraphMarginService;
import ghidra.app.plugin.core.marker.MarginProviderSupplier;
import ghidra.app.plugin.core.marker.MarkerMarginProvider;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.MarkerClickedListener; import ghidra.app.util.viewer.listingpanel.MarkerClickedListener;
import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncDebouncer;
@ -501,6 +504,16 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
} }
} }
private class DefaultMarginProviderSupplier implements MarginProviderSupplier {
@Override
public MarkerMarginProvider createMarginProvider() {
if (markerService != null) {
return markerService.createMarginProvider();
}
return null;
}
}
protected static State computeState(LogicalBreakpoint breakpoint, Program programOrView) { protected static State computeState(LogicalBreakpoint breakpoint, Program programOrView) {
if (programOrView instanceof TraceProgramView view) { if (programOrView instanceof TraceProgramView view) {
return breakpoint.computeStateForTrace(view.getTrace()); return breakpoint.computeStateForTrace(view.getTrace());
@ -735,6 +748,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
private DebuggerControlService controlService; private DebuggerControlService controlService;
// @AutoServiceConsumed via method // @AutoServiceConsumed via method
DecompilerMarginService decompilerMarginService; DecompilerMarginService decompilerMarginService;
// @AutoServiceConsumed via method
private FunctionGraphMarginService functionGraphMarginService;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring; private final AutoService.Wiring autoServiceWiring;
@ -793,6 +808,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
DebuggerPlaceBreakpointDialog placeBreakpointDialog = new DebuggerPlaceBreakpointDialog(); DebuggerPlaceBreakpointDialog placeBreakpointDialog = new DebuggerPlaceBreakpointDialog();
BreakpointsDecompilerMarginProvider decompilerMarginProvider; BreakpointsDecompilerMarginProvider decompilerMarginProvider;
private MarginProviderSupplier functionGraphMarginSupplier =
new DefaultMarginProviderSupplier();
public DebuggerBreakpointMarkerPlugin(PluginTool tool) { public DebuggerBreakpointMarkerPlugin(PluginTool tool) {
super(tool); super(tool);
@ -1038,6 +1055,21 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
} }
} }
@AutoServiceConsumed
private void setFunctionGraphMarginService(
FunctionGraphMarginService functionGraphMarginService) {
if (this.functionGraphMarginService != null) {
this.functionGraphMarginService
.removeMarkerProviderSupplier(functionGraphMarginSupplier);
}
this.functionGraphMarginService = functionGraphMarginService;
if (this.functionGraphMarginService != null) {
this.functionGraphMarginService.addMarkerProviderSupplier(functionGraphMarginSupplier);
}
}
protected void createActions() { protected void createActions() {
actionSetSoftwareBreakpoint = actionSetSoftwareBreakpoint =
new SetBreakpointAction(Set.of(TraceBreakpointKind.SW_EXECUTE)); new SetBreakpointAction(Set.of(TraceBreakpointKind.SW_EXECUTE));

View file

@ -0,0 +1,28 @@
/* ###
* 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.marker;
/**
* Supplies {@link MarkerMarginProvider}s.
*/
public interface MarginProviderSupplier {
/**
* Creates a new marker margin provider.
* @return the provider.
*/
public MarkerMarginProvider createMarginProvider();
}

View file

@ -25,9 +25,7 @@ import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
/** //@formatter:off
* Plugin to manage marker and navigation panels.
*/
@PluginInfo( @PluginInfo(
status = PluginStatus.RELEASED, status = PluginStatus.RELEASED,
packageName = CorePluginPackage.NAME, packageName = CorePluginPackage.NAME,
@ -43,15 +41,17 @@ import ghidra.util.HelpLocation;
"as bookmarks.", "as bookmarks.",
servicesRequired = { CodeViewerService.class, GoToService.class }, servicesRequired = { CodeViewerService.class, GoToService.class },
servicesProvided = { MarkerService.class }, servicesProvided = { MarkerService.class },
eventsConsumed = {}) eventsConsumed = {}
)
//@formatter:on
/**
* Plugin to manage marker and navigation panels.
*/
public class MarkerManagerPlugin extends Plugin { public class MarkerManagerPlugin extends Plugin {
private CodeViewerService codeViewerService; private CodeViewerService codeViewerService;
private MarkerManager markerManager; private MarkerManager markerManager;
/**
* @param tool
*/
public MarkerManagerPlugin(PluginTool tool) { public MarkerManagerPlugin(PluginTool tool) {
super(tool); super(tool);
markerManager = new MarkerManager(this); markerManager = new MarkerManager(this);

View file

@ -32,6 +32,7 @@ import ghidra.app.plugin.core.functiongraph.graph.*;
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex; import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex; import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex;
import ghidra.app.plugin.core.functiongraph.mvc.*; import ghidra.app.plugin.core.functiongraph.mvc.*;
import ghidra.app.plugin.core.marker.MarginProviderSupplier;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.ListingHighlightProvider; import ghidra.app.util.ListingHighlightProvider;
import ghidra.framework.model.*; import ghidra.framework.model.*;
@ -129,8 +130,6 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
new SwingUpdateManager(250, 750, () -> setPendingLocationFromUpdateManager()); new SwingUpdateManager(250, 750, () -> setPendingLocationFromUpdateManager());
clipboardProvider = new FGClipboardProvider(tool, controller); clipboardProvider = new FGClipboardProvider(tool, controller);
ClipboardService service = tool.getService(ClipboardService.class);
setClipboardService(service);
} }
@Override @Override
@ -139,7 +138,7 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
return !isConnected(); return !isConnected();
} }
public void setClipboardService(ClipboardService service) { void setClipboardService(ClipboardService service) {
clipboardService = service; clipboardService = service;
if (clipboardService != null) { if (clipboardService != null) {
clipboardService.registerClipboardContentProvider(clipboardProvider); clipboardService.registerClipboardContentProvider(clipboardProvider);
@ -1136,6 +1135,16 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
controller.setGraphPerspective(info); controller.setGraphPerspective(info);
} }
void addMarkerProviderSupplier(MarginProviderSupplier supplier) {
controller.addMarkerProviderSupplier(supplier);
refreshAndKeepPerspective();
}
void removeMarkerProviderSupplier(MarginProviderSupplier supplier) {
controller.removeMarkerProviderSupplier(supplier);
refreshAndKeepPerspective();
}
//================================================================================================== //==================================================================================================
// Navigatable interface methods // Navigatable interface methods
//================================================================================================== //==================================================================================================
@ -1298,7 +1307,8 @@ public class FGProvider extends VisualGraphComponentProvider<FGVertex, FGEdge, F
} }
@Override @Override
public void removeHighlightProvider(ListingHighlightProvider highlightProvider, Program program) { public void removeHighlightProvider(ListingHighlightProvider highlightProvider,
Program program) {
// currently unsupported // currently unsupported
} }

View file

@ -0,0 +1,42 @@
/* ###
* 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.functiongraph;
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
import ghidra.app.plugin.core.marker.MarginProviderSupplier;
import ghidra.framework.plugintool.ServiceInfo;
/**
* A service that allows clients to add custom margins in the {@link FunctionGraph} UI.
*/
@ServiceInfo(defaultProvider = FunctionGraphPlugin.class)
public interface FunctionGraphMarginService {
/**
* Add a marker margin supplier to Function Graph's primary window. The supplier will be called
* for each node in the graph.
*
* @param supplier the supplier.
*/
public void addMarkerProviderSupplier(MarginProviderSupplier supplier);
/**
* Removes the given margin supplier from the Function Graph's UI.
*
* @param supplier the supplier.
*/
public void removeMarkerProviderSupplier(MarginProviderSupplier supplier);
}

View file

@ -33,6 +33,7 @@ import ghidra.app.plugin.core.colorizer.ColorizingService;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphOptions; import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphOptions;
import ghidra.app.plugin.core.marker.MarginProviderSupplier;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.format.FormatManager;
import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFile;
@ -53,10 +54,13 @@ import ghidra.util.exception.AssertException;
category = PluginCategoryNames.GRAPH, category = PluginCategoryNames.GRAPH,
shortDescription = FunctionGraphPlugin.FUNCTION_GRAPH_NAME, shortDescription = FunctionGraphPlugin.FUNCTION_GRAPH_NAME,
description = "Plugin for show a graphical representation of the code blocks of a function", description = "Plugin for show a graphical representation of the code blocks of a function",
servicesRequired = { GoToService.class, BlockModelService.class, CodeViewerService.class, ProgramManager.class } servicesRequired = { GoToService.class, BlockModelService.class, CodeViewerService.class, ProgramManager.class },
servicesProvided = { FunctionGraphMarginService.class }
) )
//@formatter:on //@formatter:on
public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeListener { public class FunctionGraphPlugin extends ProgramPlugin
implements OptionsChangeListener, FunctionGraphMarginService {
static final String FUNCTION_GRAPH_NAME = "Function Graph"; static final String FUNCTION_GRAPH_NAME = "Function Graph";
static final String OPTIONS_NAME_PATH = static final String OPTIONS_NAME_PATH =
ToolConstants.GRAPH_OPTIONS + Options.DELIMITER + FUNCTION_GRAPH_NAME; ToolConstants.GRAPH_OPTIONS + Options.DELIMITER + FUNCTION_GRAPH_NAME;
@ -111,20 +115,32 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
colorProvider = new ToolBasedColorProvider(this, (ColorizingService) service); colorProvider = new ToolBasedColorProvider(this, (ColorizingService) service);
connectedProvider.refreshAndKeepPerspective(); connectedProvider.refreshAndKeepPerspective();
} }
else if (interfaceClass == MarkerService.class) {
for (FGProvider disconnectedProvider : disconnectedProviders) {
disconnectedProvider.refreshAndKeepPerspective();
}
connectedProvider.refreshAndKeepPerspective();
}
} }
@Override @Override
public void serviceRemoved(Class<?> interfaceClass, Object service) { public void serviceRemoved(Class<?> interfaceClass, Object service) {
if (interfaceClass == ClipboardService.class) { if (interfaceClass == ClipboardService.class) {
connectedProvider.setClipboardService((ClipboardService) service); connectedProvider.setClipboardService(null);
for (FGProvider disconnectedProvider : disconnectedProviders) { for (FGProvider disconnectedProvider : disconnectedProviders) {
disconnectedProvider.setClipboardService((ClipboardService) service); disconnectedProvider.setClipboardService(null);
} }
} }
else if (interfaceClass == ColorizingService.class) { else if (interfaceClass == ColorizingService.class) {
colorProvider = new IndependentColorProvider(tool); colorProvider = new IndependentColorProvider(tool);
connectedProvider.refreshAndKeepPerspective(); connectedProvider.refreshAndKeepPerspective();
} }
else if (interfaceClass == MarkerService.class) {
for (FGProvider disconnectedProvider : disconnectedProviders) {
disconnectedProvider.refreshAndKeepPerspective();
}
connectedProvider.refreshAndKeepPerspective();
}
} }
private List<FGLayoutProvider> loadLayoutProviders() { private List<FGLayoutProvider> loadLayoutProviders() {
@ -194,6 +210,22 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
} }
} }
@Override
public void addMarkerProviderSupplier(MarginProviderSupplier supplier) {
for (FGProvider disconnectedProvider : disconnectedProviders) {
disconnectedProvider.addMarkerProviderSupplier(supplier);
}
connectedProvider.addMarkerProviderSupplier(supplier);
}
@Override
public void removeMarkerProviderSupplier(MarginProviderSupplier supplier) {
for (FGProvider disconnectedProvider : disconnectedProviders) {
disconnectedProvider.removeMarkerProviderSupplier(supplier);
}
connectedProvider.removeMarkerProviderSupplier(supplier);
}
@Override @Override
protected void programActivated(Program program) { protected void programActivated(Program program) {
if (connectedProvider == null) { if (connectedProvider == null) {
@ -431,6 +463,10 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
return colorProvider; return colorProvider;
} }
public <T> T getService(Class<T> serviceClass) {
return tool.getService(serviceClass);
}
public FunctionGraphOptions getFunctionGraphOptions() { public FunctionGraphOptions getFunctionGraphOptions() {
return functionGraphOptions; return functionGraphOptions;
} }

View file

@ -73,10 +73,9 @@ public class FGVertexListingPanel extends ListingPanel {
Color color = options.getDefaultVertexBackgroundColor(); Color color = options.getDefaultVertexBackgroundColor();
setTextBackgroundColor(color); setTextBackgroundColor(color);
// Custom colors are in use when the ColorizingService is not installed.
FGColorProvider colorProvider = controller.getColorProvider(); FGColorProvider colorProvider = controller.getColorProvider();
if (!colorProvider.isUsingCustomColors()) { enablePropertyBasedColorModel(!colorProvider.isUsingCustomColors());
enablePropertyBasedColorModel(true); // turn on user colors in the graph
}
} }
@Override @Override
@ -95,7 +94,7 @@ public class FGVertexListingPanel extends ListingPanel {
* our methods that calculate preferred size from going 'out to lunch' when attempting to * our methods that calculate preferred size from going 'out to lunch' when attempting to
* examine the entire program instead of just the given view. * examine the entire program instead of just the given view.
* *
* @param model The listing model needed by the layout model * * @param model The listing model needed by the layout model
* @return the new model adapter * @return the new model adapter
*/ */
@Override @Override

View file

@ -23,10 +23,12 @@ import java.awt.event.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.BevelBorder; import javax.swing.border.BevelBorder;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.event.ChangeListener;
import docking.ActionContext; import docking.ActionContext;
import docking.GenericHeader; import docking.GenericHeader;
@ -41,16 +43,19 @@ import generic.theme.GColor;
import generic.theme.GIcon; import generic.theme.GIcon;
import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Tooltips; import generic.theme.GThemeDefaults.Colors.Tooltips;
import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
import ghidra.app.plugin.core.codebrowser.hover.ListingHoverService; import ghidra.app.plugin.core.codebrowser.hover.ListingHoverService;
import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin; import ghidra.app.plugin.core.functiongraph.FunctionGraphPlugin;
import ghidra.app.plugin.core.functiongraph.graph.FGEdge; import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
import ghidra.app.plugin.core.functiongraph.mvc.FGController; import ghidra.app.plugin.core.functiongraph.mvc.FGController;
import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphOptions; import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphOptions;
import ghidra.app.plugin.core.marker.MarginProviderSupplier;
import ghidra.app.plugin.core.marker.MarkerMarginProvider;
import ghidra.app.services.HoverService; import ghidra.app.services.HoverService;
import ghidra.app.services.MarkerService;
import ghidra.app.util.AddEditDialog; import ghidra.app.util.AddEditDialog;
import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingHoverProvider; import ghidra.app.util.viewer.listingpanel.*;
import ghidra.app.util.viewer.listingpanel.ListingModel;
import ghidra.app.util.viewer.util.AddressIndexMap; import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.app.util.viewer.util.FieldNavigator; import ghidra.app.util.viewer.util.FieldNavigator;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
@ -105,6 +110,12 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
} }
}; };
private final ChangeListener markerChangeListener = e -> {
if (controller != null) {
controller.repaint();
}
};
ListingGraphComponentPanel(final FGVertex vertex, final FGController controller, ListingGraphComponentPanel(final FGVertex vertex, final FGController controller,
PluginTool tool, Program program, AddressSetView addressSet) { PluginTool tool, Program program, AddressSetView addressSet) {
super(controller, vertex); super(controller, vertex);
@ -122,6 +133,21 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
.addButtonPressedListener(controller.getSharedHighlighterButtonPressedListener()); .addButtonPressedListener(controller.getSharedHighlighterButtonPressedListener());
listingPanel.setStringSelectionListener(controller.getSharedStringSelectionListener()); listingPanel.setStringSelectionListener(controller.getSharedStringSelectionListener());
MarkerService markerService = controller.getService(MarkerService.class);
if (markerService != null) {
ListingBackgroundColorModel colorModel = new MarkerServiceBackgroundColorModel(
markerService, listingPanel.getAddressIndexMap());
listingPanel.setBackgroundColorModel(colorModel);
markerService.addChangeListener(markerChangeListener);
}
// The margin providers may be installed by services other than the MarkerService
Set<MarginProviderSupplier> marginProviders = controller.getMarginProviderSuppliers();
for (MarginProviderSupplier supplier : marginProviders) {
MarkerMarginProvider marginProvider = supplier.createMarginProvider();
listingPanel.addMarginProvider(marginProvider);
}
fieldPanel = listingPanel.getFieldPanel(); fieldPanel = listingPanel.getFieldPanel();
fieldPanel.setCursorOn(false); fieldPanel.setCursorOn(false);
@ -676,6 +702,11 @@ public class ListingGraphComponentPanel extends AbstractGraphComponentPanel {
// references and removing the data from Jung's graph // references and removing the data from Jung's graph
// //
MarkerService markerService = controller.getService(MarkerService.class);
if (markerService != null) {
markerService.removeChangeListener(markerChangeListener);
}
removeAll(); removeAll();
listingPanel.setStringSelectionListener(null); listingPanel.setStringSelectionListener(null);

View file

@ -18,9 +18,8 @@ package ghidra.app.plugin.core.functiongraph.mvc;
import java.awt.*; import java.awt.*;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -40,6 +39,7 @@ import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph; import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
import ghidra.app.plugin.core.functiongraph.graph.vertex.*; import ghidra.app.plugin.core.functiongraph.graph.vertex.*;
import ghidra.app.plugin.core.marker.MarginProviderSupplier;
import ghidra.app.services.ButtonPressedListener; import ghidra.app.services.ButtonPressedListener;
import ghidra.app.services.CodeViewerService; import ghidra.app.services.CodeViewerService;
import ghidra.app.util.ListingHighlightProvider; import ghidra.app.util.ListingHighlightProvider;
@ -58,6 +58,8 @@ import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
public class FGController implements ProgramLocationListener, ProgramSelectionListener { public class FGController implements ProgramLocationListener, ProgramSelectionListener {
@ -90,6 +92,9 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
// dummy // dummy
}; };
private WeakSet<MarginProviderSupplier> marginProviders =
WeakDataStructureFactory.createSingleThreadAccessWeakSet();
public FGController(FGProvider provider, FunctionGraphPlugin plugin) { public FGController(FGProvider provider, FunctionGraphPlugin plugin) {
this.provider = provider; this.provider = provider;
this.plugin = plugin; this.plugin = plugin;
@ -421,7 +426,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
//================================================================================================== //==================================================================================================
//================================================================================================== //==================================================================================================
// Methods call by the providers // Methods called by the providers
//================================================================================================== //==================================================================================================
public void programClosed(Program program) { public void programClosed(Program program) {
@ -830,8 +835,16 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
} }
} }
public void addMarkerProviderSupplier(MarginProviderSupplier supplier) {
marginProviders.add(supplier);
}
public void removeMarkerProviderSupplier(MarginProviderSupplier supplier) {
marginProviders.add(supplier);
}
//================================================================================================== //==================================================================================================
// Methods call by the model // Methods called by the model
//================================================================================================== //==================================================================================================
public void setFunctionGraphData(FGData data) { public void setFunctionGraphData(FGData data) {
@ -916,7 +929,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
} }
//================================================================================================== //==================================================================================================
// Methods call by the vertices (actions and such) // Methods called by the vertices (actions and such)
//================================================================================================== //==================================================================================================
/** Zooms so that the graph will fit completely into the size of the primary viewer */ /** Zooms so that the graph will fit completely into the size of the primary viewer */
@ -1007,6 +1020,10 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
return plugin.getColorProvider(); return plugin.getColorProvider();
} }
public <T> T getService(Class<T> serviceClass) {
return plugin.getService(serviceClass);
}
/** /**
* Update the graph's notion of the current location based upon that of the Tool. This method is * Update the graph's notion of the current location based upon that of the Tool. This method is
* meant to be called from internal mutative operations. * meant to be called from internal mutative operations.
@ -1063,6 +1080,10 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
}; };
} }
public Set<MarginProviderSupplier> getMarginProviderSuppliers() {
return Collections.unmodifiableSet(marginProviders);
}
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================

View file

@ -105,8 +105,12 @@ public class ServiceManager {
List<Object> list = List<Object> list =
servicesByInterface.computeIfAbsent(interfaceClass, (k) -> new ArrayList<>()); servicesByInterface.computeIfAbsent(interfaceClass, (k) -> new ArrayList<>());
if (list.contains(service)) { if (list.contains(service)) {
// Note: this can happen if a plugin implements a service it declares and also calls
// Plugin.registerServiceProvided(), which is a mistake, since the plugin will get
// auto-wired when it implements the service interface.
throw new AssertException( throw new AssertException(
"Same Service implementation cannot be " + "added more than once"); "The same Service implementation cannot be added more than once. Interface: " +
interfaceClass + ". Service: " + service);
} }
list.add(service); list.add(service);
@ -120,6 +124,8 @@ public class ServiceManager {
/** /**
* Remove the service from the tool. * Remove the service from the tool.
* @param interfaceClass the service interface
* @param service the service implementation
*/ */
public void removeService(Class<?> interfaceClass, Object service) { public void removeService(Class<?> interfaceClass, Object service) {
List<Object> list = servicesByInterface.get(interfaceClass); List<Object> list = servicesByInterface.get(interfaceClass);