mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-09 14:08:03 -05:00
GP-6085 - Flow Arrows - Change to a service-based system for margin providers to allow flow arrows in snapshots
This commit is contained in:
@@ -85,7 +85,8 @@ import ghidra.util.Msg;
|
||||
servicesRequired = {
|
||||
DebuggerLogicalBreakpointService.class,
|
||||
MarkerService.class,
|
||||
})
|
||||
}
|
||||
)
|
||||
public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
|
||||
private static final Color COLOR_BREAKPOINT_ENABLED_MARKER =
|
||||
@@ -496,7 +497,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
new ProgramLocationActionContext(null, location.getProgram(),
|
||||
new ProgramLocation(location.getProgram(), location.getAddr()), null, null);
|
||||
if (contextCanManipulateBreakpoints(context)) {
|
||||
doToggleBreakpointsAt(ToggleBreakpointAction.NAME, context);
|
||||
doToggleBreakpointsAt(AbstractToggleBreakpointAction.NAME, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,28 +754,32 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND,
|
||||
description = "Whether or not to color background for memory at an enabled breakpoint",
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
help = @HelpInfo(anchor = "colors")
|
||||
)
|
||||
private boolean breakpointEnabledColoringBackground =
|
||||
DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_COLORING_BACKGROUND;
|
||||
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND,
|
||||
description = "Whether or not to color background for memory at a disabled breakpoint",
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
help = @HelpInfo(anchor = "colors")
|
||||
)
|
||||
private boolean breakpointDisabledColoringBackground =
|
||||
DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND;
|
||||
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND,
|
||||
description = "Whether or not to color background for memory at an enabled, but ineffective, breakpoint",
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
help = @HelpInfo(anchor = "colors")
|
||||
)
|
||||
private boolean breakpointIneffEnColoringBackground =
|
||||
DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND;
|
||||
|
||||
@AutoOptionDefined(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND,
|
||||
description = "Whether or not to color background for memory at an disabled, but ineffective, breakpoint",
|
||||
help = @HelpInfo(anchor = "colors"))
|
||||
help = @HelpInfo(anchor = "colors")
|
||||
)
|
||||
private boolean breakpointIneffDisColoringBackground =
|
||||
DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND;
|
||||
|
||||
@@ -832,7 +837,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND)
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_ENABLED_BREAKPOINT_COLORING_BACKGROUND
|
||||
)
|
||||
private void setEnabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setEnabledColoringBackground(breakpointColoringBackground);
|
||||
@@ -840,7 +846,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND)
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_DISABLED_BREAKPOINT_COLORING_BACKGROUND
|
||||
)
|
||||
private void setDisabledBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setDisabledColoringBackground(breakpointColoringBackground);
|
||||
@@ -848,7 +855,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND)
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND
|
||||
)
|
||||
private void setIneffectiveEBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setIneffectiveEnabledColoringBackground(breakpointColoringBackground);
|
||||
@@ -856,7 +864,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND)
|
||||
name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND
|
||||
)
|
||||
private void setIneffectiveDBreakpointMarkerBackground(boolean breakpointColoringBackground) {
|
||||
for (BreakpointMarkerSets markers : markersByProgram.values()) {
|
||||
markers.setIneffectiveDisabledColoringBackground(breakpointColoringBackground);
|
||||
@@ -1089,7 +1098,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin {
|
||||
actionDisableBreakpoint = new DisableBreakpointAction();
|
||||
actionClearBreakpoint = new ClearBreakpointAction();
|
||||
|
||||
tool.setMenuGroup(new String[] { SetBreakpointAction.NAME }, SetBreakpointAction.GROUP);
|
||||
tool.setMenuGroup(new String[] { AbstractSetBreakpointAction.NAME },
|
||||
SetBreakpointAction.GROUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -198,7 +198,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
|
||||
}
|
||||
|
||||
@Override
|
||||
public void locationChanged(CodeViewerProvider provider, ProgramLocation location) {
|
||||
public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation location) {
|
||||
// TODO: Fix cursor?
|
||||
// Do not fire ProgramLocationPluginEvent.
|
||||
if (provider == connectedProvider) {
|
||||
@@ -207,7 +207,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
|
||||
public void broadcastSelectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
|
||||
if (provider != connectedProvider) {
|
||||
return;
|
||||
}
|
||||
@@ -220,7 +220,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
|
||||
}
|
||||
|
||||
@Override
|
||||
public void highlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
|
||||
public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
|
||||
if (provider != connectedProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.listing;
|
||||
|
||||
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.ICON_REGISTER_MARKER;
|
||||
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
@@ -130,13 +130,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
||||
}
|
||||
}
|
||||
|
||||
protected class MarkerSetChangeListener implements ChangeListener {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
getListingPanel().getFieldPanel().repaint();
|
||||
}
|
||||
}
|
||||
|
||||
protected class ForStaticSyncMappingChangeListener
|
||||
implements DebuggerStaticMappingChangeListener {
|
||||
@Override
|
||||
@@ -335,7 +328,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
||||
protected final JLabel trackingLabel = new JLabel();
|
||||
|
||||
protected final MultiBlendedListingBackgroundColorModel colorModel;
|
||||
protected final MarkerSetChangeListener markerChangeListener = new MarkerSetChangeListener();
|
||||
protected MarkerServiceBackgroundColorModel markerServiceColorModel;
|
||||
protected MarkerMarginProvider markerMarginProvider;
|
||||
protected MarkerOverviewProvider markerOverviewProvider;
|
||||
@@ -573,28 +565,13 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
||||
|
||||
@AutoServiceConsumed
|
||||
private void setMarkerService(MarkerService markerService) {
|
||||
if (this.markerService != null) {
|
||||
this.markerService.removeChangeListener(markerChangeListener);
|
||||
removeMarginProvider(markerMarginProvider);
|
||||
markerMarginProvider = null;
|
||||
removeOverviewProvider(markerOverviewProvider);
|
||||
markerOverviewProvider = null;
|
||||
}
|
||||
ListingPanel listingPanel = getListingPanel();
|
||||
listingPanel.setMarkerService(markerService);
|
||||
|
||||
removeOldStaticTrackingMarker();
|
||||
this.markerService = markerService;
|
||||
createNewStaticTrackingMarker();
|
||||
updateMarkerServiceColorModel();
|
||||
|
||||
if (this.markerService != null && !isMainListing()) {
|
||||
// NOTE: Connected provider marker listener is taken care of by CodeBrowserPlugin
|
||||
this.markerService.addChangeListener(markerChangeListener);
|
||||
}
|
||||
if (this.markerService != null) {
|
||||
markerMarginProvider = markerService.createMarginProvider();
|
||||
addMarginProvider(markerMarginProvider);
|
||||
markerOverviewProvider = markerService.createOverviewProvider();
|
||||
addOverviewProvider(markerOverviewProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoServiceConsumed
|
||||
|
||||
@@ -24,7 +24,7 @@ import javax.swing.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@@ -35,7 +35,7 @@ import ghidra.util.task.SwingUpdateManager;
|
||||
* Overview bar component. Uses color to indicate various snap-based properties for a program.
|
||||
* Uses an {@link TimeOverviewColorService} to get the appropriate color for a snaps.
|
||||
*/
|
||||
public class TimeOverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
public class TimeOverviewColorComponent extends JPanel implements ListingOverviewProvider {
|
||||
private static final Color DEFAULT_COLOR = Color.GRAY;
|
||||
protected TimeOverviewColorService service;
|
||||
private Color[] colorsByPixel = new Color[0];
|
||||
@@ -101,7 +101,7 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
|
||||
stop = start;
|
||||
start = tmp;
|
||||
}
|
||||
span = Lifespan.span(start, stop);
|
||||
span = Lifespan.span(start, stop);
|
||||
}
|
||||
plugin.setLifespan(span);
|
||||
enableDrag = false;
|
||||
@@ -113,6 +113,11 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
|
||||
actions = service.getActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
uninstallActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs actions for this component
|
||||
*/
|
||||
@@ -233,13 +238,13 @@ public class TimeOverviewColorComponent extends JPanel implements OverviewProvid
|
||||
public Lifespan getLifespan() {
|
||||
return service.getBounds();
|
||||
}
|
||||
|
||||
|
||||
public void setLifespan(Lifespan bounds) {
|
||||
service.setBounds(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program, AddressIndexMap map) {
|
||||
public void screenDataChanged(Program program, AddressIndexMap map) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
|
||||
@@ -221,8 +221,8 @@ public class TimeOverviewColorPlugin extends AbstractDebuggerPlugin {
|
||||
|
||||
private void uninstallOverview(TimeOverviewColorService overviewColorService) {
|
||||
TimeOverviewColorComponent overviewComponent = activeServices.get(overviewColorService);
|
||||
overviewComponent.uninstallActions();
|
||||
listingService.removeOverviewProvider(overviewComponent);
|
||||
overviewComponent.dispose();
|
||||
activeServices.remove(overviewColorService);
|
||||
overviewColorService.setTrace(null);
|
||||
}
|
||||
|
||||
@@ -19,11 +19,9 @@ import java.awt.Color;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.widgets.fieldpanel.*;
|
||||
@@ -31,7 +29,6 @@ import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.FieldSelection;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.events.ProgramHighlightPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
@@ -72,24 +69,15 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
private static final GColor CURRENT_LINE_HIGHLIGHT_COLOR = new GColor("color.bg.currentline.listing");
|
||||
//@formatter:on
|
||||
|
||||
// - Icon -
|
||||
private static final Icon CURSOR_LOC_ICON =
|
||||
new GIcon("icon.plugin.codebrowser.cursor.location");
|
||||
protected final P connectedProvider;
|
||||
protected List<P> disconnectedProviders = new ArrayList<>();
|
||||
protected FormatManager formatMgr;
|
||||
protected ViewManagerService viewManager;
|
||||
private MarkerService markerService;
|
||||
|
||||
protected AddressSetView currentView = ImmutableAddressSet.EMPTY_SET;
|
||||
protected Program currentProgram;
|
||||
private boolean selectionChanging;
|
||||
private MarkerSet currentSelectionMarkers;
|
||||
private MarkerSet currentHighlightMarkers;
|
||||
private MarkerSet currentCursorMarkers;
|
||||
private ChangeListener markerChangeListener;
|
||||
|
||||
private Color cursorHighlightColor;
|
||||
private boolean isHighlightCursorLine;
|
||||
private ProgramDropProvider dndProvider;
|
||||
|
||||
public AbstractCodeBrowserPlugin(PluginTool tool) {
|
||||
@@ -113,7 +101,6 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
initMiscellaneousOptions();
|
||||
displayOptions.addOptionsChangeListener(this);
|
||||
fieldOptions.addOptionsChangeListener(this);
|
||||
markerChangeListener = new MarkerChangeListener(connectedProvider);
|
||||
}
|
||||
|
||||
protected abstract P createProvider(FormatManager formatManager, boolean isConnected);
|
||||
@@ -138,16 +125,14 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
|
||||
private void viewUpdated() {
|
||||
updateBackgroundColorModel();
|
||||
setHighlight(connectedProvider.getHighlight());
|
||||
setSelection(connectedProvider.getSelection());
|
||||
connectedProvider.setHighlight(connectedProvider.getHighlight());
|
||||
setConnectedProviderSelection(connectedProvider.getSelection());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
markerService = tool.getService(MarkerService.class);
|
||||
if (markerService != null) {
|
||||
markerService.addChangeListener(markerChangeListener);
|
||||
}
|
||||
MarkerService markerService = tool.getService(MarkerService.class);
|
||||
setMarkerService(markerService);
|
||||
updateBackgroundColorModel();
|
||||
|
||||
if (viewManager == null) {
|
||||
@@ -161,20 +146,31 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
provider.setClipboardService(clipboardService);
|
||||
}
|
||||
}
|
||||
|
||||
ListingMarginProviderService[] marginServices =
|
||||
tool.getServices(ListingMarginProviderService.class);
|
||||
for (ListingMarginProviderService marginService : marginServices) {
|
||||
connectedProvider.addMarginService(marginService);
|
||||
}
|
||||
|
||||
ListingOverviewProviderService[] overviewServices =
|
||||
tool.getServices(ListingOverviewProviderService.class);
|
||||
for (ListingOverviewProviderService service : overviewServices) {
|
||||
connectedProvider.addOverviewService(service);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateBackgroundColorModel() {
|
||||
ListingPanel listingPanel = connectedProvider.getListingPanel();
|
||||
if (markerService != null) {
|
||||
AddressIndexMap indexMap = connectedProvider.getListingPanel().getAddressIndexMap();
|
||||
listingPanel.setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerService, indexMap));
|
||||
}
|
||||
else {
|
||||
listingPanel.setBackgroundColorModel(null);
|
||||
}
|
||||
|
||||
// Note: this should update all providers, not just the connected provider
|
||||
updateBackgroundColorModel(connectedProvider);
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
updateBackgroundColorModel(provider);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateBackgroundColorModel(CodeViewerProvider provider) {
|
||||
ListingPanel listingPanel = provider.getListingPanel();
|
||||
listingPanel.updateBackgroundColorModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,20 +178,46 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
P newProvider = createProvider(formatMgr.createClone(), false);
|
||||
newProvider.setClipboardService(tool.getService(ClipboardService.class));
|
||||
|
||||
ListingPanel listingPanel = newProvider.getListingPanel();
|
||||
FieldPanel fieldPanel = listingPanel.getFieldPanel();
|
||||
List<ListingPanel> listingPanels = List.of(listingPanel);
|
||||
List<FieldPanel> fieldPanels = List.of(fieldPanel);
|
||||
initPanelOptions(listingPanels, fieldPanels);
|
||||
|
||||
disconnectedProviders.add(newProvider);
|
||||
if (dndProvider != null) {
|
||||
newProvider.addProgramDropProvider(dndProvider);
|
||||
}
|
||||
tool.showComponentProvider(newProvider, true);
|
||||
|
||||
ListingHoverService[] hoverServices = tool.getServices(ListingHoverService.class);
|
||||
for (ListingHoverService hoverService : hoverServices) {
|
||||
newProvider.getListingPanel().addHoverService(hoverService);
|
||||
listingPanel.addHoverService(hoverService);
|
||||
}
|
||||
|
||||
ListingMarginProviderService[] marginServices =
|
||||
tool.getServices(ListingMarginProviderService.class);
|
||||
for (ListingMarginProviderService service : marginServices) {
|
||||
newProvider.addMarginService(service);
|
||||
}
|
||||
|
||||
ListingOverviewProviderService[] overviewServices =
|
||||
tool.getServices(ListingOverviewProviderService.class);
|
||||
for (ListingOverviewProviderService service : overviewServices) {
|
||||
newProvider.addOverviewService(service);
|
||||
}
|
||||
|
||||
MarkerService markerService = tool.getService(MarkerService.class);
|
||||
listingPanel.setMarkerService(markerService);
|
||||
|
||||
updateBackgroundColorModel(newProvider);
|
||||
|
||||
tool.showComponentProvider(newProvider, true);
|
||||
|
||||
return newProvider;
|
||||
}
|
||||
|
||||
protected void setHighlight(FieldSelection highlight) {
|
||||
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
|
||||
// this is for tool highlights coming in to the plugin
|
||||
protected void setConnectedProviderHighlight(FieldSelection highlight) {
|
||||
|
||||
if (highlight != null && !highlight.isEmpty()) {
|
||||
ListingPanel listingPanel = connectedProvider.getListingPanel();
|
||||
@@ -204,17 +226,9 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
|
||||
firePluginEvent(
|
||||
new ProgramHighlightPluginEvent(this.getName(), programHighlight, currentProgram));
|
||||
|
||||
if (highlightMarkers != null) {
|
||||
highlightMarkers.clearAll();
|
||||
highlightMarkers.add(programHighlight);
|
||||
}
|
||||
}
|
||||
else {
|
||||
connectedProvider.setHighlight(new ProgramSelection());
|
||||
if (highlightMarkers != null) {
|
||||
highlightMarkers.clearAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,10 +243,11 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
viewManager = (ViewManagerService) service;
|
||||
setView(viewManager.getCurrentView());
|
||||
}
|
||||
if (interfaceClass == MarkerService.class && markerService == null) {
|
||||
markerService = tool.getService(MarkerService.class);
|
||||
markerService.addChangeListener(markerChangeListener);
|
||||
if (interfaceClass == MarkerService.class) {
|
||||
MarkerService markerService = tool.getService(MarkerService.class);
|
||||
setMarkerService(markerService);
|
||||
updateBackgroundColorModel();
|
||||
|
||||
if (viewManager != null) {
|
||||
viewUpdated();
|
||||
}
|
||||
@@ -248,6 +263,32 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
otherPanel.addHoverService(hoverService);
|
||||
}
|
||||
}
|
||||
if (interfaceClass == ListingMarginProviderService.class) {
|
||||
ListingMarginProviderService marginService = (ListingMarginProviderService) service;
|
||||
connectedProvider.addMarginService(marginService);
|
||||
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
provider.addMarginService(marginService);
|
||||
}
|
||||
}
|
||||
if (interfaceClass == ListingOverviewProviderService.class) {
|
||||
ListingOverviewProviderService overviewService =
|
||||
(ListingOverviewProviderService) service;
|
||||
connectedProvider.addOverviewService(overviewService);
|
||||
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
provider.addOverviewService(overviewService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setMarkerService(MarkerService markerService) {
|
||||
ListingPanel listingPanel = connectedProvider.getListingPanel();
|
||||
listingPanel.setMarkerService(markerService);
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
listingPanel = provider.getListingPanel();
|
||||
listingPanel.setMarkerService(markerService);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -256,42 +297,55 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
viewManager = null;
|
||||
setView(currentProgram.getMemory());
|
||||
}
|
||||
if (service == markerService) {
|
||||
markerService.removeChangeListener(markerChangeListener);
|
||||
clearMarkers(currentProgram);
|
||||
markerService = null;
|
||||
if (interfaceClass == MarkerService.class) {
|
||||
setMarkerService(null);
|
||||
connectedProvider.clearMarkers(currentProgram);
|
||||
updateBackgroundColorModel();
|
||||
}
|
||||
if (interfaceClass == ListingHoverService.class) {
|
||||
ListingHoverService hoverService = (ListingHoverService) service;
|
||||
connectedProvider.getListingPanel().removeHoverService(hoverService);
|
||||
connectedProvider.removeHoverService(hoverService);
|
||||
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
provider.getListingPanel().removeHoverService(hoverService);
|
||||
provider.removeHoverService(hoverService);
|
||||
}
|
||||
ListingPanel otherPanel = connectedProvider.getOtherPanel();
|
||||
if (otherPanel != null) {
|
||||
otherPanel.removeHoverService(hoverService);
|
||||
}
|
||||
if (interfaceClass == ListingMarginProviderService.class) {
|
||||
ListingMarginProviderService marginService = (ListingMarginProviderService) service;
|
||||
connectedProvider.removeMarginService(marginService);
|
||||
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
provider.removeMarginService(marginService);
|
||||
}
|
||||
}
|
||||
if (interfaceClass == ListingOverviewProviderService.class) {
|
||||
ListingOverviewProviderService overviewService =
|
||||
(ListingOverviewProviderService) service;
|
||||
connectedProvider.removeOverviewService(overviewService);
|
||||
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
provider.removeOverviewService(overviewService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOverviewProvider(OverviewProvider overviewProvider) {
|
||||
public void addOverviewProvider(ListingOverviewProvider overviewProvider) {
|
||||
connectedProvider.addOverviewProvider(overviewProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMarginProvider(MarginProvider marginProvider) {
|
||||
public void addMarginProvider(ListingMarginProvider marginProvider) {
|
||||
connectedProvider.addMarginProvider(marginProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOverviewProvider(OverviewProvider overviewProvider) {
|
||||
public void removeOverviewProvider(ListingOverviewProvider overviewProvider) {
|
||||
connectedProvider.removeOverviewProvider(overviewProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMarginProvider(MarginProvider marginProvider) {
|
||||
public void removeMarginProvider(ListingMarginProvider marginProvider) {
|
||||
connectedProvider.removeMarginProvider(marginProvider);
|
||||
}
|
||||
|
||||
@@ -338,10 +392,6 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
connectedProvider.setHighlightProvider(highlightProvider, highlightProgram);
|
||||
}
|
||||
|
||||
protected void updateHighlightProvider() {
|
||||
connectedProvider.updateHighlightProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListingPanel(ListingPanel lp) {
|
||||
connectedProvider.setOtherPanel(lp);
|
||||
@@ -379,7 +429,7 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
if (currentProgram != null) {
|
||||
currentProgram.removeListener(this);
|
||||
}
|
||||
clearMarkers(currentProgram);
|
||||
connectedProvider.clearMarkers(currentProgram);
|
||||
formatMgr.dispose();
|
||||
removeProvider(connectedProvider);
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
@@ -391,68 +441,94 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
ListingPanel listingPanel = connectedProvider.getListingPanel();
|
||||
if (options.getName().equals(GhidraOptions.CATEGORY_BROWSER_FIELDS)) {
|
||||
if (!options.getName().equals(GhidraOptions.CATEGORY_BROWSER_FIELDS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FieldPanel fieldPanel = listingPanel.getFieldPanel();
|
||||
if (optionName.equals(GhidraOptions.OPTION_SELECTION_COLOR)) {
|
||||
Color color = ((Color) newValue);
|
||||
fieldPanel.setSelectionColor(color);
|
||||
MarkerSet selectionMarkers = getSelectionMarkers(currentProgram);
|
||||
if (selectionMarkers != null) {
|
||||
selectionMarkers.setMarkerColor(color);
|
||||
}
|
||||
ListingPanel otherPanel = connectedProvider.getOtherPanel();
|
||||
if (otherPanel != null) {
|
||||
otherPanel.getFieldPanel().setSelectionColor(color);
|
||||
}
|
||||
}
|
||||
else if (optionName.equals(GhidraOptions.OPTION_HIGHLIGHT_COLOR)) {
|
||||
Color color = ((Color) newValue);
|
||||
fieldPanel.setHighlightColor(color);
|
||||
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
|
||||
if (highlightMarkers != null) {
|
||||
highlightMarkers.setMarkerColor(color);
|
||||
}
|
||||
}
|
||||
else if (optionName.equals(CURSOR_COLOR_OPTIONS_NAME)) {
|
||||
Color color = ((Color) newValue);
|
||||
fieldPanel.setFocusedCursorColor(color);
|
||||
}
|
||||
else if (optionName.equals(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME)) {
|
||||
Color color = ((Color) newValue);
|
||||
fieldPanel.setNonFocusCursorColor(color);
|
||||
}
|
||||
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)) {
|
||||
cursorHighlightColor = (Color) newValue;
|
||||
if (currentCursorMarkers != null) {
|
||||
currentCursorMarkers.setMarkerColor(cursorHighlightColor);
|
||||
}
|
||||
}
|
||||
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE)) {
|
||||
isHighlightCursorLine = (Boolean) newValue;
|
||||
if (currentCursorMarkers != null) {
|
||||
currentCursorMarkers.setColoringBackground(isHighlightCursorLine);
|
||||
}
|
||||
}
|
||||
else if (optionName.equals(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME)) {
|
||||
fieldPanel.setHorizontalScrollingEnabled((Boolean) newValue);
|
||||
}
|
||||
List<ListingPanel> listingPanels = allListingPanels();
|
||||
List<FieldPanel> fieldPanels = allFieldPanels();
|
||||
if (optionName.equals(GhidraOptions.OPTION_SELECTION_COLOR)) {
|
||||
Color color = ((Color) newValue);
|
||||
onListingPanels(listingPanels, lp -> lp.setSelectionColor(color));
|
||||
}
|
||||
else if (optionName.equals(GhidraOptions.OPTION_HIGHLIGHT_COLOR)) {
|
||||
Color color = ((Color) newValue);
|
||||
onListingPanels(listingPanels, lp -> lp.setHighlightColor(color));
|
||||
}
|
||||
else if (optionName.equals(CURSOR_COLOR_OPTIONS_NAME)) {
|
||||
Color color = ((Color) newValue);
|
||||
onFieldPanels(fieldPanels, fp -> fp.setFocusedCursorColor(color));
|
||||
}
|
||||
else if (optionName.equals(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME)) {
|
||||
Color color = ((Color) newValue);
|
||||
onFieldPanels(fieldPanels, fp -> fp.setNonFocusCursorColor(color));
|
||||
}
|
||||
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)) {
|
||||
Color color = (Color) newValue;
|
||||
onListingPanels(listingPanels, lp -> lp.setCursorHighlightColor(color));
|
||||
}
|
||||
else if (optionName.equals(GhidraOptions.HIGHLIGHT_CURSOR_LINE)) {
|
||||
Boolean doHighlight = (Boolean) newValue;
|
||||
onListingPanels(listingPanels, lp -> lp.setHighlightCursorLineEnabled(doHighlight));
|
||||
}
|
||||
else if (optionName.equals(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME)) {
|
||||
Boolean doScroll = (Boolean) newValue;
|
||||
onFieldPanels(fieldPanels, fp -> fp.setHorizontalScrollingEnabled(doScroll));
|
||||
}
|
||||
}
|
||||
|
||||
protected void onFieldPanels(List<FieldPanel> panels, Consumer<FieldPanel> c) {
|
||||
for (FieldPanel fp : panels) {
|
||||
c.accept(fp);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onListingPanels(List<ListingPanel> listingPanels, Consumer<ListingPanel> c) {
|
||||
for (ListingPanel lp : listingPanels) {
|
||||
c.accept(lp);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ListingPanel> allListingPanels() {
|
||||
|
||||
List<ListingPanel> results = new ArrayList<>();
|
||||
results.add(connectedProvider.getListingPanel());
|
||||
|
||||
ListingPanel otherPanel = connectedProvider.getOtherPanel();
|
||||
if (otherPanel != null) {
|
||||
results.add(otherPanel);
|
||||
}
|
||||
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
results.add(provider.getListingPanel());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<FieldPanel> allFieldPanels() {
|
||||
List<FieldPanel> results = new ArrayList<>();
|
||||
|
||||
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
|
||||
results.add(fieldPanel);
|
||||
|
||||
ListingPanel otherPanel = connectedProvider.getOtherPanel();
|
||||
if (otherPanel != null) {
|
||||
FieldPanel otherFieldPanel = otherPanel.getFieldPanel();
|
||||
results.add(otherFieldPanel);
|
||||
}
|
||||
|
||||
for (CodeViewerProvider provider : disconnectedProviders) {
|
||||
fieldPanel = provider.getListingPanel().getFieldPanel();
|
||||
results.add(fieldPanel);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
|
||||
public void broadcastSelectionChanged(CodeViewerProvider provider, ProgramSelection selection) {
|
||||
if (provider == connectedProvider) {
|
||||
MarkerSet selectionMarkers = getSelectionMarkers(currentProgram);
|
||||
if (selectionMarkers != null) {
|
||||
selectionMarkers.clearAll();
|
||||
}
|
||||
if (selection != null) {
|
||||
if (selectionMarkers != null) {
|
||||
selectionMarkers.add(selection);
|
||||
}
|
||||
}
|
||||
if (!selectionChanging) {
|
||||
tool.firePluginEvent(new ProgramSelectionPluginEvent(getName(), selection,
|
||||
connectedProvider.getProgram()));
|
||||
@@ -460,88 +536,21 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
}
|
||||
}
|
||||
|
||||
protected void setHighlight(ProgramSelection highlight) {
|
||||
connectedProvider.setHighlight(highlight);
|
||||
}
|
||||
|
||||
protected void setSelection(ProgramSelection sel) {
|
||||
protected void setConnectedProviderSelection(ProgramSelection sel) {
|
||||
selectionChanging = true;
|
||||
connectedProvider.setSelection(sel);
|
||||
selectionChanging = false;
|
||||
}
|
||||
|
||||
protected void clearMarkers(Program program) {
|
||||
if (markerService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (program == null) {
|
||||
return; // can happen during dispose after a programDeactivated()
|
||||
}
|
||||
|
||||
if (currentSelectionMarkers != null) {
|
||||
markerService.removeMarker(currentSelectionMarkers, program);
|
||||
currentSelectionMarkers = null;
|
||||
}
|
||||
|
||||
if (currentHighlightMarkers != null) {
|
||||
markerService.removeMarker(currentHighlightMarkers, program);
|
||||
currentHighlightMarkers = null;
|
||||
}
|
||||
|
||||
if (currentCursorMarkers != null) {
|
||||
markerService.removeMarker(currentCursorMarkers, program);
|
||||
currentCursorMarkers = null;
|
||||
}
|
||||
}
|
||||
|
||||
private MarkerSet getSelectionMarkers(Program program) {
|
||||
if (markerService == null || program == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already created
|
||||
if (currentSelectionMarkers != null) {
|
||||
return currentSelectionMarkers;
|
||||
}
|
||||
|
||||
FieldPanel fp = connectedProvider.getListingPanel().getFieldPanel();
|
||||
currentSelectionMarkers = markerService.createAreaMarker("Selection", "Selection Display",
|
||||
program, MarkerService.SELECTION_PRIORITY, false, true, false, fp.getSelectionColor());
|
||||
return currentSelectionMarkers;
|
||||
}
|
||||
|
||||
protected MarkerSet getHighlightMarkers(Program program) {
|
||||
if (markerService == null || program == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already created
|
||||
if (currentHighlightMarkers != null) {
|
||||
return currentHighlightMarkers;
|
||||
}
|
||||
|
||||
FieldPanel fp = connectedProvider.getListingPanel().getFieldPanel();
|
||||
currentHighlightMarkers = markerService.createAreaMarker("Highlight", "Highlight Display ",
|
||||
program, MarkerService.HIGHLIGHT_PRIORITY, false, true, false, fp.getHighlightColor());
|
||||
return currentHighlightMarkers;
|
||||
}
|
||||
|
||||
protected MarkerSet getCursorMarkers(Program program) {
|
||||
if (markerService == null || program == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already created
|
||||
if (currentCursorMarkers != null) {
|
||||
return currentCursorMarkers;
|
||||
}
|
||||
|
||||
currentCursorMarkers = markerService.createPointMarker("Cursor", "Cursor Location", program,
|
||||
MarkerService.CURSOR_PRIORITY, true, true, isHighlightCursorLine, cursorHighlightColor,
|
||||
CURSOR_LOC_ICON);
|
||||
|
||||
return currentCursorMarkers;
|
||||
private void initMiscellaneousOptions() {
|
||||
// make sure the following options are registered
|
||||
HelpLocation helpLocation =
|
||||
new HelpLocation("ShowInstructionInfoPlugin", "Processor_Manual_Options");
|
||||
Options options = tool.getOptions(ManualViewerCommandWrappedOption.OPTIONS_CATEGORY_NAME);
|
||||
options.registerOption(ManualViewerCommandWrappedOption.MANUAL_VIEWER_OPTIONS,
|
||||
OptionType.CUSTOM_TYPE,
|
||||
ManualViewerCommandWrappedOption.getDefaultBrowserLoaderOptions(), helpLocation,
|
||||
"Options for running manual viewer", () -> new ManualViewerCommandEditor());
|
||||
}
|
||||
|
||||
private void initOptions(Options fieldOptions) {
|
||||
@@ -572,50 +581,44 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
helpLocation, "Enables horizontal scrolling by holding the Shift key while " +
|
||||
"using the mouse scroll wheel");
|
||||
|
||||
Color color = fieldOptions.getColor(GhidraOptions.OPTION_SELECTION_COLOR,
|
||||
GhidraOptions.DEFAULT_SELECTION_COLOR);
|
||||
|
||||
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
|
||||
fieldPanel.setSelectionColor(color);
|
||||
MarkerSet selectionMarkers = getSelectionMarkers(currentProgram);
|
||||
if (selectionMarkers != null) {
|
||||
selectionMarkers.setMarkerColor(color);
|
||||
}
|
||||
|
||||
color = fieldOptions.getColor(GhidraOptions.OPTION_HIGHLIGHT_COLOR,
|
||||
GhidraOptions.DEFAULT_HIGHLIGHT_COLOR);
|
||||
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
|
||||
fieldPanel.setHighlightColor(color);
|
||||
if (highlightMarkers != null) {
|
||||
highlightMarkers.setMarkerColor(color);
|
||||
}
|
||||
|
||||
color = fieldOptions.getColor(CURSOR_COLOR_OPTIONS_NAME, FOCUSED_CURSOR_COLOR);
|
||||
fieldPanel.setFocusedCursorColor(color);
|
||||
|
||||
color = fieldOptions.getColor(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME, UNFOCUSED_CURSOR_COLOR);
|
||||
fieldPanel.setNonFocusCursorColor(color);
|
||||
|
||||
boolean horizontalScrollingEnabled =
|
||||
fieldOptions.getBoolean(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME, true);
|
||||
fieldPanel.setHorizontalScrollingEnabled(horizontalScrollingEnabled);
|
||||
|
||||
cursorHighlightColor = fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR,
|
||||
CURRENT_LINE_HIGHLIGHT_COLOR);
|
||||
|
||||
isHighlightCursorLine = fieldOptions.getBoolean(GhidraOptions.HIGHLIGHT_CURSOR_LINE, true);
|
||||
List<ListingPanel> listingPanels = allListingPanels();
|
||||
List<FieldPanel> fieldPanels = allFieldPanels();
|
||||
initPanelOptions(listingPanels, fieldPanels);
|
||||
}
|
||||
|
||||
private void initMiscellaneousOptions() {
|
||||
// make sure the following options are registered
|
||||
HelpLocation helpLocation =
|
||||
new HelpLocation("ShowInstructionInfoPlugin", "Processor_Manual_Options");
|
||||
Options options = tool.getOptions(ManualViewerCommandWrappedOption.OPTIONS_CATEGORY_NAME);
|
||||
options.registerOption(ManualViewerCommandWrappedOption.MANUAL_VIEWER_OPTIONS,
|
||||
OptionType.CUSTOM_TYPE,
|
||||
ManualViewerCommandWrappedOption.getDefaultBrowserLoaderOptions(), helpLocation,
|
||||
"Options for running manual viewer", () -> new ManualViewerCommandEditor());
|
||||
private void initPanelOptions(List<ListingPanel> listingPanels, List<FieldPanel> fieldPanels) {
|
||||
|
||||
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
|
||||
Color selectionColor = fieldOptions.getColor(GhidraOptions.OPTION_SELECTION_COLOR,
|
||||
GhidraOptions.DEFAULT_SELECTION_COLOR);
|
||||
onListingPanels(listingPanels, lp -> lp.setSelectionColor(selectionColor));
|
||||
|
||||
Color hlColor = fieldOptions.getColor(GhidraOptions.OPTION_HIGHLIGHT_COLOR,
|
||||
GhidraOptions.DEFAULT_HIGHLIGHT_COLOR);
|
||||
onListingPanels(listingPanels, lp -> lp.setHighlightColor(hlColor));
|
||||
|
||||
Color focusedCursorColor =
|
||||
fieldOptions.getColor(CURSOR_COLOR_OPTIONS_NAME, FOCUSED_CURSOR_COLOR);
|
||||
onFieldPanels(fieldPanels, fp -> fp.setFocusedCursorColor(focusedCursorColor));
|
||||
|
||||
Color unfocusedCursorColor =
|
||||
fieldOptions.getColor(UNFOCUSED_CURSOR_COLOR_OPTIONS_NAME, UNFOCUSED_CURSOR_COLOR);
|
||||
onFieldPanels(fieldPanels, fp -> fp.setNonFocusCursorColor(unfocusedCursorColor));
|
||||
|
||||
boolean scrollingEnabled =
|
||||
fieldOptions.getBoolean(MOUSE_WHEEL_HORIZONTAL_SCROLLING_OPTIONS_NAME, true);
|
||||
onFieldPanels(fieldPanels, fp -> fp.setHorizontalScrollingEnabled(scrollingEnabled));
|
||||
|
||||
Color cursorHighlightColor =
|
||||
fieldOptions.getColor(GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR,
|
||||
CURRENT_LINE_HIGHLIGHT_COLOR);
|
||||
onListingPanels(listingPanels, lp -> lp.setCursorHighlightColor(cursorHighlightColor));
|
||||
|
||||
boolean isHighlightCursorLine =
|
||||
fieldOptions.getBoolean(GhidraOptions.HIGHLIGHT_CURSOR_LINE, true);
|
||||
onListingPanels(listingPanels,
|
||||
lp -> lp.setHighlightCursorLineEnabled(isHighlightCursorLine));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -876,21 +879,4 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
|
||||
disconnectedProviders.remove(codeViewerProvider);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
static class MarkerChangeListener implements ChangeListener {
|
||||
private FieldPanel fieldPanel;
|
||||
|
||||
MarkerChangeListener(CodeViewerProvider provider) {
|
||||
this.fieldPanel = provider.getListingPanel().getFieldPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
fieldPanel.repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,22 +97,21 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void highlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
|
||||
MarkerSet highlightMarkers = getHighlightMarkers(currentProgram);
|
||||
if (highlightMarkers != null) {
|
||||
highlightMarkers.clearAll();
|
||||
}
|
||||
if (highlight != null && currentProgram != null) {
|
||||
if (highlightMarkers != null) {
|
||||
highlightMarkers.add(highlight);
|
||||
}
|
||||
}
|
||||
public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight) {
|
||||
if (provider == connectedProvider) {
|
||||
tool.firePluginEvent(new ProgramHighlightPluginEvent(getName(), highlight,
|
||||
connectedProvider.getProgram()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation location) {
|
||||
if (provider == connectedProvider) {
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent(getName(), location,
|
||||
connectedProvider.getProgram()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
if (event instanceof ProgramClosedPluginEvent) {
|
||||
@@ -125,7 +124,7 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||
currentProgram.removeListener(this);
|
||||
}
|
||||
ProgramActivatedPluginEvent evt = (ProgramActivatedPluginEvent) event;
|
||||
clearMarkers(currentProgram); // do this just before changing the program
|
||||
connectedProvider.clearMarkers(currentProgram); // do this before changing the program
|
||||
|
||||
currentProgram = evt.getActiveProgram();
|
||||
if (currentProgram != null) {
|
||||
@@ -135,12 +134,13 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||
else {
|
||||
currentView = new AddressSet();
|
||||
}
|
||||
|
||||
connectedProvider.doSetProgram(currentProgram);
|
||||
|
||||
updateHighlightProvider();
|
||||
updateBackgroundColorModel();
|
||||
setHighlight(new FieldSelection());
|
||||
setSelection(new ProgramSelection());
|
||||
connectedProvider.updateHighlightProvider();
|
||||
updateBackgroundColorModel(connectedProvider);
|
||||
setConnectedProviderHighlight(new FieldSelection());
|
||||
setConnectedProviderSelection(new ProgramSelection());
|
||||
}
|
||||
else if (event instanceof ProgramLocationPluginEvent) {
|
||||
ProgramLocationPluginEvent evt = (ProgramLocationPluginEvent) event;
|
||||
@@ -156,12 +156,12 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||
}
|
||||
else if (event instanceof ProgramSelectionPluginEvent) {
|
||||
ProgramSelectionPluginEvent evt = (ProgramSelectionPluginEvent) event;
|
||||
setSelection(evt.getSelection());
|
||||
setConnectedProviderSelection(evt.getSelection());
|
||||
}
|
||||
else if (event instanceof ProgramHighlightPluginEvent) {
|
||||
ProgramHighlightPluginEvent evt = (ProgramHighlightPluginEvent) event;
|
||||
if (evt.getProgram() == currentProgram) {
|
||||
setHighlight(evt.getHighlight());
|
||||
connectedProvider.setHighlight(evt.getHighlight());
|
||||
}
|
||||
}
|
||||
else if (event instanceof ViewChangedPluginEvent) {
|
||||
@@ -206,10 +206,13 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||
if (location != null) {
|
||||
connectedProvider.setLocation(location);
|
||||
}
|
||||
setHighlight(highlight);
|
||||
|
||||
connectedProvider.setHighlight(highlight);
|
||||
|
||||
if (selection != null) {
|
||||
connectedProvider.setSelection(selection);
|
||||
}
|
||||
|
||||
if (vp != null) {
|
||||
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
|
||||
fieldPanel.setViewerPosition(vp.getIndex(), vp.getXOffset(), vp.getYOffset());
|
||||
@@ -268,7 +271,7 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||
FieldSelection highlight = new FieldSelection();
|
||||
highlight.load(saveState);
|
||||
if (!highlight.isEmpty()) {
|
||||
setHighlight(highlight);
|
||||
setConnectedProviderHighlight(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,19 +289,6 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
|
||||
connectedProvider.readState(saveState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void locationChanged(CodeViewerProvider provider, ProgramLocation location) {
|
||||
if (provider == connectedProvider) {
|
||||
MarkerSet cursorMarkers = getCursorMarkers(currentProgram);
|
||||
if (cursorMarkers != null) {
|
||||
cursorMarkers.clearAll();
|
||||
cursorMarkers.add(location.getAddress());
|
||||
}
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent(getName(), location,
|
||||
connectedProvider.getProgram()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewManagerService getViewManager(CodeViewerProvider codeViewerProvider) {
|
||||
if (codeViewerProvider == connectedProvider) {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -23,22 +22,21 @@ import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public interface CodeBrowserPluginInterface {
|
||||
|
||||
PluginTool getTool();
|
||||
public PluginTool getTool();
|
||||
|
||||
String getName();
|
||||
public String getName();
|
||||
|
||||
void providerClosed(CodeViewerProvider codeViewerProvider);
|
||||
public boolean isDisposed();
|
||||
|
||||
boolean isDisposed();
|
||||
public void providerClosed(CodeViewerProvider provider);
|
||||
|
||||
void locationChanged(CodeViewerProvider codeViewerProvider, ProgramLocation loc);
|
||||
public void broadcastLocationChanged(CodeViewerProvider provider, ProgramLocation loc);
|
||||
|
||||
void selectionChanged(CodeViewerProvider codeViewerProvider, ProgramSelection currentSelection);
|
||||
public void broadcastSelectionChanged(CodeViewerProvider provider, ProgramSelection selection);
|
||||
|
||||
void highlightChanged(CodeViewerProvider codeViewerProvider, ProgramSelection highlight);
|
||||
public void broadcastHighlightChanged(CodeViewerProvider provider, ProgramSelection highlight);
|
||||
|
||||
ViewManagerService getViewManager(CodeViewerProvider codeViewerProvider);
|
||||
|
||||
CodeViewerProvider createNewDisconnectedProvider();
|
||||
public ViewManagerService getViewManager(CodeViewerProvider provider);
|
||||
|
||||
public CodeViewerProvider createNewDisconnectedProvider();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.awt.Point;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
|
||||
@@ -112,7 +111,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
liveSelection = selection;
|
||||
updateSubTitle();
|
||||
};
|
||||
private FocusingMouseListener focusingMouseListener;
|
||||
|
||||
private CodeBrowserClipboardProvider codeViewerClipboardProvider;
|
||||
private ClipboardService clipboardService;
|
||||
@@ -151,6 +149,11 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
setDefaultWindowPosition(WindowPosition.RIGHT);
|
||||
|
||||
listingPanel = new ListingPanel(formatMgr);
|
||||
if (!isConnected) {
|
||||
// update the marker set names to be unique so that each listing can have its own
|
||||
listingPanel.setUseMarkerNameSuffix(true);
|
||||
}
|
||||
|
||||
listingPanel.enablePropertyBasedColorModel(true);
|
||||
|
||||
decorationPanel = new ListingPanelContainer(listingPanel, isConnected);
|
||||
@@ -253,6 +256,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
|
||||
clearMarkers(program);
|
||||
|
||||
tool.removePopupActionProvider(this);
|
||||
|
||||
if (clipboardService != null) {
|
||||
@@ -324,8 +329,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
|
||||
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
|
||||
Object source = event.getSource();
|
||||
List<MarginProvider> marginProviders = lp.getMarginProviders();
|
||||
for (MarginProvider marginProvider : marginProviders) {
|
||||
List<ListingMarginProvider> marginProviders = lp.getMarginProviders();
|
||||
for (ListingMarginProvider marginProvider : marginProviders) {
|
||||
JComponent c = marginProvider.getComponent();
|
||||
if (c == source) {
|
||||
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
|
||||
@@ -337,8 +342,8 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
List<OverviewProvider> overviewProviders = lp.getOverviewProviders();
|
||||
for (OverviewProvider overviewProvider : overviewProviders) {
|
||||
List<ListingOverviewProvider> overviewProviders = lp.getOverviewProviders();
|
||||
for (ListingOverviewProvider overviewProvider : overviewProviders) {
|
||||
JComponent c = overviewProvider.getComponent();
|
||||
if (c == source) {
|
||||
return source;
|
||||
@@ -444,6 +449,10 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
void clearMarkers(Program p) {
|
||||
listingPanel.clearMarkers(p);
|
||||
}
|
||||
|
||||
protected void updateTitle() {
|
||||
String subTitle = program == null ? "" : ' ' + program.getDomainFile().getName();
|
||||
String newTitle = TITLE + subTitle;
|
||||
@@ -657,6 +666,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
}
|
||||
}
|
||||
|
||||
// events coming from the ListingPanel
|
||||
@Override
|
||||
public void programLocationChanged(ProgramLocation loc, EventTrigger trigger) {
|
||||
if (plugin.isDisposed()) {
|
||||
@@ -665,7 +675,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
if (!loc.equals(currentLocation)) {
|
||||
codeViewerClipboardProvider.setLocation(loc);
|
||||
currentLocation = loc;
|
||||
plugin.locationChanged(this, loc);
|
||||
plugin.broadcastLocationChanged(this, loc);
|
||||
contextChanged();
|
||||
}
|
||||
}
|
||||
@@ -696,7 +706,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
currentSelection = selection;
|
||||
codeViewerClipboardProvider.setSelection(currentSelection);
|
||||
listingPanel.setSelection(currentSelection);
|
||||
plugin.selectionChanged(this, currentSelection);
|
||||
plugin.broadcastSelectionChanged(this, currentSelection);
|
||||
contextChanged();
|
||||
updateSubTitle();
|
||||
}
|
||||
@@ -792,7 +802,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
private void doSetHighlight(ProgramSelection highlight) {
|
||||
listingPanel.setHighlight(highlight);
|
||||
currentHighlight = highlight;
|
||||
plugin.highlightChanged(this, highlight);
|
||||
plugin.broadcastHighlightChanged(this, highlight);
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@@ -1161,45 +1171,51 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
listingPanel.removeDisplayListener(listener);
|
||||
}
|
||||
|
||||
private synchronized void createFocusingMouseListener() {
|
||||
if (focusingMouseListener == null) {
|
||||
focusingMouseListener = new FocusingMouseListener();
|
||||
}
|
||||
}
|
||||
|
||||
public void addOverviewProvider(OverviewProvider overviewProvider) {
|
||||
createFocusingMouseListener();
|
||||
JComponent component = overviewProvider.getComponent();
|
||||
|
||||
// just in case we get repeated calls
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
component.addMouseListener(focusingMouseListener);
|
||||
public void addOverviewProvider(ListingOverviewProvider overviewProvider) {
|
||||
overviewProvider.setNavigatable(this);
|
||||
getListingPanel().addOverviewProvider(overviewProvider);
|
||||
}
|
||||
|
||||
public void addMarginProvider(MarginProvider marginProvider) {
|
||||
createFocusingMouseListener();
|
||||
JComponent component = marginProvider.getComponent();
|
||||
|
||||
// just in case we get repeated calls
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
component.addMouseListener(focusingMouseListener);
|
||||
getListingPanel().addMarginProvider(marginProvider);
|
||||
}
|
||||
|
||||
public void removeOverviewProvider(OverviewProvider overviewProvider) {
|
||||
JComponent component = overviewProvider.getComponent();
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
public void removeOverviewProvider(ListingOverviewProvider overviewProvider) {
|
||||
getListingPanel().removeOverviewProvider(overviewProvider);
|
||||
}
|
||||
|
||||
public void removeMarginProvider(MarginProvider marginProvider) {
|
||||
JComponent component = marginProvider.getComponent();
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
public void addMarginProvider(ListingMarginProvider marginProvider) {
|
||||
getListingPanel().addMarginProvider(marginProvider);
|
||||
}
|
||||
|
||||
public void removeMarginProvider(ListingMarginProvider marginProvider) {
|
||||
getListingPanel().removeMarginProvider(marginProvider);
|
||||
}
|
||||
|
||||
public void addMarginService(ListingMarginProviderService service) {
|
||||
getListingPanel().addMarginService(service, isConnected());
|
||||
}
|
||||
|
||||
public void addOverviewService(ListingOverviewProviderService service) {
|
||||
getListingPanel().addOverviewService(service, this, isConnected());
|
||||
}
|
||||
|
||||
public void removeOverviewService(ListingOverviewProviderService service) {
|
||||
getListingPanel().removeOverviewService(service);
|
||||
}
|
||||
|
||||
public void removeMarginService(ListingMarginProviderService service) {
|
||||
getListingPanel().removeMarginService(service);
|
||||
}
|
||||
|
||||
public void addHoverService(ListingHoverService hoverService) {
|
||||
getListingPanel().addHoverService(hoverService);
|
||||
}
|
||||
|
||||
public void removeHoverService(ListingHoverService hoverService) {
|
||||
getListingPanel().removeHoverService(hoverService);
|
||||
|
||||
if (otherPanel != null) {
|
||||
otherPanel.removeHoverService(hoverService);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
@@ -1280,11 +1296,4 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||
return list.toArray(new Highlight[list.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
private class FocusingMouseListener extends MouseAdapter {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
getListingPanel().getFieldPanel().requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -16,11 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.flowarrow;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
class ConditionalFlowArrow extends FlowArrow {
|
||||
|
||||
private static final Stroke CONDITIONAL_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE,
|
||||
@@ -28,9 +27,9 @@ class ConditionalFlowArrow extends FlowArrow {
|
||||
private static final Stroke NORMAL_ACTIVE_STROKE = new BasicStroke(2, BasicStroke.CAP_SQUARE,
|
||||
BasicStroke.JOIN_MITER);
|
||||
|
||||
ConditionalFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
|
||||
RefType referenceType) {
|
||||
super(plugin, canvas, start, end, referenceType);
|
||||
ConditionalFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start,
|
||||
Address end, RefType referenceType) {
|
||||
super(provider, canvas, start, end, referenceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -16,11 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.flowarrow;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
class DefaultFlowArrow extends FlowArrow {
|
||||
|
||||
private static final Stroke NORMAL_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE,
|
||||
@@ -28,9 +27,9 @@ class DefaultFlowArrow extends FlowArrow {
|
||||
private static final Stroke NORMAL_ACTIVE_STROKE = new BasicStroke(2, BasicStroke.CAP_SQUARE,
|
||||
BasicStroke.JOIN_MITER);
|
||||
|
||||
DefaultFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
|
||||
DefaultFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start, Address end,
|
||||
RefType referenceType) {
|
||||
super(plugin, canvas, start, end, referenceType);
|
||||
super(provider, canvas, start, end, referenceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -16,11 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.flowarrow;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
class FallthroughFlowArrow extends FlowArrow {
|
||||
|
||||
private static final Stroke FALLTHROUGH_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE,
|
||||
@@ -28,9 +27,9 @@ class FallthroughFlowArrow extends FlowArrow {
|
||||
private static final Stroke FALLTHROUGH_ACTIVE_STROKE = new BasicStroke(2,
|
||||
BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10, new float[] { 8, 3, 2, 3 }, 0);
|
||||
|
||||
FallthroughFlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
|
||||
RefType referenceType) {
|
||||
super(plugin, canvas, start, end, referenceType);
|
||||
FallthroughFlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start,
|
||||
Address end, RefType referenceType) {
|
||||
super(provider, canvas, start, end, referenceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.flowarrow;
|
||||
|
||||
import static ghidra.util.HTMLUtilities.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
@@ -23,7 +25,6 @@ import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
abstract class FlowArrow {
|
||||
@@ -36,15 +37,16 @@ abstract class FlowArrow {
|
||||
|
||||
Address start;
|
||||
Address end;
|
||||
AddressSet addressSet;
|
||||
int depth = -1;
|
||||
AddressSet addresses;
|
||||
int column = -1;
|
||||
RefType refType;
|
||||
private int maxColumn;
|
||||
private boolean isUp;
|
||||
|
||||
boolean active;
|
||||
boolean selected;
|
||||
|
||||
private FlowArrowPlugin plugin;
|
||||
private FlowArrowMarginProvider provider;
|
||||
private Component canvas;
|
||||
protected Shape arrowBody;
|
||||
protected Shape arrowHead;
|
||||
@@ -52,15 +54,16 @@ abstract class FlowArrow {
|
||||
/** The shape of the arrow body, but with added size */
|
||||
private List<Shape> clickableShapes = new ArrayList<>();
|
||||
|
||||
FlowArrow(FlowArrowPlugin plugin, Component canvas, Address start, Address end,
|
||||
FlowArrow(FlowArrowMarginProvider provider, Component canvas, Address start, Address end,
|
||||
RefType referenceType) {
|
||||
this.plugin = plugin;
|
||||
this.provider = provider;
|
||||
this.canvas = canvas;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.refType = referenceType;
|
||||
this.addressSet = new AddressSet(new AddressRangeImpl(start, end));
|
||||
isUp = start.compareTo(end) > 0;
|
||||
this.maxColumn = provider.getMaxColumn();
|
||||
this.addresses = new AddressSet(new AddressRangeImpl(start, end));
|
||||
this.isUp = start.compareTo(end) > 0;
|
||||
}
|
||||
|
||||
abstract Stroke getSelectedStroke();
|
||||
@@ -101,7 +104,7 @@ abstract class FlowArrow {
|
||||
g2.setStroke(oldStroke);
|
||||
}
|
||||
|
||||
/** True if this arrow points up instead of down */
|
||||
/** {@return true if this arrow points up instead of down} */
|
||||
boolean isUp() {
|
||||
return isUp;
|
||||
}
|
||||
@@ -242,20 +245,20 @@ abstract class FlowArrow {
|
||||
int displayWidth = canvas.getWidth();// - FlowArrowPlugin.LEFT_OFFSET;
|
||||
int lineWidth = calculateLineWidth(displayWidth);
|
||||
|
||||
arrowBody = FlowArrowShapeFactory.createArrowBody(plugin, this, displayWidth, displayHeight,
|
||||
lineWidth);
|
||||
arrowBody =
|
||||
FlowArrowShapeFactory.createArrowBody(provider, this, displayWidth, displayHeight,
|
||||
lineWidth);
|
||||
|
||||
arrowHead = FlowArrowShapeFactory.createArrowHead(plugin, this, displayWidth, displayHeight,
|
||||
lineWidth);
|
||||
arrowHead =
|
||||
FlowArrowShapeFactory.createArrowHead(provider, this, displayWidth, displayHeight,
|
||||
lineWidth);
|
||||
}
|
||||
|
||||
private int calculateLineWidth(int displayWidth) {
|
||||
// Crunch or stretch spacing depending upon width and maximum depth
|
||||
int lineWidth = DEFAULT_LINE_SPACING;
|
||||
int maxDepth = plugin.getMaxDepth();
|
||||
|
||||
if (maxDepth >= 0) {
|
||||
int availabeWidth = displayWidth - FlowArrowPlugin.LEFT_OFFSET;
|
||||
if (maxColumn >= 0) {
|
||||
int availabeWidth = displayWidth - FlowArrowMarginProvider.LEFT_OFFSET;
|
||||
lineWidth = (int) (availabeWidth * ARROW_SPACING_RATIO);
|
||||
}
|
||||
if (lineWidth < MIN_LINE_SPACING) {
|
||||
@@ -321,9 +324,17 @@ abstract class FlowArrow {
|
||||
}
|
||||
|
||||
public String getDisplayString() {
|
||||
return "<html><table><tr><td>start</td><td>" + HTMLUtilities.escapeHTML(start.toString()) +
|
||||
"</td><tr><td>end</td><td>" + HTMLUtilities.escapeHTML(end.toString()) +
|
||||
"</td><tr><td>ref type</td><td>" + refType + "</td></tr></table>";
|
||||
//@formatter:off
|
||||
return """
|
||||
<html><table>
|
||||
<tr><td>start</td><td>%s</td></tr>
|
||||
<tr><td>end</td><td>%s</td></tr>
|
||||
<tr><td>ref type</td><td>%s</td></tr>
|
||||
</table>
|
||||
""".formatted(escapeHTML(start.toString()),
|
||||
escapeHTML(end.toString()),
|
||||
refType).trim();
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,737 @@
|
||||
/* ###
|
||||
* 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.flowarrow;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import ghidra.app.util.viewer.field.ListingColors;
|
||||
import ghidra.app.util.viewer.field.ListingColors.FlowArrowColors;
|
||||
import ghidra.app.util.viewer.listingpanel.*;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.MarkerLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.UniversalID;
|
||||
|
||||
class FlowArrowMarginProvider implements ListingMarginProvider {
|
||||
|
||||
static final int LEFT_OFFSET = 3;
|
||||
private static final int MAX_DEPTH = 16;
|
||||
private static final int MAX_REFSTO_SHOW = 10;
|
||||
|
||||
/** Start address to the index of the layout for that start address */
|
||||
private Map<Address, Integer> startAddressToPixel = new HashMap<>();
|
||||
|
||||
/** End address to the index of the layout for that end address */
|
||||
private Map<Address, Integer> endAddressToPixel = new HashMap<>();
|
||||
|
||||
private FlowArrowPlugin plugin;
|
||||
private ListingPanel listingPanel;
|
||||
private Program program;
|
||||
private Address currentAddr;
|
||||
private FlowArrowPanel flowArrowPanel;
|
||||
|
||||
/** The column furthers away from the listing, to the left */
|
||||
private int maxColumn;
|
||||
private boolean isShowing = true;
|
||||
private boolean validState = false;
|
||||
|
||||
/** On-screen layouts and their start/end addresses */
|
||||
private VerticalPixelAddressMap layoutToPixel;
|
||||
private Address screenTop;
|
||||
private Address screenBottom;
|
||||
|
||||
/**
|
||||
* We keep arrows in 3 sets: all arrows, selected arrows, and active arrows.
|
||||
* Further, we rebuild the full set of arrows as the screen moves. However, the selected and
|
||||
* active arrows do not get cleared when we move the screen. This allows us to keep painting
|
||||
* selected arrows as the screen changes. The selected arrows are changed by user clicking. The
|
||||
* active arrows are changed by program location updates.
|
||||
*/
|
||||
private List<FlowArrow> flowArrows = new ArrayList<>();
|
||||
|
||||
/** Arrows manually clicked by the user */
|
||||
private Set<FlowArrow> selectedArrows = new HashSet<>();
|
||||
|
||||
/** Those arrows that start at the current address */
|
||||
private Set<FlowArrow> activeArrows = new HashSet<>();
|
||||
|
||||
FlowArrowMarginProvider(FlowArrowPlugin plugin) {
|
||||
|
||||
this.plugin = plugin;
|
||||
|
||||
flowArrowPanel = new FlowArrowPanel(this);
|
||||
flowArrowPanel.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
boolean previousState = isShowing;
|
||||
isShowing = flowArrowPanel.getWidth() > LEFT_OFFSET;
|
||||
if (isShowing && !previousState) {
|
||||
updateAndRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown(ComponentEvent e) {
|
||||
boolean previousState = isShowing;
|
||||
isShowing = flowArrowPanel.getWidth() > LEFT_OFFSET;
|
||||
if (isShowing && !previousState) {
|
||||
updateAndRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
isShowing = false;
|
||||
}
|
||||
});
|
||||
|
||||
flowArrowPanel.setBackground(ListingColors.BACKGROUND);
|
||||
flowArrowPanel.setForeground(FlowArrowColors.INACTIVE);
|
||||
flowArrowPanel.setHighlightColor(FlowArrowColors.ACTIVE);
|
||||
flowArrowPanel.setSelectedColor(FlowArrowColors.SELECTED);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UniversalID ownerId) {
|
||||
// we don't need the owner id
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return flowArrowPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkerLocation getMarkerLocation(int x, int y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResizeable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(ProgramLocation location) {
|
||||
|
||||
currentAddr = location.getAddress();
|
||||
clearActiveArrows();
|
||||
assignActiveArrows();
|
||||
flowArrowPanel.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void screenDataChanged(ListingPanel listing, AddressIndexMap addrMap,
|
||||
VerticalPixelAddressMap pixMap) {
|
||||
|
||||
this.listingPanel = listing;
|
||||
Program currentProgram = listing.getProgram();
|
||||
if (this.program != currentProgram) {
|
||||
clearAllArrows();
|
||||
}
|
||||
|
||||
this.program = currentProgram;
|
||||
this.layoutToPixel = pixMap;
|
||||
validateState();
|
||||
updateAndRepaint();
|
||||
}
|
||||
|
||||
Address getCurrentAddress() {
|
||||
return currentAddr;
|
||||
}
|
||||
|
||||
Address getScreenBottomAddr() {
|
||||
return screenBottom;
|
||||
}
|
||||
|
||||
int getMaxColumn() {
|
||||
return maxColumn;
|
||||
}
|
||||
|
||||
boolean isOnScreen(Address address) {
|
||||
if (screenBottom == null || screenTop == null) {
|
||||
return true; // shouldn't happen
|
||||
}
|
||||
|
||||
if (address.compareTo(screenTop) < 0) {
|
||||
// above the top of the screen
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address.compareTo(screenBottom) > 0) {
|
||||
// below the bottom of the screen
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isOffscreen(FlowArrow arrow) {
|
||||
if (screenBottom == null || screenTop == null) {
|
||||
return true; // shouldn't happen
|
||||
}
|
||||
|
||||
if (arrow.start.compareTo(screenTop) < 0 && arrow.end.compareTo(screenTop) < 0) {
|
||||
// start and end are above the top of the screen
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arrow.start.compareTo(screenBottom) > 0 && arrow.end.compareTo(screenBottom) > 0) {
|
||||
// start and end are below the bottom of the screen
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isBelowScreen(Address address) {
|
||||
if (screenBottom == null || screenTop == null) {
|
||||
return true; // shouldn't happen
|
||||
}
|
||||
|
||||
return address.compareTo(screenBottom) > 0;
|
||||
}
|
||||
|
||||
/* The y value of the start of the layout at the given address. */
|
||||
Integer getStartPos(Address addr) {
|
||||
return startAddressToPixel.get(addr);
|
||||
}
|
||||
|
||||
/* The y value of the end of the layout at the given address. */
|
||||
Integer getEndPos(Address addr) {
|
||||
return endAddressToPixel.get(addr);
|
||||
}
|
||||
|
||||
void setArrowSelected(FlowArrow arrow, boolean selected) {
|
||||
if (selected) {
|
||||
selectedArrows.add(arrow);
|
||||
}
|
||||
else {
|
||||
selectedArrows.remove(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<FlowArrow> getSelectedFlowArrows() {
|
||||
return selectedArrows.iterator();
|
||||
}
|
||||
|
||||
Iterator<FlowArrow> getFlowArrowIterator() {
|
||||
return flowArrows.iterator();
|
||||
}
|
||||
|
||||
/* Those arrows starting at the current address */
|
||||
Iterator<FlowArrow> getActiveArrows() {
|
||||
return activeArrows.iterator();
|
||||
}
|
||||
|
||||
private void resetSelectedArrows() {
|
||||
for (FlowArrow arrow : selectedArrows) {
|
||||
arrow.resetShape();
|
||||
}
|
||||
}
|
||||
|
||||
private void clearAllArrows() {
|
||||
flowArrows.clear();
|
||||
activeArrows.clear();
|
||||
selectedArrows.clear();
|
||||
}
|
||||
|
||||
private void clearActiveArrows() {
|
||||
for (FlowArrow f : activeArrows) {
|
||||
f.active = false;
|
||||
}
|
||||
activeArrows.clear();
|
||||
}
|
||||
|
||||
private void resetActiveArrows() {
|
||||
for (FlowArrow arrow : activeArrows) {
|
||||
arrow.resetShape();
|
||||
}
|
||||
}
|
||||
|
||||
private void assignActiveArrows() {
|
||||
|
||||
if (!activeArrows.isEmpty()) {
|
||||
resetActiveArrows();
|
||||
return; // don't overwrite existing values
|
||||
}
|
||||
|
||||
if (currentAddr == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (FlowArrow arrow : flowArrows) {
|
||||
if (currentAddr.equals(arrow.start)) {
|
||||
arrow.active = true;
|
||||
activeArrows.add(arrow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mapArrowsByEndpoints(Map<Address, List<FlowArrow>> arrowsByStart,
|
||||
Map<Address, List<FlowArrow>> arrowsByEnd) {
|
||||
|
||||
for (FlowArrow arrow : flowArrows) {
|
||||
arrowsByStart.computeIfAbsent(arrow.start, f -> new ArrayList<>()).add(arrow);
|
||||
arrowsByEnd.computeIfAbsent(arrow.end, f -> new ArrayList<>()).add(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ArrowGroup> groupArrowsBySharedEndpoints() {
|
||||
|
||||
// not sure this is still needed; keeping for posterity
|
||||
Collections.sort(flowArrows, (a1, a2) -> (a1).end.compareTo((a2).end));
|
||||
|
||||
Map<Address, List<FlowArrow>> arrowsByStart = new HashMap<>();
|
||||
Map<Address, List<FlowArrow>> arrowsByEnd = new HashMap<>();
|
||||
mapArrowsByEndpoints(arrowsByStart, arrowsByEnd);
|
||||
|
||||
List<ArrowGroup> groups = new ArrayList<>();
|
||||
|
||||
Set<FlowArrow> unprocessed = new HashSet<>(flowArrows);
|
||||
for (FlowArrow arrow : flowArrows) {
|
||||
|
||||
if (!unprocessed.contains(arrow)) {
|
||||
continue; // already grouped
|
||||
}
|
||||
|
||||
// put all arrows in this group that share a start or end, as they will all occupy the
|
||||
// same column
|
||||
ArrowGroup group = new ArrowGroup();
|
||||
List<FlowArrow> starts = arrowsByStart.get(arrow.start);
|
||||
for (FlowArrow f : starts) {
|
||||
group.add(f);
|
||||
unprocessed.remove(arrow);
|
||||
}
|
||||
List<FlowArrow> ends = arrowsByEnd.get(arrow.end);
|
||||
for (FlowArrow f : ends) {
|
||||
group.add(f);
|
||||
unprocessed.remove(arrow);
|
||||
}
|
||||
|
||||
group.add(arrow);
|
||||
unprocessed.remove(arrow);
|
||||
|
||||
groups.add(group);
|
||||
}
|
||||
|
||||
// Sort the groups so that the lowest end address is first. I'm assuming that we wish to
|
||||
// start at the top of the screen and paint incoming arrows first, closest to the Listing.
|
||||
groups.sort((g1, g2) -> g1.getSortAddress().compareTo(g2.getSortAddress()));
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns all arrow columns (horizontal positioning). All arrows that share a start and or
|
||||
* end point will all share the same column. This reduce clutter by having them all share the
|
||||
* same vertical segment.
|
||||
* <p>
|
||||
* As the groups are assigned columns, each arrow in the group is updated. When this method is
|
||||
* finished, all arrows will have a column assigned.
|
||||
*/
|
||||
private void assignArrowColumns() {
|
||||
|
||||
Collections.sort(flowArrows, (a1, a2) -> (a1).end.compareTo((a2).end));
|
||||
|
||||
// assign groups and then assign columns to the groups
|
||||
int count = 0;
|
||||
List<ArrowGroup> groups = groupArrowsBySharedEndpoints();
|
||||
for (ArrowGroup group : groups) {
|
||||
// assignGroupColumn(group, groups);
|
||||
int column = Math.min(MAX_DEPTH, count++);
|
||||
group.setColumn(column);
|
||||
maxColumn = column;
|
||||
}
|
||||
}
|
||||
|
||||
private List<FlowArrow> getFlowArrowsForScreenInstructions(AddressSetView screenAddresses) {
|
||||
|
||||
// A cache of arrows encountered going off the screen, above or below. For any given arrow
|
||||
// start, we wish to only show one arrow for each of three references flow types. The cache
|
||||
// will record when we have seen each of the types so that we can skip adding arrows for
|
||||
// that type from that address again.
|
||||
OffscreenArrowsFlow offscreenArrows = new OffscreenArrowsFlow();
|
||||
|
||||
Set<FlowArrow> results = new HashSet<>();
|
||||
Listing listing = program.getListing();
|
||||
InstructionIterator it = listing.getInstructions(screenAddresses, true);
|
||||
for (Instruction inst : it) {
|
||||
|
||||
// incoming
|
||||
ReferenceManager refManager = program.getReferenceManager();
|
||||
int refCount = refManager.getReferenceCountTo(inst.getMinAddress());
|
||||
if (refCount < MAX_REFSTO_SHOW) {
|
||||
for (Reference ref : inst.getReferenceIteratorTo()) {
|
||||
createFlowArrow(results, offscreenArrows, ref);
|
||||
}
|
||||
}
|
||||
|
||||
// clearing the cache resets the check for duplicates, keeping incoming and outgoing
|
||||
// references separate
|
||||
offscreenArrows.clear();
|
||||
|
||||
// outgoing
|
||||
for (Reference ref : inst.getReferencesFrom()) {
|
||||
createFlowArrow(results, offscreenArrows, ref);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(results);
|
||||
}
|
||||
|
||||
private void createFlowArrow(Set<FlowArrow> results, OffscreenArrowsFlow offscreenArrows,
|
||||
Reference ref) {
|
||||
RefType type = ref.getReferenceType();
|
||||
if (!(type.isJump() || type.isFallthrough())) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlowArrow arrow = doCreateFlowArrow(ref);
|
||||
if (arrow == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.contains(arrow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (offscreenArrows.exists(arrow)) {
|
||||
// We have already seen an offscreen arrow coming from the same start address in the
|
||||
// same direction for this arrow's flow type. No need to add another one.
|
||||
return;
|
||||
}
|
||||
|
||||
results.add(arrow);
|
||||
updateArrowSets(arrow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unusual Code: We keep arrows in 3 sets: all arrows, selected arrows, and active arrows.
|
||||
* Further, we rebuild arrows as the screen moves, causing the x coordinate to change as arrows
|
||||
* that are no longer on the screen are removed and as new arrows are added. We want to make
|
||||
* sure that we don't end up with an arrow in the selected/active sets that are the same as the
|
||||
* one in the 'all' set, but with a different width. This causes both arrows to become
|
||||
* visible--basically, the selected arrows can become stale as their width changes. This code is
|
||||
* meant to address this out-of-sync behavior.
|
||||
*
|
||||
* @param arrow the updated form of the arrow
|
||||
*/
|
||||
private void updateArrowSets(FlowArrow arrow) {
|
||||
if (selectedArrows.remove(arrow)) {
|
||||
arrow.selected = true;
|
||||
selectedArrows.add(arrow);
|
||||
}
|
||||
|
||||
if (activeArrows.remove(arrow)) {
|
||||
arrow.active = true;
|
||||
activeArrows.add(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
private FlowArrow doCreateFlowArrow(Reference ref) {
|
||||
Address start = toLayoutAddress(ref.getFromAddress());
|
||||
Address end = toLayoutAddress(ref.getToAddress());
|
||||
if (start == null || end == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!start.hasSameAddressSpace(end)) {
|
||||
return null; // is this right??
|
||||
}
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
if (!memory.contains(end)) {
|
||||
return null; // bad disassembly
|
||||
}
|
||||
|
||||
RefType refType = ref.getReferenceType();
|
||||
if (refType.isFallthrough()) {
|
||||
return new FallthroughFlowArrow(this, flowArrowPanel, start, end, refType);
|
||||
}
|
||||
else if (refType.isConditional()) {
|
||||
return new ConditionalFlowArrow(this, flowArrowPanel, start, end, refType);
|
||||
}
|
||||
|
||||
return new DefaultFlowArrow(this, flowArrowPanel, start, end, refType);
|
||||
}
|
||||
|
||||
private void validateState() {
|
||||
validState = true;
|
||||
if (program == null || layoutToPixel == null) {
|
||||
validState = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int n = layoutToPixel.getNumLayouts();
|
||||
validState = n != 0;
|
||||
}
|
||||
|
||||
void updateAndRepaint() {
|
||||
update();
|
||||
flowArrowPanel.repaint();
|
||||
}
|
||||
|
||||
private void update() {
|
||||
if (!isShowing || !validState) {
|
||||
return;
|
||||
}
|
||||
|
||||
int n = layoutToPixel.getNumLayouts();
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Address startAddress = layoutToPixel.getLayoutAddress(0);
|
||||
Address endAddress = layoutToPixel.getLayoutAddress(n - 1);
|
||||
|
||||
screenTop = startAddress;
|
||||
screenBottom = endAddress;
|
||||
flowArrows.clear();
|
||||
startAddressToPixel.clear();
|
||||
endAddressToPixel.clear();
|
||||
maxColumn = 0;
|
||||
|
||||
resetSelectedArrows();
|
||||
|
||||
if (screenTop == null || screenBottom == null || n > 500) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find all addresses that are on the screen and compute y co-ordinate
|
||||
for (int layout = 0; layout < n; layout++) {
|
||||
Address addr = layoutToPixel.getLayoutAddress(layout);
|
||||
if (addr != null) {
|
||||
startAddressToPixel.put(addr, layoutToPixel.getBeginPosition(layout));
|
||||
endAddressToPixel.put(addr, layoutToPixel.getEndPosition(layout));
|
||||
}
|
||||
}
|
||||
|
||||
AddressSetView flowSet = layoutToPixel.getAddressSet();
|
||||
flowArrows = getFlowArrowsForScreenInstructions(flowSet);
|
||||
|
||||
assignArrowColumns();
|
||||
|
||||
assignActiveArrows();
|
||||
}
|
||||
|
||||
private Address toLayoutAddress(Address addr) {
|
||||
Object pixel = startAddressToPixel.get(addr);
|
||||
if (pixel != null || addr.compareTo(screenTop) < 0 ||
|
||||
addr.compareTo(screenBottom) > 0) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
int n = layoutToPixel.getNumLayouts();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Address layoutAddr = layoutToPixel.getLayoutAddress(i);
|
||||
Address endLayoutAddr = layoutToPixel.getLayoutEndAddress(i);
|
||||
if (layoutAddr == null || endLayoutAddr == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layoutAddr.compareTo(addr) >= 0) {
|
||||
// have gone past the address, then there is a gap
|
||||
return null;
|
||||
}
|
||||
|
||||
// we are between the start and end; inside the layout
|
||||
if (addr.compareTo(endLayoutAddr) <= 0) {
|
||||
return layoutAddr;
|
||||
}
|
||||
}
|
||||
|
||||
return addr; // should never get here
|
||||
}
|
||||
|
||||
void setBackground(Color c) {
|
||||
flowArrowPanel.setBackground(c);
|
||||
}
|
||||
|
||||
void setForeground(Color c) {
|
||||
flowArrowPanel.setForeground(c);
|
||||
}
|
||||
|
||||
void setHighlightColor(Color c) {
|
||||
flowArrowPanel.setHighlightColor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
plugin.remove(this);
|
||||
program = null;
|
||||
layoutToPixel = null;
|
||||
startAddressToPixel.clear();
|
||||
endAddressToPixel.clear();
|
||||
clearAllArrows();
|
||||
flowArrowPanel.dispose();
|
||||
}
|
||||
|
||||
void goTo(Address address) {
|
||||
ProgramLocation location = new ProgramLocation(program, address);
|
||||
listingPanel.goTo(location);
|
||||
}
|
||||
|
||||
void scrollTo(Address address) {
|
||||
ProgramLocation location = new ProgramLocation(program, address);
|
||||
listingPanel.scrollTo(location);
|
||||
}
|
||||
|
||||
Address getLastAddressOnScreen(Address end, boolean up) {
|
||||
if (up) {
|
||||
return screenTop;
|
||||
}
|
||||
return screenBottom;
|
||||
}
|
||||
|
||||
public void forwardMouseEventToListing(MouseWheelEvent e) {
|
||||
FieldPanel fieldPanel = listingPanel.getFieldPanel();
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
kfm.redispatchEvent(fieldPanel, e);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
/**
|
||||
* An arrow group is all arrows that will be in the same column. The column for an arrow
|
||||
* will be based on the first group to assign a column.
|
||||
*/
|
||||
private class ArrowGroup {
|
||||
|
||||
private Set<FlowArrow> arrows = new HashSet<>();
|
||||
private AddressSet addrs = new AddressSet();
|
||||
private Address lowestEndAddress;
|
||||
|
||||
@SuppressWarnings("unused") // for debug
|
||||
private int column;
|
||||
|
||||
Address getSortAddress() {
|
||||
return lowestEndAddress;
|
||||
}
|
||||
|
||||
void setColumn(int column) {
|
||||
this.column = column;
|
||||
|
||||
for (FlowArrow f : arrows) {
|
||||
f.column = column;
|
||||
}
|
||||
}
|
||||
|
||||
void add(FlowArrow f) {
|
||||
if (lowestEndAddress == null) {
|
||||
lowestEndAddress = f.end;
|
||||
}
|
||||
else if (lowestEndAddress.compareTo(f.end) >= 0) {
|
||||
lowestEndAddress = f.end;
|
||||
}
|
||||
|
||||
arrows.add(f);
|
||||
addrs.add(f.addresses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache of all arrows that start at a given address. This is only used while building the
|
||||
* set of arrows. This tracks arrow usage from the start address to limit the number of arrows
|
||||
* that go offscreen. We allow 1 offscreen arrow above and below for each of the three flow
|
||||
* types: conditional, fallthrough and other. This is used to prevent too many arrows from
|
||||
* cluttering the screen when there are many references starting at the same address.
|
||||
*/
|
||||
private class OffscreenArrowsFlow {
|
||||
|
||||
private Map<Address, OffScreenFlow> flowsAbove = new HashMap<>();
|
||||
private Map<Address, OffScreenFlow> flowsBelow = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Tracks the given arrow and records whether we have seen an arrow at this start address,
|
||||
* going offscreen in the same direction with the same flow category.
|
||||
*
|
||||
* @param arrow the arrow
|
||||
* @return true if we already have a representative arrow
|
||||
*/
|
||||
boolean exists(FlowArrow arrow) {
|
||||
|
||||
boolean isAbove = arrow.end.compareTo(screenTop) < 0;
|
||||
boolean isBelow = arrow.end.compareTo(screenBottom) > 0;
|
||||
if (!(isAbove || isBelow)) {
|
||||
return false; // on-screen
|
||||
}
|
||||
|
||||
OffScreenFlow flow;
|
||||
if (isAbove) {
|
||||
flow = flowsAbove.get(arrow.start);
|
||||
if (flow == null) {
|
||||
flow = new OffScreenFlow();
|
||||
flowsAbove.put(arrow.start, flow);
|
||||
}
|
||||
}
|
||||
else { // isBelow
|
||||
flow = flowsBelow.get(arrow.start);
|
||||
if (flow == null) {
|
||||
flow = new OffScreenFlow();
|
||||
flowsBelow.put(arrow.start, flow);
|
||||
}
|
||||
}
|
||||
|
||||
// sets the flow type and returns true if that type was already set, signalling that we
|
||||
// have seen this an offscreen arrow with this flow type coming from this address in the
|
||||
// up or down direction
|
||||
boolean alreadyHasArrow = flow.setFlow(arrow.refType);
|
||||
return alreadyHasArrow;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
flowsAbove.clear();
|
||||
flowsBelow.clear();
|
||||
}
|
||||
|
||||
private class OffScreenFlow {
|
||||
private boolean conditional;
|
||||
private boolean fallthrough;
|
||||
private boolean other;
|
||||
|
||||
boolean setFlow(RefType type) {
|
||||
boolean wasSet = false;
|
||||
if (type.isConditional()) {
|
||||
wasSet = conditional;
|
||||
conditional = true;
|
||||
}
|
||||
else if (type.isFallthrough()) {
|
||||
wasSet = fallthrough;
|
||||
fallthrough = true;
|
||||
}
|
||||
else {
|
||||
wasSet = other;
|
||||
other = true;
|
||||
}
|
||||
|
||||
return wasSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ class FlowArrowPanel extends JPanel {
|
||||
private Cursor clickCursor;
|
||||
private Cursor defaultCursor;
|
||||
|
||||
private FlowArrowPlugin plugin;
|
||||
private FlowArrowMarginProvider provider;
|
||||
private Color foregroundColor;
|
||||
private Color highlightColor;
|
||||
private Color selectedColor;
|
||||
@@ -44,8 +44,8 @@ class FlowArrowPanel extends JPanel {
|
||||
private SwingUpdateManager mouseClickUpdater;
|
||||
private Point pendingMouseClickPoint;
|
||||
|
||||
FlowArrowPanel(FlowArrowPlugin p) {
|
||||
this.plugin = p;
|
||||
FlowArrowPanel(FlowArrowMarginProvider provider) {
|
||||
this.provider = provider;
|
||||
setMinimumSize(new Dimension(0, 0));
|
||||
setPreferredSize(new Dimension(32, 1));
|
||||
|
||||
@@ -110,18 +110,18 @@ class FlowArrowPanel extends JPanel {
|
||||
}
|
||||
|
||||
private FlowArrow getArrow(Point p) {
|
||||
FlowArrow arrow = getArrow(p, plugin.getFlowArrowIterator());
|
||||
FlowArrow arrow = getArrow(p, provider.getFlowArrowIterator());
|
||||
if (arrow != null) {
|
||||
return arrow;
|
||||
}
|
||||
|
||||
// try the arrows that hang around a bit
|
||||
arrow = getArrow(p, plugin.getSelectedFlowArrows());
|
||||
arrow = getArrow(p, provider.getSelectedFlowArrows());
|
||||
if (arrow != null) {
|
||||
return arrow;
|
||||
}
|
||||
|
||||
return getArrow(p, plugin.getActiveArrows());
|
||||
return getArrow(p, provider.getActiveArrows());
|
||||
}
|
||||
|
||||
private FlowArrow getArrow(Point p, Iterator<FlowArrow> it) {
|
||||
@@ -139,26 +139,24 @@ class FlowArrowPanel extends JPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO to do this, we should probably have another concept of 'navigated'/'current' as to not
|
||||
// confuse the concept of selecting arrows
|
||||
// select any arrow we double-click
|
||||
arrow.selected = true;
|
||||
plugin.setArrowSelected(arrow, true);
|
||||
provider.setArrowSelected(arrow, true);
|
||||
|
||||
Address end = arrow.end;
|
||||
if (end.equals(plugin.getCurrentAddress())) {
|
||||
if (end.equals(provider.getCurrentAddress())) {
|
||||
// go back the other direction
|
||||
end = arrow.start;
|
||||
}
|
||||
|
||||
if (plugin.isOnScreen(end)) {
|
||||
if (provider.isOnScreen(end)) {
|
||||
// don't animate arrows completely on screen
|
||||
plugin.goTo(end);
|
||||
provider.goTo(end);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the animation at the edge of the screen
|
||||
Address start = plugin.getLastAddressOnScreen(end, arrow.isUp());
|
||||
Address start = provider.getLastAddressOnScreen(end, arrow.isUp());
|
||||
|
||||
ScrollingCallback callback = new ScrollingCallback(start, end);
|
||||
Animator animator = AnimationUtils.executeSwingAnimationCallback(callback);
|
||||
@@ -169,7 +167,7 @@ class FlowArrowPanel extends JPanel {
|
||||
FlowArrow arrow = getArrow(point);
|
||||
if (arrow != null) {
|
||||
arrow.selected = !arrow.selected; // toggle
|
||||
plugin.setArrowSelected(arrow, arrow.selected);
|
||||
provider.setArrowSelected(arrow, arrow.selected);
|
||||
repaint();
|
||||
return; // only select one line at a time
|
||||
}
|
||||
@@ -179,13 +177,13 @@ class FlowArrowPanel extends JPanel {
|
||||
public void setBounds(int x, int y, int width, int height) {
|
||||
// note: this gets called as the user drags the divider pane
|
||||
super.setBounds(x, y, width, height);
|
||||
plugin.updateAndRepaint();
|
||||
provider.updateAndRepaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent e) {
|
||||
Point point = e.getPoint();
|
||||
Iterator<FlowArrow> it = plugin.getFlowArrowIterator();
|
||||
Iterator<FlowArrow> it = provider.getFlowArrowIterator();
|
||||
while (it.hasNext()) {
|
||||
FlowArrow arrow = it.next();
|
||||
if (arrow.intersects(point)) {
|
||||
@@ -223,7 +221,7 @@ class FlowArrowPanel extends JPanel {
|
||||
|
||||
super.paintComponent(g);
|
||||
|
||||
Address currentAddress = plugin.getCurrentAddress();
|
||||
Address currentAddress = provider.getCurrentAddress();
|
||||
if (currentAddress == null) {
|
||||
return;
|
||||
}
|
||||
@@ -235,7 +233,7 @@ class FlowArrowPanel extends JPanel {
|
||||
//
|
||||
// Non-selected arrows
|
||||
//
|
||||
Iterator<FlowArrow> it = plugin.getFlowArrowIterator();
|
||||
Iterator<FlowArrow> it = provider.getFlowArrowIterator();
|
||||
while (it.hasNext()) {
|
||||
FlowArrow arrow = it.next();
|
||||
if (arrow.active || arrow.selected) {
|
||||
@@ -251,7 +249,7 @@ class FlowArrowPanel extends JPanel {
|
||||
//
|
||||
// Active arrows--those at the selected address; paint on top of normal arrows
|
||||
//
|
||||
it = plugin.getActiveArrows();
|
||||
it = provider.getActiveArrows();
|
||||
while (it.hasNext()) {
|
||||
FlowArrow arrow = it.next();
|
||||
if (arrow.selected) {
|
||||
@@ -266,7 +264,7 @@ class FlowArrowPanel extends JPanel {
|
||||
// Selected arrows
|
||||
//
|
||||
fgColor = selectedColor;
|
||||
it = plugin.getSelectedFlowArrows();
|
||||
it = provider.getSelectedFlowArrows();
|
||||
while (it.hasNext()) {
|
||||
FlowArrow arrow = it.next();
|
||||
paintJump(g2, arrow, fgColor);
|
||||
@@ -274,7 +272,7 @@ class FlowArrowPanel extends JPanel {
|
||||
}
|
||||
|
||||
private void paintJump(Graphics2D g2, FlowArrow arrow, Color fgColor) {
|
||||
if (plugin.isOffscreen(arrow)) {
|
||||
if (provider.isOffscreen(arrow)) {
|
||||
return; // don't paint linger arrows, such as selected or active arrows
|
||||
}
|
||||
|
||||
@@ -329,19 +327,19 @@ class FlowArrowPanel extends JPanel {
|
||||
// System.err.printf("%1.3f%%\t", (percentComplete * 100));
|
||||
// System.err.println("scrolling to: " + current);
|
||||
|
||||
plugin.scrollTo(current);
|
||||
provider.scrollTo(current);
|
||||
lastAddress = current; // let's us avoid multiple duplicate requests
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
// set the final position
|
||||
// TODO This happens after the animation is finished, which is jarring. If we want this centered,
|
||||
// then we need an entirely different way of animating the transition so that the centering
|
||||
// is part of the animation.
|
||||
// Note: This happens after the animation is finished, which is jarring. If we want this centered,
|
||||
// then we need an entirely different way of animating the transition so that the centering
|
||||
// is part of the animation.
|
||||
// plugin.scrollToCenter(end);
|
||||
|
||||
plugin.goTo(end);
|
||||
provider.goTo(end);
|
||||
}
|
||||
|
||||
void setAnimator(Animator animator) {
|
||||
@@ -392,7 +390,7 @@ class FlowArrowPanel extends JPanel {
|
||||
private class FlowArrowPanelMouseWheelListener implements MouseWheelListener {
|
||||
@Override
|
||||
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||
plugin.forwardMouseEventToListing(e);
|
||||
provider.forwardMouseEventToListing(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,34 +15,18 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.flowarrow;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.*;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.services.CodeViewerService;
|
||||
import ghidra.app.util.viewer.field.ListingColors;
|
||||
import ghidra.app.util.viewer.field.ListingColors.FlowArrowColors;
|
||||
import ghidra.app.util.viewer.listingpanel.*;
|
||||
import ghidra.app.services.ListingMarginProviderService;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
|
||||
import ghidra.app.util.viewer.options.OptionsGui;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.MarkerLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* Plugin that has a margin provider to show the program flow.
|
||||
@@ -53,595 +37,53 @@ import ghidra.program.util.ProgramLocation;
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.CODE_VIEWER,
|
||||
shortDescription = "Show arrows for execution flow",
|
||||
description = "This plugin shows arrows to graphically illustrate "
|
||||
+ "the flow of execution within a function. The arrows indicate "
|
||||
+ "source and destination for jumps; solid lines indicate "
|
||||
+ "unconditional jumps; dashed lines indicate conditional jumps.",
|
||||
servicesRequired = { CodeViewerService.class },
|
||||
description = "This plugin shows arrows to graphically illustrate the flow of execution " +
|
||||
"within a function. The arrows indicate source and destination for jumps; solid lines " +
|
||||
"indicate unconditional jumps; dashed lines indicate conditional jumps.",
|
||||
servicesProvided = { ListingMarginProviderService.class },
|
||||
eventsConsumed = { ProgramActivatedPluginEvent.class, ProgramClosedPluginEvent.class, ProgramLocationPluginEvent.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class FlowArrowPlugin extends Plugin implements MarginProvider, OptionsChangeListener {
|
||||
public class FlowArrowPlugin extends Plugin implements ListingMarginProviderService {
|
||||
|
||||
static final int LEFT_OFFSET = 3;
|
||||
static final int MAX_DEPTH = 16;
|
||||
private static final int MAX_REFSTO_SHOW = 10; // TODO this was 20--sounded like too many
|
||||
|
||||
private FlowArrowPanel flowArrowPanel;
|
||||
private boolean enabled = true;
|
||||
private boolean validState = false;
|
||||
private Address currentAddr;
|
||||
|
||||
/** Start address to the index of the layout for that start address */
|
||||
private Map<Address, Integer> startAddressToPixel = new HashMap<>();
|
||||
|
||||
/** End address to the index of the layout for that end address */
|
||||
private Map<Address, Integer> endAddressToPixel = new HashMap<>();
|
||||
|
||||
/** On-screen layouts and their start/end addresses */
|
||||
private VerticalPixelAddressMap layoutToPixel;
|
||||
private Address screenTop;
|
||||
private Address screenBottom;
|
||||
private int maxDepth;
|
||||
|
||||
private Program program;
|
||||
private CodeViewerService codeViewerService;
|
||||
|
||||
private List<FlowArrow> flowArrows = new ArrayList<>();
|
||||
private Set<FlowArrow> selectedArrows = new HashSet<>();
|
||||
/** Those arrows that start at the current address */
|
||||
private Set<FlowArrow> activeArrows = new HashSet<>();
|
||||
private List<FlowArrowMarginProvider> providers = new ArrayList<>();
|
||||
|
||||
public FlowArrowPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
flowArrowPanel = new FlowArrowPanel(this);
|
||||
|
||||
flowArrowPanel.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
boolean previousState = enabled;
|
||||
enabled = flowArrowPanel.getWidth() > LEFT_OFFSET;
|
||||
if (enabled && !previousState) {
|
||||
updateAndRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown(ComponentEvent e) {
|
||||
boolean previousState = enabled;
|
||||
enabled = flowArrowPanel.getWidth() > LEFT_OFFSET;
|
||||
if (enabled && !previousState) {
|
||||
updateAndRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
enabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
getOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return flowArrowPanel;
|
||||
public ListingMarginProvider createMarginProvider() {
|
||||
FlowArrowMarginProvider provider = new FlowArrowMarginProvider(this);
|
||||
providers.add(provider);
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkerLocation getMarkerLocation(int x, int y) {
|
||||
return null;
|
||||
public boolean isOwner(ListingMarginProvider provider) {
|
||||
return providers.contains(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResizeable() {
|
||||
return true;
|
||||
void remove(FlowArrowMarginProvider provider) {
|
||||
providers.remove(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program, AddressIndexMap addrMap,
|
||||
VerticalPixelAddressMap pixmap) {
|
||||
this.layoutToPixel = pixmap;
|
||||
validateState();
|
||||
updateFlowArrows();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
boolean repaintReqd = false;
|
||||
if (event instanceof ProgramActivatedPluginEvent) {
|
||||
ProgramActivatedPluginEvent evt = (ProgramActivatedPluginEvent) event;
|
||||
program = evt.getActiveProgram();
|
||||
flowArrows.clear();
|
||||
validateState();
|
||||
repaintReqd = true;
|
||||
}
|
||||
else if (event instanceof ProgramLocationPluginEvent) {
|
||||
ProgramLocationPluginEvent evt = (ProgramLocationPluginEvent) event;
|
||||
ProgramLocation location = evt.getLocation();
|
||||
currentAddr = location.getAddress();
|
||||
activeArrows.clear();
|
||||
repaintReqd = true;
|
||||
}
|
||||
else if (event instanceof ProgramClosedPluginEvent) {
|
||||
ProgramClosedPluginEvent programClosedPluginEvent = (ProgramClosedPluginEvent) event;
|
||||
Program closedProgram = programClosedPluginEvent.getProgram();
|
||||
if (program == closedProgram || program == null) {
|
||||
program = null;
|
||||
currentAddr = null;
|
||||
activeArrows.clear();
|
||||
flowArrows.clear();
|
||||
validateState();
|
||||
repaintReqd = true;
|
||||
}
|
||||
}
|
||||
if (repaintReqd) {
|
||||
updateAndRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
if (optionName.equals(OptionsGui.BACKGROUND.getColorOptionName())) {
|
||||
Color c = (Color) newValue;
|
||||
flowArrowPanel.setBackground(c);
|
||||
}
|
||||
else if (optionName.equals(OptionsGui.FLOW_ARROW_NON_ACTIVE.getColorOptionName())) {
|
||||
Color c = (Color) newValue;
|
||||
flowArrowPanel.setForeground(c);
|
||||
}
|
||||
else if (optionName.equals(OptionsGui.FLOW_ARROW_ACTIVE.getColorOptionName())) {
|
||||
Color c = (Color) newValue;
|
||||
flowArrowPanel.setHighlightColor(c);
|
||||
}
|
||||
List<FlowArrowMarginProvider> getProviders() {
|
||||
return Collections.unmodifiableList(providers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
|
||||
startAddressToPixel.clear();
|
||||
endAddressToPixel.clear();
|
||||
layoutToPixel = null;
|
||||
flowArrows.clear();
|
||||
flowArrowPanel.dispose();
|
||||
|
||||
codeViewerService.removeMarginProvider(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
codeViewerService = tool.getService(CodeViewerService.class);
|
||||
codeViewerService.addMarginProvider(this);
|
||||
}
|
||||
|
||||
Address getCurrentAddress() {
|
||||
return currentAddr;
|
||||
}
|
||||
|
||||
Address getScreenBottomAddr() {
|
||||
return screenBottom;
|
||||
}
|
||||
|
||||
int getMaxDepth() {
|
||||
return maxDepth;
|
||||
}
|
||||
|
||||
boolean isOnScreen(Address address) {
|
||||
if (screenBottom == null || screenTop == null) {
|
||||
return true; // shouldn't happen
|
||||
}
|
||||
|
||||
if (address.compareTo(screenTop) < 0) {
|
||||
// above the top of the screen
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address.compareTo(screenBottom) > 0) {
|
||||
// below the bottom of the screen
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isOffscreen(FlowArrow arrow) {
|
||||
if (screenBottom == null || screenTop == null) {
|
||||
return true; // shouldn't happen
|
||||
}
|
||||
|
||||
if (arrow.start.compareTo(screenTop) < 0 && arrow.end.compareTo(screenTop) < 0) {
|
||||
// start and end are above the top of the screen
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arrow.start.compareTo(screenBottom) > 0 && arrow.end.compareTo(screenBottom) > 0) {
|
||||
// start and end are below the bottom of the screen
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isBelowScreen(Address address) {
|
||||
if (screenBottom == null || screenTop == null) {
|
||||
return true; // shouldn't happen
|
||||
}
|
||||
|
||||
return address.compareTo(screenBottom) > 0;
|
||||
}
|
||||
|
||||
/* The y value of the start of the layout at the given address. */
|
||||
Integer getStartPos(Address addr) {
|
||||
return startAddressToPixel.get(addr);
|
||||
}
|
||||
|
||||
/* The y value of the end of the layout at the given address. */
|
||||
Integer getEndPos(Address addr) {
|
||||
return endAddressToPixel.get(addr);
|
||||
}
|
||||
|
||||
void setArrowSelected(FlowArrow arrow, boolean selected) {
|
||||
if (selected) {
|
||||
selectedArrows.add(arrow);
|
||||
}
|
||||
else {
|
||||
selectedArrows.remove(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<FlowArrow> getSelectedFlowArrows() {
|
||||
return selectedArrows.iterator();
|
||||
}
|
||||
|
||||
Iterator<FlowArrow> getFlowArrowIterator() {
|
||||
return flowArrows.iterator();
|
||||
}
|
||||
|
||||
/* Those arrows starting at the current address */
|
||||
Iterator<FlowArrow> getActiveArrows() {
|
||||
return activeArrows.iterator();
|
||||
}
|
||||
|
||||
private void resetSelectedArrows() {
|
||||
for (FlowArrow arrow : selectedArrows) {
|
||||
arrow.resetShape();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetActiveArrows() {
|
||||
for (FlowArrow arrow : activeArrows) {
|
||||
arrow.resetShape();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveActiveArrows() {
|
||||
|
||||
if (!activeArrows.isEmpty()) {
|
||||
resetActiveArrows();
|
||||
return; // don't overwrite existing values
|
||||
}
|
||||
|
||||
if (currentAddr == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (FlowArrow arrow : flowArrows) {
|
||||
if (currentAddr.equals(arrow.start)) {
|
||||
arrow.active = true;
|
||||
activeArrows.add(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeArrows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over each other FlowArrow object to check overlaps
|
||||
*
|
||||
* @return FlowArrow objects that have a start/end address in common
|
||||
*/
|
||||
private List<FlowArrow> getArrowsAtSameDepth(FlowArrow jump, List<FlowArrow> allArrows) {
|
||||
|
||||
List<FlowArrow> results = new ArrayList<>();
|
||||
for (FlowArrow otherArrows : allArrows) {
|
||||
if (jump == otherArrows) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sharesEndpoint(jump, otherArrows)) {
|
||||
results.add(otherArrows);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private boolean sharesEndpoint(FlowArrow a1, FlowArrow a2) {
|
||||
return a1.start.equals(a2.start) || a1.end.equals(a2.end);
|
||||
}
|
||||
|
||||
private void computeAllArrowsDepth() {
|
||||
|
||||
// Find overlapping arrows and compute depth
|
||||
for (FlowArrow arrow : flowArrows) {
|
||||
|
||||
List<FlowArrow> sameDepth = null;
|
||||
|
||||
// If we have not already assigned a depth to this FlowArrow object
|
||||
if (arrow.depth == -1) {
|
||||
|
||||
sameDepth = getArrowsAtSameDepth(arrow, flowArrows);
|
||||
|
||||
// Compute the full address set for all same-depth arrows
|
||||
AddressSet sameDepthAddrs = new AddressSet(arrow.addressSet);
|
||||
|
||||
for (FlowArrow otherArrow : sameDepth) {
|
||||
sameDepthAddrs.add(otherArrow.addressSet);
|
||||
}
|
||||
|
||||
List<FlowArrow> differentDepth = new ArrayList<>(flowArrows);
|
||||
differentDepth.removeAll(sameDepth);
|
||||
differentDepth.remove(arrow);
|
||||
assignArrowDepth(arrow, sameDepthAddrs, differentDepth);
|
||||
}
|
||||
else {
|
||||
sameDepth = Collections.emptyList();
|
||||
}
|
||||
|
||||
// If this is the deepest arrow seen, increase maxDepth
|
||||
if (arrow.depth > maxDepth) {
|
||||
maxDepth = arrow.depth;
|
||||
}
|
||||
|
||||
// Make same source/dest arrows the same depth
|
||||
for (FlowArrow same : sameDepth) {
|
||||
same.depth = arrow.depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Calculates depth based on all other arrows that DO NOT share an endpoint */
|
||||
private void assignArrowDepth(FlowArrow arrow, AddressSet overlappingAddresses,
|
||||
List<FlowArrow> allArrows) {
|
||||
|
||||
//Keep track of which depths are used over current arrow range
|
||||
boolean[] usedDepths = new boolean[MAX_DEPTH];
|
||||
|
||||
// Find all intersecting used depths
|
||||
for (FlowArrow otherArrow : allArrows) {
|
||||
if (otherArrow.depth == -1 || otherArrow.depth >= MAX_DEPTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sharesEndpoint(arrow, otherArrow)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (overlappingAddresses.intersects(otherArrow.addressSet)) {
|
||||
usedDepths[otherArrow.depth] = true;
|
||||
}
|
||||
}
|
||||
|
||||
arrow.depth = 0;
|
||||
while (arrow.depth < usedDepths.length && usedDepths[arrow.depth]) {
|
||||
arrow.depth++;
|
||||
}
|
||||
}
|
||||
|
||||
private List<FlowArrow> getFlowArrowsForScreenInstructions(AddressSetView screenAddresses) {
|
||||
|
||||
List<FlowArrow> results = new ArrayList<>();
|
||||
ArrowCache arrowCache = new ArrowCache();
|
||||
CodeUnitIterator it = program.getListing()
|
||||
.getCodeUnitIterator(CodeUnit.INSTRUCTION_PROPERTY, screenAddresses, true);
|
||||
|
||||
while (it.hasNext()) {
|
||||
CodeUnit cu = it.next();
|
||||
Instruction instruction = (Instruction) cu;
|
||||
|
||||
// incoming
|
||||
int refCount = program.getReferenceManager().getReferenceCountTo(cu.getMinAddress());
|
||||
if (refCount < MAX_REFSTO_SHOW) {
|
||||
ReferenceIterator instructionIt = instruction.getReferenceIteratorTo();
|
||||
while (instructionIt.hasNext()) {
|
||||
Reference ref = instructionIt.next();
|
||||
createFlowArrow(results, arrowCache, ref);
|
||||
}
|
||||
}
|
||||
|
||||
arrowCache.clear();
|
||||
|
||||
// outgoing
|
||||
Reference[] refs = instruction.getReferencesFrom();
|
||||
for (Reference ref : refs) {
|
||||
createFlowArrow(results, arrowCache, ref);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private void createFlowArrow(List<FlowArrow> results, ArrowCache arrowCache, Reference ref) {
|
||||
RefType type = ref.getReferenceType();
|
||||
if (!(type.isJump() || type.isFallthrough())) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlowArrow arrow = getFlowArrow(ref);
|
||||
if (arrow == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arrowCache.isDuplicateOffscreen(arrow.start, arrow.end, type)) {
|
||||
results.add(arrow);
|
||||
updateArrowSets(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unusual Code: We keep arrows in 3 sets: all arrows, selected arrows, and active arrows.
|
||||
* Further, we rebuild arrows as the screen moves, causing the x coordinate to change as arrows
|
||||
* that are no longer on the screen are removed and as new arrows are added. We want to make
|
||||
* sure that we don't end up with an arrow in the selected/active sets that are the same as the
|
||||
* one in the 'all' set, but with a different width. This causes both arrows to become
|
||||
* visible--basically, the selected arrows can become stale as their width changes. This code is
|
||||
* meant to address this out-of-sync behavior.
|
||||
*
|
||||
* @param arrow the updated form of the arrow
|
||||
*/
|
||||
private void updateArrowSets(FlowArrow arrow) {
|
||||
if (selectedArrows.remove(arrow)) {
|
||||
arrow.selected = true;
|
||||
selectedArrows.add(arrow);
|
||||
}
|
||||
|
||||
if (activeArrows.remove(arrow)) {
|
||||
arrow.active = true;
|
||||
activeArrows.add(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
private FlowArrow getFlowArrow(Reference ref) {
|
||||
Address start = toLayoutAddress(ref.getFromAddress());
|
||||
Address end = toLayoutAddress(ref.getToAddress());
|
||||
if (start == null || end == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!start.hasSameAddressSpace(end)) {
|
||||
return null; // is this right??
|
||||
}
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
if (!memory.contains(end)) {
|
||||
return null; // bad disassembly
|
||||
}
|
||||
|
||||
RefType refType = ref.getReferenceType();
|
||||
if (refType.isFallthrough()) {
|
||||
return new FallthroughFlowArrow(this, flowArrowPanel, start, end, refType);
|
||||
}
|
||||
else if (refType.isConditional()) {
|
||||
return new ConditionalFlowArrow(this, flowArrowPanel, start, end, refType);
|
||||
}
|
||||
|
||||
return new DefaultFlowArrow(this, flowArrowPanel, start, end, refType);
|
||||
}
|
||||
|
||||
private void validateState() {
|
||||
validState = false;
|
||||
if (program == null || layoutToPixel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int n = layoutToPixel.getNumLayouts();
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Address bottomAddr = layoutToPixel.getLayoutAddress(n - 1);
|
||||
if (bottomAddr != null) {
|
||||
AddressSpace testSpace = bottomAddr.getAddressSpace();
|
||||
validState =
|
||||
(program.getAddressFactory().getAddressSpace(testSpace.getSpaceID()) == testSpace);
|
||||
}
|
||||
}
|
||||
|
||||
void updateAndRepaint() {
|
||||
update();
|
||||
flowArrowPanel.repaint();
|
||||
}
|
||||
|
||||
private void update() {
|
||||
if (!enabled || !validState) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Compute addresses in local range
|
||||
int n = layoutToPixel.getNumLayouts();
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Address startAddress = layoutToPixel.getLayoutAddress(0);
|
||||
Address endAddress = layoutToPixel.getLayoutAddress(n - 1);
|
||||
|
||||
screenTop = startAddress;
|
||||
screenBottom = endAddress;
|
||||
flowArrows.clear();
|
||||
startAddressToPixel.clear();
|
||||
endAddressToPixel.clear();
|
||||
maxDepth = 0;
|
||||
|
||||
resetSelectedArrows();
|
||||
|
||||
if (screenTop == null || screenBottom == null || n > 500) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all addresses that are on the screen and compute y co-ordinate
|
||||
for (int layout = 0; layout < n; layout++) {
|
||||
Address addr = layoutToPixel.getLayoutAddress(layout);
|
||||
if (addr != null) {
|
||||
startAddressToPixel.put(addr, layoutToPixel.getBeginPosition(layout));
|
||||
endAddressToPixel.put(addr, layoutToPixel.getEndPosition(layout));
|
||||
}
|
||||
}
|
||||
|
||||
// Intersect the screenTop and screenBottom with the currentView
|
||||
AddressSetView flowSet = layoutToPixel.getAddressSet();
|
||||
|
||||
// Find references at the instructions on the screen
|
||||
flowArrows = getFlowArrowsForScreenInstructions(flowSet);
|
||||
|
||||
Collections.sort(flowArrows, (a1, a2) -> (a1).end.compareTo((a2).end));
|
||||
|
||||
computeAllArrowsDepth();
|
||||
|
||||
saveActiveArrows();
|
||||
}
|
||||
|
||||
private Address toLayoutAddress(Address addr) {
|
||||
Object pixel = startAddressToPixel.get(addr);
|
||||
if (pixel != null || addr.compareTo(screenTop) < 0 || addr.compareTo(screenBottom) > 0) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
int n = layoutToPixel.getNumLayouts();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Address layoutAddr = layoutToPixel.getLayoutAddress(i);
|
||||
Address endLayoutAddr = layoutToPixel.getLayoutEndAddress(i);
|
||||
if (layoutAddr == null || endLayoutAddr == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layoutAddr.compareTo(addr) >= 0) {
|
||||
// have gone past the address, then there is a gap
|
||||
return null;
|
||||
}
|
||||
|
||||
// we are between the start and end; inside the layout
|
||||
if (addr.compareTo(endLayoutAddr) <= 0) {
|
||||
return layoutAddr;
|
||||
}
|
||||
}
|
||||
|
||||
return addr; // should never get here
|
||||
}
|
||||
|
||||
private void updateFlowArrows() {
|
||||
if (enabled) {
|
||||
updateAndRepaint();
|
||||
for (FlowArrowMarginProvider provider : new ArrayList<>(providers)) {
|
||||
provider.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void getOptions() {
|
||||
// Note: these are here merely as a convenience so users don't have to use the theme editor.
|
||||
ToolOptions opt = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
|
||||
|
||||
opt.registerThemeColorBinding(OptionsGui.FLOW_ARROW_NON_ACTIVE.getColorOptionName(),
|
||||
OptionsGui.FLOW_ARROW_NON_ACTIVE.getThemeColorId(), null,
|
||||
"The color for an arrow with no endpoint at the current address");
|
||||
@@ -651,136 +93,6 @@ public class FlowArrowPlugin extends Plugin implements MarginProvider, OptionsCh
|
||||
opt.registerThemeColorBinding(OptionsGui.FLOW_ARROW_SELECTED.getColorOptionName(),
|
||||
OptionsGui.FLOW_ARROW_SELECTED.getThemeColorId(), null,
|
||||
"The color for an arrow that has been selected by the user");
|
||||
|
||||
flowArrowPanel.setBackground(ListingColors.BACKGROUND);
|
||||
flowArrowPanel.setForeground(FlowArrowColors.INACTIVE);
|
||||
flowArrowPanel.setHighlightColor(FlowArrowColors.ACTIVE);
|
||||
flowArrowPanel.setSelectedColor(FlowArrowColors.SELECTED);
|
||||
|
||||
opt.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class ArrowCache {
|
||||
Address address;
|
||||
boolean conditionalOffTop;
|
||||
boolean conditionalOffBottom;
|
||||
boolean fallthroughOffTop;
|
||||
boolean fallthroughOffBottom;
|
||||
boolean otherOffTop;
|
||||
boolean otherOffBottom;
|
||||
|
||||
void clear() {
|
||||
address = null;
|
||||
conditionalOffTop = false;
|
||||
conditionalOffBottom = false;
|
||||
fallthroughOffTop = false;
|
||||
fallthroughOffBottom = false;
|
||||
otherOffTop = false;
|
||||
otherOffBottom = false;
|
||||
}
|
||||
|
||||
boolean isDuplicateOffscreen(Address pointOfInterest, Address otherEnd, RefType refType) {
|
||||
if (!pointOfInterest.equals(address)) {
|
||||
clear();
|
||||
address = pointOfInterest;
|
||||
}
|
||||
|
||||
// above the top of the screen
|
||||
if (otherEnd.compareTo(screenTop) < 0) {
|
||||
if (refType.isConditional()) {
|
||||
if (conditionalOffTop) {
|
||||
return true;
|
||||
}
|
||||
conditionalOffTop = true;
|
||||
}
|
||||
else if (refType.isFallthrough()) {
|
||||
if (fallthroughOffTop) {
|
||||
return true;
|
||||
}
|
||||
fallthroughOffTop = true;
|
||||
}
|
||||
else {
|
||||
if (otherOffTop) {
|
||||
return true;
|
||||
}
|
||||
otherOffTop = true;
|
||||
}
|
||||
}
|
||||
|
||||
// below the bottom of the screen
|
||||
else if (otherEnd.compareTo(screenBottom) > 0) {
|
||||
if (refType.isConditional()) {
|
||||
if (conditionalOffBottom) {
|
||||
return true;
|
||||
}
|
||||
conditionalOffBottom = true;
|
||||
}
|
||||
else if (refType.isFallthrough()) {
|
||||
if (fallthroughOffBottom) {
|
||||
return true;
|
||||
}
|
||||
fallthroughOffBottom = true;
|
||||
}
|
||||
else {
|
||||
if (otherOffBottom) {
|
||||
return true;
|
||||
}
|
||||
otherOffBottom = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void goTo(Address address) {
|
||||
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
|
||||
ListingPanel listingPanel = codeViewer.getListingPanel();
|
||||
ProgramLocation location = new ProgramLocation(program, address);
|
||||
listingPanel.goTo(location, false);
|
||||
}
|
||||
|
||||
void scrollTo(Address address) {
|
||||
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
|
||||
ListingPanel listingPanel = codeViewer.getListingPanel();
|
||||
ProgramLocation location = new ProgramLocation(program, address);
|
||||
// listingPanel.setCursorPosition(location);
|
||||
// listingPanel.goTo(location, true);
|
||||
listingPanel.scrollTo(location);
|
||||
}
|
||||
|
||||
void scrollToCenter(Address address) {
|
||||
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
|
||||
ListingPanel listingPanel = codeViewer.getListingPanel();
|
||||
ProgramLocation location = new ProgramLocation(program, address);
|
||||
listingPanel.center(location);
|
||||
}
|
||||
|
||||
Address getAddressAtPoint(Point p) {
|
||||
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
|
||||
ListingPanel listingPanel = codeViewer.getListingPanel();
|
||||
ProgramLocation location = listingPanel.getProgramLocation(p);
|
||||
if (location == null) {
|
||||
return null;
|
||||
}
|
||||
return location.getAddress();
|
||||
}
|
||||
|
||||
Address getLastAddressOnScreen(Address end, boolean up) {
|
||||
if (up) {
|
||||
return screenTop;
|
||||
}
|
||||
return screenBottom;
|
||||
}
|
||||
|
||||
public void forwardMouseEventToListing(MouseWheelEvent e) {
|
||||
CodeViewerService codeViewer = tool.getService(CodeViewerService.class);
|
||||
ListingPanel listingPanel = codeViewer.getListingPanel();
|
||||
FieldPanel fieldPanel = listingPanel.getFieldPanel();
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().redispatchEvent(fieldPanel, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -28,26 +27,27 @@ class FlowArrowShapeFactory {
|
||||
private static final int TRIANGLE_HEIGHT = 9;
|
||||
private static final int TRIANGLE_WIDTH = 7;
|
||||
|
||||
static Shape createArrowBody(FlowArrowPlugin plugin, FlowArrow arrow, int width, int height,
|
||||
static Shape createArrowBody(FlowArrowMarginProvider provider, FlowArrow arrow, int width,
|
||||
int height,
|
||||
int lineSpacing) {
|
||||
|
||||
GeneralPath linePath = new GeneralPath();
|
||||
|
||||
// Compute the start Y coordinate (place at proper offset, fix if off screen)
|
||||
Integer startTop = plugin.getStartPos(arrow.start);
|
||||
Integer startBottom = plugin.getEndPos(arrow.start);
|
||||
Integer startTop = provider.getStartPos(arrow.start);
|
||||
Integer startBottom = provider.getEndPos(arrow.start);
|
||||
int startY = 0;
|
||||
if (startTop != null && startBottom != null) {
|
||||
int start = startTop;
|
||||
int end = startBottom;
|
||||
startY = (start + end) / 2;// middle of line
|
||||
}
|
||||
else if (plugin.isBelowScreen(arrow.start)) {
|
||||
else if (provider.isBelowScreen(arrow.start)) {
|
||||
startY = height;
|
||||
}
|
||||
|
||||
Integer endTop = plugin.getStartPos(arrow.end);
|
||||
Integer endBottom = plugin.getEndPos(arrow.end);
|
||||
Integer endTop = provider.getStartPos(arrow.end);
|
||||
Integer endBottom = provider.getEndPos(arrow.end);
|
||||
int endY = 0;
|
||||
if (endTop != null && endBottom != null) {
|
||||
int start = endTop;
|
||||
@@ -55,11 +55,11 @@ class FlowArrowShapeFactory {
|
||||
endY = (start + end) / 2;
|
||||
endY = Math.min(endY, height); // ensure on screen
|
||||
}
|
||||
else if (plugin.isBelowScreen(arrow.end)) {
|
||||
else if (provider.isBelowScreen(arrow.end)) {
|
||||
endY = height;
|
||||
}
|
||||
|
||||
int x = width - ((arrow.depth + 1) * lineSpacing);
|
||||
int x = width - ((arrow.column + 1) * lineSpacing);
|
||||
if (x < 3) {
|
||||
x = 3;
|
||||
}
|
||||
@@ -126,12 +126,13 @@ class FlowArrowShapeFactory {
|
||||
return linePath;
|
||||
}
|
||||
|
||||
static Shape createArrowHead(FlowArrowPlugin plugin, FlowArrow arrow, int width, int height,
|
||||
static Shape createArrowHead(FlowArrowMarginProvider provider, FlowArrow arrow, int width,
|
||||
int height,
|
||||
int lineSpacing) {
|
||||
|
||||
// Compute the start Y coordinate (place at proper offset, fix if off screen)
|
||||
Integer addrStartInt = plugin.getStartPos(arrow.end);
|
||||
Integer addrEndInt = plugin.getEndPos(arrow.end);
|
||||
Integer addrStartInt = provider.getStartPos(arrow.end);
|
||||
Integer addrEndInt = provider.getEndPos(arrow.end);
|
||||
int endY = 0;
|
||||
if (addrStartInt != null && addrEndInt != null) {
|
||||
int start = addrStartInt;
|
||||
@@ -139,11 +140,11 @@ class FlowArrowShapeFactory {
|
||||
endY = (start + end) / 2;
|
||||
endY = Math.min(endY, height); // ensure on screen
|
||||
}
|
||||
else if (plugin.isBelowScreen(arrow.end)) {
|
||||
else if (provider.isBelowScreen(arrow.end)) {
|
||||
endY = height;
|
||||
}
|
||||
|
||||
int x = width - ((arrow.depth + 1) * lineSpacing);
|
||||
int x = width - ((arrow.column + 1) * lineSpacing);
|
||||
if (x < 0) {
|
||||
x = 3;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -40,6 +40,7 @@ import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.ColorUtils.ColorBlender;
|
||||
import ghidra.util.UniversalID;
|
||||
import ghidra.util.datastruct.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
@@ -70,11 +71,9 @@ public class MarkerManager implements MarkerService {
|
||||
private SwingUpdateManager updater;
|
||||
private GoToService goToService;
|
||||
|
||||
private MarkerMarginProvider primaryMarginProvider;
|
||||
private WeakSet<MarkerMarginProvider> marginProviders =
|
||||
WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||
|
||||
private MarkerOverviewProvider primaryOverviewProvider;
|
||||
private WeakSet<MarkerOverviewProvider> overviewProviders =
|
||||
WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||
|
||||
@@ -101,9 +100,6 @@ public class MarkerManager implements MarkerService {
|
||||
notifyListeners();
|
||||
});
|
||||
|
||||
primaryMarginProvider = createMarginProvider();
|
||||
primaryOverviewProvider = createOverviewProvider();
|
||||
|
||||
Gui.addThemeListener(themeListener);
|
||||
}
|
||||
|
||||
@@ -220,8 +216,12 @@ public class MarkerManager implements MarkerService {
|
||||
|
||||
}
|
||||
|
||||
public MarkerMarginProvider getMarginProvider() {
|
||||
return primaryMarginProvider;
|
||||
public boolean contains(ListingMarginProvider provider) {
|
||||
return marginProviders.contains(provider);
|
||||
}
|
||||
|
||||
public boolean contains(ListingOverviewProvider provider) {
|
||||
return overviewProviders.contains(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -231,8 +231,8 @@ public class MarkerManager implements MarkerService {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public OverviewProvider getOverviewProvider() {
|
||||
return primaryOverviewProvider;
|
||||
public void removeProvider(MarkerMarginProvider provider) {
|
||||
marginProviders.remove(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -246,7 +246,6 @@ public class MarkerManager implements MarkerService {
|
||||
Gui.removeThemeListener(themeListener);
|
||||
updater.dispose();
|
||||
markerSetCache.clear();
|
||||
overviewProviders.forEach(provider -> provider.dispose());
|
||||
}
|
||||
|
||||
void navigateTo(Navigatable navigatable, Program program, int x, int y, int viewHeight,
|
||||
@@ -276,13 +275,13 @@ public class MarkerManager implements MarkerService {
|
||||
entry.paintNavigation(g, panel.getViewHeight(), panel.getWidth(), addrMap);
|
||||
}
|
||||
|
||||
void paintMarkers(Program program, Graphics g, VerticalPixelAddressMap pixmap,
|
||||
AddressIndexMap addrMap) {
|
||||
void paintMarkers(UniversalID ownerId, Program program, Graphics g,
|
||||
VerticalPixelAddressMap pixMap, AddressIndexMap addrMap) {
|
||||
MarkerSetCacheEntry entry = markerSetCache.get(program);
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
entry.paintMarkers(g, pixmap, addrMap);
|
||||
entry.paintMarkers(ownerId, g, pixMap, addrMap);
|
||||
}
|
||||
|
||||
void showToolTipPopup(MouseEvent event, String tip) {
|
||||
@@ -301,10 +300,6 @@ public class MarkerManager implements MarkerService {
|
||||
popupWindow.showPopup(event);
|
||||
}
|
||||
|
||||
/*testing*/ String generateToolTip(MouseEvent event) {
|
||||
return primaryMarginProvider.generateToolTip(event);
|
||||
}
|
||||
|
||||
List<String> getMarkerTooltipLines(Program program, int y, int x, Address minAddr,
|
||||
Address maxAddr) {
|
||||
MarkerSetCacheEntry entry = markerSetCache.get(program);
|
||||
@@ -484,6 +479,7 @@ public class MarkerManager implements MarkerService {
|
||||
if (program == null || program.isClosed()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MarkerSetCacheEntry entry = map.computeIfAbsent(program, this::newEntry);
|
||||
if (program.isClosed()) {
|
||||
map.remove(program);
|
||||
@@ -571,13 +567,22 @@ public class MarkerManager implements MarkerService {
|
||||
}
|
||||
}
|
||||
|
||||
void paintMarkers(Graphics g, VerticalPixelAddressMap pixmap, AddressIndexMap addrMap) {
|
||||
void paintMarkers(UniversalID ownerId, Graphics g, VerticalPixelAddressMap pixMap,
|
||||
AddressIndexMap addrMap) {
|
||||
int count = 0;
|
||||
for (MarkerSetImpl markers : markerSets) {
|
||||
count++;
|
||||
if (markers.active) {
|
||||
markers.paintMarkers(g, count++, pixmap, addrMap);
|
||||
if (!markers.active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UniversalID markerId = markers.getOwnerId();
|
||||
if (markerId != null && markerId != ownerId) {
|
||||
// a non-global marker that does not match the owner being painted
|
||||
continue;
|
||||
}
|
||||
|
||||
markers.paintMarkers(g, count++, pixMap, addrMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,4 +643,5 @@ public class MarkerManager implements MarkerService {
|
||||
return new ArrayList<>(markerSets);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -20,6 +20,8 @@ import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
@@ -39,17 +41,17 @@ import ghidra.util.HelpLocation;
|
||||
"supported; point markers and area markers. Area markers are used to indicate a range " +
|
||||
"value such as selection. Point markers are used to represent individual addresses such " +
|
||||
"as bookmarks.",
|
||||
servicesRequired = { CodeViewerService.class, GoToService.class },
|
||||
servicesProvided = { MarkerService.class },
|
||||
servicesRequired = { GoToService.class },
|
||||
servicesProvided = { MarkerService.class, ListingMarginProviderService.class, ListingOverviewProviderService.class },
|
||||
eventsConsumed = {}
|
||||
)
|
||||
//@formatter:on
|
||||
/**
|
||||
* Plugin to manage marker and navigation panels.
|
||||
*/
|
||||
public class MarkerManagerPlugin extends Plugin {
|
||||
public class MarkerManagerPlugin extends Plugin
|
||||
implements ListingMarginProviderService, ListingOverviewProviderService {
|
||||
|
||||
private CodeViewerService codeViewerService;
|
||||
private MarkerManager markerManager;
|
||||
|
||||
public MarkerManagerPlugin(PluginTool tool) {
|
||||
@@ -65,17 +67,26 @@ public class MarkerManagerPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
if (codeViewerService != null) {
|
||||
codeViewerService.removeMarginProvider(markerManager.getMarginProvider());
|
||||
codeViewerService.removeOverviewProvider(markerManager.getOverviewProvider());
|
||||
}
|
||||
markerManager.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
codeViewerService = tool.getService(CodeViewerService.class);
|
||||
codeViewerService.addMarginProvider(markerManager.getMarginProvider());
|
||||
codeViewerService.addOverviewProvider(markerManager.getOverviewProvider());
|
||||
public ListingMarginProvider createMarginProvider() {
|
||||
return markerManager.createMarginProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwner(ListingMarginProvider provider) {
|
||||
return markerManager.contains(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListingOverviewProvider createOverviewProvider() {
|
||||
return markerManager.createOverviewProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwner(ListingOverviewProvider provider) {
|
||||
return markerManager.contains(provider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -28,6 +28,8 @@ import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.MarkerLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.UniversalID;
|
||||
|
||||
/**
|
||||
* The provider which renders the marker margin, usually placed to the left of listing
|
||||
@@ -37,7 +39,7 @@ import ghidra.program.util.MarkerLocation;
|
||||
* These are managed by a {@link MarkerManager}. Obtain one via
|
||||
* {@link MarkerService#createMarginProvider()}.
|
||||
*/
|
||||
public class MarkerMarginProvider implements MarginProvider {
|
||||
public class MarkerMarginProvider implements ListingMarginProvider {
|
||||
private final MarkerManager markerManager;
|
||||
private final MarkerPanel markerPanel;
|
||||
|
||||
@@ -62,6 +64,22 @@ public class MarkerMarginProvider implements MarginProvider {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UniversalID ownerId) {
|
||||
markerPanel.setOwnerId(ownerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(ProgramLocation location) {
|
||||
// we don't use locations
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
markerManager.removeProvider(this);
|
||||
markerPanel.dispose();
|
||||
}
|
||||
|
||||
void repaintPanel() {
|
||||
markerPanel.repaint();
|
||||
}
|
||||
@@ -95,12 +113,11 @@ public class MarkerMarginProvider implements MarginProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program, AddressIndexMap addrMap,
|
||||
VerticalPixelAddressMap pixmap) {
|
||||
this.program = program;
|
||||
this.pixmap = pixmap;
|
||||
|
||||
this.markerPanel.setProgram(program, addrMap, pixmap);
|
||||
public void screenDataChanged(ListingPanel listing, AddressIndexMap addrMap,
|
||||
VerticalPixelAddressMap pixMap) {
|
||||
this.program = listing.getProgram();
|
||||
this.pixmap = pixMap;
|
||||
this.markerPanel.listingUpdated(listing, addrMap, pixMap);
|
||||
|
||||
markerManager.updateMarkerSets(program, true, false, true);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import ghidra.GhidraOptions;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.services.MarkerService;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@@ -46,7 +46,7 @@ import ghidra.util.Swing;
|
||||
* These are managed by a {@link MarkerManager}. Obtain one via
|
||||
* {@link MarkerService#createOverviewProvider()}.
|
||||
*/
|
||||
public class MarkerOverviewProvider implements OverviewProvider {
|
||||
public class MarkerOverviewProvider implements ListingOverviewProvider {
|
||||
private final PluginTool tool;
|
||||
private final String owner;
|
||||
|
||||
@@ -74,7 +74,8 @@ public class MarkerOverviewProvider implements OverviewProvider {
|
||||
actionList = new MarkerActionList();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
actionList.dispose();
|
||||
}
|
||||
|
||||
@@ -88,11 +89,11 @@ public class MarkerOverviewProvider implements OverviewProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program, AddressIndexMap map) {
|
||||
this.program = program;
|
||||
public void screenDataChanged(Program p, AddressIndexMap addressMap) {
|
||||
this.program = p;
|
||||
|
||||
navigationPanel.setProgram(program, map);
|
||||
markerManager.updateMarkerSets(program, true, true, false);
|
||||
navigationPanel.setProgram(p, addressMap);
|
||||
markerManager.updateMarkerSets(p, true, true, false);
|
||||
actionList.refresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -24,10 +24,12 @@ import javax.swing.JPanel;
|
||||
import javax.swing.ToolTipManager;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.listingpanel.VerticalPixelAddressMap;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.UniversalID;
|
||||
|
||||
/**
|
||||
* Panel to display markers. Normally placed to the left hand side of the scrolled
|
||||
@@ -36,29 +38,40 @@ import ghidra.program.model.listing.Program;
|
||||
public class MarkerPanel extends JPanel {
|
||||
|
||||
private MarkerManager manager;
|
||||
|
||||
private Program program;
|
||||
private AddressIndexMap addrMap;
|
||||
private VerticalPixelAddressMap pixmap;
|
||||
private VerticalPixelAddressMap pixMap;
|
||||
private UniversalID ownerId;
|
||||
|
||||
MarkerPanel(MarkerManager manager) {
|
||||
super();
|
||||
this.manager = manager;
|
||||
|
||||
this.setPreferredSize(new Dimension(16, 1));
|
||||
ToolTipManager.sharedInstance().registerComponent(this);
|
||||
}
|
||||
|
||||
void setProgram(Program program, AddressIndexMap addrMap, VerticalPixelAddressMap pixmap) {
|
||||
this.program = program;
|
||||
this.addrMap = addrMap;
|
||||
this.pixmap = pixmap;
|
||||
void setOwnerId(UniversalID ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
this.program = null;
|
||||
this.addrMap = null;
|
||||
this.pixMap = null;
|
||||
ToolTipManager.sharedInstance().unregisterComponent(this);
|
||||
}
|
||||
|
||||
void listingUpdated(ListingPanel listingPanel, AddressIndexMap addressMap,
|
||||
VerticalPixelAddressMap pixelMap) {
|
||||
this.program = listingPanel.getProgram();
|
||||
this.addrMap = addressMap;
|
||||
this.pixMap = pixelMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
manager.paintMarkers(program, g, pixmap, addrMap);
|
||||
manager.paintMarkers(ownerId, program, g, pixMap, addrMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,14 +94,14 @@ public class MarkerPanel extends JPanel {
|
||||
}
|
||||
|
||||
String generateToolTip(MouseEvent event) {
|
||||
if (pixmap == null) {
|
||||
if (pixMap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int y = event.getY();
|
||||
int x = event.getX();
|
||||
int layoutIndex = pixmap.findLayoutAt(y);
|
||||
Address layoutAddress = pixmap.getLayoutAddress(layoutIndex);
|
||||
int layoutIndex = pixMap.findLayoutAt(y);
|
||||
Address layoutAddress = pixMap.getLayoutAddress(layoutIndex);
|
||||
if (layoutAddress == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -99,7 +112,8 @@ public class MarkerPanel extends JPanel {
|
||||
|
||||
private List<String> getMarkerTooltipLines(int y, int x, int layoutIndex,
|
||||
Address layoutAddress) {
|
||||
Address endAddr = pixmap.getLayoutEndAddress(layoutIndex);
|
||||
Address endAddr = pixMap.getLayoutEndAddress(layoutIndex);
|
||||
return manager.getMarkerTooltipLines(program, y, layoutIndex, layoutAddress, endAddr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -35,8 +35,7 @@ import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.MarkerLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.ColorUtils;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.SortedRangeList;
|
||||
|
||||
abstract class MarkerSetImpl implements MarkerSet {
|
||||
@@ -52,7 +51,7 @@ abstract class MarkerSetImpl implements MarkerSet {
|
||||
protected AddressSetCollection markers;
|
||||
protected SortedRangeList overview = null;
|
||||
|
||||
protected VerticalPixelAddressMap activePixmap = null;
|
||||
protected VerticalPixelAddressMap activePixMap = null;
|
||||
protected List<Integer> activeLayouts = null;
|
||||
|
||||
protected Color markerColor;
|
||||
@@ -72,9 +71,11 @@ abstract class MarkerSetImpl implements MarkerSet {
|
||||
private boolean colorBackground;
|
||||
private boolean isPreferred;
|
||||
|
||||
/** Optional owner ID. A null ID implies a global marker set. */
|
||||
private UniversalID ownerId;
|
||||
|
||||
MarkerSetImpl(MarkerManager mgr, Program program, String name, String desc, int priority,
|
||||
boolean showMarkers,
|
||||
boolean showNavigation, boolean colorBackground, Color markerColor,
|
||||
boolean showMarkers, boolean showNavigation, boolean colorBackground, Color markerColor,
|
||||
boolean isPreferred) {
|
||||
|
||||
this.mgr = mgr;
|
||||
@@ -93,6 +94,16 @@ abstract class MarkerSetImpl implements MarkerSet {
|
||||
markers = new ModifiableAddressSetCollection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UniversalID ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniversalID getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
protected abstract void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixmap, int index,
|
||||
AddressIndexMap map, List<Integer> layouts);
|
||||
|
||||
@@ -244,11 +255,11 @@ abstract class MarkerSetImpl implements MarkerSet {
|
||||
}
|
||||
}
|
||||
|
||||
public final void paintMarkers(Graphics g, int index, VerticalPixelAddressMap pixmap,
|
||||
public final void paintMarkers(Graphics g, int index, VerticalPixelAddressMap pixMap,
|
||||
AddressIndexMap map) {
|
||||
if (showMarkers) {
|
||||
List<Integer> layouts = computeActiveLayouts(pixmap, map);
|
||||
doPaintMarkers(g, pixmap, index, map, layouts);
|
||||
List<Integer> layouts = computeActiveLayouts(pixMap, map);
|
||||
doPaintMarkers(g, pixMap, index, map, layouts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,32 +300,32 @@ abstract class MarkerSetImpl implements MarkerSet {
|
||||
return markers.contains(addr);
|
||||
}
|
||||
|
||||
private List<Integer> computeActiveLayouts(VerticalPixelAddressMap pixmap,
|
||||
private List<Integer> computeActiveLayouts(VerticalPixelAddressMap pixMap,
|
||||
AddressIndexMap map) {
|
||||
|
||||
if (pixmap == null) {
|
||||
if (pixMap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (activeLayouts != null && activePixmap == pixmap) {
|
||||
if (activeLayouts != null && activePixMap == pixMap) {
|
||||
return activeLayouts; // use cache
|
||||
}
|
||||
|
||||
List<Integer> newLayouts = new ArrayList<>();
|
||||
int n = pixmap.getNumLayouts();
|
||||
int n = pixMap.getNumLayouts();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Address addr = pixmap.getLayoutAddress(i);
|
||||
Address addr = pixMap.getLayoutAddress(i);
|
||||
if (addr == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Address end = pixmap.getLayoutEndAddress(i);
|
||||
Address end = pixMap.getLayoutEndAddress(i);
|
||||
if (markers.intersects(addr, end)) {
|
||||
newLayouts.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
activePixmap = pixmap;
|
||||
activePixMap = pixMap;
|
||||
activeLayouts = newLayouts;
|
||||
return newLayouts;
|
||||
}
|
||||
@@ -536,6 +547,6 @@ abstract class MarkerSetImpl implements MarkerSet {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Json.toString(this, "active", "colorBackground", "markers");
|
||||
return Json.toString(this, "name", "active", "colorBackground", "markers");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -95,7 +95,7 @@ class PointMarkerSet extends MarkerSetImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixmap, int index,
|
||||
protected void doPaintMarkers(Graphics g, VerticalPixelAddressMap pixMap, int index,
|
||||
AddressIndexMap map, List<Integer> layouts) {
|
||||
|
||||
if (layouts == null) {
|
||||
@@ -105,20 +105,20 @@ class PointMarkerSet extends MarkerSetImpl {
|
||||
Iterator<Integer> it = layouts.iterator();
|
||||
while (it.hasNext()) {
|
||||
int i = it.next().intValue();
|
||||
int yStart = pixmap.getMarkPosition(i);
|
||||
int yStart = pixMap.getMarkPosition(i);
|
||||
|
||||
Image curImage = getMarkerImage(pixmap, i, yStart);
|
||||
Image curImage = getMarkerImage(pixMap, i, yStart);
|
||||
|
||||
g.drawImage(curImage, 0, yStart, imageObserver);
|
||||
}
|
||||
}
|
||||
|
||||
private Image getMarkerImage(VerticalPixelAddressMap pixmap, int i, int yStart) {
|
||||
private Image getMarkerImage(VerticalPixelAddressMap pixMap, int i, int yStart) {
|
||||
if (markerDescriptor == null) {
|
||||
return image;
|
||||
}
|
||||
|
||||
Address address = pixmap.getLayoutAddress(i);
|
||||
Address address = pixMap.getLayoutAddress(i);
|
||||
MarkerLocation loc = new MarkerLocation(this, program, address, 0, yStart);
|
||||
ImageIcon icon = markerDescriptor.getIcon(loc);
|
||||
if (icon != null) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -28,7 +28,7 @@ import docking.action.DockingActionIf;
|
||||
import generic.theme.GColor;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
@@ -40,13 +40,13 @@ import help.Help;
|
||||
* Overview bar component. Uses color to indicate various address based properties for a program.
|
||||
* Uses an {@link OverviewColorService} to get the appropriate color for an address.
|
||||
*/
|
||||
public class OverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
public class OverviewColorComponent extends JPanel implements ListingOverviewProvider {
|
||||
private static final Color DEFAULT_COLOR = new GColor("color.bg.plugin.overview.defalt");
|
||||
private OverviewColorService service;
|
||||
private Color[] colors = new Color[0];
|
||||
private final SwingUpdateManager refreshUpdater =
|
||||
new SwingUpdateManager(100, 15000, () -> doRefresh());
|
||||
private AddressIndexMap map;
|
||||
private AddressIndexMap addressMap;
|
||||
private Navigatable navigatable;
|
||||
private PluginTool tool;
|
||||
private List<DockingActionIf> actions;
|
||||
@@ -76,6 +76,12 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
actions = service.getActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
service = null;
|
||||
ToolTipManager.sharedInstance().unregisterComponent(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs actions for this component
|
||||
*/
|
||||
@@ -154,10 +160,10 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
}
|
||||
|
||||
private void doRefresh() {
|
||||
if (map == null) {
|
||||
if (addressMap == null) {
|
||||
return;
|
||||
}
|
||||
BigInteger indexCount = map.getIndexCount();
|
||||
BigInteger indexCount = addressMap.getIndexCount();
|
||||
if (indexCount.equals(BigInteger.ZERO)) {
|
||||
Arrays.fill(colors, DEFAULT_COLOR);
|
||||
repaint();
|
||||
@@ -168,7 +174,7 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
if (colors[i] == null) {
|
||||
BigInteger index = indexCount.multiply(BigInteger.valueOf(i)).divide(bigTotal);
|
||||
Address address = map.getAddress(index);
|
||||
Address address = addressMap.getAddress(index);
|
||||
colors[i] = service.getColor(address);
|
||||
}
|
||||
}
|
||||
@@ -178,17 +184,17 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
private Address getAddress(int pixelIndex) {
|
||||
BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount());
|
||||
BigInteger bigPixelIndex = BigInteger.valueOf(pixelIndex);
|
||||
BigInteger bigIndex = map.getIndexCount().multiply(bigPixelIndex).divide(bigHeight);
|
||||
return map.getAddress(bigIndex);
|
||||
BigInteger bigIndex = addressMap.getIndexCount().multiply(bigPixelIndex).divide(bigHeight);
|
||||
return addressMap.getAddress(bigIndex);
|
||||
}
|
||||
|
||||
private int getPixelIndex(Address address) {
|
||||
BigInteger addressIndex = map.getIndex(address);
|
||||
BigInteger addressIndex = addressMap.getIndex(address);
|
||||
if (addressIndex == null) {
|
||||
return -1;
|
||||
}
|
||||
BigInteger bigHeight = BigInteger.valueOf(getOverviewPixelCount());
|
||||
BigInteger indexCount = map.getIndexCount();
|
||||
BigInteger indexCount = addressMap.getIndexCount();
|
||||
return addressIndex.multiply(bigHeight).divide(indexCount).intValue();
|
||||
}
|
||||
|
||||
@@ -198,10 +204,10 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program, AddressIndexMap map) {
|
||||
this.map = map;
|
||||
colors = new Color[getOverviewPixelCount()];
|
||||
refreshUpdater.updateLater();
|
||||
public void screenDataChanged(Program program, AddressIndexMap map) {
|
||||
this.addressMap = map;
|
||||
this.colors = new Color[getOverviewPixelCount()];
|
||||
this.refreshUpdater.updateLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,5 +254,4 @@ public class OverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -92,5 +92,4 @@ public interface OverviewColorService extends ExtensionPoint {
|
||||
* @return the current program used by the service.
|
||||
*/
|
||||
public Program getProgram();
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -143,6 +143,8 @@ public class AddressTypeOverviewColorService
|
||||
@Override
|
||||
public void setOverviewComponent(OverviewColorComponent component) {
|
||||
this.overviewComponent = component;
|
||||
this.overviewComponent.getAccessibleContext()
|
||||
.setAccessibleName("Address Type Overview Component");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -33,6 +33,7 @@ import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.app.services.CodeViewerService;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@@ -147,7 +148,10 @@ public class PrintingPlugin extends ProgramPlugin {
|
||||
format = job.defaultPage();
|
||||
}
|
||||
|
||||
LayoutModel lm = cvService.getFieldPanel().getLayoutModel();
|
||||
ListingPanel listingPanel = cvService.getListingPanel();
|
||||
FieldPanel fp = listingPanel.getFieldPanel();
|
||||
LayoutModel lm = fp.getLayoutModel();
|
||||
|
||||
//Scale everything down if appropriate to fit on the page
|
||||
double scaleAmount =
|
||||
format.getImageableWidth() / lm.getPreferredViewSize().width;
|
||||
@@ -316,7 +320,8 @@ public class PrintingPlugin extends ProgramPlugin {
|
||||
private void printVisibleContent(TaskMonitor monitor, Date startDate,
|
||||
PrinterJob job, Book book, LayoutModel lm, double scaleAmount,
|
||||
int maxPageHeight) {
|
||||
FieldPanel fp = cvService.getFieldPanel();
|
||||
ListingPanel listingPanel = cvService.getListingPanel();
|
||||
FieldPanel fp = listingPanel.getFieldPanel();
|
||||
List<AnchoredLayout> visibleLayouts = fp.getVisibleLayouts();
|
||||
BigInteger startIndex = visibleLayouts.get(0).getIndex();
|
||||
BigInteger endIndex = visibleLayouts.get(visibleLayouts.size() - 1).getIndex();
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -34,39 +34,48 @@ import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
/**
|
||||
* Service provided by a plugin that shows the listing from a Program, i.e., a
|
||||
* Code Viewer. The service allows other plugins to add components and
|
||||
* actions local to the Code Viewer.
|
||||
*
|
||||
*
|
||||
* Service provided by a plugin that shows the Code Viewer listing for a Program. The service
|
||||
* allows other plugins to add components and actions local to the Code Viewer.
|
||||
*/
|
||||
@ServiceInfo(defaultProvider = CodeBrowserPlugin.class)
|
||||
public interface CodeViewerService {
|
||||
/**
|
||||
* Add a provider that shows an overview of the program.
|
||||
* <p>
|
||||
* Note: the provider passed here will only appear in the primary code viewer, not in any of the
|
||||
* snapshot windows. to have an overview provider appear in all windows, you must create a
|
||||
* {@link ListingOverviewProviderService} implementation instead of using this method.
|
||||
*
|
||||
* @param overviewProvider provider to add
|
||||
*/
|
||||
public void addOverviewProvider(OverviewProvider overviewProvider);
|
||||
public void addOverviewProvider(ListingOverviewProvider overviewProvider);
|
||||
|
||||
/**
|
||||
* Remove a provider that shows an overview of the program.
|
||||
* @param overviewProvider provider to remove
|
||||
* @see CodeViewerService#addOverviewProvider(ListingOverviewProvider)
|
||||
*/
|
||||
public void removeOverviewProvider(OverviewProvider overviewProvider);
|
||||
public void removeOverviewProvider(ListingOverviewProvider overviewProvider);
|
||||
|
||||
/**
|
||||
* Add a provider that shows markers in a program for the portion
|
||||
* that is visible.
|
||||
* @param marginProvider provider to add
|
||||
* @deprecated clients that wish to be margin providers should now create a
|
||||
* {@link ListingMarginProviderService}.
|
||||
*/
|
||||
public void addMarginProvider(MarginProvider marginProvider);
|
||||
@Deprecated(since = "12.1", forRemoval = true)
|
||||
public void addMarginProvider(ListingMarginProvider marginProvider);
|
||||
|
||||
/**
|
||||
* Remove a provider that shows markers in a program for the portion
|
||||
* that is visible.
|
||||
* @param marginProvider provider to remove
|
||||
* @deprecated clients that wish to be margin providers should now create a
|
||||
* {@link ListingMarginProviderService}.
|
||||
*/
|
||||
public void removeMarginProvider(MarginProvider marginProvider);
|
||||
@Deprecated(since = "12.1", forRemoval = true)
|
||||
public void removeMarginProvider(ListingMarginProvider marginProvider);
|
||||
|
||||
/**
|
||||
* Add an action that is local to the Code Viewer.
|
||||
@@ -88,13 +97,13 @@ public interface CodeViewerService {
|
||||
|
||||
/**
|
||||
* Add a listener that is notified when a mouse button is pressed.
|
||||
* @param listener
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void addButtonPressedListener(ButtonPressedListener listener);
|
||||
|
||||
/**
|
||||
* Remove the button pressed listener.
|
||||
* @param listener
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void removeButtonPressedListener(ButtonPressedListener listener);
|
||||
|
||||
@@ -114,7 +123,8 @@ public interface CodeViewerService {
|
||||
public void removeHighlightProvider(ListingHighlightProvider provider, Program program);
|
||||
|
||||
/**
|
||||
* Set a listing panel on the code viewer.
|
||||
* Set a listing panel on the code viewer. The given listing panel is a secondary listing panel
|
||||
* to be displayed along with the primary listing panel.
|
||||
* @param listingPanel the panel to add.
|
||||
*/
|
||||
public void setListingPanel(ListingPanel listingPanel);
|
||||
@@ -127,11 +137,12 @@ public interface CodeViewerService {
|
||||
|
||||
/**
|
||||
* Remove the given listing panel from the code viewer.
|
||||
* @param listingPanel the listing panel
|
||||
*/
|
||||
public void removeListingPanel(ListingPanel listingPanel);
|
||||
|
||||
/**
|
||||
* Get Current view that the CodeViewer is showing.
|
||||
* @return the current set of addresses that the CodeViewer is showing.}
|
||||
*/
|
||||
public AddressSetView getView();
|
||||
|
||||
@@ -145,15 +156,21 @@ public interface CodeViewerService {
|
||||
public boolean goTo(ProgramLocation loc, boolean centerOnScreen);
|
||||
|
||||
/**
|
||||
* Return the fieldPanel.
|
||||
* Returns the current field panel.
|
||||
* @return the current field panel.
|
||||
* @deprecated use {@link #getListingPanel()} to get the field panel
|
||||
*/
|
||||
@Deprecated(since = "12.1", forRemoval = true)
|
||||
public FieldPanel getFieldPanel();
|
||||
|
||||
/**
|
||||
* Returns the current address-index-map
|
||||
* {@return the current address index map.}
|
||||
*/
|
||||
public AddressIndexMap getAddressIndexMap();
|
||||
|
||||
/**
|
||||
* {@return the format manager.}
|
||||
*/
|
||||
public FormatManager getFormatManager();
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/* ###
|
||||
* 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.services;
|
||||
|
||||
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
|
||||
|
||||
/**
|
||||
* A service to provide a widget that is designed to appear on the left-hand side of the Code Viewer
|
||||
*/
|
||||
public interface ListingMarginProviderService {
|
||||
|
||||
/**
|
||||
* Creates a new margin provider.
|
||||
* @return the provider
|
||||
*/
|
||||
public ListingMarginProvider createMarginProvider();
|
||||
|
||||
/**
|
||||
* True if this service is the owner of the given provider.
|
||||
* @param provider the provider to check
|
||||
* @return true if the owner
|
||||
*/
|
||||
public boolean isOwner(ListingMarginProvider provider);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* ###
|
||||
* 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.services;
|
||||
|
||||
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
|
||||
|
||||
public interface ListingOverviewProviderService {
|
||||
|
||||
/**
|
||||
* Creates a new overview provider.
|
||||
* @return the provider
|
||||
*/
|
||||
public ListingOverviewProvider createOverviewProvider();
|
||||
|
||||
/**
|
||||
* True if this service is the owner of the given provider.
|
||||
* @param provider the provider to check
|
||||
* @return true if the owner
|
||||
*/
|
||||
public boolean isOwner(ListingOverviewProvider provider);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package ghidra.app.services;
|
||||
import java.awt.Color;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.UniversalID;
|
||||
|
||||
/**
|
||||
* Defines methods for working with a set of addresses that correspond to markers.
|
||||
@@ -25,6 +26,19 @@ import ghidra.program.model.address.*;
|
||||
*/
|
||||
public interface MarkerSet extends Comparable<MarkerSet> {
|
||||
|
||||
/**
|
||||
* Sets an optional owner ID that signals when this marker set should be painted. A null ID
|
||||
* means that this marker set is global and should always be painted. Otherwise, this marker
|
||||
* set will be painted when its owner ID matches the owner ID being painted.
|
||||
* @param ownerId the ID
|
||||
*/
|
||||
public void setOwnerId(UniversalID ownerId);
|
||||
|
||||
/**
|
||||
* {@return the owner ID. See #setOwner(UniversalID).}
|
||||
*/
|
||||
public UniversalID getOwnerId();
|
||||
|
||||
/**
|
||||
* Add a marker at the address
|
||||
* @param addr the address
|
||||
@@ -151,8 +165,9 @@ public interface MarkerSet extends Comparable<MarkerSet> {
|
||||
public void setMarkerColor(Color color);
|
||||
|
||||
/**
|
||||
* Set the marker manager listener to use for user interaction
|
||||
* with markers owned by this manager.
|
||||
* Set the marker set descriptor. This allows clients to control tooltip text and program
|
||||
* location generation for individual markers on-the-fly.
|
||||
*
|
||||
* @param markerDescriptor the descriptor
|
||||
*/
|
||||
public void setMarkerDescriptor(MarkerDescriptor markerDescriptor);
|
||||
|
||||
@@ -965,7 +965,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||
* A library that has not been processed by the loader yet
|
||||
*
|
||||
* @param name The name of the library
|
||||
* @param depth The recursive load depth of the library (based on the original binary being
|
||||
* @param column The recursive load depth of the library (based on the original binary being
|
||||
* loaded)
|
||||
* @param temporary True if the library is temporary and should be discarded prior to returning
|
||||
* from the load
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/* ###
|
||||
* 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.util.viewer.listingpanel;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.program.util.MarkerLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.UniversalID;
|
||||
|
||||
/**
|
||||
* Interface for objects that want to add a component to the listing's left margin.
|
||||
*/
|
||||
public interface ListingMarginProvider {
|
||||
|
||||
/**
|
||||
* Sets the optional owner ID to be used with this margin provider. Implementations may use
|
||||
* this ID to determine when they should paint.
|
||||
* @param ownerId the ID
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets an optional owner ID that signals when the markers for this provider should be painted.
|
||||
* A null ID means that this provider is a non-snapshot provider and should paint all markers.
|
||||
* A non-null ID means this provider's markers will be painted when the marker's owner ID
|
||||
* this provider's ID
|
||||
* .
|
||||
* @param ownerId the ID
|
||||
*/
|
||||
public void setOwnerId(UniversalID ownerId);
|
||||
|
||||
/**
|
||||
* Get the component to show the margin markers.
|
||||
* @return the component
|
||||
*/
|
||||
public JComponent getComponent();
|
||||
|
||||
/**
|
||||
* Return true if can be resized.
|
||||
* @return true if can be resized.
|
||||
*/
|
||||
public boolean isResizeable();
|
||||
|
||||
/**
|
||||
* Called to notify this margin provider that the current screen information has changed.
|
||||
*
|
||||
* @param listingPanel the listing panel.
|
||||
* @param addressIndexMap the address index map to use.
|
||||
* @param pixelMap the vertical pixel map to use.
|
||||
*/
|
||||
public void screenDataChanged(ListingPanel listingPanel, AddressIndexMap addressIndexMap,
|
||||
VerticalPixelAddressMap pixelMap);
|
||||
|
||||
/**
|
||||
* Called from the client when their location changes internally. This is different from a tool
|
||||
* location event, which is considered a global event.
|
||||
* @param location the location
|
||||
*/
|
||||
public void setLocation(ProgramLocation location);
|
||||
|
||||
/**
|
||||
* Get the marker location for the given x, y point.
|
||||
*
|
||||
* @param x the horizontal coordinate.
|
||||
* @param y the vertical coordinate.
|
||||
* @return the location
|
||||
*/
|
||||
public MarkerLocation getMarkerLocation(int x, int y);
|
||||
|
||||
/**
|
||||
* Called when the client is done with this provider.
|
||||
*/
|
||||
public void dispose();
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -22,28 +22,32 @@ import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Interface implemented by classes that provide overview components to the right side of the
|
||||
* listing.
|
||||
* An overview component that will be placed to the right side of the listing.
|
||||
*/
|
||||
public interface OverviewProvider {
|
||||
public interface ListingOverviewProvider {
|
||||
/**
|
||||
* Returns the component to display in the right margin of the listing.
|
||||
* @return the component
|
||||
*/
|
||||
JComponent getComponent();
|
||||
public JComponent getComponent();
|
||||
|
||||
/**
|
||||
* Sets the current program and associated address-index map
|
||||
* Called to notify this margin provider that the current screen information has changed.
|
||||
*
|
||||
* @param program the program to use.
|
||||
* @param map the address-index map to use.
|
||||
* @param program the program to use
|
||||
* @param map the address index map to use
|
||||
*/
|
||||
void setProgram(Program program, AddressIndexMap map);
|
||||
public void screenDataChanged(Program program, AddressIndexMap map);
|
||||
|
||||
/**
|
||||
* Set the component provider that this overview navigates
|
||||
*
|
||||
* @param navigatable the navigatable provider
|
||||
*/
|
||||
void setNavigatable(Navigatable navigatable);
|
||||
public void setNavigatable(Navigatable navigatable);
|
||||
|
||||
/**
|
||||
* Clients call this when they are done with this provider.
|
||||
*/
|
||||
public void dispose();
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
@@ -31,10 +32,13 @@ import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.listener.*;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import docking.widgets.indexedscrollpane.IndexedScrollPane;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.codebrowser.LayeredColorModel;
|
||||
import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
|
||||
import ghidra.app.plugin.core.codebrowser.hover.ListingHoverService;
|
||||
import ghidra.app.services.ButtonPressedListener;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.field.*;
|
||||
import ghidra.app.util.viewer.format.FieldHeader;
|
||||
@@ -44,14 +48,15 @@ import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.layout.HorizontalLayout;
|
||||
|
||||
public class ListingPanel extends JPanel implements FieldMouseListener, FieldLocationListener,
|
||||
FieldSelectionListener, LayoutListener {
|
||||
|
||||
public static final int DEFAULT_DIVIDER_LOCATION = 70;
|
||||
private static final Icon CURSOR_LOC_ICON =
|
||||
new GIcon("icon.plugin.codebrowser.cursor.location");
|
||||
|
||||
private FormatManager formatManager;
|
||||
private ListingModelAdapter layoutModel;
|
||||
@@ -61,6 +66,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
private JSplitPane splitPane;
|
||||
private int splitPaneDividerLocation = DEFAULT_DIVIDER_LOCATION;
|
||||
|
||||
private FocusingMouseListener focusingMouseListener = new FocusingMouseListener();
|
||||
private ProgramLocationListener programLocationListener;
|
||||
private ProgramSelectionListener programSelectionListener;
|
||||
private ProgramSelectionListener liveProgramSelectionListener;
|
||||
@@ -84,8 +90,21 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
private ListingHoverProvider listingHoverHandler;
|
||||
|
||||
private List<MarginProvider> marginProviders = new ArrayList<>();
|
||||
private List<OverviewProvider> overviewProviders = new ArrayList<>();
|
||||
private List<ListingMarginProvider> marginProviders = new ArrayList<>();
|
||||
private List<ListingOverviewProvider> overviewProviders = new ArrayList<>();
|
||||
|
||||
private String currentTextSelection;
|
||||
private boolean useMarkerNameSuffix;
|
||||
private UniversalID marginOwnerId = UniversalIdGenerator.nextID();
|
||||
|
||||
private ChangeListener markerChangeListener;
|
||||
private MarkerService markerService;
|
||||
private Color cursorLineHighlightColor;
|
||||
private boolean isHighlightCursorLineEnabled;
|
||||
private MarkerSet selectionMarkers;
|
||||
private MarkerSet highlightMarkers;
|
||||
private MarkerSet cursorMarkers;
|
||||
|
||||
private VerticalPixelAddressMapImpl pixmap;
|
||||
private PropertyBasedBackgroundColorModel propertyBasedColorModel;
|
||||
private LayeredColorModel layeredColorModel;
|
||||
@@ -103,8 +122,6 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
};
|
||||
private List<AddressSetDisplayListener> displayListeners = new ArrayList<>();
|
||||
|
||||
private String currentTextSelection;
|
||||
|
||||
/**
|
||||
* Constructs a new ListingPanel using the given FormatManager
|
||||
*
|
||||
@@ -128,7 +145,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
fieldPanel.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
for (MarginProvider provider : marginProviders) {
|
||||
for (ListingMarginProvider provider : marginProviders) {
|
||||
provider.getComponent().invalidate();
|
||||
}
|
||||
validate();
|
||||
@@ -138,6 +155,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
String viewName = "Assembly Listing View";
|
||||
fieldPanel.setName(viewName);
|
||||
fieldPanel.getAccessibleContext().setAccessibleName(viewName);
|
||||
|
||||
markerChangeListener = new MarkerChangeListener();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,8 +320,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
private void updateProviders() {
|
||||
AddressIndexMap addressIndexMap = layoutModel.getAddressIndexMap();
|
||||
for (OverviewProvider element : overviewProviders) {
|
||||
element.setProgram(getProgram(), addressIndexMap);
|
||||
for (ListingMarginProvider provider : marginProviders) {
|
||||
provider.screenDataChanged(this, addressIndexMap, pixmap);
|
||||
}
|
||||
for (ListingOverviewProvider provider : overviewProviders) {
|
||||
provider.screenDataChanged(getProgram(), addressIndexMap);
|
||||
}
|
||||
for (ChangeListener indexMapChangeListener : indexMapChangeListeners) {
|
||||
indexMapChangeListener.stateChanged(null);
|
||||
@@ -323,20 +345,99 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
layoutModel.dataChanged(updateImmediately);
|
||||
}
|
||||
|
||||
public void removeMarginService(ListingMarginProviderService service) {
|
||||
for (ListingMarginProvider provider : marginProviders) {
|
||||
if (service.isOwner(provider)) {
|
||||
removeMarginProvider(provider);
|
||||
provider.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addMarginService(ListingMarginProviderService service, boolean isConnected) {
|
||||
if (containsMarginProviver(service)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ListingMarginProvider provider = service.createMarginProvider();
|
||||
provider.setOwnerId(marginOwnerId);
|
||||
addMarginProvider(provider);
|
||||
}
|
||||
|
||||
private boolean containsMarginProviver(ListingMarginProviderService service) {
|
||||
for (ListingMarginProvider provider : marginProviders) {
|
||||
if (service.isOwner(provider)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeOverviewService(ListingOverviewProviderService service) {
|
||||
for (ListingOverviewProvider provider : overviewProviders) {
|
||||
if (service.isOwner(provider)) {
|
||||
removeOverviewProvider(provider);
|
||||
provider.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addOverviewService(ListingOverviewProviderService service, Navigatable navigatable,
|
||||
boolean connected) {
|
||||
if (containsOverviewProvider(service)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ListingOverviewProvider provider = service.createOverviewProvider();
|
||||
provider.setNavigatable(navigatable);
|
||||
addOverviewProvider(provider);
|
||||
}
|
||||
|
||||
private boolean containsOverviewProvider(ListingOverviewProviderService service) {
|
||||
for (ListingOverviewProvider provider : overviewProviders) {
|
||||
if (service.isOwner(provider)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the MarginProvider to this panel
|
||||
* Removes the given margin provider from this panel
|
||||
*
|
||||
* @param provider the MarginProvider that will provide components to display in this panel's
|
||||
* left margin area.
|
||||
* @param provider the MarginProvider to remove.
|
||||
*/
|
||||
public void addMarginProvider(MarginProvider provider) {
|
||||
public void removeMarginProvider(ListingMarginProvider provider) {
|
||||
JComponent component = provider.getComponent();
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
|
||||
marginProviders.remove(provider);
|
||||
buildPanels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the margin provider to this panel.
|
||||
* <p>
|
||||
* This method is for clients that create and manage their own listing panels that are not the
|
||||
* main listing panel.
|
||||
*
|
||||
* @param provider the provider that will display in this listing panel's left margin area
|
||||
*/
|
||||
public void addMarginProvider(ListingMarginProvider provider) {
|
||||
|
||||
JComponent component = provider.getComponent();
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
component.addMouseListener(focusingMouseListener);
|
||||
|
||||
if (provider.isResizeable()) {
|
||||
marginProviders.add(0, provider);
|
||||
}
|
||||
else {
|
||||
marginProviders.add(provider);
|
||||
}
|
||||
provider.setProgram(getProgram(), layoutModel.getAddressIndexMap(), pixmap);
|
||||
provider.screenDataChanged(this, layoutModel.getAddressIndexMap(), pixmap);
|
||||
buildPanels();
|
||||
}
|
||||
|
||||
@@ -363,26 +464,26 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
return null;
|
||||
}
|
||||
JPanel rightPanel = new JPanel(new HorizontalLayout(0));
|
||||
for (OverviewProvider overviewProvider : overviewProviders) {
|
||||
for (ListingOverviewProvider overviewProvider : overviewProviders) {
|
||||
rightPanel.add(overviewProvider.getComponent());
|
||||
}
|
||||
return rightPanel;
|
||||
}
|
||||
|
||||
private JComponent buildLeftComponent() {
|
||||
List<MarginProvider> marginProviderList = getNonResizeableMarginProviders();
|
||||
List<ListingMarginProvider> marginProviderList = getNonResizeableMarginProviders();
|
||||
JPanel leftPanel = new JPanel(new ScrollpaneAlignedHorizontalLayout(scroller));
|
||||
for (MarginProvider marginProvider : marginProviderList) {
|
||||
for (ListingMarginProvider marginProvider : marginProviderList) {
|
||||
leftPanel.add(marginProvider.getComponent());
|
||||
}
|
||||
return leftPanel;
|
||||
}
|
||||
|
||||
private List<MarginProvider> getNonResizeableMarginProviders() {
|
||||
private List<ListingMarginProvider> getNonResizeableMarginProviders() {
|
||||
if (marginProviders.isEmpty()) {
|
||||
return marginProviders;
|
||||
}
|
||||
MarginProvider firstMarginProvider = marginProviders.get(0);
|
||||
ListingMarginProvider firstMarginProvider = marginProviders.get(0);
|
||||
if (firstMarginProvider.isResizeable()) {
|
||||
return marginProviders.subList(1, marginProviders.size());
|
||||
}
|
||||
@@ -391,7 +492,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
private JComponent buildCenterComponent() {
|
||||
JComponent centerComponent = scroller;
|
||||
MarginProvider resizeableMarginProvider = getResizeableMarginProvider();
|
||||
ListingMarginProvider resizeableMarginProvider = getResizeableMarginProvider();
|
||||
if (resizeableMarginProvider != null) {
|
||||
if (splitPane != null) {
|
||||
splitPaneDividerLocation = splitPane.getDividerLocation();
|
||||
@@ -414,11 +515,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
return centerComponent;
|
||||
}
|
||||
|
||||
private MarginProvider getResizeableMarginProvider() {
|
||||
private ListingMarginProvider getResizeableMarginProvider() {
|
||||
if (marginProviders.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
MarginProvider marginProvider = marginProviders.get(0);
|
||||
ListingMarginProvider marginProvider = marginProviders.get(0);
|
||||
return marginProvider.isResizeable() ? marginProvider : null;
|
||||
}
|
||||
|
||||
@@ -440,24 +541,22 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
indexMapChangeListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given margin provider from this panel
|
||||
*
|
||||
* @param provider the MarginProvider to remove.
|
||||
*/
|
||||
public void removeMarginProvider(MarginProvider provider) {
|
||||
marginProviders.remove(provider);
|
||||
buildPanels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given OverviewProvider with will be displayed in this panels right margin area.
|
||||
* <p>
|
||||
* This method is for clients that create and manage their own listing panels that are not the
|
||||
* main listing panel.
|
||||
*
|
||||
* @param provider the OverviewProvider to display.
|
||||
*/
|
||||
public void addOverviewProvider(OverviewProvider provider) {
|
||||
public void addOverviewProvider(ListingOverviewProvider provider) {
|
||||
|
||||
JComponent component = provider.getComponent();
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
component.addMouseListener(focusingMouseListener);
|
||||
|
||||
overviewProviders.add(provider);
|
||||
provider.setProgram(getProgram(), layoutModel.getAddressIndexMap());
|
||||
provider.screenDataChanged(getProgram(), layoutModel.getAddressIndexMap());
|
||||
buildPanels();
|
||||
}
|
||||
|
||||
@@ -466,7 +565,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
*
|
||||
* @param provider the OverviewProvider to remove.
|
||||
*/
|
||||
public void removeOverviewProvider(OverviewProvider provider) {
|
||||
public void removeOverviewProvider(ListingOverviewProvider provider) {
|
||||
|
||||
JComponent component = provider.getComponent();
|
||||
component.removeMouseListener(focusingMouseListener);
|
||||
|
||||
overviewProviders.remove(provider);
|
||||
buildPanels();
|
||||
}
|
||||
@@ -525,8 +628,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
public void layoutsChanged(List<AnchoredLayout> layouts) {
|
||||
AddressIndexMap addrMap = layoutModel.getAddressIndexMap();
|
||||
this.pixmap = new VerticalPixelAddressMapImpl(layouts, addrMap);
|
||||
for (MarginProvider element : marginProviders) {
|
||||
element.setProgram(getProgram(), addrMap, pixmap);
|
||||
for (ListingMarginProvider provider : marginProviders) {
|
||||
provider.screenDataChanged(this, addrMap, pixmap);
|
||||
}
|
||||
|
||||
for (AddressSetDisplayListener listener : displayListeners) {
|
||||
@@ -594,6 +697,10 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
setListingModel(null);
|
||||
|
||||
for (ListingMarginProvider provider : marginProviders) {
|
||||
provider.dispose();
|
||||
}
|
||||
|
||||
removeAll();
|
||||
listingHoverHandler.dispose();
|
||||
layoutModel.dispose();
|
||||
@@ -885,11 +992,19 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
headerPanel.setTabLock(false);
|
||||
}
|
||||
|
||||
ProgramLocation pLoc = layoutModel.getProgramLocation(location, field);
|
||||
if (pLoc == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (programLocationListener != null) {
|
||||
ProgramLocation pLoc = layoutModel.getProgramLocation(location, field);
|
||||
if (pLoc != null) {
|
||||
programLocationListener.programLocationChanged(pLoc, trigger);
|
||||
}
|
||||
programLocationListener.programLocationChanged(pLoc, trigger);
|
||||
}
|
||||
|
||||
setCursorMarkerAddress(pLoc.getAddress());
|
||||
|
||||
for (ListingMarginProvider provider : marginProviders) {
|
||||
provider.setLocation(pLoc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,7 +1120,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
*
|
||||
* @return the providers
|
||||
*/
|
||||
public List<MarginProvider> getMarginProviders() {
|
||||
public List<ListingMarginProvider> getMarginProviders() {
|
||||
return marginProviders;
|
||||
}
|
||||
|
||||
@@ -1014,7 +1129,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
*
|
||||
* @return the providers
|
||||
*/
|
||||
public List<OverviewProvider> getOverviewProviders() {
|
||||
public List<ListingOverviewProvider> getOverviewProviders() {
|
||||
return overviewProviders;
|
||||
}
|
||||
|
||||
@@ -1182,8 +1297,16 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
* @param trigger the cause of the change
|
||||
*/
|
||||
public void setSelection(ProgramSelection sel, EventTrigger trigger) {
|
||||
|
||||
Program program = getProgram();
|
||||
MarkerSet markers = getSelectionMarkers(program);
|
||||
|
||||
if (sel == null) {
|
||||
fieldPanel.setSelection(layoutModel.getFieldSelection(null), trigger);
|
||||
|
||||
if (markers != null) {
|
||||
markers.clearAll();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1216,6 +1339,11 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
}
|
||||
fieldPanel.setSelection(layoutModel.getFieldSelection(sel), trigger);
|
||||
|
||||
if (markers != null) {
|
||||
markers.clearAll();
|
||||
markers.add(sel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1225,6 +1353,19 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
*/
|
||||
public void setHighlight(ProgramSelection highlight) {
|
||||
fieldPanel.setHighlight(layoutModel.getFieldSelection(highlight));
|
||||
|
||||
Program program = getProgram();
|
||||
MarkerSet markers = getHighlightMarkers(program);
|
||||
if (markers == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
markers.clearAll();
|
||||
|
||||
if (highlight != null && program != null) {
|
||||
markers.setAddressSet(highlight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ProgramSelection getProgramHighlight() {
|
||||
@@ -1320,4 +1461,192 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
// we are not focusable, defer to contained field panel
|
||||
fieldPanel.removeFocusListener(l);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Markers
|
||||
//==================================================================================================
|
||||
|
||||
public void setUseMarkerNameSuffix(boolean b) {
|
||||
// Note: this happens just after construction, so no need to recreate the markers
|
||||
this.useMarkerNameSuffix = b;
|
||||
}
|
||||
|
||||
public void setMarkerService(MarkerService markerService) {
|
||||
|
||||
if (this.markerService != null) {
|
||||
this.markerService.removeChangeListener(markerChangeListener);
|
||||
}
|
||||
|
||||
if (markerService != null) {
|
||||
markerService.addChangeListener(markerChangeListener);
|
||||
}
|
||||
else {
|
||||
doClearMarkers(getProgram());
|
||||
}
|
||||
|
||||
this.markerService = markerService;
|
||||
}
|
||||
|
||||
public void clearMarkers(Program program) {
|
||||
doClearMarkers(program);
|
||||
}
|
||||
|
||||
private void doClearMarkers(Program program) {
|
||||
if (markerService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (program == null) {
|
||||
return; // can happen during dispose after a programDeactivated()
|
||||
}
|
||||
|
||||
if (selectionMarkers != null) {
|
||||
markerService.removeMarker(selectionMarkers, program);
|
||||
selectionMarkers = null;
|
||||
}
|
||||
|
||||
if (highlightMarkers != null) {
|
||||
markerService.removeMarker(highlightMarkers, program);
|
||||
highlightMarkers = null;
|
||||
}
|
||||
|
||||
if (cursorMarkers != null) {
|
||||
markerService.removeMarker(cursorMarkers, program);
|
||||
cursorMarkers = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectionColor(Color color) {
|
||||
|
||||
fieldPanel.setSelectionColor(color);
|
||||
if (selectionMarkers != null) {
|
||||
selectionMarkers.setMarkerColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHighlightColor(Color color) {
|
||||
fieldPanel.setHighlightColor(color);
|
||||
if (highlightMarkers != null) {
|
||||
highlightMarkers.setMarkerColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMarkerName(String baseName) {
|
||||
if (useMarkerNameSuffix) {
|
||||
return baseName + ' ' + marginOwnerId.toString();
|
||||
}
|
||||
return baseName;
|
||||
}
|
||||
|
||||
private MarkerSet getSelectionMarkers(Program program) {
|
||||
if (markerService == null || program == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already created
|
||||
if (selectionMarkers != null) {
|
||||
return selectionMarkers;
|
||||
}
|
||||
|
||||
String markerName = getMarkerName("Selection");
|
||||
Color color = fieldPanel.getSelectionColor();
|
||||
selectionMarkers = markerService.createAreaMarker(markerName, "Selection Display",
|
||||
program, MarkerService.SELECTION_PRIORITY, false, true, false, color);
|
||||
selectionMarkers.setOwnerId(marginOwnerId);
|
||||
|
||||
return selectionMarkers;
|
||||
}
|
||||
|
||||
private MarkerSet getHighlightMarkers(Program program) {
|
||||
if (markerService == null || program == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already created
|
||||
if (highlightMarkers != null) {
|
||||
return highlightMarkers;
|
||||
}
|
||||
|
||||
String markerName = getMarkerName("Highlight");
|
||||
Color color = fieldPanel.getHighlightColor();
|
||||
highlightMarkers = markerService.createAreaMarker(markerName, "Highlight Display ",
|
||||
program, MarkerService.HIGHLIGHT_PRIORITY, false, true, false, color);
|
||||
highlightMarkers.setOwnerId(marginOwnerId);
|
||||
|
||||
return highlightMarkers;
|
||||
}
|
||||
|
||||
private MarkerSet getCursorMarkers(Program program) {
|
||||
if (markerService == null || program == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already created
|
||||
if (cursorMarkers != null) {
|
||||
return cursorMarkers;
|
||||
}
|
||||
|
||||
String markerName = getMarkerName("Cursor");
|
||||
cursorMarkers = markerService.createPointMarker(markerName, "Cursor Location",
|
||||
program, MarkerService.CURSOR_PRIORITY, true, true, isHighlightCursorLineEnabled,
|
||||
cursorLineHighlightColor, CURSOR_LOC_ICON);
|
||||
cursorMarkers.setOwnerId(marginOwnerId);
|
||||
|
||||
return cursorMarkers;
|
||||
}
|
||||
|
||||
public void setCursorHighlightColor(Color cursorHighlightColor) {
|
||||
this.cursorLineHighlightColor = cursorHighlightColor;
|
||||
if (cursorMarkers != null) {
|
||||
cursorMarkers.setMarkerColor(cursorHighlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHighlightCursorLineEnabled(boolean b) {
|
||||
this.isHighlightCursorLineEnabled = b;
|
||||
if (cursorMarkers != null) {
|
||||
cursorMarkers.setColoringBackground(b);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCursorMarkerAddress(Address address) {
|
||||
MarkerSet markers = getCursorMarkers(getProgram());
|
||||
if (markers != null) {
|
||||
markers.clearAll();
|
||||
markers.add(address);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateBackgroundColorModel() {
|
||||
if (markerService == null) {
|
||||
setBackgroundColorModel(null);
|
||||
}
|
||||
else {
|
||||
AddressIndexMap indexMap = getAddressIndexMap();
|
||||
setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerService, indexMap));
|
||||
}
|
||||
}
|
||||
//==================================================================================================
|
||||
// End Markers
|
||||
//==================================================================================================
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class MarkerChangeListener implements ChangeListener {
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
fieldPanel.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private class FocusingMouseListener extends MouseAdapter {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
fieldPanel.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +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.util.viewer.listingpanel;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.MarkerLocation;
|
||||
|
||||
/**
|
||||
* Interface for objects that want to add a component to the listing's left margin.
|
||||
*/
|
||||
public interface MarginProvider {
|
||||
|
||||
/**
|
||||
* Get the component to show the margin markers.
|
||||
* @return the component
|
||||
*/
|
||||
JComponent getComponent();
|
||||
|
||||
/**
|
||||
* Return true if can be resized.
|
||||
* @return true if can be resized.
|
||||
*/
|
||||
boolean isResizeable();
|
||||
|
||||
/**
|
||||
* Set the program and associated maps.
|
||||
*
|
||||
* @param program the program to use.
|
||||
* @param addressIndexMap the address-index map to use.
|
||||
* @param pixelMap the vertical pixel map to use.
|
||||
*/
|
||||
void setProgram(Program program, AddressIndexMap addressIndexMap,
|
||||
VerticalPixelAddressMap pixelMap);
|
||||
|
||||
/**
|
||||
* Get the marker location for the given x, y point.
|
||||
*
|
||||
* @param x the horizontal coordinate.
|
||||
* @param y the vertical coordinate.
|
||||
* @return the location
|
||||
*/
|
||||
public MarkerLocation getMarkerLocation(int x, int y);
|
||||
|
||||
}
|
||||
@@ -394,8 +394,8 @@ public class ListingCodeComparisonView
|
||||
public Object getContextObjectForMarginPanels(ListingPanel panel, MouseEvent event) {
|
||||
Object source = event.getSource();
|
||||
// Is event source a marker margin provider on the left side of the listing?
|
||||
List<MarginProvider> marginProviders = panel.getMarginProviders();
|
||||
for (MarginProvider marginProvider : marginProviders) {
|
||||
List<ListingMarginProvider> marginProviders = panel.getMarginProviders();
|
||||
for (ListingMarginProvider marginProvider : marginProviders) {
|
||||
JComponent c = marginProvider.getComponent();
|
||||
if (c == source) {
|
||||
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
|
||||
@@ -406,8 +406,8 @@ public class ListingCodeComparisonView
|
||||
}
|
||||
}
|
||||
// Is event source an overview provider on the right side of the listing?
|
||||
List<OverviewProvider> overviewProviders = panel.getOverviewProviders();
|
||||
for (OverviewProvider overviewProvider : overviewProviders) {
|
||||
List<ListingOverviewProvider> overviewProviders = panel.getOverviewProviders();
|
||||
for (ListingOverviewProvider overviewProvider : overviewProviders) {
|
||||
JComponent c = overviewProvider.getComponent();
|
||||
if (c == source) {
|
||||
return source; // Return the overview provider that was clicked.
|
||||
@@ -623,8 +623,8 @@ public class ListingCodeComparisonView
|
||||
|
||||
private Object getContextForMarginPanels(ListingPanel lp, MouseEvent event) {
|
||||
Object source = event.getSource();
|
||||
List<MarginProvider> marginProvidersForLP = lp.getMarginProviders();
|
||||
for (MarginProvider marginProvider : marginProvidersForLP) {
|
||||
List<ListingMarginProvider> marginProvidersForLP = lp.getMarginProviders();
|
||||
for (ListingMarginProvider marginProvider : marginProvidersForLP) {
|
||||
JComponent c = marginProvider.getComponent();
|
||||
if (c == source) {
|
||||
MarkerLocation loc = marginProvider.getMarkerLocation(event.getX(), event.getY());
|
||||
@@ -634,8 +634,8 @@ public class ListingCodeComparisonView
|
||||
return source;
|
||||
}
|
||||
}
|
||||
List<OverviewProvider> overviewProvidersForLP = lp.getOverviewProviders();
|
||||
for (OverviewProvider overviewProvider : overviewProvidersForLP) {
|
||||
List<ListingOverviewProvider> overviewProvidersForLP = lp.getOverviewProviders();
|
||||
for (ListingOverviewProvider overviewProvider : overviewProvidersForLP) {
|
||||
JComponent c = overviewProvider.getComponent();
|
||||
if (c == source) {
|
||||
return source;
|
||||
|
||||
@@ -18,6 +18,7 @@ package ghidra.features.base.codecompare.listing;
|
||||
import static ghidra.GhidraOptions.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
@@ -94,9 +95,12 @@ public class ListingDisplay implements ListingDiffChangeListener {
|
||||
private void createMarkerManager(String owner) {
|
||||
markerManager = new ListingDisplayMarkerManager(tool, owner);
|
||||
markerManager.addChangeListener(e -> listingPanel.repaint());
|
||||
MarginProvider marginProvider = markerManager.getMarginProvider();
|
||||
|
||||
// Manually install our custom margin provider. We are not calling setMarginService(),
|
||||
// which means that many of the listing markers will not work in the compare view.
|
||||
ListingMarginProvider marginProvider = markerManager.createMarginProvider();
|
||||
listingPanel.addMarginProvider(marginProvider);
|
||||
OverviewProvider overviewProvider = markerManager.getOverviewProvider();
|
||||
ListingOverviewProvider overviewProvider = markerManager.createOverviewProvider();
|
||||
listingPanel.addOverviewProvider(overviewProvider);
|
||||
}
|
||||
|
||||
@@ -171,7 +175,12 @@ public class ListingDisplay implements ListingDiffChangeListener {
|
||||
markerManager.clearAll();
|
||||
listingPanel.setView(view);
|
||||
AddressIndexMap indexMap = listingPanel.getAddressIndexMap();
|
||||
markerManager.getOverviewProvider().setProgram(program, indexMap);
|
||||
|
||||
List<ListingOverviewProvider> providers = listingPanel.getOverviewProviders();
|
||||
for (ListingOverviewProvider provider : providers) {
|
||||
provider.screenDataChanged(program, indexMap);
|
||||
}
|
||||
|
||||
listingPanel.setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManager, program, indexMap));
|
||||
setUpAreaMarkerSets(program, name);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -68,11 +68,11 @@ public class MarkerLocation implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Marker Manager.
|
||||
* Returns the marker set.
|
||||
*
|
||||
* @return the marker manager
|
||||
* @return the marker set
|
||||
*/
|
||||
public MarkerSet getMarkerManager() {
|
||||
public MarkerSet getMarkerSet() {
|
||||
return markers;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ import ghidra.app.util.viewer.field.FieldFactory;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.listingpanel.MarginProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
|
||||
import ghidra.docking.settings.SettingsDefinition;
|
||||
import ghidra.framework.ToolUtils;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
@@ -1247,9 +1247,9 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
|
||||
CodeBrowserPlugin plugin = getPlugin(tool, CodeBrowserPlugin.class);
|
||||
ListingPanel listingPanel = plugin.getListingPanel();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<MarginProvider> list =
|
||||
(List<MarginProvider>) getInstanceField("marginProviders", listingPanel);
|
||||
for (MarginProvider marginProvider : list) {
|
||||
List<ListingMarginProvider> list =
|
||||
(List<ListingMarginProvider>) getInstanceField("marginProviders", listingPanel);
|
||||
for (ListingMarginProvider marginProvider : list) {
|
||||
listingPanel.removeMarginProvider(marginProvider);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -669,6 +669,8 @@ public class CommentsPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
addReference(0x1001020, 0x1008294, RefType.DATA);
|
||||
addReference(0x1001030, 0x1008394, RefType.DATA);
|
||||
|
||||
waitForProgram(program);
|
||||
|
||||
Address srcAddr = addr(0x01006990);
|
||||
CodeUnit cu = program.getListing().getCodeUnitAt(srcAddr);
|
||||
|
||||
|
||||
@@ -44,17 +44,15 @@ import ghidra.app.plugin.core.clear.ClearPlugin;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.highlight.SetHighlightPlugin;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.BookmarkType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.test.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
|
||||
public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
@@ -162,8 +160,13 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
setSelection(fp, sel);
|
||||
|
||||
MarkerManager mm = (MarkerManager) markerService;
|
||||
OverviewProvider op = mm.getOverviewProvider();
|
||||
JPanel navPanel = (JPanel) op.getComponent();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
WeakSet<MarkerOverviewProvider> overviewProviders =
|
||||
(WeakSet<MarkerOverviewProvider>) getInstanceField("overviewProviders", mm);
|
||||
MarkerOverviewProvider provider = overviewProviders.iterator().next();
|
||||
|
||||
JPanel navPanel = (JPanel) provider.getComponent();
|
||||
|
||||
waitForProgram(program);
|
||||
|
||||
@@ -224,8 +227,13 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertTrue(addrSet.contains(addr("0xf000131b")));
|
||||
|
||||
MarkerManager mm = (MarkerManager) markerService;
|
||||
OverviewProvider op = mm.getOverviewProvider();
|
||||
JPanel navPanel = (JPanel) op.getComponent();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
WeakSet<MarkerOverviewProvider> overviewProviders =
|
||||
(WeakSet<MarkerOverviewProvider>) getInstanceField("overviewProviders", mm);
|
||||
MarkerOverviewProvider provider = overviewProviders.iterator().next();
|
||||
|
||||
JPanel navPanel = (JPanel) provider.getComponent();
|
||||
|
||||
waitForProgram(program);
|
||||
|
||||
@@ -278,7 +286,12 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
MouseEvent dummyEvent =
|
||||
new MouseEvent(cb.getFieldPanel(), (int) time, time, 0, x, y, 1, false);
|
||||
MarkerManager mm = (MarkerManager) markerService;
|
||||
String tooltip = runSwing(() -> mm.generateToolTip(dummyEvent));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
WeakSet<MarkerMarginProvider> marginProviders =
|
||||
(WeakSet<MarkerMarginProvider>) getInstanceField("marginProviders", mm);
|
||||
MarkerMarginProvider provider = marginProviders.iterator().next();
|
||||
String tooltip = runSwing(() -> provider.generateToolTip(dummyEvent));
|
||||
assertEquals(
|
||||
"<html><font size=\"4\">Cursor<BR>Note [TEST]: comment<BR>Changes: Unsaved<BR>",
|
||||
tooltip);
|
||||
@@ -316,11 +329,11 @@ public class MarkerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
MouseEvent dummyEvent = new MouseEvent(cb.getFieldPanel(), (int) System.currentTimeMillis(),
|
||||
System.currentTimeMillis(), 0, x, y, 1, false);
|
||||
|
||||
// debug
|
||||
ProgramLocation location = cb.getListingPanel().getProgramLocation(dummyEvent.getPoint());
|
||||
Msg.debug(this, "location for point: " + location + "; at " + location.getAddress());
|
||||
|
||||
String tooltip = runSwing(() -> mm.generateToolTip(dummyEvent));
|
||||
@SuppressWarnings("unchecked")
|
||||
WeakSet<MarkerMarginProvider> marginProviders =
|
||||
(WeakSet<MarkerMarginProvider>) getInstanceField("marginProviders", mm);
|
||||
MarkerMarginProvider provider = marginProviders.iterator().next();
|
||||
String tooltip = runSwing(() -> provider.generateToolTip(dummyEvent));
|
||||
assertNotNull("No tooltip for field: " + cb.getCurrentField() + "\n\tat address: " +
|
||||
cb.getCurrentAddress(), tooltip);
|
||||
|
||||
|
||||
@@ -83,9 +83,9 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
protected void dispose() {
|
||||
fidFileManager.removeChangeListener(this);
|
||||
super.cleanup();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -133,7 +133,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
private DiffController diffControl;
|
||||
private Program primaryProgram;
|
||||
private Program secondaryDiffProgram;
|
||||
private AddressFactory p2AddressFactory;
|
||||
private ProgramDiffDetails diffDetails;
|
||||
|
||||
private ProgramSelection p2DiffHighlight;
|
||||
@@ -286,33 +285,39 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
if (primaryProgram != null && !showingSecondProgram) {
|
||||
return;
|
||||
}
|
||||
|
||||
p1ViewAddrSet = p1AddressSet;
|
||||
if (!showingSecondProgram) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (showingSecondProgram) {
|
||||
ProgramSelection previousP1Selection = currentSelection;
|
||||
ProgramSelection previousP2DiffHighlight = p2DiffHighlight;
|
||||
ProgramSelection previousP2Selection = p2Selection;
|
||||
ProgramSelection previousP1Selection = currentSelection;
|
||||
ProgramSelection previousP2DiffHighlight = p2DiffHighlight;
|
||||
ProgramSelection previousP2Selection = p2Selection;
|
||||
|
||||
AddressSet p2ViewAddrSet =
|
||||
DiffUtility.getCompatibleAddressSet(p1ViewAddrSet, secondaryDiffProgram);
|
||||
diffListingPanel.setView(p2ViewAddrSet);
|
||||
FieldPanel fp = diffListingPanel.getFieldPanel();
|
||||
AddressSet p2ViewAddrSet =
|
||||
DiffUtility.getCompatibleAddressSet(p1ViewAddrSet, secondaryDiffProgram);
|
||||
diffListingPanel.setView(p2ViewAddrSet);
|
||||
FieldPanel fp = diffListingPanel.getFieldPanel();
|
||||
|
||||
AddressSet p1AddressSetAsP2 =
|
||||
DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram);
|
||||
AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2);
|
||||
markerManager.getOverviewProvider().setProgram(secondaryDiffProgram, p2IndexMap);
|
||||
fp.setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManager,
|
||||
secondaryDiffProgram, p2IndexMap));
|
||||
AddressSet p1AddressSetAsP2 =
|
||||
DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram);
|
||||
AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2);
|
||||
List<ListingOverviewProvider> providers = diffListingPanel.getOverviewProviders();
|
||||
for (ListingOverviewProvider provider : providers) {
|
||||
provider.screenDataChanged(secondaryDiffProgram, p2IndexMap);
|
||||
}
|
||||
|
||||
currentSelection = previousP1Selection;
|
||||
p2DiffHighlight = previousP2DiffHighlight;
|
||||
fp.setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManager,
|
||||
secondaryDiffProgram, p2IndexMap));
|
||||
|
||||
p2Selection = previousP2Selection;
|
||||
setProgram2Selection(p2Selection);
|
||||
if (p2DiffHighlight != null) {
|
||||
setDiffHighlight(p2DiffHighlight);
|
||||
}
|
||||
currentSelection = previousP1Selection;
|
||||
p2DiffHighlight = previousP2DiffHighlight;
|
||||
|
||||
p2Selection = previousP2Selection;
|
||||
setProgram2Selection(p2Selection);
|
||||
if (p2DiffHighlight != null) {
|
||||
setDiffHighlight(p2DiffHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,9 +561,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
return;
|
||||
}
|
||||
if (currentSelection == null) {
|
||||
AddressFactory p1AddressFactory =
|
||||
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
|
||||
currentSelection = new ProgramSelection(p1AddressFactory);
|
||||
currentSelection = new ProgramSelection();
|
||||
}
|
||||
actionManager.setP1SelectToP2ActionEnabled(
|
||||
secondaryDiffProgram != null && !currentSelection.isEmpty());
|
||||
@@ -605,8 +608,13 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
diffListingPanel.setProgramLocationListener(this);
|
||||
diffListingPanel.setProgramSelectionListener(this);
|
||||
diffListingPanel.getFieldPanel().addFieldMouseListener(new MyFieldMouseListener());
|
||||
diffListingPanel.addMarginProvider(markerManager.getMarginProvider());
|
||||
diffListingPanel.addOverviewProvider(markerManager.getOverviewProvider());
|
||||
|
||||
// Manually install our custom margin provider. We are not calling setMarginService(),
|
||||
// which means that many of the listing markers will not work in the diff view.
|
||||
MarkerService markerService = tool.getService(MarkerService.class);
|
||||
diffListingPanel.addMarginProvider(markerService.createMarginProvider());
|
||||
diffListingPanel.addOverviewProvider(markerService.createOverviewProvider());
|
||||
|
||||
diffNavigatable = new DiffNavigatable(this, codeViewerService.getNavigatable());
|
||||
diffFieldNavigator = new FieldNavigator(diffServiceProvider, diffNavigatable);
|
||||
diffListingPanel.addButtonPressedListener(diffFieldNavigator);
|
||||
@@ -678,9 +686,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
|
||||
ProgramSelection getCurrentSelection() {
|
||||
if (currentSelection == null) {
|
||||
AddressFactory p1AddressFactory =
|
||||
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
|
||||
currentSelection = new ProgramSelection(p1AddressFactory);
|
||||
currentSelection = new ProgramSelection();
|
||||
}
|
||||
return currentSelection;
|
||||
}
|
||||
@@ -753,10 +759,8 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
// Make sure that the Diff selection is to the code unit boundary.
|
||||
ProgramSelection p2CodeUnitSelection =
|
||||
new ProgramSelection(DiffUtility.getCodeUnitSet(newP2Selection, secondaryDiffProgram));
|
||||
AddressFactory p1AddressFactory =
|
||||
(primaryProgram != null) ? primaryProgram.getAddressFactory() : null;
|
||||
ProgramSelection intersection =
|
||||
new ProgramSelection(p2AddressFactory, p2CodeUnitSelection.intersect(p2DiffHighlight));
|
||||
new ProgramSelection(p2CodeUnitSelection.intersect(p2DiffHighlight));
|
||||
|
||||
p2Selection = intersection;
|
||||
AddressSet p2SelectionAsP1Set =
|
||||
@@ -767,8 +771,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
// of the MultiListing Layout being used.
|
||||
///////////////////////////////////////////
|
||||
|
||||
ProgramSelection p2SelectionAsP1 =
|
||||
new ProgramSelection(p1AddressFactory, p2SelectionAsP1Set);
|
||||
ProgramSelection p2SelectionAsP1 = new ProgramSelection(p2SelectionAsP1Set);
|
||||
runSwing(() -> {
|
||||
MarkerSet selectionMarkers = getSelectionMarkers();
|
||||
selectionMarkers.clearAll();
|
||||
@@ -783,7 +786,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
actionManager.setP1SelectToP2ActionEnabled(
|
||||
(secondaryDiffProgram != null) && !currentSelection.isEmpty());
|
||||
firePluginEvent(new ProgramSelectionPluginEvent(this.getName(),
|
||||
new ProgramSelection(p1AddressFactory, currentSelection), primaryProgram));
|
||||
new ProgramSelection(currentSelection), primaryProgram));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,7 +876,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
}
|
||||
|
||||
AddressSet p2DiffSet = DiffUtility.getCompatibleAddressSet(p1DiffSet, secondaryDiffProgram);
|
||||
ProgramSelection p2DiffSelection = new ProgramSelection(p2AddressFactory, p2DiffSet);
|
||||
ProgramSelection p2DiffSelection = new ProgramSelection(p2DiffSet);
|
||||
p2DiffHighlight = p2DiffSelection;
|
||||
AddressSet p2DiffSetAsP1 = DiffUtility.getCompatibleAddressSet(p2DiffSet, primaryProgram);
|
||||
|
||||
@@ -918,7 +921,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
if (diffControl.hasNext()) {
|
||||
diffControl.next();
|
||||
}
|
||||
setProgram2Selection(new ProgramSelection(p2AddressFactory, getDiffHighlightBlock()));
|
||||
setProgram2Selection(new ProgramSelection(getDiffHighlightBlock()));
|
||||
}
|
||||
|
||||
void previousDiff() {
|
||||
@@ -926,7 +929,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
if (diffControl.hasPrevious()) {
|
||||
diffControl.previous();
|
||||
}
|
||||
setProgram2Selection(new ProgramSelection(p2AddressFactory, getDiffHighlightBlock()));
|
||||
setProgram2Selection(new ProgramSelection(getDiffHighlightBlock()));
|
||||
}
|
||||
|
||||
private void clearDiff() {
|
||||
@@ -992,8 +995,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
AddressSet p1IgnoreSet =
|
||||
DiffUtility.getCompatibleAddressSet(p2IgnoreSet, primaryProgram);
|
||||
diffControl.ignore(p1IgnoreSet, null);
|
||||
p2DiffHighlight =
|
||||
new ProgramSelection(p2AddressFactory, p2DiffHighlight.subtract(p2IgnoreSet));
|
||||
p2DiffHighlight = new ProgramSelection(p2DiffHighlight.subtract(p2IgnoreSet));
|
||||
|
||||
adjustDiffDisplay();
|
||||
|
||||
@@ -1047,7 +1049,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
}
|
||||
AddressSetView p2ViewAddrSet =
|
||||
DiffUtility.getCompatibleAddressSet(adjustedView, secondaryDiffProgram);
|
||||
setProgram2Selection(new ProgramSelection(p2AddressFactory, p2ViewAddrSet));
|
||||
setProgram2Selection(new ProgramSelection(p2ViewAddrSet));
|
||||
}
|
||||
finally {
|
||||
diffListingPanel.getFieldPanel().requestFocus();
|
||||
@@ -1078,7 +1080,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
primaryProgram.removeListener(this);
|
||||
primaryProgram = null;
|
||||
secondaryDiffProgram = null;
|
||||
p2AddressFactory = null;
|
||||
}
|
||||
sameProgramContext = false;
|
||||
updatePgm2Enablement();
|
||||
@@ -1221,7 +1222,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
if (!currentSelection.isEmpty()) {
|
||||
AddressSet p2SelectionSet =
|
||||
DiffUtility.getCompatibleAddressSet(currentSelection, secondaryDiffProgram);
|
||||
setProgram2Selection(new ProgramSelection(p2AddressFactory,
|
||||
setProgram2Selection(new ProgramSelection(
|
||||
DiffUtility.getCodeUnitSet(p2SelectionSet, secondaryDiffProgram)));
|
||||
}
|
||||
if (p2Selection.isEmpty()) {
|
||||
@@ -1630,7 +1631,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
|
||||
primaryProgram = currentProgram;
|
||||
secondaryDiffProgram = newProgram;
|
||||
p2AddressFactory = secondaryDiffProgram.getAddressFactory();
|
||||
applyFilter = applySettingsMgr.getDefaultApplyFilter();
|
||||
diffDetails = new ProgramDiffDetails(primaryProgram, secondaryDiffProgram);
|
||||
primaryProgram.addListener(this);
|
||||
@@ -1884,14 +1884,14 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
}
|
||||
}
|
||||
|
||||
if (set.equals(p2Selection)) {
|
||||
AddressSetView asView = p2Selection;
|
||||
if (set.equals(asView)) {
|
||||
return; // Selection is unchanged so do nothing.
|
||||
}
|
||||
MarkerSet selectionMarkers = getSelectionMarkers();
|
||||
selectionMarkers.clearAll();
|
||||
|
||||
programSelectionChanged(new ProgramSelection(p2AddressFactory, set),
|
||||
EventTrigger.GUI_ACTION);
|
||||
programSelectionChanged(new ProgramSelection(set), EventTrigger.GUI_ACTION);
|
||||
updatePgm2Enablement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -762,7 +762,8 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
|
||||
}
|
||||
|
||||
private boolean isCursorOnScreen(CodeViewerService service) {
|
||||
FieldPanel fieldPanel = service.getFieldPanel();
|
||||
ListingPanel listingPanel = service.getListingPanel();
|
||||
FieldPanel fieldPanel = listingPanel.getFieldPanel();
|
||||
int cursorOffset = fieldPanel.getCursorOffset();
|
||||
return cursorOffset >= 0; // negative offset means offscreen
|
||||
}
|
||||
@@ -779,7 +780,10 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
FieldSelection selection = service.getFieldPanel().getSelection();
|
||||
|
||||
ListingPanel listingPanel = service.getListingPanel();
|
||||
FieldPanel fieldPanel = listingPanel.getFieldPanel();
|
||||
FieldSelection selection = fieldPanel.getSelection();
|
||||
AddressIndexMap addressIndexMap = service.getListingPanel().getAddressIndexMap();
|
||||
AddressSet addressSet = addressIndexMap.getAddressSet(selection);
|
||||
return addressSet;
|
||||
@@ -788,13 +792,11 @@ public class VTSubToolManager implements VTControllerListener, OptionsChangeList
|
||||
/**
|
||||
* Sets the address set to be the selection in the tool.
|
||||
*
|
||||
* @param tool
|
||||
* the tool
|
||||
* @param set
|
||||
* the addressSet to use for the selection
|
||||
* @param tool the tool
|
||||
* @param addresses the addressSet to use for the selection
|
||||
*/
|
||||
private void setSelectionInTool(PluginTool tool, AddressSetView addressSet) {
|
||||
ProgramSelection programSelection = new ProgramSelection(addressSet);
|
||||
private void setSelectionInTool(PluginTool tool, AddressSetView addresses) {
|
||||
ProgramSelection programSelection = new ProgramSelection(addresses);
|
||||
CodeViewerService service = tool.getService(CodeViewerService.class);
|
||||
if (service == null) {
|
||||
return;
|
||||
|
||||
@@ -268,6 +268,10 @@ public abstract class Plugin implements ExtensionPoint, PluginEventListener, Ser
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the framework to dispose of this plugin and unregister for events and services.
|
||||
* Subclasses should not override this method, but should instead override {@link #dispose()}.
|
||||
*/
|
||||
protected void cleanup() {
|
||||
if (!disposed) {
|
||||
Throwable thr = null;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.control;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -26,6 +26,7 @@ import org.junit.Test;
|
||||
import db.Transaction;
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.context.ProgramLocationActionContext;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerIntegrationTest;
|
||||
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
|
||||
@@ -50,6 +51,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
|
||||
|
||||
@Before
|
||||
public void setUpMethodActionsTest() throws Exception {
|
||||
addPlugin(tool, CodeBrowserPlugin.class);
|
||||
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
|
||||
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
|
||||
methodsPlugin = addPlugin(tool, DebuggerMethodActionsPlugin.class);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -31,7 +31,7 @@ import ghidra.app.util.viewer.field.*;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.listingpanel.MarginProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.program.model.address.Address;
|
||||
@@ -138,11 +138,11 @@ public class BlockModelScreenShots extends GhidraScreenShotGenerator {
|
||||
final ListingPanel lp = cb.getListingPanel();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<MarginProvider> list =
|
||||
new ArrayList<>((List<MarginProvider>) getInstanceField("marginProviders", lp));
|
||||
final List<ListingMarginProvider> list =
|
||||
new ArrayList<>((List<ListingMarginProvider>) getInstanceField("marginProviders", lp));
|
||||
runSwing(() -> {
|
||||
invokeInstanceMethod("buildPanels", lp);
|
||||
for (MarginProvider marginProvider : list) {
|
||||
for (ListingMarginProvider marginProvider : list) {
|
||||
lp.removeMarginProvider(marginProvider);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
||||
import ghidra.app.plugin.core.programtree.ViewManagerComponentProvider;
|
||||
import ghidra.app.util.viewer.field.*;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingOverviewProvider;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.CommentType;
|
||||
|
||||
@@ -265,10 +265,10 @@ public class CodeBrowserPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
public void testCaptureMarkerPopup() {
|
||||
setToolSize(1400, 1200);
|
||||
ListingPanel listingPanel = plugin.getListingPanel();
|
||||
List<OverviewProvider> overviewProviders = listingPanel.getOverviewProviders();
|
||||
List<ListingOverviewProvider> overviewProviders = listingPanel.getOverviewProviders();
|
||||
assertEquals(1, overviewProviders.size());
|
||||
|
||||
OverviewProvider provider = overviewProviders.get(0);
|
||||
ListingOverviewProvider provider = overviewProviders.get(0);
|
||||
rightClick(provider.getComponent(), 1, 1);
|
||||
|
||||
captureMenu();
|
||||
|
||||
Reference in New Issue
Block a user